(A-compU+758B): New abstract node.
[chise/xemacs-chise.git.1] / lwlib / xlwscrollbar.c
1 /* Implements a lightweight scrollbar widget.
2    Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3    Copyright (C) 1997 Sun Microsystems, Inc.
4
5 This file is part of the Lucid Widget Library.
6
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Created by Douglas Keller <dkeller@vnet.ibm.com> */
23 /* Lots of hacking by Martin Buchholz */
24
25 /*
26  * Athena-style scrollbar button bindings added on Sun Dec 24 22:03:57 1995
27  * by Jonathan Stigelman <Stig@hackvan.com>...   Ho ho ho!
28  *
29  * To use them, put this resource in your .Xdefaults
30  *
31  * Emacs*XlwScrollBar.translations: #override \n\
32  *   <Btn1Down>:     PageDownOrRight()    \n\
33  *   <Btn3Down>:     PageUpOrLeft()     \n\
34  *   <Btn3Up>:  Release()
35  *
36  */
37
38 /*
39  * Resources Supported:
40  *     XmNforeground
41  *     XmNbackground
42  *     XmNtopShadowColor
43  *     XmNtopShadowPixmap
44  *     XmNbottomShadowColor
45  *     XmNbottomShadowPixmap
46  *     XmNtroughColor
47  *     XmNshadowThickness
48  *     XmNshowArrows
49  *     XmNorientation
50  *     XmNborderWidth
51  *
52  *     XmNminimum
53  *     XmNmaximum
54  *     XmNvalue
55  *     XmNincrement
56  *     XmNpageIncrement
57  *
58  *     XmNvalueChangedCallback
59  *     XmNincrementCallback
60  *     XmNdecrementCallback
61  *     XmNpageIncrementCallback
62  *     XmNpageDecrementCallback
63  *     XmNtoTopCallback
64  *     XmNtoBottomCallback
65  *     XmNdragCallback
66  *
67  *     XmNsliderStyle    - values can be: "plain" or "dimple"
68  *     XmNarrowPosition  - values can be: "opposite" or "same"
69  *
70  */
71
72 #include <config.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <limits.h>
76
77 #include <X11/IntrinsicP.h>
78 #include <X11/StringDefs.h>
79 #include <X11/bitmaps/gray>
80
81 #include "xlwscrollbarP.h"
82 #include "xlwscrollbar.h"
83
84 #ifdef USE_DEBUG_MALLOC
85 #include <dmalloc.h>
86 #endif
87
88 #define DBUG(x)
89
90 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
91                   ? ((unsigned long) (x)) : ((unsigned long) (y)))
92
93 #define VERT(w) ((w)->sb.orientation == XmVERTICAL)
94
95 #define SS_MIN 8
96
97 typedef enum
98 {
99   BUTTON_NONE,
100   BUTTON_SLIDER,
101   BUTTON_UP_ARROW,
102   BUTTON_DOWN_ARROW,
103   BUTTON_TROUGH_ABOVE,
104   BUTTON_TROUGH_BELOW
105 } button_where;
106
107 typedef enum
108 {
109   SLIDER_PLAIN,
110   SLIDER_DIMPLE
111 } SliderStyle;
112
113 /*-------------------------- Resources ----------------------------------*/
114 #define offset(field) XtOffset(XlwScrollBarWidget, field)
115
116 static XtResource resources[] = {
117     { XmNforeground, XmCForeground, XtRPixel, sizeof(Pixel),
118       offset(sb.foreground), XtRImmediate, (XtPointer) XtDefaultForeground },
119
120     { XmNtopShadowColor, XmCTopShadowColor, XtRPixel,
121       sizeof(Pixel), offset(sb.topShadowColor), XtRImmediate, (XtPointer) ~0 },
122     { XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel,
123       sizeof(Pixel), offset(sb.bottomShadowColor), XtRImmediate,
124       (XtPointer)~0 },
125
126     { XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap,
127       sizeof (Pixmap), offset(sb.topShadowPixmap), XtRImmediate,
128       (XtPointer)None},
129     { XmNbottomShadowPixmap, XmCBottomShadowPixmap,
130       XtRPixmap, sizeof (Pixmap), offset(sb.bottomShadowPixmap),
131       XtRImmediate, (XtPointer)None},
132
133     { XmNtroughColor, XmCTroughColor, XtRPixel, sizeof(Pixel),
134       offset(sb.troughColor), XtRImmediate, (XtPointer)~0 },
135
136     { XmNshadowThickness, XmCShadowThickness, XtRInt,
137       sizeof(int), offset(sb.shadowThickness), XtRImmediate, (XtPointer)2 },
138
139     { XmNborderWidth, XmCBorderWidth, XtRDimension,
140       sizeof(Dimension), offset(core.border_width), XtRImmediate,
141       (XtPointer)0 },
142
143     { XmNshowArrows, XmCShowArrows, XtRBoolean,
144       sizeof(Boolean), offset(sb.showArrows), XtRImmediate, (XtPointer)True },
145
146     { XmNinitialDelay, XmCInitialDelay, XtRInt, sizeof(int),
147       offset(sb.initialDelay), XtRImmediate, (XtPointer) 250 },
148     { XmNrepeatDelay, XmCRepeatDelay, XtRInt, sizeof(int),
149       offset(sb.repeatDelay), XtRImmediate, (XtPointer) 50 },
150
151     { XmNorientation, XmCOrientation, XtROrientation,
152       sizeof(unsigned char), offset(sb.orientation), XtRImmediate,
153       (XtPointer) XmVERTICAL },
154
155     { XmNminimum, XmCMinimum, XtRInt, sizeof(int),
156       offset(sb.minimum), XtRImmediate, (XtPointer) 0},
157     { XmNmaximum, XmCMaximum, XtRInt, sizeof(int),
158       offset(sb.maximum), XtRImmediate, (XtPointer) 100},
159     { XmNvalue, XmCValue, XtRInt, sizeof(int),
160       offset(sb.value), XtRImmediate, (XtPointer) 0},
161     { XmNsliderSize, XmCSliderSize, XtRInt, sizeof(int),
162       offset(sb.sliderSize), XtRImmediate, (XtPointer) 10},
163     { XmNincrement, XmCIncrement, XtRInt, sizeof(int),
164       offset(sb.increment), XtRImmediate, (XtPointer) 1},
165     { XmNpageIncrement, XmCPageIncrement, XtRInt, sizeof(int),
166       offset(sb.pageIncrement), XtRImmediate, (XtPointer) 10},
167
168     { XmNvalueChangedCallback, XmCValueChangedCallback,
169       XtRCallback, sizeof(XtPointer), offset(sb.valueChangedCBL),
170       XtRCallback, NULL},
171     { XmNincrementCallback, XmCIncrementCallback,
172       XtRCallback, sizeof(XtPointer), offset(sb.incrementCBL),
173       XtRCallback, NULL},
174     { XmNdecrementCallback, XmCDecrementCallback,
175       XtRCallback, sizeof(XtPointer), offset(sb.decrementCBL),
176       XtRCallback, NULL},
177     { XmNpageIncrementCallback, XmCPageIncrementCallback,
178       XtRCallback, sizeof(XtPointer), offset(sb.pageIncrementCBL),
179       XtRCallback, NULL},
180     { XmNpageDecrementCallback, XmCPageDecrementCallback,
181       XtRCallback, sizeof(XtPointer), offset(sb.pageDecrementCBL),
182       XtRCallback, NULL},
183     { XmNtoTopCallback, XmCToTopCallback, XtRCallback,
184       sizeof(XtPointer), offset(sb.toTopCBL), XtRCallback, NULL},
185     { XmNtoBottomCallback, XmCToBottomCallback, XtRCallback,
186       sizeof(XtPointer), offset(sb.toBottomCBL), XtRCallback, NULL},
187     { XmNdragCallback, XmCDragCallback, XtRCallback,
188       sizeof(XtPointer), offset(sb.dragCBL), XtRCallback, NULL},
189
190       /* "knob" is obsolete; use "slider" instead. */
191     { XmNsliderStyle, XmCSliderStyle, XtRString, sizeof(char *),
192       offset(sb.sliderStyle), XtRImmediate, NULL},
193     { XmNknobStyle, XmCKnobStyle, XtRString, sizeof(char *),
194        offset(sb.knobStyle), XtRImmediate, NULL},
195
196     { XmNarrowPosition, XmCArrowPosition, XtRString, sizeof(char *),
197       offset(sb.arrowPosition), XtRImmediate, NULL},
198 };
199
200 /*-------------------------- Prototypes ---------------------------------*/
201
202 /* Actions */
203 typedef void Action(Widget w, XEvent *event, String *parms, Cardinal *num_parms);
204 static Action Select, PageUpOrLeft, PageDownOrRight, Drag, Release, Jump, Abort;
205
206 /* Methods */
207 static void Initialize(Widget treq, Widget tnew, ArgList args, Cardinal *num_args);
208 static Boolean SetValues(Widget current, Widget request, Widget nw, ArgList args, Cardinal *num_args);
209 static void Destroy(Widget widget);
210 static void Redisplay(Widget widget, XEvent *event, Region region);
211 static void Resize(Widget widget);
212 static void Realize(Widget widget, XtValueMask *valuemask, XSetWindowAttributes *attr);
213
214 /* Private */
215
216 /*-------------------------- Actions Table ------------------------------*/
217 static XtActionsRec actions[] =
218 {
219   {"Select",            Select},
220   {"PageDownOrRight",   PageDownOrRight},
221   {"PageUpOrLeft",      PageUpOrLeft},
222   {"Drag",              Drag},
223   {"Release",           Release},
224   {"Jump",              Jump},
225   {"Abort",             Abort},
226 };
227
228 /*--------------------- Default Translation Table -----------------------*/
229 static char default_translations[] =
230   "<Btn1Down>:    Select()\n"
231   "<Btn1Motion>:  Drag()\n"
232   "<Btn1Up>:      Release()\n"
233   "<Btn2Down>:    Jump()\n"
234   "<Btn2Motion>:  Drag()\n"
235   "<Btn2Up>:      Release()\n"
236   "<Key>Delete:   Abort()"
237 ;
238
239 /*------------------- Class record initialization -----------------------*/
240 XlwScrollBarClassRec xlwScrollBarClassRec = {
241     /* core_class fields */
242     {
243     /* superclass          */ (WidgetClass) &coreClassRec,
244     /* class_name          */ "XlwScrollBar",
245     /* widget_size         */ sizeof(XlwScrollBarRec),
246     /* class_initialize    */ NULL,
247     /* class_part_init     */ NULL,
248     /* class_inited        */ False,
249     /* initialize          */ Initialize,
250     /* initialize_hook     */ NULL,
251     /* realize             */ Realize,
252     /* actions             */ actions,
253     /* num_actions         */ XtNumber(actions),
254     /* resources           */ resources,
255     /* num_resources       */ XtNumber(resources),
256     /* xrm_class           */ NULLQUARK,
257     /* compress_motion     */ True,
258     /* compress_exposure   */ XtExposeCompressMultiple,
259     /* compress_enterleave */ True,
260     /* visible_interest    */ False,
261     /* destroy             */ Destroy,
262     /* resize              */ Resize,
263     /* expose              */ Redisplay,
264     /* set_values          */ SetValues,
265     /* set_values_hook     */ NULL,
266     /* set_values_almost   */ XtInheritSetValuesAlmost,
267     /* get_values_hook     */ NULL,
268     /* accept_focus        */ NULL,
269     /* version             */ XtVersionDontCheck,
270     /* callback_private    */ NULL,
271     /* tm_table            */ default_translations,
272     /* query_geometry      */ NULL,
273     },
274     /* scrollbar_class fields */
275     {
276         /* dummy_field         */ 0,
277     },
278 };
279
280 WidgetClass xlwScrollBarWidgetClass = (WidgetClass) &xlwScrollBarClassRec;
281
282 /*-------------------------- Debug Functions ----------------------------*/
283
284 #ifdef SHOW_CLEAR
285 static void
286 myXClearArea(Display *dpy, Drawable d, int x, int y, int w, int h,
287              Boolean exp, XlwScrollBarWidget widget)
288 {
289   XFillRectangle (dpy, d, widget->sb.topShadowGC, x, y, w, h);
290   XSync (dpy, False);
291   sleep (2);
292   XClearArea (dpy, d, x, y, w, h, exp);
293 }
294
295 #define XClearArea(dpy,win,x,y,width,height,exp) myXClearArea(dpy,win,x,y,width,height,exp,w)
296 #endif
297
298 #ifdef CHECK_VALUES
299 static void
300 check(XlwScrollBarWidget w)
301 {
302   int height = widget_h (w);
303   if (w->sb.showArrows)
304     height -= (2 * arrow_h (w));
305
306   if ((w->sb.above + w->sb.ss + w->sb.below > height) ||
307       (w->sb.value < w->sb.minimum) ||
308       (w->sb.value > w->sb.maximum - w->sb.sliderSize))
309     {
310       printf("above=%d ss=%d below=%d height=%d\n",
311              w->sb.above, w->sb.ss, w->sb.below, height);
312       printf("value=%d min=%d max=%d ss=%d max-ss=%d\n",
313              w->sb.value, w->sb.minimum, w->sb.maximum,
314              w->sb.sliderSize, w->sb.maximum - w->sb.sliderSize);
315       abort();
316     }
317 }
318
319 #  define CHECK(w) check(w)
320 #else
321 #  define CHECK(w)
322 #endif
323
324 /*-------------------------- Static functions ---------------------------*/
325
326 static void
327 call_callbacks (XlwScrollBarWidget w, int reason,
328                 int value, int pixel, XEvent *event)
329 {
330   XlwScrollBarCallbackStruct cbs;
331   Boolean called_anything;
332
333   cbs.reason = reason;
334   cbs.event  = event;
335   cbs.value  = value;
336   cbs.pixel  = pixel;
337
338   called_anything = False;
339
340   switch (reason)
341     {
342     case XmCR_VALUE_CHANGED:
343       XtCallCallbackList ((Widget) w, w->sb.valueChangedCBL, &cbs);
344       called_anything = True;
345       break;
346     case XmCR_INCREMENT:
347       if (w->sb.incrementCBL)
348         {
349           XtCallCallbackList ((Widget) w, w->sb.incrementCBL, &cbs);
350           called_anything = True;
351         }
352       break;
353     case XmCR_DECREMENT:
354       if (w->sb.decrementCBL)
355         {
356           XtCallCallbackList ((Widget) w, w->sb.decrementCBL, &cbs);
357           called_anything = True;
358         }
359       break;
360     case XmCR_PAGE_INCREMENT:
361       if (w->sb.incrementCBL)
362         {
363           XtCallCallbackList ((Widget) w, w->sb.pageIncrementCBL, &cbs);
364           called_anything = True;
365         }
366       break;
367     case XmCR_PAGE_DECREMENT:
368       if (w->sb.decrementCBL)
369         {
370           XtCallCallbackList ((Widget) w, w->sb.pageDecrementCBL, &cbs);
371           called_anything = True;
372         }
373       break;
374     case XmCR_TO_TOP:
375       if (w->sb.toTopCBL)
376         {
377           XtCallCallbackList ((Widget) w, w->sb.toTopCBL, &cbs);
378           called_anything = True;
379         }
380       break;
381     case XmCR_TO_BOTTOM:
382       if (w->sb.toBottomCBL)
383         {
384           XtCallCallbackList ((Widget) w, w->sb.toBottomCBL, &cbs);
385           called_anything = True;
386         }
387       break;
388     case XmCR_DRAG:
389       if (w->sb.dragCBL)
390         {
391           XtCallCallbackList ((Widget) w, w->sb.dragCBL, &cbs);
392         }
393       called_anything = True; /* Special Case */
394       break;
395     }
396
397   if (!called_anything)
398     {
399       cbs.reason = XmCR_VALUE_CHANGED;
400       XtCallCallbackList ((Widget) w, w->sb.valueChangedCBL, &cbs);
401     }
402 }
403
404 /* Widget sizes minus the shadow and highlight area */
405
406 static int
407 widget_x (XlwScrollBarWidget w)
408 {
409   return w->sb.shadowThickness;
410 }
411
412 static int
413 widget_y (XlwScrollBarWidget w)
414 {
415   return w->sb.shadowThickness;
416 }
417
418 static int
419 widget_w (XlwScrollBarWidget w)
420 {
421   int x = w->sb.shadowThickness;
422   int width = (VERT (w) ? w->core.width : w->core.height) - (2 * x);
423   return width > 1 ? width : 1;
424 }
425
426 static int
427 widget_h (XlwScrollBarWidget w)
428 {
429   int y = w->sb.shadowThickness;
430   int height = (VERT (w) ? w->core.height : w->core.width) - (2 * y);
431
432   return height > 1 ? height : 1;
433 }
434
435 static int
436 arrow_h (XlwScrollBarWidget w)
437 {
438   int width = widget_w (w);
439   int minimum_size = ((widget_h (w) - SS_MIN) / 2) - 1;
440   return minimum_size < width ? minimum_size : width;
441 }
442
443 static int
444 event_x (XlwScrollBarWidget w, XEvent *event)
445 {
446   return VERT (w) ? event->xbutton.x : event->xbutton.y;
447 }
448
449 static int
450 event_y (XlwScrollBarWidget w, XEvent *event)
451 {
452   return VERT (w) ? event->xbutton.y : event->xbutton.x;
453 }
454
455 /* Safe addition and subtraction */
456 static void
457 increment_value (XlwScrollBarWidget w, int diff)
458 {
459   w->sb.value = w->sb.maximum - diff < w->sb.value ?
460     w->sb.maximum :
461     w->sb.value + diff;
462 }
463
464 static void
465 decrement_value (XlwScrollBarWidget w, int diff)
466 {
467   w->sb.value = w->sb.minimum + diff > w->sb.value ?
468     w->sb.minimum :
469     w->sb.value - diff;
470 }
471
472 static SliderStyle
473 slider_style (XlwScrollBarWidget w)
474 {
475   return (w->sb.sliderStyle ? w->sb.sliderStyle[0] == 'd' :
476           w->sb.knobStyle   ? w->sb.knobStyle[0]   == 'd' :
477           0) ?
478     SLIDER_DIMPLE :
479     SLIDER_PLAIN;
480 }
481
482 static Boolean
483 arrow_same_end (XlwScrollBarWidget w)
484 {
485   return w->sb.arrowPosition && w->sb.arrowPosition[0] == 's' ? True : False;
486 }
487
488 /*-------------------------- GC and Pixel allocation --------------------*/
489 #ifndef XmUNSPECIFIED_PIXMAP
490 #define XmUNSPECIFIED_PIXMAP 2
491 #endif
492
493 static GC
494 get_gc (XlwScrollBarWidget w, Pixel fg, Pixel bg, Pixmap pm)
495 {
496   XGCValues values;
497   XtGCMask mask;
498
499   if (pm == w->sb.grayPixmap)
500     {
501       /* If we're using the gray pixmap, guarantee white on black ...
502        * otherwise, we could end up with something odd like grey on white
503        * when we're on a color display that ran out of color cells
504        */
505
506       fg = WhitePixelOfScreen (DefaultScreenOfDisplay (XtDisplay (w)));
507       bg = BlackPixelOfScreen (DefaultScreenOfDisplay (XtDisplay (w)));
508     }
509
510   values.foreground = fg;
511   values.background = bg;
512   values.fill_style = FillOpaqueStippled;
513   values.stipple    = pm;
514 /*  mask = GCForeground | GCBackground |
515     (pm == None ? 0 : GCStipple | GCFillStyle); gtb */
516   if (pm != None && pm != 0 && pm != XmUNSPECIFIED_PIXMAP)
517      values.stipple = pm;
518   else
519      values.stipple = None;
520   mask = GCForeground | GCBackground |
521    (values.stipple == None ? 0 : GCStipple | GCFillStyle);
522
523   return XtGetGC((Widget) w, mask, &values);
524 }
525
526 /* Replacement for XAllocColor() that tries to return the nearest
527    available color if the colormap is full.  From FSF Emacs. */
528
529 static int
530 allocate_nearest_color (Display *display, Colormap screen_colormap,
531                         XColor *color_def)
532 {
533   int status = XAllocColor (display, screen_colormap, color_def);
534   if (status)
535     return status;
536
537     {
538       /* If we got to this point, the colormap is full, so we're
539          going to try to get the next closest color.
540          The algorithm used is a least-squares matching, which is
541          what X uses for closest color matching with StaticColor visuals.  */
542
543       int nearest, x;
544       unsigned long nearest_delta = ULONG_MAX;
545
546       int no_cells = XDisplayCells (display, XDefaultScreen (display));
547       /* Don't use alloca here because lwlib doesn't have the
548          necessary configuration information that src does. */
549       XColor *cells = (XColor *) malloc (sizeof (XColor) * no_cells);
550
551       for (x = 0; x < no_cells; x++)
552         cells[x].pixel = x;
553
554       XQueryColors (display, screen_colormap, cells, no_cells);
555
556       for (nearest = 0, x = 0; x < no_cells; x++)
557         {
558           long dred   = (color_def->red   >> 8) - (cells[x].red   >> 8);
559           long dgreen = (color_def->green >> 8) - (cells[x].green >> 8);
560           long dblue  = (color_def->blue  >> 8) - (cells[x].blue  >> 8);
561           unsigned long delta = dred * dred + dgreen * dgreen + dblue * dblue;
562
563           if (delta < nearest_delta)
564             {
565               nearest = x;
566               nearest_delta = delta;
567             }
568         }
569       color_def->red   = cells[nearest].red;
570       color_def->green = cells[nearest].green;
571       color_def->blue  = cells[nearest].blue;
572       free (cells);
573       return XAllocColor (display, screen_colormap, color_def);
574     }
575 }
576
577 static void
578 make_shadow_pixels (XlwScrollBarWidget w)
579 {
580   Display *dpy = XtDisplay((Widget) w);
581   Colormap cmap = w->core.colormap;
582   XColor topc, botc;
583   int top_frobbed, bottom_frobbed;
584   Pixel bg, fg;
585
586   top_frobbed = bottom_frobbed = 0;
587
588   bg = w->core.background_pixel;
589   fg = w->sb.foreground;
590
591   if (w->sb.topShadowColor    == (Pixel)~0) w->sb.topShadowColor    = bg;
592   if (w->sb.bottomShadowColor == (Pixel)~0) w->sb.bottomShadowColor = fg;
593
594   if (w->sb.topShadowColor == bg || w->sb.topShadowColor == fg)
595     {
596       topc.pixel = bg;
597       XQueryColor (dpy, cmap, &topc);
598       /* don't overflow/wrap! */
599       topc.red   = MINL(65535, topc.red   * 1.2);
600       topc.green = MINL(65535, topc.green * 1.2);
601       topc.blue  = MINL(65535, topc.blue  * 1.2);
602       if (allocate_nearest_color (dpy, cmap, &topc))
603         {
604           if (topc.pixel == bg)
605             {
606               XFreeColors (dpy, cmap, &topc.pixel, 1, 0);
607               topc.red   = MINL(65535, topc.red   + 0x8000);
608               topc.green = MINL(65535, topc.green + 0x8000);
609               topc.blue  = MINL(65535, topc.blue  + 0x8000);
610               if (allocate_nearest_color (dpy, cmap, &topc))
611                 {
612                   w->sb.topShadowColor = topc.pixel;
613                 }
614             }
615           else
616             {
617               w->sb.topShadowColor = topc.pixel;
618             }
619
620           top_frobbed = 1;
621         }
622     }
623
624   if (w->sb.bottomShadowColor == fg || w->sb.bottomShadowColor == bg)
625     {
626       botc.pixel = bg;
627       XQueryColor (dpy, cmap, &botc);
628       botc.red   = (botc.red   * 3) / 5;
629       botc.green = (botc.green * 3) / 5;
630       botc.blue  = (botc.blue  * 3) / 5;
631       if (allocate_nearest_color (dpy, cmap, &botc))
632         {
633           if (botc.pixel == bg)
634             {
635               XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
636               botc.red   = MINL(65535, botc.red   + 0x4000);
637               botc.green = MINL(65535, botc.green + 0x4000);
638               botc.blue  = MINL(65535, botc.blue  + 0x4000);
639               if (allocate_nearest_color (dpy, cmap, &botc))
640                 {
641                   w->sb.bottomShadowColor = botc.pixel;
642                 }
643             }
644           else
645             {
646               w->sb.bottomShadowColor = botc.pixel;
647             }
648           bottom_frobbed = 1;
649         }
650     }
651
652   if (top_frobbed && bottom_frobbed)
653     {
654       int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
655       int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
656       if (bot_avg > top_avg)
657         {
658           Pixel tmp = w->sb.topShadowColor;
659           w->sb.topShadowColor = w->sb.bottomShadowColor;
660           w->sb.bottomShadowColor = tmp;
661         }
662       else if (topc.pixel == botc.pixel)
663         {
664           if (botc.pixel == bg)
665             w->sb.topShadowColor = bg;
666           else
667             w->sb.bottomShadowColor = fg;
668         }
669     }
670
671   if (w->sb.topShadowColor    == w->core.background_pixel ||
672       w->sb.bottomShadowColor == w->core.background_pixel)
673     {
674       /* Assume we're in mono. This code should be okay even if we're
675        * really in color but just short on color cells -- We want the
676        * following behavior, which has been empirically determined to
677        * work well for all fg/bg combinations in mono: If the trough
678        * and slider are BOTH black, then use a white top shadow and a
679        * grey bottom shadow, otherwise use a grey top shadow and a
680        * black bottom shadow.
681        */
682
683       Pixel white = WhitePixelOfScreen (DefaultScreenOfDisplay (XtDisplay (w)));
684       Pixel black = BlackPixelOfScreen (DefaultScreenOfDisplay (XtDisplay (w)));
685
686       /* Note: core.background_pixel is the color of the slider ... */
687
688       if (w->core.background_pixel == black &&
689           w->sb.troughColor == black)
690         {
691           w->sb.topShadowColor = white;
692           w->sb.bottomShadowPixmap = w->sb.grayPixmap;
693         } else {
694           w->sb.topShadowPixmap = w->sb.grayPixmap;
695           w->sb.bottomShadowColor = black;
696         }
697     }
698 }
699
700 static void
701 make_trough_pixel (XlwScrollBarWidget w)
702 {
703   Display *dpy = XtDisplay((Widget) w);
704   Colormap cmap = w->core.colormap;
705   XColor troughC;
706
707   if (w->sb.troughColor == (Pixel)~0) w->sb.troughColor = w->core.background_pixel;
708
709   if (w->sb.troughColor == w->core.background_pixel)
710     {
711       troughC.pixel = w->core.background_pixel;
712       XQueryColor (dpy, cmap, &troughC);
713       troughC.red   = (troughC.red   * 4) / 5;
714       troughC.green = (troughC.green * 4) / 5;
715       troughC.blue  = (troughC.blue  * 4) / 5;
716       if (allocate_nearest_color (dpy, cmap, &troughC))
717         w->sb.troughColor = troughC.pixel;
718     }
719 }
720
721 /*-------------------------- Draw 3D Border -----------------------------*/
722 static void
723 draw_shadows (Display *dpy, Drawable d, GC shine_gc, GC shadow_gc,
724               int x, int y, int width, int height, int shadowT)
725 {
726   XSegment shine[10], shadow[10];
727   int i;
728
729   if (shadowT > (width  / 2)) shadowT = (width  / 2);
730   if (shadowT > (height / 2)) shadowT = (height / 2);
731   if (shadowT <= 0) return;
732
733   for (i = 0; i < shadowT; i++)
734     {
735       /*  Top segments  */
736       shine[i].x1 = x;
737       shine[i].y2 = shine[i].y1 = y + i;
738       shine[i].x2 = x + width - i - 1;
739       /*  Left segments  */
740       shine[i + shadowT].x2 = shine[i + shadowT].x1 = x + i;
741       shine[i + shadowT].y1 = y + shadowT;
742       shine[i + shadowT].y2 = y + height - i - 1;
743
744       /*  Bottom segments  */
745       shadow[i].x1 = x + i;
746       shadow[i].y2 = shadow[i].y1 = y + height - i - 1;
747       shadow[i].x2 = x + width - 1 ;
748       /*  Right segments  */
749       shadow[i + shadowT].x2 = shadow[i + shadowT].x1 = x + width - i - 1;
750       shadow[i + shadowT].y1 = y + i + 1;
751       shadow[i + shadowT].y2 = y + height - 1 ;
752     }
753
754   XDrawSegments (dpy, d, shine_gc,  shine,  shadowT * 2);
755   XDrawSegments (dpy, d, shadow_gc, shadow, shadowT * 2);
756 }
757
758 /*------------------ Draw 3D Arrows: left, up, down, right --------------*/
759 static int
760 make_vert_seg (XSegment *seg, int x1, int y1, int x2, int y2, int shadowT)
761 {
762   int i;
763
764   for (i=0; i<shadowT; i++, seg++)
765     {
766       seg->x1 = x1;
767       seg->y1 = y1++;
768       seg->x2 = x2;
769       seg->y2 = y2++;
770     }
771   return shadowT;
772 }
773
774 static int
775 make_hor_seg (XSegment *seg, int x1, int y1, int x2, int y2, int shadowT)
776 {
777   int i;
778
779   for (i=0; i<shadowT; i++, seg++)
780     {
781       seg->x1 = x1++;
782       seg->y1 = y1;
783       seg->x2 = x2++;
784       seg->y2 = y2;
785     }
786   return shadowT;
787 }
788
789 static void
790 draw_arrow_up (Display *dpy, Drawable win, GC bgGC, GC shineGC, GC shadowGC,
791                int x, int y, int width, int height, int shadowT)
792 {
793   XSegment shine[10], shadow[10];
794   XPoint triangle[3];
795   int mid;
796
797   mid = width / 2;
798
799   if (shadowT > (width  / 2)) shadowT = (width  / 2);
800   if (shadowT > (height / 2)) shadowT = (height / 2);
801   if (shadowT < 0) shadowT = 0;
802
803   /*  /  */
804   make_vert_seg (shine,
805                  x, y + height - shadowT - 1,
806                  x + mid, y, shadowT);
807   /*  _\  */
808   make_vert_seg (shadow,
809                  x, y + height - shadowT - 1,
810                  x + width - 1, y + height - shadowT - 1, shadowT);
811   make_vert_seg (shadow + shadowT,
812                  x + mid, y,
813                  x + width - 1, y + height - shadowT - 1, shadowT);
814
815   triangle[0].x = x;
816   triangle[0].y = y + height - 1;
817   triangle[1].x = x + mid;
818   triangle[1].y = y;
819   triangle[2].x = x + width - 1;
820   triangle[2].y = y + height - 1;
821
822   XFillPolygon (dpy, win, bgGC, triangle, 3, Convex, ArcChord);
823
824   XDrawSegments (dpy, win, shadowGC, shadow, shadowT * 2);
825   XDrawSegments (dpy, win, shineGC,  shine,  shadowT);
826 }
827
828 static void
829 draw_arrow_left (Display *dpy, Drawable win, GC bgGC, GC shineGC, GC shadowGC,
830                  int x, int y, int width, int height, int shadowT)
831 {
832   XSegment shine[10], shadow[10];
833   XPoint triangle[3];
834
835   int mid = width / 2;
836
837   if (shadowT > (width  / 2)) shadowT = (width  / 2);
838   if (shadowT > (height / 2)) shadowT = (height / 2);
839   if (shadowT < 0) shadowT = 0;
840
841   /*  /  */
842   make_hor_seg (shine,
843                 x, y + mid,
844                 x + width - shadowT - 1, y, shadowT);
845   /*  \|  */
846   make_hor_seg (shadow,
847                 x, y + mid,
848                 x + width - shadowT - 1, y + height - 1, shadowT);
849   make_hor_seg (shadow + shadowT,
850                 x + width - shadowT - 1, y,
851                 x + width - shadowT - 1, y + height - 1, shadowT);
852
853   triangle[0].x = x + width - 1;
854   triangle[0].y = y + height - 1;
855   triangle[1].x = x;
856   triangle[1].y = y + mid;
857   triangle[2].x = x + width - 1;
858   triangle[2].y = y;
859
860   XFillPolygon (dpy, win, bgGC, triangle, 3, Convex, ArcChord);
861
862   XDrawSegments (dpy, win, shadowGC, shadow, shadowT * 2);
863   XDrawSegments (dpy, win, shineGC,  shine,  shadowT);
864 }
865
866 static void
867 draw_arrow_down (Display *dpy, Drawable win, GC bgGC, GC shineGC, GC shadowGC,
868                  int x, int y, int width, int height, int shadowT)
869 {
870   XSegment shine[10], shadow[10];
871   XPoint triangle[3];
872   int mid;
873
874   mid = width / 2;
875
876   if (shadowT > (width  / 2)) shadowT = (width  / 2);
877   if (shadowT > (height / 2)) shadowT = (height / 2);
878   if (shadowT < 0) shadowT = 0;
879
880   /*  \-  */
881   make_vert_seg (shine,
882                  x, y,
883                  x + mid, y + height - shadowT - 1, shadowT);
884   make_vert_seg (shine + shadowT,
885                  x, y,
886                  x + width - 1, y, shadowT);
887   /*  /  */
888   make_vert_seg (shadow,
889                  x + width - 1, y,
890                  x + mid, y + height - shadowT - 1, shadowT);
891
892   triangle[0].x = x;
893   triangle[0].y = y;
894   triangle[1].x = x + mid;
895   triangle[1].y = y + height - 1;
896   triangle[2].x = x + width - 1;
897   triangle[2].y = y;
898
899   XFillPolygon (dpy, win, bgGC, triangle, 3, Convex, ArcChord);
900
901   XDrawSegments (dpy, win, shadowGC, shadow, shadowT);
902   XDrawSegments (dpy, win, shineGC,  shine,  shadowT * 2);
903 }
904
905 static void
906 draw_arrow_right (Display *dpy, Drawable win, GC bgGC, GC shineGC, GC shadowGC,
907                   int x, int y, int width, int height, int shadowT)
908 {
909   XSegment shine[10], shadow[10];
910   XPoint triangle[3];
911   int mid;
912
913   mid = width / 2;
914
915   if (shadowT > (width  / 2)) shadowT = (width  / 2);
916   if (shadowT > (height / 2)) shadowT = (height / 2);
917   if (shadowT < 0) shadowT = 0;
918
919   /*  |\  */
920   make_hor_seg (shine,
921                 x, y,
922                 x + width - shadowT - 1, y + mid, shadowT);
923   make_hor_seg (shine + shadowT,
924                 x, y,
925                 x, y + height - 1, shadowT);
926   /*  /  */
927   make_hor_seg (shadow,
928                 x, y + height - 1,
929                 x + width - shadowT - 1, y + mid, shadowT);
930
931   triangle[0].x = x + 1;
932   triangle[0].y = y + height - 1;
933   triangle[1].x = x + width  - 1;
934   triangle[1].y = y + mid;
935   triangle[2].x = x + 1;
936   triangle[2].y = y;
937
938   XFillPolygon (dpy, win, bgGC, triangle, 3, Convex, ArcChord);
939
940   XDrawSegments (dpy, win, shadowGC, shadow, shadowT);
941   XDrawSegments (dpy, win, shineGC,  shine,  shadowT * 2);
942 }
943
944 static void
945 draw_dimple (Display *dpy, Drawable win, GC shine, GC shadow,
946              int x, int y, int width, int height)
947 {
948   XDrawArc (dpy, win, shine,  x, y, width, height, 46*64, 180*64);
949   XDrawArc (dpy, win, shadow, x, y, width, height, 45*64, -179*64);
950 }
951
952 /*------- Scrollbar values -> pixels, pixels -> scrollbar values --------*/
953
954 static void
955 seg_pixel_sizes (XlwScrollBarWidget w, int *above_return,
956                  int *ss_return, int *below_return)
957 {
958   float total, height, fuz;
959   int value, above, ss, below;
960
961   height = widget_h (w);
962   if (w->sb.showArrows) height -= (2 * arrow_h (w));
963
964   value = w->sb.value - w->sb.minimum;
965
966   total = w->sb.maximum - w->sb.minimum;
967   fuz   = total / 2;
968
969   ss    = (int) ((height * w->sb.sliderSize + fuz) / total);
970   above = (int) ((height * value + fuz) / total);
971   below = (int) ((height) - (ss + above));
972
973   /* Don't let slider get smaller than SS_MIN */
974   if (ss < SS_MIN)
975     {
976       /* add a percent amount for integer rounding */
977       float tmp = (((float) (SS_MIN - ss) * (float) value) / total) + 0.5;
978
979       above -= (int) tmp;
980       ss = SS_MIN;
981       below = (int) ((height) - (ss + above));
982
983       if (above < 0)
984         {
985           above = 0;
986           below = (int) (height - ss);
987         }
988       if (below < 0)
989         {
990           above = (int) (height - ss);
991           below = 0;
992         }
993       if (ss > height)
994         {
995           above = 0;
996           ss    = (int) height;
997           below = 0;
998         }
999     }
1000
1001   *above_return = above;
1002   *ss_return    = ss;
1003   *below_return = below;
1004
1005   CHECK (w);
1006 }
1007
1008 static void
1009 verify_values (XlwScrollBarWidget w)
1010 {
1011   int total = w->sb.maximum - w->sb.minimum;
1012
1013   if (w->sb.sliderSize > total)
1014       w->sb.sliderSize = total;
1015
1016   if (w->sb.pageIncrement > total)
1017       w->sb.pageIncrement = total;
1018
1019   if (w->sb.increment > total)
1020       w->sb.increment = total;
1021
1022   if (w->sb.value < w->sb.minimum)
1023       w->sb.value = w->sb.minimum;
1024
1025   if (w->sb.value > w->sb.maximum)
1026       w->sb.value = w->sb.maximum;
1027
1028   if (w->sb.sliderSize > w->sb.maximum - w->sb.value)
1029       w->sb.sliderSize = w->sb.maximum - w->sb.value;
1030 }
1031
1032 static int
1033 value_from_pixel (XlwScrollBarWidget w, int above)
1034 {
1035   float total, height, fuz;
1036   int value, ss;
1037
1038   height = widget_h (w);
1039   if (w->sb.showArrows)
1040     height -= (2 * arrow_h (w));
1041
1042   total = w->sb.maximum - w->sb.minimum;
1043   fuz = height / 2;
1044
1045   ss = (int) ((height * w->sb.sliderSize + (total / 2)) / total);
1046
1047   if (ss < SS_MIN)
1048     {
1049       /* add a percent amount for integer rounding */
1050       above += (int) ((((SS_MIN - ss) * above) + fuz) / height);
1051     }
1052
1053   {
1054     /* Prevent SIGFPE's that would occur if we don't truncate the value. */
1055     float floatval = w->sb.minimum + ((float)(above * total + fuz) / height);
1056     if (floatval >= (float) INT_MAX)
1057       value = INT_MAX;
1058     else if (floatval <= (float) INT_MIN)
1059       value = INT_MIN;
1060     else
1061       value = (int) floatval;
1062   }
1063
1064   return value;
1065 }
1066
1067
1068 static void
1069 redraw_dimple (XlwScrollBarWidget w, Display *dpy, Window win,
1070                int x, int y, int width, int height)
1071 {
1072   if (SLIDER_DIMPLE == slider_style (w))
1073     {
1074       int size;
1075       int slider_p = (w->sb.armed == ARM_SLIDER);
1076       GC shine  = slider_p ? w->sb.bottomShadowGC : w->sb.topShadowGC;
1077       GC shadow = slider_p ? w->sb.topShadowGC    : w->sb.bottomShadowGC;
1078       int shadowT = w->sb.shadowThickness;
1079
1080       x += shadowT;
1081       y += shadowT;
1082       width  -= 2*shadowT;
1083       height -= 2*shadowT;
1084
1085       size = (width < height ? width : height) * 3 / 4;
1086
1087       if (size%2 != (width < height ? width : height)%2) size--;
1088
1089       DBUG (fprintf (stderr, "%d %d\n",
1090                      x + (width / 2) - (size / 2) - 2*shadowT,
1091                      width - size - shadowT));
1092
1093       draw_dimple (dpy, win, shine, shadow,
1094                    x + (width  / 2) - (size / 2),
1095                    y + (height / 2) - (size / 2),
1096                    size, size);
1097     }
1098 }
1099
1100 static void
1101 draw_slider (XlwScrollBarWidget w, int above, int ss, int below)
1102 {
1103   Display *dpy = XtDisplay ((Widget) w);
1104   Window   win = XtWindow  ((Widget) w);
1105
1106   int x       = widget_x (w);
1107   int y       = widget_y (w);
1108   int width   = widget_w (w);
1109   int height  = widget_h (w);
1110   int shadowT = w->sb.shadowThickness;
1111   int vert_p  = VERT (w);
1112
1113   if (shadowT > (width  / 2)) shadowT = (width  / 2);
1114   if (shadowT > (height / 2)) shadowT = (height / 2);
1115   if (shadowT < 0)            shadowT = 0;
1116
1117   if (w->sb.showArrows && !arrow_same_end (w))
1118     y += arrow_h (w);
1119
1120   /* trough above slider */
1121   if (above > 0)
1122     {
1123       if (vert_p)
1124         XClearArea (dpy, win, x, y, width, above, False);
1125       else
1126         XClearArea (dpy, win, y, x, above, width, False);
1127     }
1128
1129   /* slider */
1130   if (vert_p)
1131     {
1132       draw_shadows (dpy, win, w->sb.topShadowGC, w->sb.bottomShadowGC,
1133                     x, y + above, width, ss, shadowT);
1134       XFillRectangle (dpy, win, w->sb.backgroundGC,
1135                       x+shadowT, y + above + shadowT,
1136                       width-2*shadowT, ss-2*shadowT);
1137       redraw_dimple (w, dpy, win, x, y + above, width, ss);
1138     }
1139   else
1140     {
1141       draw_shadows (dpy, win, w->sb.topShadowGC, w->sb.bottomShadowGC,
1142                     y + above, x, ss, width, shadowT);
1143       XFillRectangle (dpy, win, w->sb.backgroundGC,
1144                       y + above + shadowT, x+shadowT,
1145                       ss-2*shadowT, width-2*shadowT);
1146       redraw_dimple (w, dpy, win, y + above, x, ss, width);
1147     }
1148
1149   /* trough below slider */
1150   if (below > 0)
1151     {
1152       if (vert_p)
1153         XClearArea (dpy, win, x, y + above + ss, width, below, False);
1154       else
1155         XClearArea (dpy, win, y + above + ss, x, below, width, False);
1156     }
1157
1158   CHECK (w);
1159 }
1160
1161 static void
1162 redraw_up_arrow (XlwScrollBarWidget w, Boolean armed, Boolean clear_behind)
1163 {
1164   Display *dpy = XtDisplay ((Widget) w);
1165   Window   win = XtWindow  ((Widget) w);
1166
1167   int x       = widget_x (w);
1168   int y       = widget_y (w);
1169   int width   = widget_w (w);
1170   int height  = widget_h (w);
1171   int shadowT = w->sb.shadowThickness;
1172   int arrow_height = arrow_h (w);
1173
1174   GC bg     = w->sb.backgroundGC;
1175   GC shine  = armed ? w->sb.bottomShadowGC : w->sb.topShadowGC;
1176   GC shadow = armed ? w->sb.topShadowGC    : w->sb.bottomShadowGC;
1177
1178   if (VERT (w))
1179     {
1180       if (arrow_same_end (w))
1181         y += height - 2 * arrow_height;
1182       if (clear_behind)
1183         XClearArea (dpy, win, x, y, width, arrow_height + 1, False);
1184       draw_arrow_up (dpy, win, bg, shine, shadow,
1185                      x + (width - arrow_height)/2, y,
1186                      arrow_height, arrow_height, shadowT);
1187     }
1188   else
1189     {
1190       if (arrow_same_end (w))
1191         y += height - 2 * arrow_height;
1192       if (clear_behind)
1193         XClearArea (dpy, win, y, x, arrow_height + 1, height, False);
1194       draw_arrow_left (dpy, win, bg, shine, shadow,
1195                        y, x + (width - arrow_height)/2,
1196                        arrow_height, arrow_height, shadowT);
1197     }
1198 }
1199
1200 static void
1201 redraw_down_arrow (XlwScrollBarWidget w, Boolean armed, Boolean clear_behind)
1202 {
1203   Display *dpy = XtDisplay ((Widget) w);
1204   Window   win = XtWindow  ((Widget) w);
1205
1206   int x       = widget_x (w);
1207   int y       = widget_y (w);
1208   int width   = widget_w (w);
1209   int height  = widget_h (w);
1210   int shadowT = w->sb.shadowThickness;
1211   int arrow_height = arrow_h (w);
1212
1213   GC bg     = w->sb.backgroundGC;
1214   GC shine  = armed ? w->sb.bottomShadowGC : w->sb.topShadowGC;
1215   GC shadow = armed ? w->sb.topShadowGC    : w->sb.bottomShadowGC;
1216
1217   if (VERT (w))
1218     {
1219       if (clear_behind)
1220         XClearArea (dpy, win, x, y + height - arrow_height, width,
1221                     arrow_height + 1, False);
1222       draw_arrow_down (dpy, win, bg, shine, shadow,
1223                        x + (width - arrow_height)/2,
1224                        y + height - arrow_height + 1,
1225                        arrow_height, arrow_height, shadowT);
1226     }
1227   else
1228     {
1229       if (clear_behind)
1230         XClearArea (dpy, win, y + height - arrow_height, x,
1231                     arrow_height + 1, height, False);
1232       draw_arrow_right (dpy, win, bg, shine, shadow,
1233                         y + height - arrow_height + 1,
1234                         x + (width - arrow_height)/2,
1235                         arrow_height, arrow_height, shadowT);
1236     }
1237 }
1238
1239 static void
1240 redraw_everything (XlwScrollBarWidget w, Region region, Boolean behind_arrows)
1241 {
1242   Display *dpy = XtDisplay ((Widget) w);
1243   Window   win = XtWindow  ((Widget) w);
1244
1245   if (w->sb.showArrows)
1246     {
1247       if (region == NULL)
1248         {
1249           redraw_up_arrow   (w, False, behind_arrows);
1250           redraw_down_arrow (w, False, behind_arrows);
1251         }
1252       else
1253         {
1254           int x        = widget_x (w);
1255           int y        = widget_y (w);
1256           int width    = widget_w (w);
1257           int height   = widget_h (w);
1258           int arrow_height = arrow_h (w);
1259           int ax = x, ay = y;
1260
1261           if (arrow_same_end (w))
1262             {
1263               if (VERT (w))
1264                 ay = y + height - arrow_height - arrow_height;
1265               else
1266                 ax = x + height - arrow_height - arrow_height;
1267             }
1268           if (XRectInRegion (region, ax, ay, width, width))
1269             redraw_up_arrow (w, False, behind_arrows);
1270
1271           if (VERT (w))
1272             ay = y + height - arrow_height;
1273           else
1274             ax = x + height - arrow_height;
1275           if (XRectInRegion (region, ax, ay, width, width))
1276             redraw_down_arrow (w, False, behind_arrows);
1277         }
1278     }
1279
1280   draw_shadows (dpy, win, w->sb.bottomShadowGC, w->sb.topShadowGC, 0, 0,
1281                 w->core.width, w->core.height, w->sb.shadowThickness);
1282
1283   draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1284 }
1285
1286 /*-------------------------- Method Functions ---------------------------*/
1287
1288 static void
1289 Initialize (Widget treq, Widget tnew, ArgList args, Cardinal *num_args)
1290 {
1291   XlwScrollBarWidget request = (XlwScrollBarWidget) treq;
1292   XlwScrollBarWidget w = (XlwScrollBarWidget) tnew;
1293   Display *dpy = XtDisplay ((Widget) w);
1294   Window win = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
1295
1296   if (request->core.width  == 0) w->core.width  += (VERT (w) ? 12 : 25);
1297   if (request->core.height == 0) w->core.height += (VERT (w) ? 25 : 12);
1298
1299   verify_values (w);
1300
1301   w->sb.lastY = 0;
1302   w->sb.above = 0;
1303   w->sb.ss    = 0;
1304   w->sb.below = 0;
1305   w->sb.armed = ARM_NONE;
1306   w->sb.forced_scroll = FORCED_SCROLL_NONE;
1307
1308   if (w->sb.shadowThickness > 5) w->sb.shadowThickness = 5;
1309
1310   w->sb.grayPixmap =
1311     XCreatePixmapFromBitmapData (dpy, win, (char *) gray_bits, gray_width,
1312                                  gray_height, 1, 0, 1);
1313
1314   make_trough_pixel (w);
1315
1316   make_shadow_pixels (w);
1317
1318   w->sb.backgroundGC =
1319     get_gc (w, w->core.background_pixel, w->core.background_pixel, None);
1320   w->sb.topShadowGC =
1321     get_gc (w, w->sb.topShadowColor, w->core.background_pixel,
1322             w->sb.topShadowPixmap);
1323   w->sb.bottomShadowGC =
1324     get_gc (w, w->sb.bottomShadowColor, w->core.background_pixel,
1325             w->sb.bottomShadowPixmap);
1326
1327   w->sb.fullRedrawNext = True;
1328
1329   w->sb.timerActive = False;
1330 }
1331
1332 static void
1333 Destroy (Widget widget)
1334 {
1335   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1336   Display *dpy = XtDisplay ((Widget) w);
1337
1338   XtReleaseGC (widget, w->sb.bottomShadowGC);
1339   XtReleaseGC (widget, w->sb.topShadowGC);
1340   XtReleaseGC (widget, w->sb.backgroundGC);
1341
1342   XFreePixmap (dpy, w->sb.grayPixmap);
1343
1344   if (w->sb.timerActive)
1345     {
1346       XtRemoveTimeOut (w->sb.timerId);
1347       w->sb.timerActive = False; /* Should be a no-op, but you never know */
1348     }
1349 }
1350
1351 static void
1352 Realize (Widget widget, XtValueMask *valuemask, XSetWindowAttributes *attr)
1353 {
1354   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1355   Display *dpy = XtDisplay ((Widget) w);
1356   Window win;
1357   XSetWindowAttributes win_attr;
1358
1359   (*coreClassRec.core_class.realize)(widget, valuemask, attr);
1360
1361   win = XtWindow ((Widget) w);
1362
1363   seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1364
1365   XSetWindowBackground (dpy, win, w->sb.troughColor);
1366
1367   /* Change bit gravity so widget is not cleared on resize */
1368   win_attr.bit_gravity = NorthWestGravity;
1369   XChangeWindowAttributes (dpy, win, CWBitGravity , &win_attr);
1370
1371 }
1372
1373 static void
1374 Resize (Widget widget)
1375 {
1376   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1377   Display *dpy = XtDisplay ((Widget) w);
1378   Window win   = XtWindow  ((Widget) w);
1379
1380   if (XtIsRealized (widget))
1381     {
1382       DBUG (fprintf (stderr, "Resize = %08lx\n", w));
1383
1384       seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1385
1386       /* redraw_everything (w, NULL, True); */
1387
1388       w->sb.fullRedrawNext = True;
1389       /* Force expose event */
1390       XClearArea (dpy, win, widget_x (w), widget_y (w), 1, 1, True);
1391     }
1392
1393   if (w->sb.timerActive)
1394     {
1395       XtRemoveTimeOut (w->sb.timerId);
1396       w->sb.timerActive = False;
1397     }
1398 }
1399
1400 static void
1401 Redisplay (Widget widget, XEvent *event, Region region)
1402 {
1403   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1404
1405   DBUG (fprintf (stderr, "Redisplay = %08lx\n", w));
1406
1407   if (XtIsRealized (widget))
1408     {
1409       if (w->sb.fullRedrawNext)
1410         redraw_everything (w, NULL, True);
1411       else
1412         redraw_everything (w, region, False);
1413       w->sb.fullRedrawNext = False;
1414     }
1415 }
1416
1417 static Boolean
1418 SetValues (Widget current, Widget request, Widget neww,
1419            ArgList args, Cardinal *num_args)
1420 {
1421   XlwScrollBarWidget cur = (XlwScrollBarWidget) current;
1422   XlwScrollBarWidget w = (XlwScrollBarWidget) neww;
1423   Boolean do_redisplay = False;
1424
1425   if (cur->sb.troughColor != w->sb.troughColor)
1426     {
1427       if (XtIsRealized ((Widget) w))
1428         {
1429           XSetWindowBackground (XtDisplay((Widget) w), XtWindow ((Widget) w),
1430                                 w->sb.troughColor);
1431           do_redisplay = True;
1432         }
1433     }
1434
1435   if (cur->core.background_pixel != w->core.background_pixel)
1436     {
1437       XtReleaseGC ((Widget)cur, cur->sb.backgroundGC);
1438       w->sb.backgroundGC =
1439         get_gc (w, w->core.background_pixel, w->core.background_pixel, None);
1440       do_redisplay = True;
1441     }
1442
1443   if (cur->sb.topShadowColor != w->sb.topShadowColor ||
1444       cur->sb.topShadowPixmap != w->sb.topShadowPixmap)
1445     {
1446       XtReleaseGC ((Widget)cur, cur->sb.topShadowGC);
1447       w->sb.topShadowGC =
1448         get_gc (w, w->sb.topShadowColor, w->core.background_pixel,
1449                 w->sb.topShadowPixmap);
1450       do_redisplay = True;
1451     }
1452
1453   if (cur->sb.bottomShadowColor != w->sb.bottomShadowColor ||
1454       cur->sb.bottomShadowPixmap != w->sb.bottomShadowPixmap)
1455     {
1456       XtReleaseGC ((Widget)cur, cur->sb.bottomShadowGC);
1457       w->sb.bottomShadowGC =
1458         get_gc (w, w->sb.bottomShadowColor, w->core.background_pixel,
1459                 w->sb.bottomShadowPixmap);
1460       do_redisplay = True;
1461     }
1462
1463   if (cur->sb.orientation != w->sb.orientation)
1464     do_redisplay = True;
1465
1466
1467   if (cur->sb.minimum       != w->sb.minimum       ||
1468       cur->sb.maximum       != w->sb.maximum       ||
1469       cur->sb.sliderSize    != w->sb.sliderSize    ||
1470       cur->sb.value         != w->sb.value         ||
1471       cur->sb.pageIncrement != w->sb.pageIncrement ||
1472       cur->sb.increment     != w->sb.increment)
1473     {
1474       verify_values (w);
1475       if (XtIsRealized ((Widget) w))
1476         {
1477           seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1478           draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1479         }
1480     }
1481
1482   if (w->sb.shadowThickness > 5) w->sb.shadowThickness = 5;
1483
1484   return do_redisplay;
1485 }
1486
1487 void
1488 XlwScrollBarGetValues (Widget widget, int *value, int *sliderSize,
1489                        int *increment, int *pageIncrement)
1490 {
1491   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1492
1493   if (w && XtClass ((Widget) w) == xlwScrollBarWidgetClass)
1494     {
1495       if (value)         *value         = w->sb.value;
1496       if (sliderSize)    *sliderSize    = w->sb.sliderSize;
1497       if (increment)     *increment     = w->sb.increment;
1498       if (pageIncrement) *pageIncrement = w->sb.pageIncrement;
1499     }
1500 }
1501
1502 void
1503 XlwScrollBarSetValues (Widget widget, int value, int sliderSize,
1504                        int increment, int pageIncrement, Boolean notify)
1505 {
1506   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1507
1508   if (w && XtClass ((Widget) w) == xlwScrollBarWidgetClass &&
1509       (w->sb.value         != value         ||
1510        w->sb.sliderSize    != sliderSize    ||
1511        w->sb.increment     != increment     ||
1512        w->sb.pageIncrement != pageIncrement))
1513     {
1514       int last_value = w->sb.value;
1515
1516       w->sb.value         = value;
1517       w->sb.sliderSize    = sliderSize;
1518       w->sb.increment     = increment;
1519       w->sb.pageIncrement = pageIncrement;
1520
1521       verify_values (w);
1522
1523       if (XtIsRealized (widget))
1524         {
1525           seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1526           draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1527
1528           if (w->sb.value != last_value && notify)
1529             call_callbacks (w, XmCR_VALUE_CHANGED, w->sb.value, 0, NULL);
1530         }
1531     }
1532 }
1533
1534 /*-------------------------- Action Functions ---------------------------*/
1535
1536 static void
1537 timer (XtPointer data, XtIntervalId *id)
1538 {
1539   XlwScrollBarWidget w = (XlwScrollBarWidget) data;
1540   w->sb.timerActive = False;
1541
1542   if (w->sb.armed != ARM_NONE)
1543     {
1544       int last_value = w->sb.value;
1545       int reason;
1546
1547       switch (w->sb.armed)
1548         {
1549         case ARM_PAGEUP:
1550           decrement_value (w, w->sb.pageIncrement);
1551           reason = XmCR_PAGE_DECREMENT;
1552           break;
1553         case ARM_PAGEDOWN:
1554           increment_value (w, w->sb.pageIncrement);
1555           reason = XmCR_PAGE_INCREMENT;
1556           break;
1557         case ARM_UP:
1558           decrement_value (w, w->sb.increment);
1559           reason = XmCR_DECREMENT;
1560           break;
1561         case ARM_DOWN:
1562           increment_value (w, w->sb.increment);
1563           reason = XmCR_INCREMENT;
1564           break;
1565         default:
1566           reason = XmCR_NONE;
1567         }
1568
1569       verify_values (w);
1570
1571       if (last_value != w->sb.value)
1572         {
1573           seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1574           draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1575
1576           call_callbacks (w, reason, w->sb.value, 0, NULL);
1577
1578           w->sb.timerId =
1579             XtAppAddTimeOut (XtWidgetToApplicationContext ((Widget) w),
1580                              (unsigned long) w->sb.repeatDelay,
1581                              timer,  (XtPointer) w);
1582           w->sb.timerActive = True;
1583         }
1584     }
1585 }
1586
1587 static button_where
1588 what_button (XlwScrollBarWidget w, int mouse_x, int mouse_y)
1589 {
1590   int width   = widget_w (w);
1591   int height  = widget_h (w);
1592   int arrow_height = arrow_h (w);
1593
1594   mouse_x -= widget_x (w);
1595   mouse_y -= widget_y (w);
1596
1597   if (mouse_x < 0 || mouse_x >= width ||
1598       mouse_y < 0 || mouse_y >= height)
1599     return BUTTON_NONE;
1600
1601   if (w->sb.showArrows)
1602     {
1603       if (mouse_y >= (height -= arrow_height))
1604         return BUTTON_DOWN_ARROW;
1605
1606       if (arrow_same_end (w))
1607         {
1608           if (mouse_y >= (height -= arrow_height))
1609             return BUTTON_UP_ARROW;
1610         }
1611       else
1612         if ( (mouse_y -= arrow_height) < 0)
1613           return BUTTON_UP_ARROW;
1614     }
1615
1616   if ( (mouse_y -= w->sb.above) < 0)
1617     return BUTTON_TROUGH_ABOVE;
1618
1619   if ( (mouse_y -= w->sb.ss) < 0)
1620     return BUTTON_SLIDER;
1621
1622   return BUTTON_TROUGH_BELOW;
1623 }
1624
1625 static void
1626 Select (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1627 {
1628   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1629   button_where sb_button;
1630
1631   int mouse_x = event_x (w, event);
1632   int mouse_y = event_y (w, event);
1633
1634   int last_value = w->sb.savedValue = w->sb.value;
1635   int reason     = XmCR_NONE;
1636
1637   XtGrabKeyboard ((Widget) w, False, GrabModeAsync, GrabModeAsync,
1638                   event->xbutton.time);
1639
1640   sb_button = what_button (w, mouse_x, mouse_y);
1641
1642   if (w->sb.forced_scroll != FORCED_SCROLL_NONE)
1643     {
1644       switch (sb_button)
1645         {
1646         case BUTTON_TROUGH_ABOVE:
1647         case BUTTON_TROUGH_BELOW:
1648         case BUTTON_SLIDER:
1649           sb_button= BUTTON_NONE; /* cause next switch to fall through */
1650           if (w->sb.forced_scroll == FORCED_SCROLL_UPLEFT)
1651             {
1652               decrement_value (w, w->sb.pageIncrement);
1653               w->sb.armed = ARM_PAGEUP;
1654               reason      = XmCR_PAGE_DECREMENT;
1655               break;
1656             }
1657           else if (w->sb.forced_scroll == FORCED_SCROLL_DOWNRIGHT)
1658             {
1659               increment_value (w, w->sb.pageIncrement);
1660               w->sb.armed = ARM_PAGEDOWN;
1661               reason      = XmCR_PAGE_INCREMENT;
1662               break;
1663             }
1664           abort();
1665         default:
1666           ; /* Do nothing */
1667         }
1668     }
1669
1670   switch (sb_button)
1671     {
1672     case BUTTON_TROUGH_ABOVE:
1673       decrement_value (w, w->sb.pageIncrement);
1674       w->sb.armed = ARM_PAGEUP;
1675       reason      = XmCR_PAGE_DECREMENT;
1676       break;
1677     case BUTTON_TROUGH_BELOW:
1678       increment_value (w, w->sb.pageIncrement);
1679       w->sb.armed = ARM_PAGEDOWN;
1680       reason      = XmCR_PAGE_INCREMENT;
1681       break;
1682     case BUTTON_SLIDER:
1683       w->sb.lastY = mouse_y;
1684       w->sb.armed = ARM_SLIDER;
1685       draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1686       break;
1687     case BUTTON_UP_ARROW:
1688       if (event->xbutton.state & ControlMask)
1689         {
1690           w->sb.value = w->sb.minimum;
1691           reason      = XmCR_TO_TOP;
1692         }
1693       else
1694         {
1695           decrement_value (w, w->sb.increment);
1696           reason      = XmCR_DECREMENT;
1697         }
1698       w->sb.armed = ARM_UP;
1699       redraw_up_arrow (w, True, False);
1700       break;
1701     case BUTTON_DOWN_ARROW:
1702       if (event->xbutton.state & ControlMask)
1703         {
1704           w->sb.value = w->sb.maximum;
1705           reason      = XmCR_TO_BOTTOM;
1706         }
1707       else
1708         {
1709           increment_value (w, w->sb.increment);
1710           reason      = XmCR_INCREMENT;
1711         }
1712       w->sb.armed = ARM_DOWN;
1713       redraw_down_arrow (w, True, False);
1714       break;
1715     case BUTTON_NONE:
1716       ; /* Do nothing */
1717     }
1718
1719   verify_values (w);
1720
1721   if (last_value != w->sb.value)
1722     {
1723       seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1724       draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1725
1726       call_callbacks (w, reason, w->sb.value, mouse_y, event);
1727
1728       if (w->sb.timerActive)
1729         XtRemoveTimeOut (w->sb.timerId);
1730
1731       w->sb.timerId =
1732         XtAppAddTimeOut (XtWidgetToApplicationContext ((Widget) w),
1733                          (unsigned long) w->sb.initialDelay,
1734                          timer,  (XtPointer) w);
1735       w->sb.timerActive = True;
1736     }
1737
1738   CHECK (w);
1739 }
1740
1741 static void
1742 PageDownOrRight (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1743 {
1744   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1745   w->sb.forced_scroll = FORCED_SCROLL_DOWNRIGHT;
1746   Select (widget, event, parms, num_parms);
1747   w->sb.forced_scroll = FORCED_SCROLL_NONE;
1748 }
1749
1750 static void
1751 PageUpOrLeft (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1752 {
1753   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1754   w->sb.forced_scroll = FORCED_SCROLL_UPLEFT;
1755   Select (widget, event, parms, num_parms);
1756   w->sb.forced_scroll = FORCED_SCROLL_NONE;
1757 }
1758
1759 static void
1760 Drag (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1761 {
1762   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1763
1764   if (w->sb.armed == ARM_SLIDER)
1765     {
1766       int mouse_y = event_y (w, event);
1767       int diff    = mouse_y - w->sb.lastY;
1768
1769       if (diff < -(w->sb.above)) /* up */
1770         {
1771           mouse_y -= (diff + w->sb.above);
1772           diff = -(w->sb.above);
1773         }
1774       else if (diff > w->sb.below) /* down */
1775         {
1776           mouse_y -= (diff - w->sb.below);
1777           diff = w->sb.below;
1778         }
1779
1780       if (diff)
1781         {
1782           w->sb.above += diff;
1783           w->sb.below -= diff;
1784
1785           draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1786
1787           w->sb.lastY = mouse_y;
1788
1789           w->sb.value = value_from_pixel (w, w->sb.above);
1790           verify_values (w);
1791           CHECK (w);
1792
1793           call_callbacks (w, XmCR_DRAG, w->sb.value, event_y (w, event), event);
1794         }
1795     }
1796   CHECK (w);
1797 }
1798
1799 static void
1800 Release (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1801 {
1802   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1803
1804   switch (w->sb.armed)
1805     {
1806     case ARM_SLIDER:
1807       call_callbacks (w, XmCR_VALUE_CHANGED, w->sb.value, event_y (w, event), event);
1808       w->sb.armed = ARM_NONE;
1809       draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1810       break;
1811     case ARM_UP:
1812       redraw_up_arrow (w, False, False);
1813       break;
1814     case ARM_DOWN:
1815       redraw_down_arrow (w, False, False);
1816       break;
1817     default:
1818       ; /* Do nothing */
1819     }
1820
1821   XtUngrabKeyboard ((Widget) w, event->xbutton.time);
1822
1823   w->sb.armed = ARM_NONE;
1824 }
1825
1826 static void
1827 Jump (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1828 {
1829   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1830   int last_value;
1831
1832   int mouse_x = event_x (w, event);
1833   int mouse_y = event_y (w, event);
1834
1835   int scroll_region_y = widget_y (w);
1836   int scroll_region_h = widget_h (w);
1837
1838   if (w->sb.showArrows)
1839     {
1840       int arrow_height = arrow_h (w);
1841       scroll_region_h -= 2 * arrow_height;
1842       if (!arrow_same_end (w))
1843         scroll_region_y += arrow_height;
1844     }
1845
1846   XtGrabKeyboard ((Widget) w, False, GrabModeAsync, GrabModeAsync,
1847                   event->xbutton.time);
1848
1849   switch (what_button (w, mouse_x, mouse_y))
1850     {
1851     case BUTTON_TROUGH_ABOVE:
1852     case BUTTON_TROUGH_BELOW:
1853     case BUTTON_SLIDER:
1854       w->sb.savedValue = w->sb.value;
1855
1856       last_value = w->sb.value;
1857
1858       w->sb.above = mouse_y - (w->sb.ss / 2) - scroll_region_y;
1859       if (w->sb.above < 0)
1860         w->sb.above = 0;
1861       else if (w->sb.above + w->sb.ss > scroll_region_h)
1862         w->sb.above = scroll_region_h - w->sb.ss;
1863
1864       w->sb.below = scroll_region_h - w->sb.ss - w->sb.above;
1865
1866       w->sb.armed = ARM_SLIDER;
1867       draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1868
1869       w->sb.value = value_from_pixel (w, w->sb.above);
1870       verify_values (w);
1871       CHECK (w);
1872
1873       w->sb.lastY = mouse_y;
1874
1875       if (w->sb.value != last_value)
1876         call_callbacks (w, XmCR_DRAG, w->sb.value, mouse_y, event);
1877
1878       break;
1879     default:
1880       ; /* Do nothing */
1881     }
1882   CHECK (w);
1883 }
1884
1885 static void
1886 Abort (Widget widget, XEvent *event, String *parms, Cardinal *num_parms)
1887 {
1888   XlwScrollBarWidget w = (XlwScrollBarWidget) widget;
1889
1890   if (w->sb.armed != ARM_NONE)
1891     {
1892       if (w->sb.value != w->sb.savedValue)
1893         {
1894           w->sb.value = w->sb.savedValue;
1895
1896           seg_pixel_sizes (w, &w->sb.above, &w->sb.ss, &w->sb.below);
1897           draw_slider (w, w->sb.above, w->sb.ss, w->sb.below);
1898
1899           call_callbacks (w, XmCR_VALUE_CHANGED, w->sb.value,
1900                           event_y (w, event), event);
1901         }
1902
1903       switch (w->sb.armed)
1904         {
1905         case ARM_UP:   redraw_up_arrow   (w, False, False); break;
1906         case ARM_DOWN: redraw_down_arrow (w, False, False); break;
1907         default: ; /* Do nothing */
1908         }
1909
1910       w->sb.armed = ARM_NONE;
1911
1912       XtUngrabKeyboard ((Widget) w, event->xbutton.time);
1913     }
1914 }