mirror of
https://github.com/gryf/wmaker.git
synced 2026-01-09 23:34:14 +01:00
- Fixed bug in icon chooser dialog that could cause a segmentation fault in some cases (Pascal Hofstee <caelian@gmail.com>) - Fixed crash in asm code in wrlib, with new versions of gcc. - Fixed bug in the x86_PseudoColor_32_to_8() function which incorrectly used the r, g, b fields in the conversion. - Fixed x86 ASM code in wrlib to work on 64 bit architectures. - Fixed the focus flicker seen with some apps (notably gtk2) (Alexey Spiridonov <snarkmaster@gmail.com>) - Fixed all crashing bugs that were generated by wmaker starting with the WMState file missing. - Added NetWM support (a modified version of the patch originaly written by Peter Zijlstra <a.p.zijlstra@chello.nl>) - Applied patch to enhance the Virtual Desktop behaviour, and to integrate it with the NetWM code (Peter Zijlstra <a.p.zijlstra@chello.nl>) - Applied a few xinerama and placement fixes (Peter Zijlstra <a.p.zijlstra@chello.nl>) - Fixed memory leak in dock code. - Fixed and enhanced the text wrapping in WINGs. - Fixed the layout of some elements in WPrefs.app - Added workaround for aplications that don't set the required hints on the client leader window, but they set them on normal windows (observer with KDE 3.3.0 mainly). This will allow these apps to get an appicon again. (they should be fixed still) - Added workaround for applications that do not set a command with XSetCommand(), but instead they set the _NET_WM_PID property. This works with operating systems that offer a /proc interface similar to what linux has. (This also is to fix problems with KDE 3.3.0 apps, but not only them). - Fixed bug with autostart and exit scripts not being executed if user GNUstep path was different from ~/GNUstep (when setting GNUSTEP_USER_ROOT) - Added utf8 support in WINGs (removed old X core font code) - Added utility to convert old font names to new font names in style files
919 lines
22 KiB
C
919 lines
22 KiB
C
|
|
|
|
|
|
|
|
#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 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);
|
|
}
|
|
|