Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wscroller.c
1
2 #include "WINGsP.h"
3
4 #include <math.h>
5
6 /* undefine will disable the autoadjusting of the knob dimple to be
7  * directly below the cursor
8  * DOES NOT WORK */
9 #undef STRICT_NEXT_BEHAVIOUR
10
11 #define AUTOSCROLL_INITIAL_DELAY        200
12
13 #define AUTOSCROLL_DELAY                40
14
15 char *WMScrollerDidScrollNotification = "WMScrollerDidScrollNotification";
16
17 typedef struct W_Scroller {
18         W_Class widgetClass;
19         W_View *view;
20
21         void *clientData;
22         WMAction *action;
23
24         float knobProportion;
25         float floatValue;
26
27         WMHandlerID timerID;    /* for continuous scrolling mode */
28
29 #ifndef STRICT_NEXT_BEHAVIOUR
30         int dragPoint;          /* point where the knob is being
31                                  * dragged */
32 #endif
33         struct {
34                 WMScrollArrowPosition arrowsPosition:4;
35
36                 unsigned int horizontal:1;
37
38                 WMScrollerPart hitPart:4;
39
40                 /* */
41                 unsigned int documentFullyVisible:1;    /* document is fully visible */
42
43                 unsigned int prevSelected:1;
44
45                 unsigned int pushed:1;
46
47                 unsigned int incrDown:1;        /* whether increment button is down */
48
49                 unsigned int decrDown:1;
50
51                 unsigned int draggingKnob:1;
52
53                 unsigned int configured:1;
54
55                 unsigned int redrawPending:1;
56         } flags;
57 } Scroller;
58
59 #define DEFAULT_HEIGHT          60
60 #define DEFAULT_WIDTH           SCROLLER_WIDTH
61 #define DEFAULT_ARROWS_POSITION WSAMinEnd
62
63 #define BUTTON_SIZE             ((SCROLLER_WIDTH) - 4)
64
65 static void destroyScroller(Scroller * sPtr);
66 static void paintScroller(Scroller * sPtr);
67
68 static void willResizeScroller();
69 static void handleEvents(XEvent * event, void *data);
70 static void handleActionEvents(XEvent * event, void *data);
71
72 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY);
73
74 W_ViewDelegate _ScrollerViewDelegate = {
75         NULL,
76         NULL,
77         NULL,
78         NULL,
79         willResizeScroller
80 };
81
82 WMScroller *WMCreateScroller(WMWidget * parent)
83 {
84         Scroller *sPtr;
85
86         sPtr = wmalloc(sizeof(Scroller));
87         memset(sPtr, 0, sizeof(Scroller));
88
89         sPtr->widgetClass = WC_Scroller;
90
91         sPtr->view = W_CreateView(W_VIEW(parent));
92         if (!sPtr->view) {
93                 wfree(sPtr);
94                 return NULL;
95         }
96         sPtr->view->self = sPtr;
97
98         sPtr->view->delegate = &_ScrollerViewDelegate;
99
100         sPtr->flags.documentFullyVisible = 1;
101
102         WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask
103                              | ClientMessageMask, handleEvents, sPtr);
104
105         W_ResizeView(sPtr->view, DEFAULT_WIDTH, DEFAULT_WIDTH);
106         sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
107
108         WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
109                              | EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, sPtr);
110
111         sPtr->flags.hitPart = WSNoPart;
112
113         sPtr->floatValue = 0.0;
114         sPtr->knobProportion = 1.0;
115
116         return sPtr;
117 }
118
119 void WMSetScrollerArrowsPosition(WMScroller * sPtr, WMScrollArrowPosition position)
120 {
121         sPtr->flags.arrowsPosition = position;
122         if (sPtr->view->flags.realized) {
123                 paintScroller(sPtr);
124         }
125 }
126
127 static void willResizeScroller(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height)
128 {
129         WMScroller *sPtr = (WMScroller *) view->self;
130
131         if (*width > *height) {
132                 sPtr->flags.horizontal = 1;
133                 *height = SCROLLER_WIDTH;
134         } else {
135                 sPtr->flags.horizontal = 0;
136                 *width = SCROLLER_WIDTH;
137         }
138 }
139
140 void WMSetScrollerAction(WMScroller * sPtr, WMAction * action, void *clientData)
141 {
142         CHECK_CLASS(sPtr, WC_Scroller);
143
144         sPtr->action = action;
145
146         sPtr->clientData = clientData;
147 }
148
149 void WMSetScrollerParameters(WMScroller * sPtr, float floatValue, float knobProportion)
150 {
151         CHECK_CLASS(sPtr, WC_Scroller);
152
153         assert(!isnan(floatValue));
154
155         if (floatValue < 0.0)
156                 sPtr->floatValue = 0.0;
157         else if (floatValue > 1.0)
158                 sPtr->floatValue = 1.0;
159         else
160                 sPtr->floatValue = floatValue;
161
162         if (knobProportion <= 0.0) {
163
164                 sPtr->knobProportion = 0.0;
165                 sPtr->flags.documentFullyVisible = 0;
166
167         } else if (knobProportion >= 1.0) {
168
169                 sPtr->knobProportion = 1.0;
170                 sPtr->flags.documentFullyVisible = 1;
171
172         } else {
173                 sPtr->knobProportion = knobProportion;
174                 sPtr->flags.documentFullyVisible = 0;
175         }
176
177         if (sPtr->view->flags.realized)
178                 paintScroller(sPtr);
179
180         /*   WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL); */
181 }
182
183 float WMGetScrollerKnobProportion(WMScroller * sPtr)
184 {
185         CHECK_CLASS(sPtr, WC_Scroller);
186
187         return sPtr->knobProportion;
188 }
189
190 float WMGetScrollerValue(WMScroller * sPtr)
191 {
192         CHECK_CLASS(sPtr, WC_Scroller);
193
194         return sPtr->floatValue;
195 }
196
197 WMScrollerPart WMGetScrollerHitPart(WMScroller * sPtr)
198 {
199         CHECK_CLASS(sPtr, WC_Scroller);
200
201         return sPtr->flags.hitPart;
202 }
203
204 static void paintArrow(WMScroller * sPtr, Drawable d, int part)
205 /*
206  * part- 0 paints the decrement arrow, 1 the increment arrow
207  */
208 {
209         WMView *view = sPtr->view;
210         WMScreen *scr = view->screen;
211         int ofs;
212         W_Pixmap *arrow;
213
214 #ifndef DOUBLE_BUFFER
215         GC gc = scr->lightGC;
216 #endif
217
218         if (part == 0) {        /* decrement button */
219                 if (sPtr->flags.horizontal) {
220                         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
221                                 ofs = view->size.width - 2 * (BUTTON_SIZE + 1) - 1;
222                         } else {
223                                 ofs = 2;
224                         }
225                         if (sPtr->flags.decrDown)
226                                 arrow = scr->hiLeftArrow;
227                         else
228                                 arrow = scr->leftArrow;
229
230                 } else {
231                         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
232                                 ofs = view->size.height - 2 * (BUTTON_SIZE + 1) - 1;
233                         } else {
234                                 ofs = 2;
235                         }
236                         if (sPtr->flags.decrDown)
237                                 arrow = scr->hiUpArrow;
238                         else
239                                 arrow = scr->upArrow;
240                 }
241
242 #ifndef DOUBLE_BUFFER
243                 if (sPtr->flags.decrDown)
244                         gc = WMColorGC(scr->white);
245 #endif
246         } else {                /* increment button */
247                 if (sPtr->flags.horizontal) {
248                         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
249                                 ofs = view->size.width - BUTTON_SIZE + 1 - 3;
250                         } else {
251                                 ofs = 2 + BUTTON_SIZE + 1;
252                         }
253                         if (sPtr->flags.incrDown)
254                                 arrow = scr->hiRightArrow;
255                         else
256                                 arrow = scr->rightArrow;
257                 } else {
258                         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
259                                 ofs = view->size.height - BUTTON_SIZE + 1 - 3;
260                         } else {
261                                 ofs = 2 + BUTTON_SIZE + 1;
262                         }
263                         if (sPtr->flags.incrDown)
264                                 arrow = scr->hiDownArrow;
265                         else
266                                 arrow = scr->downArrow;
267                 }
268
269 #ifndef DOUBLE_BUFFER
270                 if (sPtr->flags.incrDown)
271                         gc = scr->whiteGC;
272 #endif
273         }
274
275         if (sPtr->flags.horizontal) {
276                 /* paint button */
277 #ifndef DOUBLE_BUFFER
278                 XFillRectangle(scr->display, d, gc, ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
279 #else
280                 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
281                         XFillRectangle(scr->display, d, WMColorGC(scr->white),
282                                        ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
283 #endif                          /* DOUBLE_BUFFER */
284                 W_DrawRelief(scr, d, ofs, 2, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
285
286                 /* paint arrow */
287                 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
288                 XSetClipOrigin(scr->display, scr->clipGC,
289                                ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
290
291                 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
292                           0, 0, arrow->width, arrow->height,
293                           ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
294
295         } else {                /* vertical */
296
297                 /* paint button */
298 #ifndef DOUBLE_BUFFER
299                 XFillRectangle(scr->display, d, gc, 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
300 #else
301                 if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
302                         XFillRectangle(scr->display, d, WMColorGC(scr->white),
303                                        2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
304 #endif                          /* DOUBLE_BUFFER */
305                 W_DrawRelief(scr, d, 2, ofs, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
306
307                 /* paint arrow */
308
309                 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
310                 XSetClipOrigin(scr->display, scr->clipGC,
311                                2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
312                 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
313                           0, 0, arrow->width, arrow->height,
314                           2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
315         }
316 }
317
318 static int knobLength(Scroller * sPtr)
319 {
320         int tmp, length;
321
322         if (sPtr->flags.horizontal)
323                 length = sPtr->view->size.width - 4;
324         else
325                 length = sPtr->view->size.height - 4;
326
327         if (sPtr->flags.arrowsPosition != WSANone) {
328                 length -= 2 * (BUTTON_SIZE + 1);
329         }
330
331         tmp = (int)((float)length * sPtr->knobProportion + 0.5);
332         /* keep minimum size */
333         if (tmp < BUTTON_SIZE)
334                 tmp = BUTTON_SIZE;
335
336         return tmp;
337 }
338
339 static void paintScroller(Scroller * sPtr)
340 {
341         WMView *view = sPtr->view;
342         WMScreen *scr = view->screen;
343 #ifdef DOUBLE_BUFFER
344         Pixmap d;
345 #else
346         Drawable d = view->window;
347 #endif
348         int length, ofs;
349         float knobP, knobL;
350
351 #ifdef DOUBLE_BUFFER
352         d = XCreatePixmap(scr->display, view->window, view->size.width, view->size.height, scr->depth);
353         XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0, view->size.width, view->size.height);
354 #endif
355
356         XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0, view->size.width - 1, view->size.height - 1);
357 #ifndef DOUBLE_BUFFER
358         XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1, view->size.width - 3, view->size.height - 3);
359 #endif
360
361         if (sPtr->flags.horizontal)
362                 length = view->size.width - 4;
363         else
364                 length = view->size.height - 4;
365
366         if (sPtr->flags.documentFullyVisible) {
367                 XFillRectangle(scr->display, d, scr->stippleGC, 2, 2, view->size.width - 4, view->size.height - 4);
368         } else {
369                 ofs = 2;
370                 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
371                         length -= (BUTTON_SIZE + 1) * 2;
372                 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
373                         ofs += (BUTTON_SIZE + 1) * 2;
374                         length -= (BUTTON_SIZE + 1) * 2;
375                 }
376
377                 knobL = (float)knobLength(sPtr);
378
379                 knobP = sPtr->floatValue * ((float)length - knobL);
380
381                 if (sPtr->flags.horizontal) {
382                         /* before */
383                         XFillRectangle(scr->display, d, scr->stippleGC, ofs, 2, (int)knobP, view->size.height - 4);
384
385                         /* knob */
386 #ifndef DOUBLE_BUFFER
387                         XFillRectangle(scr->display, d, scr->lightGC,
388                                        ofs + (int)knobP + 2, 2 + 2, (int)knobL - 4, view->size.height - 4 - 4);
389 #endif
390                         W_DrawRelief(scr, d, ofs + (int)knobP, 2, (int)knobL, view->size.height - 4, WRRaised);
391
392                         XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
393                                   scr->copyGC, 0, 0,
394                                   scr->scrollerDimple->width, scr->scrollerDimple->height,
395                                   ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->width - 1) / 2,
396                                   (view->size.height - scr->scrollerDimple->height - 1) / 2);
397
398                         /* after */
399                         if ((int)(knobP + knobL) < length)
400                                 XFillRectangle(scr->display, d, scr->stippleGC,
401                                                ofs + (int)(knobP + knobL), 2,
402                                                length - (int)(knobP + knobL), view->size.height - 4);
403                 } else {
404                         /* before */
405                         if (knobP > 0.0)
406                                 XFillRectangle(scr->display, d, scr->stippleGC,
407                                                2, ofs, view->size.width - 4, (int)knobP);
408
409                         /* knob */
410 #ifndef DOUBLE_BUFFER
411                         XFillRectangle(scr->display, d, scr->lightGC,
412                                        2 + 2, ofs + (int)knobP + 2, view->size.width - 4 - 4, (int)knobL - 4);
413 #endif
414                         XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
415                                   scr->copyGC, 0, 0,
416                                   scr->scrollerDimple->width, scr->scrollerDimple->height,
417                                   (view->size.width - scr->scrollerDimple->width - 1) / 2,
418                                   ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->height - 1) / 2);
419
420                         W_DrawRelief(scr, d, 2, ofs + (int)knobP, view->size.width - 4, (int)knobL, WRRaised);
421
422                         /* after */
423                         if ((int)(knobP + knobL) < length)
424                                 XFillRectangle(scr->display, d, scr->stippleGC,
425                                                2, ofs + (int)(knobP + knobL),
426                                                view->size.width - 4, length - (int)(knobP + knobL));
427                 }
428
429                 if (sPtr->flags.arrowsPosition != WSANone) {
430                         paintArrow(sPtr, d, 0);
431                         paintArrow(sPtr, d, 1);
432                 }
433         }
434
435 #ifdef DOUBLE_BUFFER
436         XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
437         XFreePixmap(scr->display, d);
438 #endif
439 }
440
441 static void handleEvents(XEvent * event, void *data)
442 {
443         Scroller *sPtr = (Scroller *) data;
444
445         CHECK_CLASS(data, WC_Scroller);
446
447         switch (event->type) {
448         case Expose:
449                 if (event->xexpose.count == 0)
450                         paintScroller(sPtr);
451                 break;
452
453         case DestroyNotify:
454                 destroyScroller(sPtr);
455                 break;
456         }
457 }
458
459 /*
460  * locatePointInScroller-
461  *     Return the part of the scroller where the point is located.
462  */
463 static WMScrollerPart locatePointInScroller(Scroller * sPtr, int x, int y, int alternate)
464 {
465         int width = sPtr->view->size.width;
466         int height = sPtr->view->size.height;
467         int c, p1, p2, p3, p4, p5, p6;
468         int knobL, slotL;
469
470         /* if there is no knob... */
471         if (sPtr->flags.documentFullyVisible)
472                 return WSKnobSlot;
473
474         if (sPtr->flags.horizontal)
475                 c = x;
476         else
477                 c = y;
478
479         /*     p1  p2           p3           p4   p5   p6
480          * |   |   |###########|             |#####|   |   |
481          * | < | > |###########|      O      |#####| < | > |
482          * |   |   |###########|             |#####|   |   |
483          */
484
485         if (sPtr->flags.arrowsPosition == WSAMinEnd) {
486                 p1 = 18;
487                 p2 = 36;
488
489                 if (sPtr->flags.horizontal) {
490                         slotL = width - 36;
491                         p5 = width;
492                 } else {
493                         slotL = height - 36;
494                         p5 = height;
495                 }
496                 p6 = p5;
497         } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
498                 if (sPtr->flags.horizontal) {
499                         slotL = width - 36;
500                         p6 = width - 18;
501                 } else {
502                         slotL = height - 36;
503                         p6 = height - 18;
504                 }
505                 p5 = p6 - 18;
506
507                 p1 = p2 = 0;
508         } else {
509                 /* no arrows */
510                 p1 = p2 = 0;
511
512                 if (sPtr->flags.horizontal) {
513                         slotL = p5 = p6 = width;
514                 } else {
515                         slotL = p5 = p6 = height;
516                 }
517         }
518
519         knobL = knobLength(sPtr);
520         p3 = p2 + (int)((float)(slotL - knobL) * sPtr->floatValue);
521         p4 = p3 + knobL;
522
523         /* uses a mix of the NS and Win ways of doing scroll page */
524         if (c <= p1)
525                 return alternate ? WSDecrementPage : WSDecrementLine;
526         else if (c <= p2)
527                 return alternate ? WSIncrementPage : WSIncrementLine;
528         else if (c <= p3)
529                 return WSDecrementPage;
530         else if (c <= p4)
531                 return WSKnob;
532         else if (c <= p5)
533                 return WSIncrementPage;
534         else if (c <= p6)
535                 return alternate ? WSDecrementPage : WSDecrementLine;
536         else
537                 return alternate ? WSIncrementPage : WSIncrementLine;
538 }
539
540 static void handlePush(Scroller * sPtr, int pushX, int pushY, int alternate)
541 {
542         WMScrollerPart part;
543         int doAction = 0;
544
545         part = locatePointInScroller(sPtr, pushX, pushY, alternate);
546
547         sPtr->flags.hitPart = part;
548
549         switch (part) {
550         case WSIncrementLine:
551                 sPtr->flags.incrDown = 1;
552                 doAction = 1;
553                 break;
554
555         case WSIncrementPage:
556                 doAction = 1;
557                 break;
558
559         case WSDecrementLine:
560                 sPtr->flags.decrDown = 1;
561                 doAction = 1;
562                 break;
563
564         case WSDecrementPage:
565                 doAction = 1;
566                 break;
567
568         case WSKnob:
569                 sPtr->flags.draggingKnob = 1;
570 #ifndef STRICT_NEXT_BEHAVIOUR
571                 if (sPtr->flags.horizontal)
572                         sPtr->dragPoint = pushX;
573                 else
574                         sPtr->dragPoint = pushY;
575
576                 {
577                         int length, knobP;
578                         int buttonsLen;
579
580                         if (sPtr->flags.arrowsPosition != WSANone)
581                                 buttonsLen = 2 * (BUTTON_SIZE + 1);
582                         else
583                                 buttonsLen = 0;
584
585                         if (sPtr->flags.horizontal)
586                                 length = sPtr->view->size.width - 4 - buttonsLen;
587                         else
588                                 length = sPtr->view->size.height - 4 - buttonsLen;
589
590                         knobP = (int)(sPtr->floatValue * (float)(length - knobLength(sPtr)));
591
592                         if (sPtr->flags.arrowsPosition == WSAMinEnd)
593                                 sPtr->dragPoint -= 2 + buttonsLen + knobP;
594                         else
595                                 sPtr->dragPoint -= 2 + knobP;
596                 }
597 #endif                          /* STRICT_NEXT_BEHAVIOUR */
598                 /* This does not seem necesary here since we don't know yet if the
599                  * knob will be dragged later. -Dan
600                  handleMotion(sPtr, pushX, pushY); */
601                 break;
602
603         case WSDecrementWheel:
604         case WSIncrementWheel:
605         case WSKnobSlot:
606         case WSNoPart:
607                 /* dummy */
608                 break;
609         }
610
611         if (doAction && sPtr->action) {
612                 (*sPtr->action) (sPtr, sPtr->clientData);
613
614                 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
615         }
616 }
617
618 static float floatValueForPoint(Scroller * sPtr, int point)
619 {
620         float floatValue = 0;
621         float position;
622         int slotOfs, slotLength, knobL;
623
624         if (sPtr->flags.horizontal)
625                 slotLength = sPtr->view->size.width - 4;
626         else
627                 slotLength = sPtr->view->size.height - 4;
628
629         slotOfs = 2;
630         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
631                 slotLength -= (BUTTON_SIZE + 1) * 2;
632         } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
633                 slotOfs += (BUTTON_SIZE + 1) * 2;
634                 slotLength -= (BUTTON_SIZE + 1) * 2;
635         }
636
637         knobL = (float)knobLength(sPtr);
638 #ifdef STRICT_NEXT_BEHAVIOUR
639         if (point < slotOfs + knobL / 2)
640                 position = (float)(slotOfs + knobL / 2);
641         else if (point > slotOfs + slotLength - knobL / 2)
642                 position = (float)(slotOfs + slotLength - knobL / 2);
643         else
644                 position = (float)point;
645
646         floatValue = (position - (float)(slotOfs + slotLength / 2))
647             / (float)(slotLength - knobL);
648 #else
649         /* Adjust the last point to lie inside the knob slot */
650         if (point < slotOfs)
651                 position = (float)slotOfs;
652         else if (point > slotOfs + slotLength)
653                 position = (float)(slotOfs + slotLength);
654         else
655                 position = (float)point;
656
657         /* Compute the float value */
658         floatValue = (position - (float)slotOfs) / (float)(slotLength - knobL);
659 #endif
660
661         assert(!isnan(floatValue));
662         return floatValue;
663 }
664
665 static void handleMotion(Scroller * sPtr, int mouseX, int mouseY)
666 {
667         if (sPtr->flags.draggingKnob) {
668                 float newFloatValue;
669                 int point;
670
671                 if (sPtr->flags.horizontal) {
672                         point = mouseX;
673                 } else {
674                         point = mouseY;
675                 }
676
677 #ifndef STRICT_NEXT_BEHAVIOUR
678                 point -= sPtr->dragPoint;
679 #endif
680
681                 newFloatValue = floatValueForPoint(sPtr, point);
682                 WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
683                 if (sPtr->action) {
684                         (*sPtr->action) (sPtr, sPtr->clientData);
685                         WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
686                 }
687         } else {
688                 int part;
689
690                 part = locatePointInScroller(sPtr, mouseX, mouseY, False);
691
692                 sPtr->flags.hitPart = part;
693
694                 if (part == WSIncrementLine && sPtr->flags.decrDown) {
695                         sPtr->flags.decrDown = 0;
696                         sPtr->flags.incrDown = 1;
697                 } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
698                         sPtr->flags.incrDown = 0;
699                         sPtr->flags.decrDown = 1;
700                 } else if (part != WSIncrementLine && part != WSDecrementLine) {
701                         sPtr->flags.incrDown = 0;
702                         sPtr->flags.decrDown = 0;
703                 }
704         }
705 }
706
707 static void autoScroll(void *clientData)
708 {
709         Scroller *sPtr = (Scroller *) clientData;
710
711         if (sPtr->action) {
712                 (*sPtr->action) (sPtr, sPtr->clientData);
713                 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
714         }
715         sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
716 }
717
718 static void handleActionEvents(XEvent * event, void *data)
719 {
720         Scroller *sPtr = (Scroller *) data;
721         int wheelDecrement, wheelIncrement;
722         int id, dd;
723
724         /* check if we're really dealing with a scroller, as something
725          * might have gone wrong in the event dispatching stuff */
726         CHECK_CLASS(sPtr, WC_Scroller);
727
728         id = sPtr->flags.incrDown;
729         dd = sPtr->flags.decrDown;
730
731         switch (event->type) {
732         case EnterNotify:
733                 break;
734
735         case LeaveNotify:
736                 if (sPtr->timerID) {
737                         WMDeleteTimerHandler(sPtr->timerID);
738                         sPtr->timerID = NULL;
739                 }
740                 sPtr->flags.incrDown = 0;
741                 sPtr->flags.decrDown = 0;
742                 break;
743
744         case ButtonPress:
745                 /* FIXME: change Mod1Mask with something else */
746                 if (sPtr->flags.documentFullyVisible)
747                         break;
748
749                 if (sPtr->flags.horizontal) {
750                         wheelDecrement = WINGsConfiguration.mouseWheelDown;
751                         wheelIncrement = WINGsConfiguration.mouseWheelUp;
752                 } else {
753                         wheelDecrement = WINGsConfiguration.mouseWheelUp;
754                         wheelIncrement = WINGsConfiguration.mouseWheelDown;
755                 }
756
757                 if (event->xbutton.button == wheelDecrement) {
758                         if (event->xbutton.state & ControlMask) {
759                                 sPtr->flags.hitPart = WSDecrementPage;
760                         } else if (event->xbutton.state & ShiftMask) {
761                                 sPtr->flags.hitPart = WSDecrementLine;
762                         } else {
763                                 sPtr->flags.hitPart = WSDecrementWheel;
764                         }
765                         if (sPtr->action) {
766                                 (*sPtr->action) (sPtr, sPtr->clientData);
767                                 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
768                         }
769                 } else if (event->xbutton.button == wheelIncrement) {
770                         if (event->xbutton.state & ControlMask) {
771                                 sPtr->flags.hitPart = WSIncrementPage;
772                         } else if (event->xbutton.state & ShiftMask) {
773                                 sPtr->flags.hitPart = WSIncrementLine;
774                         } else {
775                                 sPtr->flags.hitPart = WSIncrementWheel;
776                         }
777                         if (sPtr->action) {
778                                 (*sPtr->action) (sPtr, sPtr->clientData);
779                                 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
780                         }
781                 } else {
782                         handlePush(sPtr, event->xbutton.x, event->xbutton.y, (event->xbutton.state & Mod1Mask)
783                                    || event->xbutton.button == Button2);
784                         /* continue scrolling if pushed on the buttons */
785                         if (sPtr->flags.hitPart == WSIncrementLine || sPtr->flags.hitPart == WSDecrementLine) {
786                                 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY, autoScroll, sPtr);
787                         }
788                 }
789                 break;
790
791         case ButtonRelease:
792                 if (sPtr->flags.draggingKnob) {
793                         if (sPtr->action) {
794                                 (*sPtr->action) (sPtr, sPtr->clientData);
795                                 WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
796                         }
797                 }
798                 if (sPtr->timerID) {
799                         WMDeleteTimerHandler(sPtr->timerID);
800                         sPtr->timerID = NULL;
801                 }
802                 sPtr->flags.incrDown = 0;
803                 sPtr->flags.decrDown = 0;
804                 sPtr->flags.draggingKnob = 0;
805                 break;
806
807         case MotionNotify:
808                 handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
809                 if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
810                     && sPtr->flags.hitPart != WSDecrementLine) {
811                         WMDeleteTimerHandler(sPtr->timerID);
812                         sPtr->timerID = NULL;
813                 }
814                 break;
815         }
816         if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
817                 paintScroller(sPtr);
818 }
819
820 static void destroyScroller(Scroller * sPtr)
821 {
822         /* we don't want autoscroll try to scroll a freed widget */
823         if (sPtr->timerID) {
824                 WMDeleteTimerHandler(sPtr->timerID);
825         }
826
827         wfree(sPtr);
828 }