WINGs: Added 'const' attribute to function 'WMCreateHashTable'
[wmaker-crm.git] / WINGs / wscroller.c
blob4a5ae8c4ff1f39f465e0230eca99c3cb6190e4ed
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(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height);
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 /* Parameter not used, but tell the compiler that it is ok */
138 (void) self;
140 if (*width > *height) {
141 sPtr->flags.horizontal = 1;
142 *height = SCROLLER_WIDTH;
143 } else {
144 sPtr->flags.horizontal = 0;
145 *width = SCROLLER_WIDTH;
149 void WMSetScrollerAction(WMScroller * sPtr, WMAction * action, void *clientData)
151 CHECK_CLASS(sPtr, WC_Scroller);
153 sPtr->action = action;
155 sPtr->clientData = clientData;
158 void WMSetScrollerParameters(WMScroller * sPtr, float floatValue, float knobProportion)
161 * This value represents 1 pixel on a 4k wide screen, it makes
162 * a good minimum; this ensure a non-null value to avoid
163 * potential division-by-0.
164 * Please note that there is another size check when drawing
165 * the knob to make sure it will remain selectable.
167 static const float min_knob_proportion = 1.0 / 4096.0;
169 CHECK_CLASS(sPtr, WC_Scroller);
171 assert(!isnan(floatValue));
173 if (floatValue < 0.0)
174 sPtr->floatValue = 0.0;
175 else if (floatValue > 1.0)
176 sPtr->floatValue = 1.0;
177 else
178 sPtr->floatValue = floatValue;
180 if (knobProportion <= min_knob_proportion) {
182 sPtr->knobProportion = min_knob_proportion;
183 sPtr->flags.documentFullyVisible = 0;
185 } else if (knobProportion >= 1.0) {
187 sPtr->knobProportion = 1.0;
188 sPtr->flags.documentFullyVisible = 1;
190 } else {
191 sPtr->knobProportion = knobProportion;
192 sPtr->flags.documentFullyVisible = 0;
195 if (sPtr->view->flags.realized)
196 paintScroller(sPtr);
198 /* WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL); */
201 float WMGetScrollerKnobProportion(WMScroller * sPtr)
203 CHECK_CLASS(sPtr, WC_Scroller);
205 return sPtr->knobProportion;
208 float WMGetScrollerValue(WMScroller * sPtr)
210 CHECK_CLASS(sPtr, WC_Scroller);
212 return sPtr->floatValue;
215 WMScrollerPart WMGetScrollerHitPart(WMScroller * sPtr)
217 CHECK_CLASS(sPtr, WC_Scroller);
219 return sPtr->flags.hitPart;
222 static void paintArrow(WMScroller * sPtr, Drawable d, int part)
224 * part- 0 paints the decrement arrow, 1 the increment arrow
227 WMView *view = sPtr->view;
228 WMScreen *scr = view->screen;
229 int ofs;
230 W_Pixmap *arrow;
232 #ifndef DOUBLE_BUFFER
233 GC gc = scr->lightGC;
234 #endif
236 if (part == 0) { /* decrement button */
237 if (sPtr->flags.horizontal) {
238 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
239 ofs = view->size.width - 2 * (BUTTON_SIZE + 1) - 1;
240 } else {
241 ofs = 2;
243 if (sPtr->flags.decrDown)
244 arrow = scr->hiLeftArrow;
245 else
246 arrow = scr->leftArrow;
248 } else {
249 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
250 ofs = view->size.height - 2 * (BUTTON_SIZE + 1) - 1;
251 } else {
252 ofs = 2;
254 if (sPtr->flags.decrDown)
255 arrow = scr->hiUpArrow;
256 else
257 arrow = scr->upArrow;
260 #ifndef DOUBLE_BUFFER
261 if (sPtr->flags.decrDown)
262 gc = WMColorGC(scr->white);
263 #endif
264 } else { /* increment button */
265 if (sPtr->flags.horizontal) {
266 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
267 ofs = view->size.width - BUTTON_SIZE + 1 - 3;
268 } else {
269 ofs = 2 + BUTTON_SIZE + 1;
271 if (sPtr->flags.incrDown)
272 arrow = scr->hiRightArrow;
273 else
274 arrow = scr->rightArrow;
275 } else {
276 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
277 ofs = view->size.height - BUTTON_SIZE + 1 - 3;
278 } else {
279 ofs = 2 + BUTTON_SIZE + 1;
281 if (sPtr->flags.incrDown)
282 arrow = scr->hiDownArrow;
283 else
284 arrow = scr->downArrow;
287 #ifndef DOUBLE_BUFFER
288 if (sPtr->flags.incrDown)
289 gc = scr->whiteGC;
290 #endif
293 if (sPtr->flags.horizontal) {
294 /* paint button */
295 #ifndef DOUBLE_BUFFER
296 XFillRectangle(scr->display, d, gc, ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
297 #else
298 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
299 XFillRectangle(scr->display, d, WMColorGC(scr->white),
300 ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
301 #endif /* DOUBLE_BUFFER */
302 W_DrawRelief(scr, d, ofs, 2, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
304 /* paint arrow */
305 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
306 XSetClipOrigin(scr->display, scr->clipGC,
307 ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
309 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
310 0, 0, arrow->width, arrow->height,
311 ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
313 } else { /* vertical */
315 /* paint button */
316 #ifndef DOUBLE_BUFFER
317 XFillRectangle(scr->display, d, gc, 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
318 #else
319 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
320 XFillRectangle(scr->display, d, WMColorGC(scr->white),
321 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
322 #endif /* DOUBLE_BUFFER */
323 W_DrawRelief(scr, d, 2, ofs, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
325 /* paint arrow */
327 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
328 XSetClipOrigin(scr->display, scr->clipGC,
329 2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
330 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
331 0, 0, arrow->width, arrow->height,
332 2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
336 static int knobLength(Scroller * sPtr)
338 int tmp, length;
340 if (sPtr->flags.horizontal)
341 length = sPtr->view->size.width - 4;
342 else
343 length = sPtr->view->size.height - 4;
345 if (sPtr->flags.arrowsPosition != WSANone) {
346 length -= 2 * (BUTTON_SIZE + 1);
349 tmp = (int)((float)length * sPtr->knobProportion + 0.5);
350 /* keep minimum size */
351 if (tmp < BUTTON_SIZE)
352 tmp = BUTTON_SIZE;
354 return tmp;
357 static void paintScroller(Scroller * sPtr)
359 WMView *view = sPtr->view;
360 WMScreen *scr = view->screen;
361 #ifdef DOUBLE_BUFFER
362 Pixmap d;
363 #else
364 Drawable d = view->window;
365 #endif
366 int length, ofs;
367 float knobP, knobL;
369 #ifdef DOUBLE_BUFFER
370 d = XCreatePixmap(scr->display, view->window, view->size.width, view->size.height, scr->depth);
371 XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0, view->size.width, view->size.height);
372 #endif
374 XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0, view->size.width - 1, view->size.height - 1);
375 #ifndef DOUBLE_BUFFER
376 XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1, view->size.width - 3, view->size.height - 3);
377 #endif
379 if (sPtr->flags.horizontal)
380 length = view->size.width - 4;
381 else
382 length = view->size.height - 4;
384 if (sPtr->flags.documentFullyVisible) {
385 XFillRectangle(scr->display, d, scr->stippleGC, 2, 2, view->size.width - 4, view->size.height - 4);
386 } else {
387 ofs = 2;
388 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
389 length -= (BUTTON_SIZE + 1) * 2;
390 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
391 ofs += (BUTTON_SIZE + 1) * 2;
392 length -= (BUTTON_SIZE + 1) * 2;
395 knobL = (float)knobLength(sPtr);
397 knobP = sPtr->floatValue * ((float)length - knobL);
399 if (sPtr->flags.horizontal) {
400 /* before */
401 XFillRectangle(scr->display, d, scr->stippleGC, ofs, 2, (int)knobP, view->size.height - 4);
403 /* knob */
404 #ifndef DOUBLE_BUFFER
405 XFillRectangle(scr->display, d, scr->lightGC,
406 ofs + (int)knobP + 2, 2 + 2, (int)knobL - 4, view->size.height - 4 - 4);
407 #endif
408 W_DrawRelief(scr, d, ofs + (int)knobP, 2, (int)knobL, view->size.height - 4, WRRaised);
410 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
411 scr->copyGC, 0, 0,
412 scr->scrollerDimple->width, scr->scrollerDimple->height,
413 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->width - 1) / 2,
414 (view->size.height - scr->scrollerDimple->height - 1) / 2);
416 /* after */
417 if ((int)(knobP + knobL) < length)
418 XFillRectangle(scr->display, d, scr->stippleGC,
419 ofs + (int)(knobP + knobL), 2,
420 length - (int)(knobP + knobL), view->size.height - 4);
421 } else {
422 /* before */
423 if (knobP > 0.0)
424 XFillRectangle(scr->display, d, scr->stippleGC,
425 2, ofs, view->size.width - 4, (int)knobP);
427 /* knob */
428 #ifndef DOUBLE_BUFFER
429 XFillRectangle(scr->display, d, scr->lightGC,
430 2 + 2, ofs + (int)knobP + 2, view->size.width - 4 - 4, (int)knobL - 4);
431 #endif
432 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
433 scr->copyGC, 0, 0,
434 scr->scrollerDimple->width, scr->scrollerDimple->height,
435 (view->size.width - scr->scrollerDimple->width - 1) / 2,
436 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->height - 1) / 2);
438 W_DrawRelief(scr, d, 2, ofs + (int)knobP, view->size.width - 4, (int)knobL, WRRaised);
440 /* after */
441 if ((int)(knobP + knobL) < length)
442 XFillRectangle(scr->display, d, scr->stippleGC,
443 2, ofs + (int)(knobP + knobL),
444 view->size.width - 4, length - (int)(knobP + knobL));
447 if (sPtr->flags.arrowsPosition != WSANone) {
448 paintArrow(sPtr, d, 0);
449 paintArrow(sPtr, d, 1);
453 #ifdef DOUBLE_BUFFER
454 XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
455 XFreePixmap(scr->display, d);
456 #endif
459 static void handleEvents(XEvent * event, void *data)
461 Scroller *sPtr = (Scroller *) data;
463 CHECK_CLASS(data, WC_Scroller);
465 switch (event->type) {
466 case Expose:
467 if (event->xexpose.count == 0)
468 paintScroller(sPtr);
469 break;
471 case DestroyNotify:
472 destroyScroller(sPtr);
473 break;
478 * locatePointInScroller-
479 * Return the part of the scroller where the point is located.
481 static WMScrollerPart locatePointInScroller(Scroller * sPtr, int x, int y, int alternate)
483 int width = sPtr->view->size.width;
484 int height = sPtr->view->size.height;
485 int c, p1, p2, p3, p4, p5, p6;
486 int knobL, slotL;
488 /* if there is no knob... */
489 if (sPtr->flags.documentFullyVisible)
490 return WSKnobSlot;
492 if (sPtr->flags.horizontal)
493 c = x;
494 else
495 c = y;
497 /* p1 p2 p3 p4 p5 p6
498 * | | |###########| |#####| | |
499 * | < | > |###########| O |#####| < | > |
500 * | | |###########| |#####| | |
503 if (sPtr->flags.arrowsPosition == WSAMinEnd) {
504 p1 = 18;
505 p2 = 36;
507 if (sPtr->flags.horizontal) {
508 slotL = width - 36;
509 p5 = width;
510 } else {
511 slotL = height - 36;
512 p5 = height;
514 p6 = p5;
515 } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
516 if (sPtr->flags.horizontal) {
517 slotL = width - 36;
518 p6 = width - 18;
519 } else {
520 slotL = height - 36;
521 p6 = height - 18;
523 p5 = p6 - 18;
525 p1 = p2 = 0;
526 } else {
527 /* no arrows */
528 p1 = p2 = 0;
530 if (sPtr->flags.horizontal) {
531 slotL = p5 = p6 = width;
532 } else {
533 slotL = p5 = p6 = height;
537 knobL = knobLength(sPtr);
538 p3 = p2 + (int)((float)(slotL - knobL) * sPtr->floatValue);
539 p4 = p3 + knobL;
541 /* uses a mix of the NS and Win ways of doing scroll page */
542 if (c <= p1)
543 return alternate ? WSDecrementPage : WSDecrementLine;
544 else if (c <= p2)
545 return alternate ? WSIncrementPage : WSIncrementLine;
546 else if (c <= p3)
547 return WSDecrementPage;
548 else if (c <= p4)
549 return WSKnob;
550 else if (c <= p5)
551 return WSIncrementPage;
552 else if (c <= p6)
553 return alternate ? WSDecrementPage : WSDecrementLine;
554 else
555 return alternate ? WSIncrementPage : WSIncrementLine;
558 static void handlePush(Scroller * sPtr, int pushX, int pushY, int alternate)
560 WMScrollerPart part;
561 int doAction = 0;
563 part = locatePointInScroller(sPtr, pushX, pushY, alternate);
565 sPtr->flags.hitPart = part;
567 switch (part) {
568 case WSIncrementLine:
569 sPtr->flags.incrDown = 1;
570 doAction = 1;
571 break;
573 case WSIncrementPage:
574 doAction = 1;
575 break;
577 case WSDecrementLine:
578 sPtr->flags.decrDown = 1;
579 doAction = 1;
580 break;
582 case WSDecrementPage:
583 doAction = 1;
584 break;
586 case WSKnob:
587 sPtr->flags.draggingKnob = 1;
588 #ifndef STRICT_NEXT_BEHAVIOUR
589 if (sPtr->flags.horizontal)
590 sPtr->dragPoint = pushX;
591 else
592 sPtr->dragPoint = pushY;
595 int length, knobP;
596 int buttonsLen;
598 if (sPtr->flags.arrowsPosition != WSANone)
599 buttonsLen = 2 * (BUTTON_SIZE + 1);
600 else
601 buttonsLen = 0;
603 if (sPtr->flags.horizontal)
604 length = sPtr->view->size.width - 4 - buttonsLen;
605 else
606 length = sPtr->view->size.height - 4 - buttonsLen;
608 knobP = (int)(sPtr->floatValue * (float)(length - knobLength(sPtr)));
610 if (sPtr->flags.arrowsPosition == WSAMinEnd)
611 sPtr->dragPoint -= 2 + buttonsLen + knobP;
612 else
613 sPtr->dragPoint -= 2 + knobP;
615 #endif /* STRICT_NEXT_BEHAVIOUR */
616 /* This does not seem necesary here since we don't know yet if the
617 * knob will be dragged later. -Dan
618 handleMotion(sPtr, pushX, pushY); */
619 break;
621 case WSDecrementWheel:
622 case WSIncrementWheel:
623 case WSKnobSlot:
624 case WSNoPart:
625 /* dummy */
626 break;
629 if (doAction && sPtr->action) {
630 (*sPtr->action) (sPtr, sPtr->clientData);
632 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
636 static float floatValueForPoint(Scroller * sPtr, int point)
638 float floatValue = 0;
639 float position;
640 int slotOfs, slotLength, knobL;
642 if (sPtr->flags.horizontal)
643 slotLength = sPtr->view->size.width - 4;
644 else
645 slotLength = sPtr->view->size.height - 4;
647 slotOfs = 2;
648 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
649 slotLength -= (BUTTON_SIZE + 1) * 2;
650 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
651 slotOfs += (BUTTON_SIZE + 1) * 2;
652 slotLength -= (BUTTON_SIZE + 1) * 2;
655 knobL = (float)knobLength(sPtr);
656 #ifdef STRICT_NEXT_BEHAVIOUR
657 if (point < slotOfs + knobL / 2)
658 position = (float)(slotOfs + knobL / 2);
659 else if (point > slotOfs + slotLength - knobL / 2)
660 position = (float)(slotOfs + slotLength - knobL / 2);
661 else
662 position = (float)point;
664 floatValue = (position - (float)(slotOfs + slotLength / 2))
665 / (float)(slotLength - knobL);
666 #else
667 /* Adjust the last point to lie inside the knob slot */
668 if (point < slotOfs)
669 position = (float)slotOfs;
670 else if (point > slotOfs + slotLength)
671 position = (float)(slotOfs + slotLength);
672 else
673 position = (float)point;
675 /* Compute the float value */
676 floatValue = (position - (float)slotOfs) / (float)(slotLength - knobL);
677 #endif
679 assert(!isnan(floatValue));
680 return floatValue;
683 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY)
685 if (sPtr->flags.draggingKnob) {
686 float newFloatValue;
687 int point;
689 if (sPtr->flags.horizontal) {
690 point = mouseX;
691 } else {
692 point = mouseY;
695 #ifndef STRICT_NEXT_BEHAVIOUR
696 point -= sPtr->dragPoint;
697 #endif
699 newFloatValue = floatValueForPoint(sPtr, point);
700 WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
701 if (sPtr->action) {
702 (*sPtr->action) (sPtr, sPtr->clientData);
703 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
705 } else {
706 int part;
708 part = locatePointInScroller(sPtr, mouseX, mouseY, False);
710 sPtr->flags.hitPart = part;
712 if (part == WSIncrementLine && sPtr->flags.decrDown) {
713 sPtr->flags.decrDown = 0;
714 sPtr->flags.incrDown = 1;
715 } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
716 sPtr->flags.incrDown = 0;
717 sPtr->flags.decrDown = 1;
718 } else if (part != WSIncrementLine && part != WSDecrementLine) {
719 sPtr->flags.incrDown = 0;
720 sPtr->flags.decrDown = 0;
725 static void autoScroll(void *clientData)
727 Scroller *sPtr = (Scroller *) clientData;
729 if (sPtr->action) {
730 (*sPtr->action) (sPtr, sPtr->clientData);
731 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
733 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
736 static void handleActionEvents(XEvent * event, void *data)
738 Scroller *sPtr = (Scroller *) data;
739 int wheelDecrement, wheelIncrement;
740 int id, dd;
742 /* check if we're really dealing with a scroller, as something
743 * might have gone wrong in the event dispatching stuff */
744 CHECK_CLASS(sPtr, WC_Scroller);
746 id = sPtr->flags.incrDown;
747 dd = sPtr->flags.decrDown;
749 switch (event->type) {
750 case EnterNotify:
751 break;
753 case LeaveNotify:
754 if (sPtr->timerID) {
755 WMDeleteTimerHandler(sPtr->timerID);
756 sPtr->timerID = NULL;
758 sPtr->flags.incrDown = 0;
759 sPtr->flags.decrDown = 0;
760 break;
762 case ButtonPress:
763 /* FIXME: change Mod1Mask with something else */
764 if (sPtr->flags.documentFullyVisible)
765 break;
767 if (sPtr->flags.horizontal) {
768 wheelDecrement = WINGsConfiguration.mouseWheelDown;
769 wheelIncrement = WINGsConfiguration.mouseWheelUp;
770 } else {
771 wheelDecrement = WINGsConfiguration.mouseWheelUp;
772 wheelIncrement = WINGsConfiguration.mouseWheelDown;
775 if (event->xbutton.button == wheelDecrement) {
776 if (event->xbutton.state & ControlMask) {
777 sPtr->flags.hitPart = WSDecrementPage;
778 } else if (event->xbutton.state & ShiftMask) {
779 sPtr->flags.hitPart = WSDecrementLine;
780 } else {
781 sPtr->flags.hitPart = WSDecrementWheel;
783 if (sPtr->action) {
784 (*sPtr->action) (sPtr, sPtr->clientData);
785 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
787 } else if (event->xbutton.button == wheelIncrement) {
788 if (event->xbutton.state & ControlMask) {
789 sPtr->flags.hitPart = WSIncrementPage;
790 } else if (event->xbutton.state & ShiftMask) {
791 sPtr->flags.hitPart = WSIncrementLine;
792 } else {
793 sPtr->flags.hitPart = WSIncrementWheel;
795 if (sPtr->action) {
796 (*sPtr->action) (sPtr, sPtr->clientData);
797 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
799 } else {
800 handlePush(sPtr, event->xbutton.x, event->xbutton.y, (event->xbutton.state & Mod1Mask)
801 || event->xbutton.button == Button2);
802 /* continue scrolling if pushed on the buttons */
803 if (sPtr->flags.hitPart == WSIncrementLine || sPtr->flags.hitPart == WSDecrementLine) {
804 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY, autoScroll, sPtr);
807 break;
809 case ButtonRelease:
810 if (sPtr->flags.draggingKnob) {
811 if (sPtr->action) {
812 (*sPtr->action) (sPtr, sPtr->clientData);
813 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
816 if (sPtr->timerID) {
817 WMDeleteTimerHandler(sPtr->timerID);
818 sPtr->timerID = NULL;
820 sPtr->flags.incrDown = 0;
821 sPtr->flags.decrDown = 0;
822 sPtr->flags.draggingKnob = 0;
823 break;
825 case MotionNotify:
826 handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
827 if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
828 && sPtr->flags.hitPart != WSDecrementLine) {
829 WMDeleteTimerHandler(sPtr->timerID);
830 sPtr->timerID = NULL;
832 break;
834 if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
835 paintScroller(sPtr);
838 static void destroyScroller(Scroller * sPtr)
840 /* we don't want autoscroll try to scroll a freed widget */
841 if (sPtr->timerID) {
842 WMDeleteTimerHandler(sPtr->timerID);
845 wfree(sPtr);