7 /* undefine will disable the autoadjusting of the knob dimple to be
8 * directly below the cursor
10 #undef STRICT_NEXT_BEHAVIOUR
12 #define AUTOSCROLL_INITIAL_DELAY 200
14 #define AUTOSCROLL_DELAY 40
17 typedef struct W_Scroller
{
27 WMHandlerID timerID
; /* for continuous scrolling mode */
29 #ifndef STRICT_NEXT_BEHAVIOUR
30 int dragPoint
; /* point where the knob is being
34 WMScrollArrowPosition arrowsPosition
:3;
36 unsigned int horizontal
:1;
38 WMScrollerPart hitPart
:3;
41 unsigned int documentFullyVisible
:1; /* document is fully visible */
43 unsigned int prevSelected
:1;
45 unsigned int pushed
:1;
47 unsigned int incrDown
:1; /* whether increment button is down */
49 unsigned int decrDown
:1;
51 unsigned int draggingKnob
:1;
53 unsigned int configured
:1;
55 unsigned int redrawPending
:1;
61 #define DEFAULT_HEIGHT 60
62 #define DEFAULT_WIDTH SCROLLER_WIDTH
63 #define DEFAULT_ARROWS_POSITION WSAMinEnd
67 static void destroyScroller(Scroller
*sPtr
);
68 static void paintScroller(Scroller
*sPtr
);
70 static void resizeScroller();
71 static void handleEvents(XEvent
*event
, void *data
);
72 static void handleActionEvents(XEvent
*event
, void *data
);
74 static void handleMotion(Scroller
*sPtr
, int mouseX
, int mouseY
);
77 W_ViewProcedureTable _ScrollerViewProcedures
= {
87 WMCreateScroller(WMWidget
*parent
)
91 sPtr
= wmalloc(sizeof(Scroller
));
92 memset(sPtr
, 0, sizeof(Scroller
));
94 sPtr
->widgetClass
= WC_Scroller
;
96 sPtr
->view
= W_CreateView(W_VIEW(parent
));
101 sPtr
->view
->self
= sPtr
;
103 sPtr
->flags
.documentFullyVisible
= 1;
105 WMCreateEventHandler(sPtr
->view
, ExposureMask
|StructureNotifyMask
106 |ClientMessageMask
, handleEvents
, sPtr
);
108 resizeScroller(sPtr
, DEFAULT_WIDTH
, DEFAULT_WIDTH
);
109 sPtr
->flags
.arrowsPosition
= DEFAULT_ARROWS_POSITION
;
111 WMCreateEventHandler(sPtr
->view
, ButtonPressMask
|ButtonReleaseMask
112 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
113 handleActionEvents
, sPtr
);
115 sPtr
->flags
.hitPart
= WSNoPart
;
123 WMSetScrollerArrowsPosition(WMScroller
*sPtr
, WMScrollArrowPosition position
)
125 sPtr
->flags
.arrowsPosition
= position
;
126 if (sPtr
->view
->flags
.realized
) {
133 resizeScroller(WMScroller
*sPtr
, unsigned int width
, unsigned int height
)
135 if (width
> height
) {
136 sPtr
->flags
.horizontal
= 1;
137 W_ResizeView(sPtr
->view
, width
, SCROLLER_WIDTH
);
139 sPtr
->flags
.horizontal
= 0;
140 W_ResizeView(sPtr
->view
, SCROLLER_WIDTH
, height
);
142 if (sPtr
->view
->flags
.realized
) {
149 WMSetScrollerAction(WMScroller
*sPtr
, WMAction
*action
, void *clientData
)
151 CHECK_CLASS(sPtr
, WC_Scroller
);
153 sPtr
->action
= action
;
155 sPtr
->clientData
= clientData
;
160 WMSetScrollerParameters(WMScroller
*sPtr
, float floatValue
,
161 float knobProportion
)
163 CHECK_CLASS(sPtr
, WC_Scroller
);
165 if (floatValue
< 0.0)
166 sPtr
->floatValue
= 0.0;
167 else if (floatValue
> 1.0)
168 sPtr
->floatValue
= 1.0;
170 sPtr
->floatValue
= floatValue
;
172 if (knobProportion
<= 0.0) {
174 sPtr
->knobProportion
= 0.0;
175 sPtr
->flags
.documentFullyVisible
= 0;
177 } else if (knobProportion
>= 1.0) {
179 sPtr
->knobProportion
= 1.0;
180 sPtr
->flags
.documentFullyVisible
= 1;
183 sPtr
->knobProportion
= knobProportion
;
184 sPtr
->flags
.documentFullyVisible
= 0;
187 if (sPtr
->view
->flags
.realized
)
193 WMGetScrollerKnobProportion(WMScroller
*sPtr
)
195 CHECK_CLASS(sPtr
, WC_Scroller
);
197 return sPtr
->knobProportion
;
202 WMGetScrollerValue(WMScroller
*sPtr
)
204 CHECK_CLASS(sPtr
, WC_Scroller
);
206 return sPtr
->floatValue
;
211 WMGetScrollerHitPart(WMScroller
*sPtr
)
213 CHECK_CLASS(sPtr
, WC_Scroller
);
215 return sPtr
->flags
.hitPart
;
220 paintArrow(WMScroller
*sPtr
, Drawable d
, int part
)
222 * part- 0 paints the decrement arrow, 1 the increment arrow
225 WMView
*view
= sPtr
->view
;
226 WMScreen
*scr
= view
->screen
;
230 #ifndef DOUBLE_BUFFER
231 GC gc
= scr
->lightGC
;
234 bsize
= SCROLLER_WIDTH
- 4;
237 if (part
== 0) { /* decrement button */
238 if (sPtr
->flags
.horizontal
) {
239 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
240 ofs
= view
->size
.width
- 2*(bsize
+1) - 1;
244 if (sPtr
->flags
.decrDown
)
245 arrow
= scr
->hiLeftArrow
;
247 arrow
= scr
->leftArrow
;
250 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
251 ofs
= view
->size
.height
- 2*(bsize
+1) - 1;
255 if (sPtr
->flags
.decrDown
)
256 arrow
= scr
->hiUpArrow
;
258 arrow
= scr
->upArrow
;
261 #ifndef DOUBLE_BUFFER
262 if (sPtr
->flags
.decrDown
)
263 gc
= WMColorGC(scr
->white
);
265 } else { /* increment button */
266 if (sPtr
->flags
.horizontal
) {
267 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
268 ofs
= view
->size
.width
- bsize
+1 - 3;
272 if (sPtr
->flags
.incrDown
)
273 arrow
= scr
->hiRightArrow
;
275 arrow
= scr
->rightArrow
;
277 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
278 ofs
= view
->size
.height
- bsize
+1 - 3;
282 if (sPtr
->flags
.incrDown
)
283 arrow
= scr
->hiDownArrow
;
285 arrow
= scr
->downArrow
;
288 #ifndef DOUBLE_BUFFER
289 if (sPtr
->flags
.incrDown
)
295 if (sPtr
->flags
.horizontal
) {
298 #ifndef DOUBLE_BUFFER
299 XFillRectangle(scr
->display
, d
, gc
,
300 ofs
+1, 2+1, bsize
+1-3, bsize
-3);
302 if ((!part
&&sPtr
->flags
.decrDown
) || (part
&&sPtr
->flags
.incrDown
))
303 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->white
),
304 ofs
+1, 2+1, bsize
+1-3, bsize
-3);
305 #endif /* DOUBLE_BUFFER */
306 W_DrawRelief(scr
, d
, ofs
, 2, bsize
, bsize
, WRRaised
);
309 XSetClipMask(scr
->display
, scr
->clipGC
, arrow
->mask
);
310 XSetClipOrigin(scr
->display
, scr
->clipGC
,
311 ofs
+ (bsize
- arrow
->width
) / 2,
312 2 + (bsize
- arrow
->height
) / 2);
314 XCopyArea(scr
->display
, arrow
->pixmap
, d
, scr
->clipGC
,
315 0, 0, arrow
->width
, arrow
->height
,
316 ofs
+ (bsize
- arrow
->width
) / 2,
317 2 + (bsize
- arrow
->height
) / 2);
319 } else { /* vertical */
322 #ifndef DOUBLE_BUFFER
323 XFillRectangle(scr
->display
, d
, gc
,
324 2+1, ofs
+1, bsize
-3, bsize
+1-3);
326 if ((!part
&&sPtr
->flags
.decrDown
) || (part
&&sPtr
->flags
.incrDown
))
327 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->white
),
328 2+1, ofs
+1, bsize
-3, bsize
+1-3);
329 #endif /* DOUBLE_BUFFER */
330 W_DrawRelief(scr
, d
, 2, ofs
, bsize
, bsize
, WRRaised
);
334 XSetClipMask(scr
->display
, scr
->clipGC
, arrow
->mask
);
335 XSetClipOrigin(scr
->display
, scr
->clipGC
,
336 2 + (bsize
- arrow
->width
) / 2,
337 ofs
+ (bsize
- arrow
->height
) / 2);
338 XCopyArea(scr
->display
, arrow
->pixmap
, d
, scr
->clipGC
,
339 0, 0, arrow
->width
, arrow
->height
,
340 2 + (bsize
- arrow
->width
) / 2,
341 ofs
+ (bsize
- arrow
->height
) / 2);
347 knobLength(Scroller
*sPtr
)
352 if (sPtr
->flags
.horizontal
)
353 length
= sPtr
->view
->size
.width
- 4;
355 length
= sPtr
->view
->size
.height
- 4;
357 if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
358 length
-= (SCROLLER_WIDTH
- 4 + 1)*2;
359 } else if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
) {
360 length
-= (SCROLLER_WIDTH
- 4 + 1)*2;
363 tmp
= (int)((float)length
* sPtr
->knobProportion
+ 0.5);
364 /* keep minimum size */
365 if (tmp
< SCROLLER_WIDTH
-4)
366 tmp
= SCROLLER_WIDTH
-4;
373 paintScroller(Scroller
*sPtr
)
375 WMView
*view
= sPtr
->view
;
376 WMScreen
*scr
= view
->screen
;
380 Drawable d
= view
->window
;
387 d
= XCreatePixmap(scr
->display
, view
->window
, view
->size
.width
,
388 view
->size
.height
, scr
->depth
);
389 XFillRectangle(scr
->display
, d
, WMColorGC(scr
->gray
), 0, 0,
390 view
->size
.width
, view
->size
.height
);
393 XDrawRectangle(scr
->display
, d
, WMColorGC(scr
->black
), 0, 0,
394 view
->size
.width
-1, view
->size
.height
-1);
395 #ifndef DOUBLE_BUFFER
396 XDrawRectangle(scr
->display
, d
, WMColorGC(scr
->gray
), 1, 1,
397 view
->size
.width
-3, view
->size
.height
-3);
400 if (sPtr
->flags
.horizontal
)
401 length
= view
->size
.width
- 4;
403 length
= view
->size
.height
- 4;
405 if (sPtr
->flags
.documentFullyVisible
) {
406 XFillRectangle(scr
->display
, d
, scr
->stippleGC
, 2, 2,
407 view
->size
.width
-4, view
->size
.height
-4);
409 if (sPtr
->flags
.arrowsPosition
==WSAMaxEnd
) {
411 length
-= (SCROLLER_WIDTH
- 4 + 1)*2;
412 } else if (sPtr
->flags
.arrowsPosition
==WSAMinEnd
) {
413 ofs
= (SCROLLER_WIDTH
- 4 + 1)*2;
414 length
-= (SCROLLER_WIDTH
- 4 + 1)*2;
419 knobL
= (float)knobLength(sPtr
);
421 knobP
= sPtr
->floatValue
* ((float)length
- knobL
);
424 if (sPtr
->flags
.horizontal
) {
426 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
427 ofs
+2, 2, (int)knobP
, view
->size
.height
-4);
430 #ifndef DOUBLE_BUFFER
431 XFillRectangle(scr
->display
, d
, scr
->lightGC
,
432 ofs
+2+(int)knobP
+2, 2+2, (int)knobL
-4,
433 view
->size
.height
-4-4);
435 W_DrawRelief(scr
, d
, ofs
+2+(int)knobP
, 2, (int)knobL
,
436 view
->size
.height
-4, WRRaised
);
438 XCopyArea(scr
->display
, scr
->scrollerDimple
->pixmap
, d
,
440 scr
->scrollerDimple
->width
, scr
->scrollerDimple
->height
,
441 ofs
+2+(int)knobP
+((int)knobL
-scr
->scrollerDimple
->width
-1)/2,
442 (view
->size
.height
-scr
->scrollerDimple
->height
-1)/2);
445 if ((int)(knobP
+knobL
) < length
)
446 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
447 ofs
+2+(int)(knobP
+knobL
), 2,
448 length
-(int)(knobP
+knobL
),
449 view
->size
.height
-4);
453 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
454 2, ofs
+2, view
->size
.width
-4, (int)knobP
);
457 #ifndef DOUBLE_BUFFER
458 XFillRectangle(scr
->display
, d
, scr
->lightGC
,
459 2+2, ofs
+2+(int)knobP
+2,
460 view
->size
.width
-4-4, (int)knobL
-4);
462 XCopyArea(scr
->display
, scr
->scrollerDimple
->pixmap
, d
,
464 scr
->scrollerDimple
->width
, scr
->scrollerDimple
->height
,
465 (view
->size
.width
-scr
->scrollerDimple
->width
-1)/2,
466 ofs
+2+(int)knobP
+((int)knobL
-scr
->scrollerDimple
->height
-1)/2);
468 W_DrawRelief(scr
, d
, 2, ofs
+2+(int)knobP
,
469 view
->size
.width
-4, (int)knobL
, WRRaised
);
472 if ((int)(knobP
+knobL
) < length
)
473 XFillRectangle(scr
->display
, d
, scr
->stippleGC
,
474 2, ofs
+2+(int)(knobP
+knobL
),
476 length
-(int)(knobP
+knobL
));
479 if (sPtr
->flags
.arrowsPosition
!= WSANone
) {
480 paintArrow(sPtr
, d
, 0);
481 paintArrow(sPtr
, d
, 1);
486 XCopyArea(scr
->display
, d
, view
->window
, scr
->copyGC
, 0, 0,
487 view
->size
.width
, view
->size
.height
, 0, 0);
488 XFreePixmap(scr
->display
, d
);
495 handleEvents(XEvent
*event
, void *data
)
497 Scroller
*sPtr
= (Scroller
*)data
;
499 CHECK_CLASS(data
, WC_Scroller
);
502 switch (event
->type
) {
504 if (event
->xexpose
.count
==0)
509 destroyScroller(sPtr
);
517 * locatePointInScroller-
518 * Return the part of the scroller where the point is located.
520 static WMScrollerPart
521 locatePointInScroller(Scroller
*sPtr
, int x
, int y
, int alternate
)
523 int width
= sPtr
->view
->size
.width
;
524 int height
= sPtr
->view
->size
.height
;
525 int c
, p1
, p2
, p3
, p4
, p5
, p6
;
529 /* if there is no knob... */
530 if (sPtr
->flags
.documentFullyVisible
)
533 if (sPtr
->flags
.horizontal
)
539 * | | |###########| |#####| | |
540 * | < | > |###########| O |#####| < | > |
541 * | | |###########| |#####| | |
544 if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
) {
548 if (sPtr
->flags
.horizontal
) {
556 } else if (sPtr
->flags
.arrowsPosition
== WSAMaxEnd
) {
557 if (sPtr
->flags
.horizontal
) {
571 if (sPtr
->flags
.horizontal
) {
572 slotL
= p5
= p6
= width
;
574 slotL
= p5
= p6
= height
;
578 knobL
= knobLength(sPtr
);
579 p3
= p2
+ (int)((float)(slotL
-knobL
) * sPtr
->floatValue
);
582 /* uses a mix of the NS and Win ways of doing scroll page */
584 return alternate
? WSDecrementPage
: WSDecrementLine
;
586 return alternate
? WSIncrementPage
: WSIncrementLine
;
588 return WSDecrementPage
;
592 return WSIncrementPage
;
594 return alternate
? WSDecrementPage
: WSDecrementLine
;
596 return alternate
? WSIncrementPage
: WSIncrementLine
;
602 handlePush(Scroller
*sPtr
, int pushX
, int pushY
, int alternate
)
607 part
= locatePointInScroller(sPtr
, pushX
, pushY
, alternate
);
609 sPtr
->flags
.hitPart
= part
;
612 case WSIncrementLine
:
613 sPtr
->flags
.incrDown
= 1;
617 case WSIncrementPage
:
621 case WSDecrementLine
:
622 sPtr
->flags
.decrDown
= 1;
626 case WSDecrementPage
:
631 sPtr
->flags
.draggingKnob
= 1;
632 #ifndef STRICT_NEXT_BEHAVIOUR
633 if (sPtr
->flags
.horizontal
)
634 sPtr
->dragPoint
= pushX
;
636 sPtr
->dragPoint
= pushY
;
639 int noButtons
= (sPtr
->flags
.arrowsPosition
== WSANone
);
642 if (sPtr
->flags
.horizontal
)
643 length
= sPtr
->view
->size
.width
- 4;
645 length
= sPtr
->view
->size
.height
- 4;
650 knobP
= (int)(sPtr
->floatValue
* (float)(length
-knobLength(sPtr
)));
652 if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
)
653 sPtr
->dragPoint
-= 2 + (noButtons
? 0 : 36) + knobP
;
655 sPtr
->dragPoint
-= 2 + knobP
;
657 #endif /* STRICT_NEXT_BEHAVIOUR */
658 handleMotion(sPtr
, pushX
, pushY
);
667 if (doAction
&& sPtr
->action
) {
668 (*sPtr
->action
)(sPtr
, sPtr
->clientData
);
674 floatValueForPoint(int slotOfs
, int slotLength
, int knobLength
, int point
)
676 float floatValue
= 0;
679 #ifdef STRICT_NEXT_BEHAVIOUR
680 if (point
< slotOfs
+ knobLength
/2)
681 position
= (float)(slotOfs
+ knobLength
/2);
682 else if (point
> slotOfs
+ slotLength
- knobLength
/2)
683 position
= (float)(slotOfs
+ slotLength
- knobLength
/2);
685 position
= (float)point
;
687 floatValue
= (position
-(float)(slotOfs
+slotLength
/2))
688 /(float)(slotLength
-knobLength
);
690 /* Adjust the last point to lie inside the knob slot */
692 position
= (float)slotOfs
;
693 else if (point
> slotOfs
+ slotLength
)
694 position
= (float)(slotOfs
+ slotLength
);
696 position
= (float)point
;
698 /* Compute the float value */
699 floatValue
= (position
-(float)slotOfs
) / (float)(slotLength
-knobLength
);
707 handleMotion(Scroller
*sPtr
, int mouseX
, int mouseY
)
711 int noButtons
= (sPtr
->flags
.arrowsPosition
== WSANone
);
713 if (sPtr
->flags
.arrowsPosition
== WSAMinEnd
)
714 slotOffset
= 2 + (noButtons
? 0 : 36);
718 if (sPtr
->flags
.draggingKnob
) {
720 #ifdef STRICT_NEXT_BEHAVIOUR
721 if (sPtr
->flags
.horizontal
) {
722 slotLength
= sPtr
->view
->size
.width
-4-(noButtons
? 0 : 36);
723 newFloatValue
= floatValueForPoint(slotOffset
, slotLength
,
724 (int)(slotLength
*sPtr
->knobProportion
),
727 slotLength
= sPtr
->view
->size
.height
-4-(noButtons
? 0 : 36);
728 newFloatValue
= floatValueForPoint(slotOffset
, slotLength
,
729 (int)(slotLength
*sPtr
->knobProportion
),
733 if (sPtr
->flags
.horizontal
) {
734 slotLength
= sPtr
->view
->size
.width
-4-(noButtons
? 0 : 36);
735 newFloatValue
= floatValueForPoint(slotOffset
, slotLength
,
736 (int)(slotLength
*sPtr
->knobProportion
),
737 mouseX
-sPtr
->dragPoint
);
739 slotLength
= sPtr
->view
->size
.height
-4-(noButtons
? 0 : 36);
740 newFloatValue
= floatValueForPoint(slotOffset
, slotLength
,
741 (int)(slotLength
*sPtr
->knobProportion
),
742 mouseY
-sPtr
->dragPoint
);
744 #endif /* !STRICT_NEXT_BEHAVIOUR */
745 WMSetScrollerParameters(sPtr
, newFloatValue
, sPtr
->knobProportion
);
747 (*sPtr
->action
)(sPtr
, sPtr
->clientData
);
752 part
= locatePointInScroller(sPtr
, mouseX
, mouseY
, False
);
754 sPtr
->flags
.hitPart
= part
;
756 if (part
== WSIncrementLine
&& sPtr
->flags
.decrDown
) {
757 sPtr
->flags
.decrDown
= 0;
758 sPtr
->flags
.incrDown
= 1;
759 } else if (part
== WSDecrementLine
&& sPtr
->flags
.incrDown
) {
760 sPtr
->flags
.incrDown
= 0;
761 sPtr
->flags
.decrDown
= 1;
762 } else if (part
!= WSIncrementLine
&& part
!= WSDecrementLine
) {
763 sPtr
->flags
.incrDown
= 0;
764 sPtr
->flags
.decrDown
= 0;
771 autoScroll(void *clientData
)
773 Scroller
*sPtr
= (Scroller
*)clientData
;
776 (*sPtr
->action
)(sPtr
, sPtr
->clientData
);
778 sPtr
->timerID
= WMAddTimerHandler(AUTOSCROLL_DELAY
, autoScroll
, clientData
);
783 handleActionEvents(XEvent
*event
, void *data
)
785 Scroller
*sPtr
= (Scroller
*)data
;
789 /* check if we're really dealing with a scroller, as something
790 * might have gone wrong in the event dispatching stuff */
791 CHECK_CLASS(sPtr
, WC_Scroller
);
793 id
= sPtr
->flags
.incrDown
;
794 dd
= sPtr
->flags
.decrDown
;
796 switch (event
->type
) {
803 WMDeleteTimerHandler(sPtr
->timerID
);
804 sPtr
->timerID
= NULL
;
806 sPtr
->flags
.incrDown
= 0;
807 sPtr
->flags
.decrDown
= 0;
811 /* FIXME: change Mod1Mask with something else */
812 handlePush(sPtr
, event
->xbutton
.x
, event
->xbutton
.y
,
813 (event
->xbutton
.state
& Mod1Mask
)
814 ||event
->xbutton
.button
==Button2
);
815 /* continue scrolling if pushed on the buttons */
816 if (sPtr
->flags
.hitPart
== WSIncrementLine
817 || sPtr
->flags
.hitPart
== WSDecrementLine
) {
818 sPtr
->timerID
= WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY
,
824 if (sPtr
->flags
.draggingKnob
) {
826 (*sPtr
->action
)(sPtr
, sPtr
->clientData
);
830 WMDeleteTimerHandler(sPtr
->timerID
);
831 sPtr
->timerID
= NULL
;
833 sPtr
->flags
.incrDown
= 0;
834 sPtr
->flags
.decrDown
= 0;
835 sPtr
->flags
.draggingKnob
= 0;
839 handleMotion(sPtr
, event
->xbutton
.x
, event
->xbutton
.y
);
840 if (sPtr
->timerID
&& sPtr
->flags
.hitPart
!= WSIncrementLine
841 && sPtr
->flags
.hitPart
!= WSDecrementLine
) {
842 WMDeleteTimerHandler(sPtr
->timerID
);
843 sPtr
->timerID
= NULL
;
847 if (id
!= sPtr
->flags
.incrDown
|| dd
!= sPtr
->flags
.decrDown
)
854 destroyScroller(Scroller
*sPtr
)
856 /* we don't want autoscroll try to scroll a freed widget */
858 WMDeleteTimerHandler(sPtr
->timerID
);