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#ifndef OLDXAW
320    NULL,
321#endif
322  },
323  /* scrollbar */
324  {
325    NULL,				/* extension */
326  },
327};
328
329WidgetClass scrollbarWidgetClass = (WidgetClass)&scrollbarClassRec;
330
331/*
332 * Implementation
333 */
334static void
335XawScrollbarClassInitialize(void)
336{
337    XawInitializeWidgetSet();
338    XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
339		   NULL, 0);
340    XtSetTypeConverter(XtROrientation, XtRString, XmuCvtOrientationToString,
341		       NULL, 0, XtCacheNone, NULL);
342}
343
344/*
345 * Make sure the first number is within the range specified by the other
346 * two numbers.
347 */
348static int
349InRange(int num, int small, int big)
350{
351    return ((num < small) ? small : ((num > big) ? big : num));
352}
353
354/*
355 * Same as above, but for floating numbers
356 */
357static float
358FloatInRange(float num, float small, float big)
359{
360    return ((num < small) ? small : ((num > big) ? big : num));
361}
362
363/* Fill the area specified by top and bottom with the given pattern */
364static float
365FractionLoc(ScrollbarWidget w, int x, int y)
366{
367    float   result;
368
369    result = PICKLENGTH(w, (float)x / (float)XtWidth(w), (float)y / (float)XtHeight(w));
370
371    return (FloatInRange(result, 0.0, 1.0));
372}
373
374static void
375FillArea(ScrollbarWidget w, int top, int bottom, int thumb)
376{
377    Dimension length;
378
379    top = XawMax(1, top);
380    if (w->scrollbar.orientation == XtorientHorizontal)
381    bottom = XawMin(bottom, XtWidth(w) - 1);
382    else
383    bottom = XawMin(bottom, XtHeight(w) - 1);
384
385    if (bottom <= top)
386	return;
387
388    length = (Dimension)(bottom - top);
389
390    switch(thumb) {
391	/* Fill the new Thumb location */
392	case 1:
393	    if (w->scrollbar.orientation == XtorientHorizontal)
394		XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc,
395			       top, 1, length, (unsigned)(XtHeight(w) - 2));
396	    else
397		XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc,
398			       1, top, (unsigned)(XtWidth(w) - 2), length);
399	    break;
400	/* Clear the old Thumb location */
401	case 0:
402	    if (w->scrollbar.orientation == XtorientHorizontal)
403		XClearArea(XtDisplay(w), XtWindow(w),
404			   top, 1, length, (unsigned)(XtHeight(w) - 2), False);
405	    else
406		XClearArea(XtDisplay(w), XtWindow(w),
407			   1, top, (unsigned)(XtWidth(w) - 2), length, False);
408	    break;
409    }
410}
411
412
413/* Paint the thumb in the area specified by w->top and
414   w->shown.  The old area is erased.  The painting and
415   erasing is done cleverly so that no flickering will occur. */
416static void
417PaintThumb(ScrollbarWidget w)
418{
419    Position oldtop, oldbot, newtop, newbot;
420
421    oldtop = w->scrollbar.topLoc;
422    oldbot = (Position)(oldtop + w->scrollbar.shownLength);
423    newtop = (Position)(w->scrollbar.length * w->scrollbar.top);
424    newbot = (Position)(newtop + (int)(w->scrollbar.length * w->scrollbar.shown));
425    if (newbot < newtop + (int)w->scrollbar.min_thumb)
426	newbot = (Position)(newtop + w->scrollbar.min_thumb);
427    w->scrollbar.topLoc = newtop;
428    w->scrollbar.shownLength = (Dimension)(newbot - newtop);
429
430    if (XtIsRealized((Widget)w)) {
431	if (newtop < oldtop)
432	    FillArea(w, newtop, XawMin(newbot, oldtop), 1);
433	if (newtop > oldtop)
434	    FillArea(w, oldtop, XawMin(newtop, oldbot), 0);
435	if (newbot < oldbot)
436	    FillArea(w, XawMax(newbot, oldtop), oldbot, 0);
437	if (newbot > oldbot)
438	    FillArea(w, XawMax(newtop, oldbot), newbot, 1);
439    }
440}
441
442static void
443SetDimensions(ScrollbarWidget w)
444{
445    if (w->scrollbar.orientation == XtorientVertical) {
446	w->scrollbar.length = XtHeight(w);
447	w->scrollbar.thickness = XtWidth(w);
448    }
449    else {
450	w->scrollbar.length = XtWidth(w);
451	w->scrollbar.thickness = XtHeight(w);
452    }
453}
454
455static void
456XawScrollbarDestroy(Widget w)
457{
458    ScrollbarWidget sbw = (ScrollbarWidget)w;
459
460    XtReleaseGC(w, sbw->scrollbar.gc);
461}
462
463static void
464CreateGC(Widget w)
465{
466    ScrollbarWidget sbw = (ScrollbarWidget)w;
467    XGCValues gcValues;
468    XtGCMask mask;
469    unsigned int depth = 1;
470
471    if (sbw->scrollbar.thumb == XtUnspecifiedPixmap)
472	sbw->scrollbar.thumb = XmuCreateStippledPixmap(XtScreen(w),
473						       (Pixel)1, (Pixel)0,
474						       depth);
475    else if (sbw->scrollbar.thumb != None) {
476	Window root;
477	int x, y;
478	unsigned int width, height, bw;
479
480	XGetGeometry(XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y,
481		     &width, &height, &bw, &depth);
482    }
483
484    gcValues.foreground = sbw->scrollbar.foreground;
485    gcValues.background = sbw->core.background_pixel;
486    mask = GCForeground | GCBackground;
487
488    if (sbw->scrollbar.thumb != None) {
489	if (depth == 1) {
490	    gcValues.fill_style = FillOpaqueStippled;
491	    gcValues.stipple = sbw->scrollbar.thumb;
492	    mask |= GCFillStyle | GCStipple;
493	}
494	else {
495	    gcValues.fill_style = FillTiled;
496	    gcValues.tile = sbw->scrollbar.thumb;
497	    mask |= GCFillStyle | GCTile;
498	}
499    }
500    sbw->scrollbar.gc = XtGetGC(w, mask, &gcValues);
501}
502
503/* ARGSUSED */
504static void
505XawScrollbarInitialize(Widget request _X_UNUSED, Widget cnew,
506		       ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
507{
508    ScrollbarWidget w = (ScrollbarWidget)cnew;
509
510    CreateGC(cnew);
511
512    if (XtWidth(w) == 0)
513	XtWidth(w) = w->scrollbar.orientation == XtorientVertical ?
514			w->scrollbar.thickness : w->scrollbar.length;
515
516    if (XtHeight(w) == 0)
517	XtHeight(w) = w->scrollbar.orientation == XtorientHorizontal ?
518			w->scrollbar.thickness : w->scrollbar.length;
519
520    SetDimensions(w);
521    w->scrollbar.direction = 0;
522    w->scrollbar.topLoc = 0;
523    w->scrollbar.shownLength = w->scrollbar.min_thumb;
524}
525
526static void
527XawScrollbarRealize(Widget gw, Mask *valueMask,
528		    XSetWindowAttributes *attributes)
529{
530    ScrollbarWidget w = (ScrollbarWidget)gw;
531
532    w->scrollbar.inactiveCursor = w->scrollbar.orientation == XtorientVertical ?
533		w->scrollbar.verCursor : w->scrollbar.horCursor;
534
535    XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL);
536
537    /*
538     * The Simple widget actually stuffs the value in the valuemask
539     */
540    (*scrollbarWidgetClass->core_class.superclass->core_class.realize)
541	(gw, valueMask, attributes);
542}
543
544/*ARGSUSED*/
545static Boolean
546XawScrollbarSetValues(Widget current, Widget request _X_UNUSED, Widget desired,
547		      ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
548{
549    ScrollbarWidget w = (ScrollbarWidget)current;
550    ScrollbarWidget dw = (ScrollbarWidget)desired;
551    Boolean redraw = False;
552
553    /*
554     * If these values are outside the acceptable range ignore them...
555     */
556    if (dw->scrollbar.top < 0.0 || dw->scrollbar.top > 1.0)
557	dw->scrollbar.top = w->scrollbar.top;
558
559    if (dw->scrollbar.shown < 0.0 || dw->scrollbar.shown > 1.0)
560	dw->scrollbar.shown = w->scrollbar.shown;
561
562    if (XtIsRealized (desired)) {
563	if (w->scrollbar.foreground != dw->scrollbar.foreground ||
564	    w->core.background_pixel != dw->core.background_pixel ||
565	    w->scrollbar.thumb != dw->scrollbar.thumb) {
566	    XtReleaseGC((Widget)dw, w->scrollbar.gc);
567	    CreateGC((Widget)dw);
568	    redraw = True;
569	}
570	if (w->scrollbar.top != dw->scrollbar.top ||
571	    w->scrollbar.shown != dw->scrollbar.shown)
572	    redraw = True;
573    }
574
575    return (redraw);
576}
577
578static void
579XawScrollbarResize(Widget gw)
580{
581    /* ForgetGravity has taken care of background, but thumb may
582     * have to move as a result of the new size. */
583    SetDimensions((ScrollbarWidget)gw);
584    XawScrollbarRedisplay(gw, NULL, NULL);
585}
586
587/*ARGSUSED*/
588static void
589XawScrollbarRedisplay(Widget gw, XEvent *event, Region region)
590{
591    ScrollbarWidget w = (ScrollbarWidget)gw;
592    int x, y;
593    unsigned int width, height;
594
595    if (Superclass->core_class.expose)
596	(*Superclass->core_class.expose)(gw, event, region);
597
598    if (w->scrollbar.orientation == XtorientHorizontal) {
599	x = w->scrollbar.topLoc;
600	y = 1;
601	width = (unsigned)(w->scrollbar.shownLength);
602	height = (unsigned)(XtHeight(w) - 2);
603    }
604    else {
605	x = 1;
606	y = w->scrollbar.topLoc;
607	width = (unsigned)(XtWidth(w) - 2);
608	height = (unsigned)(w->scrollbar.shownLength);
609    }
610
611    if (region == NULL ||
612	XRectInRegion(region, x, y, width, height) != RectangleOut) {
613	/* Forces entire thumb to be painted */
614	w->scrollbar.topLoc = (Position)(-(w->scrollbar.length + 1));
615	PaintThumb(w);
616    }
617}
618
619/*ARGSUSED*/
620static void
621StartScroll(Widget gw, XEvent *event _X_UNUSED, String *params, Cardinal *num_params)
622{
623    ScrollbarWidget w = (ScrollbarWidget)gw;
624    Cursor cursor;
625    char direction;
626
627    if (w->scrollbar.direction != 0)	/* if we're already scrolling */
628	return;
629    if (*num_params > 0)
630	direction = *params[0];
631    else
632	direction = 'C';
633
634    w->scrollbar.direction = direction;
635
636    switch(direction) {
637	case 'B':
638	case 'b':
639	    cursor = w->scrollbar.orientation == XtorientVertical ?
640		w->scrollbar.downCursor : w->scrollbar.rightCursor;
641	    break;
642	case 'F':
643	case 'f':
644	    cursor = w->scrollbar.orientation == XtorientVertical ?
645		w->scrollbar.upCursor : w->scrollbar.leftCursor;
646	    break;
647	case 'C':
648	case 'c':
649	     cursor = w->scrollbar.orientation == XtorientVertical ?
650		w->scrollbar.rightCursor : w->scrollbar.upCursor;
651	     break;
652	default:
653	     return;	/* invalid invocation */
654    }
655
656    XtVaSetValues(gw, XtNcursor, cursor, NULL);
657
658    XFlush(XtDisplay(w));
659}
660
661static Boolean
662CompareEvents(XEvent *oldEvent, XEvent *newEvent)
663{
664#define Check(field) if (newEvent->field != oldEvent->field) return (False)
665
666    Check(xany.display);
667    Check(xany.type);
668    Check(xany.window);
669
670    switch(newEvent->type) {
671	case MotionNotify:
672	    Check(xmotion.state);
673	    break;
674	case ButtonPress:
675	case ButtonRelease:
676	    Check(xbutton.state);
677	    Check(xbutton.button);
678	    break;
679	case KeyPress:
680	case KeyRelease:
681	    Check(xkey.state);
682	    Check(xkey.keycode);
683	    break;
684	case EnterNotify:
685	case LeaveNotify:
686	    Check(xcrossing.mode);
687	    Check(xcrossing.detail);
688	    Check(xcrossing.state);
689	    break;
690    }
691#undef Check
692
693    return (True);
694}
695
696struct EventData {
697    XEvent *oldEvent;
698    int count;
699};
700
701static Bool
702PeekNotifyEvent(Display *dpy, XEvent *event, char *args)
703{
704    struct EventData *eventData = (struct EventData*)args;
705
706    return (++eventData->count == QLength(dpy)	/* since PeekIf blocks */
707	    || CompareEvents(event, eventData->oldEvent));
708}
709
710static Bool
711LookAhead(Widget w, XEvent *event)
712{
713    XEvent newEvent;
714    struct EventData eventData;
715
716    if (QLength(XtDisplay(w)) == 0)
717	return (False);
718
719    eventData.count = 0;
720    eventData.oldEvent = event;
721
722    XPeekIfEvent(XtDisplay(w), &newEvent, PeekNotifyEvent, (char*)&eventData);
723
724    if (CompareEvents(event, &newEvent))
725	return (True);
726
727    return (False);
728}
729
730static void
731ExtractPosition(XEvent *event, Position *x, Position *y)
732{
733    switch(event->type) {
734	case MotionNotify:
735	    *x = (Position)event->xmotion.x;
736	    *y = (Position)event->xmotion.y;
737	    break;
738	case ButtonPress:
739	case ButtonRelease:
740	    *x = (Position)event->xbutton.x;
741	    *y = (Position)event->xbutton.y;
742	    break;
743	case KeyPress:
744	case KeyRelease:
745	    *x = (Position)event->xkey.x;
746	    *y = (Position)event->xkey.y;
747	    break;
748	case EnterNotify:
749	case LeaveNotify:
750	    *x = (Position)event->xcrossing.x;
751	    *y = (Position)event->xcrossing.y;
752	    break;
753	default:
754	    *x = 0;
755	    *y = 0;
756	    break;
757    }
758}
759
760static void
761NotifyScroll(Widget gw, XEvent *event, String *params, Cardinal *num_params)
762{
763    ScrollbarWidget w = (ScrollbarWidget)gw;
764    long call_data = 0;
765    char style;
766    Position x, y;
767
768    if (w->scrollbar.direction == 0)	/* if no StartScroll */
769	return;
770
771    if (LookAhead(gw, event))
772	return;
773
774    if (*num_params > 0)
775	style = *params[0];
776    else
777	style = 'P';
778
779    switch(style) {
780	case 'P':    /* Proportional */
781	case 'p':
782	    ExtractPosition(event, &x, &y);
783	    call_data = InRange(PICKLENGTH(w, x, y), 0, (int)w->scrollbar.length);
784	    break;
785	case 'F':    /* FullLength */
786	case 'f':
787	    call_data = w->scrollbar.length;
788	    break;
789    }
790
791    switch(w->scrollbar.direction) {
792	case 'B':
793	case 'b':
794	    call_data = -call_data;
795	    /*FALLTHROUGH*/
796	case 'F':
797	case 'f':
798	    XtCallCallbacks(gw, XtNscrollProc, (XtPointer)call_data);
799	    break;
800	case 'C':
801	case 'c':    /* NotifyThumb has already called the thumbProc(s) */
802	    break;
803    }
804}
805
806/*ARGSUSED*/
807static void
808EndScroll(Widget gw, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
809{
810    ScrollbarWidget w = (ScrollbarWidget)gw;
811
812    XtVaSetValues(gw, XtNcursor, w->scrollbar.inactiveCursor, NULL);
813    XFlush(XtDisplay(w));		/* make sure it get propagated */
814
815    w->scrollbar.direction = 0;
816}
817
818/*ARGSUSED*/
819static void
820MoveThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
821{
822    ScrollbarWidget w = (ScrollbarWidget)gw;
823    Position x, y;
824
825    if (w->scrollbar.direction == 0)	/* if no StartScroll */
826	return;
827
828    if (LookAhead(gw, event))
829	return;
830
831    if (!event->xmotion.same_screen)
832	return;
833
834    ExtractPosition(event, &x, &y);
835    w->scrollbar.top = FractionLoc(w, x, y);
836}
837
838/*ARGSUSED*/
839static void
840NotifyThumb(Widget gw, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
841{
842    ScrollbarWidget w = (ScrollbarWidget)gw;
843    union {
844	XtPointer xtp;
845	float xtf;
846    } xtpf;
847
848    if (w->scrollbar.direction == 0)	/* if no StartScroll */
849	return;
850
851    if (LookAhead(gw, event))
852	return;
853
854    /* thumbProc is not pretty, but is necessary for backwards
855       compatibility on those architectures for which it work{s,ed};
856       the intent is to pass a (truncated) float by value. */
857    xtpf.xtf = w->scrollbar.top;
858    XtCallCallbacks(gw, XtNthumbProc, xtpf.xtp);
859    XtCallCallbacks(gw, XtNjumpProc, (XtPointer)&w->scrollbar.top);
860
861    PaintThumb(w);
862}
863
864/*
865 *  Public routines
866 */
867/* Set the scroll bar to the given location. */
868void
869XawScrollbarSetThumb(Widget gw,
870#if NeedWidePrototypes
871		     double top, double shown
872#else
873		     float top, float shown
874#endif
875		     )
876{
877    ScrollbarWidget w = (ScrollbarWidget)gw;
878
879    if (w->scrollbar.direction == 'c')	/* if still thumbing */
880	return;
881
882    w->scrollbar.top = (float)((top > 1.0)
883			       ? 1.0
884			       : ((top >= 0.0)
885			          ? top
886			          : w->scrollbar.top));
887
888    w->scrollbar.shown = (float)((shown > 1.0)
889				  ? 1.0
890				  : (shown >= 0.0
891				     ? shown
892				     : w->scrollbar.shown));
893    PaintThumb(w);
894}
895