Blob Blame History Raw
/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/ 
/* 
 * HISTORY
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: ScrollBar.c /main/20 1997/03/10 14:52:28 dbl $"
#endif
#endif
/* (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */

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


#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#endif

#include <Xm/DisplayP.h>        /* for enableThinThickness */
#include <Xm/DrawP.h>
#include <Xm/DropSMgr.h>	/* for XmDropSiteStartUpdate/EndUPdate */
#include <Xm/ManagerP.h>
#include <Xm/NavigatorT.h>
#include <Xm/ScrollBarP.h>
#include <Xm/TraitP.h>
#include <Xm/TransltnsP.h>
#include <Xm/VaSimpleP.h>
#include "ColorI.h"
#include "MessagesI.h"
#include "RepTypeI.h"
#include "ScreenI.h"
#include "XmI.h"

/* see comments in ScrollBarP.h */
#define slider_visual	etched_slider
#define flat_slider_GC	unhighlight_GC

#define MESSAGE1	_XmMMsgScrollBar_0000
#define MESSAGE2	_XmMMsgScrollBar_0001
#define MESSAGE3	_XmMMsgScrollBar_0002
#define MESSAGE4	_XmMMsgScrollBar_0003
#define MESSAGE6	_XmMMsgScaleScrBar_0004
#define MESSAGE7	_XmMMsgScrollBar_0004
#define MESSAGE8	_XmMMsgScrollBar_0005
#define MESSAGE9	_XmMMsgScrollBar_0006
#define MESSAGE10	_XmMMsgScrollBar_0007
#define MESSAGE13	_XmMMsgScrollBar_0008
#define MESSAGE14	_XmMMsgMotif_0001

#define MAXDIMENSION	65535

#define DRAWARROW(sbw, t_gc, b_gc, x, y, dir)\
    XmeDrawArrow(XtDisplay ((Widget) sbw),\
		XtWindow ((Widget) sbw),\
		t_gc, b_gc,\
		sbw->scrollBar.foreground_GC,\
		x-1, y-1,\
		sbw->scrollBar.arrow_width+2,\
		sbw->scrollBar.arrow_height+2,\
		sbw->primitive.shadow_thickness,\
		dir);

#define PROCESS_DIR_INVERSED(sbw) \
    ((sbw->scrollBar.processing_direction == XmMAX_ON_LEFT) || \
     (sbw->scrollBar.processing_direction == XmMAX_ON_TOP))

#define INVERSED_VALUE(sbw) \
    (sbw->scrollBar.maximum + sbw->scrollBar.minimum - \
	sbw->scrollBar.value - sbw->scrollBar.slider_size)

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

static void ClassPartInitialize( 
                        WidgetClass wc) ;
static void ProcessingDirectionDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void BackgroundPixelDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void TraversalDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void HighlightDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void SliderVisualDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void SliderMarkDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void EditableDefault( 
                        XmScrollBarWidget widget,
                        int offset,
                        XrmValue *value) ;
static void Initialize( 
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static void GetForegroundGC( 
                        XmScrollBarWidget sbw) ;
static void GetUnavailableGC( 
                        XmScrollBarWidget sbw) ;
static void GetFlatSliderGC( 
                        XmScrollBarWidget sbw) ;
static void GetSliderPixmap( 
                        XmScrollBarWidget sbw) ;
static void CalcSliderRect( 
                        XmScrollBarWidget sbw,
                        short *slider_x,
                        short *slider_y,
                        short *slider_width,
                        short *slider_height) ;
static void DrawSliderPixmap( 
                        XmScrollBarWidget sbw) ;
static void Redisplay( 
                        Widget wid,
                        XEvent *event,
                        Region region) ;
static void Resize( 
                        Widget wid) ;
static void Realize( 
                        Widget sbw,
                        XtValueMask *window_mask,
                        XSetWindowAttributes *window_attributes) ;
static void Destroy( 
                        Widget wid) ;
static Boolean ValidateInputs( 
                        XmScrollBarWidget current,
                        XmScrollBarWidget request,
                        XmScrollBarWidget new_w) ;
static Boolean SetValues( 
                        Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static int CalcSliderVal( 
                        XmScrollBarWidget sbw,
                        int x,
                        int y) ;
static void Select( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void Release( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void Moved( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void TopOrBottom( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void IncrementUpOrLeft( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void IncrementDownOrRight( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void PageUpOrLeft( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void PageDownOrRight( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void CancelDrag( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void MoveSlider( 
                        XmScrollBarWidget sbw,
                        int currentX,
                        int currentY) ;
static void RedrawSliderWindow( 
                        XmScrollBarWidget sbw) ;
static Boolean ChangeScrollBarValue( 
                        XmScrollBarWidget sbw) ;
static void TimerEvent( 
                        XtPointer closure,
                        XtIntervalId *id) ;
static void ScrollCallback( 
                        XmScrollBarWidget sbw,
                        int reason,
                        int value,
                        int xpixel,
                        int ypixel,
                        XEvent *event) ;
static void ExportScrollBarValue( 
                        Widget wid,
                        int offset,
                        XtArgVal *value) ;
static XmImportOperator ImportScrollBarValue( 
                        Widget wid,
                        int offset,
                        XtArgVal *value) ;


static void NavigChangeMoveCB(Widget nav, 
			      XtCallbackProc moveProc,
			      XtPointer closure,
			      Boolean setunset) ;
static void NavigSetValue(Widget nav, 
			  XmNavigatorData nav_data, 
			  Boolean notify) ;
static void NavigGetValue(Widget nav, 
			  XmNavigatorData nav_data) ;

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

/*  Default translation table and action list  */

#define defaultTranslations	_XmScrollBar_defaultTranslations

static XtActionsRec actions[] =
{
	{ "Select",                 Select },
	{ "Release",                Release },
	{ "Moved",                  Moved },
	{ "TopOrBottom",            TopOrBottom },
	{ "IncrementUpOrLeft",      IncrementUpOrLeft },
	{ "IncrementDownOrRight",   IncrementDownOrRight },
	{ "PageUpOrLeft",           PageUpOrLeft },
	{ "PageDownOrRight",        PageDownOrRight },
	{ "CancelDrag",             CancelDrag },
};


/*  Resource list for ScrollBar  */

static XtResource resources[] = 
{
	{ XmNnavigationType, XmCNavigationType, XmRNavigationType,
	  sizeof(unsigned char),
	  XtOffsetOf(XmScrollBarRec, primitive.navigation_type),
	  XmRImmediate, (XtPointer) XmSTICKY_TAB_GROUP
	},
	{ XmNbackground, XmCBackground, XmRPixel, sizeof(Pixel),
	  XtOffsetOf(XmScrollBarRec, core.background_pixel),
	  XmRCallProc, (XtPointer) BackgroundPixelDefault
	},
	{ XmNtroughColor, XmCTroughColor, XmRPixel, sizeof(Pixel),
	  XtOffsetOf(XmScrollBarRec, scrollBar.trough_color),
	  XmRCallProc, (XtPointer) _XmSelectColorDefault
	},
	{ XmNvalue, XmCValue, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.value),
	  XmRImmediate, (XtPointer) XmINVALID_DIMENSION
	},
	{ XmNminimum, XmCMinimum, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.minimum),
	  XmRImmediate, (XtPointer) 0
	},
	{ XmNmaximum, XmCMaximum, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.maximum),
	  XmRImmediate, (XtPointer) 100
	},
	{ XmNsliderSize, XmCSliderSize, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.slider_size),
	  XmRImmediate, (XtPointer) XmINVALID_DIMENSION
	},
	{ XmNshowArrows, XmCShowArrows, XmRShowArrows, sizeof (XtEnum),
	  XtOffsetOf(XmScrollBarRec, scrollBar.show_arrows),
	  XmRImmediate, (XtPointer) XmEACH_SIDE
	},
	{ XmNorientation, XmCOrientation, 
	  XmROrientation, sizeof (unsigned char),
	  XtOffsetOf(XmScrollBarRec, scrollBar.orientation),
	  XmRImmediate, (XtPointer) XmVERTICAL
	},
	{ XmNprocessingDirection, XmCProcessingDirection, 
	  XmRProcessingDirection, sizeof (unsigned char), 
	  XtOffsetOf(XmScrollBarRec, scrollBar.processing_direction),
	  XmRCallProc, (XtPointer) ProcessingDirectionDefault
	},
	{ XmNincrement, XmCIncrement, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.increment),
	  XmRImmediate, (XtPointer) 1
	},
	{ XmNpageIncrement, XmCPageIncrement, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.page_increment),
	  XmRImmediate, (XtPointer) 10
	},
	{ XmNinitialDelay, XmCInitialDelay, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.initial_delay),
	  XmRImmediate, (XtPointer) 250
	},
	{ XmNrepeatDelay, XmCRepeatDelay, XmRInt, sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.repeat_delay),
	  XmRImmediate, (XtPointer) 50
	},
	{ XmNvalueChangedCallback, XmCCallback, 
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.value_changed_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNincrementCallback, XmCCallback, 
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.increment_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNdecrementCallback, XmCCallback, 
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.decrement_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNpageIncrementCallback, XmCCallback,
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.page_increment_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNpageDecrementCallback, XmCCallback, 
	  XmRCallback, sizeof (XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.page_decrement_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNtoTopCallback, XmCCallback,
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.to_top_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNtoBottomCallback, XmCCallback,
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.to_bottom_callback),
	  XmRPointer, (XtPointer) NULL
	},
	{ XmNdragCallback, XmCCallback,
	  XmRCallback, sizeof(XtCallbackList),
	  XtOffsetOf(XmScrollBarRec, scrollBar.drag_callback),
	  XmRPointer, (XtPointer) NULL
	},
        {
          XmNtraversalOn, XmCTraversalOn, XmRBoolean, sizeof (Boolean),
          XtOffsetOf(XmPrimitiveRec, primitive.traversal_on),
          XmRCallProc, (XtPointer) TraversalDefault
        },
        {
          XmNhighlightThickness, XmCHighlightThickness, 
	  XmRHorizontalDimension, sizeof (Dimension),
          XtOffsetOf(XmPrimitiveRec, primitive.highlight_thickness),
          XmRCallProc, (XtPointer) HighlightDefault
        },
        {
	  XmNsnapBackMultiple, XmCSnapBackMultiple, XmRShort,
          sizeof (unsigned short),
          XtOffsetOf(XmScrollBarRec, scrollBar.snap_back_multiple),
          XmRImmediate, (XtPointer) MAXDIMENSION
        },   
        {
          XmNslidingMode, XmCSlidingMode, XmRSlidingMode, 
	  sizeof(XtEnum), XtOffsetOf(XmScrollBarRec, scrollBar.sliding_mode), 
	  XmRImmediate, (XtPointer) XmSLIDER
        },
	{
          XmNeditable, XmCEditable, XmRBoolean, 
          sizeof(XtEnum), XtOffsetOf(XmScrollBarRec,scrollBar.editable), 
          XmRCallProc, (XtPointer) EditableDefault
        },
        {
	  XmNsliderVisual, XmCSliderVisual, XmRSliderVisual,
          sizeof (XtEnum),
          XtOffsetOf(XmScrollBarRec, scrollBar.slider_visual),
          XmRCallProc, (XtPointer) SliderVisualDefault
        },
        {
	  XmNsliderMark, XmCSliderMark, XmRSliderMark,
          sizeof (XtEnum),
          XtOffsetOf(XmScrollBarRec, scrollBar.slider_mark),
          XmRCallProc, (XtPointer) SliderMarkDefault
        },
   };


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

static XmSyntheticResource syn_resources[] =
{
	{ XmNvalue,
	  sizeof (int),
	  XtOffsetOf(XmScrollBarRec, scrollBar.value), 
	  ExportScrollBarValue,
	  ImportScrollBarValue
	},
};


externaldef(xmscrollbarclassrec) XmScrollBarClassRec xmScrollBarClassRec =
{
   {
      (WidgetClass) &xmPrimitiveClassRec, /* superclass	         */
      "XmScrollBar",                    /* class_name	         */
      sizeof(XmScrollBarRec),           /* widget_size	         */
      NULL,                             /* class_initialize      */
      ClassPartInitialize,              /* class_part_initialize */
      FALSE,                            /* class_inited          */
      Initialize,                       /* initialize	         */
      (XtArgsProc)NULL,                 /* initialize_hook       */
      Realize,                          /* realize	         */	
      actions,                          /* actions               */	
      XtNumber(actions),                /* 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       */
      (XtAcceptFocusProc)NULL,          /* accept_focus	         */	
      XtVersion,                        /* version               */
      NULL,                             /* callback private      */
      defaultTranslations,              /* tm_table              */
      (XtGeometryHandler)NULL,          /* query_geometry        */
      (XtStringProc)NULL,               /* display_accelerator   */
      (XtPointer) NULL,                 /* extension             */
   },

   {
      XmInheritWidgetProc,		/* border_highlight   */
      XmInheritWidgetProc,		/* border_unhighlight */
      NULL,				/* translations       */
      (XtActionProc)NULL,		/* arm_and_activate   */
      syn_resources,   			/* syn_resources      */
      XtNumber(syn_resources),		/* num syn_resources  */
      NULL,				/* extension          */
   },

   {
      (XtPointer) NULL,			/* extension          */
   },
};

externaldef(xmscrollbarwidgetclass) WidgetClass xmScrollBarWidgetClass = 
     (WidgetClass) &xmScrollBarClassRec;


/* Trait record for ScrollBar */

static XmConst XmNavigatorTraitRec scrollBarNT = {
  0,		/* version */
  NavigChangeMoveCB,
  NavigSetValue,
  NavigGetValue,
};

/*********************************************************************
 *
 *  ExportScrollBarValue
 *	Convert the scrollbar value from the normal processing direction
 *	to reverse processing if needed.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
ExportScrollBarValue(
        Widget wid,
        int offset,		/* unused */
        XtArgVal *value )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
	if (PROCESS_DIR_INVERSED(sbw))
		*value = (XtArgVal) INVERSED_VALUE(sbw);
	else
		*value = (XtArgVal) sbw->scrollBar.value;
}

/*********************************************************************
 *
 *  ImportScrollBarValue
 *  Indicate that the value did indeed change.
 *
 *********************************************************************/
/*ARGSUSED*/
static XmImportOperator 
ImportScrollBarValue(
        Widget wid,
        int offset,		/* unused */
        XtArgVal *value )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;

	sbw->scrollBar.flags |= VALUE_SET_FLAG;
	*value = (XtArgVal)sbw->scrollBar.value;
	return(XmSYNTHETIC_LOAD);
}


/*********************************************************************
 *
 * ProcessingDirectionDefault
 *    This procedure provides the dynamic default behavior for 
 *    the processing direction resource dependent on the orientation.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
ProcessingDirectionDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
	static unsigned char direction;

	value->addr = (XPointer) &direction;

	if (widget->scrollBar.orientation == XmHORIZONTAL)
        {
           if (LayoutIsRtoLP(widget))
	     direction = XmMAX_ON_LEFT;
           else
	     direction = XmMAX_ON_RIGHT;
        }
	else /* XmVERTICAL  -- range checking done during widget
		initialization */
	  direction = XmMAX_ON_BOTTOM;
}


/*********************************************************************
 *
 * BackgroundPixelDefault
 *    This procedure provides the dynamic default behavior for 
 *    the background color. It looks to see if the parent is a
 *    ScrolledWindow, and if so, it uses the parent background.
 *    This is mostly for compatibility with 1.1 where the scrolledwindow
 *    was forcing its scrollbar color to its own background.
 *    Note that it works for both automatic and non automatic SW,
 *    which is a new feature for non automatic.
 *
 *********************************************************************/
static void 
BackgroundPixelDefault(
        XmScrollBarWidget widget,
        int offset,
        XrmValue *value )
{
	static Pixel background;
	Widget parent = XtParent(widget) ;

	if (XmIsScrolledWindow(parent)) {
	    value->addr = (XPointer) &background;
	    background = parent->core.background_pixel;
	    return ;
	}

	/* else use the primitive defaulting mechanism */

	_XmBackgroundColorDefault((Widget )widget, offset, value);
}

/*********************************************************************
 *
 * TraversalDefault
 *    This procedure provides the dynamic default behavior for 
 *    the traversal. It looks to see if the parent is a
 *    ScrolledWindow, and if so, it sets it to On.
 *    This is mostly for compatibility with 1.1 where the scrolledwindow
 *    was forcing its scrollbar traversal to On
 *    Note that it works only for automatic.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
TraversalDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
      static Boolean traversal ;
      Widget parent = XtParent(widget) ;
      Arg al[1] ;
      unsigned char sp ;

      traversal = False ;
      value->addr = (XPointer) &traversal;
              
      if (XmIsScrolledWindow(parent)) {
          XtSetArg(al[0], XmNscrollingPolicy, &sp);
          XtGetValues(parent, al, 1);
          if (sp == XmAUTOMATIC) {
              traversal = True ;
              return ;
          }
      }
}



/*********************************************************************
 *
 * SliderVisualDefault
 *    
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
SliderVisualDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
      static XtEnum slider_visual ;

      value->addr = (XPointer) &slider_visual;
              
      if (widget->scrollBar.sliding_mode == XmTHERMOMETER) {
          slider_visual = XmTROUGH_COLOR ;
      } else {
	  slider_visual = XmSHADOWED_BACKGROUND ;
      }
      
}



/*********************************************************************
 *
 * SliderMarkDefault
 *    
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
SliderMarkDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
      static XtEnum slider_mark ;

      value->addr = (XPointer) &slider_mark;
              
      if ((widget->scrollBar.sliding_mode == XmTHERMOMETER) &&
	  (widget->scrollBar.editable))
	  slider_mark = XmROUND_MARK ;
      else
	  slider_mark = XmNONE ;
}



/*********************************************************************
 *
 * EditableDefault
 *    
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
EditableDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
      static XtEnum editable ;

      value->addr = (XPointer) &editable;
              
      if (widget->scrollBar.sliding_mode == XmTHERMOMETER) {
          editable = False ;
      } else {
	  editable = True ;
      }
      
}

/*********************************************************************
 *
 * HighlightDefault
 *    This procedure provides the dynamic default behavior for 
 *    the highlight. It looks to see if the parent is a
 *    ScrolledWindow, and if so, it sets it to 2 or 1 depending on 
 *    the enableThinThickness resource, otherwise, 0.
 *    Note that it works only for automatic.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
HighlightDefault(
        XmScrollBarWidget widget,
        int offset,		/* unused */
        XrmValue *value )
{
    static Dimension highlight ;
    Widget parent = XtParent(widget) ;
    Arg al[1] ;
    unsigned char sp ;
    Boolean thinthickness = False;
    
    highlight = 0 ;
    value->addr = (XPointer) &highlight;
    
    if (XmIsScrolledWindow(parent)) {
	XtSetArg(al[0], XmNscrollingPolicy, &sp);
	XtGetValues(parent, al, 1);
	if (sp == XmAUTOMATIC) {
	    XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(widget));
	    thinthickness = dpy->display.enable_thin_thickness;

	    if (thinthickness) {
		highlight = 1;
	    }
	    else {
		highlight = 2 ;
	    }
	    return ;
	}
    }
}




/*********************************************************************
 *
 *  ClassPartInitialize
 *     Initialize the fast subclassing.
 *
 *********************************************************************/
static void 
ClassPartInitialize(
        WidgetClass wc )
{
    _XmFastSubclassInit (wc, XmSCROLL_BAR_BIT);

    /* Install the navigator trait for all subclasses */
    XmeTraitSet((XtPointer)wc, XmQTnavigator, (XtPointer) &scrollBarNT);
}






/*********************************************************************
 *
 *  Initialize
 *     The main widget instance initialization routine.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
Initialize(
        Widget rw,
        Widget nw,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
    XmScrollBarWidget request = (XmScrollBarWidget) rw ;
    XmScrollBarWidget new_w = (XmScrollBarWidget) nw ;
    
    Boolean default_value = FALSE;
    
    if(!XmRepTypeValidValue( XmRID_SHOW_ARROWS,
			    new_w->scrollBar.show_arrows, (Widget) new_w) )
	{
	    new_w->scrollBar.show_arrows = XmEACH_SIDE;
	}
    
    if(!XmRepTypeValidValue( XmRID_SLIDER_VISUAL,
			    new_w->scrollBar.slider_visual, (Widget) new_w) )
	{
	    new_w->scrollBar.slider_visual = XmSHADOWED_BACKGROUND;
	}
    
    if(!XmRepTypeValidValue( XmRID_SLIDER_MARK,
			    new_w->scrollBar.slider_mark, (Widget) new_w) )
	{
	    new_w->scrollBar.slider_mark = XmNONE;
	}
    
    if(!XmRepTypeValidValue( XmRID_SLIDING_MODE,
			    new_w->scrollBar.sliding_mode, (Widget) new_w) )
	{
	    new_w->scrollBar.sliding_mode = XmSLIDER;
	}
    
     if (new_w->scrollBar.value == XmINVALID_DIMENSION)
	{
	    new_w->scrollBar.value = 0;
	    default_value = True;
	}
    
    /* Validate the incoming data  */                      
    
    if (new_w->scrollBar.minimum >= new_w->scrollBar.maximum)
	{
	    new_w->scrollBar.minimum = 0;
	    new_w->scrollBar.maximum = 100;
	    XmeWarning( (Widget) new_w, MESSAGE1);
	}
    
    if (new_w->scrollBar.slider_size == XmINVALID_DIMENSION)
	{
	    new_w->scrollBar.slider_size = (new_w->scrollBar.maximum
					    - new_w->scrollBar.minimum) / 10;
	    if (new_w->scrollBar.slider_size < 1)
		new_w->scrollBar.slider_size = 1;
	}
    
    if (new_w->scrollBar.slider_size < 1)
	{
	    new_w->scrollBar.slider_size = 1;
	    XmeWarning( (Widget) new_w, MESSAGE2);
	}

    if (new_w->scrollBar.slider_size > 
	(new_w->scrollBar.maximum - new_w->scrollBar.minimum))
	{
	    new_w->scrollBar.slider_size = new_w->scrollBar.maximum
		- new_w->scrollBar.minimum;
	    XmeWarning( (Widget) new_w, MESSAGE13);
	}
    
    /* in thermo, slider_size is forced to be 0 */
    if (new_w->scrollBar.sliding_mode == XmTHERMOMETER)
	new_w->scrollBar.slider_size = 0 ;

    if (new_w->scrollBar.value < new_w->scrollBar.minimum)
	{
	    new_w->scrollBar.value = new_w->scrollBar.minimum;
	    if (!default_value) XmeWarning( (Widget) new_w, MESSAGE3);
	}
    
    if (new_w->scrollBar.value > 
	new_w->scrollBar.maximum - new_w->scrollBar.slider_size)
	{
	    new_w->scrollBar.value = new_w->scrollBar.minimum;
	    if (!default_value) XmeWarning( (Widget) new_w, MESSAGE4);
	}
    
    if(    !XmRepTypeValidValue(XmRID_ORIENTATION, 
				new_w->scrollBar.orientation, (Widget) new_w))
	{
	    new_w->scrollBar.orientation = XmVERTICAL;
	}
    
    if (new_w->scrollBar.orientation == XmHORIZONTAL)
	{
	    if ((new_w->scrollBar.processing_direction != XmMAX_ON_RIGHT) &&
		(new_w->scrollBar.processing_direction != XmMAX_ON_LEFT))
		
		{
		    new_w->scrollBar.processing_direction = XmMAX_ON_RIGHT;
		    XmeWarning( (Widget) new_w, MESSAGE6);
		}
	}
    else
	{
	    if ((new_w->scrollBar.processing_direction != XmMAX_ON_TOP) &&
		(new_w->scrollBar.processing_direction != XmMAX_ON_BOTTOM))
		{
		    new_w->scrollBar.processing_direction = XmMAX_ON_BOTTOM;
		    XmeWarning( (Widget) new_w, MESSAGE6);
		}
	}
    
    if (new_w->scrollBar.increment <= 0)
	{
	    new_w->scrollBar.increment = 1;
	    XmeWarning( (Widget) new_w, MESSAGE7);
	}
    
    if (new_w->scrollBar.page_increment <= 0)
	{
	    new_w->scrollBar.page_increment = 10;
	    XmeWarning( (Widget) new_w, MESSAGE8);
	}
    
    if (new_w->scrollBar.initial_delay <= 0)
	{
	    new_w->scrollBar.initial_delay = 250;
	    XmeWarning( (Widget) new_w, MESSAGE9);
	}
    
    if (new_w->scrollBar.repeat_delay <= 0)
	{
	    new_w->scrollBar.repeat_delay = 75;
	    XmeWarning( (Widget) new_w, MESSAGE10);
	}
    
    /*  Set up a geometry for the widget if it is currently 0.  */
    
    if (request->core.width == 0)
	{
	    if (new_w->scrollBar.orientation == XmHORIZONTAL)
		new_w->core.width += 100;
	    else
		new_w->core.width += 11;
	}
    if (request->core.height == 0)
	{
	    if (new_w->scrollBar.orientation == XmHORIZONTAL)
		new_w->core.height += 11;
	    else
		new_w->core.height += 100;
	}
    
    /*  Reverse the value for reverse processing.  */
    
    if (PROCESS_DIR_INVERSED(new_w))
	new_w->scrollBar.value = INVERSED_VALUE(new_w);
    
    /*  Set the internally used variables.  */
    
    new_w->scrollBar.flags = 0;
    if (new_w->scrollBar.slider_size < (new_w->scrollBar.maximum
					- new_w->scrollBar.minimum))
	{
	    new_w->scrollBar.flags |= SLIDER_AVAILABLE;
	    
	    if (new_w->scrollBar.value > new_w->scrollBar.minimum)
		new_w->scrollBar.flags |= ARROW1_AVAILABLE;
	    if (new_w->scrollBar.value < (new_w->scrollBar.maximum
					  - new_w->scrollBar.slider_size))
		new_w->scrollBar.flags |= ARROW2_AVAILABLE;
	}
    else
	{
	    /*
	     * For correct setvalues processing, when the slider is
	     * unavailable, the arrows should be available.
	     */
	    new_w->scrollBar.flags |= ARROW1_AVAILABLE;
	    new_w->scrollBar.flags |= ARROW2_AVAILABLE;
	}
    
    new_w->scrollBar.pixmap = 0;
    new_w->scrollBar.sliding_on = FALSE;
    new_w->scrollBar.timer = 0;
    new_w->scrollBar.add_flags = 0 ;
    
    new_w->scrollBar.arrow_width = 0;
    new_w->scrollBar.arrow_height = 0;
    
    new_w->scrollBar.arrow1_x = 0;
    new_w->scrollBar.arrow1_y = 0;
    new_w->scrollBar.arrow1_selected = FALSE;
    
    new_w->scrollBar.arrow2_x = 0;
    new_w->scrollBar.arrow2_y = 0;
    new_w->scrollBar.arrow2_selected = FALSE;
    
    new_w->scrollBar.saved_value = new_w->scrollBar.value;
    
    if (LayoutIsRtoLP(new_w))
	new_w->scrollBar.flags &= ~VALUE_SET_FLAG;
    
    /*  Get the drawing graphics contexts.  */
    
    GetForegroundGC(new_w);
    GetUnavailableGC(new_w);
    GetFlatSliderGC(new_w);
    
    /* call the resize method to get an initial size */

    {
	XtWidgetProc resize;
	_XmProcessLock();
	resize = new_w->core.widget_class->core_class.resize;
	_XmProcessUnlock();

	(* (resize)) ((Widget) new_w);
    }

}




/************************************************************************
 *
 *  GetForegroundGC
 *     Get the graphics context used for drawing the slider and arrows.
 *
 ************************************************************************/
static void 
GetForegroundGC(
        XmScrollBarWidget sbw )
{
    XGCValues values;
    XtGCMask  valueMask;

    valueMask = GCForeground | GCBackground | GCGraphicsExposures;
    values.foreground = sbw->core.background_pixel;
    values.background = sbw->primitive.foreground;
    values.graphics_exposures = False;

    sbw->scrollBar.foreground_GC = XtAllocateGC ((Widget) sbw, 0, valueMask, 
						 &values, 0, GCFont);
}

/************************************************************************
 *
 *  GetFlatSliderGC
 *     Get the graphics context used for drawing the flat slider
 *
 ************************************************************************/
static void 
GetFlatSliderGC(
        XmScrollBarWidget sbw )
{
    XGCValues values;
    XtGCMask  valueMask, unusedMask;

    valueMask = GCForeground | GCBackground | GCGraphicsExposures;
    unusedMask = GCFont | GCClipXOrigin | GCClipYOrigin;
    if (sbw->scrollBar.slider_visual == XmTROUGH_COLOR)
	values.foreground = sbw->scrollBar.trough_color;
    else 
	values.foreground = sbw->primitive.foreground;
    values.background = sbw->core.background_pixel;
    values.graphics_exposures = False;

    sbw->scrollBar.flat_slider_GC = XtAllocateGC ((Widget) sbw, 0, valueMask, 
						  &values, GCClipMask, 
						  unusedMask);
}



/************************************************************************
 *
 *  GetUnavailableGC
 *     Get the graphics context used for drawing the slider and arrows
 *     as being unavailable.
 *
 ************************************************************************/
static void 
GetUnavailableGC(
        XmScrollBarWidget sbw )
{
    XGCValues values;
    XtGCMask  valueMask, unusedMask;
    
    valueMask = GCForeground | GCBackground | GCGraphicsExposures | 
	        GCFillStyle | GCStipple;
    unusedMask = GCClipXOrigin | GCClipYOrigin | GCFont;
    values.graphics_exposures = False;
    values.fill_style = FillStippled;
    values.background = sbw->core.background_pixel;
    values.foreground = sbw->primitive.foreground;

    values.stipple = _XmGetInsensitiveStippleBitmap((Widget) sbw);

    sbw->scrollBar.unavailable_GC = XtAllocateGC((Widget) sbw, 0, valueMask,
						 &values, GCClipMask, 
						 unusedMask);
}




/************************************************************************
 *
 *  Logic of the scrollbar pixmap management:
 *  ----------------------------------------
 *     A pixmap the size of the trough area is created each time the
 *     scrollbar changes size.
 *     This pixmap receives the drawing of the slider which is then
 *     copied on the scrollbar window whenever exposure is needed.
 *     GetSliderPixmap:
 *         creates the pixmap and possibly free the current one if present.
 *         the pixmap is free upon destruction of the widget.
 *         the field pixmap == 0 means there is no pixmap to freed.
 *         Is called from Resize method.
 *     DrawSliderPixmap: 
 *         draws the slider graphics (sized shadowed rectangle) in the pixmap.
 *         the fields slider_width and height must have been calculated.
 *         Is called from Resize, after the pixmap has been created,
 *           and from SetValues, if something has changed in the visual 
 *           of the slider.
 *     RedrawSliderWindow:
 *         clears the current scrollbar slider area, computes the
 *         new position and call CopySliderInWindow.
 *         Is called from SetValues method, from increment actions, and
 *         from ChangeScrollBarValue (from Select, Timer).
 *    CopySliderInWindow:
 *         color slider case and then dump the slider pixmap using
 *         XCopyArea. Called from Redisplay and from Move, where
 *         the more expensive RedrawSliderWindow is not needed.
 *
 ************************************************************************/


/************************************************************************
 *
 *  GetSliderPixmap
 *     Create the new pixmap for the slider.
 *     This pixmap is the size of the widget minus the arrows.
 *
 ************************************************************************/
static void 
GetSliderPixmap(
        XmScrollBarWidget sbw )
{

   if (sbw->scrollBar.pixmap)
      XFreePixmap (XtDisplay (sbw), sbw->scrollBar.pixmap);

   sbw->scrollBar.pixmap = 
      XCreatePixmap (XtDisplay(sbw), RootWindowOfScreen(XtScreen(sbw)),
                     sbw->scrollBar.slider_area_width, 
		     sbw->scrollBar.slider_area_height, 
		     sbw->core.depth);
}





/************************************************************************
 *
 *  DrawSliderPixmap
 *     Draw the slider graphic into the pixmap.
 *     Draw the rectangle with a shadow or not and a mark in 
 *     the middle or the side.
 *
 ************************************************************************/
static void 
DrawSliderPixmap(
        XmScrollBarWidget sbw )
{
   register int slider_width = sbw->scrollBar.slider_width;
   register int slider_height = sbw->scrollBar.slider_height;
   register Drawable slider = sbw->scrollBar.pixmap;

   if ((sbw->scrollBar.slider_visual ==  XmFOREGROUND_COLOR) ||
       (sbw->scrollBar.slider_visual ==  XmTROUGH_COLOR)) {   
	   /* we use the same GC, previously filled with either the 
	      foreground or the trough_color pixel */
	   /* The trough area itself has been set, as the window background,
	      to either the trough color (not in that case) or the background
	      pixel */ 
	   XSetClipMask(XtDisplay((Widget) sbw), 
			sbw->scrollBar.flat_slider_GC, 
			None);
	   XFillRectangle (XtDisplay ((Widget) sbw), slider,
			   sbw->scrollBar.flat_slider_GC,
			   0, 0, slider_width, slider_height);
   } else 
   if ((sbw->scrollBar.slider_visual == XmBACKGROUND_COLOR) ||
       (sbw->scrollBar.slider_visual == XmSHADOWED_BACKGROUND)) {
   
       /* in all other case, draw the shadow */
       XFillRectangle (XtDisplay ((Widget) sbw), slider,
		       sbw->scrollBar.foreground_GC,
		       0, 0, slider_width, slider_height);
   
       if (sbw->scrollBar.slider_visual == XmSHADOWED_BACKGROUND)
	   XmeDrawShadows (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, 
			   0, 0, slider_width, slider_height,
			   sbw->primitive.shadow_thickness,
			   XmSHADOW_OUT);
   } 


   if (sbw->scrollBar.sliding_mode == XmTHERMOMETER) {
       /* in thermo mode, the mark must go on the side
	  of the slider, not in the middle.
	  We do that by modifying slider_width or _height
	  up front and share the same code thereafter */
       if (sbw->scrollBar.orientation == XmHORIZONTAL) {
	   if (PROCESS_DIR_INVERSED(sbw)) {
	       slider_width = THERMO_MARK_OFFSET ;
	   } else {
	       slider_width = 2 * slider_width - THERMO_MARK_OFFSET ;
	   }
       } else {
	   if (PROCESS_DIR_INVERSED(sbw)) {
	       slider_height = THERMO_MARK_OFFSET;
	   } else {
	       slider_height = 2 * slider_height - THERMO_MARK_OFFSET;
	   }
       }
   }

   if (sbw->scrollBar.slider_mark == XmETCHED_LINE) {

      if (sbw->scrollBar.orientation == XmHORIZONTAL) {
         XDrawLine (XtDisplay (sbw), slider,
                    sbw->primitive.bottom_shadow_GC,
                    slider_width / 2 - 1, 1, 
                    slider_width / 2 - 1, slider_height - 2);
         XDrawLine (XtDisplay (sbw), slider,
                    sbw->primitive.top_shadow_GC,
                    slider_width / 2, 1, 
                    slider_width / 2, slider_height - 2);
      } else {
         XDrawLine (XtDisplay (sbw), slider,
                    sbw->primitive.bottom_shadow_GC,
                    1, slider_height / 2 - 1,
                    slider_width - 2, slider_height / 2 - 1);
         XDrawLine (XtDisplay (sbw), slider,
                    sbw->primitive.top_shadow_GC,
                    1, slider_height / 2,
                    slider_width - 2, slider_height / 2);
      }
   } else

   if (sbw->scrollBar.slider_mark == XmTHUMB_MARK) {
       Dimension thumb_spacing = 4, margin = 2 ;
       
       if (sbw->scrollBar.orientation == XmHORIZONTAL) {
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   slider_width / 2, 0,
			   2, slider_height, 2, margin,
			   XmVERTICAL, XmSHADOW_ETCHED_OUT);
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   slider_width / 2 - thumb_spacing, 0,
			   2, slider_height, 2, margin,
			   XmVERTICAL, XmSHADOW_ETCHED_OUT);
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   slider_width / 2 + thumb_spacing, 0,
			   2, slider_height, 2, margin,
			   XmVERTICAL, XmSHADOW_ETCHED_OUT);
      }
      else
      {
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   0, slider_height / 2,
			   slider_width, 2, 2, margin,
			   XmHORIZONTAL, XmSHADOW_ETCHED_OUT);
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   0, slider_height / 2 - thumb_spacing,
			   slider_width, 2, 2, margin,
			   XmHORIZONTAL, XmSHADOW_ETCHED_OUT);
         XmeDrawSeparator (XtDisplay (sbw), slider,
			   sbw->primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC, NULL,
			   0, slider_height / 2 + thumb_spacing,
			   slider_width, 2, 2, margin,
			   XmHORIZONTAL, XmSHADOW_ETCHED_OUT);
         
      }
   } 

   if (sbw->scrollBar.slider_mark == XmROUND_MARK) {
       Dimension radius = DEFAULT_ROUND_MARK_RADIUS ;

       XmeDrawCircle(XtDisplay (sbw), slider, 
		     sbw->primitive.top_shadow_GC,
		     sbw->primitive.bottom_shadow_GC, 
		     NULL,
		     slider_width / 2 - radius, 
		     slider_height / 2 - radius, 
		     2*radius, 2*radius, 
		     sbw->primitive.shadow_thickness, 0);
   }
     
}

/************************************************************************
 *
 *  CopySliderInWindow
 *	Dump the slider pixmap into the window using CopyArea.
 *
 ************************************************************************/
static void 
CopySliderInWindow(
        XmScrollBarWidget sbw )
{
    /* use the pixmap that contains the slider graphics */
    if (XtIsRealized((Widget)sbw) && sbw->scrollBar.pixmap) {
	XCopyArea (XtDisplay ((Widget) sbw),
		   sbw->scrollBar.pixmap, XtWindow ((Widget) sbw),
		   sbw->scrollBar.foreground_GC,
		   0, 0,
		   sbw->scrollBar.slider_width, sbw->scrollBar.slider_height,
		   sbw->scrollBar.slider_x, sbw->scrollBar.slider_y);
    }
}

/************************************************************************
 *
 *  RedrawSliderWindow
 *	Clear the trough area at the current slider position,
 *      recompute the slider coordinates and redraw the slider the window by
 *      copying from the pixmap graphics.
 *
 ************************************************************************/
static void 
RedrawSliderWindow(
        XmScrollBarWidget sbw )
{
    short old_slider_width = sbw->scrollBar.slider_width ;
    short old_slider_height = sbw->scrollBar.slider_height ;
    
    if (XtIsRealized((Widget)sbw))
	XClearArea(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
		   (int) sbw->scrollBar.slider_area_x,
		   (int) sbw->scrollBar.slider_area_y,
		   (unsigned int) sbw->scrollBar.slider_area_width,
		   (unsigned int) sbw->scrollBar.slider_area_height,
		   (Bool) FALSE);

    CalcSliderRect(sbw,
		   &(sbw->scrollBar.slider_x),
		   &(sbw->scrollBar.slider_y), 
		   &(sbw->scrollBar.slider_width),
		   &(sbw->scrollBar.slider_height));

    if ((old_slider_width != sbw->scrollBar.slider_width) ||
	(old_slider_height != sbw->scrollBar.slider_height))
	DrawSliderPixmap(sbw);

    CopySliderInWindow(sbw);
}




/************************************************************************
 *
 *  CalcSliderRect
 *     Calculate the slider location and size in pixels so that
 *     it can be drawn.  Note that number and location of pixels
 *     is always positive, so no special case rounding is needed.
 *     DD: better be a CalcSliderPosition and CalcSliderSize, since
 *         this routine is often use for one _or_ the other case.
 *
 ************************************************************************/
static void 
CalcSliderRect(
        XmScrollBarWidget sbw,
        short *slider_x,
        short *slider_y,
        short *slider_width,
        short *slider_height )
{
	float range;
	float trueSize;
	float factor;
	float slideSize;
	int minSliderWidth;
	int minSliderHeight;
	int hitTheWall = 0;
	int value ;

	/* Set up */
	if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
		trueSize =  sbw->scrollBar.slider_area_width;
		minSliderWidth = MIN_SLIDER_LENGTH;
		if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
		    minSliderWidth = 1;
		minSliderHeight = MIN_SLIDER_THICKNESS;
		
	}
	else /* orientation == XmVERTICAL */
	{
		trueSize = sbw->scrollBar.slider_area_height;
		minSliderWidth = MIN_SLIDER_THICKNESS;
		minSliderHeight = MIN_SLIDER_LENGTH;
		if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
		    minSliderHeight = 1;
	}

	/* Total number of user units displayed */
	range = sbw->scrollBar.maximum - sbw->scrollBar.minimum;

	/* A naive notion of pixels per user unit */
	factor = trueSize / range;

	if (PROCESS_DIR_INVERSED(sbw))
	    value = INVERSED_VALUE(sbw);
	else
	    value = sbw->scrollBar.value ;

	/* A naive notion of the size of the slider in pixels */
	/* in thermo, slider_size is 0 ans is ignored */
	if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
	    slideSize = (float) value * factor;
	else
	    slideSize = (float) (sbw->scrollBar.slider_size) * factor;
	    


	/* NOTE SIDE EFFECT */
#define MAX_SCROLLBAR_DIMENSION(val, min)\
	((val) > (min)) ? (val) : (hitTheWall = min)


	/* Don't let the slider get too small */
	if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
		*slider_width = MAX_SCROLLBAR_DIMENSION(
			(int) (slideSize + 0.5), minSliderWidth);
		*slider_height = MAX(sbw->scrollBar.slider_area_height,
			minSliderHeight);
	}
	else /* orientation == XmVERTICAL */
	{
		*slider_width = MAX(sbw->scrollBar.slider_area_width,
			minSliderWidth);
		*slider_height = MAX_SCROLLBAR_DIMENSION((int)
			(slideSize + 0.5), minSliderHeight);
	}

	if (hitTheWall)
	{
		/*
		 * The slider has not been allowed to take on its true
		 * proportionate size (it would have been too small).  This
		 * breaks proportionality of the slider and the conversion
		 * between pixels and user units.
		 *
		 * The factor needs to be tweaked in this case.
		 */

		trueSize -= hitTheWall; /* actual pixels available */
		range -= sbw->scrollBar.slider_size; /* actual range */
	        if (range == 0) range = 1;
		factor = trueSize / range;

	}

	if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
		/* Many parentheses to explicitly control type conversion. */
		if (sbw->scrollBar.sliding_mode == XmTHERMOMETER) {
		    if (PROCESS_DIR_INVERSED(sbw)) {
			*slider_x = sbw->scrollBar.slider_area_x +
			    sbw->scrollBar.slider_area_width - *slider_width;
		    } else {
			*slider_x = sbw->scrollBar.slider_area_x;
		    }
		} else
		    *slider_x = ((int) (((((float) sbw->scrollBar.value)
			- ((float) sbw->scrollBar.minimum)) * factor) + 0.5))
			+ sbw->scrollBar.slider_area_x;
		*slider_y = sbw->scrollBar.slider_area_y ;
	}
	else
	{
		*slider_x = sbw->scrollBar.slider_area_x;
		if (sbw->scrollBar.sliding_mode == XmTHERMOMETER) {
		    if (PROCESS_DIR_INVERSED(sbw)) {
			*slider_y = sbw->scrollBar.slider_area_y +
			    sbw->scrollBar.slider_area_height - *slider_height;
		    } else {
			*slider_y = sbw->scrollBar.slider_area_y ;
		    }
		} else 
		    *slider_y = ((int) (((((float) sbw->scrollBar.value)
			- ((float) sbw->scrollBar.minimum)) * factor) + 0.5))
			+ sbw->scrollBar.slider_area_y;
	}

	/* One final adjustment (of questionable value--preserved
	   for visual backward compatibility) */

	if ((sbw->scrollBar.orientation == XmHORIZONTAL)
		&&
		((*slider_x + *slider_width) > (sbw->scrollBar.slider_area_x
			+ sbw->scrollBar.slider_area_width)))
	{
		*slider_x = sbw->scrollBar.slider_area_x
			+ sbw->scrollBar.slider_area_width - *slider_width;
	}

	if ((sbw->scrollBar.orientation == XmVERTICAL)
		&&
		((*slider_y + *slider_height) > (sbw->scrollBar.slider_area_y
			+ sbw->scrollBar.slider_area_height)))
	{
		*slider_y = sbw->scrollBar.slider_area_y
			+ sbw->scrollBar.slider_area_height - *slider_height;
	}
}




/************************************************************************
 *
 *  Redisplay
 *     General redisplay function called on exposure events.
 *
 ************************************************************************/
static void 
Redisplay(
        Widget wid,
        XEvent *event,
        Region region )
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;


    if (sbw->primitive.shadow_thickness > 0)
	XmeDrawShadows (XtDisplay (sbw), XtWindow (sbw), 
		      sbw->primitive.bottom_shadow_GC, 
		      sbw->primitive.top_shadow_GC,
		      sbw->primitive.highlight_thickness,
		      sbw->primitive.highlight_thickness,
		      sbw->core.width-2 * 
		        sbw->primitive.highlight_thickness,
		      sbw->core.height-2 * 
		        sbw->primitive.highlight_thickness,
		      sbw->primitive.shadow_thickness,
		      XmSHADOW_OUT);

    /* dump the pixmap that contains the slider graphics */
    CopySliderInWindow(sbw);

    if (sbw -> scrollBar.show_arrows) {

	DRAWARROW(sbw, ((sbw->scrollBar.arrow1_selected)?
		 sbw -> primitive.bottom_shadow_GC:
		 sbw -> primitive.top_shadow_GC),
		((sbw->scrollBar.arrow1_selected)?
		 sbw -> primitive.top_shadow_GC :
		 sbw -> primitive.bottom_shadow_GC),
		sbw->scrollBar.arrow1_x,
		sbw->scrollBar.arrow1_y,
		sbw->scrollBar.arrow1_orientation);
	DRAWARROW(sbw, ((sbw->scrollBar.arrow2_selected)?
		 sbw -> primitive.bottom_shadow_GC:
		 sbw -> primitive.top_shadow_GC),
		((sbw->scrollBar.arrow2_selected)?
		 sbw -> primitive.top_shadow_GC :
		 sbw -> primitive.bottom_shadow_GC),
		sbw->scrollBar.arrow2_x, 
		sbw->scrollBar.arrow2_y,
		sbw->scrollBar.arrow2_orientation);
  }

    if (!(XtIsSensitive(wid))) {
        XSetClipMask(XtDisplay(sbw), sbw->scrollBar.unavailable_GC, None);
	XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
		       sbw->scrollBar.unavailable_GC,
		       sbw->primitive.highlight_thickness
		       + sbw->primitive.shadow_thickness,
		       sbw->primitive.highlight_thickness
		       + sbw->primitive.shadow_thickness,
		       XtWidth(sbw) - (2 * (sbw->primitive.highlight_thickness
				+ sbw->primitive.shadow_thickness)),
		       XtHeight(sbw) - (2 * (sbw->primitive.highlight_thickness
				+ sbw->primitive.shadow_thickness)));
    }
#ifdef FUNKY_INSENSITIVE_VISUAL
    else if (sbw->scrollBar.show_arrows)
    {
        XSetClipMask(XtDisplay(sbw), sbw->scrollBar.unavailable_GC, None);
        if (!(sbw->scrollBar.flags & ARROW1_AVAILABLE))
        {
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);
        }
        if (!(sbw->scrollBar.flags & ARROW2_AVAILABLE))
        {
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);
        }
    }
#endif


    /* envelop primitive expose method for highlight */
    {
	XtExposeProc expose;

	_XmProcessLock();
	expose = xmPrimitiveClassRec.core_class.expose;
	_XmProcessUnlock();

	(*(expose))(wid, event, region) ;
    }
   
}





/************************************************************************
 *
 *  Resize
 *     Process resizes on the widget by destroying and recreating the
 *     slider pixmap.
 *     Also draw the correct sized slider onto this pixmap.
 *
 ************************************************************************/
static void 
Resize(
        Widget wid )
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
    register int ht = sbw->primitive.highlight_thickness;
    register int st = sbw->primitive.shadow_thickness;

#define CHECK(x) if (x <= 0) x = 1 

#define BOTH_ARROWS_NEAR_SIDE(sbw) \
	    (((sbw->scrollBar.show_arrows == XmMIN_SIDE) &&\
	      !PROCESS_DIR_INVERSED(sbw)) ||\
	     ((sbw->scrollBar.show_arrows == XmMAX_SIDE) &&\
	      PROCESS_DIR_INVERSED(sbw)))

#define BOTH_ARROWS_FAR_SIDE(sbw) \
	  (((sbw->scrollBar.show_arrows == XmMIN_SIDE) &&\
	    PROCESS_DIR_INVERSED(sbw)) ||\
	   ((sbw->scrollBar.show_arrows == XmMAX_SIDE) &&\
	    !PROCESS_DIR_INVERSED(sbw)))

#define ARROW1_NEAR_SIDE(sbw) \
	  ( (sbw->scrollBar.show_arrows == XmEACH_SIDE) ||\
	   BOTH_ARROWS_NEAR_SIDE (sbw) )

#define ARROW2_FAR_SIDE(sbw) \
	 ((sbw->scrollBar.show_arrows == XmEACH_SIDE) ||\
	  BOTH_ARROWS_FAR_SIDE (sbw) )

    /*  Calculate all of the internal data for slider */

    if (sbw->scrollBar.show_arrows) {

	if (sbw->scrollBar.orientation == XmHORIZONTAL) {

	    sbw->scrollBar.arrow1_orientation = XmARROW_LEFT;
	    sbw->scrollBar.arrow2_orientation = XmARROW_RIGHT;

	    /*  left arrow position and size  */

	    sbw->scrollBar.arrow1_y = ht + st;

	    sbw->scrollBar.arrow_width = 
		sbw->scrollBar.arrow_height = sbw->core.height
		    - 2 * (ht + st);

	    if (ARROW1_NEAR_SIDE (sbw)) {
		 sbw->scrollBar.arrow1_x = ht + st;
	     } else {
		 sbw->scrollBar.arrow1_x = sbw->core.width -ht -st 
		     - 2 * sbw->scrollBar.arrow_width;
	     }
	
	    if (sbw->core.width < 
		2 * (sbw->scrollBar.arrow_width + ht + st)
		+ MIN_SLIDER_LENGTH + 2)
		sbw->scrollBar.arrow_width = (sbw->core.width 
			 - (MIN_SLIDER_LENGTH + 2 + 2 * (ht + st))) / 2;

	    /*  slide area position and size  */

	    if (sbw->scrollBar.show_arrows == XmEACH_SIDE) {
		sbw->scrollBar.slider_area_x = 
		    ht + st + sbw->scrollBar.arrow_width + 1;
	    } else 
	    if (BOTH_ARROWS_NEAR_SIDE (sbw)) {
		sbw->scrollBar.slider_area_x = 
		    ht + st + 2 * sbw->scrollBar.arrow_width + 2;
	    } else {
		sbw->scrollBar.slider_area_x = ht + st ;
	    }

	    sbw->scrollBar.slider_area_width =
		sbw->core.width 
		    - 2 * (ht + st + sbw->scrollBar.arrow_width + 1);

	    if ((2*(ht+st)) > XtHeight(sbw))
		sbw->scrollBar.slider_area_y = XtHeight(sbw) / 2;
	    else
		sbw->scrollBar.slider_area_y = ht + st;

	    sbw->scrollBar.slider_area_height =
		sbw->core.height - 2 * (ht + st);


	    /*  right arrow position  */

	    if (ARROW2_FAR_SIDE(sbw)) {
		sbw->scrollBar.arrow2_x = ht + st
		    + sbw->scrollBar.arrow_width + 1 +
			sbw->scrollBar.slider_area_width + 1;
	    } else {
		sbw->scrollBar.arrow2_x = ht + st
		    + sbw->scrollBar.arrow_width ;
	    }
	    
	    sbw->scrollBar.arrow2_y = ht + st;

	} else { /* VERTICAL */

	    sbw->scrollBar.arrow1_orientation = XmARROW_UP;
	    sbw->scrollBar.arrow2_orientation = XmARROW_DOWN;

	    /*  top arrow position and size  */

	    sbw->scrollBar.arrow1_x = ht + st;

	    sbw->scrollBar.arrow_width = sbw->scrollBar.arrow_height =
		sbw->core.width - 2 * (ht + st);

	    if (ARROW1_NEAR_SIDE(sbw)) {
		sbw->scrollBar.arrow1_y = ht + st;
	    } else {
		sbw->scrollBar.arrow1_y = sbw->core.height -ht -st 
		    - 2 * sbw->scrollBar.arrow_height;
	    }

	    if (sbw->core.height < 
		2 * (sbw->scrollBar.arrow_height + ht + st)
		+ MIN_SLIDER_LENGTH +2)
		sbw->scrollBar.arrow_height = (sbw->core.height
			- (MIN_SLIDER_LENGTH + 2 + 2 * (ht + st))) / 2;

	    /*  slide area position and size  */

	    if (sbw->scrollBar.show_arrows == XmEACH_SIDE) {
		sbw->scrollBar.slider_area_y = 
		    ht + st + sbw->scrollBar.arrow_height + 1;
	    } else 
	    if (BOTH_ARROWS_NEAR_SIDE (sbw)) {
		sbw->scrollBar.slider_area_y = 
		    ht + st + 2 * sbw->scrollBar.arrow_height + 2 ;
	    } else {
		sbw->scrollBar.slider_area_y = ht + st ;
	    }

	    sbw->scrollBar.slider_area_height = sbw->core.height
		- 2 * (ht + st + sbw->scrollBar.arrow_height +1);

	    if ((2*(st+ht)) > XtWidth(sbw))
		sbw->scrollBar.slider_area_x = XtWidth(sbw) / 2;
	    else
		sbw->scrollBar.slider_area_x = ht + st;

	    sbw->scrollBar.slider_area_width = sbw->core.width
		- 2 * (ht + st);


	    /*  down arrow position  */
	    if (ARROW2_FAR_SIDE(sbw)) {
		sbw->scrollBar.arrow2_y = ht + st
		    + sbw->scrollBar.arrow_height + 1 +
			sbw->scrollBar.slider_area_height + 1;
	    } else {
		sbw->scrollBar.arrow2_y = ht + st
		    + sbw->scrollBar.arrow_height ;
	    } 
	    
	    sbw->scrollBar.arrow2_x = ht + st;
	}

	CHECK(sbw->scrollBar.arrow_height);
	CHECK(sbw->scrollBar.arrow_width);
    } else {
	sbw->scrollBar.arrow_width = 0;
	sbw->scrollBar.arrow_height = 0;

	if (sbw->scrollBar.orientation == XmHORIZONTAL) {
	    /*  slide area position and size  */

	    sbw->scrollBar.slider_area_x = ht + st;
	    sbw->scrollBar.slider_area_width = sbw->core.width
		- 2 * (ht + st);

	    if ((2*(ht+st)) > XtHeight(sbw))
		sbw->scrollBar.slider_area_y = XtHeight(sbw) / 2;
	    else
		sbw->scrollBar.slider_area_y = ht + st;
	    sbw->scrollBar.slider_area_height = sbw->core.height
		- 2 * (ht + st);
	} else {
	    /*  slide area position and size  */

	    sbw->scrollBar.slider_area_y = ht + st;
	    sbw->scrollBar.slider_area_height = sbw->core.height
		- 2 * (ht + st);

	    if ((2*(st+ht)) > XtWidth(sbw))
		sbw->scrollBar.slider_area_x = XtWidth(sbw) / 2;
	    else
		sbw->scrollBar.slider_area_x = ht + st;
	    sbw->scrollBar.slider_area_width = sbw->core.width
		- 2 * (ht + st);
	}
    }

    CHECK(sbw->scrollBar.slider_area_height);
    CHECK(sbw->scrollBar.slider_area_width);

    GetSliderPixmap (sbw); /* the size of the scrollbar window - arrows */

    CalcSliderRect(sbw,
		   &(sbw->scrollBar.slider_x),
		   &(sbw->scrollBar.slider_y), 
		   &(sbw->scrollBar.slider_width),
		   &(sbw->scrollBar.slider_height));
	
    DrawSliderPixmap (sbw); 
}




/*********************************************************************
 *
 * Realize
 *
 ********************************************************************/
static void 
Realize(
        Widget wid,
        XtValueMask *window_mask,
        XSetWindowAttributes *window_attributes )
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;

    *window_mask |= CWBitGravity;
    window_attributes->bit_gravity = ForgetGravity;
    
    /* if we are in the slider color = trough color case, we need to
       get the regular background as the trough (= window) color,
       otherwise, we need to get the trough color */
    if (sbw->scrollBar.slider_visual != XmTROUGH_COLOR) {
	*window_mask |= CWBackPixel ;
	window_attributes->background_pixel = sbw->scrollBar.trough_color;
    }    

    XtCreateWindow (wid, InputOutput, CopyFromParent, *window_mask,
		    window_attributes);
}




/************************************************************************
 *
 *  Destroy
 *	Clean up allocated resources when the widget is destroyed.
 *
 ************************************************************************/
static void 
Destroy(
        Widget wid )
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;

    XtReleaseGC ((Widget) sbw, sbw->scrollBar.foreground_GC);
    XtReleaseGC ((Widget) sbw, sbw->scrollBar.unavailable_GC);
    XtReleaseGC ((Widget) sbw, sbw->scrollBar.flat_slider_GC);

    if (sbw->scrollBar.pixmap != 0)
	XFreePixmap (XtDisplay (sbw), sbw->scrollBar.pixmap);

    if (sbw->scrollBar.timer != 0)
     {
       XtRemoveTimeOut (sbw->scrollBar.timer);
       sbw->scrollBar.timer = 0;
     }
}




/************************************************************************
 *
 *  ValidateInputs
 *
 ************************************************************************/
/*ARGSUSED*/
static Boolean 
ValidateInputs(
	       XmScrollBarWidget current,
	       XmScrollBarWidget request, /* unused */
	       XmScrollBarWidget new_w )
{
    Boolean returnFlag = TRUE;
    int value ;

    /* Validate the incoming data  */                      
    
    if (new_w->scrollBar.minimum >= new_w->scrollBar.maximum)
	{
	    new_w->scrollBar.minimum = current->scrollBar.minimum;
	    new_w->scrollBar.maximum = current->scrollBar.maximum;
	    XmeWarning( (Widget) new_w, MESSAGE1);
	    returnFlag = FALSE;
	}

    if (new_w->scrollBar.sliding_mode != current->scrollBar.sliding_mode) {
	if (new_w->scrollBar.sliding_mode != XmTHERMOMETER) {
	    new_w->scrollBar.slider_size = (new_w->scrollBar.maximum
					    - new_w->scrollBar.minimum) / 10;
	    if (new_w->scrollBar.slider_size < 1)
		new_w->scrollBar.slider_size = 1;
	} else 
	    new_w->scrollBar.slider_size = 0 ;
    }

    if (new_w->scrollBar.sliding_mode != XmTHERMOMETER) {

	if (new_w->scrollBar.slider_size < 1) {
	    if ((new_w->scrollBar.maximum - new_w->scrollBar.minimum) <
		current->scrollBar.slider_size)
		new_w->scrollBar.slider_size = new_w->scrollBar.maximum
		    - new_w->scrollBar.minimum;
	    else
		new_w->scrollBar.slider_size = current->scrollBar.slider_size;
	    XmeWarning( (Widget) new_w, MESSAGE2);
	    returnFlag = FALSE;
	}
    
	if ((new_w->scrollBar.slider_size > 
	     new_w->scrollBar.maximum - new_w->scrollBar.minimum)) {
	    if ((new_w->scrollBar.maximum - new_w->scrollBar.minimum) <
		current->scrollBar.slider_size)
		new_w->scrollBar.slider_size = new_w->scrollBar.maximum
		    - new_w->scrollBar.minimum;
	    else
		new_w->scrollBar.slider_size = current->scrollBar.slider_size;
	    XmeWarning( (Widget) new_w, MESSAGE13);
	    returnFlag = FALSE;
	}
    } else 
	new_w->scrollBar.slider_size = 0 ;

    if (new_w->scrollBar.value < new_w->scrollBar.minimum)
	{
	    new_w->scrollBar.value = new_w->scrollBar.minimum;
	    XmeWarning( (Widget) new_w, MESSAGE3);
	    returnFlag = FALSE;
	}
    
    /* do the checking on the real user value */
    if (new_w->scrollBar.value == current->scrollBar.value) {
	if (PROCESS_DIR_INVERSED(new_w))
	    /* use new for value since that's the one getting changed below */
#ifdef FIX_1396
	    value = INVERSED_VALUE(new_w);
#else
	    value = INVERSED_VALUE(current);
#endif	    
	else 
	    value = new_w->scrollBar.value ;
    } else
	value = new_w->scrollBar.value ;

    if (value > new_w->scrollBar.maximum - new_w->scrollBar.slider_size)
	{
	    new_w->scrollBar.value = 
		new_w->scrollBar.maximum - new_w->scrollBar.slider_size;
	    new_w->scrollBar.flags |= VALUE_SET_FLAG;
	    XmeWarning( (Widget) new_w, MESSAGE4);
	}

    if(  !XmRepTypeValidValue( XmRID_ORIENTATION,
			      new_w->scrollBar.orientation, (Widget) new_w))
	{
	    new_w->scrollBar.orientation = current->scrollBar.orientation;
	    returnFlag = FALSE;
	}
    
    if (new_w->scrollBar.orientation == XmHORIZONTAL)
	{
	    if ((new_w->scrollBar.processing_direction != XmMAX_ON_LEFT) &&
		(new_w->scrollBar.processing_direction != XmMAX_ON_RIGHT))
		{
		    new_w->scrollBar.processing_direction = 
			current->scrollBar.processing_direction;
		    XmeWarning( (Widget) new_w, MESSAGE6);
		    returnFlag = FALSE;
		}
	}
    else /* new_w->scrollBar.orientation == XmVERTICAL */
	{
	    if ((new_w->scrollBar.processing_direction != XmMAX_ON_TOP) &&
		(new_w->scrollBar.processing_direction != XmMAX_ON_BOTTOM))
		{
		    new_w->scrollBar.processing_direction =
			current->scrollBar.processing_direction;
		    XmeWarning( (Widget) new_w, MESSAGE6);
		    returnFlag = FALSE;
		}
	}
    
    if (new_w->scrollBar.increment <= 0)
	{
	    new_w->scrollBar.increment = current->scrollBar.increment;
	    XmeWarning( (Widget) new_w, MESSAGE7);
	    returnFlag = FALSE;
	}
    
    if (new_w->scrollBar.page_increment <= 0)
	{
	    new_w->scrollBar.page_increment = 
		current->scrollBar.page_increment;
	    XmeWarning( (Widget) new_w,  MESSAGE8);
	    returnFlag = FALSE;
	}
    
    if (new_w->scrollBar.initial_delay <= 0)
	{
	    new_w->scrollBar.initial_delay = current->scrollBar.initial_delay;
	    XmeWarning( (Widget) new_w, MESSAGE9);
	    returnFlag = FALSE;
	}
    
    if (new_w->scrollBar.repeat_delay <= 0)
	{
	    new_w->scrollBar.repeat_delay = current->scrollBar.repeat_delay;
	    XmeWarning( (Widget) new_w, MESSAGE10);
	    returnFlag = FALSE;
	}
    
    if (new_w->core.width == 0)
	{
	    if (new_w->scrollBar.orientation == XmHORIZONTAL)
		new_w->core.width += 100;
	    else
		new_w->core.width += 11;
	}
    
    if (new_w->core.height == 0)
	{
	    if (new_w->scrollBar.orientation == XmHORIZONTAL)
		new_w->core.height += 11;
	    else
		new_w->core.height += 100;
	}
    
    return(returnFlag);
}

/************************************************************************
 *
 *  SetValues
 *
 ************************************************************************/
/*ARGSUSED*/
static Boolean 
SetValues(
        Widget cw,
        Widget rw,
        Widget nw,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
    XmScrollBarWidget current = (XmScrollBarWidget) cw ;
    XmScrollBarWidget request = (XmScrollBarWidget) rw ;
    XmScrollBarWidget new_w = (XmScrollBarWidget) nw ;
    Boolean returnFlag = FALSE;
    Boolean current_backwards = PROCESS_DIR_INVERSED(current);
    Boolean new_backwards = PROCESS_DIR_INVERSED(new_w);

    

    if(!XmRepTypeValidValue( XmRID_SHOW_ARROWS,
			    new_w->scrollBar.show_arrows, (Widget) new_w) )
	{
	    new_w->scrollBar.show_arrows = current->scrollBar.sliding_mode;
	}

    if(!XmRepTypeValidValue( XmRID_SLIDING_MODE,
			    new_w->scrollBar.sliding_mode, (Widget) new_w) )
	{
	    new_w->scrollBar.sliding_mode = current->scrollBar.sliding_mode;
	}

    if(!XmRepTypeValidValue( XmRID_SLIDER_VISUAL,
			    new_w->scrollBar.slider_visual, (Widget) new_w) )
	{
	    new_w->scrollBar.slider_visual = current->scrollBar.slider_visual;
	}

    if(!XmRepTypeValidValue( XmRID_SLIDER_MARK,
			    new_w->scrollBar.slider_mark, (Widget) new_w) )
	{
	    new_w->scrollBar.slider_mark = current->scrollBar.slider_mark;
	}

    if (new_w->scrollBar.orientation == XmHORIZONTAL)
      {
	if (new_w->scrollBar.processing_direction == XmMAX_ON_LEFT &&
	    !(new_w->scrollBar.flags & VALUE_SET_FLAG) &&
	    ((new_w->scrollBar.slider_size != current->scrollBar.slider_size) ||
	     (new_w->scrollBar.maximum != current->scrollBar.maximum) ||
	     (new_w->scrollBar.minimum != current->scrollBar.minimum) ))
	  
	  {
	    new_w->scrollBar.value = (new_w->scrollBar.maximum
				      + new_w->scrollBar.minimum
				      - new_w->scrollBar.slider_size) -
					  INVERSED_VALUE(current);
	    new_backwards = FALSE;
	    current_backwards = FALSE;
	  }
      }


    /* Make sure that processing direction tracks orientation */
    
    if ((new_w->scrollBar.orientation != current->scrollBar.orientation)
	&&
	(new_w->scrollBar.processing_direction ==
	 current->scrollBar.processing_direction))
	{
	    if ((new_w->scrollBar.orientation == XmHORIZONTAL) &&
		(current->scrollBar.processing_direction == XmMAX_ON_TOP))
		new_w->scrollBar.processing_direction = XmMAX_ON_LEFT;
	    else if ((new_w->scrollBar.orientation == XmHORIZONTAL) &&
		     (current->scrollBar.processing_direction ==
		      XmMAX_ON_BOTTOM))
		new_w->scrollBar.processing_direction = XmMAX_ON_RIGHT;
	    else if ((new_w->scrollBar.orientation == XmVERTICAL) &&
		     (current->scrollBar.processing_direction == XmMAX_ON_LEFT))
		new_w->scrollBar.processing_direction = XmMAX_ON_TOP;
	    else if ((new_w->scrollBar.orientation == XmVERTICAL) &&
		     (current->scrollBar.processing_direction == XmMAX_ON_RIGHT))
		new_w->scrollBar.processing_direction = XmMAX_ON_BOTTOM;
	}
    
    while (!ValidateInputs(current, request, new_w)) /*EMPTY*/;
    
    /*
     * Because someone somewhere originally thought that it was clever
     * for the scrollbar widget to do all of its internal processing in
     * just one direction, all of the interface procedures have to go
     * through extreme gymnastics to support reversal.
     */
    if ((new_backwards && !current_backwards) ||
	(!new_backwards && current_backwards))
	{
	    if (new_w->scrollBar.flags & VALUE_SET_FLAG)
		{
		    if (new_backwards)
			new_w->scrollBar.value = INVERSED_VALUE(new_w);
		}
	    else
		{
		    new_w->scrollBar.value = INVERSED_VALUE(new_w);
		}
	}
    else
	{
	    if ((new_w->scrollBar.flags & VALUE_SET_FLAG) &&
		(new_backwards))
		new_w->scrollBar.value = INVERSED_VALUE(new_w);
	}
    
    if (new_w->scrollBar.flags & VALUE_SET_FLAG)
	new_w->scrollBar.flags &= ~VALUE_SET_FLAG;
    
    /*  See if the GC needs to be regenerated  */
    
    if (new_w->core.background_pixel != current->core.background_pixel)
	{
	    XtReleaseGC((Widget) new_w, new_w->scrollBar.foreground_GC);
	    GetForegroundGC(new_w);
	}

    if (((new_w->scrollBar.slider_visual == XmTROUGH_COLOR) &&
	(new_w->scrollBar.trough_color != current->scrollBar.trough_color)) ||
	((new_w->scrollBar.slider_visual == XmFOREGROUND_COLOR) &&
	 (new_w->primitive.foreground != current->primitive.foreground)))
	{
	    XtReleaseGC((Widget) new_w, new_w->scrollBar.flat_slider_GC);
	    GetFlatSliderGC(new_w);
	}
    
    /*
     * See if the trough (a.k.a the window background) needs to be
     * changed to use a different pixel.
     */
    if (XtIsRealized(nw)) {
	Pixel change_to = XmUNSPECIFIED_PIXEL ;
	/* slider_visual == XmTROUGH_COLOR is the case where the
	   window background is the real core.background_pixel, all the
	   other use the trough_color as the window background */

	if ((new_w->scrollBar.slider_visual == XmTROUGH_COLOR) &&
	    (current->scrollBar.slider_visual != XmTROUGH_COLOR)) {
	    /* no need to care for background change since Core did it */ 
	    change_to = new_w->core.background_pixel;
	}
	if ((new_w->scrollBar.slider_visual != XmTROUGH_COLOR) &&
	    ((current->scrollBar.slider_visual == XmTROUGH_COLOR) ||
	     (new_w->scrollBar.trough_color != current->scrollBar.trough_color) ||
	     /* if the background had changed, Core has certainly reset the
		window background already, so we need to undo that */
	     (new_w->core.background_pixel != current->core.background_pixel))) {
	    change_to = new_w->scrollBar.trough_color ;
	}
	if (change_to != XmUNSPECIFIED_PIXEL) {
	    returnFlag = TRUE;
	    XtReleaseGC((Widget) new_w, new_w->scrollBar.flat_slider_GC);
	    GetFlatSliderGC(new_w);
	    XSetWindowBackground(XtDisplay((Widget)new_w),
				 XtWindow((Widget)new_w), change_to);
	}
    }
    
    /*
     * See if the widget needs to be redrawn.  Minimize the amount
     * of redraw by having specific checks.
     */
    
    if ((new_w->scrollBar.orientation != 
	 current->scrollBar.orientation)         ||
	(new_w->primitive.shadow_thickness !=
	 current->primitive.shadow_thickness)    || 
	(new_w->primitive.highlight_thickness !=
	 current->primitive.highlight_thickness) || 
	(new_w->scrollBar.show_arrows != 
	 current->scrollBar.show_arrows))
	{
	    /* call Resize method, that will have the effect of
	       recomputing all the internal variables (arrow size, 
	       trough are) and recreating the slider pixmap. */
	    XtWidgetProc resize;
	    _XmProcessLock();
	    resize = new_w->core.widget_class->core_class.resize;
	    _XmProcessUnlock();

	    (* (resize)) ((Widget) new_w);
	    returnFlag = TRUE;
	}
    
    if ((new_w->primitive.foreground != 
	 current->primitive.foreground)
	||
	(new_w->core.background_pixel != current->core.background_pixel)
	||
	(new_w->primitive.top_shadow_color !=
	 current->primitive.top_shadow_color)
	||
	(new_w->scrollBar.slider_visual !=
	 current->scrollBar.slider_visual)
	||
	(new_w->scrollBar.slider_mark !=
	 current->scrollBar.slider_mark)
	||
	(new_w->scrollBar.trough_color !=
	 current->scrollBar.trough_color)
	||
	(new_w->primitive.bottom_shadow_color !=
	 current->primitive.bottom_shadow_color))
	{
	    returnFlag = TRUE;
	    /* only draw the slider graphics, no need to change the
	       pixmap (call to GetSliderPixmap) nor the slider size 
	       (call to CalcSliderRect). */
	    DrawSliderPixmap(new_w);
	    
	}
    
    if ((new_w->scrollBar.slider_size != 
	 current->scrollBar.slider_size)                    ||
	(new_w->scrollBar.minimum != current->scrollBar.minimum) ||
	(new_w->scrollBar.maximum != current->scrollBar.maximum) ||
	(new_w->scrollBar.processing_direction != 
	 current->scrollBar.processing_direction)) {
	
	/* have to clear the current slider before setting the
	   new slider position and size */
	if (XtIsRealized(nw))
	    XClearArea(XtDisplay((Widget)new_w), 
		       XtWindow((Widget)new_w),
		       new_w->scrollBar.slider_x, 
		       new_w->scrollBar.slider_y,
		       new_w->scrollBar.slider_width,
		       new_w->scrollBar.slider_height, False);
	    
	/* recompute the slider size and draw in the pixmap */
	CalcSliderRect(new_w,
		       &(new_w->scrollBar.slider_x),
		       &(new_w->scrollBar.slider_y), 
		       &(new_w->scrollBar.slider_width),
		       &(new_w->scrollBar.slider_height));
	
	/* redraw the slider in the pixmap */
	DrawSliderPixmap (new_w); 

	if (new_w->scrollBar.slider_size >= (new_w->scrollBar.maximum
					     - new_w->scrollBar.minimum))
	    {
		new_w->scrollBar.flags &= ~SLIDER_AVAILABLE;
		/*
		 * Disabling the slider enables the arrows.  This 
		 * leaves the scrollbar in a state amenable to reenabling
		 * the slider.
		 */
		new_w->scrollBar.flags |= ARROW1_AVAILABLE;
		new_w->scrollBar.flags |= ARROW2_AVAILABLE;
		returnFlag = TRUE;	
	    }
	else
	    {
		if (! (new_w->scrollBar.flags & SLIDER_AVAILABLE)) {
		    returnFlag = TRUE;
		    new_w->scrollBar.flags |= SLIDER_AVAILABLE;
		} else {
		    /* directly use the pixmap that contains the slider 
		       graphics, no need to call RedrawSliderWindow since the
		       cleararea and the calcrect have already been made */
		    CopySliderInWindow(new_w);
		}
	    }
    }
    
    
    if (new_w->scrollBar.value != current->scrollBar.value) {
	/* the value has changed, the slider needs to move. */
	RedrawSliderWindow (new_w);

	if (XtIsRealized(nw))
	{
	/* Following lines taken from Redisplay code; the XmNvalue can change
        ** even when the widget is insensitive. Other paths through the code
	** involve user interaction and so sensitivity doesn't need to be 
	** considered.
	** NOTE! doesn't deal with FUNKY_INSENSITIVE_VISUAL
	*/
	    if (!(XtIsSensitive((Widget)new_w))) {
		XmScrollBarWidget sbw = (XmScrollBarWidget) new_w;
		XSetClipMask(XtDisplay(sbw), sbw->scrollBar.unavailable_GC, None);
		XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
			       sbw->scrollBar.unavailable_GC,
			       sbw->primitive.highlight_thickness
			       + sbw->primitive.shadow_thickness,
			       sbw->primitive.highlight_thickness
			       + sbw->primitive.shadow_thickness,
			       XtWidth(sbw) - (2 * (sbw->primitive.highlight_thickness
					+ sbw->primitive.shadow_thickness)),
			       XtHeight(sbw) - (2 * (sbw->primitive.highlight_thickness
					+ sbw->primitive.shadow_thickness)));
	    }
	}
    }
    
    if (XtIsSensitive(nw) != XtIsSensitive(cw))
	returnFlag = TRUE;
    
    return(returnFlag);
}




/************************************************************************
 *
 *  CalcSliderVal
 *     Calculate the slider val in application coordinates given
 *     the input x and y.
 *
 ************************************************************************/
static int 
CalcSliderVal(
        XmScrollBarWidget sbw,
        int x,
        int y )
{
	float range;
	float trueSize;       /* size of slider area in pixels */
	float referencePoint; /* origin of slider */
	float proportion;
	int int_proportion;
	int slider_area_origin;


	if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
	    referencePoint = (float) x - sbw->scrollBar.separation_x;
	    trueSize = sbw->scrollBar.slider_area_width;
	    if (sbw->scrollBar.sliding_mode != XmTHERMOMETER)
		trueSize -= sbw->scrollBar.slider_width;
	    slider_area_origin = sbw->scrollBar.slider_area_x;
	}
	else
	{
	    referencePoint = (float) y - sbw->scrollBar.separation_y;
	    trueSize = sbw->scrollBar.slider_area_height;
	    if (sbw->scrollBar.sliding_mode != XmTHERMOMETER)
		trueSize -= sbw->scrollBar.slider_height;
	    slider_area_origin = sbw->scrollBar.slider_area_y;
	}

	if (trueSize > 0)
		
	    /* figure the proportion of slider area between the origin
	       of the slider area and the origin of the slider. */
	    proportion = (referencePoint - slider_area_origin
		 + (((sbw->scrollBar.show_arrows == XmEACH_SIDE) &&
		    (sbw->scrollBar.sliding_mode != XmTHERMOMETER))?1:0)) / 
			      trueSize;
	else
		/*
		 * We've got an interesting problem here.  There isn't any
		 * slider area available to slide in.  What should the value
		 * of the scrollbar be when the user tries to drag the slider?  
		 *
		 * Setting proportion to 1 snaps to maximum.  Setting
		 * proportion to the reciprocal of "range" will cause the
		 * slider to snap to the minimum.
		 *
		 */ 
		proportion = 1;

	/* Actual range displayed */
	range = sbw->scrollBar.maximum - sbw->scrollBar.minimum
		- sbw->scrollBar.slider_size;

	/* Now scale the proportion in pixels to user units */
	proportion = (proportion * range)
		+ ((float) sbw->scrollBar.minimum);
	
	/* Round off appropriately */
	if (proportion > 0)
		proportion += 0.5;
	else if (proportion < 0)
		proportion -= 0.5;

	int_proportion = (int) proportion;

	if (int_proportion < sbw->scrollBar.minimum)
		int_proportion = sbw->scrollBar.minimum;
	else if (int_proportion > (sbw->scrollBar.maximum
			- sbw->scrollBar.slider_size))
		int_proportion = sbw->scrollBar.maximum
			- sbw->scrollBar.slider_size;

	return (int_proportion);
}




/************************************************************************
 *
 *  Select
 *     This function processes selections occuring on the scrollBar.
 *
 ************************************************************************/
static void 
Select(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
    XButtonPressedEvent *buttonEvent = (XButtonPressedEvent *) event ;
    int slider_x = sbw->scrollBar.slider_x;
    int slider_y = sbw->scrollBar.slider_y;
    int slider_width = sbw->scrollBar.slider_width;
    int slider_height = sbw->scrollBar.slider_height;
    Boolean slider_moved;
     
    if (!sbw->scrollBar.editable) return ;


    /* add a start update when the button is pressed
       so that scrollbar moves generating widget
       configurations be bracketed for dropsite update.
       The endupdate is done in Release */
	       
    XmDropSiteStartUpdate(wid);
    
    sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;

#ifndef DEBUG_NO_SB_GRAB
    if (XtGrabKeyboard(wid, False, GrabModeAsync,
		       GrabModeAsync, buttonEvent->time) == GrabSuccess)
	sbw->scrollBar.flags |= KEYBOARD_GRABBED;
#endif

    XAllowEvents(XtDisplay(wid), AsyncPointer, CurrentTime);
    XAllowEvents(XtDisplay(wid), AsyncKeyboard, CurrentTime);
    
    if (!(sbw->scrollBar.flags & SLIDER_AVAILABLE))
	return;
    if ((buttonEvent->button == Button1) &&
	(!XmIsScrolledWindow(XtParent(wid))))
	(void) XmProcessTraversal( (Widget) sbw, XmTRAVERSE_CURRENT);
    
    sbw->scrollBar.separation_x = 0;
    sbw->scrollBar.separation_y = 0;


    if ((sbw->scrollBar.orientation == XmHORIZONTAL) &&
	(buttonEvent->y >= slider_y)                      &&
	(buttonEvent->y <= slider_y + slider_height)  &&
	(buttonEvent->button == Button1) &&
	(sbw->scrollBar.sliding_mode == XmTHERMOMETER) &&
	(((PROCESS_DIR_INVERSED(sbw)) &&
	  (buttonEvent->x >= slider_x) &&
	  (buttonEvent->x <= slider_x + THERMO_MARK_OFFSET)) ||
	 (!PROCESS_DIR_INVERSED(sbw) &&
	  (buttonEvent->x <= slider_x + slider_width)   &&
	  (buttonEvent->x >= slider_x + slider_width - THERMO_MARK_OFFSET))))
	/* hack */
	buttonEvent->button = Button2 ;
	
    if ((sbw->scrollBar.orientation == XmVERTICAL) &&
	(buttonEvent->x >= slider_x) &&
	(buttonEvent->x <= slider_x + slider_width)   &&
	(buttonEvent->button == Button1) &&
	(sbw->scrollBar.sliding_mode == XmTHERMOMETER) &&
	(((PROCESS_DIR_INVERSED(sbw)) &&
	  (buttonEvent->y >= slider_y) &&
	  (buttonEvent->y <= slider_y + THERMO_MARK_OFFSET)) ||
	 (!PROCESS_DIR_INVERSED(sbw) &&
	  (buttonEvent->y >= slider_y + slider_height - THERMO_MARK_OFFSET) &&
	  (buttonEvent->y <= slider_y + slider_height))))
	/* hack */
	buttonEvent->button = Button2 ;
	
    /*  Calculate whether the selection point is in the slider  */
    if ((buttonEvent->x >= slider_x)                 &&
	(buttonEvent->x <= slider_x + slider_width)   &&
	(buttonEvent->y >= slider_y)                      &&
	(buttonEvent->y <= slider_y + slider_height) &&
	((buttonEvent->button != Button1) ||
	 (sbw->scrollBar.sliding_mode != XmTHERMOMETER)))
	{
	    sbw->scrollBar.initial_x = slider_x;
	    sbw->scrollBar.initial_y = slider_y;
	    sbw->scrollBar.sliding_on = True;
	    sbw->scrollBar.saved_value = sbw->scrollBar.value;
	    sbw->scrollBar.arrow1_selected = FALSE;
	    sbw->scrollBar.arrow2_selected = FALSE;

	    if ((buttonEvent->button == Button1) &&
		(sbw->scrollBar.sliding_mode != XmTHERMOMETER))
		{
		    sbw->scrollBar.separation_x = buttonEvent->x - slider_x;
		    sbw->scrollBar.separation_y = buttonEvent->y - slider_y;
		}
	    else  if (buttonEvent->button == Button2)
		{
		    /* Warp the slider to the cursor, and then drag */
		    if  (sbw->scrollBar.sliding_mode != XmTHERMOMETER) {
			if (sbw->scrollBar.orientation == XmHORIZONTAL)
			    sbw->scrollBar.separation_x = 
				sbw->scrollBar.slider_width / 2;
			else
			    sbw->scrollBar.separation_y = 
				sbw->scrollBar.slider_height / 2;
		    } else {
			sbw->scrollBar.separation_x = 0 ;
			sbw->scrollBar.separation_y = 0 ;
		    }
		    Moved ((Widget) sbw, (XEvent *) buttonEvent,
			   params, num_params);
		}
	    
	    return;
	}
    
    /* ... in the trough (i.e. slider area)... */
    else if ((buttonEvent->x >= sbw->scrollBar.slider_area_x)   &&
	     (buttonEvent->y >= sbw->scrollBar.slider_area_y)        &&
	     (buttonEvent->x <= sbw->scrollBar.slider_area_x 
	      + sbw->scrollBar.slider_area_width)                 &&
	     (buttonEvent->y <= sbw->scrollBar.slider_area_y 
	      + sbw->scrollBar.slider_area_height)) {

	sbw->scrollBar.arrow1_selected = FALSE;
	sbw->scrollBar.arrow2_selected = FALSE;
	sbw->scrollBar.saved_value = sbw->scrollBar.value;
	
	if (buttonEvent->button == Button1) {
	    Position limit_x, limit_y ;

	    /* Page the slider up or down */
	    /* what is up or down depends on the processing direction... */

	    limit_x = sbw->scrollBar.slider_x ;
	    limit_y = sbw->scrollBar.slider_y ;
	    if  (sbw->scrollBar.sliding_mode == XmTHERMOMETER) {
		if (PROCESS_DIR_INVERSED(sbw)) {
		    limit_x = sbw->scrollBar.slider_area_width -
			sbw->scrollBar.slider_width ;
		    limit_y = sbw->scrollBar.slider_area_height -
			sbw->scrollBar.slider_height ;
		} else {
		    limit_x = sbw->scrollBar.slider_width ;
		    limit_y = sbw->scrollBar.slider_height ;
		}
	    }

	    if (sbw->scrollBar.orientation == XmHORIZONTAL) {
		if (buttonEvent->x < limit_x)
		    sbw->scrollBar.change_type = XmCR_PAGE_DECREMENT;
		else
		    sbw->scrollBar.change_type = XmCR_PAGE_INCREMENT;
	    }
	    else
		{
		    if (buttonEvent->y < limit_y)
			sbw->scrollBar.change_type = XmCR_PAGE_DECREMENT;
		    else
			sbw->scrollBar.change_type = XmCR_PAGE_INCREMENT;
		}
	    slider_moved = ChangeScrollBarValue(sbw);
	}
	else  /* Button2 */ {
		/* Warp the slider to the cursor, and then drag */
		
		 if  (sbw->scrollBar.sliding_mode != XmTHERMOMETER) {
			if (sbw->scrollBar.orientation == XmHORIZONTAL)
			    sbw->scrollBar.separation_x = 
				sbw->scrollBar.slider_width / 2;
			else
			    sbw->scrollBar.separation_y = 
				sbw->scrollBar.slider_height / 2;
		    } else {
			sbw->scrollBar.separation_x = 0 ;
			sbw->scrollBar.separation_y = 0 ;
		    }
		
		sbw->scrollBar.initial_x = slider_x;
		sbw->scrollBar.initial_y = slider_y;
		sbw->scrollBar.sliding_on = True;

		Moved ((Widget) sbw, (XEvent *) buttonEvent,
		       params, num_params);
		return;
	    }
    }
    
    /* ... in arrow 1 */
    else if ((buttonEvent->x >= sbw->scrollBar.arrow1_x)  &&
	     (buttonEvent->y >= sbw->scrollBar.arrow1_y)       &&
	     (buttonEvent->x <= sbw->scrollBar.arrow1_x 
	      + sbw->scrollBar.arrow_width)                 &&
	     (buttonEvent->y <= sbw->scrollBar.arrow1_y 
	      + sbw->scrollBar.arrow_height))
	{   
	    sbw->scrollBar.change_type = XmCR_DECREMENT;
	    sbw->scrollBar.saved_value = sbw->scrollBar.value;
	    sbw->scrollBar.arrow1_selected = True;
	    
	    slider_moved = ChangeScrollBarValue(sbw) ;
	    DRAWARROW(sbw, sbw->primitive.bottom_shadow_GC,
		      sbw -> primitive.top_shadow_GC,
		      sbw->scrollBar.arrow1_x,
		      sbw->scrollBar.arrow1_y,
		      sbw->scrollBar.arrow1_orientation);
	}
    
    /* ... in arrow 2 */
    else if ((buttonEvent->x >= sbw->scrollBar.arrow2_x)      &&
	     (buttonEvent->y >= sbw->scrollBar.arrow2_y)           &&
	     (buttonEvent->x <= sbw->scrollBar.arrow2_x 
	      + sbw->scrollBar.arrow_width)                     &&
	     (buttonEvent->y <= sbw->scrollBar.arrow2_y 
	      + sbw->scrollBar.arrow_height))
	{
	    sbw->scrollBar.change_type = XmCR_INCREMENT;
	    sbw->scrollBar.saved_value = sbw->scrollBar.value;
	    sbw->scrollBar.arrow2_selected = True;
	    
	    slider_moved = ChangeScrollBarValue(sbw) ;
	    DRAWARROW(sbw, sbw->primitive.bottom_shadow_GC,
		      sbw -> primitive.top_shadow_GC,
		      sbw->scrollBar.arrow2_x,
		      sbw->scrollBar.arrow2_y,
		      sbw->scrollBar.arrow2_orientation);
	}
    else
	/* ... in the highlight area.  */
	return;
    
    if (slider_moved) {
	    
	ScrollCallback (sbw, sbw->scrollBar.change_type, 
			sbw->scrollBar.value, 0, 0, (XEvent *) buttonEvent);
	    
	XSync (XtDisplay((Widget)sbw), False);
	    
	sbw->scrollBar.flags |= FIRST_SCROLL_FLAG ;
	sbw->scrollBar.flags &= ~END_TIMER;
	    
	    
	if (!sbw->scrollBar.timer)
	    sbw->scrollBar.timer = XtAppAddTimeOut
		(XtWidgetToApplicationContext((Widget) sbw),
		 (unsigned long) sbw->scrollBar.initial_delay,
		 TimerEvent, (XtPointer) sbw);
    }
}




/************************************************************************
 *
 *  Release
 *     This function processes releases occuring on the scrollBar.
 *
 ************************************************************************/
/*ARGSUSED*/
static void 
Release(
        Widget wid,
        XEvent *event,
        String *params,		/* unused */
        Cardinal *num_params )	/* unused */
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;

    if (!sbw->scrollBar.editable) return ;


    /* add an end update when the button is released.
       see comment in Select for the start update */

    XmDropSiteEndUpdate(wid);

    sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;
    
    if (sbw->scrollBar.flags & KEYBOARD_GRABBED)
	{
	    XtUngrabKeyboard(wid, ((XButtonPressedEvent *)event)->time);
	    sbw->scrollBar.flags &= ~KEYBOARD_GRABBED;
	}
    
#ifdef FUNKY_INSENSITIVE_VISUAL
    if ( (!(sbw->scrollBar.flags & ARROW1_AVAILABLE)) &&
	(sbw->scrollBar.value > sbw->scrollBar.minimum))
	{
	    XClearArea(XtDisplay(sbw), XtWindow(sbw),
		       sbw->scrollBar.arrow1_x,
		       sbw->scrollBar.arrow1_y,
		       sbw->scrollBar.arrow_width,
		       sbw->scrollBar.arrow_height,
		       FALSE);
	    
	    DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
		       sbw->primitive.bottom_shadow_GC,
		       sbw->scrollBar.arrow1_x,
		       sbw->scrollBar.arrow1_y,
		       sbw->scrollBar.arrow1_orientation);
	    
	    sbw->scrollBar.flags |= ARROW1_AVAILABLE;
	}
    else if (sbw->scrollBar.value == sbw->scrollBar.minimum)
	sbw->scrollBar.flags &= ~ARROW1_AVAILABLE;
    
    if ( (!(sbw->scrollBar.flags & ARROW2_AVAILABLE)) &&
	(sbw->scrollBar.value < (sbw->scrollBar.maximum
				 - sbw->scrollBar.slider_size)))
	{
	    XClearArea(XtDisplay(sbw), XtWindow(sbw),
		       sbw->scrollBar.arrow2_x,
		       sbw->scrollBar.arrow2_y,
		       sbw->scrollBar.arrow_width,
		       sbw->scrollBar.arrow_height,
		       FALSE);
	    
	    DRAWARROW (sbw, sbw->primitive.top_shadow_GC,
		       sbw -> primitive.bottom_shadow_GC,
		       sbw->scrollBar.arrow2_x,
		       sbw->scrollBar.arrow2_y,
		       sbw->scrollBar.arrow2_orientation);
	    
	    sbw->scrollBar.flags |= ARROW2_AVAILABLE;
	}
    else if (sbw->scrollBar.value == (sbw->scrollBar.maximum
				      - sbw->scrollBar.slider_size))
	sbw->scrollBar.flags &= ~ARROW2_AVAILABLE;
#endif
    if (sbw->scrollBar.arrow1_selected)
	{
	    sbw->scrollBar.arrow1_selected = False;
	    
	    DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
		       sbw->primitive.bottom_shadow_GC,
		       sbw->scrollBar.arrow1_x,
		       sbw->scrollBar.arrow1_y,
		       sbw->scrollBar.arrow1_orientation);
	}
    
    if (sbw->scrollBar.arrow2_selected)
	{
	    sbw->scrollBar.arrow2_selected = False;
	    
	    DRAWARROW (sbw, sbw->primitive.top_shadow_GC,
		       sbw -> primitive.bottom_shadow_GC,
		       sbw->scrollBar.arrow2_x,
		       sbw->scrollBar.arrow2_y,
		       sbw->scrollBar.arrow2_orientation);
	}
    
    if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
        return;

    if (sbw->scrollBar.timer != 0)
	{
	    sbw->scrollBar.flags |= END_TIMER;
	}
    
    if (sbw->scrollBar.sliding_on == True)
	{
	    sbw->scrollBar.sliding_on = False;
	    ScrollCallback (sbw, XmCR_VALUE_CHANGED, sbw->scrollBar.value, 
			    event->xbutton.x, event->xbutton.y, event);
	}
    
#ifdef FUNKY_INSENSITIVE_VISUAL
    XSetClipMask(XtDisplay(sbw), sbw->scrollBar.unavailable_GC, None);
    if (! (sbw->scrollBar.flags & ARROW1_AVAILABLE))
	{
	    XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
			   sbw->scrollBar.unavailable_GC,
			   sbw->scrollBar.arrow1_x,
			   sbw->scrollBar.arrow1_y,
			   sbw->scrollBar.arrow_width,
			   sbw->scrollBar.arrow_height);
	}
    else if (! (sbw->scrollBar.flags & ARROW2_AVAILABLE))
	{
	    XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
			   sbw->scrollBar.unavailable_GC,
			   sbw->scrollBar.arrow2_x,
			   sbw->scrollBar.arrow2_y,
			   sbw->scrollBar.arrow_width,
			   sbw->scrollBar.arrow_height);
	}
#endif
}




/************************************************************************
 *
 *  Moved
 *     This function processes mouse moved events during interactive
 *     slider moves.
 *
 ************************************************************************/
/*ARGSUSED*/
static void 
Moved(
        Widget wid,
        XEvent *event,
        String *params,		/* unused */
        Cardinal *num_params )	/* unused */
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
    XButtonPressedEvent * buttonEvent = (XButtonPressedEvent *) event;
    int newX, newY;
    int realX, realY;
    int slideVal;
    int button_x;
    int button_y;
    int real_width_limit = 
	(sbw->scrollBar.snap_back_multiple + 
	 (buttonEvent->x > 0)) * XtWidth(wid);
    int real_height_limit = 
	(sbw->scrollBar.snap_back_multiple + 
	 (buttonEvent->y > 0)) * XtHeight(wid) ;

    if (!sbw->scrollBar.editable) return ;

    if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE)) return;
    
    /* operation was cancelled, so don't restart the move
       as it could happen with the snapBack stuff */
    if (sbw->scrollBar.flags & OPERATION_CANCELLED) return;
    
    if (!sbw->scrollBar.sliding_on) return;
    
    /* Only deal with snap_back if operation was started by a 
       click in the slider. */
    if (((sbw->scrollBar.orientation == XmVERTICAL) &&
	 ((buttonEvent->x > real_width_limit) ||
	  (-buttonEvent->x > real_width_limit))) ||
	((sbw->scrollBar.orientation == XmHORIZONTAL) &&
	 ((buttonEvent->y > real_height_limit) ||
	  (-buttonEvent->y > real_height_limit)))) {
	/* going out of the snap back area */
	
	if (!(sbw->scrollBar.add_flags & SNAPPED_OUT)) {
	    short savedX, savedY, j1, j2;
	    
	    /* get the saved value, also used by the Cancel action */
	    sbw->scrollBar.value = sbw->scrollBar.saved_value;
	    CalcSliderRect(sbw, &savedX, &savedY, &j1, &j2);
	    MoveSlider(sbw, savedX, savedY);
	    if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
	    ScrollCallback (sbw, XmCR_VALUE_CHANGED,
			    sbw->scrollBar.value, savedX, savedY, 
			    (XEvent *) buttonEvent);
	    
	    sbw->scrollBar.add_flags |= SNAPPED_OUT ;
	}
	return ;
    } else {
	/* moving in the snap back area */
	sbw->scrollBar.add_flags &= ~SNAPPED_OUT ; ;
    }
    
    button_x = buttonEvent->x;
    button_y = buttonEvent->y;
    
    /*
     * Force button_x and button_y to be within the slider_area.
     */
    if (button_x < sbw->scrollBar.slider_area_x)
	button_x = sbw->scrollBar.slider_area_x;
    
    if (button_x > 
	sbw->scrollBar.slider_area_x + sbw->scrollBar.slider_area_width)
	button_x = sbw->scrollBar.slider_area_x 
	    + sbw->scrollBar.slider_area_width;
    
    if (button_y < sbw->scrollBar.slider_area_y)
	button_y = sbw->scrollBar.slider_area_y;
    
    if (button_y > 
	sbw->scrollBar.slider_area_y 
	+ sbw->scrollBar.slider_area_height)
	button_y = sbw->scrollBar.slider_area_y 
	    + sbw->scrollBar.slider_area_height;
    
    
    /*
     * Calculate the new origin of the slider.  
     * Bound the values with the slider area.
     */
    if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
	    newX = realX = button_x - sbw->scrollBar.separation_x;
	    newY = realY = sbw->scrollBar.slider_y;
	    
	    if (newX < sbw->scrollBar.slider_area_x)
		newX = sbw->scrollBar.slider_area_x;
	    
	    if ((newX + sbw->scrollBar.slider_width > 
		sbw->scrollBar.slider_area_x 
		+ sbw->scrollBar.slider_area_width) &&
		    (sbw->scrollBar.sliding_mode != XmTHERMOMETER))
		newX = sbw->scrollBar.slider_area_x
		    + sbw->scrollBar.slider_area_width
			- sbw->scrollBar.slider_width;
	}
    else
	{
	    newX = realX = sbw->scrollBar.slider_x;
	    newY = realY = button_y - sbw->scrollBar.separation_y;
	    
	    
	    if (newY < sbw->scrollBar.slider_area_y)
		newY = sbw->scrollBar.slider_area_y;
	    
	    if ((newY + sbw->scrollBar.slider_height > 
		sbw->scrollBar.slider_area_y 
		+ sbw->scrollBar.slider_area_height) &&
		    (sbw->scrollBar.sliding_mode != XmTHERMOMETER))
		newY = sbw->scrollBar.slider_area_y 
		    + sbw->scrollBar.slider_area_height
			- sbw->scrollBar.slider_height;
	}
    
    
    if (((sbw->scrollBar.orientation == XmHORIZONTAL) && 
	 (realX != sbw->scrollBar.initial_x))
	||
	((sbw->scrollBar.orientation == XmVERTICAL)   &&
	 (realY != sbw->scrollBar.initial_y)))
	{
	    slideVal = CalcSliderVal (sbw, button_x, button_y);
	    
	    if ((newX != sbw->scrollBar.initial_x) || 
		(newY != sbw->scrollBar.initial_y))
		{
		    MoveSlider (sbw, newX, newY);
		    sbw->scrollBar.initial_x = newX;
		    sbw->scrollBar.initial_y = newY;
		}
	    
	    if (slideVal != sbw->scrollBar.value)
		{
		    sbw->scrollBar.value = slideVal;
		    
		    if (slideVal >= (sbw->scrollBar.maximum
				     - sbw->scrollBar.slider_size))
			slideVal = sbw->scrollBar.maximum
			    - sbw->scrollBar.slider_size;
		    
		    if (slideVal <= sbw->scrollBar.minimum)
			slideVal = sbw->scrollBar.minimum;
		    if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
			RedrawSliderWindow (sbw);
		    ScrollCallback(sbw, XmCR_DRAG, 
				   sbw->scrollBar.value = slideVal, 
				   buttonEvent->x, buttonEvent->y,
				   (XEvent *) buttonEvent);
		}
	}
}




/*********************************************************************
 *
 *  TopOrBottom
 *	Issue the to top or bottom callbacks.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
TopOrBottom(
        Widget wid,
        XEvent *event,
        String *params,		/* unused */
        Cardinal *num_params )	/* unused */
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
    XmScrollBarPart *sbp = (XmScrollBarPart *) &(sbw->scrollBar);

    if (!sbw->scrollBar.editable) return ;

    sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;
    
    if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
	return;
    
    if (event->type == KeyPress) {
	Modifiers junk;
	KeySym key_sym;
	XKeyPressedEvent *keyEvent = (XKeyPressedEvent *) event;
	
	key_sym = XtGetActionKeysym(event, &junk);
	
	if (key_sym == osfXK_BeginLine) {
	    if (sbp->orientation == XmVERTICAL) {
		if (sbp->processing_direction == XmMAX_ON_BOTTOM)
		    MoveSlider(sbw, sbp->slider_x, sbp->slider_area_y);
		else
		    MoveSlider(sbw,
			       sbp->slider_x, 
			       sbp->slider_area_y + sbp->slider_area_height
			       - sbp->slider_height);
	    } else {
		if (sbp->processing_direction == XmMAX_ON_RIGHT)
		    MoveSlider(sbw, sbp->slider_area_x, sbp->slider_y);
		else
		    MoveSlider(sbw,
			       sbp->slider_area_x + sbp->slider_area_width
			       - sbp->slider_width,
			       sbp->slider_y);
	    }
	    /*
	     * The following grevious bogosity is due to the fact
	     * that the key behavior was implemented long after 
	     * the rest of this code, and so we have to work around
	     * currently operating nonsense.
	     *
	     * Specifically, since the dawn of time, ScrollBar
	     * processes in just one direction, and does any necessary
	     * reversal just before calling the callback.
	     *
	     * We now proceed to trick that code into doing the right
	     * thing anyway
	     */
	    if (!PROCESS_DIR_INVERSED(sbw)) {
		sbp->value = sbp->minimum;
		if (sbp->sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback(sbw, XmCR_TO_TOP, sbp->value,
			       keyEvent->x, keyEvent->y,
			       (XEvent *) keyEvent);
	    } else {
		sbp->value = sbp->maximum - sbp->slider_size;
		if (sbp->sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback(sbw, XmCR_TO_BOTTOM, sbp->value,
			       keyEvent->x, keyEvent->y,
			       (XEvent *) keyEvent);
	    }
	}
	else /* key_sym == osfXK_EndLine */ {
	    if (sbp->orientation == XmVERTICAL) {
		if (sbp->processing_direction == XmMAX_ON_BOTTOM)
		    MoveSlider(sbw,
			       sbp->slider_x, 
			       sbp->slider_area_y + sbp->slider_area_height
			       - sbp->slider_height);
		else
		    MoveSlider(sbw, sbp->slider_x, sbp->slider_area_y);
	    } else {
		if (sbp->processing_direction == XmMAX_ON_RIGHT)
		    MoveSlider(sbw,
			       sbp->slider_area_x + sbp->slider_area_width
			       - sbp->slider_width,
			       sbp->slider_y);
		else
		    MoveSlider(sbw, sbp->slider_area_x, sbp->slider_y);
	    }
	    
	    /* See above for explanation of this nonsense */
	    if (!PROCESS_DIR_INVERSED(sbw)) {
		sbp->value = sbp->maximum - sbp->slider_size;
		if (sbp->sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback(sbw, XmCR_TO_BOTTOM, sbp->value,
			       keyEvent->x, keyEvent->y,
			       (XEvent *) keyEvent);
	    } else {
		sbp->value = sbp->minimum;
		if (sbp->sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback (sbw, XmCR_TO_TOP, sbp->value,
				keyEvent->x, keyEvent->y,
				(XEvent *) keyEvent);
	    }
	}
    } else  /* event->type == ButtonPress */ {
	XButtonPressedEvent *buttonEvent =
	    (XButtonPressedEvent *) event;
	
	XmDropSiteStartUpdate(wid);
	
	if /* In arrow1... */
	    ((buttonEvent->x >= sbp->arrow1_x)                   &&
	     (buttonEvent->y >= sbp->arrow1_y)                    &&
	     (buttonEvent->x <= sbp->arrow1_x + sbp->arrow_width) &&
	     (buttonEvent->y <= sbp->arrow1_y + sbp->arrow_height)) {
		sbp->change_type = XmCR_DECREMENT;
		sbp->arrow1_selected = True;
		
		DRAWARROW(sbw, sbw->primitive.bottom_shadow_GC,
			  sbw -> primitive.top_shadow_GC,
			  sbw->scrollBar.arrow1_x,
			  sbw->scrollBar.arrow1_y,
			  sbw->scrollBar.arrow1_orientation);
		
		if (sbp->orientation == XmVERTICAL)
		    MoveSlider(sbw, sbp->slider_x, sbp->slider_area_y);
		else
		    MoveSlider(sbw, sbp->slider_area_x, sbp->slider_y);

		sbp->value = sbp->minimum;
		if (sbp->sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback (sbw, XmCR_TO_TOP, sbp->value,
				buttonEvent->x, buttonEvent->y,
				(XEvent *) buttonEvent);
	    }
	
	else if /* In arrow2... */
	    ((buttonEvent->x >= sbp->arrow2_x)  &&
	     (buttonEvent->y >= sbp->arrow2_y)   &&
	     (buttonEvent->x <= sbp->arrow2_x 
	      + sbp->arrow_width)             &&
	     (buttonEvent->y <= sbp->arrow2_y 
	      + sbp->arrow_height))
		{
		    sbp->change_type = XmCR_INCREMENT;
		    sbp->arrow2_selected = True;
		    
		    DRAWARROW (sbw, sbw->primitive.bottom_shadow_GC,
			       sbw -> primitive.top_shadow_GC,
			       sbw->scrollBar.arrow2_x,
			       sbw->scrollBar.arrow2_y,
			       sbw->scrollBar.arrow2_orientation);
		    if (sbp->orientation == XmVERTICAL)
			MoveSlider(sbw,
				   sbp->slider_x, 
				   sbp->slider_area_y + sbp->slider_area_height
				   - sbp->slider_height);
		    else
			MoveSlider(sbw,
				   sbp->slider_area_x + sbp->slider_area_width
				   - sbp->slider_width,
				   sbp->slider_y);
		    sbp->value = sbp->maximum - sbp->slider_size;
		    if (sbp->sliding_mode == XmTHERMOMETER)
			RedrawSliderWindow (sbw);
		    ScrollCallback (sbw, XmCR_TO_BOTTOM, 
				    sbp->value, buttonEvent->x, buttonEvent->y,
				    (XEvent *) buttonEvent);
		} 
	else if /* in the trough between arrow2 and the slider... */
	    (((sbp->sliding_mode != XmTHERMOMETER) &&
	      (((sbp->orientation == XmHORIZONTAL)       &&
	      (buttonEvent->x >= sbp->slider_area_x) &&
	      (buttonEvent->x < sbp->slider_x)       &&
	      (buttonEvent->y >= sbp->slider_area_y) &&
	      (buttonEvent->y <= sbp->slider_area_y
	       + sbp->slider_area_height))
	     ||
	     ((sbp->orientation == XmVERTICAL) &&
	      (buttonEvent->y >= sbp->slider_area_y)  &&
	      (buttonEvent->y < sbp->slider_y)        &&
	      (buttonEvent->x >= sbp->slider_area_x)  &&
	      (buttonEvent->x < sbp->slider_area_x
	       + sbp->slider_area_width)))) ||
	     /* only partial treatment, processing direction
		has to be handled here */
	     ((sbp->sliding_mode == XmTHERMOMETER) &&
	      (((sbp->orientation == XmHORIZONTAL)       &&
	      (buttonEvent->x >= sbp->slider_area_x) &&
	      (buttonEvent->x < sbp->slider_width)       &&
	      (buttonEvent->y >= sbp->slider_area_y) &&
	      (buttonEvent->y <= sbp->slider_area_y
	       + sbp->slider_area_height))
	     ||
	     ((sbp->orientation == XmVERTICAL) &&
	      (buttonEvent->y < sbp->slider_area_height
	       - sbp->slider_height) &&
	      (buttonEvent->x >= sbp->slider_area_x)  &&
	      (buttonEvent->x < sbp->slider_area_x
	       + sbp->slider_area_width)))))
		{
		    if (sbp->orientation == XmVERTICAL)
			MoveSlider(sbw, sbp->slider_x, sbp->slider_area_y);
		    else
			MoveSlider(sbw, sbp->slider_area_x, sbp->slider_y);
		    sbp->value = sbp->minimum;
		    if (sbp->sliding_mode == XmTHERMOMETER)
			RedrawSliderWindow (sbw);
		    ScrollCallback (sbw, XmCR_TO_TOP, sbp->value,
				    buttonEvent->x, buttonEvent->y,
				    (XEvent *) buttonEvent);
		}
	else if 
		/* in the trough between arrow1 and the slider... */
	    (((sbp->orientation == XmHORIZONTAL)                     &&
	      (buttonEvent->x > sbp->slider_x + sbp->slider_width) &&
	      (buttonEvent->x <= sbp->slider_area_x
	       + sbp->slider_area_width)                        &&
	      (buttonEvent->y >= sbp->slider_area_y)               &&
	      (buttonEvent->y <= sbp->slider_area_y
	       + sbp->slider_area_height))
	     ||
	     ((sbp->orientation == XmVERTICAL)           &&
	      (buttonEvent->y > sbp->slider_y 
	       + sbp->slider_height)               &&
	      (buttonEvent->y <= sbp->slider_area_y
	       + sbp->slider_area_height)          &&
	      (buttonEvent->x >= sbp->slider_area_x)  &&
	      (buttonEvent->x <= sbp->slider_area_x 
	       + sbp->slider_area_width))
	     || (sbp->sliding_mode == XmTHERMOMETER)) {
		if (sbp->orientation == XmVERTICAL)
		    MoveSlider(sbw,
			       sbp->slider_x, 
			       sbp->slider_area_y + sbp->slider_area_height
			       - sbp->slider_height);
		else
		    MoveSlider(sbw,
			       sbp->slider_area_x + sbp->slider_area_width
			       - sbp->slider_width,
			       sbp->slider_y);
		sbp->value = sbp->maximum - sbp->slider_size;
		if (sbp->sliding_mode == XmTHERMOMETER)
			RedrawSliderWindow (sbw);
		ScrollCallback (sbw, XmCR_TO_BOTTOM, 
				sbp->value, buttonEvent->x, buttonEvent->y,
				(XEvent *) buttonEvent);
	    } 
		
    }
#ifdef FUNKY_INSENSITIVE_VISUAL
    XSetClipMask(XtDisplay(sbw), sbw->scrollBar.unavailable_GC, None);
    if (sbp->value == sbp->minimum) {
	XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
		       sbw->scrollBar.unavailable_GC,
		       sbw->scrollBar.arrow1_x,
		       sbw->scrollBar.arrow1_y,
		       sbw->scrollBar.arrow_width,
		       sbw->scrollBar.arrow_height);
	
	sbw->scrollBar.flags &= ~ARROW1_AVAILABLE;
	
	if (! (sbw->scrollBar.flags & ARROW2_AVAILABLE)) {
	    XClearArea(XtDisplay(sbw), XtWindow(sbw),
		       sbw->scrollBar.arrow2_x,
		       sbw->scrollBar.arrow2_y,
		       sbw->scrollBar.arrow_width,
		       sbw->scrollBar.arrow_height,
		       FALSE);
	    
	    DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
		       sbw->primitive.bottom_shadow_GC,
		       sbw->scrollBar.arrow2_x,
		       sbw->scrollBar.arrow2_y,
		       sbw->scrollBar.arrow2_orientation);
	    
	    sbw->scrollBar.flags |= ARROW2_AVAILABLE;
	}
    }
    else /* sbp->value == (sbp->maximum - sbp->slider_size) */
	{
	    /*		XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
			sbw->scrollBar.unavailable_GC,
			sbw->scrollBar.arrow2_x,
			sbw->scrollBar.arrow2_y,
			sbw->scrollBar.arrow_width,
			sbw->scrollBar.arrow_height);
			*/
	    sbw->scrollBar.flags &= ~ARROW2_AVAILABLE;
	    
	    if (! (sbw->scrollBar.flags & ARROW1_AVAILABLE)) {
		XClearArea(XtDisplay(sbw), XtWindow(sbw),
			   sbw->scrollBar.arrow1_x,
			   sbw->scrollBar.arrow1_y,
			   sbw->scrollBar.arrow_width,
			   sbw->scrollBar.arrow_height,
			   FALSE);
		
		DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
			   sbw->primitive.bottom_shadow_GC,
			   sbw->scrollBar.arrow1_x,
			   sbw->scrollBar.arrow1_y,
			   sbw->scrollBar.arrow1_orientation);
		
		sbw->scrollBar.flags |= ARROW1_AVAILABLE;
	    }
	}
#endif
}




/*********************************************************************
 *
 *  IncrementUpOrLeft
 *	The up or left key was pressed, decrease the value by 
 *	one increment.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
IncrementUpOrLeft(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;

	int new_value;
	int key_pressed;

	if (!num_params || (*num_params != 1) || !params)
	{
	    XmeWarning(wid, MESSAGE14);
	    return;
	}

	if (!sbw->scrollBar.editable) return ;


	sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;

	if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
		return;

	/* 
 	 * arg value passed in will either be "up" for the up key or
	 * "left" for the left arrow key (or for compatibility 0 -> up
	 * key or 1 -> left key. The key needs to be compared with the
	 * scrollbar orientation to ensure only the proper directional key
	 * presses work.
	 */

	if (_XmConvertActionParamToRepTypeId((Widget) sbw,
		     XmRID_SCROLL_BAR_INCREMENT_UP_OR_LEFT_ACTION_PARAMS,
		     params[0], True, &key_pressed) == False)
	{
	    /* We couldn't convert the value. Just assume a value of 0. */
	    key_pressed = 0;
	}

	if (((key_pressed == 0) && 
		(sbw->scrollBar.orientation == XmHORIZONTAL)) 
		||
		((key_pressed == 1) && 
		(sbw->scrollBar.orientation == XmVERTICAL)))
		return;

	new_value = sbw->scrollBar.value - sbw->scrollBar.increment;

	if (new_value < sbw->scrollBar.minimum)
		new_value = sbw->scrollBar.minimum;

	if (new_value != sbw->scrollBar.value)
	{
		sbw->scrollBar.value = new_value;
#ifdef FUNKY_INSENSITIVE_VISUAL
		if ((sbw->scrollBar.value = new_value)
			== sbw->scrollBar.minimum)
		{
		        XSetClipMask(XtDisplay(sbw), 
				     sbw->scrollBar.unavailable_GC, None);
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);

            sbw->scrollBar.flags &= ~ARROW1_AVAILABLE;
		}
#endif
		if ((sbw->scrollBar.show_arrows) &&
		    (! (sbw->scrollBar.flags & ARROW2_AVAILABLE)))
		{
			XClearArea(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height,
				FALSE);
			
			DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
				sbw->primitive.bottom_shadow_GC,
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow2_orientation);

			sbw->scrollBar.flags |= ARROW2_AVAILABLE;
		}

		RedrawSliderWindow (sbw);

		ScrollCallback (sbw, XmCR_DECREMENT, sbw->scrollBar.value,
                        event->xbutton.x, event->xbutton.y, event);
	}
}




/*********************************************************************
 *
 *  IncrementDownOrRight
 *	The down or right key was pressed, increase the value by 
 *	one increment.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
IncrementDownOrRight(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
	int new_value;
	int key_pressed;

	if (!num_params || (*num_params != 1) || !params)
	{
	    XmeWarning(wid, MESSAGE14);
	    return;
	}

	if (!sbw->scrollBar.editable) return ;

	sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;

	if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
		return;

	/* 
 	 * arg value passed in will either be "down" for the down key or
	 * "right" for the right arrow key (or for compatibility 0 -> down
	 * key or 1 -> right key. The key needs to be compared with the
	 * scrollbar orientation to ensure only the proper directional key
	 * presses work.
	 */

	if (_XmConvertActionParamToRepTypeId((Widget) sbw,
		     XmRID_SCROLL_BAR_INCREMENT_DOWN_OR_RIGHT_ACTION_PARAMS,
		     params[0], True, &key_pressed) == False)
	{
	    /* We couldn't convert the value. Just assume a value of 0. */
	    key_pressed = 0;
	}
	
	if (((key_pressed == 0) && 
		(sbw->scrollBar.orientation == XmHORIZONTAL))
		||
		((key_pressed == 1) && 
		(sbw->scrollBar.orientation == XmVERTICAL)))
		return;

	new_value = sbw->scrollBar.value + sbw->scrollBar.increment;

	if (new_value > sbw->scrollBar.maximum - sbw->scrollBar.slider_size)
		new_value = sbw->scrollBar.maximum - sbw->scrollBar.slider_size;

	if (new_value != sbw->scrollBar.value)
	{
		sbw->scrollBar.value = new_value;
#ifdef FUNKY_INSENSITIVE_VISUAL
		if ((sbw->scrollBar.value = new_value)
			== (sbw->scrollBar.maximum - sbw->scrollBar.slider_size))
		{
		        XSetClipMask(XtDisplay(sbw), 
				     sbw->scrollBar.unavailable_GC, None);
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);

            sbw->scrollBar.flags &= ~ARROW2_AVAILABLE;
		}
#endif
		if ((sbw->scrollBar.show_arrows) &&
		    (! (sbw->scrollBar.flags & ARROW1_AVAILABLE)))

		{
			XClearArea(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height,
				FALSE);
			
			DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
				sbw->primitive.bottom_shadow_GC,
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow1_orientation);

			sbw->scrollBar.flags |= ARROW1_AVAILABLE;
		}

		RedrawSliderWindow (sbw);

		ScrollCallback (sbw, XmCR_INCREMENT, sbw->scrollBar.value,
                                    event->xbutton.x, event->xbutton.y, event);
	}
}




/*********************************************************************
 *
 *  PageUpOrLeft
 *	The up or left key was pressed, decrease the value by 
 *	one increment.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
PageUpOrLeft(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
	int new_value;
	int key_pressed;

	if (!num_params || (*num_params != 1) || !params)
	{
	    XmeWarning(wid, MESSAGE14);
	    return;
	}

	if (!sbw->scrollBar.editable) return ;

	sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;

	if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
		return;
	/* 
 	 * arg value passed in will either be "up" for the up key or
	 * "left" for the left arrow key (or for compatibility 0 -> up
	 * key or 1 -> left key. The key needs to be compared with the
	 * scrollbar orientation to ensure only the proper directional key
	 * presses work.
	 */

	if (_XmConvertActionParamToRepTypeId((Widget) sbw,
		     XmRID_SCROLL_BAR_PAGE_UP_OR_LEFT_ACTION_PARAMS,
		     params[0], True, &key_pressed) == False)
	{
	    /* We couldn't convert the value. Just assume a value of 0. */
	    key_pressed = 0;
	}

	if (((key_pressed == 0) && 
		(sbw->scrollBar.orientation == XmHORIZONTAL)) 
		||
		((key_pressed == 1) && 
		(sbw->scrollBar.orientation == XmVERTICAL)))
		return;

	new_value = sbw->scrollBar.value - sbw->scrollBar.page_increment;

	if (new_value < sbw->scrollBar.minimum)
		new_value = sbw->scrollBar.minimum;

	if (new_value != sbw->scrollBar.value)
	{
		sbw->scrollBar.value = new_value;
#ifdef FUNKY_INSENSITIVE_VISUAL
		if ((sbw->scrollBar.value = new_value)
			== sbw->scrollBar.minimum)
		{
		        XSetClipMask(XtDisplay(sbw), 
				     sbw->scrollBar.unavailable_GC, None);
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);

            sbw->scrollBar.flags &= ~ARROW1_AVAILABLE;
		}
#endif
               if ((sbw->scrollBar.show_arrows) &&
                   (! (sbw->scrollBar.flags & ARROW2_AVAILABLE)))
		{
			XClearArea(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height,
				FALSE);
			
			DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
				sbw->primitive.bottom_shadow_GC,
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow2_orientation);

			sbw->scrollBar.flags |= ARROW2_AVAILABLE;
		}

		RedrawSliderWindow (sbw);

		ScrollCallback (sbw, XmCR_PAGE_DECREMENT, 
				sbw->scrollBar.value,
				event->xbutton.x, event->xbutton.y, event);
	}
}




/*********************************************************************
 *
 *  PageDownOrRight
 *	The down or right key was pressed, increase the value by 
 *	one increment.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
PageDownOrRight(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) wid ;
	int new_value;
	int key_pressed;

	if (!num_params || (*num_params != 1) || !params)
	{
	    XmeWarning(wid, MESSAGE14);
	    return;
	}

	if (!sbw->scrollBar.editable) return ;

	sbw->scrollBar.flags &=  ~OPERATION_CANCELLED ;

	if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
		return;

	/* 
 	 * arg value passed in will either be "down" for the down key or
	 * "right" for the right arrow key (or for compatibility 0 -> down
	 * key or 1 -> right key. The key needs to be compared with the
	 * scrollbar orientation to ensure only the proper directional key
	 * presses work.
	 */

	if (_XmConvertActionParamToRepTypeId((Widget) sbw,
		     XmRID_SCROLL_BAR_PAGE_DOWN_OR_RIGHT_ACTION_PARAMS,
		     params[0], True, &key_pressed) == False)
	{
	    /* We couldn't convert the value. Just assume a value of 0. */
	    key_pressed = 0;
	}

	if (((key_pressed == 0) && 
		(sbw->scrollBar.orientation == XmHORIZONTAL))
		||
		((key_pressed == 1) && 
		(sbw->scrollBar.orientation == XmVERTICAL)))
		return;

	new_value = sbw->scrollBar.value + sbw->scrollBar.page_increment;

	if (new_value > sbw->scrollBar.maximum - sbw->scrollBar.slider_size)
		new_value = sbw->scrollBar.maximum - sbw->scrollBar.slider_size;

	if (new_value != sbw->scrollBar.value)
	{
		sbw->scrollBar.value = new_value;
#ifdef FUNKY_INSENSITIVE_VISUAL
		if ((sbw->scrollBar.value = new_value)
			== (sbw->scrollBar.maximum - sbw->scrollBar.slider_size))
		{
		        XSetClipMask(XtDisplay(sbw), 
				     sbw->scrollBar.unavailable_GC, None);
			XFillRectangle(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.unavailable_GC,
				sbw->scrollBar.arrow2_x,
				sbw->scrollBar.arrow2_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height);

            sbw->scrollBar.flags &= ~ARROW2_AVAILABLE;
		}
#endif
               if ((sbw->scrollBar.show_arrows) &&
                   (! (sbw->scrollBar.flags & ARROW1_AVAILABLE)))
		{
			XClearArea(XtDisplay(sbw), XtWindow(sbw),
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow_width,
				sbw->scrollBar.arrow_height,
				FALSE);
			
			DRAWARROW (sbw, sbw -> primitive.top_shadow_GC,
				sbw->primitive.bottom_shadow_GC,
				sbw->scrollBar.arrow1_x,
				sbw->scrollBar.arrow1_y,
				sbw->scrollBar.arrow1_orientation);

			sbw->scrollBar.flags |= ARROW1_AVAILABLE;
		}

		RedrawSliderWindow (sbw);

		ScrollCallback (sbw, XmCR_PAGE_INCREMENT, sbw->scrollBar.value,
                                    event->xbutton.x, event->xbutton.y, event);
	}
}

/*ARGSUSED*/
static void 
CancelDrag(
        Widget wid,
        XEvent *event,
        String *params,
        Cardinal *num_params)
{
	XmScrollBarWidget sbw = (XmScrollBarWidget) wid;

	if (!sbw->scrollBar.editable) return ;

	if (sbw->scrollBar.flags & KEYBOARD_GRABBED)
	{
		short savedX, savedY, j1, j2;

		XtUngrabKeyboard(wid, ((XButtonPressedEvent *)event)->time);
		sbw->scrollBar.flags &= ~KEYBOARD_GRABBED;

		sbw->scrollBar.flags |= OPERATION_CANCELLED;

		sbw->scrollBar.sliding_on = False;
		sbw->scrollBar.value = sbw->scrollBar.saved_value;
		CalcSliderRect(sbw, &savedX, &savedY, &j1, &j2);
		MoveSlider(sbw, savedX, savedY);
		if (sbw->scrollBar.sliding_mode == XmTHERMOMETER)
		    RedrawSliderWindow (sbw);
		ScrollCallback (sbw, XmCR_VALUE_CHANGED,
		                sbw->scrollBar.value, savedX, savedY, 
				(XEvent *) event);

		if (sbw->scrollBar.timer != 0)
		{
			sbw->scrollBar.flags |= END_TIMER;
		}

	}
	else
	{
		XmParentInputActionRec pp_data ;

		pp_data.process_type = XmINPUT_ACTION ;
		pp_data.action = XmPARENT_CANCEL ;
		pp_data.event = event ;
		pp_data.params = params ;
		pp_data.num_params = num_params ;

		_XmParentProcess( XtParent( wid), (XmParentProcessData) &pp_data) ;
	}
}


/*********************************************************************
 *
 *  MoveSlider
 *	Given x and y positions, move the slider and clear the area
 *	moved out of.
 *
 *********************************************************************/
static void 
MoveSlider(
        XmScrollBarWidget sbw,
        int currentX,
        int currentY )
{
    int oldX = sbw->scrollBar.slider_x;
    int oldY = sbw->scrollBar.slider_y;
    int width = sbw->scrollBar.slider_width;
    int height = sbw->scrollBar.slider_height;
    
    XSegment seg[2];
    
    if ((currentX == oldX) && (currentY == oldY))
	return;
    
    if (sbw->scrollBar.sliding_mode == XmTHERMOMETER) {
	if (sbw->scrollBar.orientation == XmHORIZONTAL)
	    sbw->scrollBar.slider_x = currentX;
	else 
	    sbw->scrollBar.slider_y = currentY;
	return ;
    }

    if (sbw->scrollBar.orientation == XmHORIZONTAL)
	{
	    sbw->scrollBar.slider_x = currentX;
	    
	    seg[0].y1 = seg[0].y2 = oldY + 2;
	    seg[1].y1 = seg[1].y2 = oldY + height - 3;
	    
	    if (oldX < currentX)
		{
		    seg[0].x1 = seg[1].x1 = oldX;
		    seg[0].x2 = seg[1].x2 = oldX + currentX - oldX - 1;
		}
	    else
		{
		    seg[0].x1 = seg[1].x1 = currentX + width;
		    seg[0].x2 = seg[1].x2 = seg[0].x1 + oldX - currentX - 1;
		}
	    
	    
	    if (sbw->scrollBar.pixmap != 0)
		{
		    CopySliderInWindow(sbw);
		    XClearArea (XtDisplay((Widget)sbw), 
				XtWindow((Widget)sbw),
				seg[0].x1, oldY, 
				seg[0].x2 - seg[0].x1 + 1, 
				height, False);
		}
	} 
    else /* sbw->scrollBar.orientation == XmVERTICAL */
	{
	    sbw->scrollBar.slider_y = currentY;
	    
	    seg[0].x1 = seg[0].x2 = oldX + 2;
	    seg[1].x1 = seg[1].x2 = oldX + width - 3;
	    
	    if (oldY < currentY)
		{
		    seg[0].y1 = seg[1].y1 = oldY;
		    seg[0].y2 = seg[1].y2 = oldY + currentY - oldY - 1;
		}
	    else
		{
		    seg[0].y1 = seg[1].y1 = currentY + height;
		    seg[0].y2 = seg[1].y2 = seg[0].y1 + oldY - currentY - 1;
		}
	    
	    if (sbw->scrollBar.pixmap != 0)
		{
		    CopySliderInWindow(sbw);
		    XClearArea (XtDisplay((Widget)sbw), 
				XtWindow((Widget)sbw),
				oldX, seg[0].y1, width,
				seg[0].y2 - seg[0].y1 + 1, False);
		}
	}
}




/************************************************************************
 *
 *  ChangeScrollBarValue
 *	Change the scrollbar value by the indicated change type.  Return
 *	True if the value changes, False otherwise.
 *
 ************************************************************************/
static Boolean 
ChangeScrollBarValue(
        XmScrollBarWidget sbw )
{
    register unsigned char change_type = sbw->scrollBar.change_type;
    register int change_amount = 0;
    register Boolean returnFlag = TRUE;
    register int old_value = sbw->scrollBar.value;
    
    if (! (sbw->scrollBar.flags & SLIDER_AVAILABLE))
	return(FALSE);
    /*  Get the amount to change the scroll bar value based on  */
    /*  the type of change occuring.                            */
    
    if (change_type == XmCR_INCREMENT)
	change_amount = sbw->scrollBar.increment;
    else if (change_type == XmCR_PAGE_INCREMENT)
	change_amount = sbw->scrollBar.page_increment;
    else if (change_type == XmCR_DECREMENT)
	change_amount = -sbw->scrollBar.increment;
    else if (change_type == XmCR_PAGE_DECREMENT)
	change_amount = -sbw->scrollBar.page_increment;
    
    /* Change the value */
    sbw->scrollBar.value += change_amount;
    
    /* Truncate and set flags as appropriate */
    if (sbw->scrollBar.value >= (sbw->scrollBar.maximum
				 - sbw->scrollBar.slider_size))
	sbw->scrollBar.value = sbw->scrollBar.maximum
	    - sbw->scrollBar.slider_size;
    
    if (sbw->scrollBar.value <= sbw->scrollBar.minimum)
	sbw->scrollBar.value = sbw->scrollBar.minimum;
    
    if ((returnFlag = (sbw->scrollBar.value != old_value)) != False) {
	RedrawSliderWindow (sbw);
    }
    
    
    return (returnFlag);
}




/*********************************************************************
 *
 *  TimerEvent
 *	This is an event processing function which handles timer
 *	event evoked because of arrow selection.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
TimerEvent(
        XtPointer closure,
        XtIntervalId *id )	/* unused */
{
        XmScrollBarWidget sbw = (XmScrollBarWidget) closure ;
	Boolean flag;

	sbw->scrollBar.timer = 0;

	if (sbw->scrollBar.flags & END_TIMER)
	{
		sbw->scrollBar.flags &= ~END_TIMER;
		return;
	}

	if (sbw->scrollBar.flags & FIRST_SCROLL_FLAG)
	{
		XSync (XtDisplay (sbw), False);

		sbw->scrollBar.flags &= ~FIRST_SCROLL_FLAG;

		sbw->scrollBar.timer = 
		XtAppAddTimeOut (XtWidgetToApplicationContext((Widget) sbw),
                                   (unsigned long) sbw->scrollBar.repeat_delay,
                                                  TimerEvent, (XtPointer) sbw);
		return;
	}


	/*  Change the scrollbar slider value  */

	flag = ChangeScrollBarValue (sbw);

	/*  If the orgin was changed invoke the application supplied  */
	/*  slider moved callbacks                                    */

	if (flag)
		ScrollCallback (sbw, sbw->scrollBar.change_type, 
			sbw->scrollBar.value, 0, 0, NULL);

	/*
	 * If the callback does alot of processing, and XSync is needed
	 * to flush the output and input buffers.  If this is not done,
	 * the entry back to MainLoop will cause the flush.  The server
	 * will then perform it work which may take longer than the timer
	 * interval which will cause the scrollbar to be stuck in a loop.
	 */

	XSync (XtDisplay (sbw), False);

	/*  Add the repeat timer and check that the scrollbar hasn't been set 
	    insensitive by some callbacks */

	if (flag)
	{
		sbw->scrollBar.timer = 
		    XtAppAddTimeOut (XtWidgetToApplicationContext((Widget) sbw),
				     (unsigned long) sbw->scrollBar.repeat_delay,
				     TimerEvent, (XtPointer) sbw);
	}
}




/************************************************************************
 *
 *  ScrollCallback
 *	This routine services the widget's callbacks.  It calls the
 *	specific callback if it is not empty.  If it is empty then the 
 *	main callback is called.
 *
 ************************************************************************/
static void 
ScrollCallback(
        XmScrollBarWidget sbw,
        int reason,
        int value,
        int xpixel,
        int ypixel,
        XEvent *event )
{
   XmScrollBarCallbackStruct call_value;

   call_value.reason = reason;
   call_value.event  = event;
    
   if (PROCESS_DIR_INVERSED(sbw)) {
       switch (reason) {
       case XmCR_INCREMENT:
	   call_value.reason = reason = XmCR_DECREMENT;
	   break;
       case XmCR_DECREMENT:
	   call_value.reason = reason = XmCR_INCREMENT;
	   break;
       case XmCR_PAGE_INCREMENT:
	   call_value.reason = reason = XmCR_PAGE_DECREMENT;
	   break;
       case XmCR_PAGE_DECREMENT:
	   call_value.reason = reason = XmCR_PAGE_INCREMENT;
	   break;
       case XmCR_TO_TOP:
	   call_value.reason = reason = XmCR_TO_BOTTOM;
	   break;
       case XmCR_TO_BOTTOM:
	   call_value.reason = reason = XmCR_TO_TOP;
	   break;
       }
       call_value.value = sbw->scrollBar.maximum
	   + sbw->scrollBar.minimum - value - sbw->scrollBar.slider_size;
   }
   else
       call_value.value = value;
   

   if (sbw->scrollBar.orientation == XmHORIZONTAL)
      call_value.pixel = xpixel;
   else
      call_value.pixel = ypixel;

   switch (reason) {

   case XmCR_VALUE_CHANGED:
       XtCallCallbackList ((Widget) sbw, sbw->scrollBar.value_changed_callback,
			   &call_value);
       break;
       
   case XmCR_INCREMENT:
       if  (sbw->scrollBar.increment_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.increment_callback, &call_value);
       else
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				 sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_DECREMENT:
       if  (sbw->scrollBar.decrement_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.decrement_callback, &call_value);
       else
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_PAGE_INCREMENT:
       if  (sbw->scrollBar.page_increment_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.page_increment_callback, &call_value);
       else 
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_PAGE_DECREMENT:
       if  (sbw->scrollBar.page_decrement_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.page_decrement_callback, &call_value);
       else
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				 sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_TO_TOP:
       if (sbw->scrollBar.to_top_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.to_top_callback, &call_value);
       else 
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_TO_BOTTOM:
       if (sbw->scrollBar.to_bottom_callback)
	   XtCallCallbackList ((Widget) sbw, 
			       sbw->scrollBar.to_bottom_callback, &call_value);
       else
	   {
	       call_value.reason = XmCR_VALUE_CHANGED;
	       XtCallCallbackList ((Widget) sbw, 
				 sbw->scrollBar.value_changed_callback, &call_value);
	   }
       break;
       
   case XmCR_DRAG:
       if (sbw->scrollBar.drag_callback)
	   XtCallCallbackList ((Widget) sbw,
			       sbw->scrollBar.drag_callback, &call_value);
       break;
   }
}




/************************************************************************
 *
 *  NavigChangeMoveCB
 *	add or remove the callback list to be called on any move.
 *      Since valueChangedCallback is not called on increment (& co)
 *      if an increment callback is present, we have to set them all.
 *      
 ************************************************************************/
static void 
NavigChangeMoveCB(
           Widget nav, 
	   XtCallbackProc moveCB,
           XtPointer closure,
           Boolean setunset)
{
    if (setunset) {
	XtAddCallback (nav, XmNvalueChangedCallback, moveCB, closure);
	XtAddCallback (nav, XmNincrementCallback, moveCB, closure);
	XtAddCallback (nav, XmNdecrementCallback, moveCB, closure);
	XtAddCallback (nav, XmNpageIncrementCallback, moveCB, closure);
	XtAddCallback (nav, XmNpageDecrementCallback, moveCB, closure);
	XtAddCallback (nav, XmNtoTopCallback, moveCB, closure);
	XtAddCallback (nav, XmNtoBottomCallback, moveCB, closure);
	XtAddCallback (nav, XmNdragCallback, moveCB, closure);
    } else {
	XtRemoveCallback (nav, XmNvalueChangedCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNincrementCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNdecrementCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNpageIncrementCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNpageDecrementCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNtoTopCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNtoBottomCallback, moveCB, closure);
	XtRemoveCallback (nav, XmNdragCallback, moveCB, closure);
    }
}





/************************************************************************
 *
 *  NavigSetValue
 *	change the value and possibly call the callbacks
 *
 ************************************************************************/
static void 
NavigSetValue(
           Widget nav, 
	   XmNavigatorData nav_data,
           Boolean notify)
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) nav;
    int save_value;
    Arg arglist[6];
    Cardinal n;

    /* register which dimension this scrollbar is to operate */
    if (nav_data->valueMask & NavDimMask) {
	sbw->scrollBar.dimMask = nav_data->dimMask ;
    }

    /* scrollbar is a one dimensional navigator, it expects
       only one dimension to be set, the one that has been registered
       for using the NavDimMask (as treated above) */

    if (!(sbw->scrollBar.dimMask & nav_data->dimMask)) return ;

    /* we need to fetch either the x or y values out of the 
       passed nav_data record, depending on the dimmask.
       scrollbar is only interested in one value */


    save_value = sbw->scrollBar.value;

    n = 0;
    if (nav_data->valueMask & NavValue) {
      if ((PROCESS_DIR_INVERSED(sbw) ?
	   INVERSED_VALUE(sbw) : sbw->scrollBar.value) !=
	  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->value)) {
	XtSetArg (arglist[n], XmNvalue, 
		  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->value));n++;
      }
    }


    if ((nav_data->valueMask & NavMinimum) &&
	(sbw->scrollBar.minimum != 
	 ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->minimum))) {
	XtSetArg (arglist[n], XmNminimum, 
		  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->minimum));n++;
    }

    if ((nav_data->valueMask & NavMaximum) &&
	(sbw->scrollBar.maximum != 
	 ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->maximum))) {
	XtSetArg (arglist[n], XmNmaximum, 
		  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->maximum));n++;
    }
    
    if (sbw->scrollBar.sliding_mode != XmTHERMOMETER) {
	if ((nav_data->valueMask & NavSliderSize) &&
	    (sbw->scrollBar.slider_size != 
	     ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->slider_size)) &&
	    (ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->slider_size) != 0)) {
	    XtSetArg (arglist[n], XmNsliderSize, 
		      ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->slider_size)); 
	    n++;
	}
    }
    
    if ((nav_data->valueMask & NavIncrement) &&
	(sbw->scrollBar.increment != 
	 ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->increment)) &&
	(ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->increment) != 0)) {
	XtSetArg (arglist[n], XmNincrement, 
		  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->increment)); n++;
    }
    
    if ((nav_data->valueMask & NavPageIncrement) &&
	(sbw->scrollBar.page_increment != 
	 ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->page_increment)) &&
	(ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->page_increment) != 0)) {
	XtSetArg (arglist[n], XmNpageIncrement, 
		  ACCESS_DIM(sbw->scrollBar.dimMask, nav_data->page_increment)); 
	n++;
    }
    
    if (n) XtSetValues (nav, arglist, n);

    if (notify && sbw->scrollBar.value != save_value)
	ScrollCallback (sbw, XmCR_VALUE_CHANGED, 
			sbw->scrollBar.value, 0, 0, NULL); 

}


/************************************************************************
 *
 *  NavigGetValue
 *	reports the all the data for this navigator scrollbar
 *      nav_data allocated by the caller
 ************************************************************************/
static void
NavigGetValue(
           Widget nav,
           XmNavigatorData nav_data)
{
    XmScrollBarWidget sbw = (XmScrollBarWidget) nav;

    nav_data->dimMask =  sbw->scrollBar.dimMask;

    if (nav_data->valueMask & NavValue) {
	int value ;

	if (PROCESS_DIR_INVERSED(sbw)) {
	    value = INVERSED_VALUE(sbw);
	} else
	    value = sbw->scrollBar.value;

	ASSIGN_DIM(nav_data->dimMask, nav_data->value, value) ;
    }

    if (nav_data->valueMask & NavMinimum)
	ASSIGN_DIM(nav_data->dimMask, nav_data->minimum, 
		   sbw->scrollBar.minimum) ;
	

    if (nav_data->valueMask & NavMaximum)
	ASSIGN_DIM(nav_data->dimMask, nav_data->maximum,
		   sbw->scrollBar.maximum) ;


    if (nav_data->valueMask & NavSliderSize)
	ASSIGN_DIM(nav_data->dimMask, nav_data->slider_size,
		   sbw->scrollBar.slider_size) ;
	
    if (nav_data->valueMask & NavIncrement)
	ASSIGN_DIM(nav_data->dimMask, nav_data->increment,
		   sbw->scrollBar.increment) ;

    if (nav_data->valueMask & NavPageIncrement) 
	ASSIGN_DIM(nav_data->dimMask, nav_data->page_increment,
		   sbw->scrollBar.page_increment) ;
    
}




/************************************************************************
 *
 *		Application Accessible External Functions
 *
 ************************************************************************/


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

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

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

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

/************************************************************************
 *
 *  XmScrollBarGetValues
 *	Return some scrollbar values.
 *
 ************************************************************************/
void 
XmScrollBarGetValues(
        Widget w,
        int *value,
        int *slider_size,
        int *increment,
        int *page_increment )
{
   XmScrollBarWidget sbw = (XmScrollBarWidget) w;
   XtAppContext app = XtWidgetToApplicationContext(w);
    
   _XmAppLock(app);

   if (PROCESS_DIR_INVERSED(sbw)) {
      if (value) *value = INVERSED_VALUE(sbw);
   } else
      if (value) *value = sbw->scrollBar.value;

   if (slider_size) *slider_size = sbw->scrollBar.slider_size;
   if (increment) *increment = sbw->scrollBar.increment;
   if (page_increment) *page_increment = sbw->scrollBar.page_increment;
   _XmAppUnlock(app);
}




/************************************************************************
 *
 *  XmScrollBarSetValues
 *	Set some scrollbar values.
 *
 ************************************************************************/
void 
XmScrollBarSetValues(
        Widget w,
        int value,
        int slider_size,
        int increment,
        int page_increment,
#if NeedWidePrototypes
        int notify )
#else
        Boolean notify )
#endif /* NeedWidePrototypes */
{
   XmScrollBarWidget sbw = (XmScrollBarWidget) w;
   int save_value;
   Arg arglist[4];
   Cardinal n;
   XtAppContext app = XtWidgetToApplicationContext(w);
    
   _XmAppLock(app);


   save_value = sbw->scrollBar.value;

   n = 0;
   XtSetArg (arglist[n], XmNvalue, value);			n++;

   if (sbw->scrollBar.sliding_mode != XmTHERMOMETER) {
       if (slider_size != 0) {
	   XtSetArg (arglist[n], XmNsliderSize, slider_size);	n++;
       }
   }

   if (increment != 0) {
      XtSetArg (arglist[n], XmNincrement, increment);		n++;
   }

   if (page_increment != 0) {
      XtSetArg (arglist[n], XmNpageIncrement, page_increment);	n++;
   }

   XtSetValues ((Widget) sbw, arglist, n);

   if (notify && sbw->scrollBar.value != save_value)
      ScrollCallback (sbw, XmCR_VALUE_CHANGED, 
                      sbw->scrollBar.value, 0, 0, NULL); 
   _XmAppUnlock(app);
}