comparison lwlib/xlwscrollbar.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 9d177e8d4150
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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 }