/**
 *
 * $Id: CascadeB.c,v 1.148 1999/11/19 00:58:34 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: CascadeB.c,v 1.148 1999/11/19 00:58:34 rwscott Exp $";

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

#include <Xm/XmP.h>
#include <Xm/CascadeBP.h>
#include <Xm/CascadeBGP.h>
#include <Xm/RowColumnP.h>
#include <Xm/TearOffP.h>
#include <Xm/TransltnsP.h>
#include <Xm/MenuShellP.h>
#include <Xm/MenuUtilP.h>
#include <Xm/ScreenP.h>

#if STDC_HEADERS
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include <XmI/DebugUtil.h>

#include <stdio.h>

#ifndef XmUNSPECIFIED
#define XmUNSPECIFIED	(~0)
#endif

#define CASCADE_SPACING	4

/* Forward Declarations */

static void class_initialize(void);

static void class_part_initialize(WidgetClass w_class);

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

static void initialize_prehook(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 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 CleanupMenuBar(Widget w, XEvent *event,
			   String *params, Cardinal *num_params);

static void StartDrag(Widget, XEvent *, String *, Cardinal *);

static void KeySelect(Widget, XEvent *, String *, Cardinal *);

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

static void _XmCBMenuBarSelect(Widget, XEvent *, String *, Cardinal *);

static void _XmCBMenuBarDoSelect(Widget, XEvent *, String *, Cardinal *);

static void MenuBarEnter(Widget, XEvent *, String *, Cardinal *);

static void MenuBarLeave(Widget, XEvent *, String *, Cardinal *);

static void DelayedArm(Widget, XEvent *, String *, Cardinal *);

static void CheckDisarm(Widget, XEvent *, String *, Cardinal *);

#if 0
static void ArmAndPost(Widget, XEvent *, String *, Cardinal *);
#endif

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

/*
 * Resources for the cascade button class
 */
#define Offset(field) XtOffsetOf(XmCascadeButtonRec, cascade_button.field)
#define POffset(field) XtOffsetOf(XmCascadeButtonRec, primitive.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
    },
    {
	XmNcascadePixmap, XmCPixmap, XmRPrimForegroundPixmap,
	sizeof(Pixmap), Offset(cascade_pixmap),
	XmRImmediate, (XtPointer)XmUNSPECIFIED_PIXMAP
    },
    {
	XmNmappingDelay, XmCMappingDelay, XmRInt,
	sizeof(int), Offset(map_delay),
	XmRImmediate, (XtPointer)180
    },
    /* resources we override from XmLabel/XmPrimitive */
    {
	XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
	sizeof(Dimension), POffset(shadow_thickness),
	XmRImmediate, (XtPointer)2
    },
    {
	XmNtraversalOn, XmCTraversalOn, XmRBoolean,
	sizeof(Boolean), POffset(traversal_on),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension,
	sizeof(Dimension), POffset(highlight_thickness),
	XmRImmediate, (XtPointer)2
    },
    {
	XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
	sizeof(Dimension), XtOffsetOf(XmCascadeButtonRec, label.margin_width),
	XmRImmediate, (XtPointer)XmINVALID_DIMENSION
    }
};

static XtActionsRec actions[] =
{
    {"DelayedArm", DelayedArm},
    {"CheckDisarm", CheckDisarm},
    {"StartDrag", StartDrag},
    {"DoSelect", _XmCBMenuBarDoSelect},
    {"KeySelect", KeySelect},
    {"MenuBarSelect", _XmCBMenuBarSelect},
    {"MenuBarEnter", MenuBarEnter},
    {"MenuBarLeave", MenuBarLeave},
    {"CleanupMenuBar", CleanupMenuBar},
    {"Help", _XmCBHelp},
};

/* *INDENT-OFF* */
static XmBaseClassExtRec _XmCascadeBCoreClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,
    /* version                   */ XmBaseClassExtVersion,
    /* size                      */ sizeof(XmBaseClassExtRec),
    /* initialize_prehook        */ initialize_prehook,
    /* set_values_prehook        */ XmInheritSetValuesPrehook,
    /* initialize_posthook       */ initialize_posthook,
    /* set_values_posthook       */ XmInheritSetValuesPosthook,
    /* secondary_object_class    */ XmInheritClass,
    /* secondary_object_create   */ XmInheritSecObjectCreate,
    /* get_secondary_resources   */ XmInheritGetSecResData,
    /* fast_subclass             */ { 0 },
    /* get_values_prehook        */ XmInheritGetValuesPrehook,
    /* get_values_posthook       */ XmInheritGetValuesPosthook,
    /* 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
};

XmPrimitiveClassExtRec _XmCascadeBPrimClassExtRec = {
    /* next_extension      */ NULL,
    /* record_type         */ NULLQUARK,
    /* version             */ XmPrimitiveClassExtVersion,
    /* record_size         */ sizeof(XmPrimitiveClassExtRec),
    /* widget_baseline     */ XmInheritBaselineProc,
    /* widget_display_rect */ XmInheritDisplayRectProc,
    /* widget_margins      */ NULL
};

XmCascadeButtonClassRec xmCascadeButtonClassRec = {
    /* Core class part */
    {
	/* superclass            */ (WidgetClass) &xmLabelClassRec,
	/* class_name            */ "XmCascadeButton",
	/* widget_size           */ sizeof(XmCascadeButtonRec),
	/* class_initialize      */ class_initialize,
	/* class_part_initialize */ class_part_initialize,
	/* class_inited          */ False,
	/* initialize            */ initialize,
	/* initialize_hook       */ NULL,
	/* realize               */ XtInheritRealize,
	/* actions               */ actions,
	/* num_actions           */ XtNumber(actions),
	/* 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               */ XtVersionDontCheck,
	/* callback offsets      */ NULL,
	/* tm_table              */ NULL,
	/* query_geometry        */ XtInheritQueryGeometry,
	/* display_accelerator   */ NULL,
	/* extension             */ (XtPointer)&_XmCascadeBCoreClassExtRec
    },
    /* Primitive Class part */
    {
	/* border_highlight      */ XmInheritBorderHighlight, /* FIX ME */
	/* border_unhighlight    */ XmInheritBorderUnhighlight, /* FIX ME */
	/* translations          */ XtInheritTranslations,
	/* arm_and_activate_proc */ ArmAndActivate,
	/* synthetic resources   */ NULL,
	/* num syn res           */ 0,
	/* extension             */ (XtPointer)&_XmCascadeBPrimClassExtRec,
    },
    /* Label Class part */
    {
	/* setOverrideCallback */ XmInheritSetOverrideCallback,
	/* menuProcs           */ XmInheritMenuProc,
	/* translations        */ XtInheritTranslations,
	/* extension           */ NULL
    },
    /* CascadeButton Class part */
    {
	/* extension */ NULL
    }
};
/* *INDENT-ON* */


WidgetClass xmCascadeButtonWidgetClass = (WidgetClass)&xmCascadeButtonClassRec;

static XtTranslations menub_trans;
static XtTranslations popup_trans;

static void
class_initialize(void)
{
    _XmCascadeBCoreClassExtRec.record_type = XmQmotif;

    menub_trans = XtParseTranslationTable(_XmCascadeB_menubar_events);

    popup_trans = XtParseTranslationTable(_XmCascadeB_p_events);
}

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

static void
initialize_prehook(Widget request, Widget new_w,
		   ArgList args, Cardinal *num_args)
{
    DEBUGOUT(XdbDebug(__FILE__, new_w, "%s:initialize_prehook()\n", __FILE__));
    DEBUGOUT(XdbDebug("MENUT", new_w, "%s:initialize_prehook(%d)\n", __FILE__, __LINE__));

    _XmSaveCoreClassTranslations(new_w);

    if (XmIsRowColumn(XtParent(new_w)) &&
	RC_Type(XtParent(new_w)) == XmMENU_BAR)
    {
	DEBUGOUT(XdbDebug(__FILE__, new_w, "    MenuBar translations\n"));
	DEBUGOUT(XdbDebug0("MENUT", new_w, "    MenuBar translations\n"));

	CoreClassTranslations(new_w) = (String)menub_trans;
    }
    else if (XmIsRowColumn(XtParent(new_w)) &&
	     (RC_Type(XtParent(new_w)) == XmMENU_POPUP ||
	      RC_Type(XtParent(new_w)) == XmMENU_PULLDOWN))
    {
	DEBUGOUT(XdbDebug(__FILE__, new_w, "    Popup translations\n"));
	DEBUGOUT(XdbDebug0("MENUT", new_w, "    Popup translations\n"));

	CoreClassTranslations(new_w) = (String)popup_trans;
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, new_w, "    Default translations\n"));
	DEBUGOUT(XdbDebug0("MENUT", new_w, "    Default translations\n"));
    }

}

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

static void
place_cascade(Widget w)
{
    Position x;

    CB_Cascade_x(w) = Lab_Shadow(w) + Lab_MarginWidth(w);
    if (Lab_StringDirection(w) == XmSTRING_DIRECTION_L_TO_R)
	CB_Cascade_x(w) = XtWidth(w) - CB_Cascade_x(w) - CB_Cascade_width(w);

    CB_Cascade_y(w) = (XtHeight(w) - CB_Cascade_height(w)) / 2;

    /* Make sure the label and cascade don't overlap */

    if (Lab_StringDirection(w) == XmSTRING_DIRECTION_L_TO_R)
    {
	x = CB_Cascade_x(w) - CASCADE_SPACING - Lab_TextRect_width(w);
	if (Lab_TextRect_x(w) > x)
	    Lab_TextRect_x(w) = x;
    }
    else
    {
	x = CB_Cascade_x(w) + CB_Cascade_width(w) + CASCADE_SPACING;
	if (Lab_TextRect_x(w) < x)
	    Lab_TextRect_x(w) = x;
    }
}

static void
size_cascade(Widget w)
{
    int dummy;
    unsigned width, height;

    if (CB_CascadePixmap(w) > XmUNSPECIFIED_PIXMAP)
    {
	XGetGeometry(XtDisplay(w), CB_CascadePixmap(w),
		     (Window *)&dummy, &dummy, &dummy,
		     &width, &height, (unsigned *)&dummy, (unsigned *)&dummy);
	CB_Cascade_width(w) = width;
	CB_Cascade_height(w) = height;
    }
    else
    {
	CB_Cascade_width(w) = CB_Cascade_height(w) = 0;
    }
}

static void
initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args)
{
    DEBUGOUT(XdbDebug(__FILE__, new_w, "%s:initialize()\n", __FILE__));
    DEBUGOUT(XdbDebug("MENUT", new_w, "%s:initialize(%d)\n", __FILE__, __LINE__));

    CB_SetArmed(new_w, False);
    CB_ArmedPixmap(new_w) = None;

    if (CB_MapDelay(new_w) < 0)
    {
	_XmWarning(new_w, "MappingDelay must be non-negative.");
	CB_MapDelay(new_w) = 180;
    }
    if (CB_Submenu(new_w) && (!XmIsRowColumn(CB_Submenu(new_w)) ||
			      (RC_Type(CB_Submenu(new_w)) != XmMENU_PULLDOWN)))
    {
	_XmWarning(new_w, "Submenu must a pull-down menu.");
	CB_Submenu(new_w) = NULL;
    }

    CB_Cascade_width(new_w) = 0;
    CB_Cascade_height(new_w) = 0;
    CB_Cascade_x(new_w) = 0;
    CB_Cascade_y(new_w) = 0;

    if (Lab_MenuType(new_w) != XmMENU_BAR &&
	Lab_MenuType(new_w) != XmMENU_POPUP &&
	Lab_MenuType(new_w) != XmMENU_PULLDOWN)
    {
	_XmError(new_w, "Cascade parent is incorrect type.");
    }

    Prim_TraversalOn(new_w) = True;
    Lab_Highlight(new_w) = 0;

    if (Lab_MarginWidth(new_w) == (Dimension)XmINVALID_DIMENSION)
	Lab_MarginWidth(new_w) = Lab_MenuType(new_w) == XmMENU_BAR ? 6 : 2;

    if (Lab_MenuType(new_w) != XmMENU_BAR)
    {
	int margin;

	if (CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP &&
	    CB_Submenu(new_w))
	    _XmCreateArrowPixmaps(new_w);
	size_cascade(new_w);

	/* Make sure there's enough room on the side for the cascade pixmap */
	margin = (CB_Cascade_width(new_w) + CASCADE_SPACING)
	    - (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R
	       ? Lab_MarginRight(new_w) : Lab_MarginLeft(new_w));
	if (margin > 0)
	{
	    if (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R)
		Lab_MarginRight(new_w) += margin;
	    else
	    {
		Lab_MarginLeft(new_w) += margin;
		Lab_TextRect_x(new_w) += margin;
	    }
	    if (!XtWidth(request))
		XtWidth(new_w) += margin;
	}

	/* Make sure there's enough room vertically */
	margin = CB_Cascade_height(new_w) - (Lab_TextRect_height(new_w)
					     + Lab_MarginTop(new_w)
					     + Lab_MarginBottom(new_w));
	if (margin > 0)
	{
	    Lab_MarginTop(new_w) += margin >> 1;
	    Lab_MarginBottom(new_w) += (margin + 1) >> 1;
	    if (!XtHeight(request))
	    {
		Lab_TextRect_y(new_w) += margin >> 1;
		XtHeight(new_w) += margin;
	    }
	}

	place_cascade(new_w);
    }

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

    CB_Timer(new_w) = 0;
    LabClass_MenuProcs(XtClass(new_w)) = MenuProcEntry;
}

static void
resize(Widget w)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "%s:resize(%d) - %dx%d\n",
			__FILE__, __LINE__,
			XtWidth(w), XtHeight(w)));

#define superclass (&xmLabelClassRec)
    (*superclass->core_class.resize) (w);
#undef superclass

    if (Lab_MenuType(w) != XmMENU_BAR)
    {
	place_cascade(w);
    }
}

static void
destroy(Widget w)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "%s:destroy()\n", __FILE__));

    if (CB_Timer(w) != 0)
    {
	XtRemoveTimeOut(CB_Timer(w));
    }

    if (CB_ArmedPixmap(w))
    {
	_XmArrowPixmapCacheDelete((XtPointer)CB_ArmedPixmap(w));
	_XmArrowPixmapCacheDelete((XtPointer)CB_CascadePixmap(w));
    }
}

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,
		      "%s:set_values() - %i args\n"
		      "\t    old X %5i Y %5i W %5i H %5i\n"
		      "\trequest X %5i Y %5i W %5i H %5i\n"
		      "\t  new_w X %5i Y %5i W %5i H %5i\n",
		      __FILE__,
		      *num_args,
		      XtX(old), XtY(old),
		      XtWidth(old), XtHeight(old),
		      XtX(request), XtY(request),
		      XtWidth(request), XtHeight(request),
		      XtX(new_w), XtY(new_w),
		      XtWidth(new_w), XtHeight(new_w)));
    DEBUGOUT(XdbPrintArgList(__FILE__, new_w, args, *num_args, False));

    if (CB_MapDelay(new_w) < 0)
    {
	_XmWarning(new_w, "MappingDelay must be non-negative.");
	CB_MapDelay(new_w) = CB_MapDelay(old);
    }
    if (CB_Submenu(new_w) && (!XmIsRowColumn(CB_Submenu(new_w)) ||
			      (RC_Type(CB_Submenu(new_w)) != XmMENU_PULLDOWN)))
    {
	_XmWarning(new_w, "Submenu must a pull-down menu.");
	CB_Submenu(new_w) = CB_Submenu(old);
    }

    Prim_TraversalOn(new_w) = True;
    Lab_Highlight(new_w) = 0;

    if (CB_CascadePixmap(old) != CB_CascadePixmap(new_w) ||
	(CB_Submenu(new_w)
	 ? CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP
	 : CB_ArmedPixmap(new_w)) ||
	(CB_ArmedPixmap(new_w) && Lab_Font(old) != Lab_Font(new_w)))
    {
	if (CB_ArmedPixmap(old))
	{
	    _XmArrowPixmapCacheDelete((XtPointer)CB_CascadePixmap(old));
	    _XmArrowPixmapCacheDelete((XtPointer)CB_ArmedPixmap(old));
	    if (CB_CascadePixmap(old) == CB_CascadePixmap(new_w))
		CB_CascadePixmap(new_w) = XmUNSPECIFIED_PIXMAP;
	    CB_ArmedPixmap(new_w) = None;
	}
	if (Lab_MenuType(new_w) != XmMENU_BAR)
	{
	    if (CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP &&
		CB_Submenu(new_w))
		_XmCreateArrowPixmaps(new_w);
	    size_cascade(new_w);
	}

	refresh_needed = True;
    }

    if (XtSensitive(new_w) != XtSensitive(old))
    {
	DEBUGOUT(XdbDebug(__FILE__, new_w,
			  "SetValues: sensitive changes to %d\n",
			  XtSensitive(new_w)));

	refresh_needed = True;
    }

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

    /* Adjust margins for the cascade pixmap size if necessary.
     * Margins be increased or decreased; if the margin was explicitly set
     * in this call, don't decrease it past that (though it can get bigger).
     */
    if (Lab_MenuType(new_w) != XmMENU_BAR &&
	(CB_Cascade_width(new_w) != CB_Cascade_width(old)
	 || CB_Cascade_height(new_w) != CB_Cascade_height(old)
	 || Lab_StringDirection(new_w) != Lab_StringDirection(old)
	 || (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R
	     ? Lab_MarginRight(new_w) != Lab_MarginRight(old)
	     : Lab_MarginLeft(new_w) != Lab_MarginLeft(old))
	 || Lab_MarginTop(new_w) != Lab_MarginTop(old)
	 || Lab_MarginBottom(new_w) != Lab_MarginBottom(old)))
    {
	int margin, tm, bm;

	margin = CB_Cascade_width(new_w) + CASCADE_SPACING
	    - (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R
	       ? Lab_MarginRight(new_w) : Lab_MarginLeft(new_w));
	if (margin && (margin > 0 ||
		       (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R
			? Lab_MarginRight(new_w) == Lab_MarginRight(old)
			: Lab_MarginLeft(new_w) == Lab_MarginLeft(old))))
	{
	    if (Lab_StringDirection(new_w) == XmSTRING_DIRECTION_L_TO_R)
		Lab_MarginRight(new_w) += margin;
	    else
	    {
		Lab_MarginLeft(new_w) += margin;
		Lab_TextRect_x(new_w) += margin;
	    }
	    if (Lab_RecomputeSize(new_w) || !XtWidth(request))
		XtWidth(new_w) += margin;
	}

	margin = CB_Cascade_height(new_w) - (Lab_TextRect_height(new_w)
					     + Lab_MarginTop(new_w)
					     + Lab_MarginBottom(new_w));
	if (margin)
	{
	    tm = margin / 2;
	    if (tm < (Lab_MarginTop(new_w) == Lab_MarginTop(old)
		      ? XmDEFAULT_TOP_MARGIN - (int)Lab_MarginTop(new_w)
		      : 0))
		tm = (Lab_MarginTop(new_w) == Lab_MarginTop(old)
		      ? XmDEFAULT_TOP_MARGIN - (int)Lab_MarginTop(new_w)
		      : 0);
	    Lab_MarginTop(new_w) += tm;
	    if (Lab_RecomputeSize(new_w) || !XtHeight(request))
		XtHeight(new_w) += tm;

	    bm = (margin + 1) / 2;
	    if (bm < (Lab_MarginBottom(new_w) == Lab_MarginBottom(old)
		      ? XmDEFAULT_BOTTOM_MARGIN - (int)Lab_MarginBottom(new_w)
		      : 0))
		bm = (Lab_MarginBottom(new_w) == Lab_MarginBottom(old)
		      ? XmDEFAULT_BOTTOM_MARGIN - (int)Lab_MarginBottom(new_w)
		      : 0);
	    Lab_MarginBottom(new_w) += bm;
	    if (Lab_RecomputeSize(new_w) || !XtHeight(request))
		XtHeight(new_w) += bm;

	    if (tm != bm)
		Lab_TextRect_y(new_w) += (tm - bm) / 2;
	}

	refresh_needed = True;
    }

    if (Lab_MenuType(new_w) != XmMENU_BAR &&
	(CB_Cascade_width(new_w) != CB_Cascade_width(old)
	 || CB_Cascade_height(new_w) != CB_Cascade_height(old)
	 || Lab_Shadow(new_w) != Lab_Shadow(old)
	 || Lab_MarginWidth(new_w) != Lab_MarginWidth(old)
	 || Lab_TextRect_y(new_w) != Lab_TextRect_y(old)
	 || Lab_TextRect_height(new_w) != Lab_TextRect_height(old)
	 || Lab_StringDirection(new_w) != Lab_StringDirection(old)))
    {
	Dimension width, height;

	width = XtWidth(new_w);
	height = XtHeight(new_w);
	XtWidth(new_w) = XtWidth(old);
	XtHeight(new_w) = XtHeight(old);
	place_cascade(new_w);
	XtWidth(new_w) = width;
	XtHeight(new_w) = height;
	refresh_needed = True;
    }

    return refresh_needed;
}

static void
draw_cascade(Widget w)
{
    Pixmap pm;

    if (CB_IsArmed(w))
    {
	_XmDrawShadows(XtDisplay(w), XtWindow(w),
		       Prim_TopShadowGC(w), Prim_BottomShadowGC(w),
		       0, 0, XtWidth(w), XtHeight(w),
		       Lab_Shadow(w), XmSHADOW_OUT);
    }

    pm = CB_IsArmed(w) && CB_ArmedPixmap(w) > XmUNSPECIFIED_PIXMAP
	? CB_ArmedPixmap(w)
	: CB_CascadePixmap(w);
    if (pm > XmUNSPECIFIED_PIXMAP)
    {
	XCopyArea(XtDisplay(w), pm, XtWindow(w),
		  Lab_NormalGC(w),
		  0, 0,
		  CB_Cascade_width(w), CB_Cascade_height(w),
		  CB_Cascade_x(w), CB_Cascade_y(w));
    }
}

static void
expose(Widget w, XEvent *event, Region region)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "%s:expose() Armed(%d) type %s\n",
		      __FILE__,
		      CB_IsArmed(w), XdbMenuType2String(Lab_MenuType(w))));

    /* The label's expose routine should be called after the cascade button's
     * expose routine. */

    draw_cascade(w);

#define superclass (&xmLabelClassRec)
    (*superclass->core_class.expose) (w, event, region);
#undef superclass
}

/* ACTION PROCS */
/*
 * CleanupMenuBar
 *    w       is a cascadeButton
 */
static void
CleanupMenuBar(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "CleanupMenuBar()\n"));

    if (w)			/* Try not to crash */
    {
	RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_BAR_CLEANUP,
						XtParent(w), NULL);
    }
}

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

    /* If we're in keyboard traversal mode, and if we have a submenu
       attached, call the cascading callback, post the submenu and
       allow keyboard traversal of it. */

    /* else call the activate callback, and unpost all active menus
       in the cascade */
}

void
_XmCascadingPopup(Widget w, XEvent *event, Boolean doCascade)
{
    /* FIX ME: doCascade? */
    XmAnyCallbackStruct cbs;

#if 1
    /* rws 29 Sep 1999
       I really don't like this here, but rowcolumn/test51 shows that it 
       must be before the CascadingCallback. So I can't roll it into 
       MenuCascading.
     */
    {
    Widget shell;
    Widget sub_pane;
    Boolean was_torn;

	sub_pane = XmIsGadget(w) ?  CBG_Submenu(w) : CB_Submenu(w);
	if (sub_pane)
	{
	    RCClass_MenuProcs(XtClass(sub_pane))(XmMENU_RESTORE_TEAROFF_TO_MENUSHELL,
				     sub_pane, &shell, &was_torn, event);
	}
    }
#endif
    cbs.reason = XmCR_CASCADING;
    cbs.event = event;

    XFlush(XtDisplay(w));

    XtCallCallbackList(w,
		       (XmIsGadget(w)
			? CBG_CascadeCall(w)
			: CB_CascadeCall(w)),
		       &cbs);

    if (doCascade)
    {
	RC_MenuCascading(w, event);
    }
}

/*
 *    _XmCBMenuBarDoSelect()
 *
 *      Calls the callbacks in XmNcascadingCallback, posts the submenu attached
 *      to the CascadeButton and enables keyboard traversal within the menu.
 *      If the CascadeButton does not have a submenu attached, this action
 *      calls the callbacks in XmNactivateCallback, activates the
 *      CascadeButton, and unposts all posted menus in the cascade.
 */
void
_XmCBMenuBarDoSelect(Widget w, XEvent *event,
		     String *params, Cardinal *num_params)
{
Boolean validButton;

    DEBUGOUT(XdbDebug(__FILE__, w, "%s:_XmCBMenuBarDoSelect(%d)\n\t%s posted mine %s\n",
	__FILE__, __LINE__,
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "none"
	));
    DEBUGOUT(XdbDebug("MENU", w, "%s:_XmCBMenuBarDoSelect(%d)\n\t%s posted mine %s %s\n",
	__FILE__, __LINE__,
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "none",
	event ? XdbEventType2String(event->xany.type) : "no-event"
	));
    if (event && event->xany.type == ButtonRelease)
    {
	RC_MenuButton(w, event, &validButton);
    }
    else
    {
	validButton = True;
    }
    if (validButton)
    {
	XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);
	_XmMenuFocus(w, XmMENU_FOCUS_SET, CurrentTime);
	if (CB_Submenu(w))
	{
	    if (RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
	    {
		_XmCascadingPopup(w, event, True);
	    }

	    RCClass_MenuTraverse(CB_Submenu(w), XmTRAVERSE_HOME);

	    _XmSetInDragMode(w, False);
	}
	else
	{
	XmAnyCallbackStruct cbs;

	    cbs.reason = XmCR_ACTIVATE;
	    cbs.event = event;
	    if (XmIsRowColumn(XtParent(w)))
	    {
		RC_MenuMenuCallback(w, &cbs);
	    }
	    if (!Lab_SkipCallback(w) && CB_ActivateCall(w))
	    {
		XFlush(XtDisplay(w));

		XtCallCallbackList(w, CB_ActivateCall(w), &cbs);
	    }
	    XmCascadeButtonHighlight(w, False);
	    if (RC_PopupPosted(XtParent(w)))
	    {
	    Boolean poppedUp;

		RC_MenuShellPopdown(w, event, &poppedUp);
	    }
	}
    }
}

void
_XmCBMenuBarSelect(Widget w, XEvent *event,
		   String *params, Cardinal *num_params)
{
    Boolean validButton;

    DEBUGOUT(XdbDebug(__FILE__, w, "%s:_XmCBMenuBarSelect(%d)\n\t%s posted mine %s\n",
	__FILE__, __LINE__,
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "none"
	));
    DEBUGOUT(XdbDebug("MENU", w, "%s:_XmCBMenuBarSelect(%d)\n\t%s posted mine %s\n",
	__FILE__, __LINE__,
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "none"
	));
    if (event && event->xany.type == ButtonPress)
    {
	RC_MenuButton(w, event, &validButton);
    }
    else
    {
	validButton = True;
    }
    if (validButton)
    {
	XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);
	XmProcessTraversal(w, XmTRAVERSE_CURRENT);
	if (CB_Submenu(w))
	{
	int i;
	Widget tmp = RC_PopupPosted(XtParent(w));

	    if (RC_PopupPosted(XtParent(w)))
	    {
		if (RC_PopupPosted(XtParent(w)) && RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		{
		    _XmCascadingPopup(w, event, False);
		}
		if (RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		{
		Boolean poppedUp;

		    RC_MenuShellPopdown(w, event, &poppedUp);
		}
		else if (CB_Submenu(w) && RC_PopupPosted(CB_Submenu(w)))
		{
		Boolean poppedUp;

		    for (i = 0; i < MGR_NumChildren(RC_PopupPosted(CB_Submenu(w))); i++)
		    {
		    Widget w1 = MGR_Children(RC_PopupPosted(CB_Submenu(w)))[i];

			_XmMenuDisarmItem(w1);
		    }
		    RC_MenuShellPopdown(RC_CascadeBtn(RC_PopupPosted(CB_Submenu(w))), event, &poppedUp);
		}
		if (RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		{
		    RC_MenuCascading(w, event);
		}
		if (tmp)
		{
		    for (i = 0; i < MGR_NumChildren(tmp); i++)
		    {
		    Widget w1 = MGR_Children(tmp)[i];

			_XmMenuDisarmItem(w1);
		    }
		}
	    }
	    else
	    {
		if (RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		{
		    _XmCascadingPopup(w, event, True);
		}
		if (tmp)
		{
		    for (i = 0; i < MGR_NumChildren(tmp); i++)
		    {
		    Widget w1 = MGR_Children(tmp)[i];

			_XmMenuDisarmItem(w1);
		    }
		}
	    }

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

	}
	else
	{
	int i;
	Boolean poppedUp;

	    _XmCascadingPopup(w, event, False);
	    if (RC_PopupPosted(XtParent(w)))
	    {
		for (i = 0; i < MGR_NumChildren(RC_PopupPosted(XtParent(w))); i++)
		{
		Widget w1 = MGR_Children(RC_PopupPosted(XtParent(w)))[i];

		    _XmMenuDisarmItem(w1);
		}
	    }
	    RC_MenuShellPopdown(w, event, &poppedUp);
	}
	XmCascadeButtonHighlight(w, True);
	_XmSetInDragMode(w, True);
	RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_ARM, XtParent(w), NULL);
    }
}

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

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

static void
MenuBarEnter(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MenuBarEnter\n\t%s %s %s %s posted posting %s\n",
	_XmGetInDragMode(w) ? "Dragging" : "Not Dragging",
	XdbRcType2String(Lab_MenuType(w)),
	RC_IsArmed(XtParent(w)) ? "Armed" : "Not Armed",
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "nothing"
	));
    DEBUGOUT(XdbDebug("MENU", w, "MenuBarEnter\n\t%s %s %s %s posted posting %s\n",
	_XmGetInDragMode(w) ? "Dragging" : "Not Dragging",
	XdbRcType2String(Lab_MenuType(w)),
	RC_IsArmed(XtParent(w)) ? "Armed" : "Not Armed",
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "nothing"
	));

    if (_XmGetInDragMode(w))
    {
	if (Lab_MenuType(w) == XmMENU_BAR)
	{
	    if (RC_IsArmed(XtParent(w)))
	    {
		if (CB_Submenu(w))
		{
		    if (RC_PopupPosted(XtParent(w)) && RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		    {
			_XmCascadingPopup(w, event, False);
		    }
		    if (RC_PopupPosted(XtParent(w)) && RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		    {
		    Boolean poppedUp;

			RC_MenuShellPopdown(w, event, &poppedUp);
		    }
		    XmCascadeButtonHighlight(w, True);
		    if (!RC_PopupPosted(XtParent(w)))
		    {
			RC_MenuCascading(w, event);
		    }
		}
		else
		{
		    _XmCascadingPopup(w, event, True);
		    if (RC_PopupPosted(XtParent(w)) && RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
		    {
		    Boolean poppedUp;

			RC_MenuShellPopdown(w, event, &poppedUp);
		    }
		    XmCascadeButtonHighlight(w, True);
		}
	    }
	}
	else
	{
	    _XmWarning(w, "%s(%d) - MenuBarEnter not in MenuBar",
		__FILE__, __LINE__);
	}
    }
}

static void
MenuBarLeave(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MenuBarLeave\n\t%s %s %s %s posted mine %s\n",
	_XmGetInDragMode(w) ? "Dragging" : "Not Dragging",
	XdbRcType2String(Lab_MenuType(w)),
	RC_IsArmed(XtParent(w)) ? "Armed" : "Not Armed",
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "nothing"
	));
    DEBUGOUT(XdbDebug("MENU", w, "MenuBarLeave\n\t%s %s %s %s posted mine %s\n",
	_XmGetInDragMode(w) ? "Dragging" : "Not Dragging",
	XdbRcType2String(Lab_MenuType(w)),
	RC_IsArmed(XtParent(w)) ? "Armed" : "Not Armed",
	RC_PopupPosted(XtParent(w)) ? XtName(RC_PopupPosted(XtParent(w))) : "nothing",
	CB_Submenu(w) ? XtName(CB_Submenu(w)) : "nothing"
	));
    if (_XmGetInDragMode(w))
    {
	if (Lab_MenuType(w) == XmMENU_BAR)
	{
	    if (RC_IsArmed(XtParent(w)))
	    {
		if (!CB_Submenu(w) || (RC_PopupPosted(XtParent(w)) && RC_PopupPosted(XtParent(w)) != CB_Submenu(w)))
		{
		    XmCascadeButtonHighlight(w, False);
		}
	    }
	}
	else
	{
	    _XmWarning(w, "%s(%d) - MenuBarLeave not in MenuBar",
		__FILE__, __LINE__);
	}
    }
}

static void
StartDrag(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Boolean validButton;
    Boolean poppedUp;

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

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

    _XmRecordEvent(event);

    /* 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)
    {
	if (CB_Submenu(w) && RC_PopupPosted(XtParent(w)) == CB_Submenu(w))
	{
	int i;

	    for (i = 0; i < MGR_NumChildren(CB_Submenu(w)); i++)
	    {
		_XmMenuDisarmItem(MGR_Children(CB_Submenu(w))[i]);
	    }
	}
	else
	{
	    RC_MenuShellPopdown(w, event, &poppedUp);
	    _XmCascadingPopup(w, event, True);
	    {
	    int i;
	    Widget menu = XtParent(w);

		for (i = 0; i < MGR_NumChildren(menu); i++)
		{
		    _XmMenuDisarmItem(MGR_Children(menu)[i]);
		}
	    }
	    XmCascadeButtonHighlight(w, True);
	}

	_XmSetInDragMode(w, True);
    }
}

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

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

    CB_Timer(w) = 0;

    _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"));

    /* Signal server to release next pointer event */
    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    if (_XmGetInDragMode(w))
    {
	if (RC_PopupPosted(XtParent(w)) != CB_Submenu(w))
	{
	Boolean poppedUp;

	    RC_MenuShellPopdown(w, NULL, &poppedUp);
	}
	if (!RC_PopupPosted(XtParent(w)))
	{
	    CB_Timer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
					  CB_MapDelay(w),
					  CascadePopupHandler,
					  (XtPointer)w);

	    XmCascadeButtonHighlight(w, True);
	}
    }
}

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

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

    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime); /* NEW */

    if (_XmGetInDragMode(w))
    {
	if (CB_Timer(w))
	{
	    XtRemoveTimeOut(CB_Timer(w));
	    CB_Timer(w) = 0;
	}

	if (!RC_PopupPosted(XtParent(w)) || (CB_Submenu(w) != RC_PopupPosted(XtParent(w))))
	{
	    XmCascadeButtonHighlight(w, False);
	}
	else
	{
	    subpane = XtParent(CB_Submenu(w));

	    if (x < XtX(subpane) || x >= XtX(subpane) + XtWidth(subpane) ||
		y < XtY(subpane) || y >= XtY(subpane) + XtHeight(subpane))
	    {
		Boolean poppedUp;

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

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

    /* clean up things */

    /* invoke help callbacks */
    XtCallActionProc(w, "PrimitiveHelp", event, params, *num_params);
}

static void
ArmAndActivate(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "%s:ArmAndActivate()\n",
		__FILE__));
    if (Lab_MenuType(w) == XmMENU_BAR)
    {
	if (!RC_IsArmed(XtParent(w)))
	{
	    RCClass_MenuProcs(XtClass(XtParent(w)))(XmMENU_ARM, XtParent(w), NULL);
	    _XmMenuFocus(w, XmMENU_FOCUS_SAVE, CurrentTime);
	    _XmSetInDragMode(XtParent(w), False);
	}
	_XmMenuFocus(w, XmMENU_FOCUS_SET, CurrentTime);
	_XmCBMenuBarSelect(w, event, params, num_params);
	_XmCBMenuBarDoSelect(w, event, params, num_params);
    }
    else /* pullright */
    {
	CascadePopupHandler(w, NULL);
    }
}


#if STDC_HEADERS
static void
MenuProcEntry(int proc, Widget w,...)
{
    va_list arg_list;

    va_start(arg_list, w);
#else
MenuProcEntry(proc, w, va_alist)
     int proc;
     Widget w;
     va_dcl
{
    va_list arglist;

    va_start(arglist);
#endif

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

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

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

    va_end(arg_list);
}

void
XmCascadeButtonHighlight(Widget w, Boolean highlight)
{
    DEBUGOUT(XdbDebug(__FILE__, w,
	     "XmCascadeButtonHighlight(hl %d, armed %d, apm 0x%X, cpm 0x%X)\n",
		      highlight,
		      XmIsPrimitive(w) ? CB_IsArmed(w) : CBG_IsArmed(w),
		  XmIsPrimitive(w) ? CB_ArmedPixmap(w) : CBG_ArmedPixmap(w),
	    XmIsPrimitive(w) ? CB_CascadePixmap(w) : CBG_CascadePixmap(w)));

    if (XmIsGadget(w))
    {
	XmCascadeButtonGadgetHighlight(w, highlight);

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

	return;
    }

    CB_SetArmed(w, highlight);

    if (XtIsRealized(w))
    {
	if (!highlight)
	{
	    _XmClearBorder(XtDisplay(w), XtWindow(w),
			   0, 0, XtWidth(w), XtHeight(w), Lab_Shadow(w));
	}
	draw_cascade(w);
    }
}

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