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