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