3 * Until FreeBSD gets their act together;
4 * http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
14 /* undefine will disable the autoadjusting of the knob dimple to be
15 * directly below the cursor
17 #undef STRICT_NEXT_BEHAVIOUR
19 #define AUTOSCROLL_INITIAL_DELAY 200
21 #define AUTOSCROLL_DELAY 40
23 char *WMScrollerDidScrollNotification
= "WMScrollerDidScrollNotification";
25 typedef struct W_Scroller
{
35 WMHandlerID timerID
; /* for continuous scrolling mode */
37 #ifndef STRICT_NEXT_BEHAVIOUR
38 int dragPoint
; /* point where the knob is being
42 WMScrollArrowPosition arrowsPosition
:4;
44 unsigned int horizontal
:1;
46 WMScrollerPart hitPart
:4;
49 unsigned int documentFullyVisible
:1; /* document is fully visible */
51 unsigned int prevSelected
:1;
53 unsigned int pushed
:1;
55 unsigned int incrDown
:1; /* whether increment button is down */
57 unsigned int decrDown
:1;
59 unsigned int draggingKnob
:1;
61 unsigned int configured
:1;
63 unsigned int redrawPending
:1;
67 #define DEFAULT_HEIGHT 60
68 #define DEFAULT_WIDTH SCROLLER_WIDTH
69 #define DEFAULT_ARROWS_POSITION WSAMinEnd
71 #define BUTTON_SIZE ((SCROLLER_WIDTH) - 4)
73 static void destroyScroller(Scroller
* sPtr
);
74 static void paintScroller(Scroller
* sPtr
);
76 static void willResizeScroller();
77 static void handleEvents(XEvent
* event
, void *data
);
78 static void handleActionEvents(XEvent
* event
, void *data
);
80 static void handleMotion(Scroller
* sPtr
, int mouseX
, int mouseY
);
82 W_ViewDelegate _ScrollerViewDelegate
= {
90 WMScroller
*WMCreateScroller(WMWidget
* parent
)
94 sPtr
= wmalloc(sizeof(Scroller
));
95 sPtr
->widgetClass
= WC_Scroller
;
97 sPtr
->view
= W_CreateView(W_VIEW(parent
));
102 sPtr
->view
->self
= sPtr
;
104 sPtr
->view
->delegate
= &_ScrollerViewDelegate
;
106 sPtr
->flags
.documentFullyVisible
= 1;
108 WMCreateEventHandler(sPtr
->view
, ExposureMask
| StructureNotifyMask
109 | ClientMessageMask
, handleEvents
, sPtr
);
111 W_ResizeView(sPtr
->view
, DEFAULT_WIDTH
, DEFAULT_WIDTH
);
112 sPtr
->flags
.arrowsPosition
= DEFAULT_ARROWS_POSITION
;
114 WMCreateEventHandler(sPtr
->view
, ButtonPressMask
| ButtonReleaseMask
115 | EnterWindowMask
| LeaveWindowMask
| ButtonMotionMask
, handleActionEvents
, sPtr
);
117 sPtr
->flags
.hitPart
= WSNoPart
;
119 sPtr
->floatValue
= 0.0;
120 sPtr
->knobProportion
= 1.0;
125 void WMSetScrollerArrowsPosition(WMScroller
* sPtr
, WMScrollArrowPosition position
)
127 sPtr
->flags
.arrowsPosition
= position
;
128 if (sPtr
->view
->flags
.realized
) {
133 static void willResizeScroller(W_ViewDelegate
* self
, WMView
* view
, unsigned int *width
, unsigned int *height
)
135 WMScroller
*sPtr
= (WMScroller
*) view
->self
;
137 if (*width
> *height
) {
138 sPtr
->flags
.horizontal
= 1;
139 *height
= SCROLLER_WIDTH
;
141 sPtr
->flags
.horizontal
= 0;
142 *width
= SCROLLER_WIDTH
;
146 void WMSetScrollerAction(WMScroller
* sPtr
, WMAction
* action
, void *clientData
)
148 CHECK_CLASS(sPtr
, WC_Scroller
);
150 sPtr
->action
= action
;
152 sPtr
->clientData
= clientData
;
155 void WMSetScrollerParameters(WMScroller
* sPtr
, float floatValue
, float knobProportion
)
158 * This value represents 1 pixel on a 4k wide screen, it makes
159 * a good minimum; this ensure a non-null value to avoid
160 * potential division-by-0.
161 * Please note that there is another size check when drawing
162 * the knob to make sure it will remain selectable.
164 static const float min_knob_proportion
= 1.0 / 4096.0;
166 CHECK_CLASS(sPtr
, WC_Scroller
);
168 assert(!isnan(floatValue
));
170 if (floatValue
< 0.0)
171 sPtr
->floatValue
= 0.0;
172 else if (floatValue
> 1.0)
173 sPtr
->floatValue
= 1.0;
175 sPtr
->floatValue
= floatValue
;
177 if (knobProportion
<= min_knob_proportion
) {
179 sPtr
->knobProportion
= min_knob_proportion
;
180 sPtr
->flags
.documentFullyVisible
= 0;
182 } else if (knobProportion
>= 1.0) {
184 sPtr
->knobProportion
= 1.0;
185 sPtr
->flags
.documentFullyVisible
= 1;
188 sPtr
->knobProportion
= knobProportion
;
189 sPtr
->flags
.documentFullyVisible
= 0;
192 if (sPtr
->view
->flags
.realized
)
195 /* WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL); */
198 float WMGetScrollerKnobProportion(WMScroller
* sPtr
)
200 CHECK_CLASS(sPtr
, WC_Scroller
);
202 return sPtr
->knobProportion
;
205 float WMGetScrollerValue(WMScroller
* sPtr
)
207 CHECK_CLASS(sPtr
, WC_Scroller
);
209 return sPtr
->floatValue
;
212 WMScrollerPart
WMGetScrollerHitPart(WMScroller
* sPtr
)
214 CHECK_CLASS(sPtr
, WC_Scroller
);
216 return sPtr
->flags
.hitPart
;
219 static void paintArrow(WMScroller
* sPtr
, Drawable d
, int part
)
221 * part- 0 paints the decrement arrow, 1 the increment arrow
224 WMView
*view
= sPtr
->view
;
225 WMScreen
*scr
= view
->screen
;
229 #ifndef DOUBLE_BUFFER
230 GC gc
= scr
->lightGC
;
233 if (part
== 0) { /* decrement button */
234 if (sPtr
->flags
.horizontal
) {
235 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
236 ofs
= view
->size
.width
- 2 * (BUTTON_SIZE
+ 1) - 1;
240 if (sPtr
->flags
.decrDown
)
241 arrow
= scr
->hiLeftArrow
;
243 arrow
= scr
->leftArrow
;
246 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
247 ofs
= view
->size
.height
- 2 * (BUTTON_SIZE
+ 1) - 1;
251 if (sPtr
->flags
.decrDown
)
252 arrow
= scr
->hiUpArrow
;
254 arrow
= scr
->upArrow
;
257 #ifndef DOUBLE_BUFFER
258 if (sPtr
->flags
.decrDown
)
259 gc
= WMColorGC(scr
->white
);
261 } else { /* increment button */
262 if (sPtr
->flags
.horizontal
) {
263 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
264 ofs
= view
->size
.width
- BUTTON_SIZE
+ 1 - 3;
266 ofs
= 2 + BUTTON_SIZE
+ 1;
268 if (sPtr
->flags
.incrDown
)
269 arrow
= scr
->hiRightArrow
;
271 arrow
= scr
->rightArrow
;
273 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
274 ofs
= view
->size
.height
- BUTTON_SIZE
+ 1 - 3;
276 ofs
= 2 + BUTTON_SIZE
+ 1;
278 if (sPtr
->flags
.incrDown
)
279 arrow
= scr
->hiDownArrow
;
281 arrow
= scr
->downArrow
;
284 #ifndef DOUBLE_BUFFER
285 if (sPtr
->flags
.incrDown
)
290 if (sPtr
->flags
.horizontal
) {
292 #ifndef DOUBLE_BUFFER
293 XFillRectangle(scr
->display
, d
, gc
, ofs
+ 1, 2 + 1, BUTTON_SIZE
+ 1 - 3, BUTTON_SIZE
- 3);
295 if ((!part
&& sPtr
->flags
.decrDown
) || (part
&& sPtr
->flags
.incrDown
))
296 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->white
),
297 ofs
+ 1, 2 + 1, BUTTON_SIZE
+ 1 - 3, BUTTON_SIZE
- 3);
298 #endif /* DOUBLE_BUFFER */
299 W_DrawRelief(scr
, d
, ofs
, 2, BUTTON_SIZE
, BUTTON_SIZE
, WRRaised
);
302 XSetClipMask(scr
->display
, scr
->clipGC
, arrow
->mask
);
303 XSetClipOrigin(scr
->display
, scr
->clipGC
,
304 ofs
+ (BUTTON_SIZE
- arrow
->width
) / 2, 2 + (BUTTON_SIZE
- arrow
->height
) / 2);
306 XCopyArea(scr
->display
, arrow
->pixmap
, d
, scr
->clipGC
,
307 0, 0, arrow
->width
, arrow
->height
,
308 ofs
+ (BUTTON_SIZE
- arrow
->width
) / 2, 2 + (BUTTON_SIZE
- arrow
->height
) / 2);
310 } else { /* vertical */
313 #ifndef DOUBLE_BUFFER
314 XFillRectangle(scr
->display
, d
, gc
, 2 + 1, ofs
+ 1, BUTTON_SIZE
- 3, BUTTON_SIZE
+ 1 - 3);
316 if ((!part
&& sPtr
->flags
.decrDown
) || (part
&& sPtr
->flags
.incrDown
))
317 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->white
),
318 2 + 1, ofs
+ 1, BUTTON_SIZE
- 3, BUTTON_SIZE
+ 1 - 3);
319 #endif /* DOUBLE_BUFFER */
320 W_DrawRelief(scr
, d
, 2, ofs
, BUTTON_SIZE
, BUTTON_SIZE
, WRRaised
);
324 XSetClipMask(scr
->display
, scr
->clipGC
, arrow
->mask
);
325 XSetClipOrigin(scr
->display
, scr
->clipGC
,
326 2 + (BUTTON_SIZE
- arrow
->width
) / 2, ofs
+ (BUTTON_SIZE
- arrow
->height
) / 2);
327 XCopyArea(scr
->display
, arrow
->pixmap
, d
, scr
->clipGC
,
328 0, 0, arrow
->width
, arrow
->height
,
329 2 + (BUTTON_SIZE
- arrow
->width
) / 2, ofs
+ (BUTTON_SIZE
- arrow
->height
) / 2);
333 static int knobLength(Scroller
* sPtr
)
337 if (sPtr
->flags
.horizontal
)
338 length
= sPtr
->view
->size
.width
- 4;
340 length
= sPtr
->view
->size
.height
- 4;
342 if (sPtr
->flags
.arrowsPosition
!= WSANone
) {
343 length
-= 2 * (BUTTON_SIZE
+ 1);
346 tmp
= (int)((float)length
* sPtr
->knobProportion
+ 0.5);
347 /* keep minimum size */
348 if (tmp
< BUTTON_SIZE
)
354 static void paintScroller(Scroller
* sPtr
)
356 WMView
*view
= sPtr
->view
;
357 WMScreen
*scr
= view
->screen
;
361 Drawable d
= view
->window
;
367 d
= XCreatePixmap(scr
->display
, view
->window
, view
->size
.width
, view
->size
.height
, scr
->depth
);
368 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->gray
), 0, 0, view
->size
.width
, view
->size
.height
);
371 XDrawRectangle(scr
->display
, d
, WMColorGC(scr
->black
), 0, 0, view
->size
.width
- 1, view
->size
.height
- 1);
372 #ifndef DOUBLE_BUFFER
373 XDrawRectangle(scr
->display
, d
, WMColorGC(scr
->gray
), 1, 1, view
->size
.width
- 3, view
->size
.height
- 3);
376 if (sPtr
->flags
.horizontal
)
377 length
= view
->size
.width
- 4;
379 length
= view
->size
.height
- 4;
381 if (sPtr
->flags
.documentFullyVisible
) {
382 XFillRectangle(scr
->display
, d
, scr
->stippleGC
, 2, 2, view
->size
.width
- 4, view
->size
.height
- 4);
385 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
386 length
-= (BUTTON_SIZE
+ 1) * 2;
387 } else if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
) {
388 ofs
+= (BUTTON_SIZE
+ 1) * 2;
389 length
-= (BUTTON_SIZE
+ 1) * 2;
392 knobL
= (float)knobLength(sPtr
);
394 knobP
= sPtr
->floatValue
* ((float)length
- knobL
);
396 if (sPtr
->flags
.horizontal
) {
398 XFillRectangle(scr
->display
, d
, scr
->stippleGC
, ofs
, 2, (int)knobP
, view
->size
.height
- 4);
401 #ifndef DOUBLE_BUFFER
402 XFillRectangle(scr
->display
, d
, scr
->lightGC
,
403 ofs
+ (int)knobP
+ 2, 2 + 2, (int)knobL
- 4, view
->size
.height
- 4 - 4);
405 W_DrawRelief(scr
, d
, ofs
+ (int)knobP
, 2, (int)knobL
, view
->size
.height
- 4, WRRaised
);
407 XCopyArea(scr
->display
, scr
->scrollerDimple
->pixmap
, d
,
409 scr
->scrollerDimple
->width
, scr
->scrollerDimple
->height
,
410 ofs
+ (int)knobP
+ ((int)knobL
- scr
->scrollerDimple
->width
- 1) / 2,
411 (view
->size
.height
- scr
->scrollerDimple
->height
- 1) / 2);
414 if ((int)(knobP
+ knobL
) < length
)
415 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
416 ofs
+ (int)(knobP
+ knobL
), 2,
417 length
- (int)(knobP
+ knobL
), view
->size
.height
- 4);
421 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
422 2, ofs
, view
->size
.width
- 4, (int)knobP
);
425 #ifndef DOUBLE_BUFFER
426 XFillRectangle(scr
->display
, d
, scr
->lightGC
,
427 2 + 2, ofs
+ (int)knobP
+ 2, view
->size
.width
- 4 - 4, (int)knobL
- 4);
429 XCopyArea(scr
->display
, scr
->scrollerDimple
->pixmap
, d
,
431 scr
->scrollerDimple
->width
, scr
->scrollerDimple
->height
,
432 (view
->size
.width
- scr
->scrollerDimple
->width
- 1) / 2,
433 ofs
+ (int)knobP
+ ((int)knobL
- scr
->scrollerDimple
->height
- 1) / 2);
435 W_DrawRelief(scr
, d
, 2, ofs
+ (int)knobP
, view
->size
.width
- 4, (int)knobL
, WRRaised
);
438 if ((int)(knobP
+ knobL
) < length
)
439 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
440 2, ofs
+ (int)(knobP
+ knobL
),
441 view
->size
.width
- 4, length
- (int)(knobP
+ knobL
));
444 if (sPtr
->flags
.arrowsPosition
!= WSANone
) {
445 paintArrow(sPtr
, d
, 0);
446 paintArrow(sPtr
, d
, 1);
451 XCopyArea(scr
->display
, d
, view
->window
, scr
->copyGC
, 0, 0, view
->size
.width
, view
->size
.height
, 0, 0);
452 XFreePixmap(scr
->display
, d
);
456 static void handleEvents(XEvent
* event
, void *data
)
458 Scroller
*sPtr
= (Scroller
*) data
;
460 CHECK_CLASS(data
, WC_Scroller
);
462 switch (event
->type
) {
464 if (event
->xexpose
.count
== 0)
469 destroyScroller(sPtr
);
475 * locatePointInScroller-
476 * Return the part of the scroller where the point is located.
478 static WMScrollerPart
locatePointInScroller(Scroller
* sPtr
, int x
, int y
, int alternate
)
480 int width
= sPtr
->view
->size
.width
;
481 int height
= sPtr
->view
->size
.height
;
482 int c
, p1
, p2
, p3
, p4
, p5
, p6
;
485 /* if there is no knob... */
486 if (sPtr
->flags
.documentFullyVisible
)
489 if (sPtr
->flags
.horizontal
)
495 * | | |###########| |#####| | |
496 * | < | > |###########| O |#####| < | > |
497 * | | |###########| |#####| | |
500 if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
) {
504 if (sPtr
->flags
.horizontal
) {
512 } else if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
513 if (sPtr
->flags
.horizontal
) {
527 if (sPtr
->flags
.horizontal
) {
528 slotL
= p5
= p6
= width
;
530 slotL
= p5
= p6
= height
;
534 knobL
= knobLength(sPtr
);
535 p3
= p2
+ (int)((float)(slotL
- knobL
) * sPtr
->floatValue
);
538 /* uses a mix of the NS and Win ways of doing scroll page */
540 return alternate
? WSDecrementPage
: WSDecrementLine
;
542 return alternate
? WSIncrementPage
: WSIncrementLine
;
544 return WSDecrementPage
;
548 return WSIncrementPage
;
550 return alternate
? WSDecrementPage
: WSDecrementLine
;
552 return alternate
? WSIncrementPage
: WSIncrementLine
;
555 static void handlePush(Scroller
* sPtr
, int pushX
, int pushY
, int alternate
)
560 part
= locatePointInScroller(sPtr
, pushX
, pushY
, alternate
);
562 sPtr
->flags
.hitPart
= part
;
565 case WSIncrementLine
:
566 sPtr
->flags
.incrDown
= 1;
570 case WSIncrementPage
:
574 case WSDecrementLine
:
575 sPtr
->flags
.decrDown
= 1;
579 case WSDecrementPage
:
584 sPtr
->flags
.draggingKnob
= 1;
585 #ifndef STRICT_NEXT_BEHAVIOUR
586 if (sPtr
->flags
.horizontal
)
587 sPtr
->dragPoint
= pushX
;
589 sPtr
->dragPoint
= pushY
;
595 if (sPtr
->flags
.arrowsPosition
!= WSANone
)
596 buttonsLen
= 2 * (BUTTON_SIZE
+ 1);
600 if (sPtr
->flags
.horizontal
)
601 length
= sPtr
->view
->size
.width
- 4 - buttonsLen
;
603 length
= sPtr
->view
->size
.height
- 4 - buttonsLen
;
605 knobP
= (int)(sPtr
->floatValue
* (float)(length
- knobLength(sPtr
)));
607 if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
)
608 sPtr
->dragPoint
-= 2 + buttonsLen
+ knobP
;
610 sPtr
->dragPoint
-= 2 + knobP
;
612 #endif /* STRICT_NEXT_BEHAVIOUR */
613 /* This does not seem necesary here since we don't know yet if the
614 * knob will be dragged later. -Dan
615 handleMotion(sPtr, pushX, pushY); */
618 case WSDecrementWheel
:
619 case WSIncrementWheel
:
626 if (doAction
&& sPtr
->action
) {
627 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
629 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
633 static float floatValueForPoint(Scroller
* sPtr
, int point
)
635 float floatValue
= 0;
637 int slotOfs
, slotLength
, knobL
;
639 if (sPtr
->flags
.horizontal
)
640 slotLength
= sPtr
->view
->size
.width
- 4;
642 slotLength
= sPtr
->view
->size
.height
- 4;
645 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
646 slotLength
-= (BUTTON_SIZE
+ 1) * 2;
647 } else if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
) {
648 slotOfs
+= (BUTTON_SIZE
+ 1) * 2;
649 slotLength
-= (BUTTON_SIZE
+ 1) * 2;
652 knobL
= (float)knobLength(sPtr
);
653 #ifdef STRICT_NEXT_BEHAVIOUR
654 if (point
< slotOfs
+ knobL
/ 2)
655 position
= (float)(slotOfs
+ knobL
/ 2);
656 else if (point
> slotOfs
+ slotLength
- knobL
/ 2)
657 position
= (float)(slotOfs
+ slotLength
- knobL
/ 2);
659 position
= (float)point
;
661 floatValue
= (position
- (float)(slotOfs
+ slotLength
/ 2))
662 / (float)(slotLength
- knobL
);
664 /* Adjust the last point to lie inside the knob slot */
666 position
= (float)slotOfs
;
667 else if (point
> slotOfs
+ slotLength
)
668 position
= (float)(slotOfs
+ slotLength
);
670 position
= (float)point
;
672 /* Compute the float value */
673 floatValue
= (position
- (float)slotOfs
) / (float)(slotLength
- knobL
);
676 assert(!isnan(floatValue
));
680 static void handleMotion(Scroller
* sPtr
, int mouseX
, int mouseY
)
682 if (sPtr
->flags
.draggingKnob
) {
686 if (sPtr
->flags
.horizontal
) {
692 #ifndef STRICT_NEXT_BEHAVIOUR
693 point
-= sPtr
->dragPoint
;
696 newFloatValue
= floatValueForPoint(sPtr
, point
);
697 WMSetScrollerParameters(sPtr
, newFloatValue
, sPtr
->knobProportion
);
699 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
700 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
705 part
= locatePointInScroller(sPtr
, mouseX
, mouseY
, False
);
707 sPtr
->flags
.hitPart
= part
;
709 if (part
== WSIncrementLine
&& sPtr
->flags
.decrDown
) {
710 sPtr
->flags
.decrDown
= 0;
711 sPtr
->flags
.incrDown
= 1;
712 } else if (part
== WSDecrementLine
&& sPtr
->flags
.incrDown
) {
713 sPtr
->flags
.incrDown
= 0;
714 sPtr
->flags
.decrDown
= 1;
715 } else if (part
!= WSIncrementLine
&& part
!= WSDecrementLine
) {
716 sPtr
->flags
.incrDown
= 0;
717 sPtr
->flags
.decrDown
= 0;
722 static void autoScroll(void *clientData
)
724 Scroller
*sPtr
= (Scroller
*) clientData
;
727 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
728 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
730 sPtr
->timerID
= WMAddTimerHandler(AUTOSCROLL_DELAY
, autoScroll
, clientData
);
733 static void handleActionEvents(XEvent
* event
, void *data
)
735 Scroller
*sPtr
= (Scroller
*) data
;
736 int wheelDecrement
, wheelIncrement
;
739 /* check if we're really dealing with a scroller, as something
740 * might have gone wrong in the event dispatching stuff */
741 CHECK_CLASS(sPtr
, WC_Scroller
);
743 id
= sPtr
->flags
.incrDown
;
744 dd
= sPtr
->flags
.decrDown
;
746 switch (event
->type
) {
752 WMDeleteTimerHandler(sPtr
->timerID
);
753 sPtr
->timerID
= NULL
;
755 sPtr
->flags
.incrDown
= 0;
756 sPtr
->flags
.decrDown
= 0;
760 /* FIXME: change Mod1Mask with something else */
761 if (sPtr
->flags
.documentFullyVisible
)
764 if (sPtr
->flags
.horizontal
) {
765 wheelDecrement
= WINGsConfiguration
.mouseWheelDown
;
766 wheelIncrement
= WINGsConfiguration
.mouseWheelUp
;
768 wheelDecrement
= WINGsConfiguration
.mouseWheelUp
;
769 wheelIncrement
= WINGsConfiguration
.mouseWheelDown
;
772 if (event
->xbutton
.button
== wheelDecrement
) {
773 if (event
->xbutton
.state
& ControlMask
) {
774 sPtr
->flags
.hitPart
= WSDecrementPage
;
775 } else if (event
->xbutton
.state
& ShiftMask
) {
776 sPtr
->flags
.hitPart
= WSDecrementLine
;
778 sPtr
->flags
.hitPart
= WSDecrementWheel
;
781 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
782 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
784 } else if (event
->xbutton
.button
== wheelIncrement
) {
785 if (event
->xbutton
.state
& ControlMask
) {
786 sPtr
->flags
.hitPart
= WSIncrementPage
;
787 } else if (event
->xbutton
.state
& ShiftMask
) {
788 sPtr
->flags
.hitPart
= WSIncrementLine
;
790 sPtr
->flags
.hitPart
= WSIncrementWheel
;
793 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
794 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
797 handlePush(sPtr
, event
->xbutton
.x
, event
->xbutton
.y
, (event
->xbutton
.state
& Mod1Mask
)
798 || event
->xbutton
.button
== Button2
);
799 /* continue scrolling if pushed on the buttons */
800 if (sPtr
->flags
.hitPart
== WSIncrementLine
|| sPtr
->flags
.hitPart
== WSDecrementLine
) {
801 sPtr
->timerID
= WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY
, autoScroll
, sPtr
);
807 if (sPtr
->flags
.draggingKnob
) {
809 (*sPtr
->action
) (sPtr
, sPtr
->clientData
);
810 WMPostNotificationName(WMScrollerDidScrollNotification
, sPtr
, NULL
);
814 WMDeleteTimerHandler(sPtr
->timerID
);
815 sPtr
->timerID
= NULL
;
817 sPtr
->flags
.incrDown
= 0;
818 sPtr
->flags
.decrDown
= 0;
819 sPtr
->flags
.draggingKnob
= 0;
823 handleMotion(sPtr
, event
->xbutton
.x
, event
->xbutton
.y
);
824 if (sPtr
->timerID
&& sPtr
->flags
.hitPart
!= WSIncrementLine
825 && sPtr
->flags
.hitPart
!= WSDecrementLine
) {
826 WMDeleteTimerHandler(sPtr
->timerID
);
827 sPtr
->timerID
= NULL
;
831 if (id
!= sPtr
->flags
.incrDown
|| dd
!= sPtr
->flags
.decrDown
)
835 static void destroyScroller(Scroller
* sPtr
)
837 /* we don't want autoscroll try to scroll a freed widget */
839 WMDeleteTimerHandler(sPtr
->timerID
);