xref: /vim-8.2.3635/src/gui_at_sb.c (revision 2bf24176)
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 
16 Copyright (c) 1987, 1988, 1994	X Consortium
17 
18 Permission is hereby granted, free of charge, to any person obtaining a copy
19 of this software and associated documentation files (the "Software"), to deal
20 in the Software without restriction, including without limitation the rights
21 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22 copies of the Software, and to permit persons to whom the Software is
23 furnished to do so, subject to the following conditions:
24 
25 The above copyright notice and this permission notice shall be included in all
26 copies or substantial portions of the Software.
27 
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE X
31 CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 
35 Except as contained in this notice, the name of the X Consortium shall not be
36 used in advertising or otherwise to promote the sale, use or other dealings in
37 this Software without prior written authorization from the X Consortium.
38 
39 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
40 
41 			All Rights Reserved
42 
43 Permission to use, copy, modify, and distribute this software and its
44 documentation for any purpose and without fee is hereby granted, provided that
45 the above copyright notice appear in all copies and that both that copyright
46 notice and this permission notice appear in supporting documentation, and that
47 the name of Digital not be used in advertising or publicity pertaining to
48 distribution of the software without specific, written prior permission.
49 
50 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
51 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL
52 BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
53 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
54 OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
55 CONNECTION 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 
74 static 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 
87 static float floatZero = 0.0;
88 
89 #define Offset(field) XtOffsetOf(ScrollbarRec, field)
90 
91 static 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 
128 static void ClassInitialize __ARGS((void));
129 static void Initialize __ARGS((Widget, Widget, ArgList, Cardinal *));
130 static void Destroy __ARGS((Widget));
131 static void Realize __ARGS((Widget, Mask *, XSetWindowAttributes *));
132 static void Resize __ARGS((Widget));
133 static void Redisplay __ARGS((Widget, XEvent *, Region));
134 static Boolean SetValues __ARGS((Widget, Widget, Widget, ArgList, Cardinal *));
135 
136 static void HandleThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
137 static void MoveThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
138 static void NotifyThumb __ARGS((Widget, XEvent *, String *, Cardinal *));
139 static void NotifyScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
140 static void EndScroll __ARGS((Widget, XEvent *, String *, Cardinal *));
141 static void ScrollOneLineUp __ARGS((Widget, XEvent *, String *, Cardinal *));
142 static void ScrollOneLineDown __ARGS((Widget, XEvent *, String *, Cardinal *));
143 static void ScrollPageUp __ARGS((Widget, XEvent *, String *, Cardinal *));
144 static void ScrollPageDown __ARGS((Widget, XEvent *, String *, Cardinal *));
145 static void ScrollSome __ARGS((Widget w, XEvent *event, int call_data));
146 static void _Xaw3dDrawShadows __ARGS((Widget, XEvent *, Region, int));
147 static void AllocTopShadowGC __ARGS((Widget));
148 static void AllocBotShadowGC __ARGS((Widget));
149 
150 static 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 
164 ScrollbarClassRec 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 
211 WidgetClass 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
225 ClassInitialize()
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
235 FillArea(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
343 PaintThumb(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
377 PaintArrows(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
457 Destroy(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
469 CreateGC(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
508 SetDimensions(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
524 Initialize(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
552 Realize(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
563 SetValues(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
604 Resize(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
615 Redisplay(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
653 CompareEvents(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 
689 struct EventData
690 {
691     XEvent *oldEvent;
692     int count;
693 };
694 
695     static Bool
696 PeekNotifyEvent(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
709 LookAhead(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
729 ExtractPosition(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
771 HandleThumb(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
794 RepeatNotify(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
836 FloatInRange(num, small, big)
837     float num, small, big;
838 {
839     return (num < small) ? small : ((num > big) ? big : num);
840 }
841 
842     static void
843 ScrollOneLineUp(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
853 ScrollOneLineDown(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
863 ScrollPageDown(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
873 ScrollPageUp(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
883 ScrollSome(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
901 NotifyScroll(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
990 EndScroll(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
1005 FractionLoc(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
1021 MoveThumb(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
1066 NotifyThumb(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
1092 AllocTopShadowGC(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
1105 AllocBotShadowGC(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
1190 vim_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