Update Serbian translation from master branch
[wmaker-crm.git] / WINGs / wscroller.c
blob1533da93a37ef9eeb98e5698c30ee54cd52c0854
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 const 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_ARROWS_POSITION WSAMinEnd
69 #define BUTTON_SIZE ((SCROLLER_WIDTH) - 4)
71 static void destroyScroller(Scroller * sPtr);
72 static void paintScroller(Scroller * sPtr);
74 static void willResizeScroller(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height);
75 static void handleEvents(XEvent * event, void *data);
76 static void handleActionEvents(XEvent * event, void *data);
78 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY);
80 W_ViewDelegate _ScrollerViewDelegate = {
81 NULL,
82 NULL,
83 NULL,
84 NULL,
85 willResizeScroller
88 WMScroller *WMCreateScroller(WMWidget * parent)
90 Scroller *sPtr;
92 sPtr = wmalloc(sizeof(Scroller));
93 sPtr->widgetClass = WC_Scroller;
95 sPtr->view = W_CreateView(W_VIEW(parent));
96 if (!sPtr->view) {
97 wfree(sPtr);
98 return NULL;
100 sPtr->view->self = sPtr;
102 sPtr->view->delegate = &_ScrollerViewDelegate;
104 sPtr->flags.documentFullyVisible = 1;
106 WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask
107 | ClientMessageMask, handleEvents, sPtr);
109 W_ResizeView(sPtr->view, SCROLLER_WIDTH, SCROLLER_WIDTH);
110 sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
112 WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
113 | EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, sPtr);
115 sPtr->flags.hitPart = WSNoPart;
117 sPtr->floatValue = 0.0;
118 sPtr->knobProportion = 1.0;
120 return sPtr;
123 void WMSetScrollerArrowsPosition(WMScroller * sPtr, WMScrollArrowPosition position)
125 sPtr->flags.arrowsPosition = position;
126 if (sPtr->view->flags.realized) {
127 paintScroller(sPtr);
131 static void willResizeScroller(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height)
133 WMScroller *sPtr = (WMScroller *) view->self;
135 /* Parameter not used, but tell the compiler that it is ok */
136 (void) self;
138 if (*width > *height) {
139 sPtr->flags.horizontal = 1;
140 *height = SCROLLER_WIDTH;
141 } else {
142 sPtr->flags.horizontal = 0;
143 *width = SCROLLER_WIDTH;
147 void WMSetScrollerAction(WMScroller * sPtr, WMAction * action, void *clientData)
149 CHECK_CLASS(sPtr, WC_Scroller);
151 sPtr->action = action;
153 sPtr->clientData = clientData;
156 void WMSetScrollerParameters(WMScroller * sPtr, float floatValue, float knobProportion)
159 * This value represents 1 pixel on a 4k wide screen, it makes
160 * a good minimum; this ensure a non-null value to avoid
161 * potential division-by-0.
162 * Please note that there is another size check when drawing
163 * the knob to make sure it will remain selectable.
165 static const float min_knob_proportion = 1.0 / 4096.0;
167 CHECK_CLASS(sPtr, WC_Scroller);
169 assert(!isnan(floatValue));
171 if (floatValue < 0.0F)
172 sPtr->floatValue = 0.0F;
173 else if (floatValue > 1.0F)
174 sPtr->floatValue = 1.0F;
175 else
176 sPtr->floatValue = floatValue;
178 if (knobProportion <= min_knob_proportion) {
180 sPtr->knobProportion = min_knob_proportion;
181 sPtr->flags.documentFullyVisible = 0;
183 } else if (knobProportion >= 1.0F) {
185 sPtr->knobProportion = 1.0F;
186 sPtr->flags.documentFullyVisible = 1;
188 } else {
189 sPtr->knobProportion = knobProportion;
190 sPtr->flags.documentFullyVisible = 0;
193 if (sPtr->view->flags.realized)
194 paintScroller(sPtr);
196 /* WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL); */
199 float WMGetScrollerKnobProportion(WMScroller * sPtr)
201 CHECK_CLASS(sPtr, WC_Scroller);
203 return sPtr->knobProportion;
206 float WMGetScrollerValue(WMScroller * sPtr)
208 CHECK_CLASS(sPtr, WC_Scroller);
210 return sPtr->floatValue;
213 WMScrollerPart WMGetScrollerHitPart(WMScroller * sPtr)
215 CHECK_CLASS(sPtr, WC_Scroller);
217 return sPtr->flags.hitPart;
220 static void 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;
227 int ofs;
228 W_Pixmap *arrow;
230 #ifndef DOUBLE_BUFFER
231 GC gc = scr->lightGC;
232 #endif
234 if (part == 0) { /* decrement button */
235 if (sPtr->flags.horizontal) {
236 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
237 ofs = view->size.width - 2 * (BUTTON_SIZE + 1) - 1;
238 } else {
239 ofs = 2;
241 if (sPtr->flags.decrDown)
242 arrow = scr->hiLeftArrow;
243 else
244 arrow = scr->leftArrow;
246 } else {
247 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
248 ofs = view->size.height - 2 * (BUTTON_SIZE + 1) - 1;
249 } else {
250 ofs = 2;
252 if (sPtr->flags.decrDown)
253 arrow = scr->hiUpArrow;
254 else
255 arrow = scr->upArrow;
258 #ifndef DOUBLE_BUFFER
259 if (sPtr->flags.decrDown)
260 gc = WMColorGC(scr->white);
261 #endif
262 } else { /* increment button */
263 if (sPtr->flags.horizontal) {
264 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
265 ofs = view->size.width - BUTTON_SIZE + 1 - 3;
266 } else {
267 ofs = 2 + BUTTON_SIZE + 1;
269 if (sPtr->flags.incrDown)
270 arrow = scr->hiRightArrow;
271 else
272 arrow = scr->rightArrow;
273 } else {
274 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
275 ofs = view->size.height - BUTTON_SIZE + 1 - 3;
276 } else {
277 ofs = 2 + BUTTON_SIZE + 1;
279 if (sPtr->flags.incrDown)
280 arrow = scr->hiDownArrow;
281 else
282 arrow = scr->downArrow;
285 #ifndef DOUBLE_BUFFER
286 if (sPtr->flags.incrDown)
287 gc = scr->whiteGC;
288 #endif
291 if (sPtr->flags.horizontal) {
292 /* paint button */
293 #ifndef DOUBLE_BUFFER
294 XFillRectangle(scr->display, d, gc, ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
295 #else
296 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
297 XFillRectangle(scr->display, d, WMColorGC(scr->white),
298 ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
299 #endif /* DOUBLE_BUFFER */
300 W_DrawRelief(scr, d, ofs, 2, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
302 /* paint arrow */
303 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
304 XSetClipOrigin(scr->display, scr->clipGC,
305 ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
307 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
308 0, 0, arrow->width, arrow->height,
309 ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
311 } else { /* vertical */
313 /* paint button */
314 #ifndef DOUBLE_BUFFER
315 XFillRectangle(scr->display, d, gc, 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
316 #else
317 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
318 XFillRectangle(scr->display, d, WMColorGC(scr->white),
319 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
320 #endif /* DOUBLE_BUFFER */
321 W_DrawRelief(scr, d, 2, ofs, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
323 /* paint arrow */
325 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
326 XSetClipOrigin(scr->display, scr->clipGC,
327 2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
328 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
329 0, 0, arrow->width, arrow->height,
330 2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
334 static int knobLength(Scroller * sPtr)
336 int tmp, length;
338 if (sPtr->flags.horizontal)
339 length = sPtr->view->size.width - 4;
340 else
341 length = sPtr->view->size.height - 4;
343 if (sPtr->flags.arrowsPosition != WSANone) {
344 length -= 2 * (BUTTON_SIZE + 1);
347 tmp = (int)((float)length * sPtr->knobProportion + 0.5F);
348 /* keep minimum size */
349 if (tmp < BUTTON_SIZE)
350 tmp = BUTTON_SIZE;
352 return tmp;
355 static void paintScroller(Scroller * sPtr)
357 WMView *view = sPtr->view;
358 WMScreen *scr = view->screen;
359 #ifdef DOUBLE_BUFFER
360 Pixmap d;
361 #else
362 Drawable d = view->window;
363 #endif
364 int length, ofs;
365 float knobP, knobL;
367 #ifdef DOUBLE_BUFFER
368 d = XCreatePixmap(scr->display, view->window, view->size.width, view->size.height, scr->depth);
369 XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0, view->size.width, view->size.height);
370 #endif
372 XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0, view->size.width - 1, view->size.height - 1);
373 #ifndef DOUBLE_BUFFER
374 XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1, view->size.width - 3, view->size.height - 3);
375 #endif
377 if (sPtr->flags.horizontal)
378 length = view->size.width - 4;
379 else
380 length = view->size.height - 4;
382 if (sPtr->flags.documentFullyVisible) {
383 XFillRectangle(scr->display, d, scr->stippleGC, 2, 2, view->size.width - 4, view->size.height - 4);
384 } else {
385 ofs = 2;
386 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
387 length -= (BUTTON_SIZE + 1) * 2;
388 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
389 ofs += (BUTTON_SIZE + 1) * 2;
390 length -= (BUTTON_SIZE + 1) * 2;
393 knobL = (float)knobLength(sPtr);
395 knobP = sPtr->floatValue * ((float)length - knobL);
397 if (sPtr->flags.horizontal) {
398 /* before */
399 XFillRectangle(scr->display, d, scr->stippleGC, ofs, 2, (int)knobP, view->size.height - 4);
401 /* knob */
402 #ifndef DOUBLE_BUFFER
403 XFillRectangle(scr->display, d, scr->lightGC,
404 ofs + (int)knobP + 2, 2 + 2, (int)knobL - 4, view->size.height - 4 - 4);
405 #endif
406 W_DrawRelief(scr, d, ofs + (int)knobP, 2, (int)knobL, view->size.height - 4, WRRaised);
408 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
409 scr->copyGC, 0, 0,
410 scr->scrollerDimple->width, scr->scrollerDimple->height,
411 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->width - 1) / 2,
412 (view->size.height - scr->scrollerDimple->height - 1) / 2);
414 /* after */
415 if ((int)(knobP + knobL) < length)
416 XFillRectangle(scr->display, d, scr->stippleGC,
417 ofs + (int)(knobP + knobL), 2,
418 length - (int)(knobP + knobL), view->size.height - 4);
419 } else {
420 /* before */
421 if (knobP > 0.0F)
422 XFillRectangle(scr->display, d, scr->stippleGC,
423 2, ofs, view->size.width - 4, (int)knobP);
425 /* knob */
426 #ifndef DOUBLE_BUFFER
427 XFillRectangle(scr->display, d, scr->lightGC,
428 2 + 2, ofs + (int)knobP + 2, view->size.width - 4 - 4, (int)knobL - 4);
429 #endif
430 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
431 scr->copyGC, 0, 0,
432 scr->scrollerDimple->width, scr->scrollerDimple->height,
433 (view->size.width - scr->scrollerDimple->width - 1) / 2,
434 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->height - 1) / 2);
436 W_DrawRelief(scr, d, 2, ofs + (int)knobP, view->size.width - 4, (int)knobL, WRRaised);
438 /* after */
439 if ((int)(knobP + knobL) < length)
440 XFillRectangle(scr->display, d, scr->stippleGC,
441 2, ofs + (int)(knobP + knobL),
442 view->size.width - 4, length - (int)(knobP + knobL));
445 if (sPtr->flags.arrowsPosition != WSANone) {
446 paintArrow(sPtr, d, 0);
447 paintArrow(sPtr, d, 1);
451 #ifdef DOUBLE_BUFFER
452 XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
453 XFreePixmap(scr->display, d);
454 #endif
457 static void handleEvents(XEvent * event, void *data)
459 Scroller *sPtr = (Scroller *) data;
461 CHECK_CLASS(data, WC_Scroller);
463 switch (event->type) {
464 case Expose:
465 if (event->xexpose.count == 0)
466 paintScroller(sPtr);
467 break;
469 case DestroyNotify:
470 destroyScroller(sPtr);
471 break;
476 * locatePointInScroller-
477 * Return the part of the scroller where the point is located.
479 static WMScrollerPart locatePointInScroller(Scroller * sPtr, int x, int y, int alternate)
481 int width = sPtr->view->size.width;
482 int height = sPtr->view->size.height;
483 int c, p1, p2, p3, p4, p5, p6;
484 int knobL, slotL;
486 /* if there is no knob... */
487 if (sPtr->flags.documentFullyVisible)
488 return WSKnobSlot;
490 if (sPtr->flags.horizontal)
491 c = x;
492 else
493 c = y;
495 /* p1 p2 p3 p4 p5 p6
496 * | | |###########| |#####| | |
497 * | < | > |###########| O |#####| < | > |
498 * | | |###########| |#####| | |
501 if (sPtr->flags.arrowsPosition == WSAMinEnd) {
502 p1 = 18;
503 p2 = 36;
505 if (sPtr->flags.horizontal) {
506 slotL = width - 36;
507 p5 = width;
508 } else {
509 slotL = height - 36;
510 p5 = height;
512 p6 = p5;
513 } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
514 if (sPtr->flags.horizontal) {
515 slotL = width - 36;
516 p6 = width - 18;
517 } else {
518 slotL = height - 36;
519 p6 = height - 18;
521 p5 = p6 - 18;
523 p1 = p2 = 0;
524 } else {
525 /* no arrows */
526 p1 = p2 = 0;
528 if (sPtr->flags.horizontal) {
529 slotL = p5 = p6 = width;
530 } else {
531 slotL = p5 = p6 = height;
535 knobL = knobLength(sPtr);
536 p3 = p2 + (int)((float)(slotL - knobL) * sPtr->floatValue);
537 p4 = p3 + knobL;
539 /* uses a mix of the NS and Win ways of doing scroll page */
540 if (c <= p1)
541 return alternate ? WSDecrementPage : WSDecrementLine;
542 else if (c <= p2)
543 return alternate ? WSIncrementPage : WSIncrementLine;
544 else if (c <= p3)
545 return WSDecrementPage;
546 else if (c <= p4)
547 return WSKnob;
548 else if (c <= p5)
549 return WSIncrementPage;
550 else if (c <= p6)
551 return alternate ? WSDecrementPage : WSDecrementLine;
552 else
553 return alternate ? WSIncrementPage : WSIncrementLine;
556 static void handlePush(Scroller * sPtr, int pushX, int pushY, int alternate)
558 WMScrollerPart part;
559 int doAction = 0;
561 part = locatePointInScroller(sPtr, pushX, pushY, alternate);
563 sPtr->flags.hitPart = part;
565 switch (part) {
566 case WSIncrementLine:
567 sPtr->flags.incrDown = 1;
568 doAction = 1;
569 break;
571 case WSIncrementPage:
572 doAction = 1;
573 break;
575 case WSDecrementLine:
576 sPtr->flags.decrDown = 1;
577 doAction = 1;
578 break;
580 case WSDecrementPage:
581 doAction = 1;
582 break;
584 case WSKnob:
585 sPtr->flags.draggingKnob = 1;
586 #ifndef STRICT_NEXT_BEHAVIOUR
587 if (sPtr->flags.horizontal)
588 sPtr->dragPoint = pushX;
589 else
590 sPtr->dragPoint = pushY;
593 int length, knobP;
594 int buttonsLen;
596 if (sPtr->flags.arrowsPosition != WSANone)
597 buttonsLen = 2 * (BUTTON_SIZE + 1);
598 else
599 buttonsLen = 0;
601 if (sPtr->flags.horizontal)
602 length = sPtr->view->size.width - 4 - buttonsLen;
603 else
604 length = sPtr->view->size.height - 4 - buttonsLen;
606 knobP = (int)(sPtr->floatValue * (float)(length - knobLength(sPtr)));
608 if (sPtr->flags.arrowsPosition == WSAMinEnd)
609 sPtr->dragPoint -= 2 + buttonsLen + knobP;
610 else
611 sPtr->dragPoint -= 2 + knobP;
613 #endif /* STRICT_NEXT_BEHAVIOUR */
614 /* This does not seem necesary here since we don't know yet if the
615 * knob will be dragged later. -Dan
616 handleMotion(sPtr, pushX, pushY); */
617 break;
619 case WSDecrementWheel:
620 case WSIncrementWheel:
621 case WSKnobSlot:
622 case WSNoPart:
623 /* dummy */
624 break;
627 if (doAction && sPtr->action) {
628 (*sPtr->action) (sPtr, sPtr->clientData);
630 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
634 static float floatValueForPoint(Scroller * sPtr, int point)
636 float floatValue = 0;
637 float position;
638 int slotOfs, slotLength, knobL;
640 if (sPtr->flags.horizontal)
641 slotLength = sPtr->view->size.width - 4;
642 else
643 slotLength = sPtr->view->size.height - 4;
645 slotOfs = 2;
646 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
647 slotLength -= (BUTTON_SIZE + 1) * 2;
648 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
649 slotOfs += (BUTTON_SIZE + 1) * 2;
650 slotLength -= (BUTTON_SIZE + 1) * 2;
653 knobL = (float)knobLength(sPtr);
654 #ifdef STRICT_NEXT_BEHAVIOUR
655 if (point < slotOfs + knobL / 2)
656 position = (float)(slotOfs + knobL / 2);
657 else if (point > slotOfs + slotLength - knobL / 2)
658 position = (float)(slotOfs + slotLength - knobL / 2);
659 else
660 position = (float)point;
662 floatValue = (position - (float)(slotOfs + slotLength / 2))
663 / (float)(slotLength - knobL);
664 #else
665 /* Adjust the last point to lie inside the knob slot */
666 if (point < slotOfs)
667 position = (float)slotOfs;
668 else if (point > slotOfs + slotLength)
669 position = (float)(slotOfs + slotLength);
670 else
671 position = (float)point;
673 /* Compute the float value */
674 floatValue = (position - (float)slotOfs) / (float)(slotLength - knobL);
675 #endif
677 assert(!isnan(floatValue));
678 return floatValue;
681 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY)
683 if (sPtr->flags.draggingKnob) {
684 float newFloatValue;
685 int point;
687 if (sPtr->flags.horizontal) {
688 point = mouseX;
689 } else {
690 point = mouseY;
693 #ifndef STRICT_NEXT_BEHAVIOUR
694 point -= sPtr->dragPoint;
695 #endif
697 newFloatValue = floatValueForPoint(sPtr, point);
698 WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
699 if (sPtr->action) {
700 (*sPtr->action) (sPtr, sPtr->clientData);
701 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
703 } else {
704 int part;
706 part = locatePointInScroller(sPtr, mouseX, mouseY, False);
708 sPtr->flags.hitPart = part;
710 if (part == WSIncrementLine && sPtr->flags.decrDown) {
711 sPtr->flags.decrDown = 0;
712 sPtr->flags.incrDown = 1;
713 } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
714 sPtr->flags.incrDown = 0;
715 sPtr->flags.decrDown = 1;
716 } else if (part != WSIncrementLine && part != WSDecrementLine) {
717 sPtr->flags.incrDown = 0;
718 sPtr->flags.decrDown = 0;
723 static void autoScroll(void *clientData)
725 Scroller *sPtr = (Scroller *) clientData;
727 if (sPtr->action) {
728 (*sPtr->action) (sPtr, sPtr->clientData);
729 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
731 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
734 static void handleActionEvents(XEvent * event, void *data)
736 Scroller *sPtr = (Scroller *) data;
737 int wheelDecrement, wheelIncrement;
738 int id, dd;
740 /* check if we're really dealing with a scroller, as something
741 * might have gone wrong in the event dispatching stuff */
742 CHECK_CLASS(sPtr, WC_Scroller);
744 id = sPtr->flags.incrDown;
745 dd = sPtr->flags.decrDown;
747 switch (event->type) {
748 case EnterNotify:
749 break;
751 case LeaveNotify:
752 if (sPtr->timerID) {
753 WMDeleteTimerHandler(sPtr->timerID);
754 sPtr->timerID = NULL;
756 sPtr->flags.incrDown = 0;
757 sPtr->flags.decrDown = 0;
758 break;
760 case ButtonPress:
761 /* FIXME: change Mod1Mask with something else */
762 if (sPtr->flags.documentFullyVisible)
763 break;
765 if (sPtr->flags.horizontal) {
766 wheelDecrement = WINGsConfiguration.mouseWheelDown;
767 wheelIncrement = WINGsConfiguration.mouseWheelUp;
768 } else {
769 wheelDecrement = WINGsConfiguration.mouseWheelUp;
770 wheelIncrement = WINGsConfiguration.mouseWheelDown;
773 if (event->xbutton.button == wheelDecrement) {
774 if (event->xbutton.state & ControlMask) {
775 sPtr->flags.hitPart = WSDecrementPage;
776 } else if (event->xbutton.state & ShiftMask) {
777 sPtr->flags.hitPart = WSDecrementLine;
778 } else {
779 sPtr->flags.hitPart = WSDecrementWheel;
781 if (sPtr->action) {
782 (*sPtr->action) (sPtr, sPtr->clientData);
783 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
785 } else if (event->xbutton.button == wheelIncrement) {
786 if (event->xbutton.state & ControlMask) {
787 sPtr->flags.hitPart = WSIncrementPage;
788 } else if (event->xbutton.state & ShiftMask) {
789 sPtr->flags.hitPart = WSIncrementLine;
790 } else {
791 sPtr->flags.hitPart = WSIncrementWheel;
793 if (sPtr->action) {
794 (*sPtr->action) (sPtr, sPtr->clientData);
795 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
797 } else {
798 handlePush(sPtr, event->xbutton.x, event->xbutton.y, (event->xbutton.state & Mod1Mask)
799 || event->xbutton.button == Button2);
800 /* continue scrolling if pushed on the buttons */
801 if (sPtr->flags.hitPart == WSIncrementLine || sPtr->flags.hitPart == WSDecrementLine) {
802 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY, autoScroll, sPtr);
805 break;
807 case ButtonRelease:
808 if (sPtr->flags.draggingKnob) {
809 if (sPtr->action) {
810 (*sPtr->action) (sPtr, sPtr->clientData);
811 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
814 if (sPtr->timerID) {
815 WMDeleteTimerHandler(sPtr->timerID);
816 sPtr->timerID = NULL;
818 sPtr->flags.incrDown = 0;
819 sPtr->flags.decrDown = 0;
820 sPtr->flags.draggingKnob = 0;
821 break;
823 case MotionNotify:
824 handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
825 if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
826 && sPtr->flags.hitPart != WSDecrementLine) {
827 WMDeleteTimerHandler(sPtr->timerID);
828 sPtr->timerID = NULL;
830 break;
832 if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
833 paintScroller(sPtr);
836 static void destroyScroller(Scroller * sPtr)
838 /* we don't want autoscroll try to scroll a freed widget */
839 if (sPtr->timerID) {
840 WMDeleteTimerHandler(sPtr->timerID);
843 wfree(sPtr);