Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wscroller.c
dissimilarity index 93%
index 0197ea2..33afa4e 100644 (file)
-
-
-
-
-#include "WINGsP.h"
-
-#include <math.h>
-
-/* undefine will disable the autoadjusting of the knob dimple to be
- * directly below the cursor
- * DOES NOT WORK */
-#undef STRICT_NEXT_BEHAVIOUR
-
-#define AUTOSCROLL_INITIAL_DELAY       200
-
-#define AUTOSCROLL_DELAY               40
-
-
-char *WMScrollerDidScrollNotification = "WMScrollerDidScrollNotification";
-
-
-
-typedef struct W_Scroller {
-    W_Class widgetClass;
-    W_View *view;
-
-    void *clientData;
-    WMAction *action;
-
-    float knobProportion;
-    float floatValue;
-
-    WMHandlerID timerID;              /* for continuous scrolling mode */
-
-#ifndef STRICT_NEXT_BEHAVIOUR
-    int dragPoint;                    /* point where the knob is being
-    * dragged */
-#endif
-    struct {
-        WMScrollArrowPosition arrowsPosition:4;
-
-        unsigned int horizontal:1;
-
-        WMScrollerPart hitPart:4;
-
-        /* */
-        unsigned int documentFullyVisible:1;   /* document is fully visible */
-
-        unsigned int prevSelected:1;
-
-        unsigned int pushed:1;
-
-        unsigned int incrDown:1;      /* whether increment button is down */
-
-        unsigned int decrDown:1;
-
-        unsigned int draggingKnob:1;
-
-        unsigned int configured:1;
-
-        unsigned int redrawPending:1;
-    } flags;
-} Scroller;
-
-
-
-#define DEFAULT_HEIGHT         60
-#define DEFAULT_WIDTH          SCROLLER_WIDTH
-#define DEFAULT_ARROWS_POSITION        WSAMinEnd
-
-
-#define BUTTON_SIZE             ((SCROLLER_WIDTH) - 4)
-
-
-static void destroyScroller(Scroller *sPtr);
-static void paintScroller(Scroller *sPtr);
-
-static void willResizeScroller();
-static void handleEvents(XEvent *event, void *data);
-static void handleActionEvents(XEvent *event, void *data);
-
-static void handleMotion(Scroller *sPtr, int mouseX, int mouseY);
-
-
-W_ViewDelegate _ScrollerViewDelegate = {
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    willResizeScroller
-};
-
-
-
-
-WMScroller*
-WMCreateScroller(WMWidget *parent)
-{
-    Scroller *sPtr;
-
-    sPtr = wmalloc(sizeof(Scroller));
-    memset(sPtr, 0, sizeof(Scroller));
-
-    sPtr->widgetClass = WC_Scroller;
-
-    sPtr->view = W_CreateView(W_VIEW(parent));
-    if (!sPtr->view) {
-        wfree(sPtr);
-        return NULL;
-    }
-    sPtr->view->self = sPtr;
-
-    sPtr->view->delegate = &_ScrollerViewDelegate;
-
-    sPtr->flags.documentFullyVisible = 1;
-
-    WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
-                         |ClientMessageMask, handleEvents, sPtr);
-
-    W_ResizeView(sPtr->view, DEFAULT_WIDTH, DEFAULT_WIDTH);
-    sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
-
-    WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
-                         |EnterWindowMask|LeaveWindowMask|ButtonMotionMask,
-                         handleActionEvents, sPtr);
-
-    sPtr->flags.hitPart = WSNoPart;
-
-    sPtr->floatValue = 0.0;
-    sPtr->knobProportion = 1.0;
-
-    return sPtr;
-}
-
-
-
-void
-WMSetScrollerArrowsPosition(WMScroller *sPtr, WMScrollArrowPosition position)
-{
-    sPtr->flags.arrowsPosition = position;
-    if (sPtr->view->flags.realized) {
-        paintScroller(sPtr);
-    }
-}
-
-
-static void
-willResizeScroller(W_ViewDelegate *self, WMView *view,
-                   unsigned int *width, unsigned int *height)
-{
-    WMScroller *sPtr = (WMScroller*)view->self;
-
-    if (*width > *height) {
-        sPtr->flags.horizontal = 1;
-        *height = SCROLLER_WIDTH;
-    } else {
-        sPtr->flags.horizontal = 0;
-        *width = SCROLLER_WIDTH;
-    }
-}
-
-
-void
-WMSetScrollerAction(WMScroller *sPtr, WMAction *action, void *clientData)
-{
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    sPtr->action = action;
-
-    sPtr->clientData = clientData;
-}
-
-
-void
-WMSetScrollerParameters(WMScroller *sPtr, float floatValue,
-                        float knobProportion)
-{
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    assert(!isnan(floatValue));
-
-    if (floatValue < 0.0)
-        sPtr->floatValue = 0.0;
-    else if (floatValue > 1.0)
-        sPtr->floatValue = 1.0;
-    else
-        sPtr->floatValue = floatValue;
-
-    if (knobProportion <= 0.0) {
-
-        sPtr->knobProportion = 0.0;
-        sPtr->flags.documentFullyVisible = 0;
-
-    } else if (knobProportion >= 1.0) {
-
-        sPtr->knobProportion = 1.0;
-        sPtr->flags.documentFullyVisible = 1;
-
-    } else {
-        sPtr->knobProportion = knobProportion;
-        sPtr->flags.documentFullyVisible = 0;
-    }
-
-    if (sPtr->view->flags.realized)
-        paintScroller(sPtr);
-
-    /*   WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);*/
-}
-
-
-float
-WMGetScrollerKnobProportion(WMScroller *sPtr)
-{
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    return sPtr->knobProportion;
-}
-
-
-float
-WMGetScrollerValue(WMScroller *sPtr)
-{
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    return sPtr->floatValue;
-}
-
-
-WMScrollerPart
-WMGetScrollerHitPart(WMScroller *sPtr)
-{
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    return sPtr->flags.hitPart;
-}
-
-
-static void
-paintArrow(WMScroller *sPtr, Drawable d, int part)
-/*
- * part- 0 paints the decrement arrow, 1 the increment arrow
- */
-{
-    WMView *view = sPtr->view;
-    WMScreen *scr = view->screen;
-    int ofs;
-    W_Pixmap *arrow;
-
-#ifndef DOUBLE_BUFFER
-    GC gc = scr->lightGC;
-#endif
-
-    if (part == 0) { /* decrement button */
-        if (sPtr->flags.horizontal) {
-            if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
-                ofs = view->size.width - 2*(BUTTON_SIZE+1) - 1;
-            } else {
-                ofs = 2;
-            }
-            if (sPtr->flags.decrDown)
-                arrow = scr->hiLeftArrow;
-            else
-                arrow = scr->leftArrow;
-
-        } else {
-            if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
-                ofs = view->size.height - 2*(BUTTON_SIZE+1) - 1;
-            } else {
-                ofs = 2;
-            }
-            if (sPtr->flags.decrDown)
-                arrow = scr->hiUpArrow;
-            else
-                arrow = scr->upArrow;
-        }
-
-#ifndef DOUBLE_BUFFER
-        if (sPtr->flags.decrDown)
-            gc = WMColorGC(scr->white);
-#endif
-    } else { /* increment button */
-        if (sPtr->flags.horizontal) {
-            if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
-                ofs = view->size.width - BUTTON_SIZE+1 - 3;
-            } else {
-                ofs = 2 + BUTTON_SIZE+1;
-            }
-            if (sPtr->flags.incrDown)
-                arrow = scr->hiRightArrow;
-            else
-                arrow = scr->rightArrow;
-        } else {
-            if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
-                ofs = view->size.height - BUTTON_SIZE+1 - 3;
-            } else {
-                ofs = 2 + BUTTON_SIZE+1;
-            }
-            if (sPtr->flags.incrDown)
-                arrow = scr->hiDownArrow;
-            else
-                arrow = scr->downArrow;
-        }
-
-#ifndef DOUBLE_BUFFER
-        if (sPtr->flags.incrDown)
-            gc = scr->whiteGC;
-#endif
-    }
-
-
-    if (sPtr->flags.horizontal) {
-        /* paint button */
-#ifndef DOUBLE_BUFFER
-        XFillRectangle(scr->display, d, gc,
-                       ofs+1, 2+1, BUTTON_SIZE+1-3, BUTTON_SIZE-3);
-#else
-        if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
-            XFillRectangle(scr->display, d, WMColorGC(scr->white),
-                           ofs+1, 2+1, BUTTON_SIZE+1-3, BUTTON_SIZE-3);
-#endif /* DOUBLE_BUFFER */
-        W_DrawRelief(scr, d, ofs, 2, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
-
-        /* paint arrow */
-        XSetClipMask(scr->display, scr->clipGC, arrow->mask);
-        XSetClipOrigin(scr->display, scr->clipGC,
-                       ofs + (BUTTON_SIZE - arrow->width) / 2,
-                       2 + (BUTTON_SIZE - arrow->height) / 2);
-
-        XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
-                  0, 0, arrow->width, arrow->height,
-                  ofs + (BUTTON_SIZE - arrow->width) / 2,
-                  2 + (BUTTON_SIZE - arrow->height) / 2);
-
-    } else { /* vertical */
-
-        /* paint button */
-#ifndef DOUBLE_BUFFER
-        XFillRectangle(scr->display, d, gc,
-                       2+1, ofs+1, BUTTON_SIZE-3, BUTTON_SIZE+1-3);
-#else
-        if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
-            XFillRectangle(scr->display, d, WMColorGC(scr->white),
-                           2+1, ofs+1, BUTTON_SIZE-3, BUTTON_SIZE+1-3);
-#endif /* DOUBLE_BUFFER */
-        W_DrawRelief(scr, d, 2, ofs, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
-
-        /* paint arrow */
-
-        XSetClipMask(scr->display, scr->clipGC, arrow->mask);
-        XSetClipOrigin(scr->display, scr->clipGC,
-                       2 + (BUTTON_SIZE - arrow->width) / 2,
-                       ofs + (BUTTON_SIZE - arrow->height) / 2);
-        XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
-                  0, 0, arrow->width, arrow->height,
-                  2 + (BUTTON_SIZE - arrow->width) / 2,
-                  ofs + (BUTTON_SIZE - arrow->height) / 2);
-    }
-}
-
-
-static int
-knobLength(Scroller *sPtr)
-{
-    int tmp, length;
-
-
-    if (sPtr->flags.horizontal)
-        length = sPtr->view->size.width - 4;
-    else
-        length = sPtr->view->size.height - 4;
-
-    if (sPtr->flags.arrowsPosition != WSANone) {
-        length -= 2*(BUTTON_SIZE+1);
-    }
-
-    tmp = (int)((float)length * sPtr->knobProportion + 0.5);
-    /* keep minimum size */
-    if (tmp < BUTTON_SIZE)
-        tmp = BUTTON_SIZE;
-
-    return tmp;
-}
-
-
-static void
-paintScroller(Scroller *sPtr)
-{
-    WMView *view = sPtr->view;
-    WMScreen *scr = view->screen;
-#ifdef DOUBLE_BUFFER
-    Pixmap d;
-#else
-    Drawable d = view->window;
-#endif
-    int length, ofs;
-    float knobP, knobL;
-
-
-#ifdef DOUBLE_BUFFER
-    d = XCreatePixmap(scr->display, view->window, view->size.width,
-                      view->size.height, scr->depth);
-    XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0,
-                   view->size.width, view->size.height);
-#endif
-
-    XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0,
-                   view->size.width-1, view->size.height-1);
-#ifndef DOUBLE_BUFFER
-    XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1,
-                   view->size.width-3, view->size.height-3);
-#endif
-
-    if (sPtr->flags.horizontal)
-        length = view->size.width - 4;
-    else
-        length = view->size.height - 4;
-
-    if (sPtr->flags.documentFullyVisible) {
-        XFillRectangle(scr->display, d, scr->stippleGC, 2, 2,
-                       view->size.width-4, view->size.height-4);
-    } else {
-        ofs = 2;
-        if (sPtr->flags.arrowsPosition==WSAMaxEnd) {
-            length -= (BUTTON_SIZE+1)*2;
-        } else if (sPtr->flags.arrowsPosition==WSAMinEnd) {
-            ofs += (BUTTON_SIZE+1)*2;
-            length -= (BUTTON_SIZE+1)*2;
-        }
-
-        knobL = (float)knobLength(sPtr);
-
-        knobP = sPtr->floatValue * ((float)length - knobL);
-
-        if (sPtr->flags.horizontal) {
-            /* before */
-            XFillRectangle(scr->display, d, scr->stippleGC,
-                           ofs, 2, (int)knobP, view->size.height-4);
-
-            /* knob */
-#ifndef DOUBLE_BUFFER
-            XFillRectangle(scr->display, d, scr->lightGC,
-                           ofs+(int)knobP+2, 2+2, (int)knobL-4,
-                           view->size.height-4-4);
-#endif
-            W_DrawRelief(scr, d, ofs+(int)knobP, 2, (int)knobL,
-                         view->size.height-4, WRRaised);
-
-            XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
-                      scr->copyGC, 0, 0,
-                      scr->scrollerDimple->width, scr->scrollerDimple->height,
-                      ofs+(int)knobP+((int)knobL-scr->scrollerDimple->width-1)/2,
-                      (view->size.height-scr->scrollerDimple->height-1)/2);
-
-            /* after */
-            if ((int)(knobP+knobL) < length)
-                XFillRectangle(scr->display, d, scr->stippleGC,
-                               ofs+(int)(knobP+knobL), 2,
-                               length-(int)(knobP+knobL),
-                               view->size.height-4);
-        } else {
-            /* before */
-            if (knobP>0.0)
-                XFillRectangle(scr->display, d, scr->stippleGC,
-                               2, ofs, view->size.width-4, (int)knobP);
-
-            /* knob */
-#ifndef DOUBLE_BUFFER
-            XFillRectangle(scr->display, d, scr->lightGC,
-                           2+2, ofs+(int)knobP+2,
-                           view->size.width-4-4, (int)knobL-4);
-#endif
-            XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
-                      scr->copyGC, 0, 0,
-                      scr->scrollerDimple->width, scr->scrollerDimple->height,
-                      (view->size.width-scr->scrollerDimple->width-1)/2,
-                      ofs+(int)knobP+((int)knobL-scr->scrollerDimple->height-1)/2);
-
-            W_DrawRelief(scr, d, 2, ofs+(int)knobP,
-                         view->size.width-4, (int)knobL, WRRaised);
-
-            /* after */
-            if ((int)(knobP+knobL) < length)
-                XFillRectangle(scr->display, d, scr->stippleGC,
-                               2, ofs+(int)(knobP+knobL),
-                               view->size.width-4,
-                               length-(int)(knobP+knobL));
-        }
-
-        if (sPtr->flags.arrowsPosition != WSANone) {
-            paintArrow(sPtr, d, 0);
-            paintArrow(sPtr, d, 1);
-        }
-    }
-
-#ifdef DOUBLE_BUFFER
-    XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0,
-              view->size.width, view->size.height, 0, 0);
-    XFreePixmap(scr->display, d);
-#endif
-}
-
-
-
-static void
-handleEvents(XEvent *event, void *data)
-{
-    Scroller *sPtr = (Scroller*)data;
-
-    CHECK_CLASS(data, WC_Scroller);
-
-
-    switch (event->type) {
-    case Expose:
-        if (event->xexpose.count==0)
-            paintScroller(sPtr);
-        break;
-
-    case DestroyNotify:
-        destroyScroller(sPtr);
-        break;
-    }
-}
-
-
-
-/*
- * locatePointInScroller-
- *     Return the part of the scroller where the point is located.
- */
-static WMScrollerPart
-locatePointInScroller(Scroller *sPtr, int x, int y, int alternate)
-{
-    int width = sPtr->view->size.width;
-    int height = sPtr->view->size.height;
-    int c, p1, p2, p3, p4, p5, p6;
-    int knobL, slotL;
-
-
-    /* if there is no knob... */
-    if (sPtr->flags.documentFullyVisible)
-        return WSKnobSlot;
-
-    if (sPtr->flags.horizontal)
-        c = x;
-    else
-        c = y;
-
-    /*     p1  p2           p3           p4   p5   p6
-     * |   |   |###########|             |#####|   |   |
-     * | < | > |###########|      O      |#####| < | > |
-     * |   |   |###########|             |#####|   |   |
-     */
-
-    if (sPtr->flags.arrowsPosition == WSAMinEnd) {
-        p1 = 18;
-        p2 = 36;
-
-        if (sPtr->flags.horizontal) {
-            slotL = width - 36;
-            p5 = width;
-        } else {
-            slotL = height - 36;
-            p5 = height;
-        }
-        p6 = p5;
-    } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
-        if (sPtr->flags.horizontal) {
-            slotL = width - 36;
-            p6 = width - 18;
-        } else {
-            slotL = height - 36;
-            p6 = height - 18;
-        }
-        p5 = p6 - 18;
-
-        p1 = p2 = 0;
-    } else {
-        /* no arrows */
-        p1 = p2 = 0;
-
-        if (sPtr->flags.horizontal) {
-            slotL = p5 = p6 = width;
-        } else {
-            slotL = p5 = p6 = height;
-        }
-    }
-
-    knobL = knobLength(sPtr);
-    p3 = p2 + (int)((float)(slotL-knobL) * sPtr->floatValue);
-    p4 = p3 + knobL;
-
-    /* uses a mix of the NS and Win ways of doing scroll page */
-    if (c <= p1)
-        return alternate ? WSDecrementPage : WSDecrementLine;
-    else if (c <= p2)
-        return alternate ? WSIncrementPage : WSIncrementLine;
-    else if (c <= p3)
-        return WSDecrementPage;
-    else if (c <= p4)
-        return WSKnob;
-    else if (c <= p5)
-        return WSIncrementPage;
-    else if (c <= p6)
-        return alternate ? WSDecrementPage : WSDecrementLine;
-    else
-        return alternate ? WSIncrementPage : WSIncrementLine;
-}
-
-
-
-static void
-handlePush(Scroller *sPtr, int pushX, int pushY, int alternate)
-{
-    WMScrollerPart part;
-    int doAction = 0;
-
-    part = locatePointInScroller(sPtr, pushX, pushY, alternate);
-
-    sPtr->flags.hitPart = part;
-
-    switch (part) {
-    case WSIncrementLine:
-        sPtr->flags.incrDown = 1;
-        doAction = 1;
-        break;
-
-    case WSIncrementPage:
-        doAction = 1;
-        break;
-
-    case WSDecrementLine:
-        sPtr->flags.decrDown = 1;
-        doAction = 1;
-        break;
-
-    case WSDecrementPage:
-        doAction = 1;
-        break;
-
-    case WSKnob:
-        sPtr->flags.draggingKnob = 1;
-#ifndef STRICT_NEXT_BEHAVIOUR
-        if (sPtr->flags.horizontal)
-            sPtr->dragPoint = pushX;
-        else
-            sPtr->dragPoint = pushY;
-
-        {
-            int length, knobP;
-            int buttonsLen;
-
-            if (sPtr->flags.arrowsPosition != WSANone)
-                buttonsLen = 2*(BUTTON_SIZE+1);
-            else
-                buttonsLen = 0;
-
-            if (sPtr->flags.horizontal)
-                length = sPtr->view->size.width - 4 - buttonsLen;
-            else
-                length = sPtr->view->size.height - 4 - buttonsLen;
-
-            knobP = (int)(sPtr->floatValue * (float)(length-knobLength(sPtr)));
-
-            if (sPtr->flags.arrowsPosition == WSAMinEnd)
-                sPtr->dragPoint -= 2 + buttonsLen + knobP;
-            else
-                sPtr->dragPoint -= 2 + knobP;
-        }
-#endif /* STRICT_NEXT_BEHAVIOUR */
-        /* This does not seem necesary here since we don't know yet if the
-         * knob will be dragged later. -Dan
-         handleMotion(sPtr, pushX, pushY); */
-        break;
-
-    case WSDecrementWheel:
-    case WSIncrementWheel:
-    case WSKnobSlot:
-    case WSNoPart:
-        /* dummy */
-        break;
-    }
-
-    if (doAction && sPtr->action) {
-        (*sPtr->action)(sPtr, sPtr->clientData);
-
-        WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
-    }
-}
-
-
-static float
-floatValueForPoint(Scroller *sPtr, int point)
-{
-    float floatValue = 0;
-    float position;
-    int slotOfs, slotLength, knobL;
-
-    if (sPtr->flags.horizontal)
-        slotLength = sPtr->view->size.width - 4;
-    else
-        slotLength = sPtr->view->size.height - 4;
-
-    slotOfs = 2;
-    if (sPtr->flags.arrowsPosition==WSAMaxEnd) {
-        slotLength -= (BUTTON_SIZE+1)*2;
-    } else if (sPtr->flags.arrowsPosition==WSAMinEnd) {
-        slotOfs += (BUTTON_SIZE+1)*2;
-        slotLength -= (BUTTON_SIZE+1)*2;
-    }
-
-    knobL = (float)knobLength(sPtr);
-#ifdef STRICT_NEXT_BEHAVIOUR
-    if (point < slotOfs + knobL/2)
-        position = (float)(slotOfs + knobL/2);
-    else if (point > slotOfs + slotLength - knobL/2)
-        position = (float)(slotOfs + slotLength - knobL/2);
-    else
-        position = (float)point;
-
-    floatValue = (position-(float)(slotOfs+slotLength/2))
-        /(float)(slotLength-knobL);
-#else
-    /* Adjust the last point to lie inside the knob slot */
-    if (point < slotOfs)
-        position = (float)slotOfs;
-    else if (point > slotOfs + slotLength)
-        position = (float)(slotOfs + slotLength);
-    else
-        position = (float)point;
-
-    /* Compute the float value */
-    floatValue = (position-(float)slotOfs) / (float)(slotLength-knobL);
-#endif
-
-    assert(!isnan(floatValue));
-    return floatValue;
-}
-
-
-static void
-handleMotion(Scroller *sPtr, int mouseX, int mouseY)
-{
-    if (sPtr->flags.draggingKnob) {
-        float newFloatValue;
-        int point;
-
-        if (sPtr->flags.horizontal) {
-            point = mouseX;
-        } else {
-            point = mouseY;
-        }
-
-#ifndef STRICT_NEXT_BEHAVIOUR
-        point -= sPtr->dragPoint;
-#endif
-
-        newFloatValue = floatValueForPoint(sPtr, point);
-        WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
-        if (sPtr->action) {
-            (*sPtr->action)(sPtr, sPtr->clientData);
-            WMPostNotificationName(WMScrollerDidScrollNotification, sPtr,
-                                   NULL);
-        }
-    } else {
-        int part;
-
-        part = locatePointInScroller(sPtr, mouseX, mouseY, False);
-
-        sPtr->flags.hitPart = part;
-
-        if (part == WSIncrementLine && sPtr->flags.decrDown) {
-            sPtr->flags.decrDown = 0;
-            sPtr->flags.incrDown = 1;
-        } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
-            sPtr->flags.incrDown = 0;
-            sPtr->flags.decrDown = 1;
-        } else if (part != WSIncrementLine && part != WSDecrementLine) {
-            sPtr->flags.incrDown = 0;
-            sPtr->flags.decrDown = 0;
-        }
-    }
-}
-
-
-static void
-autoScroll(void *clientData)
-{
-    Scroller *sPtr = (Scroller*)clientData;
-
-    if (sPtr->action) {
-        (*sPtr->action)(sPtr, sPtr->clientData);
-        WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
-    }
-    sPtr->timerID= WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
-}
-
-
-static void
-handleActionEvents(XEvent *event, void *data)
-{
-    Scroller *sPtr = (Scroller*)data;
-    int wheelDecrement, wheelIncrement;
-    int id, dd;
-
-
-    /* check if we're really dealing with a scroller, as something
-     * might have gone wrong in the event dispatching stuff */
-    CHECK_CLASS(sPtr, WC_Scroller);
-
-    id = sPtr->flags.incrDown;
-    dd = sPtr->flags.decrDown;
-
-    switch (event->type) {
-    case EnterNotify:
-        break;
-
-    case LeaveNotify:
-        if (sPtr->timerID) {
-            WMDeleteTimerHandler(sPtr->timerID);
-            sPtr->timerID = NULL;
-        }
-        sPtr->flags.incrDown = 0;
-        sPtr->flags.decrDown = 0;
-        break;
-
-    case ButtonPress:
-        /* FIXME: change Mod1Mask with something else */
-        if (sPtr->flags.documentFullyVisible)
-            break;
-
-        if (sPtr->flags.horizontal) {
-            wheelDecrement = WINGsConfiguration.mouseWheelDown;
-            wheelIncrement = WINGsConfiguration.mouseWheelUp;
-        } else {
-            wheelDecrement = WINGsConfiguration.mouseWheelUp;
-            wheelIncrement = WINGsConfiguration.mouseWheelDown;
-        }
-
-        if (event->xbutton.button == wheelDecrement) {
-            if (event->xbutton.state & ControlMask) {
-                sPtr->flags.hitPart = WSDecrementPage;
-            } else if (event->xbutton.state & ShiftMask) {
-                sPtr->flags.hitPart = WSDecrementLine;
-            } else {
-                sPtr->flags.hitPart = WSDecrementWheel;
-            }
-            if (sPtr->action) {
-                (*sPtr->action)(sPtr, sPtr->clientData);
-                WMPostNotificationName(WMScrollerDidScrollNotification, sPtr,
-                                       NULL);
-            }
-        } else if (event->xbutton.button == wheelIncrement) {
-            if (event->xbutton.state & ControlMask) {
-                sPtr->flags.hitPart = WSIncrementPage;
-            } else if (event->xbutton.state & ShiftMask) {
-                sPtr->flags.hitPart = WSIncrementLine;
-            } else {
-                sPtr->flags.hitPart = WSIncrementWheel;
-            }
-            if (sPtr->action) {
-                (*sPtr->action)(sPtr, sPtr->clientData);
-                WMPostNotificationName(WMScrollerDidScrollNotification, sPtr,
-                                       NULL);
-            }
-        } else {
-            handlePush(sPtr, event->xbutton.x, event->xbutton.y,
-                       (event->xbutton.state & Mod1Mask)
-                       ||event->xbutton.button==Button2);
-            /* continue scrolling if pushed on the buttons */
-            if (sPtr->flags.hitPart == WSIncrementLine
-                || sPtr->flags.hitPart == WSDecrementLine) {
-                sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY,
-                                                  autoScroll, sPtr);
-            }
-        }
-        break;
-
-    case ButtonRelease:
-        if (sPtr->flags.draggingKnob) {
-            if (sPtr->action) {
-                (*sPtr->action)(sPtr, sPtr->clientData);
-                WMPostNotificationName(WMScrollerDidScrollNotification, sPtr,
-                                       NULL);
-            }
-        }
-        if (sPtr->timerID) {
-            WMDeleteTimerHandler(sPtr->timerID);
-            sPtr->timerID = NULL;
-        }
-        sPtr->flags.incrDown = 0;
-        sPtr->flags.decrDown = 0;
-        sPtr->flags.draggingKnob = 0;
-        break;
-
-    case MotionNotify:
-        handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
-        if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
-            && sPtr->flags.hitPart != WSDecrementLine) {
-            WMDeleteTimerHandler(sPtr->timerID);
-            sPtr->timerID = NULL;
-        }
-        break;
-    }
-    if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
-        paintScroller(sPtr);
-}
-
-
-
-static void
-destroyScroller(Scroller *sPtr)
-{
-    /* we don't want autoscroll try to scroll a freed widget */
-    if (sPtr->timerID) {
-        WMDeleteTimerHandler(sPtr->timerID);
-    }
-
-    wfree(sPtr);
-}
-
+
+#include "WINGsP.h"
+
+#include <math.h>
+
+/* undefine will disable the autoadjusting of the knob dimple to be
+ * directly below the cursor
+ * DOES NOT WORK */
+#undef STRICT_NEXT_BEHAVIOUR
+
+#define AUTOSCROLL_INITIAL_DELAY       200
+
+#define AUTOSCROLL_DELAY               40
+
+char *WMScrollerDidScrollNotification = "WMScrollerDidScrollNotification";
+
+typedef struct W_Scroller {
+       W_Class widgetClass;
+       W_View *view;
+
+       void *clientData;
+       WMAction *action;
+
+       float knobProportion;
+       float floatValue;
+
+       WMHandlerID timerID;    /* for continuous scrolling mode */
+
+#ifndef STRICT_NEXT_BEHAVIOUR
+       int dragPoint;          /* point where the knob is being
+                                * dragged */
+#endif
+       struct {
+               WMScrollArrowPosition arrowsPosition:4;
+
+               unsigned int horizontal:1;
+
+               WMScrollerPart hitPart:4;
+
+               /* */
+               unsigned int documentFullyVisible:1;    /* document is fully visible */
+
+               unsigned int prevSelected:1;
+
+               unsigned int pushed:1;
+
+               unsigned int incrDown:1;        /* whether increment button is down */
+
+               unsigned int decrDown:1;
+
+               unsigned int draggingKnob:1;
+
+               unsigned int configured:1;
+
+               unsigned int redrawPending:1;
+       } flags;
+} Scroller;
+
+#define DEFAULT_HEIGHT         60
+#define DEFAULT_WIDTH          SCROLLER_WIDTH
+#define DEFAULT_ARROWS_POSITION        WSAMinEnd
+
+#define BUTTON_SIZE             ((SCROLLER_WIDTH) - 4)
+
+static void destroyScroller(Scroller * sPtr);
+static void paintScroller(Scroller * sPtr);
+
+static void willResizeScroller();
+static void handleEvents(XEvent * event, void *data);
+static void handleActionEvents(XEvent * event, void *data);
+
+static void handleMotion(Scroller * sPtr, int mouseX, int mouseY);
+
+W_ViewDelegate _ScrollerViewDelegate = {
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       willResizeScroller
+};
+
+WMScroller *WMCreateScroller(WMWidget * parent)
+{
+       Scroller *sPtr;
+
+       sPtr = wmalloc(sizeof(Scroller));
+       memset(sPtr, 0, sizeof(Scroller));
+
+       sPtr->widgetClass = WC_Scroller;
+
+       sPtr->view = W_CreateView(W_VIEW(parent));
+       if (!sPtr->view) {
+               wfree(sPtr);
+               return NULL;
+       }
+       sPtr->view->self = sPtr;
+
+       sPtr->view->delegate = &_ScrollerViewDelegate;
+
+       sPtr->flags.documentFullyVisible = 1;
+
+       WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask
+                            | ClientMessageMask, handleEvents, sPtr);
+
+       W_ResizeView(sPtr->view, DEFAULT_WIDTH, DEFAULT_WIDTH);
+       sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
+
+       WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
+                            | EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, sPtr);
+
+       sPtr->flags.hitPart = WSNoPart;
+
+       sPtr->floatValue = 0.0;
+       sPtr->knobProportion = 1.0;
+
+       return sPtr;
+}
+
+void WMSetScrollerArrowsPosition(WMScroller * sPtr, WMScrollArrowPosition position)
+{
+       sPtr->flags.arrowsPosition = position;
+       if (sPtr->view->flags.realized) {
+               paintScroller(sPtr);
+       }
+}
+
+static void willResizeScroller(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height)
+{
+       WMScroller *sPtr = (WMScroller *) view->self;
+
+       if (*width > *height) {
+               sPtr->flags.horizontal = 1;
+               *height = SCROLLER_WIDTH;
+       } else {
+               sPtr->flags.horizontal = 0;
+               *width = SCROLLER_WIDTH;
+       }
+}
+
+void WMSetScrollerAction(WMScroller * sPtr, WMAction * action, void *clientData)
+{
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       sPtr->action = action;
+
+       sPtr->clientData = clientData;
+}
+
+void WMSetScrollerParameters(WMScroller * sPtr, float floatValue, float knobProportion)
+{
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       assert(!isnan(floatValue));
+
+       if (floatValue < 0.0)
+               sPtr->floatValue = 0.0;
+       else if (floatValue > 1.0)
+               sPtr->floatValue = 1.0;
+       else
+               sPtr->floatValue = floatValue;
+
+       if (knobProportion <= 0.0) {
+
+               sPtr->knobProportion = 0.0;
+               sPtr->flags.documentFullyVisible = 0;
+
+       } else if (knobProportion >= 1.0) {
+
+               sPtr->knobProportion = 1.0;
+               sPtr->flags.documentFullyVisible = 1;
+
+       } else {
+               sPtr->knobProportion = knobProportion;
+               sPtr->flags.documentFullyVisible = 0;
+       }
+
+       if (sPtr->view->flags.realized)
+               paintScroller(sPtr);
+
+       /*   WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL); */
+}
+
+float WMGetScrollerKnobProportion(WMScroller * sPtr)
+{
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       return sPtr->knobProportion;
+}
+
+float WMGetScrollerValue(WMScroller * sPtr)
+{
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       return sPtr->floatValue;
+}
+
+WMScrollerPart WMGetScrollerHitPart(WMScroller * sPtr)
+{
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       return sPtr->flags.hitPart;
+}
+
+static void paintArrow(WMScroller * sPtr, Drawable d, int part)
+/*
+ * part- 0 paints the decrement arrow, 1 the increment arrow
+ */
+{
+       WMView *view = sPtr->view;
+       WMScreen *scr = view->screen;
+       int ofs;
+       W_Pixmap *arrow;
+
+#ifndef DOUBLE_BUFFER
+       GC gc = scr->lightGC;
+#endif
+
+       if (part == 0) {        /* decrement button */
+               if (sPtr->flags.horizontal) {
+                       if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+                               ofs = view->size.width - 2 * (BUTTON_SIZE + 1) - 1;
+                       } else {
+                               ofs = 2;
+                       }
+                       if (sPtr->flags.decrDown)
+                               arrow = scr->hiLeftArrow;
+                       else
+                               arrow = scr->leftArrow;
+
+               } else {
+                       if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+                               ofs = view->size.height - 2 * (BUTTON_SIZE + 1) - 1;
+                       } else {
+                               ofs = 2;
+                       }
+                       if (sPtr->flags.decrDown)
+                               arrow = scr->hiUpArrow;
+                       else
+                               arrow = scr->upArrow;
+               }
+
+#ifndef DOUBLE_BUFFER
+               if (sPtr->flags.decrDown)
+                       gc = WMColorGC(scr->white);
+#endif
+       } else {                /* increment button */
+               if (sPtr->flags.horizontal) {
+                       if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+                               ofs = view->size.width - BUTTON_SIZE + 1 - 3;
+                       } else {
+                               ofs = 2 + BUTTON_SIZE + 1;
+                       }
+                       if (sPtr->flags.incrDown)
+                               arrow = scr->hiRightArrow;
+                       else
+                               arrow = scr->rightArrow;
+               } else {
+                       if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+                               ofs = view->size.height - BUTTON_SIZE + 1 - 3;
+                       } else {
+                               ofs = 2 + BUTTON_SIZE + 1;
+                       }
+                       if (sPtr->flags.incrDown)
+                               arrow = scr->hiDownArrow;
+                       else
+                               arrow = scr->downArrow;
+               }
+
+#ifndef DOUBLE_BUFFER
+               if (sPtr->flags.incrDown)
+                       gc = scr->whiteGC;
+#endif
+       }
+
+       if (sPtr->flags.horizontal) {
+               /* paint button */
+#ifndef DOUBLE_BUFFER
+               XFillRectangle(scr->display, d, gc, ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
+#else
+               if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
+                       XFillRectangle(scr->display, d, WMColorGC(scr->white),
+                                      ofs + 1, 2 + 1, BUTTON_SIZE + 1 - 3, BUTTON_SIZE - 3);
+#endif                         /* DOUBLE_BUFFER */
+               W_DrawRelief(scr, d, ofs, 2, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
+
+               /* paint arrow */
+               XSetClipMask(scr->display, scr->clipGC, arrow->mask);
+               XSetClipOrigin(scr->display, scr->clipGC,
+                              ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
+
+               XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
+                         0, 0, arrow->width, arrow->height,
+                         ofs + (BUTTON_SIZE - arrow->width) / 2, 2 + (BUTTON_SIZE - arrow->height) / 2);
+
+       } else {                /* vertical */
+
+               /* paint button */
+#ifndef DOUBLE_BUFFER
+               XFillRectangle(scr->display, d, gc, 2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
+#else
+               if ((!part && sPtr->flags.decrDown) || (part && sPtr->flags.incrDown))
+                       XFillRectangle(scr->display, d, WMColorGC(scr->white),
+                                      2 + 1, ofs + 1, BUTTON_SIZE - 3, BUTTON_SIZE + 1 - 3);
+#endif                         /* DOUBLE_BUFFER */
+               W_DrawRelief(scr, d, 2, ofs, BUTTON_SIZE, BUTTON_SIZE, WRRaised);
+
+               /* paint arrow */
+
+               XSetClipMask(scr->display, scr->clipGC, arrow->mask);
+               XSetClipOrigin(scr->display, scr->clipGC,
+                              2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
+               XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
+                         0, 0, arrow->width, arrow->height,
+                         2 + (BUTTON_SIZE - arrow->width) / 2, ofs + (BUTTON_SIZE - arrow->height) / 2);
+       }
+}
+
+static int knobLength(Scroller * sPtr)
+{
+       int tmp, length;
+
+       if (sPtr->flags.horizontal)
+               length = sPtr->view->size.width - 4;
+       else
+               length = sPtr->view->size.height - 4;
+
+       if (sPtr->flags.arrowsPosition != WSANone) {
+               length -= 2 * (BUTTON_SIZE + 1);
+       }
+
+       tmp = (int)((float)length * sPtr->knobProportion + 0.5);
+       /* keep minimum size */
+       if (tmp < BUTTON_SIZE)
+               tmp = BUTTON_SIZE;
+
+       return tmp;
+}
+
+static void paintScroller(Scroller * sPtr)
+{
+       WMView *view = sPtr->view;
+       WMScreen *scr = view->screen;
+#ifdef DOUBLE_BUFFER
+       Pixmap d;
+#else
+       Drawable d = view->window;
+#endif
+       int length, ofs;
+       float knobP, knobL;
+
+#ifdef DOUBLE_BUFFER
+       d = XCreatePixmap(scr->display, view->window, view->size.width, view->size.height, scr->depth);
+       XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0, view->size.width, view->size.height);
+#endif
+
+       XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0, view->size.width - 1, view->size.height - 1);
+#ifndef DOUBLE_BUFFER
+       XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1, view->size.width - 3, view->size.height - 3);
+#endif
+
+       if (sPtr->flags.horizontal)
+               length = view->size.width - 4;
+       else
+               length = view->size.height - 4;
+
+       if (sPtr->flags.documentFullyVisible) {
+               XFillRectangle(scr->display, d, scr->stippleGC, 2, 2, view->size.width - 4, view->size.height - 4);
+       } else {
+               ofs = 2;
+               if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+                       length -= (BUTTON_SIZE + 1) * 2;
+               } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
+                       ofs += (BUTTON_SIZE + 1) * 2;
+                       length -= (BUTTON_SIZE + 1) * 2;
+               }
+
+               knobL = (float)knobLength(sPtr);
+
+               knobP = sPtr->floatValue * ((float)length - knobL);
+
+               if (sPtr->flags.horizontal) {
+                       /* before */
+                       XFillRectangle(scr->display, d, scr->stippleGC, ofs, 2, (int)knobP, view->size.height - 4);
+
+                       /* knob */
+#ifndef DOUBLE_BUFFER
+                       XFillRectangle(scr->display, d, scr->lightGC,
+                                      ofs + (int)knobP + 2, 2 + 2, (int)knobL - 4, view->size.height - 4 - 4);
+#endif
+                       W_DrawRelief(scr, d, ofs + (int)knobP, 2, (int)knobL, view->size.height - 4, WRRaised);
+
+                       XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
+                                 scr->copyGC, 0, 0,
+                                 scr->scrollerDimple->width, scr->scrollerDimple->height,
+                                 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->width - 1) / 2,
+                                 (view->size.height - scr->scrollerDimple->height - 1) / 2);
+
+                       /* after */
+                       if ((int)(knobP + knobL) < length)
+                               XFillRectangle(scr->display, d, scr->stippleGC,
+                                              ofs + (int)(knobP + knobL), 2,
+                                              length - (int)(knobP + knobL), view->size.height - 4);
+               } else {
+                       /* before */
+                       if (knobP > 0.0)
+                               XFillRectangle(scr->display, d, scr->stippleGC,
+                                              2, ofs, view->size.width - 4, (int)knobP);
+
+                       /* knob */
+#ifndef DOUBLE_BUFFER
+                       XFillRectangle(scr->display, d, scr->lightGC,
+                                      2 + 2, ofs + (int)knobP + 2, view->size.width - 4 - 4, (int)knobL - 4);
+#endif
+                       XCopyArea(scr->display, scr->scrollerDimple->pixmap, d,
+                                 scr->copyGC, 0, 0,
+                                 scr->scrollerDimple->width, scr->scrollerDimple->height,
+                                 (view->size.width - scr->scrollerDimple->width - 1) / 2,
+                                 ofs + (int)knobP + ((int)knobL - scr->scrollerDimple->height - 1) / 2);
+
+                       W_DrawRelief(scr, d, 2, ofs + (int)knobP, view->size.width - 4, (int)knobL, WRRaised);
+
+                       /* after */
+                       if ((int)(knobP + knobL) < length)
+                               XFillRectangle(scr->display, d, scr->stippleGC,
+                                              2, ofs + (int)(knobP + knobL),
+                                              view->size.width - 4, length - (int)(knobP + knobL));
+               }
+
+               if (sPtr->flags.arrowsPosition != WSANone) {
+                       paintArrow(sPtr, d, 0);
+                       paintArrow(sPtr, d, 1);
+               }
+       }
+
+#ifdef DOUBLE_BUFFER
+       XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
+       XFreePixmap(scr->display, d);
+#endif
+}
+
+static void handleEvents(XEvent * event, void *data)
+{
+       Scroller *sPtr = (Scroller *) data;
+
+       CHECK_CLASS(data, WC_Scroller);
+
+       switch (event->type) {
+       case Expose:
+               if (event->xexpose.count == 0)
+                       paintScroller(sPtr);
+               break;
+
+       case DestroyNotify:
+               destroyScroller(sPtr);
+               break;
+       }
+}
+
+/*
+ * locatePointInScroller-
+ *     Return the part of the scroller where the point is located.
+ */
+static WMScrollerPart locatePointInScroller(Scroller * sPtr, int x, int y, int alternate)
+{
+       int width = sPtr->view->size.width;
+       int height = sPtr->view->size.height;
+       int c, p1, p2, p3, p4, p5, p6;
+       int knobL, slotL;
+
+       /* if there is no knob... */
+       if (sPtr->flags.documentFullyVisible)
+               return WSKnobSlot;
+
+       if (sPtr->flags.horizontal)
+               c = x;
+       else
+               c = y;
+
+       /*     p1  p2           p3           p4   p5   p6
+        * |   |   |###########|             |#####|   |   |
+        * | < | > |###########|      O      |#####| < | > |
+        * |   |   |###########|             |#####|   |   |
+        */
+
+       if (sPtr->flags.arrowsPosition == WSAMinEnd) {
+               p1 = 18;
+               p2 = 36;
+
+               if (sPtr->flags.horizontal) {
+                       slotL = width - 36;
+                       p5 = width;
+               } else {
+                       slotL = height - 36;
+                       p5 = height;
+               }
+               p6 = p5;
+       } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+               if (sPtr->flags.horizontal) {
+                       slotL = width - 36;
+                       p6 = width - 18;
+               } else {
+                       slotL = height - 36;
+                       p6 = height - 18;
+               }
+               p5 = p6 - 18;
+
+               p1 = p2 = 0;
+       } else {
+               /* no arrows */
+               p1 = p2 = 0;
+
+               if (sPtr->flags.horizontal) {
+                       slotL = p5 = p6 = width;
+               } else {
+                       slotL = p5 = p6 = height;
+               }
+       }
+
+       knobL = knobLength(sPtr);
+       p3 = p2 + (int)((float)(slotL - knobL) * sPtr->floatValue);
+       p4 = p3 + knobL;
+
+       /* uses a mix of the NS and Win ways of doing scroll page */
+       if (c <= p1)
+               return alternate ? WSDecrementPage : WSDecrementLine;
+       else if (c <= p2)
+               return alternate ? WSIncrementPage : WSIncrementLine;
+       else if (c <= p3)
+               return WSDecrementPage;
+       else if (c <= p4)
+               return WSKnob;
+       else if (c <= p5)
+               return WSIncrementPage;
+       else if (c <= p6)
+               return alternate ? WSDecrementPage : WSDecrementLine;
+       else
+               return alternate ? WSIncrementPage : WSIncrementLine;
+}
+
+static void handlePush(Scroller * sPtr, int pushX, int pushY, int alternate)
+{
+       WMScrollerPart part;
+       int doAction = 0;
+
+       part = locatePointInScroller(sPtr, pushX, pushY, alternate);
+
+       sPtr->flags.hitPart = part;
+
+       switch (part) {
+       case WSIncrementLine:
+               sPtr->flags.incrDown = 1;
+               doAction = 1;
+               break;
+
+       case WSIncrementPage:
+               doAction = 1;
+               break;
+
+       case WSDecrementLine:
+               sPtr->flags.decrDown = 1;
+               doAction = 1;
+               break;
+
+       case WSDecrementPage:
+               doAction = 1;
+               break;
+
+       case WSKnob:
+               sPtr->flags.draggingKnob = 1;
+#ifndef STRICT_NEXT_BEHAVIOUR
+               if (sPtr->flags.horizontal)
+                       sPtr->dragPoint = pushX;
+               else
+                       sPtr->dragPoint = pushY;
+
+               {
+                       int length, knobP;
+                       int buttonsLen;
+
+                       if (sPtr->flags.arrowsPosition != WSANone)
+                               buttonsLen = 2 * (BUTTON_SIZE + 1);
+                       else
+                               buttonsLen = 0;
+
+                       if (sPtr->flags.horizontal)
+                               length = sPtr->view->size.width - 4 - buttonsLen;
+                       else
+                               length = sPtr->view->size.height - 4 - buttonsLen;
+
+                       knobP = (int)(sPtr->floatValue * (float)(length - knobLength(sPtr)));
+
+                       if (sPtr->flags.arrowsPosition == WSAMinEnd)
+                               sPtr->dragPoint -= 2 + buttonsLen + knobP;
+                       else
+                               sPtr->dragPoint -= 2 + knobP;
+               }
+#endif                         /* STRICT_NEXT_BEHAVIOUR */
+               /* This does not seem necesary here since we don't know yet if the
+                * knob will be dragged later. -Dan
+                handleMotion(sPtr, pushX, pushY); */
+               break;
+
+       case WSDecrementWheel:
+       case WSIncrementWheel:
+       case WSKnobSlot:
+       case WSNoPart:
+               /* dummy */
+               break;
+       }
+
+       if (doAction && sPtr->action) {
+               (*sPtr->action) (sPtr, sPtr->clientData);
+
+               WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+       }
+}
+
+static float floatValueForPoint(Scroller * sPtr, int point)
+{
+       float floatValue = 0;
+       float position;
+       int slotOfs, slotLength, knobL;
+
+       if (sPtr->flags.horizontal)
+               slotLength = sPtr->view->size.width - 4;
+       else
+               slotLength = sPtr->view->size.height - 4;
+
+       slotOfs = 2;
+       if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
+               slotLength -= (BUTTON_SIZE + 1) * 2;
+       } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
+               slotOfs += (BUTTON_SIZE + 1) * 2;
+               slotLength -= (BUTTON_SIZE + 1) * 2;
+       }
+
+       knobL = (float)knobLength(sPtr);
+#ifdef STRICT_NEXT_BEHAVIOUR
+       if (point < slotOfs + knobL / 2)
+               position = (float)(slotOfs + knobL / 2);
+       else if (point > slotOfs + slotLength - knobL / 2)
+               position = (float)(slotOfs + slotLength - knobL / 2);
+       else
+               position = (float)point;
+
+       floatValue = (position - (float)(slotOfs + slotLength / 2))
+           / (float)(slotLength - knobL);
+#else
+       /* Adjust the last point to lie inside the knob slot */
+       if (point < slotOfs)
+               position = (float)slotOfs;
+       else if (point > slotOfs + slotLength)
+               position = (float)(slotOfs + slotLength);
+       else
+               position = (float)point;
+
+       /* Compute the float value */
+       floatValue = (position - (float)slotOfs) / (float)(slotLength - knobL);
+#endif
+
+       assert(!isnan(floatValue));
+       return floatValue;
+}
+
+static void handleMotion(Scroller * sPtr, int mouseX, int mouseY)
+{
+       if (sPtr->flags.draggingKnob) {
+               float newFloatValue;
+               int point;
+
+               if (sPtr->flags.horizontal) {
+                       point = mouseX;
+               } else {
+                       point = mouseY;
+               }
+
+#ifndef STRICT_NEXT_BEHAVIOUR
+               point -= sPtr->dragPoint;
+#endif
+
+               newFloatValue = floatValueForPoint(sPtr, point);
+               WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
+               if (sPtr->action) {
+                       (*sPtr->action) (sPtr, sPtr->clientData);
+                       WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+               }
+       } else {
+               int part;
+
+               part = locatePointInScroller(sPtr, mouseX, mouseY, False);
+
+               sPtr->flags.hitPart = part;
+
+               if (part == WSIncrementLine && sPtr->flags.decrDown) {
+                       sPtr->flags.decrDown = 0;
+                       sPtr->flags.incrDown = 1;
+               } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
+                       sPtr->flags.incrDown = 0;
+                       sPtr->flags.decrDown = 1;
+               } else if (part != WSIncrementLine && part != WSDecrementLine) {
+                       sPtr->flags.incrDown = 0;
+                       sPtr->flags.decrDown = 0;
+               }
+       }
+}
+
+static void autoScroll(void *clientData)
+{
+       Scroller *sPtr = (Scroller *) clientData;
+
+       if (sPtr->action) {
+               (*sPtr->action) (sPtr, sPtr->clientData);
+               WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+       }
+       sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
+}
+
+static void handleActionEvents(XEvent * event, void *data)
+{
+       Scroller *sPtr = (Scroller *) data;
+       int wheelDecrement, wheelIncrement;
+       int id, dd;
+
+       /* check if we're really dealing with a scroller, as something
+        * might have gone wrong in the event dispatching stuff */
+       CHECK_CLASS(sPtr, WC_Scroller);
+
+       id = sPtr->flags.incrDown;
+       dd = sPtr->flags.decrDown;
+
+       switch (event->type) {
+       case EnterNotify:
+               break;
+
+       case LeaveNotify:
+               if (sPtr->timerID) {
+                       WMDeleteTimerHandler(sPtr->timerID);
+                       sPtr->timerID = NULL;
+               }
+               sPtr->flags.incrDown = 0;
+               sPtr->flags.decrDown = 0;
+               break;
+
+       case ButtonPress:
+               /* FIXME: change Mod1Mask with something else */
+               if (sPtr->flags.documentFullyVisible)
+                       break;
+
+               if (sPtr->flags.horizontal) {
+                       wheelDecrement = WINGsConfiguration.mouseWheelDown;
+                       wheelIncrement = WINGsConfiguration.mouseWheelUp;
+               } else {
+                       wheelDecrement = WINGsConfiguration.mouseWheelUp;
+                       wheelIncrement = WINGsConfiguration.mouseWheelDown;
+               }
+
+               if (event->xbutton.button == wheelDecrement) {
+                       if (event->xbutton.state & ControlMask) {
+                               sPtr->flags.hitPart = WSDecrementPage;
+                       } else if (event->xbutton.state & ShiftMask) {
+                               sPtr->flags.hitPart = WSDecrementLine;
+                       } else {
+                               sPtr->flags.hitPart = WSDecrementWheel;
+                       }
+                       if (sPtr->action) {
+                               (*sPtr->action) (sPtr, sPtr->clientData);
+                               WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+                       }
+               } else if (event->xbutton.button == wheelIncrement) {
+                       if (event->xbutton.state & ControlMask) {
+                               sPtr->flags.hitPart = WSIncrementPage;
+                       } else if (event->xbutton.state & ShiftMask) {
+                               sPtr->flags.hitPart = WSIncrementLine;
+                       } else {
+                               sPtr->flags.hitPart = WSIncrementWheel;
+                       }
+                       if (sPtr->action) {
+                               (*sPtr->action) (sPtr, sPtr->clientData);
+                               WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+                       }
+               } else {
+                       handlePush(sPtr, event->xbutton.x, event->xbutton.y, (event->xbutton.state & Mod1Mask)
+                                  || event->xbutton.button == Button2);
+                       /* continue scrolling if pushed on the buttons */
+                       if (sPtr->flags.hitPart == WSIncrementLine || sPtr->flags.hitPart == WSDecrementLine) {
+                               sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY, autoScroll, sPtr);
+                       }
+               }
+               break;
+
+       case ButtonRelease:
+               if (sPtr->flags.draggingKnob) {
+                       if (sPtr->action) {
+                               (*sPtr->action) (sPtr, sPtr->clientData);
+                               WMPostNotificationName(WMScrollerDidScrollNotification, sPtr, NULL);
+                       }
+               }
+               if (sPtr->timerID) {
+                       WMDeleteTimerHandler(sPtr->timerID);
+                       sPtr->timerID = NULL;
+               }
+               sPtr->flags.incrDown = 0;
+               sPtr->flags.decrDown = 0;
+               sPtr->flags.draggingKnob = 0;
+               break;
+
+       case MotionNotify:
+               handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
+               if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
+                   && sPtr->flags.hitPart != WSDecrementLine) {
+                       WMDeleteTimerHandler(sPtr->timerID);
+                       sPtr->timerID = NULL;
+               }
+               break;
+       }
+       if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
+               paintScroller(sPtr);
+}
+
+static void destroyScroller(Scroller * sPtr)
+{
+       /* we don't want autoscroll try to scroll a freed widget */
+       if (sPtr->timerID) {
+               WMDeleteTimerHandler(sPtr->timerID);
+       }
+
+       wfree(sPtr);
+}