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 }