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