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