1/* vi:set ts=8 sts=4 sw=4: */
2/*
3 * MODIFIED ATHENA SCROLLBAR (USING ARROWHEADS AT ENDS OF TRAVEL)
4 * Modifications Copyright 1992 by Mitch Trachtenberg
5 * Rights, permissions, and disclaimer of warranty are as in the DEC and MIT
6 * notice below.
7 * $XConsortium: Scrollbar.c,v 1.72 94/04/17 20:12:40 kaleb Exp $
8 */
9
10/*
11 * Modified for Vim by Bill Foster and Bram Moolenaar
12 */
13
14/*
15
16Copyright (c) 1987, 1988, 1994	X Consortium
17
18Permission is hereby granted, free of charge, to any person obtaining a copy
19of this software and associated documentation files (the "Software"), to deal
20in the Software without restriction, including without limitation the rights
21to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22copies of the Software, and to permit persons to whom the Software is
23furnished to do so, subject to the following conditions:
24
25The above copyright notice and this permission notice shall be included in all
26copies or substantial portions of the Software.
27
28THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE X
31CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
35Except as contained in this notice, the name of the X Consortium shall not be
36used in advertising or otherwise to promote the sale, use or other dealings in
37this Software without prior written authorization from the X Consortium.
38
39Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
40
41			All Rights Reserved
42
43Permission to use, copy, modify, and distribute this software and its
44documentation for any purpose and without fee is hereby granted, provided that
45the above copyright notice appear in all copies and that both that copyright
46notice and this permission notice appear in supporting documentation, and that
47the name of Digital not be used in advertising or publicity pertaining to
48distribution of the software without specific, written prior permission.
49
50DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
51IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL
52BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
53WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
54OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
55CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
56
57*/
58
59/* ScrollBar.c */
60/* created by weissman, Mon Jul  7 13:20:03 1986 */
61/* converted by swick, Thu Aug 27 1987 */
62
63#include <X11/IntrinsicP.h>
64#include <X11/StringDefs.h>
65
66#include <X11/Xaw/XawInit.h>
67#include "vim.h"
68#include "gui_at_sb.h"
69
70#include <X11/Xmu/Drawing.h>
71
72/* Private definitions. */
73
74static char defaultTranslations[] =
75    "<Btn1Down>: NotifyScroll()\n\
76     <Btn2Down>: MoveThumb() NotifyThumb()\n\
77     <Btn3Down>: NotifyScroll()\n\
78     <Btn4Down>: ScrollOneLineUp()\n\
79     Shift<Btn4Down>: ScrollPageUp()\n\
80     <Btn5Down>: ScrollOneLineDown()\n\
81     Shift<Btn5Down>: ScrollPageDown()\n\
82     <Btn1Motion>: HandleThumb()\n\
83     <Btn3Motion>: HandleThumb()\n\
84     <Btn2Motion>: MoveThumb() NotifyThumb()\n\
85     <BtnUp>: EndScroll()";
86
87static float floatZero = 0.0;
88
89#define Offset(field) XtOffsetOf(ScrollbarRec, field)
90
91static XtResource resources[] =
92{
93  {XtNlength, XtCLength, XtRDimension, sizeof(Dimension),
94       Offset(scrollbar.length), XtRImmediate, (XtPointer) 1},
95  {XtNthickness, XtCThickness, XtRDimension, sizeof(Dimension),
96       Offset(scrollbar.thickness), XtRImmediate, (XtPointer) 14},
97  {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
98      Offset(scrollbar.orientation), XtRImmediate, (XtPointer) XtorientVertical},
99  {XtNscrollProc, XtCCallback, XtRCallback, sizeof(XtPointer),
100       Offset(scrollbar.scrollProc), XtRCallback, NULL},
101  {XtNthumbProc, XtCCallback, XtRCallback, sizeof(XtPointer),
102       Offset(scrollbar.thumbProc), XtRCallback, NULL},
103  {XtNjumpProc, XtCCallback, XtRCallback, sizeof(XtPointer),
104       Offset(scrollbar.jumpProc), XtRCallback, NULL},
105  {XtNthumb, XtCThumb, XtRBitmap, sizeof(Pixmap),
106       Offset(scrollbar.thumb), XtRImmediate, (XtPointer) XtUnspecifiedPixmap},
107  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
108       Offset(scrollbar.foreground), XtRString, XtDefaultForeground},
109  {XtNshown, XtCShown, XtRFloat, sizeof(float),
110       Offset(scrollbar.shown), XtRFloat, (XtPointer)&floatZero},
111  {XtNtopOfThumb, XtCTopOfThumb, XtRFloat, sizeof(float),
112       Offset(scrollbar.top), XtRFloat, (XtPointer)&floatZero},
113  {XtNmaxOfThumb, XtCMaxOfThumb, XtRFloat, sizeof(float),
114       Offset(scrollbar.max), XtRFloat, (XtPointer)&floatZero},
115  {XtNminimumThumb, XtCMinimumThumb, XtRDimension, sizeof(Dimension),
116       Offset(scrollbar.min_thumb), XtRImmediate, (XtPointer) 7},
117  {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
118       Offset(scrollbar.shadow_width), XtRImmediate, (XtPointer) 1},
119  {XtNtopShadowPixel, XtCTopShadowPixel, XtRPixel, sizeof(Pixel),
120       Offset(scrollbar.top_shadow_pixel), XtRString, XtDefaultBackground},
121  {XtNbottomShadowPixel, XtCBottomShadowPixel, XtRPixel, sizeof(Pixel),
122       Offset(scrollbar.bot_shadow_pixel), XtRString, XtDefaultForeground},
123  {XtNlimitThumb, XtCLimitThumb, XtRBool, sizeof(Bool),
124       Offset(scrollbar.limit_thumb), XtRImmediate, (XtPointer)0}
125};
126#undef Offset
127
128static void ClassInitialize __ARGS((void));
129static void Initialize __ARGS((Widget, Widget, ArgList, Cardinal *));
130static void Destroy __ARGS((Widget));
131static void Realize __ARGS((Widget, Mask *, XSetWindowAttributes *));
132static void Resize __ARGS((Widget));
133static void Redisplay __ARGS((Widget, XEvent *, Region));
134static Boolean SetValues __ARGS((Widget, Widget, Widget, ArgList, Cardinal *));
135
136static void HandleThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
137static void MoveThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
138static void NotifyThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
139static void NotifyScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
140static void EndScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
141static void ScrollOneLineUp __ARGS((Widget, XEvent *, String *, Cardinal *));
142static void ScrollOneLineDown __ARGS((Widget, XEvent *, String *, Cardinal *));
143static void ScrollPageUp __ARGS((Widget, XEvent *, String *, Cardinal *));
144static void ScrollPageDown __ARGS((Widget, XEvent *, String *, Cardinal *));
145static void ScrollSome __ARGS((Widget w, XEvent *event, int call_data));
146static void _Xaw3dDrawShadows __ARGS((Widget, XEvent *, Region, int));
147static void AllocTopShadowGC __ARGS((Widget));
148static void AllocBotShadowGC __ARGS((Widget));
149
150static XtActionsRec actions[] =
151{
152    {"HandleThumb",	HandleThumb},
153    {"MoveThumb",	MoveThumb},
154    {"NotifyThumb",	NotifyThumb},
155    {"NotifyScroll",	NotifyScroll},
156    {"EndScroll",	EndScroll},
157    {"ScrollOneLineUp", ScrollOneLineUp},
158    {"ScrollOneLineDown", ScrollOneLineDown},
159    {"ScrollPageUp",	ScrollPageUp},
160    {"ScrollPageDown",	ScrollPageDown}
161};
162
163
164ScrollbarClassRec vim_scrollbarClassRec =
165{
166  { /* core fields */
167    /* superclass	*/  (WidgetClass) &simpleClassRec,
168    /* class_name	*/  "Scrollbar",
169    /* size		*/  sizeof(ScrollbarRec),
170    /* class_initialize	*/  ClassInitialize,
171    /* class_part_init	*/  NULL,
172    /* class_inited	*/  FALSE,
173    /* initialize	*/  Initialize,
174    /* initialize_hook	*/  NULL,
175    /* realize		*/  Realize,
176    /* actions		*/  actions,
177    /* num_actions	*/  XtNumber(actions),
178    /* resources	*/  resources,
179    /* num_resources	*/  XtNumber(resources),
180    /* xrm_class	*/  NULLQUARK,
181    /* compress_motion	*/  TRUE,
182    /* compress_exposure*/  TRUE,
183    /* compress_enterleave*/	TRUE,
184    /* visible_interest */  FALSE,
185    /* destroy		*/  Destroy,
186    /* resize		*/  Resize,
187    /* expose		*/  Redisplay,
188    /* set_values	*/  SetValues,
189    /* set_values_hook	*/  NULL,
190    /* set_values_almost */ XtInheritSetValuesAlmost,
191    /* get_values_hook	*/  NULL,
192    /* accept_focus	*/  NULL,
193    /* version		*/  XtVersion,
194    /* callback_private */  NULL,
195    /* tm_table		*/  defaultTranslations,
196    /* query_geometry	*/  XtInheritQueryGeometry,
197    /* display_accelerator*/	XtInheritDisplayAccelerator,
198    /* extension	*/  NULL
199  },
200  { /* simple fields */
201    /* change_sensitive	*/  XtInheritChangeSensitive,
202#ifndef OLDXAW
203    /* extension */	    NULL
204#endif
205  },
206  { /* scrollbar fields */
207    /* empty	    */	    0
208  }
209};
210
211WidgetClass vim_scrollbarWidgetClass = (WidgetClass)&vim_scrollbarClassRec;
212
213#define NoButton -1
214#define PICKLENGTH(widget, x, y) \
215    ((widget->scrollbar.orientation == XtorientHorizontal) ? (x) : (y))
216#define AT_MIN(x,y)    ((x) < (y) ? (x) : (y))
217#define AT_MAX(x,y)    ((x) > (y) ? (x) : (y))
218
219#define LINE_DELAY	300
220#define PAGE_DELAY	300
221#define LINE_REPEAT	 50
222#define PAGE_REPEAT	250
223
224    static void
225ClassInitialize()
226{
227    XawInitializeWidgetSet();
228    XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
229	    (XtConvertArgList)NULL, (Cardinal)0 );
230}
231
232#define MARGIN(sbw) (sbw)->scrollbar.thickness + (sbw)->scrollbar.shadow_width
233
234    static void
235FillArea(sbw, top, bottom, fill, draw_shadow)
236    ScrollbarWidget	sbw;
237    Position		top, bottom;
238    int			fill;
239    int			draw_shadow;
240{
241    int tlen = bottom - top;	/* length of thumb in pixels */
242    int sw, margin, floor;
243    int lx, ly, lw, lh;
244
245    if (bottom <= 0 || bottom <= top)
246	return;
247    sw = sbw->scrollbar.shadow_width;
248    if (sw < 0)
249	sw = 0;
250    margin = MARGIN (sbw);
251    floor = sbw->scrollbar.length - margin + 2;
252
253    if (sbw->scrollbar.orientation == XtorientHorizontal)
254    {
255	lx = ((top < margin) ? margin : top);
256	ly = sw;
257	lw = (((top + tlen) > floor) ? floor - top : tlen);
258	lh = sbw->core.height - 2 * sw;
259    }
260    else
261    {
262	lx = sw;
263	ly = ((top < margin) ? margin : top);
264	lw = sbw->core.width - 2 * sw;
265	lh = (((top + tlen) > floor) ? floor - top : tlen);
266    }
267    if (lh <= 0 || lw <= 0)
268	return;
269
270    if (draw_shadow)
271    {
272	if (!(sbw->scrollbar.orientation == XtorientHorizontal))
273	{
274	    /* Top border */
275	    XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
276		    sbw->scrollbar.top_shadow_GC,
277		    lx, ly, lx + lw - 1, ly);
278
279	    /* Bottom border */
280	    XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
281		    sbw->scrollbar.bot_shadow_GC,
282		    lx, ly + lh - 1, lx + lw - 1, ly + lh - 1);
283	}
284	else
285	{
286	    /* Left border */
287	    XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
288		    sbw->scrollbar.top_shadow_GC,
289		    lx, ly, lx, ly + lh - 1);
290
291	    /* Right border */
292	    XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
293		    sbw->scrollbar.bot_shadow_GC,
294		    lx + lw - 1, ly, lx + lw - 1, ly + lh - 1);
295	}
296	return;
297    }
298
299    if (fill)
300    {
301	XFillRectangle(XtDisplay((Widget) sbw), XtWindow((Widget) sbw),
302		sbw->scrollbar.gc,
303		lx, ly, (unsigned int) lw, (unsigned int) lh);
304
305	if (!(sbw->scrollbar.orientation == XtorientHorizontal))
306	{
307	    /* Left border */
308	    XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
309		    sbw->scrollbar.top_shadow_GC,
310		    lx, ly, lx, ly + lh - 1);
311
312	    /* Right border */
313	    XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
314		    sbw->scrollbar.bot_shadow_GC,
315		    lx + lw - 1, ly, lx + lw - 1, ly + lh - 1);
316	}
317	else
318	{
319	    /* Top border */
320	    XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
321		    sbw->scrollbar.top_shadow_GC,
322		    lx, ly, lx + lw - 1, ly);
323
324	    /* Bottom border */
325	    XDrawLine(XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
326		    sbw->scrollbar.bot_shadow_GC,
327		    lx, ly + lh - 1, lx + lw - 1, ly + lh - 1);
328	}
329    }
330    else
331    {
332	XClearArea(XtDisplay((Widget) sbw), XtWindow((Widget) sbw),
333		lx, ly, (unsigned int) lw, (unsigned int) lh, FALSE);
334    }
335}
336
337/* Paint the thumb in the area specified by sbw->top and
338   sbw->shown.	The old area is erased.  The painting and
339   erasing is done cleverly so that no flickering will occur.
340 */
341
342    static void
343PaintThumb(sbw)
344    ScrollbarWidget sbw;
345{
346    Position	    oldtop, oldbot, newtop, newbot;
347    Dimension	    margin, tzl;
348
349    margin = MARGIN (sbw);
350    tzl = sbw->scrollbar.length - 2 * margin;
351    newtop = margin + (int)(tzl * sbw->scrollbar.top);
352    newbot = newtop + (int)(tzl * sbw->scrollbar.shown) + 1;
353    if (newbot < newtop + (int)sbw->scrollbar.min_thumb)
354	newbot = newtop + sbw->scrollbar.min_thumb;
355
356    oldtop = sbw->scrollbar.topLoc;
357    oldbot = oldtop + sbw->scrollbar.shownLength;
358    sbw->scrollbar.topLoc = newtop;
359    sbw->scrollbar.shownLength = newbot - newtop;
360    if (XtIsRealized ((Widget) sbw))
361    {
362	if (newtop < oldtop)
363	    FillArea(sbw, newtop, AT_MIN(newbot,	oldtop+1),1,0);
364	if (newtop > oldtop)
365	    FillArea(sbw, oldtop, AT_MIN(newtop,	oldbot	),0,0);
366	if (newbot < oldbot)
367	    FillArea(sbw, AT_MAX(newbot, oldtop),	oldbot,   0,0);
368	if (newbot > oldbot)
369	    FillArea(sbw, AT_MAX(newtop, oldbot-1), newbot,  1,0);
370
371	/* Only draw the missing shadows */
372	FillArea(sbw, newtop, newbot, 0, 1);
373    }
374}
375
376    static void
377PaintArrows(sbw)
378    ScrollbarWidget sbw;
379{
380    XPoint	point[6];
381    Dimension	thickness = sbw->scrollbar.thickness - 1;
382    Dimension	size;
383    Dimension	off;
384
385    if (XtIsRealized((Widget) sbw))
386    {
387	if ((int)thickness * 2 > (int)sbw->scrollbar.length)
388	{
389	    size = sbw->scrollbar.length / 2;
390	    off = (int)(thickness - size) / 2;
391	}
392	else
393	{
394	    size = thickness;
395	    off = 0;
396	}
397	point[0].x = off + sbw->scrollbar.shadow_width;
398	point[0].y = size;
399	point[1].x = thickness - off - sbw->scrollbar.shadow_width;
400	point[1].y = size;
401	point[2].x = thickness / 2;
402	point[2].y = sbw->scrollbar.shadow_width;
403
404	point[3].x = off + sbw->scrollbar.shadow_width;
405	point[3].y = sbw->scrollbar.length - size;
406	point[4].x = thickness - off - sbw->scrollbar.shadow_width;
407	point[4].y = sbw->scrollbar.length - size;
408	point[5].x = thickness / 2;
409	point[5].y = sbw->scrollbar.length - sbw->scrollbar.shadow_width - 1;
410
411	/* horizontal arrows require that x and y coordinates be swapped */
412	if (sbw->scrollbar.orientation == XtorientHorizontal)
413	{
414	    int n;
415	    int swap;
416	    for (n = 0; n < 6; n++)
417	    {
418		swap = point[n].x;
419		point[n].x = point[n].y;
420		point[n].y = swap;
421	    }
422	}
423	/* draw the up/left arrow */
424	XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
425		sbw->scrollbar.gc,
426		point, 3,
427		Convex, CoordModeOrigin);
428	XDrawLines (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
429		sbw->scrollbar.bot_shadow_GC,
430		point, 3,
431		CoordModeOrigin);
432	XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
433		sbw->scrollbar.top_shadow_GC,
434		point[0].x, point[0].y,
435		point[2].x, point[2].y);
436	/* draw the down/right arrow */
437	XFillPolygon (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
438		sbw->scrollbar.gc,
439		point+3, 3,
440		Convex, CoordModeOrigin);
441	XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
442		sbw->scrollbar.top_shadow_GC,
443		point[3].x, point[3].y,
444		point[4].x, point[4].y);
445	XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
446		sbw->scrollbar.top_shadow_GC,
447		point[3].x, point[3].y,
448		point[5].x, point[5].y);
449	XDrawLine (XtDisplay ((Widget) sbw), XtWindow ((Widget) sbw),
450		sbw->scrollbar.bot_shadow_GC,
451		point[4].x, point[4].y,
452		point[5].x, point[5].y);
453    }
454}
455
456    static void
457Destroy(w)
458    Widget w;
459{
460    ScrollbarWidget sbw = (ScrollbarWidget) w;
461    if (sbw->scrollbar.timer_id != (XtIntervalId) 0)
462	XtRemoveTimeOut (sbw->scrollbar.timer_id);
463    XtReleaseGC(w, sbw->scrollbar.gc);
464    XtReleaseGC(w, sbw->scrollbar.top_shadow_GC);
465    XtReleaseGC(w, sbw->scrollbar.bot_shadow_GC);
466}
467
468    static void
469CreateGC(w)
470    Widget w;
471{
472    ScrollbarWidget	sbw = (ScrollbarWidget) w;
473    XGCValues		gcValues;
474    XtGCMask		mask;
475    unsigned int	depth = 1;
476
477    if (sbw->scrollbar.thumb == XtUnspecifiedPixmap)
478    {
479	sbw->scrollbar.thumb = XmuCreateStippledPixmap (XtScreen(w),
480		    (Pixel) 1, (Pixel) 0, depth);
481    }
482    else if (sbw->scrollbar.thumb != None)
483    {
484	Window		root;
485	int		x, y;
486	unsigned int	width, height, bw;
487
488	if (XGetGeometry (XtDisplay(w), sbw->scrollbar.thumb, &root, &x, &y,
489		&width, &height, &bw, &depth) == 0)
490	    EMSG(_("Scrollbar Widget: Could not get geometry of thumb pixmap."));
491    }
492
493    gcValues.foreground = sbw->scrollbar.foreground;
494    gcValues.background = sbw->core.background_pixel;
495    mask = GCForeground | GCBackground;
496
497    if (sbw->scrollbar.thumb != None)
498    {
499	gcValues.fill_style = FillSolid;
500	mask |= GCFillStyle;
501    }
502    /* the creation should be non-caching, because */
503    /* we now set and clear clip masks on the gc returned */
504    sbw->scrollbar.gc = XtGetGC (w, mask, &gcValues);
505}
506
507    static void
508SetDimensions(sbw)
509    ScrollbarWidget sbw;
510{
511    if (sbw->scrollbar.orientation == XtorientVertical)
512    {
513	sbw->scrollbar.length = sbw->core.height;
514	sbw->scrollbar.thickness = sbw->core.width;
515    }
516    else
517    {
518	sbw->scrollbar.length = sbw->core.width;
519	sbw->scrollbar.thickness = sbw->core.height;
520    }
521}
522
523    static void
524Initialize(request, new, args, num_args)
525    Widget	request UNUSED;	/* what the client asked for */
526    Widget	new;		/* what we're going to give him */
527    ArgList	args UNUSED;
528    Cardinal	*num_args UNUSED;
529{
530    ScrollbarWidget sbw = (ScrollbarWidget) new;
531
532    CreateGC(new);
533    AllocTopShadowGC(new);
534    AllocBotShadowGC(new);
535
536    if (sbw->core.width == 0)
537	sbw->core.width = (sbw->scrollbar.orientation == XtorientVertical)
538	    ? sbw->scrollbar.thickness : sbw->scrollbar.length;
539
540    if (sbw->core.height == 0)
541	sbw->core.height = (sbw->scrollbar.orientation == XtorientHorizontal)
542	    ? sbw->scrollbar.thickness : sbw->scrollbar.length;
543
544    SetDimensions(sbw);
545    sbw->scrollbar.scroll_mode = SMODE_NONE;
546    sbw->scrollbar.timer_id = (XtIntervalId)0;
547    sbw->scrollbar.topLoc = 0;
548    sbw->scrollbar.shownLength = sbw->scrollbar.min_thumb;
549}
550
551    static void
552Realize(w, valueMask, attributes)
553    Widget w;
554    Mask *valueMask;
555    XSetWindowAttributes *attributes;
556{
557    /* The Simple widget actually stuffs the value in the valuemask. */
558    (*vim_scrollbarWidgetClass->core_class.superclass->core_class.realize)
559	(w, valueMask, attributes);
560}
561
562    static Boolean
563SetValues(current, request, desired, args, num_args)
564    Widget  current;	    /* what I am */
565    Widget  request UNUSED; /* what he wants me to be */
566    Widget  desired;	    /* what I will become */
567    ArgList args UNUSED;
568    Cardinal *num_args UNUSED;
569{
570    ScrollbarWidget	sbw = (ScrollbarWidget) current;
571    ScrollbarWidget	dsbw = (ScrollbarWidget) desired;
572    Boolean		redraw = FALSE;
573
574/*
575 * If these values are outside the acceptable range ignore them...
576 */
577    if (dsbw->scrollbar.top < 0.0 || dsbw->scrollbar.top > 1.0)
578	dsbw->scrollbar.top = sbw->scrollbar.top;
579
580    if (dsbw->scrollbar.shown < 0.0 || dsbw->scrollbar.shown > 1.0)
581	dsbw->scrollbar.shown = sbw->scrollbar.shown;
582
583/*
584 * Change colors and stuff...
585 */
586    if (XtIsRealized(desired))
587    {
588	if (sbw->scrollbar.foreground != dsbw->scrollbar.foreground ||
589		sbw->core.background_pixel != dsbw->core.background_pixel ||
590		sbw->scrollbar.thumb != dsbw->scrollbar.thumb)
591	{
592	    XtReleaseGC(desired, sbw->scrollbar.gc);
593	    CreateGC (desired);
594	    redraw = TRUE;
595	}
596	if (sbw->scrollbar.top != dsbw->scrollbar.top ||
597		sbw->scrollbar.shown != dsbw->scrollbar.shown)
598	    redraw = TRUE;
599    }
600    return redraw;
601}
602
603    static void
604Resize(w)
605    Widget w;
606{
607    /* ForgetGravity has taken care of background, but thumb may
608     * have to move as a result of the new size. */
609    SetDimensions ((ScrollbarWidget) w);
610    Redisplay(w, (XEvent*) NULL, (Region)NULL);
611}
612
613
614    static void
615Redisplay(w, event, region)
616    Widget w;
617    XEvent *event;
618    Region region;
619{
620    ScrollbarWidget sbw = (ScrollbarWidget) w;
621    int x, y;
622    unsigned int width, height;
623
624    _Xaw3dDrawShadows(w, event, region, FALSE);
625
626    if (sbw->scrollbar.orientation == XtorientHorizontal)
627    {
628	x = sbw->scrollbar.topLoc;
629	y = 1;
630	width = sbw->scrollbar.shownLength;
631	height = sbw->core.height - 2;
632    }
633    else
634    {
635	x = 1;
636	y = sbw->scrollbar.topLoc;
637	width = sbw->core.width - 2;
638	height = sbw->scrollbar.shownLength;
639    }
640    if (region == NULL ||
641	    XRectInRegion (region, x, y, width, height) != RectangleOut)
642    {
643	/* Forces entire thumb to be painted. */
644	sbw->scrollbar.topLoc = -(sbw->scrollbar.length + 1);
645	PaintThumb (sbw);
646    }
647    /* we'd like to be region aware here!!!! */
648    PaintArrows(sbw);
649}
650
651
652    static Boolean
653CompareEvents(oldEvent, newEvent)
654    XEvent *oldEvent, *newEvent;
655{
656#define Check(field) if (newEvent->field != oldEvent->field) return False;
657
658    Check(xany.display);
659    Check(xany.type);
660    Check(xany.window);
661
662    switch (newEvent->type)
663    {
664	case MotionNotify:
665	    Check(xmotion.state);
666	    break;
667	case ButtonPress:
668	case ButtonRelease:
669	    Check(xbutton.state);
670	    Check(xbutton.button);
671	    break;
672	case KeyPress:
673	case KeyRelease:
674	    Check(xkey.state);
675	    Check(xkey.keycode);
676	    break;
677	case EnterNotify:
678	case LeaveNotify:
679	    Check(xcrossing.mode);
680	    Check(xcrossing.detail);
681	    Check(xcrossing.state);
682	    break;
683    }
684#undef Check
685
686    return True;
687}
688
689struct EventData
690{
691    XEvent *oldEvent;
692    int count;
693};
694
695    static Bool
696PeekNotifyEvent(dpy, event, args)
697    Display *dpy;
698    XEvent *event;
699    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
707
708    static Boolean
709LookAhead(w, event)
710    Widget w;
711    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    return CompareEvents (event, &newEvent);
725}
726
727
728    static void
729ExtractPosition(event, x, y, state)
730    XEvent	    *event;
731    Position	    *x, *y;	/* RETURN */
732    unsigned int    *state;	/* RETURN */
733{
734    switch (event->type)
735    {
736	case MotionNotify:
737	    *x = event->xmotion.x;
738	    *y = event->xmotion.y;
739	    if (state != NULL)
740		*state = event->xmotion.state;
741	    break;
742	case ButtonPress:
743	case ButtonRelease:
744	    *x = event->xbutton.x;
745	    *y = event->xbutton.y;
746	    if (state != NULL)
747		*state = event->xbutton.state;
748	    break;
749	case KeyPress:
750	case KeyRelease:
751	    *x = event->xkey.x;
752	    *y = event->xkey.y;
753	    if (state != NULL)
754		*state = event->xkey.state;
755	    break;
756	case EnterNotify:
757	case LeaveNotify:
758	    *x = event->xcrossing.x;
759	    *y = event->xcrossing.y;
760	    if (state != NULL)
761		*state = event->xcrossing.state;
762	    break;
763	default:
764	    *x = 0; *y = 0;
765	    if (state != NULL)
766		*state = 0;
767    }
768}
769
770    static void
771HandleThumb(w, event, params, num_params)
772    Widget w;
773    XEvent *event;
774    String *params;
775    Cardinal *num_params;
776{
777    Position x, y, loc;
778    ScrollbarWidget sbw = (ScrollbarWidget) w;
779
780    ExtractPosition(event, &x, &y, (unsigned int *)NULL);
781    loc = PICKLENGTH(sbw, x, y);
782    /* if the motion event puts the pointer in thumb, call Move and Notify */
783    /* also call Move and Notify if we're already in continuous scroll mode */
784    if (sbw->scrollbar.scroll_mode == SMODE_CONT ||
785	    (loc >= sbw->scrollbar.topLoc &&
786	     loc <= sbw->scrollbar.topLoc + (int)sbw->scrollbar.shownLength))
787    {
788	XtCallActionProc(w, "MoveThumb", event, params, *num_params);
789	XtCallActionProc(w, "NotifyThumb", event, params, *num_params);
790    }
791}
792
793    static void
794RepeatNotify(client_data, idp)
795    XtPointer client_data;
796    XtIntervalId *idp UNUSED;
797{
798    ScrollbarWidget sbw = (ScrollbarWidget) client_data;
799    int		    call_data;
800    char	    mode = sbw->scrollbar.scroll_mode;
801    unsigned long   rep;
802
803    if (mode == SMODE_NONE || mode == SMODE_CONT)
804    {
805	sbw->scrollbar.timer_id = (XtIntervalId)0;
806	return;
807    }
808
809    if (mode == SMODE_LINE_DOWN || mode == SMODE_LINE_UP)
810    {
811	call_data = ONE_LINE_DATA;
812	rep = LINE_REPEAT;
813    }
814    else
815    {
816	call_data = ONE_PAGE_DATA;
817	rep = PAGE_REPEAT;
818    }
819
820    if (mode == SMODE_PAGE_UP || mode == SMODE_LINE_UP)
821	call_data = -call_data;
822
823    XtCallCallbacks((Widget)sbw, XtNscrollProc, (XtPointer)(long_u)call_data);
824
825    sbw->scrollbar.timer_id =
826	XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)sbw),
827		rep,
828		RepeatNotify,
829		client_data);
830}
831
832/*
833 * Same as above, but for floating numbers.
834 */
835    static float
836FloatInRange(num, small, big)
837    float num, small, big;
838{
839    return (num < small) ? small : ((num > big) ? big : num);
840}
841
842    static void
843ScrollOneLineUp(w, event, params, num_params)
844    Widget	w;
845    XEvent	*event;
846    String	*params UNUSED;
847    Cardinal	*num_params UNUSED;
848{
849    ScrollSome(w, event, -ONE_LINE_DATA);
850}
851
852    static void
853ScrollOneLineDown(w, event, params, num_params)
854    Widget	w;
855    XEvent	*event;
856    String	*params UNUSED;
857    Cardinal	*num_params UNUSED;
858{
859    ScrollSome(w, event, ONE_LINE_DATA);
860}
861
862    static void
863ScrollPageDown(w, event, params, num_params)
864    Widget	w;
865    XEvent	*event;
866    String	*params UNUSED;
867    Cardinal	*num_params UNUSED;
868{
869    ScrollSome(w, event, ONE_PAGE_DATA);
870}
871
872    static void
873ScrollPageUp(w, event, params, num_params)
874    Widget	w;
875    XEvent	*event;
876    String	*params UNUSED;
877    Cardinal	*num_params UNUSED;
878{
879    ScrollSome(w, event, -ONE_PAGE_DATA);
880}
881
882    static void
883ScrollSome(w, event, call_data)
884    Widget	w;
885    XEvent	*event;
886    int		call_data;
887{
888    ScrollbarWidget	sbw = (ScrollbarWidget) w;
889
890    if (sbw->scrollbar.scroll_mode == SMODE_CONT)   /* if scroll continuous */
891	return;
892
893    if (LookAhead(w, event))
894	return;
895
896    sbw->scrollbar.scroll_mode = SMODE_LINE_UP;
897    XtCallCallbacks(w, XtNscrollProc, (XtPointer)(long_u)call_data);
898}
899
900    static void
901NotifyScroll(w, event, params, num_params)
902    Widget	w;
903    XEvent	*event;
904    String	*params UNUSED;
905    Cardinal	*num_params UNUSED;
906{
907    ScrollbarWidget sbw = (ScrollbarWidget) w;
908    Position	    x, y, loc;
909    Dimension	    arrow_size;
910    unsigned long   delay = 0;
911    int		    call_data = 0;
912    unsigned int    state;
913
914    if (sbw->scrollbar.scroll_mode == SMODE_CONT)   /* if scroll continuous */
915	return;
916
917    if (LookAhead (w, event))
918	return;
919
920    ExtractPosition(event, &x, &y, &state);
921    loc = PICKLENGTH(sbw, x, y);
922
923    if ((int)sbw->scrollbar.thickness * 2 > (int)sbw->scrollbar.length)
924	arrow_size = sbw->scrollbar.length / 2;
925    else
926	arrow_size = sbw->scrollbar.thickness;
927
928    /*
929     * handle CTRL modifier
930     */
931    if (state & ControlMask)
932    {
933	if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength)
934	    call_data = END_PAGE_DATA;
935	else
936	    call_data = -END_PAGE_DATA;
937	sbw->scrollbar.scroll_mode = SMODE_NONE;
938    }
939    /*
940     * handle first arrow zone
941     */
942    else if (loc < (Position)arrow_size)
943    {
944	call_data = -ONE_LINE_DATA;
945	sbw->scrollbar.scroll_mode = SMODE_LINE_UP;
946	delay = LINE_DELAY;
947    }
948
949    /*
950     * handle last arrow zone
951     */
952    else if (loc > (Position)(sbw->scrollbar.length - arrow_size))
953    {
954	call_data = ONE_LINE_DATA;
955	sbw->scrollbar.scroll_mode = SMODE_LINE_DOWN;
956	delay = LINE_DELAY;
957    }
958
959    /*
960     * handle zone "above" the thumb
961     */
962    else if (loc < sbw->scrollbar.topLoc)
963    {
964	call_data = -ONE_PAGE_DATA;
965	sbw->scrollbar.scroll_mode = SMODE_PAGE_UP;
966	delay = PAGE_DELAY;
967    }
968
969    /*
970     * handle zone "below" the thumb
971     */
972    else if (loc > sbw->scrollbar.topLoc + (Position)sbw->scrollbar.shownLength)
973    {
974	call_data = ONE_PAGE_DATA;
975	sbw->scrollbar.scroll_mode = SMODE_PAGE_DOWN;
976	delay = PAGE_DELAY;
977    }
978
979    if (call_data)
980	XtCallCallbacks(w, XtNscrollProc, (XtPointer)(long_u)call_data);
981
982    /* establish autoscroll */
983    if (delay)
984	sbw->scrollbar.timer_id =
985	    XtAppAddTimeOut(XtWidgetToApplicationContext(w),
986					   delay, RepeatNotify, (XtPointer)w);
987}
988
989    static void
990EndScroll(w, event, params, num_params)
991    Widget w;
992    XEvent *event UNUSED;
993    String *params UNUSED;
994    Cardinal *num_params UNUSED;
995{
996    ScrollbarWidget sbw = (ScrollbarWidget) w;
997
998    sbw->scrollbar.scroll_mode = SMODE_NONE;
999    /* no need to remove any autoscroll timeout; it will no-op */
1000    /* because the scroll_mode is SMODE_NONE */
1001    /* but be sure to remove timeout in destroy proc */
1002}
1003
1004    static float
1005FractionLoc(sbw, x, y)
1006    ScrollbarWidget sbw;
1007    int x, y;
1008{
1009    int	    margin;
1010    float   height, width;
1011
1012    margin = MARGIN(sbw);
1013    x -= margin;
1014    y -= margin;
1015    height = (float)sbw->core.height - 2 * margin;
1016    width = (float)sbw->core.width - 2 * margin;
1017    return PICKLENGTH(sbw, x / width, y / height);
1018}
1019
1020    static void
1021MoveThumb(w, event, params, num_params)
1022    Widget	w;
1023    XEvent	*event;
1024    String	*params UNUSED;
1025    Cardinal	*num_params UNUSED;
1026{
1027    ScrollbarWidget	sbw = (ScrollbarWidget)w;
1028    Position		x, y;
1029    float		top;
1030    char		old_mode = sbw->scrollbar.scroll_mode;
1031
1032    sbw->scrollbar.scroll_mode = SMODE_CONT; /* indicate continuous scroll */
1033
1034    if (LookAhead(w, event))
1035	return;
1036
1037    if (!event->xmotion.same_screen)
1038	return;
1039
1040    ExtractPosition(event, &x, &y, (unsigned int *)NULL);
1041
1042    top = FractionLoc(sbw, x, y);
1043
1044    if (old_mode != SMODE_CONT)		    /* start dragging: set offset */
1045    {
1046	if (event->xbutton.button == Button2)
1047	    sbw->scrollbar.scroll_off = sbw->scrollbar.shown / 2.;
1048	else
1049	    sbw->scrollbar.scroll_off = top - sbw->scrollbar.top;
1050    }
1051
1052    top -= sbw->scrollbar.scroll_off;
1053    if (sbw->scrollbar.limit_thumb)
1054	top = FloatInRange(top, 0.0,
1055			sbw->scrollbar.max - sbw->scrollbar.shown + 0.000001);
1056    else
1057	top = FloatInRange(top, 0.0, sbw->scrollbar.max);
1058
1059    sbw->scrollbar.top = top;
1060    PaintThumb(sbw);
1061    XFlush(XtDisplay(w));   /* re-draw it before Notifying */
1062}
1063
1064
1065    static void
1066NotifyThumb(w, event, params, num_params)
1067    Widget	w;
1068    XEvent	*event;
1069    String	*params UNUSED;
1070    Cardinal	*num_params UNUSED;
1071{
1072    ScrollbarWidget sbw = (ScrollbarWidget)w;
1073    /* Use a union to avoid a warning for the weird conversion from float to
1074     * XtPointer.  Comes from Xaw/Scrollbar.c. */
1075    union {
1076	XtPointer xtp;
1077	float xtf;
1078    } xtpf;
1079
1080    if (LookAhead(w, event))
1081	return;
1082
1083    /* thumbProc is not pretty, but is necessary for backwards
1084       compatibility on those architectures for which it work{s,ed};
1085       the intent is to pass a (truncated) float by value. */
1086    xtpf.xtf = sbw->scrollbar.top;
1087    XtCallCallbacks(w, XtNthumbProc, xtpf.xtp);
1088    XtCallCallbacks(w, XtNjumpProc, (XtPointer)&sbw->scrollbar.top);
1089}
1090
1091    static void
1092AllocTopShadowGC(w)
1093    Widget w;
1094{
1095    ScrollbarWidget sbw = (ScrollbarWidget) w;
1096    XtGCMask	    valuemask;
1097    XGCValues	    myXGCV;
1098
1099    valuemask = GCForeground;
1100    myXGCV.foreground = sbw->scrollbar.top_shadow_pixel;
1101    sbw->scrollbar.top_shadow_GC = XtGetGC(w, valuemask, &myXGCV);
1102}
1103
1104    static void
1105AllocBotShadowGC(w)
1106    Widget w;
1107{
1108    ScrollbarWidget sbw = (ScrollbarWidget) w;
1109    XtGCMask	    valuemask;
1110    XGCValues	    myXGCV;
1111
1112    valuemask = GCForeground;
1113    myXGCV.foreground = sbw->scrollbar.bot_shadow_pixel;
1114    sbw->scrollbar.bot_shadow_GC = XtGetGC(w, valuemask, &myXGCV);
1115}
1116
1117    static void
1118_Xaw3dDrawShadows(gw, event, region, out)
1119    Widget  gw;
1120    XEvent  *event UNUSED;
1121    Region  region;
1122    int	    out;
1123{
1124    XPoint  pt[6];
1125    ScrollbarWidget sbw = (ScrollbarWidget) gw;
1126    Dimension	s = sbw->scrollbar.shadow_width;
1127    /*
1128     * draw the shadows using the core part width and height,
1129     * and the scrollbar part shadow_width.
1130     *
1131     *	no point to do anything if the shadow_width is 0 or the
1132     *	widget has not been realized.
1133     */
1134    if (s > 0 && XtIsRealized(gw))
1135    {
1136	Dimension   h = sbw->core.height;
1137	Dimension   w = sbw->core.width;
1138	Dimension   wms = w - s;
1139	Dimension   hms = h - s;
1140	Display	    *dpy = XtDisplay (gw);
1141	Window	    win = XtWindow (gw);
1142	GC	top, bot;
1143
1144	if (out)
1145	{
1146	    top = sbw->scrollbar.top_shadow_GC;
1147	    bot = sbw->scrollbar.bot_shadow_GC;
1148	}
1149	else
1150	{
1151	    top = sbw->scrollbar.bot_shadow_GC;
1152	    bot = sbw->scrollbar.top_shadow_GC;
1153	}
1154
1155	/* top-left shadow */
1156	if ((region == NULL) ||
1157		(XRectInRegion (region, 0, 0, w, s) != RectangleOut) ||
1158		(XRectInRegion (region, 0, 0, s, h) != RectangleOut))
1159	{
1160	    pt[0].x = 0;    pt[0].y = h;
1161	    pt[1].x =	    pt[1].y = 0;
1162	    pt[2].x = w;    pt[2].y = 0;
1163	    pt[3].x = wms;  pt[3].y = s;
1164	    pt[4].x =	    pt[4].y = s;
1165	    pt[5].x = s;    pt[5].y = hms;
1166	    XFillPolygon (dpy, win, top, pt, 6, Complex, CoordModeOrigin);
1167	}
1168
1169	/* bottom-right shadow */
1170	if ((region == NULL) ||
1171		(XRectInRegion (region, 0, hms, w, s) != RectangleOut) ||
1172		(XRectInRegion (region, wms, 0, s, h) != RectangleOut))
1173	{
1174	    pt[0].x = 0;    pt[0].y = h;
1175	    pt[1].x = w;    pt[1].y = h;
1176	    pt[2].x = w;    pt[2].y = 0;
1177	    pt[3].x = wms;  pt[3].y = s;
1178	    pt[4].x = wms;  pt[4].y = hms;
1179	    pt[5].x = s;    pt[5].y = hms;
1180	    XFillPolygon (dpy, win, bot, pt, 6, Complex, CoordModeOrigin);
1181	}
1182    }
1183}
1184
1185
1186/*
1187 * Set the scroll bar to the given location.
1188 */
1189    void
1190vim_XawScrollbarSetThumb(w, top, shown, max)
1191    Widget w;
1192    double top, shown, max;
1193{
1194    ScrollbarWidget sbw = (ScrollbarWidget) w;
1195
1196    if (sbw->scrollbar.scroll_mode == SMODE_CONT) /* if still thumbing */
1197	return;
1198
1199    sbw->scrollbar.max = (max > 1.0) ? 1.0 :
1200		(max >= 0.0) ? max : sbw->scrollbar.max;
1201
1202    sbw->scrollbar.top = (top > sbw->scrollbar.max) ? sbw->scrollbar.max :
1203		(top >= 0.0) ? top : sbw->scrollbar.top;
1204
1205    sbw->scrollbar.shown = (shown > 1.0) ? 1.0 :
1206		(shown >= 0.0) ? shown : sbw->scrollbar.shown;
1207
1208    PaintThumb(sbw);
1209}
1210