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

static const char rcsid[] = "$Id: CascadeBG.c,v 1.75 1998/03/16 21:52:09 rwscott Exp $";

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

#include <Xm/XmP.h>
#include <Xm/CacheP.h>
#include <Xm/CascadeB.h>	/* For XmCascadeButtonHighlight */
#include <Xm/CascadeBP.h>
#include <Xm/CascadeBGP.h>
#include <Xm/ManagerP.h>
#include <Xm/MenuShellP.h>
#include <Xm/RowColumnP.h>
#include <Xm/ScreenP.h>
#include <Xm/MenuUtilP.h>
#include <X11/Xfuncs.h>

#ifndef __STDC__
#include <varargs.h>
#else
#include <stdarg.h>
#endif

#include <XmI/DebugUtil.h>

#define	DO_SANITY

/* Forward Declarations */

static void class_initialize();

static void class_part_initialize(WidgetClass w_class);

static void initialize(Widget request, Widget new_w,
		       ArgList args, Cardinal *num_args);

static void resize(Widget w);

static void destroy(Widget w);

static void expose(Widget w, XEvent *event, Region region);

static Boolean set_values(Widget current, Widget request, Widget new_w,
			  ArgList args, Cardinal *num_args);

static void input_dispatch(Widget gadget, XEvent *event, Mask event_mask);

static Boolean visual_change(Widget w, Widget parent, Widget n);

static void secondary_object_create(Widget request, Widget new_w,
				    ArgList args, Cardinal *num_args);

static void initialize_posthook(Widget request, Widget new_w,
				ArgList args, Cardinal *num_args);

static Boolean set_values_prehook(Widget old, Widget request, Widget new_w,
				  ArgList args, Cardinal *num_args);

static Boolean set_values_posthook(Widget old, Widget request, Widget new_w,
				   ArgList args, Cardinal *num_args);

static void get_values_prehook(Widget new_w,
			       ArgList args, Cardinal *num_args);

static void get_values_posthook(Widget new_w,
				ArgList args, Cardinal *num_args);

static void border_unhighlight(Widget w);

static void border_highlight(Widget w);

static Cardinal get_sec_res_data(WidgetClass wc,
				 XmSecondaryResourceData **data);

extern int _XmCascadeBCacheCompare(XtPointer A, XtPointer B);

void _XmCBGCalcDimensions(Widget w);

static void DelayedArm(Widget w, XEvent *event,
		       String *params, Cardinal *num_params);

static void CheckDisarm(Widget w, XEvent *event,
			String *params, Cardinal *num_params);

static void ArmAndActivate(Widget w, XEvent *event,
			   String *params, Cardinal *num_params);

static void MenuProcEntry(int proc, Widget rc,...);

/*
 * cache resources
 */
#define Offset(field) XtOffsetOf(XmCascadeButtonGCacheObjRec, \
				 cascade_button_cache.field)
static XtResource cache_resources[] =
{
    {
	XmNcascadePixmap, XmCPixmap, XmRGadgetPixmap,
	sizeof(Pixmap), Offset(cascade_pixmap),
	XmRImmediate, (XtPointer)XmUNSPECIFIED_PIXMAP
    },
    {
	XmNmappingDelay, XmCMappingDelay, XmRInt,
	sizeof(int), Offset(map_delay),
	XmRImmediate, (XtPointer)180
    }
};

/* *INDENT-OFF* */
XmCascadeButtonGCacheObjClassRec xmCascadeButtonGCacheObjClassRec = {
    /* Object class part */
    {
	/* superclass            */ (WidgetClass) &xmLabelGCacheObjClassRec,
        /* class_name            */ "XmCascadeButtonGCacheObjClass",
	/* widget_size           */ sizeof(XmCascadeButtonGCacheObjRec),
	/* class_initialize      */ NULL,
	/* class_part_initialize */ NULL,
	/* class_inited          */ False,
	/* initialize            */ NULL,
	/* initialize_hook       */ NULL,
	/* realize               */ NULL,
	/* actions               */ NULL,
	/* num_actions           */ 0,
	/* resources             */ cache_resources,
	/* num_resources         */ XtNumber(cache_resources),
	/* xrm_class             */ NULLQUARK,
	/* compress_motion       */ 0,
	/* compress_exposure     */ 0,
	/* compress_enterleave   */ 0,
	/* visible_interest      */ 0,
	/* destroy               */ NULL,
	/* resize                */ NULL,
	/* expose                */ NULL,
	/* set_values            */ NULL,
	/* set_values_hook       */ NULL,
	/* set_values_almost     */ NULL,
	/* get_values_hook       */ NULL,
	/* accept_focus          */ NULL,
	/* version               */ XtVersion,
	/* callback offsets      */ NULL,
	/* tm_table              */ NULL,
	/* query_geometry        */ NULL,
        /* display_accelerator   */ NULL,
	/* extension             */ NULL
    },
    /* XmExtObject part */
    {
        /* syn_resources      */ NULL,
        /* num_syn_resources  */ 0,
        /* extension          */ NULL
    },
    /* LabelGCacheObj part */
    {
	/* foo                */ 0
    },
    /* CascadeButtonGCacheObj part */
    {
	/* foo                */ 0
    }
};

/*
 * Resources for the cascadebutton class
 */
#undef Offset
#define Offset(field) XtOffsetOf(XmCascadeButtonGadgetRec, cascade_button.field)
#define GOffset(field) XtOffsetOf(XmCascadeButtonGadgetRec, gadget.field)
static XtResource resources[] = {
    {
	XmNactivateCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(activate_callback),
	XmRCallback, (XtPointer)NULL
    },
    {
	XmNcascadingCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(cascade_callback),
	XmRCallback, (XtPointer)NULL
    },
    {
	XmNsubMenuId, XmCMenuWidget, XmRMenuWidget,
	sizeof(Widget), Offset(submenu),
	XmRMenuWidget, (XtPointer)NULL
    },
    /* resources we override from XmGadget */
    {
	XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
	sizeof(Dimension), GOffset(shadow_thickness),
	XmRCallProc, (XtPointer)_XmPrimitiveShadowThicknessDefault
    },
    {
	XmNtraversalOn, XmCTraversalOn, XmRBoolean,
	sizeof(Boolean), GOffset(traversal_on),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension,
	sizeof(Dimension), GOffset(highlight_thickness),
	XmRCallProc, (XtPointer)_XmPrimitiveHighlightThicknessDefault
    },
};

static XmBaseClassExtRec _XmCascadeBGRectClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,                             
    /* version                   */ XmBaseClassExtVersion,
    /* size                      */ sizeof(XmBaseClassExtRec),
    /* initialize_prehook        */ XmInheritInitializePrehook,
    /* set_values_prehook        */ set_values_prehook,
    /* initialize_posthook       */ initialize_posthook,
    /* set_values_posthook       */ set_values_posthook,
    /* secondary_object_class    */ (WidgetClass)&xmCascadeButtonGCacheObjClassRec,
    /* secondary_object_create   */ secondary_object_create,
    /* get_secondary_resources   */ get_sec_res_data,
    /* fast_subclass             */ { 0 },
    /* get_values_prehook        */ get_values_prehook,
    /* get_values_posthook       */ get_values_posthook,
    /* class_part_init_prehook   */ NULL,
    /* class_part_init_posthook  */ NULL,
    /* ext_resources             */ NULL,
    /* compiled_ext_resources    */ NULL,
    /* num_ext_resources         */ 0,
    /* use_sub_resources         */ False,
    /* widget_navigable          */ XmInheritWidgetNavigable,
    /* focus_change              */ XmInheritFocusChange,
    /* wrapper_data              */ NULL
};

static XmCacheClassPart cache_part = {
    /* cache head part */
    {
        /* next         */ NULL,
        /* prev         */ NULL,
        /* ref_count    */ 0
    },
    _XmCacheCopy,
    _XmCacheDelete,
    _XmCascadeBCacheCompare
};

static XmGadgetClassExtRec _XmCascadeBGGadgetClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,
    /* version                   */ XmGadgetClassExtVersion,
    /* size                      */ sizeof(XmGadgetClassExtRec),
    /* widget_baseline_proc      */ XmInheritBaselineProc,
    /* display_rect_proc         */ XmInheritDisplayRectProc,
};

XmCascadeButtonGadgetClassRec xmCascadeButtonGadgetClassRec = {
    /* RectObj class part */
    {
	/* superclass            */ (WidgetClass) &xmLabelGadgetClassRec,
	/* class_name            */ "XmCascadeButtonGadget",
	/* widget_size           */ sizeof(XmCascadeButtonGadgetRec),
	/* class_initialize      */ class_initialize,
	/* class_part_initialize */ class_part_initialize,
	/* class_inited          */ False,
	/* initialize            */ initialize,
	/* initialize_hook       */ NULL,
	/* realize               */ NULL,
	/* actions               */ NULL,
	/* num_actions           */ 0,
	/* resources             */ resources,
	/* num_resources         */ XtNumber(resources),
	/* xrm_class             */ NULLQUARK,
	/* compress_motion       */ True,
	/* compress_exposure     */ XtExposeCompressMaximal,
	/* compress_enterleave   */ True,
 	/* visible_interest      */ False,
	/* destroy               */ destroy,
	/* resize                */ resize,
	/* expose                */ expose,
	/* set_values            */ set_values,
	/* set_values_hook       */ NULL,
	/* set_values_almost     */ XtInheritSetValuesAlmost,
	/* get_values_hook       */ NULL,
	/* accept_focus          */ NULL,
	/* version               */ XtVersion,
	/* callback offsets      */ NULL,
	/* tm_table              */ NULL,
	/* query_geometry        */ XtInheritQueryGeometry,
	/* display_accelerator   */ NULL,
	/* extension             */ (XtPointer)&_XmCascadeBGRectClassExtRec
    },
    /* XmGadget part */
    {
	/* border_highlight   */ border_highlight, 
	/* border_unhighlight */ border_unhighlight,
	/* arm_and_activate   */ ArmAndActivate,
	/* input_dispatch     */ input_dispatch,
	/* visual_change      */ visual_change,
	/* syn_resources      */ NULL,
	/* num_syn_resources  */ 0,
	/* cache_part         */ &cache_part,
	/* extension          */ (XtPointer)&_XmCascadeBGGadgetClassExtRec
    },
    /* XmLabelGadget part */
    {
        /* setOverrideCallback */ XmInheritSetOverrideCallback,
        /* menuProcs           */ XmInheritMenuProc,
	/* extension           */ NULL
    },
    /* XmCascadeButtonGadget part */
    {
	/* extension */ NULL
    },
};
/* *INDENT-ON* */


WidgetClass xmCascadeButtonGadgetClass =
				(WidgetClass)&xmCascadeButtonGadgetClassRec;

/********************************* CACHE PART *******************************/
static void
secondary_object_create(Widget request, Widget new_w,
			ArgList args, Cardinal *num_args)
{
    XmBaseClassExt *bce;
    XtPointer nsec, rsec;
    XmWidgetExtData ed;
    int size;

    DEBUGOUT(XdbDebug(__FILE__, new_w,
		      "CascadeButtonGCacheRec %s being initialized.\n",
		      XtName(new_w)));

    bce = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);

    size = (*bce)->secondaryObjectClass->core_class.widget_size;
    nsec = _XmExtObjAlloc(size);
    rsec = _XmExtObjAlloc(size);

    ((XmExtRec *)nsec)->object.self = (Widget)nsec;
    ((XmExtRec *)nsec)->object.widget_class = (*bce)->secondaryObjectClass;
    ((XmExtRec *)nsec)->object.parent = XtParent(new_w);
    ((XmExtRec *)nsec)->object.xrm_name = new_w->core.xrm_name;
    ((XmExtRec *)nsec)->object.being_destroyed = False;
    ((XmExtRec *)nsec)->object.destroy_callbacks = NULL;
    ((XmExtRec *)nsec)->object.constraints = NULL;

    ExtObj_LogicalParent(nsec) = new_w;
    ExtObj_ExtensionType(nsec) = XmCACHE_EXTENSION;

    XtGetSubresources(new_w, nsec, NULL, NULL,
		      (*bce)->secondaryObjectClass->core_class.resources,
		      (*bce)->secondaryObjectClass->core_class.num_resources,
		      args, *num_args);

    ed = (XmWidgetExtData)XtMalloc(sizeof(XmWidgetExtDataRec));
    ed->widget = (Widget)nsec;
    ed->reqWidget = (Widget)rsec;

    bcopy(nsec, rsec, size);

    ((XmExtRec *)rsec)->object.self = (Widget)rsec;

    _XmPushWidgetExtData(new_w, ed, XmCACHE_EXTENSION);

    LabG_Cache(new_w) = &(((XmLabelGCacheObject)nsec)->label_cache);
    LabG_Cache(request) = &(((XmLabelGCacheObject)rsec)->label_cache);

    CBG_Cache(new_w) =
	&(((XmCascadeButtonGCacheObject)nsec)->cascade_button_cache);
    CBG_Cache(request) =
	&(((XmCascadeButtonGCacheObject)rsec)->cascade_button_cache);
}

int
_XmCascadeBCacheCompare(XtPointer A, XtPointer B)
{
    return !bcmp(((XmCascadeButtonGCacheObjPart *)A),
		 ((XmCascadeButtonGCacheObjPart *)B),
		 sizeof(XmCascadeButtonGCacheObjPart));
}

int
_XmArrowPixmapCacheCompare(XtPointer A, XtPointer B)
{
    return 0;
}

void
_XmArrowPixmapCacheDelete(XtPointer data)
{
}

/*
 * Assumes that the existing pixmaps are freed.
 *
 * Note : added pixmaps for Option Menu; you can hardly call these arrows.
 *      Danny 26/2/97.
 */
void
_XmCreateArrowPixmaps(Widget w)
{
    Pixmap apm, pm;
    GC bgc, tgc, fgc;
    Display *dpy;
    int depth;
    Dimension ww, hh, st;
    Screen *scr;
    Window win;
    Pixel tsc, bsc;
    XGCValues values;
    XtGCMask mask, dynamic, dontcare;
    int arrow;
    int mt;

    tsc = XmParentTopShadowColor(w);	/* top shadow color */
    bsc = XmParentBottomShadowColor(w);		/* bottom shadow color */

    values.foreground = bsc;
    values.background = XmParentBackground(w);
    mask = GCForeground | GCBackground;
    dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin;
    dontcare = ~(mask | dynamic);
    bgc = XtAllocateGC(w, 0, mask, &values, dynamic, dontcare);

    values.foreground = tsc;
    values.background = XmParentBackground(w);
    mask = GCForeground | GCBackground;
    dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin;
    dontcare = ~(mask | dynamic);
    tgc = XtAllocateGC(w, 0, mask, &values, dynamic, dontcare);

    values.foreground = values.background = XmParentBackground(w);
    mask = GCForeground | GCBackground;
    dynamic = GCForeground | GCClipMask | GCClipXOrigin | GCClipYOrigin;
    dontcare = ~(mask | dynamic);
    fgc = XtAllocateGC(w, 0, mask, &values, dynamic, dontcare);

    dpy = XtDisplay(w);
    scr = XtScreen(w);
    depth = DefaultDepthOfScreen(scr);

    if (XmIsGadget(w))
    {
	ww = 2 * LabG_TextRect_height(w) / 3;
	hh = LabG_TextRect_height(w) & ~1;

	CBG_Cascade_width(w) = LabG_TextRect_height(w) /* rws & ~1 */;
	CBG_Cascade_height(w) = LabG_TextRect_height(w) & ~1;
    }
    else
    {
	ww = 2 * Lab_TextRect_height(w) / 3;
	hh = Lab_TextRect_height(w) & ~1;

	CB_Cascade_width(w) = Lab_TextRect_height(w) & ~1;
	CB_Cascade_height(w) = Lab_TextRect_height(w) & ~1;
    }

    st = 2;

    win = RootWindowOfScreen(scr);

    mt = XmIsGadget(w) ? LabG_MenuType(w) : Lab_MenuType(w);
    arrow = (mt != XmMENU_OPTION);

    /* Create pixmaps, fill them up with _XmDrawArrow/Shadows */
    if (hh == 0)
    {
	hh = 1;
    }

    pm = XCreatePixmap(dpy, win, hh, hh, depth);
    apm = XCreatePixmap(dpy, win, hh, hh, depth);

    XFillRectangle(dpy, pm, fgc, 0, 0, hh, hh);
    XFillRectangle(dpy, apm, fgc, 0, 0, hh, hh);

    DEBUGOUT(XdbDebug(__FILE__, w, "_XmCreateArrowPixmaps(%s) => %s\n",
		      XdbMenuType2String(mt),
		      arrow ? "arrows" : "rectangles"));

    if (arrow)
    {				/* Arrows */
	_XmDrawArrow(dpy, pm,
		     bgc, tgc, fgc,
		     0, 0, ww, hh,
		     st, XmARROW_RIGHT);

	_XmDrawArrow(dpy, apm,
		     tgc, bgc, fgc,
		     0, 0, ww, hh,
		     st, XmARROW_RIGHT);
    }
    else
    {				/* Rectangles */
	_XmDrawShadows(dpy, pm,
		       tgc, bgc,
		       0, (hh - ww + 1) / 2, hh - st, ww - st,
		       st, XmSHADOW_OUT);

	_XmDrawShadows(dpy, apm,
		       bgc, tgc,
		       0, (hh - ww + 1) / 2, hh - st, ww - st,
		       st, XmSHADOW_OUT);
    }

    /* Copy them into the widget/gadget */
    if (XmIsGadget(w))
    {
	CBG_ArmedPixmap(w) = apm;
	CBG_CascadePixmap(w) = pm;
    }
    else
    {
	CB_ArmedPixmap(w) = apm;
	CB_CascadePixmap(w) = pm;
    }

    /* Caching ?? */
    XtReleaseGC(w, bgc);
    XtReleaseGC(w, fgc);
    XtReleaseGC(w, tgc);
}

/******************************** GADGET PART *******************************/
static void
class_initialize()
{
    XtResourceList combined, labels;
    int ncom;
    Cardinal nlabels;

    /* don't let the nulls fool you.  look at the header file -- the arg
     * isn't used. */
    ClassCacheHead(CBG_ClassCachePart(NULL)).prev =
	&ClassCacheHead(CBG_ClassCachePart(NULL));
    ClassCacheHead(CBG_ClassCachePart(NULL)).next =
	&ClassCacheHead(CBG_ClassCachePart(NULL));

    _XmCascadeBGRectClassExtRec.record_type = XmQmotif;

    /*
     * Label subclasses (ToggleBG, PushBG, CascadeBG) have a problem.  Since
     * we do all the subpart manipulation in the pre- and post- hooks, and
     * since those hooks aren't chained, we have to either make multiple
     * calls to XtGetSubresources/Xt[Get|Set]Subvalues, or merge the resource
     * lists.  Since I just wrote _XmTransformSubresources, seems like a
     * waste not to use it.
     */
    ncom = XtNumber(cache_resources) +
	xmLabelGCacheObjClassRec.object_class.num_resources;

    _XmTransformSubResources(xmLabelGCacheObjClassRec.object_class.resources,
			   xmLabelGCacheObjClassRec.object_class.num_resources,
			     &labels, &nlabels);

    combined = (XtResourceList)XtMalloc(sizeof(XtResource) * ncom);

    bcopy(labels, combined, nlabels * sizeof(XtResource));
    bcopy(cache_resources,
	  &combined[nlabels],
	  XtNumber(cache_resources) * sizeof(XtResource));

    XtFree((char *)labels);

    xmCascadeButtonGCacheObjClassRec.object_class.resources = combined;
    xmCascadeButtonGCacheObjClassRec.object_class.num_resources = ncom;
}

static void
class_part_initialize(WidgetClass widget_class)
{
    _XmFastSubclassInit(widget_class, XmCASCADE_BUTTON_GADGET_BIT);
}

static void
initialize_posthook(Widget request, Widget new_w,
		    ArgList args, Cardinal *num_args)
{
    XmWidgetExtData ext;

    DEBUGOUT(XdbDebug(__FILE__, new_w, "CascadeBG InitializePosthook\n"));

    /* don't let the null fool you */
    LabG_Cache(new_w) = (XmLabelGCacheObjPart *)
	_XmCachePart(LabG_ClassCachePart(NULL),
		     (XtPointer)LabG_Cache(new_w),
		     sizeof(XmLabelGCacheObjPart));
    CBG_Cache(new_w) = (XmCascadeButtonGCacheObjPart *)
	_XmCachePart(CBG_ClassCachePart(NULL),
		     (XtPointer)CBG_Cache(new_w),
		     sizeof(XmCascadeButtonGCacheObjPart));

    _XmPopWidgetExtData(new_w, &ext, XmCACHE_EXTENSION);

    _XmExtObjFree((XtPointer)ext->widget);
    _XmExtObjFree((XtPointer)ext->reqWidget);

    XtFree((char *)ext);
}

static void
initialize(Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    if (!XmIsManager(XtParent(new_w)))
    {
	_XmError(new_w, "parent should be manager.");
    }

    CBG_Cascade_x(new_w) = 0;
    CBG_Cascade_y(new_w) = 0;
    CBG_Cascade_width(new_w) = 0;
    CBG_Cascade_height(new_w) = 0;

    CBG_SetArmed(new_w, False);

    if (LabG_MenuType(new_w) == XmMENU_BAR ||
	LabG_MenuType(new_w) == XmMENU_POPUP ||
	LabG_MenuType(new_w) == XmMENU_PULLDOWN)
    {
	LabG_Highlight(new_w) = 0;
    }
    else if (LabG_MenuType(new_w) != XmMENU_OPTION)
    {
	_XmError(new_w, "Cascade gadget parent is incorrect type.");
    }

    if (LabG_MenuType(new_w) == XmMENU_BAR)
    {
	LabG_MarginWidth(new_w) = G_ShadowThickness(new_w) +
	    Xm3D_ENHANCE_PIXEL * 2;
    }
    else
    {
	LabG_MarginWidth(new_w) = G_ShadowThickness(new_w);
    }

    if (LabG_MenuType(new_w) == XmMENU_OPTION)
    {
#if 0
	/* rws */LabG_MarginLeft(new_w) = G_HighlightThickness(new_w);
#endif
	/* rws */LabG_MarginWidth(new_w) = G_ShadowThickness(new_w);
	LabG_MarginHeight(new_w) = G_ShadowThickness(new_w);
	LabG_MarginTop(new_w) = G_HighlightThickness(new_w);
	LabG_MarginBottom(new_w) = G_HighlightThickness(new_w);
    }

    if (LabG_MenuType(new_w) == XmMENU_POPUP ||
	LabG_MenuType(new_w) == XmMENU_PULLDOWN ||
	LabG_MenuType(new_w) == XmMENU_OPTION)	/* rws 23 Mar 1997 */
    {
	_XmCreateArrowPixmaps(new_w);

	_XmCBGCalcDimensions(new_w);
    }

    CBG_Timer(new_w) = 0;

    if (CBG_Submenu(new_w))
    {
	/* Make sure the RC also knows how to locate us */
	RC_MenuSubmenu(new_w);
    }

    G_EventMask(new_w) = XmARM_EVENT | XmACTIVATE_EVENT |
	XmENTER_EVENT | XmLEAVE_EVENT |
	XmFOCUS_IN_EVENT | XmFOCUS_OUT_EVENT |
	XmHELP_EVENT | XmBDRAG_EVENT;

    G_TraversalOn(new_w) = True;   /* Danny 18/5/1996 - FIX ME (not sure) */
    LabGClass_MenuProcs(XtClass(new_w)) = MenuProcEntry;
}

static Dimension
ComputeMaximumWidth(Widget cb)
{
    Dimension wd = 0;
    Widget sm = CBG_Submenu(cb);
    int i;

    if (!sm)
    {
	return 0;
    }

    for (i = 0; i < MGR_NumChildren(sm); i++)
    {
	if (XtWidth(MGR_Children(sm)[i]) > wd)
	{
	    wd = XtWidth(MGR_Children(sm)[i]);
	}
    }

    return wd;
}

void
_XmCBGCalcDimensions(Widget w)
{
    Dimension cw, ch, width, height;

    if (RC_Type(XtParent(w)) == XmMENU_OPTION ||
	RC_Type(XtParent(w)) == XmMENU_PULLDOWN ||
	RC_Type(XtParent(w)) == XmMENU_POPUP)
    {
	/* Cascade_width is computed in CreateArrowPixmaps */

	LabG_MarginRight(w) = CBG_Cascade_width(w) +
	    4 * LabG_MarginWidth(w);
    }

    XtWidth(w) = XtHeight(w) = 0;

    if (LabG_IsText(w))
    {
	XmString string;

	if (_XmStringIsXmString((XmString)LabG_Label(w)))
	{
	    DEBUGOUT(XdbDebug(__FILE__, w,
		   "_XmCBGCalcDimensions: convert LabG_Label to _XmString\n"));
	    LabG_Label(w) = _XmStringCreate((XmString)LabG_Label(w));
	}

	_XmStringExtent(LabG_Font(w),
			LabG_Label(w),
			&cw,
			&ch);

	/* rws 16 Aug 1997
	 * Do not let a cascade button be shorter than the height of one
	 * character.  This is to make sure that the pixmap can be drawn
	 * correctly. This shows in Mosaic with option buttons in a form.
	 */
	string = XmStringCreateSimple("M");
	if (ch < XmStringHeight(LabG_Font(w), string))
	{
	    ch = XmStringHeight(LabG_Font(w), string);
	}

	XmStringFree(string);
    }
    /* pixmap */
    else
    {
	_XmLabelGetPixmapSize(w, LabG_Pixmap(w), &cw, &ch);
    }

    if (RC_Type(XtParent(w)) == XmMENU_OPTION && CBG_Submenu(w) &&
	ComputeMaximumWidth(w) > cw)
    {
	cw = ComputeMaximumWidth(w);
	/* rws */cw -= 2 * LabG_MarginWidth(w);
	/* rws */cw -= 2 * LabG_MarginWidth(w);
	LabG_TextRect_width(w) = cw;
    }

    width = (LabG_Highlight(w)
	     + LabG_Shadow(w)
	     + LabG_MarginLeft(w)
	     + LabG_MarginWidth(w)
	     + cw
	     + LabG_MarginWidth(w)
	     + LabG_MarginRight(w)
	     + LabG_Shadow(w)
	     + LabG_Highlight(w));

    height = (LabG_Highlight(w)
	      + LabG_Shadow(w)
	      + LabG_MarginTop(w)
	      + LabG_MarginHeight(w)
	      + ch
	      + LabG_MarginHeight(w)
	      + LabG_MarginBottom(w)
	      + LabG_Shadow(w)
	      + LabG_Highlight(w));

    XtWidth(w) = width;
    XtHeight(w) = height;

    CBG_Cascade_x(w) = XtWidth(w) -
			CBG_Cascade_width(w) -
			2 * G_ShadowThickness(w) -
			G_HighlightThickness(w);
    CBG_Cascade_y(w) = LabG_TextRect_y(w);
}

static void
resize(Widget w)
{
#define superclass (&xmLabelGadgetClassRec)
    (*superclass->rect_class.resize) (w);
#undef superclass

    CBG_Cascade_x(w) = XtWidth(w) -
			CBG_Cascade_width(w) -
			2 * G_ShadowThickness(w) -
			G_HighlightThickness(w);
    CBG_Cascade_y(w) = LabG_TextRect_y(w);
}

static void
destroy(Widget w)
{
    _XmCacheDelete((XtPointer)CBG_Cache(w));
}

static Boolean
set_values_prehook(Widget old, Widget request, Widget new_w,
		   ArgList args, Cardinal *num_args)
{
    XmBaseClassExt *bce;
    XmWidgetExtData ed;
    int size;
    XtPointer nsec, rsec;

    bce = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);
    size = (*bce)->secondaryObjectClass->core_class.widget_size;

    nsec = _XmExtObjAlloc(size);
    rsec = _XmExtObjAlloc(size);

    ((XmExtRec *)nsec)->object.self = (Widget)nsec;
    ((XmExtRec *)nsec)->object.widget_class = (*bce)->secondaryObjectClass;
    ((XmExtRec *)nsec)->object.parent = XtParent(new_w);
    ((XmExtRec *)nsec)->object.xrm_name = new_w->core.xrm_name;
    ((XmExtRec *)nsec)->object.being_destroyed = False;
    ((XmExtRec *)nsec)->object.destroy_callbacks = NULL;
    ((XmExtRec *)nsec)->object.constraints = NULL;

    ExtObj_LogicalParent(nsec) = new_w;
    ExtObj_ExtensionType(nsec) = XmCACHE_EXTENSION;

    bcopy(LabG_Cache(new_w),
	  &((XmLabelGCacheObject)nsec)->label_cache,
	  sizeof(XmLabelGCacheObjPart));
    bcopy(CBG_Cache(new_w),
	  &((XmCascadeButtonGCacheObject)nsec)->cascade_button_cache,
	  sizeof(XmCascadeButtonGCacheObjPart));

    ed = (XmWidgetExtData)XtMalloc(sizeof(XmWidgetExtDataRec));

    ed->widget = (Widget)nsec;
    ed->reqWidget = (Widget)rsec;

    _XmPushWidgetExtData(new_w, ed, XmCACHE_EXTENSION);

    _XmGadgetImportSecondaryArgs(new_w, args, num_args);

    XtSetSubvalues((XtPointer)nsec,
		   (*bce)->secondaryObjectClass->core_class.resources,
		   (*bce)->secondaryObjectClass->core_class.num_resources,
		   args, *num_args);

    bcopy(nsec, rsec, size);

    LabG_Cache(new_w) = &(((XmCascadeButtonGCacheObject)nsec)->label_cache);
    LabG_Cache(request) = &(((XmCascadeButtonGCacheObject)rsec)->label_cache);

    CBG_Cache(new_w) =
	&(((XmCascadeButtonGCacheObject)nsec)->cascade_button_cache);
    CBG_Cache(request) =
	&(((XmCascadeButtonGCacheObject)rsec)->cascade_button_cache);

    _XmExtImportArgs((Widget)nsec, args, num_args);

    return False;
}

static Boolean
set_values_posthook(Widget old, Widget request, Widget new_w,
		    ArgList args, Cardinal *num_args)
{
    XmWidgetExtData ext;

    if (!_XmLabelCacheCompare((XtPointer)LabG_Cache(new_w),
			      (XtPointer)LabG_Cache(old)))
    {

	_XmCacheDelete((XtPointer)LabG_Cache(old));

	LabG_Cache(new_w) = (XmLabelGCacheObjPart *)
	    _XmCachePart(LabG_ClassCachePart(NULL),
			 (XtPointer)LabG_Cache(new_w),
			 sizeof(XmLabelGCacheObjPart));
    }
    else
    {
	LabG_Cache(new_w) = LabG_Cache(old);
    }

    if (!_XmCascadeBCacheCompare((XtPointer)CBG_Cache(new_w),
				 (XtPointer)CBG_Cache(old)))
    {
	_XmCacheDelete((XtPointer)CBG_Cache(old));

	CBG_Cache(new_w) = (XmCascadeButtonGCacheObjPart *)
	    _XmCachePart(CBG_ClassCachePart(NULL),
			 (XtPointer)CBG_Cache(new_w),
			 sizeof(XmCascadeButtonGCacheObjPart));
    }
    else
    {
	CBG_Cache(new_w) = CBG_Cache(old);
    }

    _XmPopWidgetExtData(new_w, &ext, XmCACHE_EXTENSION);

    _XmExtObjFree((XtPointer)ext->widget);
    _XmExtObjFree((XtPointer)ext->reqWidget);

    XtFree((char *)ext);

    return False;
}

static Boolean
set_values(Widget old, Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    Boolean refresh_needed = False;

    DEBUGOUT(XdbDebug(__FILE__, new_w, "set_values()\n"));

    if (CBG_CascadePixmap(old) != CBG_CascadePixmap(new_w))
    {
	_XmFreeScratchPixmap((XmScreen)XmGetXmScreen(XtScreenOfObject(old)),
			     CBG_CascadePixmap(old));
	_XmFreeScratchPixmap((XmScreen)XmGetXmScreen(XtScreenOfObject(old)),
			     CBG_ArmedPixmap(old));

	_XmCreateArrowPixmaps(new_w);

	_XmCBGCalcDimensions(new_w);

	refresh_needed = True;
    }

    if (LabG_Label(new_w) != LabG_Label(old))
    {
	_XmCBGCalcDimensions(new_w);

	refresh_needed = True;
    }

    if (CBG_Submenu(old) != CBG_Submenu(new_w))
    {
	/* Make sure the RC also knows how to locate us */
	RC_MenuSubmenu(new_w);
	_XmCBGCalcDimensions(new_w);
	refresh_needed = True;
    }

    return refresh_needed;
}

static void
get_values_prehook(Widget new_w, ArgList args, Cardinal *num_args)
{
    XmBaseClassExt *bce;
    XmWidgetExtData ed;
    int size;
    XtPointer nsec;

    bce = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);
    size = (*bce)->secondaryObjectClass->core_class.widget_size;

    nsec = _XmExtObjAlloc(size);

    bcopy(LabG_Cache(new_w),
	  &((XmLabelGCacheObject)nsec)->label_cache,
	  sizeof(XmLabelGCacheObjPart));
    bcopy(CBG_Cache(new_w),
	  &((XmCascadeButtonGCacheObject)nsec)->cascade_button_cache,
	  sizeof(XmCascadeButtonGCacheObjPart));

    /*
     * don't do this and ResInd will blow up.
     */
    ((XmExtRec *)nsec)->object.self = (Widget)nsec;
    ((XmExtRec *)nsec)->object.widget_class = (*bce)->secondaryObjectClass;
    ((XmExtRec *)nsec)->object.parent = XtParent(new_w);
    ((XmExtRec *)nsec)->object.xrm_name = new_w->core.xrm_name;
    ((XmExtRec *)nsec)->object.being_destroyed = False;
    ((XmExtRec *)nsec)->object.destroy_callbacks = NULL;
    ((XmExtRec *)nsec)->object.constraints = NULL;

    ExtObj_LogicalParent(nsec) = new_w;
    ExtObj_ExtensionType(nsec) = XmCACHE_EXTENSION;

    ed = (XmWidgetExtData)XtMalloc(sizeof(XmWidgetExtDataRec));
    ed->widget = (Widget)nsec;

    _XmPushWidgetExtData(new_w, ed, XmCACHE_EXTENSION);

    XtGetSubvalues((XtPointer)nsec,
		   (*bce)->secondaryObjectClass->core_class.resources,
		   (*bce)->secondaryObjectClass->core_class.num_resources,
		   args, *num_args);

    _XmExtGetValuesHook((Widget)nsec, args, num_args);
}

static void
get_values_posthook(Widget new_w, ArgList args, Cardinal *num_args)
{
    XmWidgetExtData ext;

    _XmPopWidgetExtData(new_w, &ext, XmCACHE_EXTENSION);

    _XmExtObjFree((XtPointer)ext->widget);

    XtFree((char *)ext);
}


static void
expose(Widget w,
       XEvent *event,
       Region region)
{
#define superclass (&xmLabelGadgetClassRec)
    (*superclass->rect_class.expose) (w, event, region);
#undef superclass

    DEBUGOUT(XdbDebug(__FILE__, w, "expose() Armed(%d) type %s\n",
		      CBG_IsArmed(w), XdbMenuType2String(LabG_MenuType(w))));

    resize(w);

    if (CBG_IsArmed(w) || LabG_MenuType(w) == XmMENU_OPTION)
    {
	XmCascadeButtonGadgetHighlight(w, True);
    }
    else
    {
	XmCascadeButtonGadgetHighlight(w, False);
    }

    if (LabG_MenuType(w) == XmMENU_OPTION)
    {
	XmGadgetClass gwc = (XmGadgetClass)XtClass(w);

        if (G_Highlighted(w))
        {
            (*gwc->gadget_class.border_highlight) (w);
        }
        else
        {
            (*gwc->gadget_class.border_unhighlight) (w);
        }
    }
}

static void
border_unhighlight(Widget w)
{

    DEBUGOUT(XdbDebug(__FILE__, w, "gadget_border_unhighlight\n"));

    /* with zero width, we don't need this... */
    if (G_HighlightThickness(w) == 0)
    {
	return;
    }

    if (XmIsManager(XtParent(w)))
    {
	_XmDrawHighlight(XtDisplayOfObject(w), XtWindowOfObject(w),
			 XmParentBackgroundGC(w),
			 XtX(w), XtY(w), XtWidth(w), XtHeight(w),
			 G_HighlightThickness(w), LineSolid);
    }
    else
    {
	_XmClearBorder(XtDisplay(w),
		       XtWindow(w),
		       XtX(w), XtY(w),
		       XtWidth(w), XtHeight(w),
		       G_HighlightThickness(w));
    }

    G_Highlighted(w) = False;
    G_HighlightDrawn(w) = False;
}

static void
border_highlight(Widget w)
{

    DEBUGOUT(XdbDebug(__FILE__, w, "gadget_border_highlight\n"));

    /* with zero width, we don't need this... */
    if (G_HighlightThickness(w) == 0)
    {
	return;
    }

    _XmDrawHighlight(XtDisplayOfObject(w), XtWindowOfObject(w),
		     XmParentHighlightGC(w),
		     XtX(w), XtY(w), XtWidth(w), XtHeight(w),
		     G_HighlightThickness(w), LineSolid);

    G_Highlighted(w) = True;
    G_HighlightDrawn(w) = True;
}

static Cardinal
get_sec_res_data(WidgetClass wc, XmSecondaryResourceData **data)
{
    /* FIX ME */

    return _XmSecondaryResourceData(&_XmCascadeBGRectClassExtRec,
				    data, NULL, NULL, NULL, NULL);
}

static void
DoSelect(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Widget submenu;

    submenu = CBG_Submenu(w);

    DEBUGOUT(XdbDebug(__FILE__, w, "DoSelect()\n"));

    /* quene events until the next button event. */
    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    _XmRecordEvent(event);

    /* if we have a submenu attached, call the cascading callback, 
     * post the submenu and allow keyboard traversal of it. */
    if (submenu)
    {
	/* Turn on keyboard traversal */
	_XmSetInDragMode(w, False);

	DEBUGOUT(XdbDebug0(__FILE__, submenu, "RC_CascadeBtn(%s)-> %s\n",
			   XtName(submenu), XtName(w)));

	RC_CascadeBtn(submenu) = w;
	RC_PopupPosted(XtParent(w)) = submenu;

	DEBUGOUT(XdbDebug0(__FILE__, w, "RC_PopupPosted(%s) set to %s\n",
			   XtName(XtParent(w)),
			   submenu ? XtName(submenu) : "(null)"));
    }
    else
    {
        XtCallbackList cbl;
        Widget p;
 
        /* Actually all this no-menu thing is totally wrong.
         *
         * This code does call the callback, which makes apps work,
         * but it doesn't do arm/disarm, and also it still activates
         * if the user moves away from the menubar, and then releases
         * the mouse button.
         *
         * Hence something is really really wrong here.
         * Danny 13/3/1997
         * For the record, I also wrote this :-)
         *
         * FIX ME
         */
        /* Call ActivateCallback */
 
        cbl = XmIsGadget(w) ? CBG_ActivateCall(w) : CB_ActivateCall(w);
 
	/* this should take care of activation when the cursor is not
	   in the window any more. Find a way to test it and then
	   un-comment it. (taken from PushB)
	 */
	/*
	if (ev && (ev->type == KeyPress || ev->type == KeyRelease
		   || ((ev->x >= 0 && ev->x < XtWidth(w))
		       && (ev->y >= 0 && ev->y < XtHeight(w)))))
	*/
	{
	    if (cbl)
	    {
		XmAnyCallbackStruct cbs;
     
		cbs.reason = XmCR_ACTIVATE;
		cbs.event = event;
     
		XtCallCallbackList(w, cbl, &cbs);
	    }
        }
 
        /* Activate the cascade button */
        /* ??? FIX ME */
        /* Unpost menus */
        /* Only call CleanupMenuBar if we're in a menu pane */
        for (p = w; p && !XmIsMenuShell(p); p = XtParent(p))
        {
        }
 
        if (p)
        {
	    RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_BAR_CLEANUP,
						    XtParent(w), NULL);
        }
    }

    CBG_SetArmed(w, False);
}

static void
MenuBarSelect(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Widget menu, mb;
    Boolean poppedUp;

    DEBUGOUT(XdbDebug(__FILE__, w, "MenuBarSelect()\n"));

    /* first thing we do is free up the next button event. */
    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    _XmRecordEvent(event);

    mb = XtParent(w);
    menu = CBG_Submenu(w);

    if (!menu)
	return;

    RC_MenuShellPopdown(w, event, &poppedUp);
    /* Set drag mode */
    _XmSetInDragMode(w, True);

    CBG_SetArmed(w, True);

    /* highlight the cascade button. */
    XmCascadeButtonHighlight(w, True);

    if (!RC_IsArmed(mb))
    {
	RCClass_MenuProcs(XtClass(mb))(XmMENU_ARM, mb, NULL);
    }

    _XmCascadingPopup(w, event, True);
    MGR_ActiveChild(XtParent(w)) = w;
}

static void
ArmAndPost(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ArmAndPost(): %p %p\n", w, CBG_Submenu(w)));

    XmProcessTraversal(w, XmTRAVERSE_CURRENT);

    if (!RC_IsArmed(CBG_Submenu(w)))
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "MENU_ARM\n"));

	RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_ARM, XtParent(w), NULL);
    }

    _XmCascadingPopup(w, event, True);
}

static void
MenuBarEnter(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MenuBarEnter()\n"));

    /* if we're not dragging, do nothing. */
    if (!_XmGetInDragMode(w))
    {
	return;
    }

    /* if we're already armed, do nothing */
    if (CBG_IsArmed(w))
    {
	return;
    }

    MenuBarSelect(w, event, NULL, num_params);
}

static void
MenuBarLeave(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MenuBarLeave\n"));

    if (CBG_IsArmed(w) && RC_PopupPosted(XtParent(w)) != CBG_Submenu(w))
    {
        XmCascadeButtonGadgetHighlight(w, False);
    }
}
 
static void
StartDrag(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmCascadeButtonGadget cw = (XmCascadeButtonGadget)w;
    Boolean validButton;
    Boolean poppedUp;

    DEBUGOUT(XdbDebug(__FILE__, w, "StartDrag()\n"));

    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    _XmRecordEvent(event);

    /* If the submenu is already active, disable keyboard traversal
       and set it to mouse traversal */

    if (CBG_Submenu(cw) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "StartDrag: no CBG_Submenu\n"));
	return;
    }

    /* Is it even the right kind of event? */
    if (!event || event->type != ButtonPress)
    {
        return;
    }
 
    /* Was it the right button? */
    RC_MenuButton(w, event, &validButton);
 
    if (!validButton)
    {
        return;
    }

    RC_MenuShellPopdown(w, event, &poppedUp);
    _XmCascadingPopup(w, event, True);

    _XmSetInDragMode(w, True);
}

static void
CascadePopupHandler(XtPointer clientData, XtIntervalId *id)
{
    Widget w = (Widget)clientData;
    Boolean poppedUp;

    CBG_Timer(w) = 0;

    DEBUGOUT(XdbDebug(__FILE__, w, "CascadePopupHandler()\n"));

    RC_MenuShellPopdown(w, NULL, &poppedUp);
    _XmCascadingPopup(w, NULL, True);	/* FIX ME: NULL? */
}


static void
DelayedArm(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DelayedArm()\n"));

    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    if (_XmGetInDragMode(w))
    {
	CBG_Timer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
				       CBG_MapDelay(w),
				       CascadePopupHandler,
				       (XtPointer)w);

	CBG_SetArmed(w, True);

	XmCascadeButtonHighlight(w, True);

	MGR_SelectedGadget(XtParent(w)) = (XmGadget)w;
    }
}

static void
CheckDisarm(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    int x = ((XLeaveWindowEvent *)event)->x;
    int y = ((XLeaveWindowEvent *)event)->y;

    DEBUGOUT(XdbDebug(__FILE__, w, "CheckDisarm() - %i %i %i\n", x, y, XtHeight(w)));

    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    if (_XmGetInDragMode(w))
    {
	if (CBG_Timer(w))
	{
	    XtRemoveTimeOut(CBG_Timer(w));
	    CBG_Timer(w) = 0;
	    if (!RC_PopupPosted(XtParent(w)))
	    {
		    CBG_SetArmed(w, False);
		    XmCascadeButtonHighlight(w, False);
	    }
	}

	/*
	 * If a submenu is already popped up, pop it down unless
	 * we've left on the right edge.
	 * The test is a bit elaborate because the right edge is 
	 * not a straight line (at least, in M*tif: FIX ME).
	 */
	if ( _XmGetRC_PopupPosted(XtParent(w)) && 
			XtX(_XmGetRC_PopupPosted(XtParent(w))) <
			XtX(XtParent(XtParent(w))))
	{
		/* submenu was posted to the left of the cascade button */
		x += XtWidth(_XmGetRC_PopupPosted(XtParent(w)));
	}
	/* FIX ME: does this test really work in case the submenu is posted
	 * to the left? I feel in that case we should test for x >= XtX(w)
	 * because, in that case, the submenu should be unposted if we leave
	 * at the _right_ edge
	 */
	if ((x <= XtX(w) || y <= XtY(w) || y >= XtY(w) + XtHeight(w)) &&
	    RC_PopupPosted(XtParent(w)) != NULL)
	{
	    Boolean poppedUp;

	    RC_MenuShellPopdown(w, event, &poppedUp);
	    CBG_SetArmed(w, False);
	    XmCascadeButtonHighlight(w, False);

	    /* FIX ME: more (disarm, RC_CascadeBtn) */
	}
    }
}


static void
Help(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Widget cur = w;
    XmAnyCallbackStruct cbs;

    cbs.reason = XmCR_HELP;
    cbs.event = event;

    while (cur != NULL)
    {
	if (XtHasCallbacks(w, XmNhelpCallback) == XtCallbackHasSome)
	{
	    XtCallCallbacks(w, XmNhelpCallback, (XtPointer)&cbs);
	    return;
	}

	cur = XtParent(cur);
    }
}

static void
ArmAndActivate(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ArmAndActivate\n"));

    MGR_ActiveChild(XtParent(w)) = w;
    if (LabG_MenuType(w) == XmMENU_BAR)
    {
	_XmSetInDragMode(XtParent(w), False);
	MenuBarSelect(w, event, params, num_params);
	DoSelect(w, event, params, num_params);
	_XmMenuFocus(XtParent(w), XmMENU_FOCUS_SET, CurrentTime);
	_XmMenuArmItem(MGR_Children(CBG_Submenu(w))[0]);
	/* I would like to use this but currently only the Vendor focus data
	 * is used not the MenuShell focus data
	XmProcessTraversal(MGR_Children(CB_Submenu(w))[0], XmTRAVERSE_HOME);
	 */
    }
    else if (LabG_MenuType(w) == XmMENU_OPTION)
    {
	XmProcessTraversal(w, XmTRAVERSE_CURRENT);

	if (CBG_Submenu(w) && !RC_IsArmed(CBG_Submenu(w)))
	{
	    DEBUGOUT(XdbDebug(__FILE__, w, "MENU_ARM\n"));

	    RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_ARM,
						    XtParent(w), NULL);
	}

	CascadePopupHandler(w, NULL);
    }
    else /* pullright */
    {
	CascadePopupHandler(w, NULL);
    }
}

static void
input_dispatch(Widget gadget, XEvent *event, Mask event_mask)
{
    Cardinal num_params = 0;

    switch (event_mask)
    {
    case XmARM_EVENT:
	if (LabG_MenuType(gadget) == XmMENU_BAR)
	{
	    MenuBarSelect(gadget, event, NULL, &num_params);
	}
	else if (LabG_MenuType(gadget) == XmMENU_OPTION)
	{
	    ArmAndPost(gadget, event, NULL, &num_params);
	}
	else
	{
	    StartDrag(gadget, event, NULL, &num_params);
	}
	break;

    case XmACTIVATE_EVENT:
	DoSelect(gadget, event, NULL, &num_params);
	break;

    case XmENTER_EVENT:
	if ((LabG_MenuType(gadget) == XmMENU_PULLDOWN) ||
	    (LabG_MenuType(gadget) == XmMENU_POPUP))	/* rws 23 Mar 1997 */
	{
	    DelayedArm(gadget, event, NULL, &num_params);
	}
	else if (LabG_MenuType(gadget) == XmMENU_BAR)
	{
	    MenuBarEnter(gadget, event, NULL, &num_params);
	}
	break;

    case XmLEAVE_EVENT:
	if ((LabG_MenuType(gadget) == XmMENU_PULLDOWN) ||
	    (LabG_MenuType(gadget) == XmMENU_POPUP))	/* rws 23 Mar 1997 */
	{
	    CheckDisarm(gadget, event, NULL, &num_params);
	}
	else if (LabG_MenuType(gadget) == XmMENU_BAR)
	{
	    MenuBarLeave(gadget, event, NULL, &num_params);
	}
	break;

    case XmFOCUS_IN_EVENT:
	_XmFocusInGadget(gadget, event, NULL, &num_params);
	break;

    case XmFOCUS_OUT_EVENT:
	_XmFocusOutGadget(gadget, event, NULL, &num_params);
	break;

    case XmBDRAG_EVENT:	/* FIX ME: MLM - is this right? */
	_XmProcessDrag(gadget, event, NULL, NULL);
	break;

    case XmHELP_EVENT:
	Help(gadget, event, NULL, &num_params);
	break;
    }
}

static Boolean
visual_change(Widget w,
	      Widget parent,
	      Widget n)
{
    _XmWarning(w,"%s(%d) - visual_change not written\n", __FILE__, __LINE__);
    return False;
}

#ifdef __STDC__
static void
MenuProcEntry(int proc, Widget w,...)
{
    va_list arg_list;
    XEvent *event;
    extern void _XmTearOffArm();
 
    va_start(arg_list, w);
#else
MenuProcEntry(proc, w, va_alist)
     int proc;
     Widget w;
     va_dcl
{
    va_list arglist;
    XEvent *event;
    extern void _XmTearOffArm();
 
    va_start(arglist);
#endif

    switch (proc)
    {
    case XmMENU_ARM:
    	{
	XtExposeProc exp = XtClass(w)->core_class.expose;

	    CBG_Armed(w) = True;
	    (exp) (w, event, (Region)NULL);
    	}
    	break;
    case XmMENU_DISARM:
    	{
	XtExposeProc exp = XtClass(w)->core_class.expose;

	    CBG_Armed(w) = False;
	    (exp) (w, event, (Region)NULL);
    	}
    	break;
    default:
	_XmWarning(w, "%s(%d) - Invalid menuProc function", __FILE__, __LINE__);
	break;
    }

    va_end(arg_list);
}

void
XmCascadeButtonGadgetHighlight(Widget cb, Boolean highlight)
{
    DEBUGOUT(XdbDebug(__FILE__, cb,
		      "XmCascadeButtonGadgetHighlight(hl %d, armed %d,"
		      " apm 0x%X, cpm 0x%X), %s, geo %d %d %dx%d\n",
		      highlight,
		      XmIsPrimitive(cb) ? CB_IsArmed(cb) : CBG_IsArmed(cb),
		  XmIsPrimitive(cb) ? CB_ArmedPixmap(cb) : CBG_ArmedPixmap(cb),
	      XmIsPrimitive(cb) ? CB_CascadePixmap(cb) : CBG_CascadePixmap(cb),
		      XdbMenuType2String(LabG_MenuType(cb)),
		      XtX(cb), XtY(cb), XtWidth(cb), XtHeight(cb)
	     ));

    if (!XtIsRealized(cb))
    {
	return;
    }

    if (XmIsPrimitive(cb))
    {
	XmCascadeButtonHighlight(cb, highlight);

	return;
    }
    else if (!XmIsGadget(cb))
    {
	_XmError(cb,
	     "XmCascadeButtonHighlight called with non-cascade button widget");

	return;
    }

#ifdef	DO_SANITY
    if (CBG_CascadePixmap(cb) == 0)
    {
	DEBUGOUT(XdbDebug(__FILE__, cb, "CascadePixmap has NULL value\n"));
    }

    if (CBG_ArmedPixmap(cb) == 0)
    {
	DEBUGOUT(XdbDebug(__FILE__, cb, "ArmedPixmap has NULL value\n"));
    }
#endif /* SANITY */

    if (LabG_MenuType(cb) == XmMENU_OPTION)
    {
	/* Still figuring out what to do here */
	_XmDrawShadows(XtDisplayOfObject(cb),
		       XtWindowOfObject(cb),
		       XmParentTopShadowGC(cb),
		       XmParentBottomShadowGC(cb),
		       XtX(cb) + LabG_Highlight(cb),
		       XtY(cb) + LabG_Highlight(cb),
		       XtWidth(cb) - 2 * LabG_Highlight(cb),
		       XtHeight(cb) - 2 * LabG_Highlight(cb),
		       LabG_Shadow(cb),
		       (int)XmSHADOW_OUT);
    }
    else
    {
	_XmDrawShadows(XtDisplayOfObject(cb),
		       XtWindowOfObject(cb),
		       XmParentTopShadowGC(cb),
		       XmParentBottomShadowGC(cb),
		       XtX(cb) + LabG_Highlight(cb),
		       XtY(cb) + LabG_Highlight(cb),
		       XtWidth(cb) - 2 * LabG_Highlight(cb),
		       XtHeight(cb) - 2 * LabG_Highlight(cb),
		       LabG_Shadow(cb),
		       highlight ? (int)XmSHADOW_OUT : (int)XmNO_LINE);
    }

    /* now draw the pixmap */
    switch (LabG_MenuType(cb))
    {
    case XmMENU_POPUP:
    case XmMENU_PULLDOWN:
    case XmMENU_OPTION:
	/* Added - LabG_Shadow(cb) to X, untested... */
	if (CBG_IsArmed(cb))
	{
	    if (CBG_ArmedPixmap(cb) != XmUNSPECIFIED_PIXMAP &&
		CBG_ArmedPixmap(cb) != 0)
	    {
		DEBUGOUT(XdbDebug(__FILE__, cb,
				  "XCopyArea 0x%X -> 0x%X geo %d %d %dx%d\n",
				  CBG_ArmedPixmap(cb), XtWindow(XtParent(cb)),
				  CBG_Cascade_width(cb), CBG_Cascade_height(cb),
				  XtX(cb) + CBG_Cascade_x(cb),
				  XtY(cb) + CBG_Cascade_y(cb)));

		XCopyArea(XtDisplay(cb), CBG_ArmedPixmap(cb),
			  XtWindow(cb),
			  LabG_NormalGC(cb),
			  0, 0,
			  CBG_Cascade_width(cb), CBG_Cascade_height(cb),
			  XtX(cb) + CBG_Cascade_x(cb),
			  XtY(cb) + CBG_Cascade_y(cb));
	    }
	}
	else if (CBG_CascadePixmap(cb) != XmUNSPECIFIED_PIXMAP &&
		 CBG_CascadePixmap(cb) != 0)
	{
	    DEBUGOUT(XdbDebug(__FILE__, cb,
			      "XCopyArea 0x%X -> 0x%X geo %d %d %dx%d\n",
			      CBG_CascadePixmap(cb), XtWindow(XtParent(cb)),
			      CBG_Cascade_width(cb), CBG_Cascade_height(cb),
			      XtX(cb) + CBG_Cascade_x(cb),
			      XtY(cb) + CBG_Cascade_y(cb)));

	    XCopyArea(XtDisplay(cb), CBG_CascadePixmap(cb),
		      XtWindow(XtParent(cb)),
		      LabG_NormalGC(cb),
		      0, 0,
		      CBG_Cascade_width(cb), CBG_Cascade_height(cb),
		      XtX(cb) + CBG_Cascade_x(cb),
		      XtY(cb) + CBG_Cascade_y(cb));
	}
	break;

	/* Add other types of menu here... */
    }
}

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