WINGs: Changed the minimum internal knob size of WScroller
[wmaker-crm.git] / WINGs / wscroller.c
blobe6d2cd3c243d358aad89d0645c99514ffb418177
2 /*
3 * Until FreeBSD gets their act together;
4 * http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
5 */
6 #if defined( FREEBSD )
7 # undef _XOPEN_SOURCE
8 #endif
10 #include "WINGsP.h"
12 #include <math.h>
14 /* undefine will disable the autoadjusting of the knob dimple to be
15 * directly below the cursor
16 * DOES NOT WORK */
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 {
26 W_Class widgetClass;
27 W_View *view;
29 void *clientData;
30 WMAction *action;
32 float knobProportion;
33 float floatValue;
35 WMHandlerID timerID; /* for continuous scrolling mode */
37 #ifndef STRICT_NEXT_BEHAVIOUR
38 int dragPoint; /* point where the knob is being
39 * dragged */
40 #endif
41 struct {
42 WMScrollArrowPosition arrowsPosition:4;
44 unsigned int horizontal:1;
46 WMScrollerPart hitPart:4;
48 /* */
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;
64 } flags;
65 } Scroller;
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 = {
83 NULL,
84 NULL,
85 NULL,
86 NULL,
87 willResizeScroller
90 WMScroller *WMCreateScroller(WMWidget * parent)
92 Scroller *sPtr;
94 sPtr = wmalloc(sizeof(Scroller));
95 sPtr->widgetClass = WC_Scroller;
97 sPtr->view = W_CreateView(W_VIEW(parent));
98 if (!sPtr->view) {
99 wfree(sPtr);
100 return NULL;
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;
122 return sPtr;
125 void WMSetScrollerArrowsPosition(WMScroller * sPtr, WMScrollArrowPosition position)
127 sPtr->flags.arrowsPosition = position;
128 if (sPtr->view->flags.realized) {
129 paintScroller(sPtr);
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;
140 } else {
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;
174 else
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;
187 } else {
188 sPtr->knobProportion = knobProportion;
189 sPtr->flags.documentFullyVisible = 0;
192 if (sPtr->view->flags.realized)
193 paintScroller(sPtr);
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;
226 int ofs;
227 W_Pixmap *arrow;
229 #ifndef DOUBLE_BUFFER
230 GC gc = scr->lightGC;
231 #endif
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;
237 } else {
238 ofs = 2;
240 if (sPtr->flags.decrDown)
241 arrow = scr->hiLeftArrow;
242 else
243 arrow = scr->leftArrow;
245 } else {
246 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
247 ofs = view->size.height - 2 * (BUTTON_SIZE + 1) - 1;
248 } else {
249 ofs = 2;
251 if (sPtr->flags.decrDown)
252 arrow = scr->hiUpArrow;
253 else
254 arrow = scr->upArrow;
257 #ifndef DOUBLE_BUFFER
258 if (sPtr->flags.decrDown)
259 gc = WMColorGC(scr->white);
260 #endif
261 } else { /* increment button */
262 if (sPtr->flags.horizontal) {
263 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
264 ofs = view->size.width - BUTTON_SIZE + 1 - 3;
265 } else {
266 ofs = 2 + BUTTON_SIZE + 1;
268 if (sPtr->flags.incrDown)
269 arrow = scr->hiRightArrow;
270 else
271 arrow = scr->rightArrow;
272 } else {
273 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
274 ofs = view->size.height - BUTTON_SIZE + 1 - 3;
275 } else {
276 ofs = 2 + BUTTON_SIZE + 1;
278 if (sPtr->flags.incrDown)
279 arrow = scr->hiDownArrow;
280 else
281 arrow = scr->downArrow;
284 #ifndef DOUBLE_BUFFER
285 if (sPtr->flags.incrDown)
286 gc = scr->whiteGC;
287 #endif
290 if (sPtr->flags.horizontal) {
291 /* paint button */
292 #ifndef DOUBLE_BUFFER
293 XFillRectangle(scr->display, d, gc, ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
294 #else
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);
301 /* paint arrow */
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 */
312 /* paint button */
313 #ifndef DOUBLE_BUFFER
314 XFillRectangle(scr->display, d, gc, 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
315 #else
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);
322 /* paint arrow */
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)
335 int tmp, length;
337 if (sPtr->flags.horizontal)
338 length = sPtr->view->size.width - 4;
339 else
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)
349 tmp = BUTTON_SIZE;
351 return tmp;
354 static void paintScroller(Scroller * sPtr)
356 WMView *view = sPtr->view;
357 WMScreen *scr = view->screen;
358 #ifdef DOUBLE_BUFFER
359 Pixmap d;
360 #else
361 Drawable d = view->window;
362 #endif
363 int length, ofs;
364 float knobP, knobL;
366 #ifdef DOUBLE_BUFFER
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);
369 #endif
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);
374 #endif
376 if (sPtr->flags.horizontal)
377 length = view->size.width - 4;
378 else
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);
383 } else {
384 ofs = 2;
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) {
397 /* before */
398 XFillRectangle(scr->display, d, scr->stippleGC, ofs, 2, (int)knobP, view->size.height - 4);
400 /* knob */
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);
404 #endif
405 W_DrawRelief(scr, d, ofs + (int)knobP, 2, (int)knobL, view->size.height - 4, WRRaised);
407 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
408 scr->copyGC, 0, 0,
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);
413 /* after */
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);
418 } else {
419 /* before */
420 if (knobP > 0.0)
421 XFillRectangle(scr->display, d, scr->stippleGC,
422 2, ofs, view->size.width - 4, (int)knobP);
424 /* knob */
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);
428 #endif
429 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
430 scr->copyGC, 0, 0,
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);
437 /* after */
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);
450 #ifdef DOUBLE_BUFFER
451 XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
452 XFreePixmap(scr->display, d);
453 #endif
456 static void handleEvents(XEvent * event, void *data)
458 Scroller *sPtr = (Scroller *) data;
460 CHECK_CLASS(data, WC_Scroller);
462 switch (event->type) {
463 case Expose:
464 if (event->xexpose.count == 0)
465 paintScroller(sPtr);
466 break;
468 case DestroyNotify:
469 destroyScroller(sPtr);
470 break;
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;
483 int knobL, slotL;
485 /* if there is no knob... */
486 if (sPtr->flags.documentFullyVisible)
487 return WSKnobSlot;
489 if (sPtr->flags.horizontal)
490 c = x;
491 else
492 c = y;
494 /* p1 p2 p3 p4 p5 p6
495 * | | |###########| |#####| | |
496 * | < | > |###########| O |#####| < | > |
497 * | | |###########| |#####| | |
500 if (sPtr->flags.arrowsPosition == WSAMinEnd) {
501 p1 = 18;
502 p2 = 36;
504 if (sPtr->flags.horizontal) {
505 slotL = width - 36;
506 p5 = width;
507 } else {
508 slotL = height - 36;
509 p5 = height;
511 p6 = p5;
512 } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
513 if (sPtr->flags.horizontal) {
514 slotL = width - 36;
515 p6 = width - 18;
516 } else {
517 slotL = height - 36;
518 p6 = height - 18;
520 p5 = p6 - 18;
522 p1 = p2 = 0;
523 } else {
524 /* no arrows */
525 p1 = p2 = 0;
527 if (sPtr->flags.horizontal) {
528 slotL = p5 = p6 = width;
529 } else {
530 slotL = p5 = p6 = height;
534 knobL = knobLength(sPtr);
535 p3 = p2 + (int)((float)(slotL - knobL) * sPtr->floatValue);
536 p4 = p3 + knobL;
538 /* uses a mix of the NS and Win ways of doing scroll page */
539 if (c <= p1)
540 return alternate ? WSDecrementPage : WSDecrementLine;
541 else if (c <= p2)
542 return alternate ? WSIncrementPage : WSIncrementLine;
543 else if (c <= p3)
544 return WSDecrementPage;
545 else if (c <= p4)
546 return WSKnob;
547 else if (c <= p5)
548 return WSIncrementPage;
549 else if (c <= p6)
550 return alternate ? WSDecrementPage : WSDecrementLine;
551 else
552 return alternate ? WSIncrementPage : WSIncrementLine;
555 static void handlePush(Scroller * sPtr, int pushX, int pushY, int alternate)
557 WMScrollerPart part;
558 int doAction = 0;
560 part = locatePointInScroller(sPtr, pushX, pushY, alternate);
562 sPtr->flags.hitPart = part;
564 switch (part) {
565 case WSIncrementLine:
566 sPtr->flags.incrDown = 1;
567 doAction = 1;
568 break;
570 case WSIncrementPage:
571 doAction = 1;
572 break;
574 case WSDecrementLine:
575 sPtr->flags.decrDown = 1;
576 doAction = 1;
577 break;
579 case WSDecrementPage:
580 doAction = 1;
581 break;
583 case WSKnob:
584 sPtr->flags.draggingKnob = 1;
585 #ifndef STRICT_NEXT_BEHAVIOUR
586 if (sPtr->flags.horizontal)
587 sPtr->dragPoint = pushX;
588 else
589 sPtr->dragPoint = pushY;
592 int length, knobP;
593 int buttonsLen;
595 if (sPtr->flags.arrowsPosition != WSANone)
596 buttonsLen = 2 * (BUTTON_SIZE + 1);
597 else
598 buttonsLen = 0;
600 if (sPtr->flags.horizontal)
601 length = sPtr->view->size.width - 4 - buttonsLen;
602 else
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;
609 else
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); */
616 break;
618 case WSDecrementWheel:
619 case WSIncrementWheel:
620 case WSKnobSlot:
621 case WSNoPart:
622 /* dummy */
623 break;
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;
636 float position;
637 int slotOfs, slotLength, knobL;
639 if (sPtr->flags.horizontal)
640 slotLength = sPtr->view->size.width - 4;
641 else
642 slotLength = sPtr->view->size.height - 4;
644 slotOfs = 2;
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);
658 else
659 position = (float)point;
661 floatValue = (position - (float)(slotOfs + slotLength / 2))
662 / (float)(slotLength - knobL);
663 #else
664 /* Adjust the last point to lie inside the knob slot */
665 if (point < slotOfs)
666 position = (float)slotOfs;
667 else if (point > slotOfs + slotLength)
668 position = (float)(slotOfs + slotLength);
669 else
670 position = (float)point;
672 /* Compute the float value */
673 floatValue = (position - (float)slotOfs) / (float)(slotLength - knobL);
674 #endif
676 assert(!isnan(floatValue));
677 return floatValue;
680 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY)
682 if (sPtr->flags.draggingKnob) {
683 float newFloatValue;
684 int point;
686 if (sPtr->flags.horizontal) {
687 point = mouseX;
688 } else {
689 point = mouseY;
692 #ifndef STRICT_NEXT_BEHAVIOUR
693 point -= sPtr->dragPoint;
694 #endif
696 newFloatValue = floatValueForPoint(sPtr, point);
697 WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
698 if (sPtr->action) {
699 (*sPtr->action) (sPtr, sPtr->clientData);
700 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
702 } else {
703 int part;
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;
726 if (sPtr->action) {
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;
737 int id, dd;
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) {
747 case EnterNotify:
748 break;
750 case LeaveNotify:
751 if (sPtr->timerID) {
752 WMDeleteTimerHandler(sPtr->timerID);
753 sPtr->timerID = NULL;
755 sPtr->flags.incrDown = 0;
756 sPtr->flags.decrDown = 0;
757 break;
759 case ButtonPress:
760 /* FIXME: change Mod1Mask with something else */
761 if (sPtr->flags.documentFullyVisible)
762 break;
764 if (sPtr->flags.horizontal) {
765 wheelDecrement = WINGsConfiguration.mouseWheelDown;
766 wheelIncrement = WINGsConfiguration.mouseWheelUp;
767 } else {
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;
777 } else {
778 sPtr->flags.hitPart = WSDecrementWheel;
780 if (sPtr->action) {
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;
789 } else {
790 sPtr->flags.hitPart = WSIncrementWheel;
792 if (sPtr->action) {
793 (*sPtr->action) (sPtr, sPtr->clientData);
794 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
796 } else {
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);
804 break;
806 case ButtonRelease:
807 if (sPtr->flags.draggingKnob) {
808 if (sPtr->action) {
809 (*sPtr->action) (sPtr, sPtr->clientData);
810 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
813 if (sPtr->timerID) {
814 WMDeleteTimerHandler(sPtr->timerID);
815 sPtr->timerID = NULL;
817 sPtr->flags.incrDown = 0;
818 sPtr->flags.decrDown = 0;
819 sPtr->flags.draggingKnob = 0;
820 break;
822 case MotionNotify:
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;
829 break;
831 if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
832 paintScroller(sPtr);
835 static void destroyScroller(Scroller * sPtr)
837 /* we don't want autoscroll try to scroll a freed widget */
838 if (sPtr->timerID) {
839 WMDeleteTimerHandler(sPtr->timerID);
842 wfree(sPtr);