1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-18 20:10:29 +01:00
Files
wmaker/WINGs/wscroller.c
Christophe CURIS 79a95d7173 Fixed floating point constants defined as double but expected as float
To preserve the accuracy of the operation, the C standard request that the
mathematical operation is performed using double precision, but in many
case this is not necessary so this patch fixes a few constants to avoid
that conversion.

Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
2015-05-24 23:25:57 +01:00

845 lines
21 KiB
C

/*
* Until FreeBSD gets their act together;
* http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
*/
#if defined( FREEBSD )
# undef _XOPEN_SOURCE
#endif
#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_ARROWS_POSITION WSAMinEnd
#define BUTTON_SIZE ((SCROLLER_WIDTH) - 4)
static void destroyScroller(Scroller * sPtr);
static void paintScroller(Scroller * sPtr);
static void willResizeScroller(W_ViewDelegate * self, WMView * view, unsigned int *width, unsigned int *height);
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));
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, SCROLLER_WIDTH, SCROLLER_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;
/* Parameter not used, but tell the compiler that it is ok */
(void) 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)
{
/*
* This value represents 1 pixel on a 4k wide screen, it makes
* a good minimum; this ensure a non-null value to avoid
* potential division-by-0.
* Please note that there is another size check when drawing
* the knob to make sure it will remain selectable.
*/
static const float min_knob_proportion = 1.0 / 4096.0;
CHECK_CLASS(sPtr, WC_Scroller);
assert(!isnan(floatValue));
if (floatValue < 0.0F)
sPtr->floatValue = 0.0F;
else if (floatValue > 1.0F)
sPtr->floatValue = 1.0F;
else
sPtr->floatValue = floatValue;
if (knobProportion <= min_knob_proportion) {
sPtr->knobProportion = min_knob_proportion;
sPtr->flags.documentFullyVisible = 0;
} else if (knobProportion >= 1.0F) {
sPtr->knobProportion = 1.0F;
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.5F);
/* 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.0F)
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);
}