Blob Blame History Raw
/* 
 * 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
*/ 
/* 
 * 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 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#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 <Xm/VaSimpleP.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 FIX_1509

#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 )
{
#ifdef FIX_1395
    Pixel tmpc;
#endif
    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) {
#ifdef FIX_1509
		if (CB_IsArmed(cb))
		    XFillRectangle(XtDisplay(cb), XtWindow(cb), CB_ArmGC(cb),
				   0, 0, cb->core.width, cb->core.height);
		else
	        XClearArea(XtDisplay(cb), XtWindow(cb),
				0, 0, cb->core.width, cb->core.height, False);
#else
	    XFillRectangle(XtDisplay(cb), XtWindow(cb),
		           CB_IsArmed(cb) ? CB_ArmGC(cb) : CB_BackgroundGC(cb),
			   0, 0, cb->core.width, cb->core.height);
#endif
#ifdef USE_XFT
	} else if (Lab_MenuType(cb) != XmWORK_AREA) { /* adeed with XFT support */
        XClearArea(XtDisplay(cb), XtWindow(cb),
			0, 0, cb->core.width, cb->core.height, False);
#endif
	}
	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);
	    }
#ifdef FIX_1395
	    /* 1395:
	     By default (if not etched and not armed) window (widget) have 
	     background color that used to draw widget bg. When widget is 
	     etched, background selected correctly (as selected color), 
	     but label exposr method will use default background color 
	     to fill area bellow lable text. Result is ugly menus.
	     We should replace colors before expose from label
	     and change it back after repainting.
	    */
            tmpc = cb->core.background_pixel;
            XSetWindowBackground(XtDisplay(cb), XtWindow(cb), select_pix);
#endif

	    _XmProcessLock();
	    expose = xmLabelClassRec.core_class.expose;
	    _XmProcessUnlock();
	    (*expose)((Widget) cb, event, region);
#ifdef FIX_1395
	    /*
	     Set correct window background (label is repainted, role back)
	    */
	    XSetWindowBackground(XtDisplay(cb), XtWindow(cb), tmpc);
	    if (cb->core.background_pixmap != XmUNSPECIFIED_PIXMAP)
	    	XSetWindowBackgroundPixmap(XtDisplay(cb), XtWindow(cb),
	    			cb->core.background_pixmap);
#endif

	    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 (old->primitive.foreground != new_w->primitive.foreground
    || old->core.background_pixel != new_w->core.background_pixel) {
      GetBackgroundGC(new_w);
  }

  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);
}

Widget 
XmVaCreateCascadeButton(
        Widget parent,
        char *name,
        ...)
{
    register Widget w;
    va_list var;
    int count;
    
    Va_start(var,name);
    count = XmeCountVaListSimple(var);
    va_end(var);

    
    Va_start(var, name);
    w = XmeVLCreateWidget(name, 
                         xmCascadeButtonWidgetClass,
                         parent, False, 
                         var, count);
    va_end(var);   
    return w;
}

Widget
XmVaCreateManagedCascadeButton(
        Widget parent,
        char *name,
        ...)
{
    Widget w = NULL;
    va_list var;
    int count;
    
    Va_start(var, name);
    count = XmeCountVaListSimple(var);
    va_end(var);
    
    Va_start(var, name);
    w = XmeVLCreateWidget(name, 
                         xmCascadeButtonWidgetClass,
                         parent, True, 
                         var, count);
    va_end(var);   
    return w;
}

/*
 * 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);
}