Scrollbar.c revision 994689c1
1/***********************************************************
2
3Copyright 1987, 1988, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
45
46******************************************************************/
47
48#ifdef HAVE_CONFIG_H
49#include <config.h>
50#endif
51#include <X11/IntrinsicP.h>
52#include <X11/StringDefs.h>
53#include <X11/Xmu/Drawing.h>
54#include <X11/Xaw/ScrollbarP.h>
55#include <X11/Xaw/XawInit.h>
56#include "Private.h"
57
58#define NoButton	-1
59#define PICKLENGTH(widget, x, y)					\
60(((widget)->scrollbar.orientation == XtorientHorizontal) ? (x) : (y))
61
62/*
63 * Class Methods
64 */
65static void XawScrollbarClassInitialize(void);
66static void XawScrollbarDestroy(Widget);
67static void XawScrollbarInitialize(Widget, Widget, ArgList, Cardinal*_args);
68static void XawScrollbarRealize(Widget, Mask*, XSetWindowAttributes*);
69static void XawScrollbarRedisplay(Widget, XEvent*, Region);
70static void XawScrollbarResize(Widget);
71static Boolean XawScrollbarSetValues(Widget, Widget, Widget,
72				     ArgList, Cardinal*);
73
74/*
75 * Prototypes
76 */
77static Boolean CompareEvents(XEvent*, XEvent*);
78static void CreateGC(Widget);
79static float FloatInRange(float, float, float);
80static float FractionLoc(ScrollbarWidget, int, int);
81static void ExtractPosition(XEvent*, Position*, Position*);
82static int InRange(int, int, int);
83static void FillArea(ScrollbarWidget, int, int, int);
84static Bool LookAhead(Widget, XEvent*);
85static void PaintThumb(ScrollbarWidget);
86static Bool PeekNotifyEvent(Display*, XEvent*, char*);
87static void SetDimensions(ScrollbarWidget);
88
89/*
90 * Actions
91 */
92static void EndScroll(Widget, XEvent*, String*, Cardinal*);
93static void MoveThumb(Widget, XEvent*, String*, Cardinal*);
94static void NotifyScroll(Widget, XEvent*, String*, Cardinal*);
95static void NotifyThumb(Widget, XEvent*, String*, Cardinal*);
96static void StartScroll(Widget, XEvent*, String*, Cardinal*);
97
98/*
99 * Initialization
100 */
101static char defaultTranslations[] =
102"<Btn1Down>:"	"StartScroll(Forward)\n"
103"<Btn2Down>:"	"StartScroll(Continuous) MoveThumb() NotifyThumb()\n"
104"<Btn3Down>:"	"StartScroll(Backward)\n"
105"<Btn2Motion>:"	"MoveThumb() NotifyThumb()\n"
106"<BtnUp>:"	"NotifyScroll(Proportional) EndScroll()\n";
107
108static float floatZero = 0.0;
109
110#define Offset(field) XtOffsetOf(ScrollbarRec, field)
111
112static XtResource resources[] = {
113  {
114    XtNlength,
115    XtCLength,
116    XtRDimension,
117    sizeof(Dimension),
118    Offset(scrollbar.length),
119    XtRImmediate,
120    (XtPointer)1
121  },
122  {
123    XtNthickness,
124    XtCThickness,
125    XtRDimension,
126    sizeof(Dimension),
127    Offset(scrollbar.thickness),
128    XtRImmediate,
129    (XtPointer)14
130  },
131  {
132    XtNorientation,
133    XtCOrientation,
134    XtROrientation,
135    sizeof(XtOrientation),
136    Offset(scrollbar.orientation),
137    XtRImmediate,
138    (XtPointer)XtorientVertical
139  },
140  {
141    XtNscrollProc,
142    XtCCallback,
143    XtRCallback,
144    sizeof(XtPointer),
145    Offset(scrollbar.scrollProc),
146    XtRCallback,
147    NULL
148  },
149  {
150    XtNthumbProc,
151    XtCCallback,
152    XtRCallback,
153    sizeof(XtPointer),
154    Offset(scrollbar.thumbProc),
155    XtRCallback,
156    NULL
157  },
158  {
159    XtNjumpProc,
160    XtCCallback,
161    XtRCallback,
162    sizeof(XtPointer),
163    Offset(scrollbar.jumpProc),
164    XtRCallback,
165    NULL
166  },
167  {
168    XtNthumb,
169    XtCThumb,
170    XtRBitmap,
171    sizeof(Pixmap),
172    Offset(scrollbar.thumb),
173    XtRImmediate,
174    (XtPointer)XtUnspecifiedPixmap
175  },
176  {
177    XtNforeground,
178    XtCForeground,
179    XtRPixel,
180    sizeof(Pixel),
181    Offset(scrollbar.foreground),
182    XtRString,
183    XtDefaultForeground
184  },
185  {
186    XtNshown,
187    XtCShown,
188    XtRFloat,
189    sizeof(float),
190    Offset(scrollbar.shown),
191    XtRFloat,
192    (XtPointer)&floatZero
193  },
194  {
195    XtNtopOfThumb,
196    XtCTopOfThumb,
197    XtRFloat,
198    sizeof(float),
199    Offset(scrollbar.top),
200    XtRFloat,
201    (XtPointer)&floatZero
202  },
203  {
204    XtNscrollVCursor,
205    XtCCursor,
206    XtRCursor,
207    sizeof(Cursor),
208    Offset(scrollbar.verCursor),
209    XtRString,
210    "sb_v_double_arrow"
211  },
212  {
213    XtNscrollHCursor,
214    XtCCursor,
215    XtRCursor,
216    sizeof(Cursor),
217    Offset(scrollbar.horCursor),
218    XtRString,
219    "sb_h_double_arrow"
220  },
221  {
222    XtNscrollUCursor,
223    XtCCursor,
224    XtRCursor,
225    sizeof(Cursor),
226    Offset(scrollbar.upCursor),
227    XtRString,
228    "sb_up_arrow"
229  },
230  {
231    XtNscrollDCursor,
232    XtCCursor,
233    XtRCursor,
234    sizeof(Cursor),
235    Offset(scrollbar.downCursor),
236    XtRString,
237    "sb_down_arrow"
238  },
239  {
240    XtNscrollLCursor,
241    XtCCursor,
242    XtRCursor,
243    sizeof(Cursor),
244    Offset(scrollbar.leftCursor),
245    XtRString,
246    "sb_left_arrow"
247  },
248  {
249    XtNscrollRCursor,
250    XtCCursor,
251    XtRCursor,
252    sizeof(Cursor),
253    Offset(scrollbar.rightCursor),
254    XtRString,
255    "sb_right_arrow"
256  },
257  {
258    XtNminimumThumb,
259    XtCMinimumThumb,
260    XtRDimension,
261    sizeof(Dimension),
262    Offset(scrollbar.min_thumb),
263    XtRImmediate,
264    (XtPointer)7
265  },
266};
267#undef Offset
268
269static XtActionsRec actions[] = {
270	{"StartScroll",		StartScroll},
271	{"MoveThumb",		MoveThumb},
272	{"NotifyThumb",		NotifyThumb},
273	{"NotifyScroll",	NotifyScroll},
274	{"EndScroll",		EndScroll},
275};
276
277#define Superclass (&simpleClassRec)
278ScrollbarClassRec scrollbarClassRec = {
279  /* core */
280  {
281    (WidgetClass)&simpleClassRec,	/* superclass */
282    "Scrollbar",			/* class_name */
283    sizeof(ScrollbarRec),		/* widget_size */
284    XawScrollbarClassInitialize,	/* class_initialize */
285    NULL,				/* class_part_init */
286    False,				/* class_inited */
287    XawScrollbarInitialize,		/* initialize */
288    NULL,				/* initialize_hook */
289    XawScrollbarRealize,		/* realize */
290    actions,				/* actions */
291    XtNumber(actions),			/* num_actions */
292    resources,				/* resources */
293    XtNumber(resources),		/* num_resources */
294    NULLQUARK,				/* xrm_class */
295    True,				/* compress_motion */
296    True,				/* compress_exposure */
297    True,				/* compress_enterleave */
298    False,				/* visible_interest */
299    XawScrollbarDestroy,		/* destroy */
300    XawScrollbarResize,			/* resize */
301    XawScrollbarRedisplay,		/* expose */
302    XawScrollbarSetValues,		/* set_values */
303    NULL,				/* set_values_hook */
304    XtInheritSetValuesAlmost,		/* set_values_almost */
305    NULL,				/* get_values_hook */
306    NULL,				/* accept_focus */
307    XtVersion,				/* version */
308    NULL,				/* callback_private */
309    defaultTranslations,		/* tm_table */
310    XtInheritQueryGeometry,		/* query_geometry */
311    XtInheritDisplayAccelerator,	/* display_accelerator */
312    NULL,				/* extension */
313  },
314  /* simple */
315  {
316    XtInheritChangeSensitive,		/* change_sensitive */
317  },
318  /* scrollbar */
319  {
320    NULL,				/* extension */
321  },
322};
323
324WidgetClass scrollbarWidgetClass = (WidgetClass)&scrollbarClassRec;
325
326/*
327 * Implementation
328 */
329static void
330XawScrollbarClassInitialize(void)
331{
332    XawInitializeWidgetSet();
333    XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
334		   NULL, 0);
335    XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString,
336		       NULL, 0, XtCacheNone, NULL);
337}
338
339/*
340 * Make sure the first number is within the range specified by the other
341 * two numbers.
342 */
343static int
344InRange(int num, int small, int big)
345{
346    return ((num < small) ? small : ((num > big) ? big : num));
347}
348
349/*
350 * Same as above, but for floating numbers
351 */
352static float
353FloatInRange(float num, float small, float big)
354{
355    return ((num < small) ? small : ((num > big) ? big : num));
356}
357
358/* Fill the area specified by top and bottom with the given pattern */
359static float
360FractionLoc(ScrollbarWidget w, int x, int y)
361{
362    float   result;
363
364    result = PICKLENGTH(w, x / (float)XtWidth(w), y / (float)XtHeight(w));
365
366    return (FloatInRange(result, 0.0, 1.0));
367}
368
369static void
370FillArea(ScrollbarWidget w, int top, int bottom, int thumb)
371{
372    Dimension length;
373
374    top = XawMax(1, top);
375    if (w->scrollbar.orientation == XtorientHorizontal)
376    bottom = XawMin(bottom, XtWidth(w) - 1);
377    else
378    bottom = XawMin(bottom, XtHeight(w) - 1);
379
380    if (bottom <= top)
381	return;
382
383    length = bottom - top;
384
385    switch(thumb) {
386	/* Fill the new Thumb location */
387	case 1:
388	    if (w->scrollbar.orientation == XtorientHorizontal)
389		XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc,
390			       top, 1, length, XtHeight(w) - 2);
391	    else
392		XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc,
393			       1, top, XtWidth(w) - 2, length);
394	    break;
395	/* Clear the old Thumb location */
396	case 0:
397	    if (w->scrollbar.orientation == XtorientHorizontal)
398		XClearArea(XtDisplay(w), XtWindow(w),
399			   top, 1, length, XtHeight(w) - 2, False);
400	    else
401		XClearArea(XtDisplay(w), XtWindow(w),
402			   1, top, XtWidth(w) - 2, length, False);
403	    break;
404    }
405}
406
407
408/* Paint the thumb in the area specified by w->top and
409   w->shown.  The old area is erased.  The painting and
410   erasing is done cleverly so that no flickering will occur. */
411static void
412PaintThumb(ScrollbarWidget w)
413{
414    Position oldtop, oldbot, newtop, newbot;
415
416    oldtop = w->scrollbar.topLoc;
417    oldbot = oldtop + w->scrollbar.shownLength;
418    newtop = w->scrollbar.length * w->scrollbar.top;
419    newbot = newtop + (int)(w->scrollbar.length * w->scrollbar.shown);
420    if (newbot < newtop + (int)w->scrollbar.min_thumb)
421	newbot = newtop + w->scrollbar.min_thumb;
422    w->scrollbar.topLoc = newtop;
423    w->scrollbar.shownLength = newbot - newtop;
424
425    if (XtIsRealized((Widget)w)) {
426	if (newtop < oldtop)
427	    FillArea(w, newtop, XawMin(newbot, oldtop), 1);
428	if (newtop > oldtop)
429	    FillArea(w, oldtop, XawMin(newtop, oldbot), 0);
430	if (newbot < oldbot)
431	    FillArea(w, XawMax(newbot, oldtop), oldbot, 0);
432	if (newbot > oldbot)
433	    FillArea(w, XawMax(newtop, oldbot), newbot, 1);
434    }
435}
436
437static void
438SetDimensions(ScrollbarWidget w)
439{
440    if (w->scrollbar.orientation == XtorientVertical) {
441	w->scrollbar.length = XtHeight(w);
442	w->scrollbar.thickness = XtWidth(w);
443    }
444    else {
445	w->scrollbar.length = XtWidth(w);
446	w->scrollbar.thickness = XtHeight(w);
447    }
448}
449
450static void
451XawScrollbarDestroy(Widget w)
452{
453    ScrollbarWidget sbw = (ScrollbarWidget)w;
454
455    XtReleaseGC(w, sbw->scrollbar.gc);
456}
457
458static void
459CreateGC(Widget w)
460{
461    ScrollbarWidget sbw = (ScrollbarWidget)w;
462    XGCValues gcValues;
463    XtGCMask mask;
464    unsigned int depth = 1;
465
466    if (sbw->scrollbar.thumb == XtUnspecifiedPixmap)
467	sbw->scrollbar.thumb = XmuCreateStippledPixmap(XtScreen(w),
468						       (Pixel)1, (Pixel)0,
469						       depth);
470    else if (sbw->scrollbar.thumb != None) {
471	Window root;
472	int x, y;
473	unsigned int width, height, bw;
474
475	XGetGeometry(XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y,
476		     &width, &height, &bw, &depth);
477    }
478
479    gcValues.foreground = sbw->scrollbar.foreground;
480    gcValues.background = sbw->core.background_pixel;
481    mask = GCForeground | GCBackground;
482
483    if (sbw->scrollbar.thumb != None) {
484	if (depth == 1) {
485	    gcValues.fill_style = FillOpaqueStippled;
486	    gcValues.stipple = sbw->scrollbar.thumb;
487	    mask |= GCFillStyle | GCStipple;
488	}
489	else {
490	    gcValues.fill_style = FillTiled;
491	    gcValues.tile = sbw->scrollbar.thumb;
492	    mask |= GCFillStyle | GCTile;
493	}
494    }
495    sbw->scrollbar.gc = XtGetGC(w, mask, &gcValues);
496}
497
498/* ARGSUSED */
499static void
500XawScrollbarInitialize(Widget request, Widget cnew,
501		       ArgList args, Cardinal *num_args)
502{
503    ScrollbarWidget w = (ScrollbarWidget)cnew;
504
505    CreateGC(cnew);
506
507    if (XtWidth(w) == 0)
508	XtWidth(w) = w->scrollbar.orientation == XtorientVertical ?
509			w->scrollbar.thickness : w->scrollbar.length;
510
511    if (XtHeight(w) == 0)
512	XtHeight(w) = w->scrollbar.orientation == XtorientHorizontal ?
513			w->scrollbar.thickness : w->scrollbar.length;
514
515    SetDimensions(w);
516    w->scrollbar.direction = 0;
517    w->scrollbar.topLoc = 0;
518    w->scrollbar.shownLength = w->scrollbar.min_thumb;
519}
520
521static void
522XawScrollbarRealize(Widget gw, Mask *valueMask,
523		    XSetWindowAttributes *attributes)
524{
525    ScrollbarWidget w = (ScrollbarWidget)gw;
526
527    w->scrollbar.inactiveCursor = w->scrollbar.orientation == XtorientVertical ?
528		w->scrollbar.verCursor : w->scrollbar.horCursor;
529
530    XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL);
531
532    /*
533     * The Simple widget actually stuffs the value in the valuemask
534     */
535    (*scrollbarWidgetClass->core_class.superclass->core_class.realize)
536	(gw, valueMask, attributes);
537}
538
539/*ARGSUSED*/
540static Boolean
541XawScrollbarSetValues(Widget current, Widget request, Widget desired,
542		      ArgList args, Cardinal *num_args)
543{
544    ScrollbarWidget w = (ScrollbarWidget)current;
545    ScrollbarWidget dw = (ScrollbarWidget)desired;
546    Boolean redraw = False;
547
548    /*
549     * If these values are outside the acceptable range ignore them...
550     */
551    if (dw->scrollbar.top < 0.0 || dw->scrollbar.top > 1.0)
552	dw->scrollbar.top = w->scrollbar.top;
553
554    if (dw->scrollbar.shown < 0.0 || dw->scrollbar.shown > 1.0)
555	dw->scrollbar.shown = w->scrollbar.shown;
556
557    if (XtIsRealized (desired)) {
558	if (w->scrollbar.foreground != dw->scrollbar.foreground ||
559	    w->core.background_pixel != dw->core.background_pixel ||
560	    w->scrollbar.thumb != dw->scrollbar.thumb) {
561	    XtReleaseGC((Widget)dw, w->scrollbar.gc);
562	    CreateGC((Widget)dw);
563	    redraw = True;
564	}
565	if (w->scrollbar.top != dw->scrollbar.top ||
566	    w->scrollbar.shown != dw->scrollbar.shown)
567	    redraw = True;
568    }
569
570    return (redraw);
571}
572
573static void
574XawScrollbarResize(Widget gw)
575{
576    /* ForgetGravity has taken care of background, but thumb may
577     * have to move as a result of the new size. */
578    SetDimensions((ScrollbarWidget)gw);
579    XawScrollbarRedisplay(gw, NULL, NULL);
580}
581
582/*ARGSUSED*/
583static void
584XawScrollbarRedisplay(Widget gw, XEvent *event, Region region)
585{
586    ScrollbarWidget w = (ScrollbarWidget)gw;
587    int x, y;
588    unsigned int width, height;
589
590    if (Superclass->core_class.expose)
591	(*Superclass->core_class.expose)(gw, event, region);
592
593    if (w->scrollbar.orientation == XtorientHorizontal) {
594	x = w->scrollbar.topLoc;
595	y = 1;
596	width = w->scrollbar.shownLength;
597	height = XtHeight(w) - 2;
598    }
599    else {
600	x = 1;
601	y = w->scrollbar.topLoc;
602	width = XtWidth(w) - 2;
603	height = w->scrollbar.shownLength;
604    }
605
606    if (region == NULL ||
607	XRectInRegion(region, x, y, width, height) != RectangleOut) {
608	/* Forces entire thumb to be painted */
609	w->scrollbar.topLoc = -(w->scrollbar.length + 1);
610	PaintThumb(w);
611    }
612}
613
614/*ARGSUSED*/
615static void
616StartScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params)
617{
618    ScrollbarWidget w = (ScrollbarWidget)gw;
619    Cursor cursor;
620    char direction;
621
622    if (w->scrollbar.direction != 0)	/* if we're already scrolling */
623	return;
624    if (*num_params > 0)
625	direction = *params[0];
626    else
627	direction = 'C';
628
629    w->scrollbar.direction = direction;
630
631    switch(direction) {
632	case 'B':
633	case 'b':
634	    cursor = w->scrollbar.orientation == XtorientVertical ?
635		w->scrollbar.downCursor : w->scrollbar.rightCursor;
636	    break;
637	case 'F':
638	case 'f':
639	    cursor = w->scrollbar.orientation == XtorientVertical ?
640		w->scrollbar.upCursor : w->scrollbar.leftCursor;
641	    break;
642	case 'C':
643	case 'c':
644	     cursor = w->scrollbar.orientation == XtorientVertical ?
645		w->scrollbar.rightCursor : w->scrollbar.upCursor;
646	     break;
647	default:
648	     return;	/* invalid invocation */
649    }
650
651    XtVaSetValues(gw, XtNcursor, cursor, NULL);
652
653    XFlush(XtDisplay(w));
654}
655
656static Boolean
657CompareEvents(XEvent *oldEvent, XEvent *newEvent)
658{
659#define Check(field) if (newEvent->field != oldEvent->field) return (False)
660
661    Check(xany.display);
662    Check(xany.type);
663    Check(xany.window);
664
665    switch(newEvent->type) {
666	case MotionNotify:
667	    Check(xmotion.state);
668	    break;
669	case ButtonPress:
670	case ButtonRelease:
671	    Check(xbutton.state);
672	    Check(xbutton.button);
673	    break;
674	case KeyPress:
675	case KeyRelease:
676	    Check(xkey.state);
677	    Check(xkey.keycode);
678	    break;
679	case EnterNotify:
680	case LeaveNotify:
681	    Check(xcrossing.mode);
682	    Check(xcrossing.detail);
683	    Check(xcrossing.state);
684	    break;
685    }
686#undef Check
687
688    return (True);
689}
690
691struct EventData {
692    XEvent *oldEvent;
693    int count;
694};
695
696static Bool
697PeekNotifyEvent(Display *dpy, XEvent *event, char *args)
698{
699    struct EventData *eventData = (struct EventData*)args;
700
701    return (++eventData->count == QLength(dpy)	/* since PeekIf blocks */
702	    || CompareEvents(event, eventData->oldEvent));
703}
704
705static Bool
706LookAhead(Widget w, XEvent *event)
707{
708    XEvent newEvent;
709    struct EventData eventData;
710
711    if (QLength(XtDisplay(w)) == 0)
712	return (False);
713
714    eventData.count = 0;
715    eventData.oldEvent = event;
716
717    XPeekIfEvent(XtDisplay(w), &newEvent, PeekNotifyEvent, (char*)&eventData);
718
719    if (CompareEvents(event, &newEvent))
720	return (True);
721
722    return (False);
723}
724
725static void
726ExtractPosition(XEvent *event, Position *x, Position *y)
727{
728    switch(event->type) {
729	case MotionNotify:
730	    *x = event->xmotion.x;
731	    *y = event->xmotion.y;
732	    break;
733	case ButtonPress:
734	case ButtonRelease:
735	    *x = event->xbutton.x;
736	    *y = event->xbutton.y;
737	    break;
738	case KeyPress:
739	case KeyRelease:
740	    *x = event->xkey.x;
741	    *y = event->xkey.y;
742	    break;
743	case EnterNotify:
744	case LeaveNotify:
745	    *x = event->xcrossing.x;
746	    *y = event->xcrossing.y;
747	    break;
748	default:
749	    *x = 0;
750	    *y = 0;
751	    break;
752    }
753}
754
755static void
756NotifyScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params)
757{
758    ScrollbarWidget w = (ScrollbarWidget)gw;
759    long call_data = 0;
760    char style;
761    Position x, y;
762
763    if (w->scrollbar.direction == 0)	/* if no StartScroll */
764	return;
765
766    if (LookAhead(gw, event))
767	return;
768
769    if (*num_params > 0)
770	style = *params[0];
771    else
772	style = 'P';
773
774    switch(style) {
775	case 'P':    /* Proportional */
776	case 'p':
777	    ExtractPosition(event, &x, &y);
778	    call_data = InRange(PICKLENGTH(w, x, y), 0, (int)w->scrollbar.length);
779	    break;
780	case 'F':    /* FullLength */
781	case 'f':
782	    call_data = w->scrollbar.length;
783	    break;
784    }
785
786    switch(w->scrollbar.direction) {
787	case 'B':
788	case 'b':
789	    call_data = -call_data;
790	    /*FALLTHROUGH*/
791	case 'F':
792	case 'f':
793	    XtCallCallbacks(gw, XtNscrollProc, (XtPointer)call_data);
794	    break;
795	case 'C':
796	case 'c':    /* NotifyThumb has already called the thumbProc(s) */
797	    break;
798    }
799}
800
801/*ARGSUSED*/
802static void
803EndScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params)
804{
805    ScrollbarWidget w = (ScrollbarWidget)gw;
806
807    XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL);
808    XFlush(XtDisplay(w));		/* make sure it get propogated */
809
810    w->scrollbar.direction = 0;
811}
812
813/*ARGSUSED*/
814static void
815MoveThumb(Widget gw, XEvent *event, String *params, Cardinal *num_params)
816{
817    ScrollbarWidget w = (ScrollbarWidget)gw;
818    Position x, y;
819
820    if (w->scrollbar.direction == 0)	/* if no StartScroll */
821	return;
822
823    if (LookAhead(gw, event))
824	return;
825
826    if (!event->xmotion.same_screen)
827	return;
828
829    ExtractPosition(event, &x, &y);
830    w->scrollbar.top = FractionLoc(w, x, y);
831}
832
833/*ARGSUSED*/
834static void
835NotifyThumb(Widget gw, XEvent *event, String *params, Cardinal *num_params)
836{
837    ScrollbarWidget w = (ScrollbarWidget)gw;
838    union {
839	XtPointer xtp;
840	float xtf;
841    } xtpf;
842
843    if (w->scrollbar.direction == 0)	/* if no StartScroll */
844	return;
845
846    if (LookAhead(gw, event))
847	return;
848
849    /* thumbProc is not pretty, but is necessary for backwards
850       compatibility on those architectures for which it work{s,ed};
851       the intent is to pass a (truncated) float by value. */
852    xtpf.xtf = w->scrollbar.top;
853    XtCallCallbacks(gw, XtNthumbProc, xtpf.xtp);
854    XtCallCallbacks(gw, XtNjumpProc, (XtPointer)&w->scrollbar.top);
855
856    PaintThumb(w);
857}
858
859/*
860 *  Public routines
861 */
862/* Set the scroll bar to the given location. */
863void
864XawScrollbarSetThumb(Widget gw,
865#if NeedWidePrototypes
866		     double top, double shown
867#else
868		     float top, float shown
869#endif
870		     )
871{
872    ScrollbarWidget w = (ScrollbarWidget)gw;
873
874    if (w->scrollbar.direction == 'c')	/* if still thumbing */
875	return;
876
877    w->scrollbar.top = top > 1.0 ? 1.0 : top >= 0.0 ? top : w->scrollbar.top;
878
879    w->scrollbar.shown = shown > 1.0 ? 1.0 : shown >= 0.0 ?
880				shown : w->scrollbar.shown;
881    PaintThumb(w);
882}
883