3 "$XConsortium: Scrollbar.c,v 1.58 89/12/15 11:36:58 kit Exp $";
6 /***********************************************************
7 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
8 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
12 Permission to use, copy, modify, and distribute this software and its
13 documentation for any purpose and without fee is hereby granted,
14 provided that the above copyright notice appear in all copies and that
15 both that copyright notice and this permission notice appear in
16 supporting documentation, and that the names of Digital or MIT not be
17 used in advertising or publicity pertaining to distribution of the
18 software without specific, written prior permission.
20 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 ******************************************************************/
31 /* created by weissman, Mon Jul 7 13:20:03 1986 */
32 /* converted by swick, Thu Aug 27 1987 */
34 #include <X11/IntrinsicP.h>
35 #include <X11/StringDefs.h>
37 #include <./Xaw3_1XawInit.h>
38 #include <./Xaw3_1ScrollbarP.h>
40 #include <X11/Xmu/Drawing.h> /* for XmuCreateStippledPixmap */
42 /* Private definitions. */
44 static char defaultTranslations
[] =
45 "<Btn1Down>: StartScroll(Forward) \n\
46 <Btn2Down>: StartScroll(Continuous) MoveThumb() NotifyThumb() \n\
47 <Btn3Down>: StartScroll(Backward) \n\
48 <Btn2Motion>: MoveThumb() NotifyThumb() \n\
49 <BtnUp>: NotifyScroll(Proportional) EndScroll()";
51 #ifdef bogusScrollKeys
53 "<KeyPress>f: StartScroll(Forward) NotifyScroll(FullLength) EndScroll()"
54 "<KeyPress>b: StartScroll(Backward) NotifyScroll(FullLength) EndScroll()"
57 static float floatZero
= 0.0;
59 #define Offset(field) XtOffset(ScrollbarWidget, field)
61 static XtResource resources
[] = {
62 {XtNlength
, XtCLength
, XtRDimension
, sizeof(Dimension
),
63 Offset(scrollbar
.length
), XtRImmediate
, (caddr_t
) 1},
64 {XtNthickness
, XtCThickness
, XtRDimension
, sizeof(Dimension
),
65 Offset(scrollbar
.thickness
), XtRImmediate
, (caddr_t
) 14},
66 {XtNorientation
, XtCOrientation
, XtROrientation
, sizeof(XtOrientation
),
67 Offset(scrollbar
.orientation
), XtRImmediate
, (caddr_t
) XtorientVertical
},
68 {XtNscrollProc
, XtCCallback
, XtRCallback
, sizeof(caddr_t
),
69 Offset(scrollbar
.scrollProc
), XtRCallback
, NULL
},
70 {XtNthumbProc
, XtCCallback
, XtRCallback
, sizeof(caddr_t
),
71 Offset(scrollbar
.thumbProc
), XtRCallback
, NULL
},
72 {XtNjumpProc
, XtCCallback
, XtRCallback
, sizeof(caddr_t
),
73 Offset(scrollbar
.jumpProc
), XtRCallback
, NULL
},
74 {XtNthumb
, XtCThumb
, XtRPixmap
, sizeof(Pixmap
),
75 Offset(scrollbar
.thumb
), XtRImmediate
, (XtPointer
) XtUnspecifiedPixmap
},
76 {XtNforeground
, XtCForeground
, XtRPixel
, sizeof(Pixel
),
77 Offset(scrollbar
.foreground
), XtRString
, XtDefaultForeground
},
78 {XtNshown
, XtCShown
, XtRFloat
, sizeof(float),
79 Offset(scrollbar
.shown
), XtRFloat
, (caddr_t
)&floatZero
},
80 {XtNtopOfThumb
, XtCTopOfThumb
, XtRFloat
, sizeof(float),
81 Offset(scrollbar
.top
), XtRFloat
, (caddr_t
)&floatZero
},
82 {XtNscrollVCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
83 Offset(scrollbar
.verCursor
), XtRString
, "sb_v_double_arrow"},
84 {XtNscrollHCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
85 Offset(scrollbar
.horCursor
), XtRString
, "sb_h_double_arrow"},
86 {XtNscrollUCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
87 Offset(scrollbar
.upCursor
), XtRString
, "sb_up_arrow"},
88 {XtNscrollDCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
89 Offset(scrollbar
.downCursor
), XtRString
, "sb_down_arrow"},
90 {XtNscrollLCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
91 Offset(scrollbar
.leftCursor
), XtRString
, "sb_left_arrow"},
92 {XtNscrollRCursor
, XtCCursor
, XtRCursor
, sizeof(Cursor
),
93 Offset(scrollbar
.rightCursor
), XtRString
, "sb_right_arrow"},
94 {XtNminimumThumb
, XtCMinimumThumb
, XtRDimension
, sizeof(Dimension
),
95 Offset(scrollbar
.min_thumb
), XtRImmediate
, (caddr_t
) 7},
98 static void ClassInitialize();
99 static void Initialize();
100 static void Destroy();
101 static void Realize();
102 static void Resize();
103 static void Redisplay();
104 static Boolean
SetValues();
106 static void StartScroll();
107 static void MoveThumb();
108 static void NotifyThumb();
109 static void NotifyScroll();
110 static void EndScroll();
112 static XtActionsRec actions
[] = {
113 {"StartScroll", StartScroll
},
114 {"MoveThumb", MoveThumb
},
115 {"NotifyThumb", NotifyThumb
},
116 {"NotifyScroll", NotifyScroll
},
117 {"EndScroll", EndScroll
},
122 ScrollbarClassRec scrollbarClassRec
= {
124 /* superclass */ (WidgetClass
) &widgetClassRec
,
125 /* class_name */ "Scrollbar",
126 /* size */ sizeof(ScrollbarRec
),
127 /* class_initialize */ ClassInitialize
,
128 /* class_part_init */ NULL
,
129 /* class_inited */ FALSE
,
130 /* initialize */ Initialize
,
131 /* initialize_hook */ NULL
,
132 /* realize */ Realize
,
133 /* actions */ actions
,
134 /* num_actions */ XtNumber(actions
),
135 /* resources */ resources
,
136 /* num_resources */ XtNumber(resources
),
137 /* xrm_class */ NULLQUARK
,
138 /* compress_motion */ TRUE
,
139 /* compress_exposure*/ TRUE
,
140 /* compress_enterleave*/ TRUE
,
141 /* visible_interest */ FALSE
,
142 /* destroy */ Destroy
,
144 /* expose */ Redisplay
,
145 /* set_values */ SetValues
,
146 /* set_values_hook */ NULL
,
147 /* set_values_almost */ XtInheritSetValuesAlmost
,
148 /* get_values_hook */ NULL
,
149 /* accept_focus */ NULL
,
150 /* version */ XtVersion
,
151 /* callback_private */ NULL
,
152 /* tm_table */ defaultTranslations
,
153 /* query_geometry */ XtInheritQueryGeometry
,
154 /* display_accelerator*/ XtInheritDisplayAccelerator
,
158 WidgetClass scrollbarWidgetClass
= (WidgetClass
)&scrollbarClassRec
;
161 #define PICKLENGTH(widget, x, y) \
162 ((widget->scrollbar.orientation == XtorientHorizontal) ? x : y)
163 #define MIN(x,y) ((x) < (y) ? (x) : (y))
164 #define MAX(x,y) ((x) > (y) ? (x) : (y))
166 static void ClassInitialize()
168 static XtConvertArgRec screenConvertArg
[] = {
169 {XtWidgetBaseOffset
, (caddr_t
) XtOffset(Widget
, core
.screen
),
173 XawInitializeWidgetSet();
174 XtAddConverter( XtRString
, XtROrientation
, XmuCvtStringToOrientation
,
176 XtAddConverter( XtRString
, XtRPixmap
, XmuCvtStringToBitmap
,
177 screenConvertArg
, XtNumber(screenConvertArg
));
181 * Make sure the first number is within the range specified by the other
185 static int InRange(num
, small
, big
)
188 return (num
< small
) ? small
: ((num
> big
) ? big
: num
);
192 * Same as above, but for floating numbers.
195 static float FloatInRange(num
, small
, big
)
196 float num
, small
, big
;
198 return (num
< small
) ? small
: ((num
> big
) ? big
: num
);
202 /* Fill the area specified by top and bottom with the given pattern. */
203 static float FractionLoc(w
, x
, y
)
209 result
= PICKLENGTH(w
, (float) x
/w
->core
.width
,
210 (float) y
/w
->core
.height
);
211 return FloatInRange(result
, 0.0, 1.0);
215 static void FillArea(w
, top
, bottom
, thumb
)
217 Position top
, bottom
;
220 Dimension length
= bottom
-top
;
222 if (bottom
< 0) return;
225 /* Fill the new Thumb location */
227 if (w
->scrollbar
.orientation
== XtorientHorizontal
)
228 XFillRectangle(XtDisplay(w
), XtWindow(w
),
229 w
->scrollbar
.gc
, top
, 1, length
,
232 else XFillRectangle(XtDisplay(w
), XtWindow(w
), w
->scrollbar
.gc
,
233 1, top
, w
->core
.width
-2, length
);
236 /* Clear the old Thumb location */
238 if (w
->scrollbar
.orientation
== XtorientHorizontal
)
239 XClearArea(XtDisplay(w
), XtWindow(w
), top
, 1,
240 length
, w
->core
.height
-2, FALSE
);
242 else XClearArea(XtDisplay(w
), XtWindow(w
), 1,
243 top
, w
->core
.width
-2, length
, FALSE
);
249 /* Paint the thumb in the area specified by w->top and
250 w->shown. The old area is erased. The painting and
251 erasing is done cleverly so that no flickering will occur. */
253 static void PaintThumb( w
)
256 Position oldtop
, oldbot
, newtop
, newbot
;
258 oldtop
= w
->scrollbar
.topLoc
;
259 oldbot
= oldtop
+ w
->scrollbar
.shownLength
;
260 newtop
= w
->scrollbar
.length
* w
->scrollbar
.top
;
261 newbot
= newtop
+ (int)(w
->scrollbar
.length
* w
->scrollbar
.shown
);
262 if (newbot
< newtop
+ w
->scrollbar
.min_thumb
)
263 newbot
= newtop
+ w
->scrollbar
.min_thumb
;
264 w
->scrollbar
.topLoc
= newtop
;
265 w
->scrollbar
.shownLength
= newbot
- newtop
;
267 if (XtIsRealized((Widget
)w
)) {
268 if (newtop
< oldtop
) FillArea(w
, newtop
, MIN(newbot
, oldtop
), 1);
269 if (newtop
> oldtop
) FillArea(w
, oldtop
, MIN(newtop
, oldbot
), 0);
270 if (newbot
< oldbot
) FillArea(w
, MAX(newbot
, oldtop
), oldbot
, 0);
271 if (newbot
> oldbot
) FillArea(w
, MAX(newtop
, oldbot
), newbot
, 1);
276 static void SetDimensions(w
)
279 if (w
->scrollbar
.orientation
== XtorientVertical
) {
280 w
->scrollbar
.length
= w
->core
.height
;
281 w
->scrollbar
.thickness
= w
->core
.width
;
284 w
->scrollbar
.length
= w
->core
.width
;
285 w
->scrollbar
.thickness
= w
->core
.height
;
289 /* Function Name: Destroy
290 * Description: Called as the scrollbar is going away...
291 * Arguments: w - the scrollbar.
299 ScrollbarWidget sbw
= (ScrollbarWidget
) w
;
301 XtReleaseGC(w
, sbw
->scrollbar
.gc
);
304 /* Function Name: CreateGC
305 * Description: Creates the GC.
306 * Arguments: w - the scrollbar widget.
314 ScrollbarWidget sbw
= (ScrollbarWidget
) w
;
317 unsigned int depth
= 1;
319 if (sbw
->scrollbar
.thumb
== XtUnspecifiedPixmap
) {
320 sbw
->scrollbar
.thumb
= XmuCreateStippledPixmap (XtScreen(w
),
321 (Pixel
) 1, (Pixel
) 0,
324 else if (sbw
->scrollbar
.thumb
!= None
) {
327 unsigned int width
, height
, bw
;
328 if (XGetGeometry(XtDisplay(w
), sbw
->scrollbar
.thumb
, &root
, &x
, &y
,
329 &width
, &height
, &bw
, &depth
) == 0) {
330 XtAppError(XtWidgetToApplicationContext(w
),
331 "Scrollbar Widget: Could not get geometry of thumb pixmap.");
335 gcValues
.foreground
= sbw
->scrollbar
.foreground
;
336 gcValues
.background
= sbw
->core
.background_pixel
;
337 mask
= GCForeground
| GCBackground
;
339 if (sbw
->scrollbar
.thumb
!= None
) {
341 gcValues
.fill_style
= FillOpaqueStippled
;
342 gcValues
.stipple
= sbw
->scrollbar
.thumb
;
343 mask
|= GCFillStyle
| GCStipple
;
346 gcValues
.fill_style
= FillTiled
;
347 gcValues
.tile
= sbw
->scrollbar
.thumb
;
348 mask
|= GCFillStyle
| GCTile
;
351 sbw
->scrollbar
.gc
= XtGetGC( w
, mask
, &gcValues
);
355 static void Initialize( request
, new )
356 Widget request
; /* what the client asked for */
357 Widget
new; /* what we're going to give him */
359 ScrollbarWidget w
= (ScrollbarWidget
) new;
363 if (w
->core
.width
== 0)
364 w
->core
.width
= (w
->scrollbar
.orientation
== XtorientVertical
)
365 ? w
->scrollbar
.thickness
: w
->scrollbar
.length
;
367 if (w
->core
.height
== 0)
368 w
->core
.height
= (w
->scrollbar
.orientation
== XtorientHorizontal
)
369 ? w
->scrollbar
.thickness
: w
->scrollbar
.length
;
372 w
->scrollbar
.direction
= 0;
373 w
->scrollbar
.topLoc
= 0;
374 w
->scrollbar
.shownLength
= w
->scrollbar
.min_thumb
;
377 static void Realize( gw
, valueMask
, attributes
)
380 XSetWindowAttributes
*attributes
;
382 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
384 w
->scrollbar
.inactiveCursor
=
385 (w
->scrollbar
.orientation
== XtorientVertical
)
386 ? w
->scrollbar
.verCursor
387 : w
->scrollbar
.horCursor
;
389 attributes
->cursor
= w
->scrollbar
.inactiveCursor
;
390 *valueMask
|= CWCursor
;
392 XtCreateWindow( gw
, InputOutput
, (Visual
*)CopyFromParent
,
393 *valueMask
, attributes
);
398 SetValues( current
, request
, desired
)
399 Widget current
, /* what I am */
400 request
, /* what he wants me to be */
401 desired
; /* what I will become */
403 ScrollbarWidget w
= (ScrollbarWidget
) current
;
404 ScrollbarWidget dw
= (ScrollbarWidget
) desired
;
405 Boolean redraw
= FALSE
;
408 * If these values are outside the acceptable range ignore them...
411 if (dw
->scrollbar
.top
< 0.0 || dw
->scrollbar
.top
> 1.0)
412 dw
->scrollbar
.top
= w
->scrollbar
.top
;
414 if (dw
->scrollbar
.shown
< 0.0 || dw
->scrollbar
.shown
> 1.0)
415 dw
->scrollbar
.shown
= w
->scrollbar
.shown
;
418 * Change colors and stuff...
421 if ( XtIsRealized (desired
) ) {
422 if ( (w
->scrollbar
.foreground
!= dw
->scrollbar
.foreground
) ||
423 (w
->core
.background_pixel
!= dw
->core
.background_pixel
) ||
424 (w
->scrollbar
.thumb
!= dw
->scrollbar
.thumb
) )
426 XtReleaseGC((Widget
)w
, w
->scrollbar
.gc
);
427 CreateGC( (Widget
) dw
);
430 if (w
->scrollbar
.top
!= dw
->scrollbar
.top
||
431 w
->scrollbar
.shown
!= dw
->scrollbar
.shown
)
438 static void Resize( gw
)
441 /* ForgetGravity has taken care of background, but thumb may
442 * have to move as a result of the new size. */
443 SetDimensions( (ScrollbarWidget
)gw
);
444 Redisplay( gw
, (XEvent
*)NULL
, (Region
)NULL
);
449 static void Redisplay( gw
, event
, region
)
454 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
456 unsigned int width
, height
;
458 if (w
->scrollbar
.orientation
== XtorientHorizontal
) {
459 x
= w
->scrollbar
.topLoc
;
461 width
= w
->scrollbar
.shownLength
;
462 height
= w
->core
.height
- 2;
465 y
= w
->scrollbar
.topLoc
;
466 width
= w
->core
.width
- 2;
467 height
= w
->scrollbar
.shownLength
;
470 if ( (region
== NULL
) ||
471 (XRectInRegion(region
, x
, y
, width
, height
) != RectangleOut
) ) {
472 /* Forces entire thumb to be painted. */
473 w
->scrollbar
.topLoc
= -(w
->scrollbar
.length
+ 1);
480 static void StartScroll( gw
, event
, params
, num_params
)
483 String
*params
; /* direction: Back|Forward|Smooth */
484 Cardinal
*num_params
; /* we only support 1 */
486 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
490 if (w
->scrollbar
.direction
!= 0) return; /* if we're already scrolling */
491 if (*num_params
> 0) direction
= *params
[0];
492 else direction
= 'C';
494 w
->scrollbar
.direction
= direction
;
496 switch( direction
) {
498 case 'b': cursor
= (w
->scrollbar
.orientation
== XtorientVertical
)
499 ? w
->scrollbar
.downCursor
500 : w
->scrollbar
.rightCursor
; break;
503 case 'f': cursor
= (w
->scrollbar
.orientation
== XtorientVertical
)
504 ? w
->scrollbar
.upCursor
505 : w
->scrollbar
.leftCursor
; break;
508 case 'c': cursor
= (w
->scrollbar
.orientation
== XtorientVertical
)
509 ? w
->scrollbar
.rightCursor
510 : w
->scrollbar
.upCursor
; break;
512 default: return; /* invalid invocation */
515 XDefineCursor(XtDisplay(w
), XtWindow(w
), cursor
);
517 XFlush(XtDisplay(w
));
521 static Boolean
CompareEvents( oldEvent
, newEvent
)
522 XEvent
*oldEvent
, *newEvent
;
524 #define Check(field) if (newEvent->field != oldEvent->field) return False;
530 switch( newEvent
->type
) {
532 Check(xmotion
.state
); break;
535 Check(xbutton
.state
);
536 Check(xbutton
.button
); break;
540 Check(xkey
.keycode
); break;
543 Check(xcrossing
.mode
);
544 Check(xcrossing
.detail
);
545 Check(xcrossing
.state
); break;
557 static Bool
PeekNotifyEvent( dpy
, event
, args
)
562 struct EventData
*eventData
= (struct EventData
*)args
;
564 return ((++eventData
->count
== QLength(dpy
)) /* since PeekIf blocks */
565 || CompareEvents(event
, eventData
->oldEvent
));
569 static Boolean
LookAhead( w
, event
)
574 struct EventData eventData
;
576 if (QLength(XtDisplay(w
)) == 0) return False
;
579 eventData
.oldEvent
= event
;
581 XPeekIfEvent(XtDisplay(w
), &newEvent
, PeekNotifyEvent
, (char*)&eventData
);
583 if (CompareEvents(event
, &newEvent
))
590 static void ExtractPosition( event
, x
, y
)
592 Position
*x
, *y
; /* RETURN */
594 switch( event
->type
) {
596 *x
= event
->xmotion
.x
; *y
= event
->xmotion
.y
; break;
599 *x
= event
->xbutton
.x
; *y
= event
->xbutton
.y
; break;
602 *x
= event
->xkey
.x
; *y
= event
->xkey
.y
; break;
605 *x
= event
->xcrossing
.x
; *y
= event
->xcrossing
.y
; break;
611 static void NotifyScroll( gw
, event
, params
, num_params
)
614 String
*params
; /* style: Proportional|FullLength */
615 Cardinal
*num_params
; /* we only support 1 */
617 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
622 if (w
->scrollbar
.direction
== 0) return; /* if no StartScroll */
624 if (LookAhead(gw
, event
)) return;
626 if (*num_params
> 0) style
= *params
[0];
630 case 'P': /* Proportional */
631 case 'p': ExtractPosition( event
, &x
, &y
);
632 call_data
= InRange( PICKLENGTH( w
, x
, y
),
634 (int) w
->scrollbar
.length
); break;
636 case 'F': /* FullLength */
637 case 'f': call_data
= w
->scrollbar
.length
; break;
640 switch( w
->scrollbar
.direction
) {
642 case 'b': call_data
= -call_data
;
645 case 'f': XtCallCallbacks( gw
, XtNscrollProc
, (caddr_t
)call_data
);
649 case 'c': /* NotifyThumb has already called the thumbProc(s) */
655 static void EndScroll(gw
, event
, params
, num_params
)
657 XEvent
*event
; /* unused */
658 String
*params
; /* unused */
659 Cardinal
*num_params
; /* unused */
661 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
663 XDefineCursor(XtDisplay(w
), XtWindow(w
), w
->scrollbar
.inactiveCursor
);
664 XFlush(XtDisplay(w
));
666 w
->scrollbar
.direction
= 0;
671 static void MoveThumb( gw
, event
, params
, num_params
)
674 String
*params
; /* unused */
675 Cardinal
*num_params
; /* unused */
677 ScrollbarWidget w
= (ScrollbarWidget
) gw
;
680 if (w
->scrollbar
.direction
== 0) return; /* if no StartScroll */
682 if (LookAhead(gw
, event
)) return;
684 ExtractPosition( event
, &x
, &y
);
685 w
->scrollbar
.top
= FractionLoc(w
, x
, y
);
687 XFlush(XtDisplay(w
)); /* re-draw it before Notifying */
692 static void NotifyThumb( gw
, event
, params
, num_params
)
695 String
*params
; /* unused */
696 Cardinal
*num_params
; /* unused */
698 register ScrollbarWidget w
= (ScrollbarWidget
) gw
;
700 if (w
->scrollbar
.direction
== 0) return; /* if no StartScroll */
702 if (LookAhead(gw
, event
)) return;
704 XtCallCallbacks( gw
, XtNthumbProc
, &w
->scrollbar
.top
);
705 XtCallCallbacks( gw
, XtNjumpProc
, &w
->scrollbar
.top
);
710 /************************************************************
714 ************************************************************/
716 /* Set the scroll bar to the given location. */
719 XawScrollbarSetThumb( gw
, top
, shown
)
723 ScrollbarWidget w
= (ScrollbarWidget
)gw
;
725 if (w
->scrollbar
.direction
== 'c') return; /* if still thumbing */
727 w
->scrollbar
.top
= (top
> 1.0) ? 1.0 :
731 w
->scrollbar
.shown
= (shown
> 1.0) ? 1.0 :
732 (shown
>= 0.0) ? shown
: