Initial revision
[wmaker-crm.git] / WINGs / wscroller.c
blob5d395c23fb1f408cefce6c958925f323e938a69e
5 #include "WINGsP.h"
7 /* undefine will disable the autoadjusting of the knob dimple to be
8 * directly below the cursor
9 * DOES NOT WORK */
10 #undef STRICT_NEXT_BEHAVIOUR
12 #define AUTOSCROLL_INITIAL_DELAY 200
14 #define AUTOSCROLL_DELAY 40
17 typedef struct W_Scroller {
18 W_Class widgetClass;
19 W_View *view;
21 void *clientData;
22 WMAction *action;
24 float knobProportion;
25 float floatValue;
27 WMHandlerID timerID; /* for continuous scrolling mode */
29 #ifndef STRICT_NEXT_BEHAVIOUR
30 int dragPoint; /* point where the knob is being
31 * dragged */
32 #endif
33 struct {
34 WMScrollArrowPosition arrowsPosition:3;
36 unsigned int horizontal:1;
38 WMScrollerPart hitPart:3;
40 /* */
41 unsigned int documentFullyVisible:1; /* document is fully visible */
43 unsigned int prevSelected:1;
45 unsigned int pushed:1;
47 unsigned int incrDown:1; /* whether increment button is down */
49 unsigned int decrDown:1;
51 unsigned int draggingKnob:1;
53 unsigned int configured:1;
55 unsigned int redrawPending:1;
56 } flags;
57 } Scroller;
61 #define DEFAULT_HEIGHT 60
62 #define DEFAULT_WIDTH SCROLLER_WIDTH
63 #define DEFAULT_ARROWS_POSITION WSAMinEnd
67 static void destroyScroller(Scroller *sPtr);
68 static void paintScroller(Scroller *sPtr);
70 static void resizeScroller();
71 static void handleEvents(XEvent *event, void *data);
72 static void handleActionEvents(XEvent *event, void *data);
74 static void handleMotion(Scroller *sPtr, int mouseX, int mouseY);
77 W_ViewProcedureTable _ScrollerViewProcedures = {
78 NULL,
79 resizeScroller,
80 NULL
86 WMScroller*
87 WMCreateScroller(WMWidget *parent)
89 Scroller *sPtr;
91 sPtr = wmalloc(sizeof(Scroller));
92 memset(sPtr, 0, sizeof(Scroller));
94 sPtr->widgetClass = WC_Scroller;
96 sPtr->view = W_CreateView(W_VIEW(parent));
97 if (!sPtr->view) {
98 free(sPtr);
99 return NULL;
101 sPtr->view->self = sPtr;
103 sPtr->flags.documentFullyVisible = 1;
105 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
106 |ClientMessageMask, handleEvents, sPtr);
108 resizeScroller(sPtr, DEFAULT_WIDTH, DEFAULT_WIDTH);
109 sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
111 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
112 |EnterWindowMask|LeaveWindowMask|ButtonMotionMask,
113 handleActionEvents, sPtr);
115 sPtr->flags.hitPart = WSNoPart;
117 return sPtr;
122 void
123 WMSetScrollerArrowsPosition(WMScroller *sPtr, WMScrollArrowPosition position)
125 sPtr->flags.arrowsPosition = position;
126 if (sPtr->view->flags.realized) {
127 paintScroller(sPtr);
132 static void
133 resizeScroller(WMScroller *sPtr, unsigned int width, unsigned int height)
135 if (width > height) {
136 sPtr->flags.horizontal = 1;
137 W_ResizeView(sPtr->view, width, SCROLLER_WIDTH);
138 } else {
139 sPtr->flags.horizontal = 0;
140 W_ResizeView(sPtr->view, SCROLLER_WIDTH, height);
142 if (sPtr->view->flags.realized) {
143 paintScroller(sPtr);
148 void
149 WMSetScrollerAction(WMScroller *sPtr, WMAction *action, void *clientData)
151 CHECK_CLASS(sPtr, WC_Scroller);
153 sPtr->action = action;
155 sPtr->clientData = clientData;
159 void
160 WMSetScrollerParameters(WMScroller *sPtr, float floatValue,
161 float knobProportion)
163 CHECK_CLASS(sPtr, WC_Scroller);
165 if (floatValue < 0.0)
166 sPtr->floatValue = 0.0;
167 else if (floatValue > 1.0)
168 sPtr->floatValue = 1.0;
169 else
170 sPtr->floatValue = floatValue;
172 if (knobProportion <= 0.0) {
174 sPtr->knobProportion = 0.0;
175 sPtr->flags.documentFullyVisible = 0;
177 } else if (knobProportion >= 1.0) {
179 sPtr->knobProportion = 1.0;
180 sPtr->flags.documentFullyVisible = 1;
182 } else {
183 sPtr->knobProportion = knobProportion;
184 sPtr->flags.documentFullyVisible = 0;
187 if (sPtr->view->flags.realized)
188 paintScroller(sPtr);
192 float
193 WMGetScrollerKnobProportion(WMScroller *sPtr)
195 CHECK_CLASS(sPtr, WC_Scroller);
197 return sPtr->knobProportion;
201 float
202 WMGetScrollerValue(WMScroller *sPtr)
204 CHECK_CLASS(sPtr, WC_Scroller);
206 return sPtr->floatValue;
210 WMScrollerPart
211 WMGetScrollerHitPart(WMScroller *sPtr)
213 CHECK_CLASS(sPtr, WC_Scroller);
215 return sPtr->flags.hitPart;
219 static void
220 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, bsize;
228 W_Pixmap *arrow;
230 #ifndef DOUBLE_BUFFER
231 GC gc = scr->lightGC;
232 #endif
234 bsize = SCROLLER_WIDTH - 4;
237 if (part == 0) { /* decrement button */
238 if (sPtr->flags.horizontal) {
239 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
240 ofs = view->size.width - 2*(bsize+1) - 1;
241 } else {
242 ofs = 2;
244 if (sPtr->flags.decrDown)
245 arrow = scr->hiLeftArrow;
246 else
247 arrow = scr->leftArrow;
249 } else {
250 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
251 ofs = view->size.height - 2*(bsize+1) - 1;
252 } else {
253 ofs = 2;
255 if (sPtr->flags.decrDown)
256 arrow = scr->hiUpArrow;
257 else
258 arrow = scr->upArrow;
261 #ifndef DOUBLE_BUFFER
262 if (sPtr->flags.decrDown)
263 gc = W_GC(scr->white);
264 #endif
265 } else { /* increment button */
266 if (sPtr->flags.horizontal) {
267 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
268 ofs = view->size.width - bsize+1 - 3;
269 } else {
270 ofs = 2 + bsize+1;
272 if (sPtr->flags.incrDown)
273 arrow = scr->hiRightArrow;
274 else
275 arrow = scr->rightArrow;
276 } else {
277 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
278 ofs = view->size.height - bsize+1 - 3;
279 } else {
280 ofs = 2 + bsize+1;
282 if (sPtr->flags.incrDown)
283 arrow = scr->hiDownArrow;
284 else
285 arrow = scr->downArrow;
288 #ifndef DOUBLE_BUFFER
289 if (sPtr->flags.incrDown)
290 gc = scr->whiteGC;
291 #endif
295 if (sPtr->flags.horizontal) {
297 /* paint button */
298 #ifndef DOUBLE_BUFFER
299 XFillRectangle(scr->display, d, gc,
300 ofs+1, 2+1, bsize+1-3, bsize-3);
301 #else
302 if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
303 XFillRectangle(scr->display, d, W_GC(scr->white),
304 ofs+1, 2+1, bsize+1-3, bsize-3);
305 #endif /* DOUBLE_BUFFER */
306 W_DrawRelief(scr, d, ofs, 2, bsize, bsize, WRRaised);
308 /* paint arrow */
309 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
310 XSetClipOrigin(scr->display, scr->clipGC,
311 ofs + (bsize - arrow->width) / 2,
312 2 + (bsize - arrow->height) / 2);
314 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
315 0, 0, arrow->width, arrow->height,
316 ofs + (bsize - arrow->width) / 2,
317 2 + (bsize - arrow->height) / 2);
319 } else { /* vertical */
321 /* paint button */
322 #ifndef DOUBLE_BUFFER
323 XFillRectangle(scr->display, d, gc,
324 2+1, ofs+1, bsize-3, bsize+1-3);
325 #else
326 if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
327 XFillRectangle(scr->display, d, W_GC(scr->white),
328 2+1, ofs+1, bsize-3, bsize+1-3);
329 #endif /* DOUBLE_BUFFER */
330 W_DrawRelief(scr, d, 2, ofs, bsize, bsize, WRRaised);
332 /* paint arrow */
334 XSetClipMask(scr->display, scr->clipGC, arrow->mask);
335 XSetClipOrigin(scr->display, scr->clipGC,
336 2 + (bsize - arrow->width) / 2,
337 ofs + (bsize - arrow->height) / 2);
338 XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
339 0, 0, arrow->width, arrow->height,
340 2 + (bsize - arrow->width) / 2,
341 ofs + (bsize - arrow->height) / 2);
346 static int
347 knobLength(Scroller *sPtr)
349 int tmp, length;
352 if (sPtr->flags.horizontal)
353 length = sPtr->view->size.width - 4;
354 else
355 length = sPtr->view->size.height - 4;
357 if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
358 length -= (SCROLLER_WIDTH - 4 + 1)*2;
359 } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
360 length -= (SCROLLER_WIDTH - 4 + 1)*2;
363 tmp = (int)((float)length * sPtr->knobProportion + 0.5);
364 /* keep minimum size */
365 if (tmp < SCROLLER_WIDTH-4)
366 tmp = SCROLLER_WIDTH-4;
368 return tmp;
372 static void
373 paintScroller(Scroller *sPtr)
375 WMView *view = sPtr->view;
376 WMScreen *scr = view->screen;
377 #ifdef DOUBLE_BUFFER
378 Pixmap d;
379 #else
380 Drawable d = view->window;
381 #endif
382 int length, ofs;
383 float knobP, knobL;
386 #ifdef DOUBLE_BUFFER
387 d = XCreatePixmap(scr->display, view->window, view->size.width,
388 view->size.height, scr->depth);
389 XFillRectangle(scr->display, d, W_GC(scr->gray), 0, 0,
390 view->size.width, view->size.height);
391 #endif
393 XDrawRectangle(scr->display, d, W_GC(scr->black), 0, 0,
394 view->size.width-1, view->size.height-1);
395 #ifndef DOUBLE_BUFFER
396 XDrawRectangle(scr->display, d, W_GC(scr->gray), 1, 1,
397 view->size.width-3, view->size.height-3);
398 #endif
400 if (sPtr->flags.horizontal)
401 length = view->size.width - 4;
402 else
403 length = view->size.height - 4;
405 if (sPtr->flags.documentFullyVisible) {
406 XFillRectangle(scr->display, d, scr->stippleGC, 2, 2,
407 view->size.width-4, view->size.height-4);
408 } else {
409 if (sPtr->flags.arrowsPosition==WSAMaxEnd) {
410 ofs = 0;
411 length -= (SCROLLER_WIDTH - 4 + 1)*2;
412 } else if (sPtr->flags.arrowsPosition==WSAMinEnd) {
413 ofs = (SCROLLER_WIDTH - 4 + 1)*2;
414 length -= (SCROLLER_WIDTH - 4 + 1)*2;
415 } else {
416 ofs = 0;
419 knobL = (float)knobLength(sPtr);
421 knobP = sPtr->floatValue * ((float)length - knobL);
424 if (sPtr->flags.horizontal) {
425 /* before */
426 XFillRectangle(scr->display, d, scr->stippleGC,
427 ofs+2, 2, (int)knobP, view->size.height-4);
429 /* knob */
430 #ifndef DOUBLE_BUFFER
431 XFillRectangle(scr->display, d, scr->lightGC,
432 ofs+2+(int)knobP+2, 2+2, (int)knobL-4,
433 view->size.height-4-4);
434 #endif
435 W_DrawRelief(scr, d, ofs+2+(int)knobP, 2, (int)knobL,
436 view->size.height-4, WRRaised);
438 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
439 scr->copyGC, 0, 0,
440 scr->scrollerDimple->width, scr->scrollerDimple->height,
441 ofs+2+(int)knobP+((int)knobL-scr->scrollerDimple->width-1)/2,
442 (view->size.height-scr->scrollerDimple->height-1)/2);
444 /* after */
445 if ((int)(knobP+knobL) < length)
446 XFillRectangle(scr->display, d, scr->stippleGC,
447 ofs+2+(int)(knobP+knobL), 2,
448 length-(int)(knobP+knobL),
449 view->size.height-4);
450 } else {
451 /* before */
452 if (knobP>0.0)
453 XFillRectangle(scr->display, d, scr->stippleGC,
454 2, ofs+2, view->size.width-4, (int)knobP);
456 /* knob */
457 #ifndef DOUBLE_BUFFER
458 XFillRectangle(scr->display, d, scr->lightGC,
459 2+2, ofs+2+(int)knobP+2,
460 view->size.width-4-4, (int)knobL-4);
461 #endif
462 XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
463 scr->copyGC, 0, 0,
464 scr->scrollerDimple->width, scr->scrollerDimple->height,
465 (view->size.width-scr->scrollerDimple->width-1)/2,
466 ofs+2+(int)knobP+((int)knobL-scr->scrollerDimple->height-1)/2);
468 W_DrawRelief(scr, d, 2, ofs+2+(int)knobP,
469 view->size.width-4, (int)knobL, WRRaised);
471 /* after */
472 if ((int)(knobP+knobL) < length)
473 XFillRectangle(scr->display, d, scr->stippleGC,
474 2, ofs+2+(int)(knobP+knobL),
475 view->size.width-4,
476 length-(int)(knobP+knobL));
479 if (sPtr->flags.arrowsPosition != WSANone) {
480 paintArrow(sPtr, d, 0);
481 paintArrow(sPtr, d, 1);
485 #ifdef DOUBLE_BUFFER
486 XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0,
487 view->size.width, view->size.height, 0, 0);
488 XFreePixmap(scr->display, d);
489 #endif
494 static void
495 handleEvents(XEvent *event, void *data)
497 Scroller *sPtr = (Scroller*)data;
499 CHECK_CLASS(data, WC_Scroller);
502 switch (event->type) {
503 case Expose:
504 if (event->xexpose.count==0)
505 paintScroller(sPtr);
506 break;
508 case DestroyNotify:
509 destroyScroller(sPtr);
510 break;
517 * locatePointInScroller-
518 * Return the part of the scroller where the point is located.
520 static WMScrollerPart
521 locatePointInScroller(Scroller *sPtr, int x, int y, int alternate)
523 int width = sPtr->view->size.width;
524 int height = sPtr->view->size.height;
525 int c, p1, p2, p3, p4, p5, p6;
526 int knobL, slotL;
529 /* if there is no knob... */
530 if (sPtr->flags.documentFullyVisible)
531 return WSKnobSlot;
533 if (sPtr->flags.horizontal)
534 c = x;
535 else
536 c = y;
538 /* p1 p2 p3 p4 p5 p6
539 * | | |###########| |#####| | |
540 * | < | > |###########| O |#####| < | > |
541 * | | |###########| |#####| | |
544 if (sPtr->flags.arrowsPosition == WSAMinEnd) {
545 p1 = 18;
546 p2 = 36;
548 if (sPtr->flags.horizontal) {
549 slotL = width - 36;
550 p5 = width;
551 } else {
552 slotL = height - 36;
553 p5 = height;
555 p6 = p5;
556 } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
557 if (sPtr->flags.horizontal) {
558 slotL = width - 36;
559 p6 = width - 18;
560 } else {
561 slotL = height - 36;
562 p6 = height - 18;
564 p5 = p6 - 18;
566 p1 = p2 = 0;
567 } else {
568 /* no arrows */
569 p1 = p2 = 0;
571 if (sPtr->flags.horizontal) {
572 slotL = p5 = p6 = width;
573 } else {
574 slotL = p5 = p6 = height;
578 knobL = knobLength(sPtr);
579 p3 = p2 + (int)((float)(slotL-knobL) * sPtr->floatValue);
580 p4 = p3 + knobL;
582 /* uses a mix of the NS and Win ways of doing scroll page */
583 if (c <= p1)
584 return alternate ? WSDecrementPage : WSDecrementLine;
585 else if (c <= p2)
586 return alternate ? WSIncrementPage : WSIncrementLine;
587 else if (c <= p3)
588 return WSDecrementPage;
589 else if (c <= p4)
590 return WSKnob;
591 else if (c <= p5)
592 return WSIncrementPage;
593 else if (c <= p6)
594 return alternate ? WSDecrementPage : WSDecrementLine;
595 else
596 return alternate ? WSIncrementPage : WSIncrementLine;
601 static void
602 handlePush(Scroller *sPtr, int pushX, int pushY, int alternate)
604 WMScrollerPart part;
605 int doAction = 0;
607 part = locatePointInScroller(sPtr, pushX, pushY, alternate);
609 sPtr->flags.hitPart = part;
611 switch (part) {
612 case WSIncrementLine:
613 sPtr->flags.incrDown = 1;
614 doAction = 1;
615 break;
617 case WSIncrementPage:
618 doAction = 1;
619 break;
621 case WSDecrementLine:
622 sPtr->flags.decrDown = 1;
623 doAction = 1;
624 break;
626 case WSDecrementPage:
627 doAction = 1;
628 break;
630 case WSKnob:
631 sPtr->flags.draggingKnob = 1;
632 #ifndef STRICT_NEXT_BEHAVIOUR
633 if (sPtr->flags.horizontal)
634 sPtr->dragPoint = pushX;
635 else
636 sPtr->dragPoint = pushY;
639 int noButtons = (sPtr->flags.arrowsPosition == WSANone);
640 int length, knobP;
642 if (sPtr->flags.horizontal)
643 length = sPtr->view->size.width - 4;
644 else
645 length = sPtr->view->size.height - 4;
647 if (!noButtons)
648 length -= 36;
650 knobP = (int)(sPtr->floatValue * (float)(length-knobLength(sPtr)));
652 if (sPtr->flags.arrowsPosition == WSAMinEnd)
653 sPtr->dragPoint -= 2 + (noButtons ? 0 : 36) + knobP;
654 else
655 sPtr->dragPoint -= 2 + knobP;
657 #endif /* STRICT_NEXT_BEHAVIOUR */
658 handleMotion(sPtr, pushX, pushY);
659 break;
661 case WSKnobSlot:
662 case WSNoPart:
663 /* dummy */
664 break;
667 if (doAction && sPtr->action) {
668 (*sPtr->action)(sPtr, sPtr->clientData);
673 static float
674 floatValueForPoint(int slotOfs, int slotLength, int knobLength, int point)
676 float floatValue = 0;
677 float position;
679 #ifdef STRICT_NEXT_BEHAVIOUR
680 if (point < slotOfs + knobLength/2)
681 position = (float)(slotOfs + knobLength/2);
682 else if (point > slotOfs + slotLength - knobLength/2)
683 position = (float)(slotOfs + slotLength - knobLength/2);
684 else
685 position = (float)point;
687 floatValue = (position-(float)(slotOfs+slotLength/2))
688 /(float)(slotLength-knobLength);
689 #else
690 /* Adjust the last point to lie inside the knob slot */
691 if (point < slotOfs)
692 position = (float)slotOfs;
693 else if (point > slotOfs + slotLength)
694 position = (float)(slotOfs + slotLength);
695 else
696 position = (float)point;
698 /* Compute the float value */
699 floatValue = (position-(float)slotOfs) / (float)(slotLength-knobLength);
700 #endif
702 return floatValue;
706 static void
707 handleMotion(Scroller *sPtr, int mouseX, int mouseY)
709 int slotOffset;
710 int slotLength;
711 int noButtons = (sPtr->flags.arrowsPosition == WSANone);
713 if (sPtr->flags.arrowsPosition == WSAMinEnd)
714 slotOffset = 2 + (noButtons ? 0 : 36);
715 else
716 slotOffset = 2;
718 if (sPtr->flags.draggingKnob) {
719 float newFloatValue;
720 #ifdef STRICT_NEXT_BEHAVIOUR
721 if (sPtr->flags.horizontal) {
722 slotLength = sPtr->view->size.width-4-(noButtons ? 0 : 36);
723 newFloatValue = floatValueForPoint(slotOffset, slotLength,
724 (int)(slotLength*sPtr->knobProportion),
725 mouseX);
726 } else {
727 slotLength = sPtr->view->size.height-4-(noButtons ? 0 : 36);
728 newFloatValue = floatValueForPoint(slotOffset, slotLength,
729 (int)(slotLength*sPtr->knobProportion),
730 mouseY);
732 #else
733 if (sPtr->flags.horizontal) {
734 slotLength = sPtr->view->size.width-4-(noButtons ? 0 : 36);
735 newFloatValue = floatValueForPoint(slotOffset, slotLength,
736 (int)(slotLength*sPtr->knobProportion),
737 mouseX-sPtr->dragPoint);
738 } else {
739 slotLength = sPtr->view->size.height-4-(noButtons ? 0 : 36);
740 newFloatValue = floatValueForPoint(slotOffset, slotLength,
741 (int)(slotLength*sPtr->knobProportion),
742 mouseY-sPtr->dragPoint);
744 #endif /* !STRICT_NEXT_BEHAVIOUR */
745 WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
746 if (sPtr->action) {
747 (*sPtr->action)(sPtr, sPtr->clientData);
749 } else {
750 int part;
752 part = locatePointInScroller(sPtr, mouseX, mouseY, False);
754 sPtr->flags.hitPart = part;
756 if (part == WSIncrementLine && sPtr->flags.decrDown) {
757 sPtr->flags.decrDown = 0;
758 sPtr->flags.incrDown = 1;
759 } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
760 sPtr->flags.incrDown = 0;
761 sPtr->flags.decrDown = 1;
762 } else if (part != WSIncrementLine && part != WSDecrementLine) {
763 sPtr->flags.incrDown = 0;
764 sPtr->flags.decrDown = 0;
770 static void
771 autoScroll(void *clientData)
773 Scroller *sPtr = (Scroller*)clientData;
775 if (sPtr->action) {
776 (*sPtr->action)(sPtr, sPtr->clientData);
778 sPtr->timerID= WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
782 static void
783 handleActionEvents(XEvent *event, void *data)
785 Scroller *sPtr = (Scroller*)data;
786 int id, dd;
789 /* check if we're really dealing with a scroller, as something
790 * might have gone wrong in the event dispatching stuff */
791 CHECK_CLASS(sPtr, WC_Scroller);
793 id = sPtr->flags.incrDown;
794 dd = sPtr->flags.decrDown;
796 switch (event->type) {
797 case EnterNotify:
799 break;
801 case LeaveNotify:
802 if (sPtr->timerID) {
803 WMDeleteTimerHandler(sPtr->timerID);
804 sPtr->timerID = NULL;
806 sPtr->flags.incrDown = 0;
807 sPtr->flags.decrDown = 0;
808 break;
810 case ButtonPress:
811 /* FIXME: change Mod1Mask with something else */
812 handlePush(sPtr, event->xbutton.x, event->xbutton.y,
813 (event->xbutton.state & Mod1Mask)
814 ||event->xbutton.button==Button2);
815 /* continue scrolling if pushed on the buttons */
816 if (sPtr->flags.hitPart == WSIncrementLine
817 || sPtr->flags.hitPart == WSDecrementLine) {
818 sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY,
819 autoScroll, sPtr);
821 break;
823 case ButtonRelease:
824 if (sPtr->flags.draggingKnob) {
825 if (sPtr->action) {
826 (*sPtr->action)(sPtr, sPtr->clientData);
829 if (sPtr->timerID) {
830 WMDeleteTimerHandler(sPtr->timerID);
831 sPtr->timerID = NULL;
833 sPtr->flags.incrDown = 0;
834 sPtr->flags.decrDown = 0;
835 sPtr->flags.draggingKnob = 0;
836 break;
838 case MotionNotify:
839 handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
840 if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
841 && sPtr->flags.hitPart != WSDecrementLine) {
842 WMDeleteTimerHandler(sPtr->timerID);
843 sPtr->timerID = NULL;
845 break;
847 if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
848 paintScroller(sPtr);
853 static void
854 destroyScroller(Scroller *sPtr)
856 /* we don't want autoscroll try to scroll a freed widget */
857 if (sPtr->timerID) {
858 WMDeleteTimerHandler(sPtr->timerID);
861 free(sPtr);