/**
 *
 * $Id: PushB.c,v 1.35 1998/03/16 21:52:13 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: PushB.c,v 1.35 1998/03/16 21:52:13 rwscott Exp $";

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

#include <Xm/XmP.h>
#include <Xm/PushBP.h>
#include <Xm/RowColumnP.h>
#include <Xm/MenuUtilP.h>
#include <Xm/TransltnsP.h>
#include <Xm/MenuShell.h>
#include <Xm/CascadeB.h>
#include <Xm/CascadeBG.h>

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

#include <XmI/DebugUtil.h>

/*
 * Experiment
 *
 * Try to add trait stuff by #ifdeffing it.
 */
#if XmVERSION > 1
#include <Xm/ActivatableT.h>

void _XmPushB_TraitAddCallback(Widget, XtCallbackProc, XtPointer, Boolean);

static XmActivatableTraitRec _XmPushBTraitRec = {
	/* version      */      0,
	/* cb           */      _XmPushB_TraitAddCallback
};
#endif

/* Forward Declarations */

/* Note: JAC speaks:
 * It's possible to invoke these routines with a NULL event via
 * XtCallActionProc.  Unfortunately, they expected an event from which to
 * get the time.  I've modified them to check for the existence of an
 * event before using it in all cases, and to use CurrentTime if it needs
 * the time of a NULL event.  I suppose you could use
 * XtLastTimestampProcessed() as well. */

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 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 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 MenuProcEntry(int proc, Widget rc,...);

/*
 * Resources for the pushButton class
 */
#define Offset(field) XtOffsetOf(XmPushButtonRec, pushbutton.field)
static XtResource resources[] =
{
    {
	XmNmultiClick, XmCMultiClick, XmRMultiClick,
	sizeof(unsigned char), Offset(multiClick),
	XmRImmediate, (XtPointer)((unsigned char)XmUNSPECIFIED)
    },
    {
	XmNfillOnArm, XmCFillOnArm, XmRBoolean,
	sizeof(unsigned char), Offset(fill_on_arm),
	XtRImmediate, (XtPointer)True
    },
    {
	XmNarmColor, XmCArmColor, XmRPixel,
	sizeof(Pixel), Offset(arm_color),
	XmRCallProc, (XtPointer)_XmSelectColorDefault
    },
    {
	XmNarmPixmap, XmCArmPixmap, XmRPrimForegroundPixmap,
	sizeof(Pixmap), Offset(arm_pixmap),
	XmRImmediate, (XtPointer)XmUNSPECIFIED_PIXMAP
    },
    {
	XmNshowAsDefault, XmCShowAsDefault, XmRBooleanDimension,
	sizeof(Dimension), Offset(show_as_default),
	XtRImmediate, (XtPointer)0
    },
    {
	XmNactivateCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(activate_callback),
	XmRPointer, (XtPointer)NULL
    },
    {
	XmNarmCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(arm_callback),
	XmRPointer, (XtPointer)NULL
    },
    {
	XmNdisarmCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(disarm_callback),
	XmRPointer, (XtPointer)NULL
    },
    {
	XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XmPushButtonRec, primitive.shadow_thickness),
	XmRCallProc, (XtPointer)_XmPrimitiveShadowThicknessDefault
    },
    {
	XmNdefaultButtonShadowThickness, XmCDefaultButtonShadowThickness, XmRHorizontalDimension,
	sizeof(Dimension), Offset(default_button_shadow_thickness),
	XmRImmediate, (XtPointer)0
    },
    /* Resources redefined from Primitive/Label */
    {
	XmNtraversalOn, XmCTraversalOn, XmRBoolean,
	sizeof(Boolean), XtOffsetOf(XmPushButtonRec, primitive.traversal_on),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension,
 sizeof(Dimension), XtOffsetOf(XmPushButtonRec, primitive.highlight_thickness),
	XmRCallProc, (XtPointer)_XmPrimitiveHighlightThicknessDefault
    },
};

static XmSyntheticResource syn_resources[] =
{
    {
	XmNshowAsDefault,
	sizeof(Dimension), Offset(show_as_default),
	NULL /* FIX ME */ , NULL	/* FIX ME */
    },
    {
	XmNdefaultButtonShadowThickness,
	sizeof(Dimension), Offset(default_button_shadow_thickness),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    },
    {
	XmNhighlightThickness,
 sizeof(Dimension), XtOffsetOf(XmPushButtonRec, primitive.highlight_thickness),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    }
};

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

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

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

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

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

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

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

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

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

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

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


static XtTranslations default_trans = NULL;
static XtTranslations menu_trans = NULL;

static XtActionsRec actions[] =
{
    {"Arm", Arm},
    {"MultiArm", MultiArm},
    {"Activate", Activate},
    {"MultiActivate", MultiActivate},
    {"ArmAndActivate", ArmAndActivate},
    {"Disarm", Disarm},
    {"BtnDown", ButtonDown},
    {"BtnUp", ButtonUp},
    {"Enter", EnterWindow},
    {"Leave", LeaveWindow},
    {"Help", Help},
};

/* *INDENT-OFF* */
static XmBaseClassExtRec _XmPushBCoreClassExtRec = {
    /* 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 _XmPushBPrimClassExtRec = {
    /* next_extension      */ NULL,
    /* record_type         */ NULLQUARK,
    /* version             */ XmPrimitiveClassExtVersion,
    /* record_size         */ sizeof(XmPrimitiveClassExtRec),
    /* widget_baseline     */ XmInheritBaselineProc,
    /* widget_display_rect */ XmInheritDisplayRectProc,
    /* widget_margins      */ NULL
};

XmPushButtonClassRec xmPushButtonClassRec = {
    /* Core class part */
    {
	/* superclass            */ (WidgetClass) &xmLabelClassRec,
        /* class_name            */ "XmPushButton",
	/* widget_size           */ sizeof(XmPushButtonRec),
	/* 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                */ XtInheritResize,
	/* 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)&_XmPushBCoreClassExtRec
    },
    /* Primitive Class part */
    {
	/* border_highlight      */ XmInheritBorderHighlight,
       	/* border_unhighlight    */ XmInheritBorderUnhighlight,
       	/* translations          */ XtInheritTranslations,
       	/* arm_and_activate_proc */ ArmAndActivate,
       	/* synthetic resources   */ syn_resources, 
        /* num syn res           */ XtNumber(syn_resources),
	/* extension             */ (XtPointer)&_XmPushBPrimClassExtRec
    },
    /* Label Class part */
    {
        /* setOverrideCallback */ XmInheritSetOverrideCallback,
        /* menuProcs           */ XmInheritMenuProc,
        /* translations        */ XtInheritTranslations,
	/* extension           */ NULL
    },
    /* PushButton Class part */
    {
	/* extension */ NULL
    }
};
/* *INDENT-ON* */

WidgetClass xmPushButtonWidgetClass = (WidgetClass)&xmPushButtonClassRec;

/* 
   Some #defines to make the code below more readable
 */

#define IN_MENU(w) (Lab_MenuType(w) == XmMENU_POPUP || \
		    Lab_MenuType(w) == XmMENU_PULLDOWN)

static void
class_initialize()
{
    menu_trans = XtParseTranslationTable(_XmPushB_menuTranslations);
    default_trans = XtParseTranslationTable(_XmPushB_defaultTranslations);

    _XmPushBCoreClassExtRec.record_type = XmQmotif;

#if XmVERSION > 1
	if (! XmeTraitSet(xmPushButtonWidgetClass, XmQTactivatable,
			&_XmPushBTraitRec)) {
		_XmWarning(NULL,
			"XmPushButton ClassInitialize: XmeTraitSet failed\n");
	}
#endif
}

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

static void
CreateFillGC(Widget w)
{
    XGCValues values;
    XtGCMask mask;

    mask = GCForeground | GCBackground | GCFillStyle | GCFunction |
	GCSubwindowMode | GCGraphicsExposures | GCPlaneMask;

    if (CoreBackgroundPixmap(w) != None &&
        CoreBackgroundPixmap(w) != XmUNSPECIFIED_PIXMAP)
    {
        /* we're dealing with a pixmap'ed background */
        mask |= GCTile;
 
        values.tile = CoreBackgroundPixmap(w);
        values.fill_style = FillTiled;
    }
    else
    {
	values.fill_style = FillSolid;
    }

    values.function = GXcopy;
    values.plane_mask = -1;
    values.subwindow_mode = ClipByChildren;
    values.graphics_exposures = False;
    values.foreground = PB_ArmColor(w);
    values.background = XtBackground(w);

    PB_FillGC(w) = XtGetGC(w, mask, &values);
}

static void
CreateBackgroundGC(Widget w)
{
    XGCValues values;
    XtGCMask mask;

    mask = GCForeground | GCBackground | GCFillStyle | GCFunction |
	GCSubwindowMode | GCGraphicsExposures | GCPlaneMask;

    if (CoreBackgroundPixmap(w) != None &&
        CoreBackgroundPixmap(w) != XmUNSPECIFIED_PIXMAP)
    {
        /* we're dealing with a pixmap'ed background */
        mask |= GCTile;
 
        values.tile = CoreBackgroundPixmap(w);
        values.fill_style = FillTiled;
    }
    else
    {
	values.fill_style = FillSolid;
    }

    values.function = GXcopy;
    values.plane_mask = -1;
    values.subwindow_mode = ClipByChildren;
    values.graphics_exposures = False;
    values.foreground = XtBackground(w);
    values.background = Prim_Foreground(w);

    PB_BackgroundGC(w) = XtGetGC(w, mask, &values);
}

static void
initialize_prehook(Widget request, Widget new_w,
		   ArgList args, Cardinal *num_args)
{
    _XmSaveCoreClassTranslations(new_w);

    if (XmIsRowColumn(XtParent(new_w)) &&
	(RC_Type(XtParent(new_w)) == XmMENU_PULLDOWN ||
	 RC_Type(XtParent(new_w)) == XmMENU_POPUP))
    {
	CoreClassTranslations(new_w) = (String)menu_trans;
    }
    else
    {
	CoreClassTranslations(new_w) = (String)default_trans;
    }
}

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

static void
initialize(Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    int margin, margin_extra;

    DEBUGOUT(XdbDebug(__FILE__, new_w,
		      "initialize: %i args\n"
		      "\trequest X %5i Y %5i W %5i H %5i\n"
		      "\t  new_w X %5i Y %5i W %5i H %5i\n",
		      *num_args,
		      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 (!Lab_Font(new_w))
    {
	Lab_Font(new_w) = _XmGetDefaultFontList(new_w,
						XmBUTTON_FONTLIST);
    }

    PB_Armed(new_w) = False;

    CreateFillGC(new_w);
    CreateBackgroundGC(new_w);

    if (Lab_Pixmap(new_w) == XmUNSPECIFIED_PIXMAP &&
	PB_ArmPixmap(new_w) != XmUNSPECIFIED_PIXMAP)
    {

	Lab_Pixmap(new_w) = PB_ArmPixmap(new_w);

	if (XtWidth(request) == 0)
	{
	    XtWidth(new_w) = 0;
	}
	if (XtHeight(request) == 0)
	{
	    XtHeight(new_w) = 0;
	}

	_XmCalcLabelDimensions(new_w);

	(*xmLabelClassRec.core_class.resize) (new_w);
    }

    PB_UnarmPixmap(new_w) = Lab_Pixmap(new_w);

    if (IN_MENU(new_w))
    {
	Lab_Highlight(new_w) = 0;
	/* Install the LabClass menuProcs */
	LabClass_MenuProcs(XtClass(new_w)) = MenuProcEntry;
    }
    else
    {
	/* take care of the default button shadow stuff */
	/*
	 * This new code adjusts button size in two cases : when ShowAsDefault
	 * is non-zero, and when DefaultButtonShadow is non-zero.
	 */
	/*
	 * This really new code finally figures out what that damned
	 * compatible flag does.  Back in the days of 1.1 and earlier,
	 * DefaultButtonShadow didn't exist.  Lesstif has come full
	 * circle, in the same way and for the same reason I suspect
	 * Motif did -- to avoid unnecessary geometry negotiation.
	 * What our original code did caused geometry negotiation to happen
	 * in the set_values method when we changed ShowAsDefault -- even
	 * though we weren't actually changing the geometry of the widget...
	 * See the tail end of the set_values method for changing ShowAsDefault
	 * MLM
	 */
	if (PB_DefaultButtonShadow(new_w) > 0)
	{
	    PB_Compatible(new_w) = False;
	}
	else
	{
	    PB_Compatible(new_w) = True;
	}

	if (PB_Compatible(new_w))
	{
	    PB_DefaultButtonShadow(new_w) = PB_ShowAsDefault(new_w);
	}

	if (PB_DefaultButtonShadow(new_w))
	{
	    margin = 2 * PB_DefaultButtonShadow(new_w) + Lab_Shadow(new_w);
	    margin_extra = Xm3D_ENHANCE_PIXEL;

	    Lab_MarginLeft(new_w) = margin + margin_extra;
	    Lab_MarginRight(new_w) = margin + margin_extra;
	    Lab_MarginTop(new_w) = margin + margin_extra;
	    Lab_MarginBottom(new_w) = margin + margin_extra;

	    XtWidth(new_w) += (margin + margin_extra) * 2;
	    XtHeight(new_w) += 2 * (margin + margin_extra);

	    (*xmLabelClassRec.core_class.resize) (new_w);
	    /* rws 19 Aug 1997
	       This does not seem to make sense! Without this the width and
	       height get set back to their original values when we hit the
	       constraint_init in the parent.
	     */
	    XtHeight(request) = XtHeight(new_w);
	    XtWidth(request) = XtWidth(new_w);

	    DEBUGOUT(XdbDebug(__FILE__, new_w,
			      "adjust margins for default button\n"
			      "\trequest X %5i Y %5i W %5i H %5i\n"
			      "\t  new_w X %5i Y %5i W %5i H %5i\n",
			      XtX(request), XtY(request),
			      XtWidth(request), XtHeight(request),
			      XtX(new_w), XtY(new_w),
			      XtWidth(new_w), XtHeight(new_w)));
	}
    }

    PB_Timer(new_w) = 0;
}

static void
destroy(Widget w)
{
    if (PB_Timer(w) != 0)
    {
	XtRemoveTimeOut(PB_Timer(w));
    }

    XtReleaseGC(w, PB_FillGC(w));
    XtReleaseGC(w, PB_BackgroundGC(w));
}

static Boolean
set_values(Widget old, Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    Boolean refresh_needed = True;
    Dimension margin, margin_extra;

    DEBUGOUT(XdbDebug(__FILE__, new_w,
		      "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",
		      *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 (PB_ArmColor(new_w) != PB_ArmColor(old))
    {
	XtReleaseGC(new_w, PB_FillGC(new_w));
	CreateFillGC(new_w);
	refresh_needed = True;
    }
    if (XtBackground(new_w) != XtBackground(old))
    {
	XtReleaseGC(new_w, PB_BackgroundGC(new_w));
	CreateBackgroundGC(new_w);
	refresh_needed = True;
    }

    if (!IN_MENU(new_w))
    {
	if (PB_DefaultButtonShadow(new_w) != PB_DefaultButtonShadow(old))
	{
	    PB_Compatible(new_w) = False;
	}

	if (PB_Compatible(new_w))
	{
	    PB_DefaultButtonShadow(new_w) = PB_ShowAsDefault(new_w);
	}

	if (PB_DefaultButtonShadow(new_w) != PB_DefaultButtonShadow(old))
	{
	    margin = 2 * PB_DefaultButtonShadow(new_w) + Lab_Shadow(new_w);
	    margin_extra = Xm3D_ENHANCE_PIXEL;

	    Lab_MarginLeft(new_w) = margin + margin_extra;
	    Lab_MarginRight(new_w) = margin + margin_extra;
	    Lab_MarginTop(new_w) = margin + margin_extra;
	    Lab_MarginBottom(new_w) = margin + margin_extra;

	    /* rws 19 Aug 1997
	       Do not add to the height/width, we are just re-distributing
	       the space of the label and margins.
	       nedit->Pref->Text Font
	       nedit->Pref->Default->Shell commands
	    */
	    if (!XtIsRealized(new_w))
	    {
		XtWidth(new_w) += (margin + margin_extra) * 2;
		XtHeight(new_w) += 2 * (margin + margin_extra);
	    }

	    (*xmLabelClassRec.core_class.resize) (new_w);

	    refresh_needed = True;
	}
    }

    if (PB_ArmPixmap(new_w) != PB_ArmPixmap(old) &&
	Lab_IsPixmap(new_w) && PB_Armed(new_w))
    {
	refresh_needed = True;
    }

    if (Lab_Pixmap(new_w) == XmUNSPECIFIED_PIXMAP &&
	PB_ArmPixmap(new_w) != XmUNSPECIFIED_PIXMAP)
    {

	Lab_Pixmap(new_w) = PB_ArmPixmap(new_w);

	if (Lab_RecomputeSize(new_w) && XtWidth(request) == 0)
	{
	    XtWidth(new_w) = 0;
	}
	if (Lab_RecomputeSize(new_w) && XtHeight(request) == 0)
	{
	    XtHeight(new_w) = 0;
	}

	_XmCalcLabelDimensions(new_w);

	(*xmLabelClassRec.core_class.resize) (new_w);

	refresh_needed = True;
    }

    if (Lab_Pixmap(new_w) != Lab_Pixmap(old))
    {
	PB_UnarmPixmap(new_w) = Lab_Pixmap(new_w);
	if (Lab_IsPixmap(new_w) && !PB_Armed(new_w))
	{
	    refresh_needed = True;
	}
    }

    if (Lab_IsPixmap(new_w) && PB_Armed(new_w) &&
	PB_ArmPixmap(new_w) != PB_ArmPixmap(old))
    {

	if (Lab_RecomputeSize(new_w) && XtWidth(request) == 0)
	{
	    XtWidth(new_w) = 0;
	}
	if (Lab_RecomputeSize(new_w) && XtHeight(request) == 0)
	{
	    XtHeight(new_w) = 0;
	}

	_XmCalcLabelDimensions(new_w);

	(*xmLabelClassRec.core_class.resize) (new_w);

	refresh_needed = True;
    }

    if (PB_FillOnArm(new_w) != PB_FillOnArm(old) && PB_Armed(new_w))
    {
	refresh_needed = True;
    }

    if (XtIsRealized(new_w) && !refresh_needed)
    {
	Position normal_shadow_x, normal_shadow_y;
	Dimension normal_shadow_width, normal_shadow_height, shad;

	normal_shadow_x = Lab_Highlight(new_w) + Lab_MarginLeft(new_w);
	normal_shadow_y = Lab_Highlight(new_w) + Lab_MarginTop(new_w);

	normal_shadow_width = XtWidth(new_w) - 2 * Lab_Highlight(new_w) -
	    Lab_MarginLeft(new_w) - Lab_MarginRight(new_w);
	normal_shadow_height = XtHeight(new_w) - 2 * Lab_Highlight(new_w) -
	    Lab_MarginTop(new_w) - Lab_MarginBottom(new_w);

	shad = PB_DefaultButtonShadow(new_w);

	if (PB_ShowAsDefault(new_w) && !PB_ShowAsDefault(old))
	{
	    _XmDrawShadows(XtDisplay(new_w), XtWindow(new_w),
			   Prim_TopShadowGC(new_w),
			   Prim_BottomShadowGC(new_w),
			   normal_shadow_x - (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_y - (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_width + 2 *
			   (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_height + 2 *
			   (2 * shad + Lab_Shadow(new_w)),
			   shad,
			   XmSHADOW_IN);
	}
	else
	{
	    _XmClearBorder(XtDisplay(new_w), XtWindow(new_w),
			   normal_shadow_x - (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_y - (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_width + 2 *
			   (2 * shad + Lab_Shadow(new_w)),
			   normal_shadow_height + 2 *
			   (2 * shad + Lab_Shadow(new_w)),
			   shad);
	}
    }

    return refresh_needed;
}

static void
expose(Widget w, XEvent *event, Region region)
{
    /* these make it easier to deal with the default button stuff */
    Dimension normal_shadow_x, normal_shadow_y;
    Dimension normal_shadow_width, normal_shadow_height;
    XmPrimitiveWidgetClass pwc = (XmPrimitiveWidgetClass)XtClass(w);

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

    DEBUGOUT(XdbDebug(__FILE__, w, "PB expose\n"));

    if (PB_DefaultButtonShadow(w) || PB_ShowAsDefault(w))
    {
	normal_shadow_x = Lab_Highlight(w) + Lab_MarginLeft(w);
	normal_shadow_y = Lab_Highlight(w) + Lab_MarginTop(w);

	normal_shadow_width = XtWidth(w) - 2 * Lab_Highlight(w) -
	    Lab_MarginLeft(w) - Lab_MarginRight(w);
	normal_shadow_height = XtHeight(w) - 2 * Lab_Highlight(w) -
	    Lab_MarginTop(w) - Lab_MarginBottom(w);
    }
    else
    {
	normal_shadow_x = Lab_Highlight(w);
	normal_shadow_y = Lab_Highlight(w);

	normal_shadow_width = XtWidth(w) - 2 * Lab_Highlight(w);
	normal_shadow_height = XtHeight(w) - 2 * Lab_Highlight(w);
    }

    DEBUGOUT(XdbDebug(__FILE__, w,
		      "x %d y %d w %d h %d b %d\n",
		      normal_shadow_x, normal_shadow_y,
		      normal_shadow_width, normal_shadow_height,
		      XtBorderWidth(w)));

    if (!IN_MENU(w))
    {
	/*
	 * this was badly wrong, and relied on label to _NOT_ overwrite
	 * PB shadows.  The correct order is: background, label, shadows.
	 *
	 * Chris:  No.  If you look at the output of xscope, you'll see 
	 * that the label's expose routine is called after the pushbuttons.
	 * In general this is the way it's done -- you call your expose
	 * routine, and then call your superclass's.
	 */
	if (!PB_Armed(w))
	{
	    DEBUGOUT(XdbDebug(__FILE__, w,
			      "Filling with background: %08x.\n",
			      CoreBackgroundPixmap(w)));

	    XFillRectangle(XtDisplay(w), XtWindow(w),
			   PB_BackgroundGC(w),
			   0, 0, XtWidth(w), XtHeight(w));
	}

	if (PB_Armed(w) && PB_FillOnArm(w) && !Lab_IsPixmap(w))
	{
	    XFillRectangle(XtDisplay(w), XtWindow(w),
			   PB_FillGC(w),
			   normal_shadow_x + Prim_ShadowThickness(w),
			   normal_shadow_y + Prim_ShadowThickness(w),
			   normal_shadow_width - 2 * Prim_ShadowThickness(w),
			   normal_shadow_height - 2 * Prim_ShadowThickness(w));
	}

	if (Lab_IsPixmap(w))
	{
	    if (PB_Armed(w) && PB_ArmPixmap(w) != XmUNSPECIFIED_PIXMAP)
	    {
		Lab_Pixmap(w) = PB_ArmPixmap(w);
	    }
	    else
	    {
		Lab_Pixmap(w) = PB_UnarmPixmap(w);
	    }
	}

	/* now draw the normal shadow */
	_XmDrawShadows(XtDisplay(w), XtWindow(w),
		       Prim_TopShadowGC(w),
		       Prim_BottomShadowGC(w),
		       normal_shadow_x,
		       normal_shadow_y,
		       normal_shadow_width,
		       normal_shadow_height,
		       Lab_Shadow(w),
		       PB_Armed(w) ? XmSHADOW_IN : XmSHADOW_OUT);

	/* take care of the default button stuff */
	if (PB_ShowAsDefault(w) > 0)
	{
	    Dimension shad;

	    shad = PB_DefaultButtonShadow(w);

	    _XmDrawShadows(XtDisplay(w), XtWindow(w),
			   Prim_TopShadowGC(w),
			   Prim_BottomShadowGC(w),
			   normal_shadow_x - (2 * shad + Lab_Shadow(w)),
			   normal_shadow_y - (2 * shad + Lab_Shadow(w)),
			   normal_shadow_width + 2 * (2 * shad + Lab_Shadow(w)),
			 normal_shadow_height + 2 * (2 * shad + Lab_Shadow(w)),
			   shad,
			   XmSHADOW_IN);
	}

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

	if (Prim_Highlighted(w))
	{
	    (*pwc->primitive_class.border_highlight) (w);
	}
	else
	{
	    (*pwc->primitive_class.border_unhighlight) (w);
	}
    }
    else
    {
	if (Lab_IsPixmap(w))
	{
	    if (PB_Armed(w) && PB_ArmPixmap(w) != XmUNSPECIFIED_PIXMAP)
	    {
		Lab_Pixmap(w) = PB_ArmPixmap(w);
	    }
	    else
	    {
		Lab_Pixmap(w) = PB_UnarmPixmap(w);
	    }
	}
	_XmDrawShadows(XtDisplay(w),
		       XtWindow(w),
		       Prim_TopShadowGC(w),
		       Prim_BottomShadowGC(w),
		       0,
		       0,
		       XtWidth(w),
		       XtHeight(w),
		       Lab_Shadow(w),
		       PB_Armed(w) ? (int)XmSHADOW_OUT : (int)XmNO_LINE);

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

static void
Arm(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmPushButtonCallbackStruct cbs;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    XmProcessTraversal(w, XmTRAVERSE_CURRENT);

    PB_Armed(w) = True;
    /* JAC added check for existence of event */
    if (event)
    {
	PB_ArmTimeStamp(w) = event->xbutton.time;
    }
    else
    {
	PB_ArmTimeStamp(w) = CurrentTime;
    }

    (*exp) (w, event, NULL);

    if (PB_ArmCallback(w))
    {
	cbs.reason = XmCR_ARM;
	cbs.event = event;
	cbs.click_count = PB_ClickCount(w);

	XFlush(XtDisplay(w));

	XtCallCallbackList(w,
			   PB_ArmCallback(w),
			   (XtPointer)&cbs);
    }
    MGR_ActiveChild(XtParent(w)) = w;
}


static void
Activate(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmPushButtonCallbackStruct cbs;
    XButtonEvent *ev = (XButtonEvent *)event;
    XtExposeProc exp = XtClass(w)->core_class.expose;

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

    /*
     * This test also broke Accelerators. Refined as well.
     * Guys, please watch out !!
     * Danny 23/5/1996
     *
     * JAC added check for existence of ev
     *
     */
    if (ev && (ev->type == ButtonPress || ev->type == ButtonRelease) &&
	PB_Armed(w) == False)
    {
	return;
    }

    PB_ClickCount(w) = 1;
    PB_Armed(w) = False;

    (*exp) (w, event, NULL);

    /*
     * This test should not be necessary.
     * It happens to break accelerators that trigger a button.
     * Danny 5/4/96
     * MLM: Not having means PB's break if the button is released outside
     * the widget after it is armed. Check testXm/pushbutton/test1.  Arm
     * (click) on the button, move outside the button, and release.  If this
     * isn't here, the arm callback will be executed (and it shouldn't be).
     *
     * Test refined so it doesn't fail for accelerators. -- Danny
     *
     * JAC added check for existence of ev
     */
    if (ev && (ev->type == KeyPress || ev->type == KeyRelease
	       || ((ev->x >= 0 && ev->x < XtWidth(w))
		   && (ev->y >= 0 && ev->y < XtHeight(w)))))
    {
	if (!Lab_SkipCallback(w) && PB_ActivateCallback(w))
	{
	    cbs.reason = XmCR_ACTIVATE;
	    cbs.event = event;
	    cbs.click_count = PB_ClickCount(w);

	    XFlush(XtDisplay(w));

	    XtCallCallbackList(w,
			       PB_ActivateCallback(w),
			       (XtPointer)&cbs);
	}
    }
}

static void
Disarm(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmPushButtonCallbackStruct cbs;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    if (PB_Armed(w))
    {
	PB_Armed(w) = False;
	(*exp) (w, event, NULL);
    }

    if (PB_DisarmCallback(w))
    {
	cbs.reason = XmCR_DISARM;
	cbs.event = event;
	cbs.click_count = PB_ClickCount(w);

	XFlush(XtDisplay(w));

	XtCallCallbackList(w,
			   PB_DisarmCallback(w),
			   (XtPointer)&cbs);
    }
}

static void
ArmTimeout(XtPointer data, XtIntervalId *id)
{
    XmPushButtonCallbackStruct cbs;
    XmArmTimeoutRec *p = (XmArmTimeoutRec *)data;
    Widget w = p->w;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    DEBUGOUT(XdbDebug(__FILE__, w, "ArmTimeout\n"));
 
    if (PB_Armed(w))
    {
 
	PB_Armed(w) = False;

	(*exp) (w, NULL, NULL);
     
	if (!Lab_SkipCallback(w) && PB_ActivateCallback(w))
	{
	    cbs.reason = XmCR_ACTIVATE;
     
	    if (p->has_event)
	    {
		cbs.event = &p->ev;
	    }
	    else
	    {
		cbs.event = NULL;
	    }
	    cbs.click_count = PB_ClickCount(w);
     
	    XFlush(XtDisplay(w));
     
	    XtCallCallbackList(w,
			       PB_ActivateCallback(w),
			       (XtPointer)&cbs);
	}

	Disarm(w, NULL, NULL, 0);

    }
    XtFree(data);
}

static void
ArmAndActivate(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmArmTimeoutRec *p = (XmArmTimeoutRec *)XtCalloc(1,
						     sizeof(XmArmTimeoutRec));
    Boolean poppedUp;
 
    DEBUGOUT(XdbDebug(__FILE__, w, "ArmAndActivate\n"));

    Arm(w, event, params, num_params);

    p->w = w;

    if (event)
    {
        p->ev = *event;
        p->has_event=True;
    }

    if (IN_MENU(w))
    {
	ArmTimeout((XtPointer)p, 0);
	RC_MenuButtonPopdown(w, event, &poppedUp);
    }
    else
    {
	if (PB_Timer(w) != 0)
	{
	    XtRemoveTimeOut(PB_Timer(w));
	}

	PB_Timer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
				  ACTIVATE_DELAY, ArmTimeout, (XtPointer)p);
    }
}

static void
Help(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    /* unpost menus */

    /* restore focus */

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

static void
EnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmPushButtonCallbackStruct cbs;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    DEBUGOUT(XdbDebug(__FILE__, w, "PushB Enter\n"));

    if (!IN_MENU(w))
    {
	_XmPrimitiveEnter(w, event, NULL, NULL);

	if (PB_Armed(w))
	{
	    (*exp) (w, event, NULL);
	}
    }
    else
	/* we're in a menu */
    {
	if (_XmGetInDragMode(w))
	{
	Boolean poppedUp;

	    RC_MenuShellPopdown(w, event, &poppedUp);

	    PB_Armed(w) = True;

	    (*exp) (w, event, NULL);

	    if (PB_ArmCallback(w))
	    {
		cbs.reason = XmCR_ARM;
		cbs.event = event;
		cbs.click_count = PB_ClickCount(w);

		XFlush(XtDisplay(w));

		XtCallCallbackList(w,
				   PB_ArmCallback(w),
				   (XtPointer)&cbs);
	    }
	}
    }
}

static void
LeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmPushButtonCallbackStruct cbs;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    DEBUGOUT(XdbDebug(__FILE__, w, "LeaveWindow()\n"));
    if (!IN_MENU(w))
    {
	_XmPrimitiveLeave(w, event, NULL, NULL);
	if (PB_Armed(w))
	{
	    (*exp) (w, event, NULL);
	}
    }
    else
	/* we're in a menu */
    {
	if (_XmGetInDragMode(w))
	{
	    PB_Armed(w) = False;

	    (*exp) (w, event, NULL);

	    if (PB_DisarmCallback(w))
	    {
		cbs.reason = XmCR_DISARM;
		cbs.event = event;
		cbs.click_count = PB_ClickCount(w);

		XFlush(XtDisplay(w));

		XtCallCallbackList(w,
				   PB_DisarmCallback(w),
				   (XtPointer)&cbs);
	    }
	}
    }
}

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

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

    if (event && (event->type == ButtonRelease))
    {
	RC_MenuButton(w, event, &validButton);
    }
    else
    {
    	validButton = False;
    }

    if (!validButton)
    {
	return;
    }

    if (!PB_Armed(w))
    {
	return;
    }

    PB_Armed(w) = False;

    RC_MenuButtonPopdown(w, event, &poppedUp);

    _XmRecordEvent(event);

    _XmDrawShadows(XtDisplay(w),
		   XtWindow(w),
		   Prim_TopShadowGC(w),
		   Prim_BottomShadowGC(w),
		   Lab_Highlight(w),
		   Lab_Highlight(w),
		   XtWidth(w) - 2 * Lab_Highlight(w),
		   XtHeight(w) - 2 * Lab_Highlight(w),
		   Lab_Shadow(w),
		   XmNO_LINE);

    if (!Lab_SkipCallback(w) && PB_ActivateCallback(w))
    {
	cbs.reason = XmCR_ACTIVATE;
	cbs.event = event;
	cbs.click_count = PB_ClickCount(w);

	XFlush(XtDisplay(w));

	XtCallCallbackList(w,
			   PB_ActivateCallback(w),
			   &cbs);
    }
    if (PB_DisarmCallback(w))
    {
	cbs.reason = XmCR_DISARM;
	cbs.event = event;
	cbs.click_count = PB_ClickCount(w);

	XFlush(XtDisplay(w));

	XtCallCallbackList(w,
			   PB_DisarmCallback(w),
			   &cbs);
    }

    _XmSetInDragMode(w, False);
}

static void
ButtonDown(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    /* modified from the MegaButton widget */
    int validButton;
    XmPushButtonCallbackStruct cbs;
    Boolean poppedUp;

    DEBUGOUT(XdbDebug(__FILE__, w, "ButtonDown()\n"));
    /* queue events until the next button event */
    XAllowEvents(XtDisplay(w), SyncPointer, CurrentTime);

    if (event && (event->type == ButtonPress))
    {
	RC_MenuButton(w, event, &validButton);

	if (!validButton)
	{
	    DEBUGOUT(XdbDebug(__FILE__, w, "ButtonDown() - Invalid button\n"));
	    return;
	}
    }

    _XmSetInDragMode(w, True);

    RC_MenuShellPopdown(w, event, &poppedUp);

    _XmDrawShadows(XtDisplay(w),
		   XtWindow(w),
		   Prim_TopShadowGC(w),
		   Prim_BottomShadowGC(w),
		   Lab_Highlight(w),
		   Lab_Highlight(w),
		   XtWidth(w) - 2 * Lab_Highlight(w),
		   XtHeight(w) - 2 * Lab_Highlight(w),
		   Lab_Shadow(w),
		   XmSHADOW_OUT);

    if (!PB_Armed(w))
    {
	PB_Armed(w) = True;
	if (PB_ArmCallback(w))
	{
	    cbs.reason = XmCR_ARM;
	    cbs.event = event;
	    cbs.click_count = PB_ClickCount(w);

	    XFlush(XtDisplay(w));

	    XtCallCallbackList(w,
			       PB_ArmCallback(w),
			       (XtPointer)&cbs);
	}
    }

    _XmRecordEvent(event);
}

static void
MultiActivate(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XButtonEvent *ev = (XButtonEvent *)event;
    XmPushButtonCallbackStruct cbs;
    XtExposeProc exp = XtClass(w)->core_class.expose;

    DEBUGOUT(XdbDebug(__FILE__, w, "PushB: MultiClick\n"));

    if (PB_MultiClick(w) == XmMULTICLICK_KEEP)
    {
	Time mctime = XtGetMultiClickTime(XtDisplay(w));
	Time evtime;
	/* JAC added check for existence of event */
	if (event)
	{
	    evtime=event->xbutton.time;
	}
	else
	{
	    evtime=CurrentTime;
	}

	if ((evtime - PB_ArmTimeStamp(w)) < mctime)
	{
	    PB_ClickCount(w)++;
	}
	else
	{
	    PB_ClickCount(w) = 1;
	}

	PB_Armed(w) = False;

	(*exp) (w, event, NULL);

	/* JAC added check for existence of ev */
	if (ev && (ev->type == KeyPress || ev->type == KeyRelease
		   || ((ev->x >= 0 && ev->x < XtWidth(w))
		       && (ev->y >= 0 && ev->y < XtHeight(w)))))
	{
	    if (PB_MultiClick(w) == XmMULTICLICK_DISCARD &&
		PB_ClickCount(w) > 1)
	    {
		return;
	    }

	    if (!Lab_SkipCallback(w) && PB_ActivateCallback(w))
	    {
		cbs.reason = XmCR_ACTIVATE;
		cbs.event = event;
		cbs.click_count = PB_ClickCount(w);

		XFlush(XtDisplay(w));

		XtCallCallbackList(w,
				   PB_ActivateCallback(w),
				   (XtPointer)&cbs);
	    }
	}
	Disarm(w, event, params, num_params);
    }
}

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

    if (PB_MultiClick(w) == XmMULTICLICK_KEEP)
	Arm(w, event, NULL, NULL);
}

void
_XmClearBCompatibility(Widget pb)
{
}

#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:
    	{
    	Cardinal num_params = 0;

	    Arm(w, NULL, NULL, &num_params);
    	}
    	break;
    case XmMENU_DISARM:
    	{
    	Cardinal num_params = 0;

	    Disarm(w, NULL, NULL, &num_params);
    	}
    	break;
    default:
	_XmWarning(w, "%s(%d) - Invalid menuProc function", __FILE__, __LINE__);
	break;
    }

    va_end(arg_list);
}

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

#if XmVERSION > 1
void _XmPushB_TraitAddCallback(Widget w,
                        XtCallbackProc cb,
                        XtPointer cbp,
                        Boolean set)
{   
        if (set)  
                XtAddCallback(w, XmNactivateCallback, cb, cbp);
        else
                XtRemoveCallback(w, XmNactivateCallback, cb, cbp);
}
#endif 
