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