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