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

#include <stdio.h>
#include <string.h>
#include <X11/IntrinsicP.h>
#include <X11/ShellP.h>
#include <Xm/ActivatableT.h>
#include <Xm/DisplayP.h>
#include <Xm/DrawP.h>
#include <Xm/ManagerP.h>
#include <Xm/MenuT.h>
#include <Xm/PushBGP.h>
#include <Xm/RowColumnP.h>
#include <Xm/ScreenP.h>
#include <Xm/TakesDefT.h>
#include <Xm/TraitP.h>
#include "BaseClassI.h"
#include "CacheI.h"
#include "ColorI.h"
#include "ExtObjectI.h"
#include "GadgetUtiI.h"
#include "LabelI.h"
#include "LabelGI.h"
#include "MenuProcI.h"
#include "MenuStateI.h"
#include "MenuUtilI.h"
#include "SyntheticI.h"
#include "TravActI.h"
#include "TraversalI.h"
#include "UniqueEvnI.h"
#include "XmI.h"

#define DELAY_DEFAULT		100
#define XmINVALID_MULTICLICK	255

struct PBTimeOutEvent {
  XmPushButtonGadget  pushbutton;
  XEvent              *xevent;
};


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

static int _XmPushBCacheCompare( 
                        XtPointer A,
                        XtPointer B);
static void InputDispatch( 
                        Widget wid,
                        XEvent *event,
                        Mask event_mask);
static void Arm( 
                        XmPushButtonGadget pb,
                        XEvent *event);
static void Activate( 
                        XmPushButtonGadget pb,
                        XEvent *event);
static void ArmAndActivate( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params);
static void ArmTimeout( 
                        XtPointer data,
                        XtIntervalId *id);
static void Disarm( 
                        XmPushButtonGadget pb,
                        XEvent *event);
static void BtnDown( 
                        XmPushButtonGadget pb,
                        XEvent *event);
static void BtnUp( 
                        Widget wid,
                        XEvent *event);
static void Enter( 
                        Widget wid,
                        XEvent *event);
static void Leave( 
                        Widget wid,
                        XEvent *event);
static void BorderHighlight( 
                        Widget wid);
static void DrawBorderHighlight( 
                        Widget wid);
static void BorderUnhighlight( 
                        Widget wid);
static void KeySelect( 
                        Widget wid,
                        XEvent *event);
static void ClassInitialize( void );
static void ClassPartInitialize( 
                        WidgetClass wc);
static void SecondaryObjectCreate( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args);
static void InitializePrehook( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args);
static void InitializePosthook( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args);
static void Initialize( 
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args);
static void GetFillGC( 
                        XmPushButtonGadget pb);
static Boolean SetValuesPrehook( 
                        Widget oldParent,
                        Widget refParent,
                        Widget newParent,
                        ArgList args,
                        Cardinal *num_args);
static void GetValuesPrehook( 
                        Widget newParent,
                        ArgList args,
                        Cardinal *num_args);
static void GetValuesPosthook( 
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args);
static Boolean SetValuesPosthook( 
                        Widget current,
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args);
static Boolean SetValues( 
                        Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args);
static void Help( 
                        XmPushButtonGadget pb,
                        XEvent *event);
static void Destroy( 
                        Widget wid);
static void Resize(
                        Widget wid);
static void ActivateCommonG( 
                        XmPushButtonGadget pb,
                        XEvent *event,
                        Mask event_mask);
static Cardinal GetPushBGClassSecResData( 
                        WidgetClass w_class,
                        XmSecondaryResourceData **data_rtn);
static XtPointer GetPushBGClassSecResBase( 
                        Widget widget,
                        XtPointer client_data);
static void EraseDefaultButtonShadow( 
                        XmPushButtonGadget pb);
static void DrawDefaultButtonShadow( 
                        XmPushButtonGadget pb);
static int AdjustHighLightThickness( 
                        XmPushButtonGadget new_w,
                        XmPushButtonGadget current);
static void Redisplay( 
                        Widget wid,
                        XEvent *event,
                        Region region);
static void DrawPushButtonLabelGadget( 
                        XmPushButtonGadget pb,
                        XEvent *event,
                        Region region);
static void DrawLabelGadget( 
                        XmPushButtonGadget pb,
                        XEvent *event,
                        Region region) ;
static void DrawPushButtonGadgetShadows( 
                        XmPushButtonGadget pb);
static void DrawPBGadgetShadows( 
                        XmPushButtonGadget pb);
static void EraseDefaultButtonShadows( 
                        XmPushButtonGadget pb);
static void DrawDefaultButtonShadows( 
                        XmPushButtonGadget pb);
static XmImportOperator ShowAsDef_ToHorizPix( 
                        Widget widget,
                        int offset,
                        XtArgVal *value);
static Boolean ComputePBLabelArea( 
                        XmPushButtonGadget pb,
                        LRectangle *box);
static void ExportHighlightThickness( 
                        Widget widget,
                        int offset,
                        XtArgVal *value);
static void SetPushButtonSize(
                        XmPushButtonGadget newpb);
static void ChangeCB(Widget w, 
		     XtCallbackProc activCB,
		     XtPointer closure,
		     Boolean setunset);
static void ShowAsDefault(Widget w,
			  XtEnum state);
static void PBG_FixTearoff( XmPushButtonGadget pb);

/********    End Static Function Declarations    ********/

/************************************************
 The uncached resources for Push Button  
 ************************************************/

static XtResource resources[] = 
{     
   {
     XmNactivateCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
     XtOffsetOf (struct _XmPushButtonGadgetRec, pushbutton.activate_callback),
     XmRPointer, (XtPointer) NULL
   },

   {
     XmNarmCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
     XtOffsetOf (struct _XmPushButtonGadgetRec, pushbutton.arm_callback),
     XmRPointer, (XtPointer) NULL
   },

   {
     XmNdisarmCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
     XtOffsetOf (struct _XmPushButtonGadgetRec, pushbutton.disarm_callback),
     XmRPointer, (XtPointer) NULL
   },

   {
     XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, 
     sizeof(Dimension),
     XtOffsetOf (struct _XmPushButtonGadgetRec, gadget.shadow_thickness),
     XmRCallProc, (XtPointer) _XmSetThickness
   },

   {
     XmNtraversalOn, XmCTraversalOn, XmRBoolean, sizeof (Boolean),
     XtOffsetOf (struct _XmGadgetRec, gadget.traversal_on),
     XmRImmediate, (XtPointer) True
   },

   {
     XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension,
     sizeof (Dimension),
     XtOffsetOf (struct _XmGadgetRec, gadget.highlight_thickness),
     XmRCallProc, (XtPointer) _XmSetThickness
   },

   {
     XmNshowAsDefault, XmCShowAsDefault, XmRBooleanDimension,
     sizeof (Dimension),
     XtOffsetOf (struct _XmPushButtonGadgetRec, pushbutton.show_as_default),
     XmRImmediate, (XtPointer) 0
   },
};

static XmSyntheticResource syn_resources[] =
{
   {
     XmNshowAsDefault,
     sizeof (Dimension),
     XtOffsetOf (struct _XmPushButtonGadgetRec, pushbutton.show_as_default),
     XmeFromHorizontalPixels,
     ShowAsDef_ToHorizPix
   },
   {
     XmNhighlightThickness,
     sizeof (Dimension),
     XtOffsetOf (struct _XmGadgetRec, gadget.highlight_thickness),
     ExportHighlightThickness,
     XmeToHorizontalPixels
   },

};

/**********************************************
 Cached resources for PushButton Gadget
 **********************************************/

static XtResource cache_resources[] =
{
   {
     XmNmultiClick, XmCMultiClick, XmRMultiClick,
     sizeof (unsigned char),
     XtOffsetOf (struct _XmPushButtonGCacheObjRec, pushbutton_cache.multiClick),
     XmRImmediate, (XtPointer) XmINVALID_MULTICLICK
   },

   {
     XmNdefaultButtonShadowThickness, XmCDefaultButtonShadowThickness,
     XmRHorizontalDimension, sizeof (Dimension),
     XtOffsetOf(struct _XmPushButtonGCacheObjRec,
		pushbutton_cache.default_button_shadow_thickness),
     XmRImmediate, (XtPointer) 0
   },

   {
     XmNfillOnArm, XmCFillOnArm, XmRBoolean, sizeof (Boolean),
     XtOffsetOf(struct _XmPushButtonGCacheObjRec, pushbutton_cache.fill_on_arm),
     XmRImmediate, (XtPointer) True
   },

   {
     XmNarmColor, XmCArmColor, XmRPixel, sizeof (Pixel),
     XtOffsetOf(struct _XmPushButtonGCacheObjRec, pushbutton_cache.arm_color),
     XmRImmediate, (XtPointer) INVALID_PIXEL
   },

   {
     XmNarmPixmap, XmCArmPixmap, XmRDynamicPixmap, sizeof (Pixmap),
     XtOffsetOf(struct _XmPushButtonGCacheObjRec, pushbutton_cache.arm_pixmap),
     XmRImmediate, (XtPointer) XmUNSPECIFIED_PIXMAP
   },
};

/*  Definition for resources that need special processing in get values  */

static XmSyntheticResource cache_syn_resources[] =
{
   {
     XmNdefaultButtonShadowThickness, sizeof (Dimension),
     XtOffsetOf(struct _XmPushButtonGCacheObjRec,
		pushbutton_cache.default_button_shadow_thickness),
     XmeFromHorizontalPixels,
     XmeToHorizontalPixels
   },
};

/*************************************<->*************************************
 *
 *
 *   Description:  global class record for instances of class: PushButton
 *   -----------
 *
 *   Defines default field settings for this class record.
 *
 *************************************<->***********************************/

static XmCacheClassPart PushButtonClassCachePart = {
    {NULL, 0, 0},            /* head of class cache list */
    _XmCacheCopy,            /* Copy routine     */
    _XmCacheDelete,          /* Delete routine   */
    _XmPushBCacheCompare,    /* Comparison routine   */
};

static XmBaseClassExtRec   PushBGClassExtensionRec = {
    NULL,   		 			/* next_extension        */
    NULLQUARK,    				/* record_typ            */
    XmBaseClassExtVersion,			/* version               */
    sizeof(XmBaseClassExtRec), 			/* record_size           */
    InitializePrehook,         			/* initializePrehook     */
    SetValuesPrehook,   			/* setValuesPrehook      */
    InitializePosthook,				/* initializePosthook    */
    SetValuesPosthook, 				/* setValuesPosthook     */
    (WidgetClass)&xmPushButtonGCacheObjClassRec,/* secondaryObjectClass  */
    SecondaryObjectCreate, 		        /* secondaryObjectCreate */
    GetPushBGClassSecResData,                   /* getSecResData         */
    {0},           		                /* fast subclass         */
    GetValuesPrehook,   			/* getValuesPrehook      */
    GetValuesPosthook,   			/* getValuesPosthook     */
    (XtWidgetClassProc)NULL,                    /* classPartInitPrehook  */
    (XtWidgetClassProc)NULL,                    /* classPartInitPosthook */
    (XtResourceList)NULL,                       /* ext_resources         */
    (XtResourceList)NULL,                       /* compiled_ext_resources*/
    0,                                          /* num_ext_resources     */
    FALSE,                                      /* use_sub_resources     */
    XmInheritWidgetNavigable,                   /* widgetNavigable       */
    XmInheritFocusChange,                       /* focusChange           */
};

/* ext rec static initialization */
externaldef(xmpushbuttongcacheobjclassrec)
XmPushButtonGCacheObjClassRec xmPushButtonGCacheObjClassRec =
{
  {
      /* superclass         */    (WidgetClass) &xmLabelGCacheObjClassRec,
      /* class_name         */    "XmPushButtonGadget",
      /* widget_size        */    sizeof(XmPushButtonGCacheObjRec),
      /* class_initialize   */    (XtProc)NULL,
      /* chained class init */    (XtWidgetClassProc)NULL,
      /* class_inited       */    False,
      /* initialize         */    (XtInitProc)NULL,
      /* initialize hook    */    (XtArgsProc)NULL,
      /* realize            */    NULL,
      /* actions            */    NULL,
      /* num_actions        */    0,
      /* resources          */    cache_resources,
      /* num_resources      */    XtNumber(cache_resources),
      /* xrm_class          */    NULLQUARK,
      /* compress_motion    */    False,
      /* compress_exposure  */    False,
      /* compress enter/exit*/    False,
      /* visible_interest   */    False,
      /* destroy            */    (XtWidgetProc)NULL,
      /* resize             */    NULL,
      /* expose             */    NULL,
      /* set_values         */    (XtSetValuesFunc)NULL,
      /* set values hook    */    (XtArgsFunc)NULL,
      /* set values almost  */    NULL,
      /* get values hook    */    (XtArgsProc)NULL,
      /* accept_focus       */    NULL,
      /* version            */    XtVersion,
      /* callback offsetlst */    NULL,
      /* default trans      */    NULL,
      /* query geo proc     */    NULL,
      /* display accelerator*/    NULL,
      /* extension record   */    NULL,
   },

   {
      /* synthetic resources */   cache_syn_resources,
      /* num_syn_resources   */   XtNumber(cache_syn_resources), 
      /* extension           */   NULL,
   }
};

/*  The PushButton class record definition  */

static XmGadgetClassExtRec _XmPushBGadClassExtRec = {
    NULL,
    NULLQUARK,
    XmGadgetClassExtVersion,
    sizeof(XmGadgetClassExtRec),
    XmInheritBaselineProc,                  /* widget_baseline */
    XmInheritDisplayRectProc,               /* widget_display_rect */
    XmInheritMarginsProc,                   /* widget_margins */
};

externaldef(xmpushbuttongadgetclassrec)
	XmPushButtonGadgetClassRec xmPushButtonGadgetClassRec = 
{
  {
      (WidgetClass) &xmLabelGadgetClassRec,  	/* superclass            */
      "XmPushButtonGadget",             	/* class_name	         */
      sizeof(XmPushButtonGadgetRec),    	/* widget_size	         */
      ClassInitialize,         			/* class_initialize      */
      ClassPartInitialize,              	/* class_part_initialize */
      FALSE,                            	/* class_inited          */
      Initialize,          	                /* initialize	         */
      (XtArgsProc)NULL,                         /* initialize_hook       */
      NULL,                     		/* realize	         */
      NULL,                             	/* actions               */
      0,			        	/* num_actions    	 */
      resources,                        	/* resources	         */
      XtNumber(resources),              	/* num_resources         */
      NULLQUARK,                        	/* xrm_class	         */
      TRUE,                             	/* compress_motion       */
      XtExposeCompressMaximal,          	/* compress_exposure     */
      TRUE,                             	/* compress_enterleave   */
      FALSE,                            	/* visible_interest      */	
      Destroy,           	                /* destroy               */	
      Resize,	                		/* resize                */
      Redisplay,        		        /* expose                */	
      SetValues,      	                        /* set_values	         */	
      (XtArgsFunc)NULL,                       	/* set_values_hook       */
      XtInheritSetValuesAlmost,         	/* set_values_almost     */
      (XtArgsProc)NULL,                        	/* get_values_hook       */
      NULL,                 			/* accept_focus	         */	
      XtVersion,                        	/* version               */
      NULL,                             	/* callback private      */
      NULL,                             	/* tm_table              */
      XtInheritQueryGeometry,           	/* query_geometry        */
      NULL,					/* display_accelerator   */
      (XtPointer)&PushBGClassExtensionRec,	/* extension             */
   },

   {	/* gadget class record */
      BorderHighlight,			  /* border highlight   */
      BorderUnhighlight,		  /* border_unhighlight */
      ArmAndActivate,			  /* arm_and_activate   */
      InputDispatch,			  /* input dispatch     */
      XmInheritVisualChange,		  /* visual_change      */
      syn_resources,   			  /* syn resources      */
      XtNumber(syn_resources),  	  /* num syn_resources  */
      &PushButtonClassCachePart,	  /* class cache part   */
      (XtPointer)&_XmPushBGadClassExtRec, /* extension          */
   },

   { 	/* label_class record */
      XmInheritWidgetProc,	/* setOverrideCallback */
      XmInheritMenuProc,	/* menu proc's entry   */
      NULL,			/* extension	       */   
   },

   {	/* pushbutton class record */
      NULL,			/* extension 	 */
   }
};

externaldef(xmpushbuttongadgetclass) WidgetClass xmPushButtonGadgetClass = 
   (WidgetClass) &xmPushButtonGadgetClassRec;


/* Trait record for pushButtonG */

static XmConst XmActivatableTraitRec pushButtonGAT = {
  0,		/* version */
  ChangeCB,
};

/* TakesDefault Trait record for pushButtonG */

static XmConst XmTakesDefaultTraitRec pushButtonGTDT = {
  0,		/* version */
  ShowAsDefault,
};

/* Menu Savvy trait record */
static XmMenuSavvyTraitRec MenuSavvyRecord = {
    /* version: */
    -1,
    NULL,
    NULL,
    NULL,
    _XmCBNameActivate,
};

/*****************************************************************
 *  SPECIAL PROPERTIES OF PUSHBUTTON GADGET INSIDE A MENU:
 *   When a PushButton (widget/gadget) is incorporated in a Menu
 *   (Pulldownor Popup) - its properties get modified in these ways:
 *   (1) The redisplay routine should not draw its background nor draw
 *	     its shadows.  It should draw only the label. To comply with 
 *	     this means that the arm-color and background color are not
 *	     of any use. As a result the values in the FillGC and BackgroundGC
 *	     are not initialized and are likely to be bogus. This causes
 *	     special casing of Initialize and SetValues routines.
 *   (2) PushButton does not show its depressed appearance in the
 *       menu. This will cause Arm(), DisArm(), ArmAndActivate routines
 *	     to have special cases.
 *   In short the properties of Pushbutton in a menu are so different
 *	  that practically all major routines in this widget will have to
 *	  special-cased to accommodate this difference as if two different
 *	  classes are being glued to one class.
 *******************************************************************/

/*******************************************************************
 *
 *  _XmPushBCacheCompare
 *
 *******************************************************************/

static int 
_XmPushBCacheCompare(
        XtPointer A,
        XtPointer B )
{
  XmPushButtonGCacheObjPart *a_inst = (XmPushButtonGCacheObjPart *) A;
  XmPushButtonGCacheObjPart *b_inst = (XmPushButtonGCacheObjPart *) B;
  
  if ((a_inst->fill_on_arm	== b_inst->fill_on_arm) &&
      (a_inst->arm_color	== b_inst->arm_color) &&
      (a_inst->arm_pixmap	== b_inst->arm_pixmap) &&
      (a_inst->unarm_pixmap	== b_inst->unarm_pixmap) &&
      (a_inst->fill_gc		== b_inst->fill_gc) &&
      (a_inst->background_gc	== b_inst->background_gc) &&
      (a_inst->multiClick	== b_inst->multiClick) &&
      (a_inst->default_button_shadow_thickness
       				== b_inst->default_button_shadow_thickness) &&
      (a_inst->timer		== b_inst->timer) &&
      (a_inst->timer_widget	== b_inst->timer_widget) 
     )
    return 1;
  else
    return 0;
}

/************************************************************************
 *
 *  InputDispatch
 *     This function catches input sent by a manager and dispatches it
 *     to the individual routines.
 *
 ************************************************************************/

static void 
InputDispatch(
        Widget wid,
        XEvent *event,
        Mask event_mask )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;

  if ((event_mask & XmARM_EVENT) ||
      ((PBG_MultiClick(pb) == XmMULTICLICK_KEEP) &&
       (event_mask & XmMULTI_ARM_EVENT)))
    {
      if (LabG_IsMenupane(pb))
	BtnDown (pb, event);
      else
	Arm (pb, event);
    }
  
  else if (event_mask & XmACTIVATE_EVENT)
    {
      PBG_ClickCount (pb) = 1; 
      /* pb->pushbutton.click_count = 1; */
      ActivateCommonG (pb, event, event_mask);
    }

  else if (event_mask & XmMULTI_ACTIVATE_EVENT)
    { 
      /* If XmNMultiClick resource is set to DISCARD - do nothing
       * else increment clickCount and call ActivateCommonG.
       */
      if (PBG_MultiClick(pb) == XmMULTICLICK_KEEP) 
	{ 
	  (PBG_ClickCount (pb))++;
	  ActivateCommonG (pb, event, event_mask);
	}
    }
  
  else if (event_mask & XmHELP_EVENT)
    Help (pb, event);

  else if (event_mask & XmENTER_EVENT)
    Enter ((Widget) pb, event);

  else if (event_mask & XmLEAVE_EVENT)
    Leave ((Widget) pb, event);

  else if (event_mask & XmFOCUS_IN_EVENT)
    _XmFocusInGadget((Widget) pb, event, NULL, NULL);
  
  else if (event_mask & XmFOCUS_OUT_EVENT)
    _XmFocusOutGadget((Widget) pb, event, NULL, NULL);
  
  else if (event_mask & XmBDRAG_EVENT)
    _XmProcessDrag ((Widget) pb, event, NULL, NULL);
}

/************************************************************************
 *
 *     Arm
 *
 *     This function processes button 1 down occuring on the pushbutton.
 *     Mark the pushbutton as armed (i.e. active).
 *     The callbacks for XmNarmCallback are called.
 *
 ************************************************************************/

static void 
Arm(
        XmPushButtonGadget pb,
        XEvent *event )
{
  XmPushButtonCallbackStruct call_value;
  XtExposeProc    expose;
  
  PBG_Armed(pb) = TRUE;
  
  _XmProcessLock();
  expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
      rect_class.expose;
  _XmProcessUnlock();

  (* (expose)) ((Widget) pb, event, (Region) NULL);
  
  if (PBG_ArmCallback(pb))
    {
      XFlush(XtDisplay (pb));
      
      call_value.reason = XmCR_ARM;
      call_value.event = event;
      XtCallCallbackList ((Widget) pb, PBG_ArmCallback(pb), &call_value);
    }
}

/************************************************************************
 *
 *     Activate
 *
 *     Mark the pushbutton as unarmed (i.e. inactive).
 *     If the button release occurs inside of the PushButton, the 
 *     callbacks for XmNactivateCallback are called.
 *
 ************************************************************************/

static void 
Activate(
        XmPushButtonGadget pb,
        XEvent *event )
{
  XmPushButtonCallbackStruct call_value;   
  XmMenuSystemTrait menuSTrait;
  XtExposeProc    expose;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(pb)), XmQTmenuSystem);
  
  PBG_Armed(pb) = FALSE;
  
   _XmProcessLock();
   expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
       rect_class.expose;
   _XmProcessUnlock();
  (* (expose)) ((Widget) pb, event, (Region) NULL);
  
  /* CR 9181: Consider clipping when testing visibility. */
  if ((event->xany.type == ButtonPress || event->xany.type == ButtonRelease) &&
      _XmGetPointVisibility((Widget)pb, 
			    event->xbutton.x_root, event->xbutton.y_root))
    {
      call_value.reason = XmCR_ACTIVATE;
      call_value.event = event;
      call_value.click_count = PBG_ClickCount(pb);
      
      /* _XmRecordEvent(event); */       /* Fix CR 3407 DRand 6/16/92 */
      
      /* If the parent is menu system able, notify it about the select. */
      if (menuSTrait != NULL)
	menuSTrait->entryCallback (XtParent(pb), (Widget) pb, &call_value);
      
      if ((! LabG_SkipCallback(pb)) &&
	  (PBG_ActivateCallback(pb)))
	{
	  XFlush (XtDisplay (pb));
	  XtCallCallbackList ((Widget) pb, PBG_ActivateCallback(pb),
			      &call_value);
	}
    }
}

static void 
PBG_FixTearoff( XmPushButtonGadget pb)	
{
	 if  (XmMENU_PULLDOWN == LabG_MenuType(pb))
	 {							
		Widget mwid = XmGetPostedFromWidget(XtParent(pb));	
		if (mwid && XmIsRowColumn(mwid)
			&& (XmMENU_OPTION == RC_Type(mwid)) 
			&& _XmIsActiveTearOff(XtParent(pb))) 
			XmProcessTraversal((Widget) pb, XmTRAVERSE_CURRENT);
	 }							
}

/************************************************************************
 *
 *     ArmAndActivate
 *
 *  - Called if the PushButtonGadget is being selected via keyboard
 *    i.e. by pressing <return> or <space-bar>.
 *  - Called by SelectionBox and FileSelectionBox code when they receive
 *    a default-action callback on their embedded XmList widgets.
 ************************************************************************/

/*ARGSUSED*/
static void 
ArmAndActivate(
        Widget wid,
        XEvent *event,
        String *params,		/* unused */
        Cardinal *num_params )	/* unused */
{  
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  Boolean already_armed = PBG_Armed(pb);
  Boolean is_menupane = LabG_IsMenupane(pb);
  Boolean torn_has_focus = FALSE;    /* must be torn! */
  XmMenuSystemTrait menuSTrait;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
  
  if (is_menupane && !XmIsMenuShell(XtParent(XtParent(pb))))
    {
      /* Because the pane is torn and the parent is a transient shell,
       * the shell's focal point from _XmGetFocusData should be valid
       * (as opposed to getting it from a MenuShell).
       */
      if (XmeFocusIsInShell((Widget)pb))
	{
	  /* In case allowAcceleratedInsensitiveUnmanagedMenuItems is True */
	  if (!XtIsSensitive((Widget)pb) || (!XtIsManaged((Widget)pb)))
            return;
	  torn_has_focus = TRUE;
	}
    }
  
  if (is_menupane && menuSTrait != NULL)
    {
      PBG_Armed(pb) = FALSE;
      
      /* CR 7799: Torn off menus shouldn't be shared, so don't reparent. */
      if (torn_has_focus)
	menuSTrait->popdown(XtParent(pb), event);
      else
	menuSTrait->buttonPopdown(XtParent(pb), event);
      
      /* If its in a torn off menu pane, show depressed button briefly. */
      if (torn_has_focus)
	{
	  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
	  Boolean etched_in = dpy->display.enable_etched_in_menu;

	  /* Set the focus here. */
	  XmProcessTraversal((Widget) pb, XmTRAVERSE_CURRENT);
	  
	  if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
	      (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
	    XmeDrawShadows
	      (XtDisplay (pb), XtWindow (pb),
	       LabG_BottomShadowGC(pb), LabG_TopShadowGC(pb),
	       pb->rectangle.x + pb->gadget.highlight_thickness,
	       pb->rectangle.y + pb->gadget.highlight_thickness,
	       pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
	       pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
	       pb->gadget.shadow_thickness, 
	       etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
	}
    }
  else
    {
      XtExposeProc    expose;
      /* We have no idea what the event is, so don't pass it through,
       * in case some future subclass is smarter about reexposure.
       */
      PBG_Armed(pb) = TRUE;
      _XmProcessLock();
      expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
		rect_class.expose;
      _XmProcessUnlock();
      (* (expose)) ((Widget) pb, (XEvent *)NULL, (Region) NULL);
    }
  
  XFlush (XtDisplay (pb));
  
  if (event)
    {
      if (event->type == KeyPress)  
	PBG_ClickCount (pb) = 1;
    }
  
  /* If the parent is menu system able, set the lastSelectToplevel before
   * the arm. It's ok if this is recalled later.
   */
  if (menuSTrait != NULL)
    menuSTrait->getLastSelectToplevel (XtParent(pb));
  
  if (PBG_ArmCallback(pb) && !already_armed)
    {
      call_value.reason = XmCR_ARM;
      call_value.event = event;
      call_value.click_count = PBG_ClickCount (pb);
      XtCallCallbackList ((Widget) pb, PBG_ArmCallback(pb), &call_value);
    }
  
  call_value.reason = XmCR_ACTIVATE;
  call_value.event = event;
  call_value.click_count = PBG_ClickCount (pb);
  
  /* If the parent is menu system able, notify it about the select */
  if (menuSTrait != NULL)
    menuSTrait->entryCallback (XtParent(pb), (Widget) pb, &call_value);
  
  if ((! LabG_SkipCallback(pb)) && (PBG_ActivateCallback(pb)))
    {
      XFlush (XtDisplay (pb));
      XtCallCallbackList ((Widget) pb, PBG_ActivateCallback(pb), &call_value);
    }
  
  PBG_Armed(pb) = FALSE;
  
  if (PBG_DisarmCallback(pb))
    {
      XFlush (XtDisplay (pb));
      call_value.reason = XmCR_DISARM;
      XtCallCallbackList ((Widget) pb, PBG_DisarmCallback(pb), &call_value);
    }
  
  if (is_menupane)
    {
      if (torn_has_focus && XtIsSensitive(wid))
	{
	  /* Leave the focus widget in an armed state */
	  PBG_Armed(pb) = TRUE;
	  
	  if (PBG_ArmCallback(pb))
	    {
	      XFlush (XtDisplay (pb));
	      call_value.reason = XmCR_ARM;
	      XtCallCallbackList((Widget) pb, PBG_ArmCallback(pb), &call_value);
	    }
	}
      else if (menuSTrait != NULL)
	{
	menuSTrait->reparentToTearOffShell(XtParent(pb), event);
	PBG_FixTearoff(pb);
	}
    }
  
  /*
   * If the button is still around, show it released, after a short delay.
   * This is done if the button is outside of a menus, or if in a torn
   * off menupane.
   */
  
  if (!is_menupane || torn_has_focus)
    {
      if ((pb->object.being_destroyed == False) && (!(PBG_Timer(pb))))
	{
	PBG_Timer(pb) = 
	  XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)pb),
			  (unsigned long) DELAY_DEFAULT, ArmTimeout,
			  (XtPointer)(pb));
	((XmPushButtonGadget)(pb))->pushbutton.cache->timer_widget = wid;
	}
    }
}

/*ARGSUSED*/
static void 
ArmTimeout(
        XtPointer data,
        XtIntervalId *id )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) data;
  
  PBG_Timer(pb) = 0;
  if (XtIsRealized ((Widget)pb) && XtIsManaged ((Widget)pb))
    {
      if (LabG_IsMenupane(pb))
	{
	  /* When rapidly clicking, the focus may have moved away from this
	   * widget, so check before changing the shadow.
	   */
	  if (XmeFocusIsInShell((Widget)pb) &&
	      (XmGetFocusWidget((Widget)pb) == (Widget)pb))
	    {
	      XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
	      Boolean etched_in = dpy->display.enable_etched_in_menu;

	      /* in a torn off menu, redraw shadows */
	      if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
		  (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
		XmeDrawShadows
		  (XtDisplay (pb), XtWindow (pb),
		   LabG_TopShadowGC(pb),
		   LabG_BottomShadowGC(pb),
		   pb->rectangle.x + pb->gadget.highlight_thickness,
		   pb->rectangle.y + pb->gadget.highlight_thickness,
		   pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		   pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		   pb->gadget.shadow_thickness, 
		   etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
	    }
	}
      else
	{
	  XtExposeProc    expose;
	  _XmProcessLock();
	  expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
		    rect_class.expose;
	  _XmProcessUnlock();
	  (* expose) ((Widget) pb, NULL, (Region) NULL);
	}
      
      XFlush (XtDisplay (pb));
    }
}

/************************************************************************
 *
 *    Disarm
 *
 *     Mark the pushbutton as unarmed (i.e. active).
 *     The callbacks for XmNdisarmCallback are called..
 *
 ************************************************************************/

static void 
Disarm(
        XmPushButtonGadget pb,
        XEvent *event )
{
  XmPushButtonCallbackStruct call_value;
  
  PBG_Armed(pb) = FALSE;
  
  if (PBG_DisarmCallback(pb))
    {
      call_value.reason = XmCR_DISARM;
      call_value.event = event;
      XtCallCallbackList ((Widget) pb, PBG_DisarmCallback(pb), &call_value);
    }
}

/************************************************************************
 *
 *     BtnDown
 *
 *     This function processes a button down occuring on the pushbutton
 *     when it is in a popup, pulldown, or option menu.
 *     Popdown the posted menu.
 *     Turn parent's traversal off.
 *     Mark the pushbutton as armed (i.e. active).
 *     The callbacks for XmNarmCallback are called.
 *
 ************************************************************************/

static void 
BtnDown(
        XmPushButtonGadget pb,
        XEvent *event )
{
  XmPushButtonCallbackStruct call_value;
  Boolean already_armed;
  ShellWidget popup;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
  Boolean etched_in = dpy->display.enable_etched_in_menu;
  XmMenuSystemTrait menuSTrait;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(pb)), XmQTmenuSystem);
  
  /* Popdown other popus that may be up */
  if (!(popup = (ShellWidget)_XmGetRC_PopupPosted(XtParent(pb))))
    {
      if (!XmIsMenuShell(XtParent(XtParent(pb))) && menuSTrait != NULL)
	{
	  /* In case tear off not armed and no grabs in place, do it now.
	   * Ok if already armed and grabbed - nothing done.
	   */
	  menuSTrait->tearOffArm(XtParent(pb));
	}
    }
  
  if (popup)
    {
      if (popup->shell.popped_up && menuSTrait != NULL)
	menuSTrait->popdownEveryone((Widget) popup, event);
    } 
  
  /* Set focus to this button.  This must follow the possible
   * unhighlighting of the CascadeButton else it'll screw up active_child.
   */
  (void)XmProcessTraversal((Widget) pb, XmTRAVERSE_CURRENT);
  /* get the location cursor - get consistent with Gadgets */

  already_armed = PBG_Armed(pb);
  PBG_Armed(pb) = TRUE;

   if (etched_in) {
       Redisplay((Widget) pb, NULL, NULL);
   }
   else
       if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
	   (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
	   XmeDrawShadows (
		    XtDisplay (pb), XtWindow (pb),
		    LabG_TopShadowGC(pb),
		    LabG_BottomShadowGC(pb),
		    pb->rectangle.x + pb->gadget.highlight_thickness,
		    pb->rectangle.y + pb->gadget.highlight_thickness,
		    pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		    pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		    pb->gadget.shadow_thickness, XmSHADOW_OUT);

  if (PBG_ArmCallback(pb) && !already_armed)
    {
      XFlush (XtDisplay (pb));
      
      call_value.reason = XmCR_ARM;
      call_value.event = event;
      XtCallCallbackList ((Widget) pb, PBG_ArmCallback(pb), &call_value);
    }
  _XmRecordEvent (event);
}

/************************************************************************
 *
 *     BtnUp
 *
 *     This function processes a button up occuring on the pushbutton
 *     when it is in a popup, pulldown, or option menu.
 *     Mark the pushbutton as unarmed (i.e. inactive).
 *     The callbacks for XmNactivateCallback are called.
 *     The callbacks for XmNdisarmCallback are called.
 *
 ************************************************************************/

static void 
BtnUp(
        Widget wid,
        XEvent *event )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  Boolean flushDone = False;
  Boolean popped_up = False;
  Boolean is_menupane = LabG_IsMenupane(pb);
  Widget shell = XtParent(XtParent(pb));
  XmMenuSystemTrait menuSTrait;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
  
  PBG_Armed(pb) = FALSE;
  
  if (menuSTrait != NULL)
    {
      if (is_menupane && !XmIsMenuShell(shell))
	popped_up = menuSTrait->popdown((Widget) pb, event);
      else
	popped_up = menuSTrait->buttonPopdown((Widget) pb, event);
    }
  
  _XmRecordEvent(event);
  
  if (popped_up)
    return;
  
  call_value.reason = XmCR_ACTIVATE;
  call_value.event = event;
  call_value.click_count = 1;  
  
  /* if the parent is menu system able, notify it about the select */
  if (menuSTrait != NULL)
    {
      menuSTrait->entryCallback(XtParent(pb), (Widget) pb, &call_value);
      flushDone = True; 
    }
  
  if ((! LabG_SkipCallback(pb)) &&
      (PBG_ActivateCallback(pb)))
    {
      XFlush (XtDisplay (pb));
      flushDone = True;
      XtCallCallbackList ((Widget) pb, PBG_ActivateCallback(pb), &call_value);
    }
  
  if (PBG_DisarmCallback(pb))
    {
      if (!flushDone)
	XFlush (XtDisplay (pb));
      call_value.reason = XmCR_DISARM;
      call_value.event = event;
      XtCallCallbackList ((Widget) pb, PBG_DisarmCallback(pb), &call_value);
    }
  
  /* If the original shell does not indicate an active menu, but rather a
   * tear off pane, leave the button in an armed state.  Also, briefly
   * display the button as depressed to give the user some feedback of
   * the selection.
   */
  if (is_menupane)
    {
      if (!XmIsMenuShell(shell))
	{
	  if (XtIsSensitive((Widget)pb))
	    {
	      XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
	      Boolean etched_in = dpy->display.enable_etched_in_menu;
	      
	      if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
		  (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
		XmeDrawShadows
		  (XtDisplay (pb), XtWindow (pb),
		   LabG_BottomShadowGC(pb),
		   LabG_TopShadowGC(pb),
		   pb->rectangle.x + pb->gadget.highlight_thickness,
		   pb->rectangle.y + pb->gadget.highlight_thickness,
		   pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		   pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		   pb->gadget.shadow_thickness, 
		   etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
	      
	      XFlush (XtDisplay (pb));
	      flushDone = True;
	      
	      /* set timer to redraw the shadow out again */
	      if (pb->object.being_destroyed == False)
		{
		  if (!(PBG_Timer(pb)))
		   {
		    PBG_Timer(pb) = 
		      XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)pb),
				      (unsigned long) DELAY_DEFAULT,
				      ArmTimeout, (XtPointer)(pb));
			((XmPushButtonGadget)(pb))->pushbutton.cache->timer_widget = wid;
		   }
		}
	      
	      PBG_Armed(pb) = TRUE;
	      if (PBG_ArmCallback(pb))
		{	
		  if (!flushDone)
		    XFlush (XtDisplay (pb));
		  call_value.reason = XmCR_ARM;
		  call_value.event = event;
		  XtCallCallbackList((Widget)pb, PBG_ArmCallback(pb),
				     &call_value);
		}
	    }
	}
      else if (menuSTrait != NULL)
	menuSTrait->reparentToTearOffShell(XtParent(pb), event);
    }
  
  _XmSetInDragMode((Widget) pb, False);
  
  /* For the benefit of tear off menus, we must set the focus item
   * to this button.  In normal menus, this would not be a problem
   * because the focus is cleared when the menu is unposted.
   */
  if (!XmIsMenuShell(shell))
    XmProcessTraversal((Widget) pb, XmTRAVERSE_CURRENT);
  PBG_FixTearoff(pb);
}

/************************************************************************
 *
 *  Enter
 *
 ************************************************************************/

static void 
Enter(
        Widget wid,
        XEvent *event )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
  Boolean etched_in = dpy->display.enable_etched_in_menu;
  
  if (LabG_IsMenupane(pb))
    {
      if ((((ShellWidget) XtParent(XtParent(pb)))->shell.popped_up) &&
          _XmGetInDragMode((Widget) pb))
	{
          if (PBG_Armed(pb))
	    return;
	  
	  /* So KHelp event is delivered correctly */
	  _XmSetFocusFlag(XtParent(XtParent(pb)), XmFOCUS_IGNORE, TRUE);
	  XtSetKeyboardFocus(XtParent(XtParent(pb)), (Widget)pb);
	  _XmSetFocusFlag(XtParent(XtParent(pb)), XmFOCUS_IGNORE, FALSE);
	  
	  PBG_Armed(pb) = TRUE;

	  ((XmManagerWidget) XtParent(wid))->manager.active_child = wid;

	  if (etched_in) {
	      Redisplay((Widget) pb, NULL, NULL);
	  }
	  else
	      if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
		  (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
		 XmeDrawShadows
		    (XtDisplay (pb), XtWindow (pb),
		     LabG_TopShadowGC(pb),
		     LabG_BottomShadowGC(pb),
		     pb->rectangle.x + pb->gadget.highlight_thickness,
		     pb->rectangle.y + pb->gadget.highlight_thickness,
		     pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		     pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		     pb->gadget.shadow_thickness, 
		     etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
	  
	  if (PBG_ArmCallback(pb))
	    {
	      XFlush (XtDisplay (pb));
	      
	      call_value.reason = XmCR_ARM;
	      call_value.event = event;
	      XtCallCallbackList((Widget) pb, PBG_ArmCallback(pb), &call_value);
	    }
	}
    }  
  else 
    {
      XtExposeProc expose;
      _XmEnterGadget((Widget) pb, event, NULL, NULL);
      if (PBG_Armed(pb) == TRUE) {	  
	_XmProcessLock();
	expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
	    rect_class.expose;
	_XmProcessUnlock();
	(* (expose)) ((Widget) pb, event, (Region) NULL);
       }
    }
}

/************************************************************************
 *
 *  Leave
 *
 ************************************************************************/
static void 
Leave(
        Widget wid,
        XEvent *event )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  
  if (LabG_IsMenupane(pb))
    {
      if (_XmGetInDragMode((Widget) pb) && PBG_Armed(pb))
	{
	   XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
	   Boolean etched_in = dpy->display.enable_etched_in_menu;

	  PBG_Armed(pb) = FALSE;

	  ((XmManagerWidget) XtParent(wid))->manager.active_child = NULL;

	  if (etched_in) {
	      Redisplay((Widget) pb, NULL, NULL);
	  }

	  XmeDrawHighlight
	    (XtDisplay(pb), XtWindow(pb), 
	     LabG_BackgroundGC(pb), 
	     pb->rectangle.x + pb->gadget.highlight_thickness,
	     pb->rectangle.y + pb->gadget.highlight_thickness,
	     pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
	     pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
	     pb->gadget.shadow_thickness);
	  
	  if (PBG_DisarmCallback(pb))
	    {
	      XFlush (XtDisplay (pb));
	      
	      call_value.reason = XmCR_DISARM;
	      call_value.event = event;
	      XtCallCallbackList ((Widget) pb, PBG_DisarmCallback(pb),
				  &call_value);
	    }
	}
    }
  else 
    {
      _XmLeaveGadget((Widget) pb, event, NULL, NULL);
      
      if (PBG_Armed(pb) == TRUE)
	{
	  XtExposeProc expose;
	  PBG_Armed(pb) = FALSE;
	  _XmProcessLock();
	  expose = ((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
	      rect_class.expose;
	  _XmProcessUnlock();
	  (* (expose)) ((Widget) pb, event, (Region) NULL);
	  PBG_Armed(pb) = TRUE;
	}
    }
}

/*************************************<->*************************************
 *
 *  BorderHighlight 
 *
 *************************************<->***********************************/

static void 
BorderHighlight(
        Widget wid )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  XEvent * event = NULL;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
  Boolean etched_in = dpy->display.enable_etched_in_menu;
  Boolean already_armed = PBG_Armed(pb);

  if (LabG_IsMenupane(pb))
    {
      PBG_Armed(pb) = TRUE;

      if (etched_in) {
	  Redisplay((Widget) pb, NULL, NULL);
      }
      else
	  if ((pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
	      (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))
	      XmeDrawShadows
		  (XtDisplay (pb), XtWindow (pb),
		   LabG_TopShadowGC(pb), LabG_BottomShadowGC(pb),
		   pb->rectangle.x + pb->gadget.highlight_thickness,
		   pb->rectangle.y + pb->gadget.highlight_thickness,
		   pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		   pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		   pb->gadget.shadow_thickness, 
		   etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
      
      if (!already_armed && PBG_ArmCallback(pb))
	{
	  XFlush (XtDisplay (pb));
	  
	  call_value.reason = XmCR_ARM;
	  call_value.event = event;
	  XtCallCallbackList ((Widget) pb, PBG_ArmCallback(pb), &call_value);
	}
      
    }
  else
    {
      DrawBorderHighlight((Widget) pb);
    } 
}


static void
DrawBorderHighlight(
        Widget wid)
{   
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  int x, y, width, height, delta;
  Dimension highlight_width;
  XtEnum default_button_emphasis;
  
  if (!pb->rectangle.width || !pb->rectangle.height)
    return;

  pb->gadget.highlighted = True;
  pb->gadget.highlight_drawn = True;
  
  if ((PBG_DefaultButtonShadowThickness(pb) > 0))
    highlight_width = pb->gadget.highlight_thickness - Xm3D_ENHANCE_PIXEL;
  else
    highlight_width = pb->gadget.highlight_thickness;
  
  if (highlight_width > 0)
    {
      XmDisplay xm_dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));

      default_button_emphasis = xm_dpy->display.default_button_emphasis;

      switch (default_button_emphasis)
	{
	case XmEXTERNAL_HIGHLIGHT:
	  delta = 0;
	  break;

	case XmINTERNAL_HIGHLIGHT:
	  if (PBG_DefaultButtonShadowThickness(pb))
	    delta = Xm3D_ENHANCE_PIXEL + 
	      2 * (PBG_Compatible(pb) ? 
		   PBG_ShowAsDefault(pb) : 
		   PBG_DefaultButtonShadowThickness(pb));
	  else
	    delta = 0;
	  break;

	default:
	  assert(FALSE);
	  return;
	}

      x = pb->rectangle.x + delta;
      y = pb->rectangle.y + delta;
      width = pb->rectangle.width - 2 * delta;
      height = pb->rectangle.height - 2 * delta;

      XmeDrawHighlight(XtDisplay(pb), XtWindow(pb), LabG_HighlightGC(pb),
		       x, y, width, height, highlight_width);
    }
} 

/*************************************<->*************************************
 *
 *  BorderUnhighlight
 *
 *************************************<->***********************************/

static void 
BorderUnhighlight(
        Widget wid )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  XEvent * event = NULL;
  
  if (LabG_IsMenupane(pb))
    {
      XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
      Boolean etched_in = dpy->display.enable_etched_in_menu;
      
      if (!PBG_Armed(pb))
	return;

      PBG_Armed(pb) = FALSE;

      if (etched_in) {
	  Redisplay((Widget) pb, NULL, NULL);
      }

      XmeClearBorder(XtDisplay (pb), XtWindow (pb),
		     pb->rectangle.x + pb->gadget.highlight_thickness,
		     pb->rectangle.y + pb->gadget.highlight_thickness,
		     pb->rectangle.width - 2 * pb->gadget.highlight_thickness,
		     pb->rectangle.height - 2 * pb->gadget.highlight_thickness,
		     pb->gadget.shadow_thickness);
      
      if (PBG_DisarmCallback(pb))
	{
	  XFlush (XtDisplay (pb));

	  call_value.reason = XmCR_DISARM;
	  call_value.event = event;
	  XtCallCallbackList((Widget) pb, PBG_DisarmCallback(pb), &call_value);
	}
    }
  else
    {
      int border = pb->gadget.highlight_thickness - Xm3D_ENHANCE_PIXEL;
      XmDisplay xm_dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
      XtEnum default_button_emphasis = xm_dpy->display.default_button_emphasis;

      switch (default_button_emphasis)
	{
	case XmINTERNAL_HIGHLIGHT:
	  if (PBG_DefaultButtonShadowThickness(pb) && (border > 0))
	    {
	      int x, y, width, height, delta;

	      pb->gadget.highlighted = False;
	      pb->gadget.highlight_drawn = False;

	      delta = Xm3D_ENHANCE_PIXEL + 
		2 * (PBG_Compatible(pb) ? 
		     PBG_ShowAsDefault(pb) :
		     PBG_DefaultButtonShadowThickness(pb));
	    
	      x = pb->rectangle.x + delta;
	      y = pb->rectangle.y + delta;
	      width = pb->rectangle.width - 2 * delta;
	      height = pb->rectangle.height - 2 * delta;

	      XmeClearBorder(XtDisplay(pb), XtWindow(pb),
			     x, y, width, height, border);
	      break;
	    }
	  /* else fall through to XmEXTERNAL_HIGHLIGHT. */

	case XmEXTERNAL_HIGHLIGHT:
	  (*(xmGadgetClassRec.gadget_class.border_unhighlight)) (wid);
	  break;

	default:
	  assert(FALSE);
	  return;
	}
    } 
}

/*************************************<->*************************************
 *
 *  KeySelect
 *
 *  If the menu system traversal is enabled, do an activate and disarm
 *
 *************************************<->***********************************/

static void 
KeySelect(
        Widget wid,
        XEvent *event )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmPushButtonCallbackStruct call_value;
  XmMenuSystemTrait menuSTrait;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
  
  if (!_XmIsEventUnique(event))
    return;
  
  if (!_XmGetInDragMode((Widget) pb))
    {
      
      PBG_Armed(pb) = FALSE;
      
      if (menuSTrait != NULL)
	menuSTrait->buttonPopdown(XtParent(pb), event);
      
      _XmRecordEvent(event);
      
      call_value.reason = XmCR_ACTIVATE;
      call_value.event = event;
      
      /* if the parent is menu system able, notify it about the select */
      if (menuSTrait != NULL)
	{
	  menuSTrait->entryCallback(XtParent(pb), (Widget) pb, &call_value);
	}
      
      if ((! LabG_SkipCallback(pb)) &&
	  (PBG_ActivateCallback(pb)))
	{
	  XFlush (XtDisplay (pb));
	  XtCallCallbackList ((Widget) pb, PBG_ActivateCallback(pb),
			      &call_value);
	}
      
      if (menuSTrait != NULL)
	menuSTrait->reparentToTearOffShell(XtParent(pb), event);
    }
}

/***********************************************************
 *
 *  ClassInitialize
 *
 ************************************************************/

static void 
ClassInitialize( void )
{
  Cardinal	 wc_num_res, sc_num_res;
  XtResource	 *merged_list;
  int		 i, j;
  XtResourceList uncompiled;
  Cardinal	 num;
  
  /*
   * Label's and Pushbutton's resource lists are being merged into one
   * and assigned to xmPushButtonGCacheObjClassRec.  This is for performance
   * reasons, since instead of two calls to XtGetSubResources(),
   * XtGetSubvaluse() and XtSetSubvalues() for both the superclass and
   * the widget class, now we have just one call with a merged resource list.
   * NOTE: At this point the resource lists for Label and Pushbutton do
   * have unique entries, but if there are resources in the superclass
   * that are being overwritten by the subclass then the merged_lists
   * need to be created differently.
   */
  
  wc_num_res = xmPushButtonGCacheObjClassRec.object_class.num_resources;
  sc_num_res = xmLabelGCacheObjClassRec.object_class.num_resources;
  
  merged_list = (XtResource *)
    XtMalloc((sizeof(XtResource) * (wc_num_res + sc_num_res)));
  
  _XmTransformSubResources(xmLabelGCacheObjClassRec.object_class.resources,
                           sc_num_res, &uncompiled, &num);
  
  for (i = 0; i < num; i++)
    merged_list[i] = uncompiled[i];
  XtFree((char *)uncompiled);
  
  for (i = 0, j = num; i < wc_num_res; i++, j++)
    merged_list[j] = xmPushButtonGCacheObjClassRec.object_class.resources[i];
  
  xmPushButtonGCacheObjClassRec.object_class.resources = merged_list;
  xmPushButtonGCacheObjClassRec.object_class.num_resources =
    wc_num_res + sc_num_res;
  
  PushBGClassExtensionRec.record_type = XmQmotif;
}

/************************************************************************
 *
 *  ClassPartInitialize
 *     Set up the fast subclassing for the widget
 *
 ************************************************************************/

static void 
ClassPartInitialize(
        WidgetClass wc )
{
  _XmFastSubclassInit (wc, XmPUSH_BUTTON_GADGET_BIT);
  
  /* Install the menu savvy trait record,  copying fields from XmLabelG */
  _XmLabelGCloneMenuSavvy (wc, &MenuSavvyRecord);

  /* Install the activatable trait for all subclasses */
  XmeTraitSet((XtPointer) wc, XmQTactivatable, (XtPointer) &pushButtonGAT);
  
  /* Install the takesDefault trait for all subclasses */
  XmeTraitSet((XtPointer) wc, XmQTtakesDefault, (XtPointer) &pushButtonGTDT);
}

/************************************************************************
 *
 *  SecondaryObjectCreate
 *
 ************************************************************************/

/* ARGSUSED */
static void 
SecondaryObjectCreate(
        Widget req,
        Widget new_w,
        ArgList args,
        Cardinal *num_args )
{
  XmBaseClassExt *cePtr;
  XmWidgetExtData extData;
  WidgetClass	  wc;
  Cardinal	  size;
  XtPointer	  newSec, reqSec;
  
  _XmProcessLock();
  cePtr = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);
  wc = (*cePtr)->secondaryObjectClass;
  size = wc->core_class.widget_size;
  
  newSec = _XmExtObjAlloc(size);
  reqSec = _XmExtObjAlloc(size);  
  _XmProcessUnlock();
  
  /*
   *  Update pointers in instance records now so references to resources
   * in the cache record will be valid for use in CallProcs.
   * CallProcs are invoked by XtGetSubresources().
   */
  
  LabG_Cache(new_w) = &(((XmLabelGCacheObject)newSec)->label_cache);
  LabG_Cache(req)   = &(((XmLabelGCacheObject)reqSec)->label_cache);
  PBG_Cache(new_w)  = &(((XmPushButtonGCacheObject)newSec)->pushbutton_cache);
  PBG_Cache(req)    = &(((XmPushButtonGCacheObject)reqSec)->pushbutton_cache);
  
  
  /*
   * Since the resource lists for label and pushbutton were merged at
   * ClassInitialize time we need to make only one call to 
   * XtGetSubresources()
   */
  
  XtGetSubresources (new_w, newSec, NULL, NULL,
		     wc->core_class.resources,
		     wc->core_class.num_resources,
		     args, *num_args);
  
  
  extData = (XmWidgetExtData) XtCalloc(1, sizeof(XmWidgetExtDataRec));
  extData->widget = (Widget)newSec;
  extData->reqWidget = (Widget)reqSec;
  
  ((XmPushButtonGCacheObject)newSec)->ext.extensionType = XmCACHE_EXTENSION;
  ((XmPushButtonGCacheObject)newSec)->ext.logicalParent = new_w;
  
  _XmPushWidgetExtData(new_w, extData,
		       ((XmPushButtonGCacheObject)newSec)->ext.extensionType);
  memcpy(reqSec, newSec, size);
}

/************************************************************************
 *
 *  InitializePosthook
 *
 ************************************************************************/

/*ARGSUSED*/
static void 
InitializePrehook(
        Widget req,
        Widget new_w,
        ArgList args,
        Cardinal *num_args )
{
  /* CR 2990: Use XmNbuttonFontList as the default. */
  if (LabG_Font(new_w) == NULL)
    LabG_Font(new_w) = XmeGetDefaultRenderTable (new_w, XmBUTTON_FONTLIST);
}

/************************************************************************
 *
 *  InitializePosthook
 *
 ************************************************************************/

/*ARGSUSED*/
static void 
InitializePosthook(
        Widget req,
        Widget new_w,
        ArgList args,
        Cardinal *num_args )
{
  XmWidgetExtData     ext;
  XmPushButtonGadget  pbw = (XmPushButtonGadget)new_w;
  
  /*
   * - register parts in cache.
   * - update cache pointers
   * - and free req
   */
  
  _XmProcessLock();
  LabG_Cache(pbw) = (XmLabelGCacheObjPart *)
    _XmCachePart(LabG_ClassCachePart(pbw),
		 (XtPointer) LabG_Cache(pbw),
		 sizeof(XmLabelGCacheObjPart));
  
  PBG_Cache(pbw) = (XmPushButtonGCacheObjPart *)
    _XmCachePart(PBG_ClassCachePart(pbw),
		 (XtPointer) PBG_Cache(pbw),
		 sizeof(XmPushButtonGCacheObjPart));
  
  /*
   * We might want to break up into per-class work that gets explicitly
   * chained. For right now, each class has to replicate all
   * superclass logic in hook routine.
   */
  
  /*
   * free the req subobject used for comparisons
   */
  _XmPopWidgetExtData ((Widget) pbw, &ext, XmCACHE_EXTENSION);
  _XmExtObjFree ((XtPointer)ext->widget);
  _XmExtObjFree ((XtPointer)ext->reqWidget);
  _XmProcessUnlock();
  XtFree((char *) ext);
}
      
/*************************************<->*************************************
 *
 *  Initialize 
 *
 *************************************<->***********************************/

/*ARGSUSED*/
static void 
Initialize(
        Widget rw,
        Widget nw,
        ArgList args,
        Cardinal *num_args )
{
  XmPushButtonGadget request = (XmPushButtonGadget) rw;
  XmPushButtonGadget new_w = (XmPushButtonGadget) nw;
  XmGadgetPart	    *pbgadget;
  int 		     increase;
  int		     adjustment = 0;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(new_w));
  Boolean etched_in = dpy->display.enable_etched_in_menu;
  
  if (PBG_MultiClick(new_w) == XmINVALID_MULTICLICK)
    {
      if (LabG_IsMenupane(new_w))
	PBG_MultiClick(new_w) = XmMULTICLICK_DISCARD;
      else
	PBG_MultiClick(new_w) = XmMULTICLICK_KEEP;
    }
  
  /* If menuProcs is not set up yet, try again. */
  _XmProcessLock();
  if (xmLabelGadgetClassRec.label_class.menuProcs == (XmMenuProc)NULL)
    xmLabelGadgetClassRec.label_class.menuProcs =
      (XmMenuProc) _XmGetMenuProcContext();
  _XmProcessUnlock();
  
  
  PBG_Armed(new_w) = FALSE;
  PBG_Timer(new_w) = 0;
  ((XmPushButtonGadget)(new_w))->pushbutton.cache->timer_widget = (Widget)NULL;
  
  /*
   * Fix to introduce Resource XmNdefaultBorderWidth and compatibility
   *	variable.
   *  if defaultBorderWidth > 0, the program knows about this resource
   *	and is therefore a Motif 1.1 program; otherwise it is a Motif 1.0
   *      program and old semantics of XmNshowAsDefault prevails.
   *  - Sankar 2/1/90.
   */
  
  if (PBG_DefaultButtonShadowThickness(new_w) > 0)
    PBG_Compatible (new_w) = False;
  else
    PBG_Compatible (new_w) = True; 
  
  if (PBG_Compatible (new_w)) 
    PBG_DefaultButtonShadowThickness(new_w) = PBG_ShowAsDefault(new_w);
  
#ifdef DEFAULT_GLYPH_PIXMAP
  if (_XmGetDefaultGlyphPixmap(XtScreen(nw),NULL,NULL) != XmUNSPECIFIED_PIXMAP)
    {
      PBG_Compatible (new_w) = False;
      PBG_DefaultButtonShadowThickness(new_w) = 0;
    }
#endif
  
  /* No unarm_pixmap but do have an arm_pixmap, use that. */
  if ((LabG_Pixmap(new_w) == XmUNSPECIFIED_PIXMAP) &&
      (PBG_ArmPixmap(new_w) != XmUNSPECIFIED_PIXMAP))
    {
      XtWidgetProc resize;
      LabG_Pixmap(new_w) = PBG_ArmPixmap(new_w);
      if (request->rectangle.width == 0)
	new_w->rectangle.width = 0;
      if (request->rectangle.height == 0)
	new_w->rectangle.height = 0;
      
      _XmCalcLabelGDimensions((Widget) new_w);
      _XmProcessLock();
      resize = xmLabelGadgetClassRec.rect_class.resize;
      _XmProcessUnlock();
      (* resize) ((Widget) new_w);
    }
  
    if ((LabG_LabelType(new_w) == XmPIXMAP) &&
       (PBG_ArmPixmap(new_w) != XmUNSPECIFIED_PIXMAP))
    {
      if (request->rectangle.width == 0)
	new_w->rectangle.width = 0;
      if (request->rectangle.height == 0)
	new_w->rectangle.height = 0;
      SetPushButtonSize(new_w);
    }
  
  PBG_UnarmPixmap(new_w) = LabG_Pixmap(new_w);
  
  
  if (PBG_DefaultButtonShadowThickness(new_w))
    { 
      /*
       * Special hack for 3d enhancement of location cursor highlight.
       *  - Make the box bigger. During drawing of location cursor
       *    make it smaller.  See in Primitive.c
       *  May be we should use the macro: G_HighLightThickness(pbgadget);
       */
      
      pbgadget = (XmGadgetPart *) (&(new_w->gadget));
      pbgadget->highlight_thickness += Xm3D_ENHANCE_PIXEL;
      adjustment += Xm3D_ENHANCE_PIXEL;
      
      increase =  2 * PBG_DefaultButtonShadowThickness(new_w) +
	new_w->gadget.shadow_thickness + adjustment;
      
      /* Add the increase to the rectangle to compensate for extra space */
      if (increase != 0)
	{
	  LabG_MarginLeft(new_w) += increase;
	  LabG_MarginRight(new_w) += increase;
	  LabG_TextRect_x(new_w) += increase;
	  new_w->rectangle.width += (increase  << 1);
	  
	  LabG_MarginTop(new_w)   += increase;
	  LabG_MarginBottom(new_w) += increase;
	  LabG_TextRect_y(new_w) += increase;
	  new_w->rectangle.height += (increase  << 1);
	}
    }
  
  if (LabG_IsMenupane(new_w))
    {
      new_w->gadget.traversal_on = TRUE;
    }
  
  
  if (PBG_ArmColor(new_w) == INVALID_PIXEL)
    {
      XrmValue value;
      
      value.size = sizeof(Pixel);
      
      _XmSelectColorDefault ((Widget)new_w,
			     XtOffsetOf(struct _XmPushButtonGCacheObjRec, 
					pushbutton_cache.arm_color),
			    &value);
      memcpy((char*) &PBG_ArmColor(new_w), value.addr, value.size);
    }
  
  /* Get the background fill GC */
  if (! LabG_IsMenupane(new_w) || etched_in)
    {
	GetFillGC (new_w);
	new_w->label.fill_bg_box = _XmALWAYS_FILL_BG_BOX;
	if (LabG_BackgroundGC(new_w))
          XtReleaseGC (XtParent (new_w), LabG_BackgroundGC(new_w));
	_XmLabelSetBackgroundGC((XmLabelGadget) new_w);
    }
  else
      PBG_FillGc(new_w) = 0;
  
  /* Set to zero so _XmPushBCacheCompare will behave correctly. */
  PBG_BackgroundGc(new_w) = 0;
  
  /*  Initialize the interesting input types.  */
  new_w->gadget.event_mask = XmARM_EVENT | XmACTIVATE_EVENT | XmHELP_EVENT |
    XmFOCUS_IN_EVENT | XmFOCUS_OUT_EVENT | XmENTER_EVENT | XmLEAVE_EVENT
      | XmMULTI_ARM_EVENT |  XmMULTI_ACTIVATE_EVENT | XmBDRAG_EVENT;
}

/************************************************************************
 *
 *  GetFillGC
 *     Get the graphics context used for filling in background of button.
 *
 ************************************************************************/

static void 
GetFillGC(
        XmPushButtonGadget pb )
{
  XGCValues values;
  XtGCMask  valueMask;
  XmManagerWidget mw = (XmManagerWidget) XtParent(pb);
  
  valueMask = GCForeground | GCBackground | GCFillStyle;
 
  values.foreground = PBG_ArmColor(pb);
  values.background = LabG_Background(pb);
  values.fill_style = FillSolid;
  
  PBG_FillGc(pb) = XtGetGC ((Widget) mw, valueMask, &values);
}

/************************************************************************
 *
 *  SetValuesPrehook
 *
 ************************************************************************/

/*ARGSUSED*/
static Boolean 
SetValuesPrehook(
        Widget oldParent,
        Widget refParent,
        Widget newParent,
        ArgList args,
        Cardinal *num_args )
{
  XmWidgetExtData             extData;
  XmBaseClassExt              *cePtr;
  WidgetClass                 ec;
  Cardinal                    size;
  XmPushButtonGCacheObject    newSec, reqSec;

  _XmProcessLock();
  cePtr = _XmGetBaseClassExtPtr(XtClass(newParent), XmQmotif);
  ec = (*cePtr)->secondaryObjectClass;
  size = ec->core_class.widget_size;
  
  newSec = (XmPushButtonGCacheObject)_XmExtObjAlloc(size);
  reqSec = (XmPushButtonGCacheObject)_XmExtObjAlloc(size);
  _XmProcessUnlock();
  
  newSec->object.self = (Widget)newSec;
  newSec->object.widget_class = ec;
  newSec->object.parent = XtParent(newParent);
  newSec->object.xrm_name = newParent->core.xrm_name;
  newSec->object.being_destroyed = False;
  newSec->object.destroy_callbacks = NULL;
  newSec->object.constraints = NULL;
  
  newSec->ext.logicalParent = newParent;
  newSec->ext.extensionType = XmCACHE_EXTENSION;
  
  memcpy(&(newSec->label_cache), 
	 LabG_Cache(newParent), 
	 sizeof(XmLabelGCacheObjPart));
  
  memcpy(&(newSec->pushbutton_cache), 
	 PBG_Cache(newParent), 
	 sizeof(XmPushButtonGCacheObjPart));
  
  extData = (XmWidgetExtData) XtCalloc(1, sizeof(XmWidgetExtDataRec));
  extData->widget = (Widget)newSec;
  extData->reqWidget = (Widget)reqSec;    
  _XmPushWidgetExtData(newParent, extData, XmCACHE_EXTENSION);
  
  /*
   * Since the resource lists for label and pushbutton were merged at
   * ClassInitialize time we need to make only one call to
   * XtSetSubvalues()
   */
  
  XtSetSubvalues((XtPointer)newSec,
		 ec->core_class.resources,
		 ec->core_class.num_resources,
		 args, *num_args);
  
  
  memcpy((XtPointer)reqSec, (XtPointer)newSec, size);    
  
  LabG_Cache(newParent) = &(((XmLabelGCacheObject)newSec)->label_cache);
  LabG_Cache(refParent) = 
    &(((XmLabelGCacheObject)extData->reqWidget)->label_cache);
  
  PBG_Cache(newParent) =
    &(((XmPushButtonGCacheObject)newSec)->pushbutton_cache);
  PBG_Cache(refParent) =
    &(((XmPushButtonGCacheObject)extData->reqWidget)->pushbutton_cache);
  
  _XmExtImportArgs((Widget)newSec, args, num_args);
  
  /* CR 2990: Use XmNbuttonFontList as the default. */
  if (LabG_Font(newParent) == NULL)
    LabG_Font(newParent) = 
      XmeGetDefaultRenderTable (newParent, XmBUTTON_FONTLIST);
  
  return FALSE;
}

/************************************************************************
 *
 *  GetValuesPrehook
 *
 ************************************************************************/

/*ARGSUSED*/
static void 
GetValuesPrehook(
        Widget newParent,
        ArgList args,
        Cardinal *num_args )
{   
  XmWidgetExtData             extData;
  XmBaseClassExt              *cePtr;
  WidgetClass                 ec;
  XmPushButtonGCacheObject    newSec;
  Cardinal                    size;

  _XmProcessLock();
  cePtr = _XmGetBaseClassExtPtr(XtClass(newParent), XmQmotif);
  ec = (*cePtr)->secondaryObjectClass;
  size = ec->core_class.widget_size;
  
  newSec = (XmPushButtonGCacheObject)_XmExtObjAlloc(size);
  _XmProcessUnlock();
  
  newSec->object.self = (Widget)newSec;
  newSec->object.widget_class = ec;
  newSec->object.parent = XtParent(newParent);
  newSec->object.xrm_name = newParent->core.xrm_name;
  newSec->object.being_destroyed = False;
  newSec->object.destroy_callbacks = NULL;
  newSec->object.constraints = NULL;
  
  newSec->ext.logicalParent = newParent;
  newSec->ext.extensionType = XmCACHE_EXTENSION;
  
  memcpy(&(newSec->label_cache),
	 LabG_Cache(newParent), 
	 sizeof(XmLabelGCacheObjPart));
  
  memcpy(&(newSec->pushbutton_cache),
	 PBG_Cache(newParent), 
	 sizeof(XmPushButtonGCacheObjPart));
  
  extData = (XmWidgetExtData) XtCalloc(1, sizeof(XmWidgetExtDataRec));
  extData->widget = (Widget)newSec;
  _XmPushWidgetExtData(newParent, extData, XmCACHE_EXTENSION);
  
  /* Note that if a resource is defined in the superclass's as well as a
   * subclass's resource list and if a NULL is passed in as the third
   * argument to XtSetArg, then when a GetSubValues() is done by the
   * superclass the NULL is replaced by a value. Now when the subclass
   * gets the arglist it doesn't see a NULL and thinks it's an address
   * it needs to stuff a value into and sure enough it breaks. 
   * This means that we have to pass the same arglist with the NULL to
   * both the superclass and subclass and propagate the values up once
   * the XtGetSubValues() are done.
   */
  
  /*
   * Since the resource lists for label and pushbutton were merged at
   * ClassInitialize time we need to make only one call to
   * XtGetSubvalues()
   */
  
  XtGetSubvalues((XtPointer)newSec,
		 ec->core_class.resources,
		 ec->core_class.num_resources,
		 args, *num_args);
  
  _XmExtGetValuesHook((Widget)newSec, args, num_args);
}

/************************************************************************
 *
 *  GetValuesPosthook
 *
 ************************************************************************/

/*ARGSUSED*/
static void 
GetValuesPosthook(
        Widget new_w,
        ArgList args,
        Cardinal *num_args )
{
  XmWidgetExtData ext;
  
  _XmPopWidgetExtData(new_w, &ext, XmCACHE_EXTENSION);
  
  _XmProcessLock();
  _XmExtObjFree((XtPointer)ext->widget);
  _XmProcessUnlock();
  XtFree((char *) ext);
}

/************************************************************************
 *
 *  SetValuesPosthook
 *
 ************************************************************************/

/*ARGSUSED*/
static Boolean 
SetValuesPosthook(
        Widget current,
        Widget req,
        Widget new_w,
        ArgList args,
        Cardinal *num_args )
{
  XmWidgetExtData                  ext;
  
  /*
   * - register parts in cache.
   * - update cache pointers
   * - and free req
   */
  /* assign if changed! */
  _XmProcessLock();
  if (!_XmLabelCacheCompare((XtPointer)LabG_Cache(new_w), 
                            (XtPointer)LabG_Cache(current)))
    {
      _XmCacheDelete((XtPointer) LabG_Cache(current));
      LabG_Cache(new_w) = (XmLabelGCacheObjPart *)
	_XmCachePart(LabG_ClassCachePart(new_w),
		     (XtPointer) LabG_Cache(new_w),
		     sizeof(XmLabelGCacheObjPart));
    }
  else
    LabG_Cache(new_w) = LabG_Cache(current);
  
  
  /* assign if changed! */
  if (!_XmPushBCacheCompare((XtPointer)PBG_Cache(new_w),
		            (XtPointer)PBG_Cache(current)))
    {
      _XmCacheDelete((XtPointer) PBG_Cache(current));  
      PBG_Cache(new_w) = (XmPushButtonGCacheObjPart *)
	_XmCachePart(PBG_ClassCachePart(new_w),
		     (XtPointer) PBG_Cache(new_w),
		     sizeof(XmPushButtonGCacheObjPart));
    }
  else
    PBG_Cache(new_w) = PBG_Cache(current);
  
  _XmPopWidgetExtData(new_w, &ext, XmCACHE_EXTENSION);
  
  _XmExtObjFree((XtPointer)ext->widget);
  _XmExtObjFree((XtPointer)ext->reqWidget);
  _XmProcessUnlock();

  XtFree((char *) ext);
  
  return FALSE;
}

/*************************************<->*************************************
 *
 *  SetValues(current, request, new_w)
 *
 *   Description:
 *   -----------
 *     This is the set values procedure for the pushbutton class.  It is
 *     called last (the set values rtnes for its superclasses are called
 *     first).
 *
 *
 *   Inputs:
 *   ------
 *    current = original widget;
 *    request = original copy of request;
 *    new_w = copy of request which reflects changes made to it by
 *          set values procedures of its superclasses;
 *    last = TRUE if this is the last set values procedure to be called.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

/*ARGSUSED*/
static Boolean 
SetValues(
        Widget cw,
        Widget rw,
        Widget nw,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
  XmPushButtonGadget current = (XmPushButtonGadget) cw;
  XmPushButtonGadget request = (XmPushButtonGadget) rw;
  XmPushButtonGadget new_w = (XmPushButtonGadget) nw;
  int		     increase;
  Boolean	     flag = FALSE;    /* our return value */
  XmManagerWidget    newmw = (XmManagerWidget) XtParent(new_w);
  int		     adjustment;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(new_w));
  Boolean etched_in = dpy->display.enable_etched_in_menu;

  /*
   * Fix to introduce Resource XmNdefaultBorderWidth and compatibility
   *      variable.
   *  if  defaultBorderWidth of the current and new are different, then
   *  the programmer is setting the resource XmNdefaultBorderWidth; i.e. it
   *  defaultBorderWidth > 0, the program knows about this resource
   *  a Motif 1.1 program; otherwise it is a Motif 1.0
   *      program and old semantics of XmNshowAsDefault prevails.
   *     Note if (PBG_ShowAsDefault(gadget) == 0) then we are NOT currently 
   *      drawing defaultBorderWidth; if it is > 0, we should be drawing
   *	    the shadow in defaultorderWidth; 
   *  - Sankar 2/1/90.
   */
  
  
  if (PBG_DefaultButtonShadowThickness(new_w) !=
      PBG_DefaultButtonShadowThickness(current))
    PBG_Compatible (new_w) = False;
  
  if (PBG_Compatible (new_w))
    PBG_DefaultButtonShadowThickness(new_w) = PBG_ShowAsDefault(new_w);
  
  adjustment = AdjustHighLightThickness (new_w, current);
  
  if (PBG_DefaultButtonShadowThickness(new_w) !=
      PBG_DefaultButtonShadowThickness(current))
    {
      if (PBG_DefaultButtonShadowThickness(new_w) >
	  PBG_DefaultButtonShadowThickness(current))
	{
	  if (PBG_DefaultButtonShadowThickness(current) > 0)
            increase =  (2 * PBG_DefaultButtonShadowThickness(new_w) +
                         new_w->gadget.shadow_thickness) -
			   (2 * PBG_DefaultButtonShadowThickness(current) +
			    current->gadget.shadow_thickness);
	  else
            increase =  (2 * PBG_DefaultButtonShadowThickness(new_w) +
                         new_w->gadget.shadow_thickness);
	}
      else
	{
	  if (PBG_DefaultButtonShadowThickness(new_w) > 0)
            increase = - ((2 * PBG_DefaultButtonShadowThickness(current) +
                           current->gadget.shadow_thickness) -
                          (2 * PBG_DefaultButtonShadowThickness(new_w) +
                           new_w->gadget.shadow_thickness));
	  else
            increase = - (2 * PBG_DefaultButtonShadowThickness(current) +
                          current->gadget.shadow_thickness);
	}
      
      increase += adjustment;
      
      if (LabG_RecomputeSize(new_w) || request->rectangle.width == 0)
	{
	  LabG_MarginLeft(new_w) += increase;
	  LabG_MarginRight(new_w) += increase;
	  new_w->rectangle.width += (increase << 1);
	  flag = TRUE;
	}
      else if (increase != 0)
	{  
	  /* add the change to the rectangle */
	  LabG_MarginLeft(new_w) += increase;
	  LabG_MarginRight(new_w) += increase;
	  new_w->rectangle.width += (increase << 1);
	  flag = TRUE;
	}
      
      if (LabG_RecomputeSize(new_w) || request->rectangle.height == 0)
	{
	  LabG_MarginTop(new_w) += increase;
	  LabG_MarginBottom(new_w) += increase;
	  new_w->rectangle.height +=  (increase << 1);
	  flag = TRUE;
	}
      else if (increase != 0)
	{ 
	  /* add the change to the rectangle */
	  LabG_MarginTop(new_w)  += increase;
	  LabG_MarginBottom(new_w) += increase;
	  new_w->rectangle.height += (increase << 1);
	  flag = TRUE;
	}
      
#ifndef XTHREADS
      _XmReCacheLabG((Widget) new_w);
#endif
    }
  
  if ((PBG_ArmPixmap(new_w) != PBG_ArmPixmap(current)) &&
      (LabG_LabelType(new_w) == XmPIXMAP) && (PBG_Armed(new_w))) 
    flag = TRUE;
  
  /* No unarm_pixmap but do have an arm_pixmap, use that. */
  if ((LabG_Pixmap(new_w) == XmUNSPECIFIED_PIXMAP) &&
      (PBG_ArmPixmap(new_w) != XmUNSPECIFIED_PIXMAP))
    {
      LabG_Pixmap(new_w) = PBG_ArmPixmap(new_w);
      if (LabG_RecomputeSize(new_w) &&
          request->rectangle.width == current->rectangle.width)
	new_w->rectangle.width = 0;
      if (LabG_RecomputeSize(new_w) &&
          request->rectangle.height == current->rectangle.height)
	new_w->rectangle.width = 0;
      
      _XmCalcLabelGDimensions((Widget) new_w);
      {	  
	XtWidgetProc resize;
	_XmProcessLock();
	resize = xmLabelGadgetClassRec.rect_class.resize;
	_XmProcessUnlock();
	(* resize) ((Widget) new_w);
      }
    }
  
  if (LabG_Pixmap(new_w) != LabG_Pixmap(current))
    {
      PBG_UnarmPixmap(new_w) = LabG_Pixmap(new_w);
      if ((LabG_LabelType(new_w) == XmPIXMAP) && (!PBG_Armed(new_w)))
	flag = TRUE;
    }
  if ((LabG_LabelType(new_w) == XmPIXMAP) &&
      (PBG_ArmPixmap(new_w) != PBG_ArmPixmap(current)))
    {
      if ((LabG_RecomputeSize(new_w)))
	{
          if (request->rectangle.width == current->rectangle.width)
	    new_w->rectangle.width = 0;
          if (request->rectangle.height == current->rectangle.height)
	    new_w->rectangle.height = 0;
	}
      SetPushButtonSize(new_w);
      flag = TRUE;
    }
  
  if ((PBG_FillOnArm(new_w) != PBG_FillOnArm(current)) &&
      (PBG_Armed(new_w) == TRUE))
    flag = TRUE;
  
  if (! LabG_IsMenupane(new_w) || etched_in) {
      /* See if the GC need to be regenerated and widget redrawn. */
      if (PBG_ArmColor(new_w) != PBG_ArmColor(current))
	{
	    if (PBG_Armed(new_w)) flag = TRUE;  /* see PIR 5091 */
	    XtReleaseGC ((Widget) newmw, PBG_FillGc(new_w));
	    GetFillGC (new_w);
	}
      
      /* Sun Apr 18 17:50:27 1993
       * Currently not using PBG_BackgroundGc().
       */
  }

  /* Initialize the interesting input types. */
  new_w->gadget.event_mask = XmARM_EVENT | XmACTIVATE_EVENT | XmHELP_EVENT |
    XmFOCUS_IN_EVENT | XmFOCUS_OUT_EVENT | XmENTER_EVENT | XmLEAVE_EVENT
      | XmMULTI_ARM_EVENT | XmMULTI_ACTIVATE_EVENT | XmBDRAG_EVENT;
  
  /* OSF Fix pir 3469 */
  if ((flag == False) && XtIsRealized((Widget) new_w))
    {
      /* No size change has taken place. */
      if ((PBG_ShowAsDefault(current) != 0) &&
	  (PBG_ShowAsDefault(new_w) == 0))
	EraseDefaultButtonShadow (new_w);
      
      if ((PBG_ShowAsDefault(current) == 0) &&
	  (PBG_ShowAsDefault(new_w) != 0))
	DrawDefaultButtonShadow (new_w);
    }
  
  return flag;
}

/************************************************************************
 *
 *  Help
 *     This function processes Function Key 1 press occuring on the PushButton.
 *
 ************************************************************************/

static void 
Help(
        XmPushButtonGadget pb,
        XEvent *event )
{
  Boolean is_menupane = LabG_IsMenupane(pb);
  XmMenuSystemTrait menuSTrait;
  
  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(pb)), XmQTmenuSystem);
  
  if (is_menupane && menuSTrait != NULL)
    menuSTrait->buttonPopdown(XtParent(pb), event);
  
  _XmSocorro((Widget) pb, event, NULL, NULL);
  
  if (is_menupane && menuSTrait != NULL)
    menuSTrait->reparentToTearOffShell(XtParent(pb), event);
}

/************************************************************************
 *
 *  Destroy
 *	Clean up allocated resources when the widget is destroyed.
 *
 ************************************************************************/

static void 
Destroy(
        Widget wid )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  XmManagerWidget mw = (XmManagerWidget) XtParent(pb);
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
  Boolean etched_in = dpy->display.enable_etched_in_menu;
  
   if (PBG_Timer(pb) &&
       ((XmPushButtonGadget)(pb))->pushbutton.cache->timer_widget == wid)
       {
       XtRemoveTimeOut (PBG_Timer(pb));
       PBG_Timer(pb) = (XtIntervalId)0;
       }
  
  /* BEGIN OSF Fix pir 2746 */
  if (!LabG_IsMenupane(pb) || etched_in) {
      XtReleaseGC ((Widget) mw, PBG_FillGc(pb));

      /*
       *  Sun Apr 18 17:51:25 1993
       *  Currently not using PBG_BackgroundGc
       */

      /* END OSF Fix pir 2746 */
  }

  _XmProcessLock();
  _XmCacheDelete((XtPointer) PBG_Cache(pb));
  _XmProcessUnlock();
}

/**************************************************************************
 *
 * Resize(w)
 *
 **************************************************************************/

static void 
Resize(
        Widget w )
{
  register XmPushButtonGadget pb = (XmPushButtonGadget) w;
  
  if (LabG_IsPixmap(w)) 
    SetPushButtonSize(pb);
  else {
    XtWidgetProc resize;
    _XmProcessLock();
    resize = xmLabelGadgetClassRec.rect_class.resize;
    _XmProcessUnlock();
    (* resize) ((Widget) pb);
  }
}

/*ARGSUSED*/
static void 
ActivateCommonG(
        XmPushButtonGadget pb,
        XEvent *event,
        Mask event_mask )
{
  if (LabG_IsMenupane(pb))
    {
      if (event->type == ButtonRelease)
	BtnUp ((Widget) pb, event);
      else  /* assume KeyRelease */
	KeySelect ((Widget) pb, event);
    }
  else
    {
      if (event->type == ButtonRelease)
	{
	  Activate (pb, event);
	  Disarm (pb, event);
	}
      else  /* assume KeyPress or KeyRelease */
        (* (((XmPushButtonGadgetClassRec *)(pb->object.widget_class))->
	    gadget_class.arm_and_activate))
	  ((Widget) pb, event, NULL, NULL);
    }
}

/*********************************************************
 *   Functions for manipulating Secondary Resources.
 *********************************************************/

/*
 * GetPushBGSecResData()
 *    Create a XmSecondaryResourceDataRec for each secondary resource;
 *    Put the pointers to these records in an array of pointers;
 *    Return the pointer to the array of pointers.
 */

/*ARGSUSED*/
static Cardinal 
GetPushBGClassSecResData(
        WidgetClass w_class,
        XmSecondaryResourceData **data_rtn )
{
  int		 arrayCount;
  XmBaseClassExt bcePtr;
  
  _XmProcessLock();
  bcePtr = &(PushBGClassExtensionRec);
  arrayCount =
    _XmSecondaryResourceData (bcePtr, data_rtn, NULL, NULL, NULL,
			      GetPushBGClassSecResBase);
  _XmProcessUnlock();
  
  return (arrayCount);
}

/*
 * GetPushBGClassResBase ()
 *   retrun the address of the base of resources.
 */

/*ARGSUSED*/
static XtPointer 
GetPushBGClassSecResBase(
        Widget widget,
        XtPointer client_data )	/* unused */
{
  XtPointer widgetSecdataPtr; 
  size_t    labg_cache_size = sizeof (XmLabelGCacheObjPart);
  size_t    pushbg_cache_size = sizeof (XmPushButtonGCacheObjPart);
  char *cp;
  
  widgetSecdataPtr = (XtPointer) 
    XtMalloc(labg_cache_size + pushbg_cache_size + 1);
  
  _XmProcessLock();
  if (widgetSecdataPtr)
    {
      cp = (char *) widgetSecdataPtr;
      memcpy(cp, LabG_Cache(widget), labg_cache_size);
      cp += labg_cache_size;
      memcpy(cp, PBG_Cache(widget), pushbg_cache_size);
    }
  /* else Warning: error cannot allocate Memory */
  /*     widgetSecdataPtr = (XtPointer) (LabG_Cache(widget)); */
  
  _XmProcessUnlock();
  return (widgetSecdataPtr);
}

#ifdef DEFAULT_GLYPH_PIXMAP
/*
 * DrawDefaultGlyphPixmap (pb)
 */

static void 
DrawDefaultGlyphPixmap(
        XmPushButtonGadget pb )
{ 
  int	       dx, dy, width, height;
  Pixmap       def_pixmap;
  unsigned int def_pixmap_width, def_pixmap_height;
  
  def_pixmap = _XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)), 
					&def_pixmap_width, 
					&def_pixmap_height);
  
  /* We draw in the margin right area here. */
  dx = pb->rectangle.x + pb->rectangle.width -
    (LabG_MarginRight(pb) + LabG_MarginWidth(pb) +
     pb->gadget.highlight_thickness + pb->gadget.shadow_thickness);
  dy = pb->rectangle.y + pb->gadget.highlight_thickness +
    pb->gadget.shadow_thickness + LabG_MarginTop(pb) + LabG_MarginHeight(pb) + 
      (MAX(LabG_TextRect(pb).height,
	   LabG_AccTextRect(pb).height) - def_pixmap_height)/2;

  width = MIN(def_pixmap_width, LabG_MarginRight(pb));
  height = MIN(def_pixmap_height, 
	       MAX(LabG_TextRect(pb).height, LabG_AccTextRect(pb).height));

  XCopyPlane (XtDisplay (pb), def_pixmap, 
	      XtWindow (XtParent(pb)),
	      LabG_NormalGC(pb), 0, 0, width, height, dx, dy, 1);
}
#endif /* DEFAULT_GLYPH_PIXMAP */

#ifdef DEFAULT_GLYPH_PIXMAP
/*
 * EraseDefaultGlyphPixmap (pb)
 */

static void 
EraseDefaultGlyphPixmap(
        XmPushButtonGadget pb )
{ 
  int dx, dy, width, height;
  
  /* we clear the margin right area here */
  dx = pb->rectangle.x + pb->rectangle.width -
    (LabG_MarginRight(pb) + LabG_MarginWidth(pb) +
     pb->gadget.highlight_thickness + pb->gadget.shadow_thickness);
  dy = pb->rectangle.y + pb->gadget.highlight_thickness +
    pb->gadget.shadow_thickness + LabG_MarginTop(pb) + LabG_MarginHeight(pb);

  width = LabG_MarginRight(pb);
  height = MAX(LabG_TextRect(pb).height, LabG_AccTextRect(pb).height);

  XClearArea (XtDisplay (pb), XtWindow (XtParent(pb)),
	      dx, dy, width, height, False);
}
#endif /* DEFAULT_GLYPH_PIXMAP */

/*
 * EraseDefaultButtonShadow (pb)
 *  - Called from SetValues() - effort to optimize shadow drawing.
 */

static void 
EraseDefaultButtonShadow(
        XmPushButtonGadget pb )
{  
  int size, x, y, width, height, delta;
  XtEnum default_button_emphasis;
  
  if (!XtIsRealized((Widget)pb) || !XtIsManaged((Widget)pb)) 
    return;
  
  if (LabG_IsMenupane(pb))
    {
      ShellWidget mshell = (ShellWidget) XtParent(XtParent(pb));
      if (!mshell->shell.popped_up) 
	return;
    }
  
#ifdef DEFAULT_GLYPH_PIXMAP
  if (_XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)), NULL, NULL) !=
      XmUNSPECIFIED_PIXMAP) 
    {
      EraseDefaultGlyphPixmap(pb);
      return;
    } 
#endif

  size = PBG_DefaultButtonShadowThickness(pb);
  if (size > 0)
    {
      XmDisplay xm_dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));

      default_button_emphasis = xm_dpy->display.default_button_emphasis;

      switch (default_button_emphasis)
	{
	case XmEXTERNAL_HIGHLIGHT:
	  delta = pb->gadget.highlight_thickness;
	  break;

	case XmINTERNAL_HIGHLIGHT:
	  delta = Xm3D_ENHANCE_PIXEL;
	  break;

	default:
	  assert(FALSE);
	  return;
	}

      size += Xm3D_ENHANCE_PIXEL;
      x = pb->rectangle.x + delta;
      y = pb->rectangle.y + delta;
      width = pb->rectangle.width - (2 * delta);
      height = pb->rectangle.height - (2 * delta);
  
      XmeClearBorder(XtDisplay(pb), XtWindow(pb), x, y, width, height, size);
    }
}

/*
 * DrawDefaultButtonShadow (pb)
 *  - Called from SetValues() - effort to optimize shadow drawing.
 */

static void 
DrawDefaultButtonShadow(
        XmPushButtonGadget pb )
{  
  if (!(XtIsRealized((Widget)pb))) 
    return;
  
  if (LabG_IsMenupane(pb))
    {
      ShellWidget mshell = (ShellWidget)XtParent(XtParent(pb));
      if (!mshell->shell.popped_up) 
	return;
    }
  
  DrawDefaultButtonShadows(pb);
}

/*
 * AdjustHighLightThickness ()
 *  HighlightThickness has a dependency on default_button-shadow-thickness;
 *  This routine (called from SetValues) adjust for that dependency.
 *  Applications should be aware that
 *  if a pushbutton gadget has  with (default_button-shadow-thickness == 0) 
 * - then if through a XtSetValue it sets (default_button-shadow-thickness > 0)
 *  the application-specified highlight-thickness is internally increased by 
 *  Xm3D_ENHANCE_PIXEL to enhance the 3D-appearance of the defaultButton
 *  Shadow. Similarly if a pushbutton gadget has (default_button-shadow_
 *  thickness > 0), and it resets the (default_button-shadow-thickness = 0)
 *  through a XtSetValue , then the existing highlight-thickness is decreased
 *  by Xm3D_ENHANCE_PIXEL.
 *  The border-highlight when drawn is however is always of the same
 *  thickness as specified by the application since compensation is done
 *  in the drawing routine (see BorderHighlight).
 */

static int 
AdjustHighLightThickness(
        XmPushButtonGadget new_w,
        XmPushButtonGadget current )
{
  XmGadgetPart  *pbnew, *pbcurrent;
  int adjustment = 0;
  
  pbnew = (XmGadgetPart *) (&(new_w->gadget));
  pbcurrent = (XmGadgetPart *) (&(current->gadget));
  
  if (PBG_DefaultButtonShadowThickness(new_w))
    {
      if (!(PBG_DefaultButtonShadowThickness(current)))
	{
	  pbnew->highlight_thickness += Xm3D_ENHANCE_PIXEL;
	  adjustment += Xm3D_ENHANCE_PIXEL;
	}
      else if (pbnew->highlight_thickness != pbcurrent->highlight_thickness)
	{
	  pbnew->highlight_thickness += Xm3D_ENHANCE_PIXEL;
	  adjustment += Xm3D_ENHANCE_PIXEL;
	}
    }
  else
    {
      if (PBG_DefaultButtonShadowThickness(current))
	{
	  /* The default_button_shadow_thickness was > 0 and is now
	   * being set to 0, so take away the adjustment for enhancement.
	   */
	  if (pbnew->highlight_thickness == pbcurrent->highlight_thickness)
	    {
	      pbnew->highlight_thickness -= Xm3D_ENHANCE_PIXEL;
	      adjustment -= Xm3D_ENHANCE_PIXEL;
	    }
	}
      /*
       * This will have a bug if in a XtSetValues the application
       * removes the default_button_shadow_thickness and also
       * sets the high-light-thickness to a value of
       * (old-high-light-thickness (from previous XtSetValue) +
       *  Xm3D_ENHANCE_PIXEL).
       * This will be documented.
       */
    }
  return (adjustment);
}

/*ARGSUSED*/
static void 
Redisplay(
        Widget wid,
        XEvent *event,
        Region region )
{
  XmPushButtonGadget pb = (XmPushButtonGadget) wid;
  
  if (XtIsRealized((Widget)pb))
    {
      if (LabG_IsMenupane(pb))
	{
	  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
	  Boolean etched_in = dpy->display.enable_etched_in_menu;
	  ShellWidget mshell = (ShellWidget)XtParent(XtParent(pb));
	  
	  if (!mshell->shell.popped_up)
	    return;

	  DrawPushButtonLabelGadget (pb, event, region);

	  /* Refresh border highlight too. */
	  if (PBG_Armed(pb))
	      DrawPushButtonGadgetShadows (pb);
	}
      else
	{
	  DrawPushButtonLabelGadget (pb, event, region);
	  DrawPushButtonGadgetShadows (pb);
	  
	  if (pb->gadget.highlighted)
	    DrawBorderHighlight((Widget) pb);
	}
    }
}

/*
 * DrawPushButtonLabelGadget()
 */

static void 
DrawPushButtonLabelGadget(
        XmPushButtonGadget pb,
        XEvent *event,
        Region region )
{
  GC	     tmp_gc = NULL;
  GC	     fill_tmp_gc = NULL;
  Boolean    replace_for_fill_tmpGC = False;
  Boolean    replaceGC = False;
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
  Boolean etched_in = dpy->display.enable_etched_in_menu;



  if (PBG_Armed(pb) &&
      ((! LabG_IsMenupane(pb) && PBG_FillOnArm(pb)) ||
       (LabG_IsMenupane(pb) && etched_in)))
    {
      if ((LabG_LabelType(pb) == XmSTRING) &&
	  (PBG_ArmColor(pb) == LabG_Foreground(pb)))
	{
	  tmp_gc = LabG_NormalGC(pb);
	  LabG_NormalGC(pb) = LabG_BackgroundGC(pb);
	  replaceGC = True;
	}
      
      fill_tmp_gc = LabG_BackgroundGC(pb);
      LabG_BackgroundGC(pb) = PBG_FillGc(pb);
      replace_for_fill_tmpGC = True;
    }

  DrawLabelGadget(pb, event, region);
  
  if (replaceGC)
    LabG_NormalGC(pb) = tmp_gc;
  
  if (replace_for_fill_tmpGC)
    LabG_BackgroundGC(pb) =  fill_tmp_gc;
}


/*
 * DrawLabelGadget()
 */

static void 
DrawLabelGadget(
        XmPushButtonGadget pb,
        XEvent *event,
        Region region )
{
  Boolean    deadjusted = False;
  LRectangle background_box;

  if (LabG_LabelType(pb) == XmPIXMAP)
    {
      if (PBG_Armed(pb))
	{
	  if (PBG_ArmPixmap(pb) != XmUNSPECIFIED_PIXMAP)
	    LabG_Pixmap(pb) = PBG_ArmPixmap(pb);
	  else
	    LabG_Pixmap(pb) = PBG_UnarmPixmap(pb);
	}      
      else   /* pushbutton is unarmed */
	LabG_Pixmap(pb) = PBG_UnarmPixmap(pb);
    }
  
  /*
   * This doesn't need to be done here every time.  But it does do
   * the right thing.
   */
  ComputePBLabelArea(pb, &background_box);
  
  /*
   * Temporarily remove the Xm3D_ENHANCE_PIXEL hack ("adjustment")
   * from the margin values, so we don't confuse LabelG.  The
   * original code did the same thing, but in a round-about way.
   */
  _XmProcessLock();

  if (PBG_DefaultButtonShadowThickness(pb) > 0)
    { 
      deadjusted = True;
      LabG_MarginLeft(pb) -= Xm3D_ENHANCE_PIXEL;
      LabG_MarginRight(pb) -= Xm3D_ENHANCE_PIXEL;
      LabG_MarginTop(pb) -= Xm3D_ENHANCE_PIXEL;
      LabG_MarginBottom(pb) -= Xm3D_ENHANCE_PIXEL;
    }
  
  _XmRedisplayLabG((Widget) pb, event, region, &background_box);
  
  if (deadjusted)
    {
      LabG_MarginLeft(pb) += Xm3D_ENHANCE_PIXEL;
      LabG_MarginRight(pb) += Xm3D_ENHANCE_PIXEL;
      LabG_MarginTop(pb) += Xm3D_ENHANCE_PIXEL;
      LabG_MarginBottom(pb) += Xm3D_ENHANCE_PIXEL;
    }

  _XmProcessUnlock();
  
}  



/*
 * DrawPushButtonGadgetShadows()
 *
 *  Note: PushButton has two types of shadows: primitive-shadow and
 *	default-button-shadow.
 *  If pushbutton is in a menu only primitive shadows are drawn.
 */

static void 
DrawPushButtonGadgetShadows(
        XmPushButtonGadget pb )
{
  if (PBG_DefaultButtonShadowThickness(pb)
#ifdef DEFAULT_GLYPH_PIXMAP
      || (_XmGetDefaultGlyphPixmap (XtScreen((Widget)(pb)), NULL, NULL) !=
	  XmUNSPECIFIED_PIXMAP) 
#endif
      ) 
    { 
      EraseDefaultButtonShadows (pb);
      if (PBG_ShowAsDefault(pb)) 
	DrawDefaultButtonShadows (pb);
    }
  
  if (pb->gadget.shadow_thickness > 0)
    DrawPBGadgetShadows(pb);
}

/*
 * DrawPBGadgetShadows (pb)
 *   - Should be called only if PrimitiveShadowThickness > 0
 */

static void 
DrawPBGadgetShadows(
        XmPushButtonGadget pb )
{
  GC top_gc, bottom_gc;
  int dx, adjust, shadow_thickness;
  
  if (PBG_Armed(pb))
    { 
      bottom_gc = LabG_TopShadowGC(pb); 
      top_gc = LabG_BottomShadowGC(pb);
    }
  else
    {
      bottom_gc = LabG_BottomShadowGC(pb);
      top_gc = LabG_TopShadowGC(pb); 
    }
  
  shadow_thickness = pb->gadget.shadow_thickness;
  
  if ((shadow_thickness > 0) && (top_gc) && (bottom_gc))
    { 
      if (PBG_Compatible(pb))
	adjust = PBG_ShowAsDefault(pb);
      else
	adjust = PBG_DefaultButtonShadowThickness(pb);
      
      if (adjust > 0)
	{   
	  adjust = (adjust << 1);
	  dx = pb->gadget.highlight_thickness + adjust +  
	    pb->gadget.shadow_thickness;
	}
      else
	dx = pb->gadget.highlight_thickness;
      
      if ((pb->rectangle.width > 2 * dx) && 
	  (pb->rectangle.height > 2 * dx))	
	{ 
	  XmeDrawShadows (XtDisplay (pb), XtWindow (pb), top_gc, bottom_gc, 
			  dx + pb->rectangle.x, dx + pb->rectangle.y, 
			  pb->rectangle.width - 2 * dx,
			  pb->rectangle.height - 2 * dx,
			  shadow_thickness, XmSHADOW_OUT);
	}
    }
}

static void 
EraseDefaultButtonShadows(
        XmPushButtonGadget pb )
{
  int x, y, width, height, delta;
  int default_button_shadow;
  XtEnum default_button_emphasis;
  
  if (PBG_Compatible(pb))
    default_button_shadow = (int) (PBG_ShowAsDefault(pb));
  else
    default_button_shadow = (int) (PBG_DefaultButtonShadowThickness(pb));
  
  if (default_button_shadow > 0)
    { 
      XmDisplay xm_dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));

      default_button_emphasis = xm_dpy->display.default_button_emphasis;

      switch (default_button_emphasis)
	{
	case XmINTERNAL_HIGHLIGHT:
	  delta = Xm3D_ENHANCE_PIXEL;
	  break;

	case XmEXTERNAL_HIGHLIGHT:
	  delta = pb->gadget.highlight_thickness;
	  break;

	default:
	  assert(FALSE);
	  return;
	}

      x = pb->rectangle.x + delta;
      y = pb->rectangle.y + delta;
      width = pb->rectangle.width - 2 * delta;
      height = pb->rectangle.height - 2 * delta;
      
      if ((width > 0) && (height > 0))	
	XmeClearBorder(XtDisplay (pb), XtWindow (XtParent(pb)),
		       x, y, width, height, default_button_shadow);
    }
  
#ifdef DEFAULT_GLYPH_PIXMAP
  else if (_XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)), NULL, NULL) !=
	   XmUNSPECIFIED_PIXMAP)
    EraseDefaultGlyphPixmap(pb);
#endif
}

/*
 * DrawDefaultButtonShadows()
 *  - get the topShadowColor and bottomShadowColor from the parent;
 *    use those colors to construct top and bottom gc; use these
 *    GCs to draw the shadows of the button.
 *  - Should not be called if pushbutton is in a row column or in a menu.
 *  - Should be called only if a defaultbuttonshadow is to be drawn.
 */

static void 
DrawDefaultButtonShadows(
        XmPushButtonGadget pb )
{
  GC top_gc, bottom_gc;
  int default_button_shadow_thickness;
  
#ifdef DEFAULT_GLYPH_PIXMAP
  if (_XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)), NULL, NULL)
      != XmUNSPECIFIED_PIXMAP) 
    {
      DrawDefaultGlyphPixmap(pb);
      return;
    } 
#endif

  top_gc = XmParentBottomShadowGC(pb);
  bottom_gc = XmParentTopShadowGC(pb);
  
  if ((bottom_gc == None) || (top_gc == None)) 
    return;
  
  if (PBG_Compatible(pb))
    default_button_shadow_thickness = PBG_ShowAsDefault(pb);
  else
    default_button_shadow_thickness = PBG_DefaultButtonShadowThickness(pb);
  
  /*
   * Compute location of bounding box to contain the defaultButtonShadow.
   */
  if ((default_button_shadow_thickness > 0) &&
      (pb->rectangle.width > 2 * pb->gadget.highlight_thickness) &&
      (pb->rectangle.height > 2 * pb->gadget.highlight_thickness))	
    { 
      int x, y, width, height, delta;
      XmDisplay xm_dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(pb));
      XtEnum default_button_emphasis = xm_dpy->display.default_button_emphasis;

      switch (default_button_emphasis)
	{
	case XmEXTERNAL_HIGHLIGHT:
	  delta = pb->gadget.highlight_thickness;
	  break;

	case XmINTERNAL_HIGHLIGHT:
	  delta = Xm3D_ENHANCE_PIXEL;
	  break;

	default:
	  assert(FALSE);
	  return;
	}

      x = pb->rectangle.x + delta;
      y = pb->rectangle.y + delta;
      width = pb->rectangle.width - 2 * delta;
      height = pb->rectangle.height - 2 * delta;

      XmeDrawShadows(XtDisplay(pb), XtWindow(pb), 
		     top_gc, bottom_gc, x, y, width, height,
		     default_button_shadow_thickness, XmSHADOW_OUT);
    }
}

static XmImportOperator 
ShowAsDef_ToHorizPix(
        Widget widget,
        int offset,
        XtArgVal *value )
{
  XtArgVal         oldValue;
  XmImportOperator returnVal;
  
  oldValue = *value;
  returnVal = XmeToHorizontalPixels(widget, offset, value);
  
  if (oldValue  &&  !*value)
    *value = (XtArgVal) 1;

  return(returnVal);
} 

static Boolean 
ComputePBLabelArea(
        XmPushButtonGadget pb,
        LRectangle *box )
{
  Boolean result = True;
  int dx, adjust;
  short fill = 0; 
  
  if ((PBG_ArmColor(pb) == LabG_TopShadowColor(pb)) ||
      (PBG_ArmColor(pb) == LabG_BottomShadowColor(pb)))
    fill = 1;
  
  if (pb == NULL) 
    result = False;
  else
    { 
      if (PBG_DefaultButtonShadowThickness(pb) > 0)
	{ 
	  adjust = PBG_DefaultButtonShadowThickness(pb);
	  if (! LabG_IsMenupane(pb))
	    adjust += pb->gadget.shadow_thickness;
	  adjust = (adjust << 1);
	  dx = pb->gadget.highlight_thickness + adjust + fill; 
	}
      else
	{
	  dx = pb->gadget.highlight_thickness;
	  if (! LabG_IsMenupane(pb))
	    dx += pb->gadget.shadow_thickness + fill;
	}
      
      box->x = dx + pb->rectangle.x;
      box->y = dx + pb->rectangle.y;
      adjust = (dx << 1);
      box->width = pb->rectangle.width - adjust;
      box->height= pb->rectangle.height - adjust;
    }

  return (result);
}

static void 
ExportHighlightThickness(
        Widget widget,
        int offset,
        XtArgVal *value )
{
  if (PBG_DefaultButtonShadowThickness(widget) ||
      PBG_ShowAsDefault(widget))
    {
      if ((int)*value >= Xm3D_ENHANCE_PIXEL)
	*value -= Xm3D_ENHANCE_PIXEL;
    }
  
  XmeFromHorizontalPixels (widget, offset, value);
}

/*************************************************************************
 *
 * SetPushButtonSize(newpb)
 *  Picks the larger dimension when the armPixmap is a
 *  different size than the label pixmap(i.e the unarm pixmap).
 *
 ************************************************************************/

static void
SetPushButtonSize(
     XmPushButtonGadget newpb)
{
  unsigned int onW = 0, onH = 0;
  
  /* We know it's a pixmap so find out how how big it is */
  if (PBG_ArmPixmap(newpb) != XmUNSPECIFIED_PIXMAP)
    XmeGetPixmapData(XtScreen(newpb), PBG_ArmPixmap(newpb),
		     NULL, NULL, NULL, NULL, NULL, NULL,
		     &onW, &onH); 
  
  if ((onW > LabG_TextRect(newpb).width) || (onH > LabG_TextRect(newpb).height))
    {
      LabG_TextRect(newpb).width =  (unsigned short) onW;
      LabG_TextRect(newpb).height = (unsigned short) onH;
    }
  
  /* Let LabelG do the rest. */
  {
      XtWidgetProc resize;
      _XmProcessLock();
      resize = xmLabelGadgetClassRec.rect_class.resize;
      _XmProcessUnlock();      
      (* resize) ((Widget) newpb);
  }
}

/************************************************************************
 *
 *  ChangeCB
 *	add or remove the activate callback list.
 *      
 ************************************************************************/

static void 
ChangeCB(
	 Widget w, 
	 XtCallbackProc activCB,
	 XtPointer closure,
	 Boolean setunset)
{
  if (setunset)
    XtAddCallback (w, XmNactivateCallback, activCB, closure);
  else
    XtRemoveCallback (w, XmNactivateCallback, activCB, closure);
}

/************************************************************************
 *
 *  ShowAsDefault
 *	set up the default visual
 *      
 ************************************************************************/

static void 
ShowAsDefault(Widget w, 
	      XtEnum state)
{
  XmPushButtonGadget pb = (XmPushButtonGadget) w;
  Dimension dbShadowTh;
  
  switch (state) 
    {
    case XmDEFAULT_READY:
      {
	/* We have pixels, but the button unit type might not be 
	 * pixel, so save it and restore it after the setvalues.
	 */
	unsigned char saved_unit_type = ((XmGadget)w)->gadget.unit_type;
#ifdef DEFAULT_GLYPH_PIXMAP
	unsigned int def_pixmap_width;
	
	if (_XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)),
				     &def_pixmap_width, NULL) != 
	    XmUNSPECIFIED_PIXMAP) 
	  {
	    /* we will use the margin right area, so increase it */
	    PBG_Compatible(pb) = False;
	    ((XmGadget)w)->gadget.unit_type = XmPIXELS;
	    XtVaSetValues(w, XmNmarginRight, def_pixmap_width, NULL);
	    ((XmGadget)w)->gadget.unit_type = saved_unit_type;
	  } 
	else 
#endif
	  if (!PBG_DefaultButtonShadowThickness(pb))
	    {
	      if (pb->gadget.shadow_thickness > 1) 
		dbShadowTh = pb->gadget.shadow_thickness >> 1;
	      else 
		dbShadowTh = pb->gadget.shadow_thickness;
	      
	      /* CR 7474: Disable pushbutton compatibility mode. */
	      PBG_Compatible(pb) = False;
	      ((XmGadget)w)->gadget.unit_type = XmPIXELS;
	      XtVaSetValues(w, XmNdefaultButtonShadowThickness, 
			    dbShadowTh, NULL);
	      ((XmGadget)w)->gadget.unit_type = saved_unit_type;
	    } 
      }
      break;
      
    case XmDEFAULT_ON :
      /* CR 7474: Disable pushbutton compatibility mode. */
      PBG_Compatible(pb) = False;
      XtVaSetValues(w, XmNshowAsDefault, True, NULL);
      break;
      
    case XmDEFAULT_OFF :
      XtVaSetValues(w, XmNshowAsDefault, False, NULL);
      break;
      
    case XmDEFAULT_FORGET :
    default:
#ifdef DEFAULT_GLYPH_PIXMAP
      if (_XmGetDefaultGlyphPixmap(XtScreen((Widget)(pb)), NULL, NULL) != 
	  XmUNSPECIFIED_PIXMAP)
	XtVaSetValues(w, XmNmarginRight, 0, NULL);
      else 
#endif
	if (!PBG_DefaultButtonShadowThickness(pb))
	  XtVaSetValues(w, XmNdefaultButtonShadowThickness, 0, NULL);
    }
}
	
/************************************************************************
 *
 *		Application Accessible External Functions
 *
 ************************************************************************/


/************************************************************************
 *
 *  XmCreatePushButton
 *	Create an instance of a pushbutton and return the widget id.
 *
 ************************************************************************/
Widget 
XmCreatePushButtonGadget(
        Widget parent,
        char *name,
        ArgList arglist,
        Cardinal argcount )
{
  return XtCreateWidget (name, xmPushButtonGadgetClass, 
			 parent, arglist, argcount);
}
