493 lines
12 KiB
C
493 lines
12 KiB
C
|
|
#include "WINGsP.h"
|
|
|
|
#undef STRICT_NEXT_BEHAVIOUR
|
|
|
|
typedef struct W_Slider {
|
|
W_Class widgetClass;
|
|
WMView *view;
|
|
|
|
int minValue;
|
|
int maxValue;
|
|
|
|
int value;
|
|
|
|
WMNativePixmap knobPixmap;
|
|
WMPixmap *backPixmap;
|
|
|
|
WMAction *action;
|
|
void *clientData;
|
|
|
|
int knobThickness;
|
|
|
|
struct {
|
|
unsigned int continuous:1;
|
|
|
|
unsigned int vertical:1;
|
|
unsigned int dragging:1;
|
|
} flags;
|
|
|
|
} Slider;
|
|
|
|
static void didResizeSlider(W_ViewDelegate * self, WMView * view);
|
|
|
|
W_ViewDelegate _SliderViewDelegate = {
|
|
NULL,
|
|
NULL,
|
|
didResizeSlider,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static void destroySlider(Slider * sPtr);
|
|
static void paintSlider(Slider * sPtr);
|
|
|
|
static void handleEvents(WMEvent * event, void *data);
|
|
static void handleActionEvents(WMEvent * event, void *data);
|
|
|
|
static void makeKnobPixmap(Slider * sPtr);
|
|
|
|
static void realizeObserver(void *self, WMNotification * not)
|
|
{
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) not;
|
|
|
|
makeKnobPixmap(self);
|
|
}
|
|
|
|
WMSlider *WMCreateSlider(WMWidget * parent)
|
|
{
|
|
Slider *sPtr;
|
|
|
|
sPtr = wmalloc(sizeof(Slider));
|
|
sPtr->widgetClass = WC_Slider;
|
|
|
|
sPtr->view = W_CreateView(W_VIEW(parent));
|
|
if (!sPtr->view) {
|
|
wfree(sPtr);
|
|
return NULL;
|
|
}
|
|
sPtr->view->self = sPtr;
|
|
|
|
sPtr->view->delegate = &_SliderViewDelegate;
|
|
|
|
WMCreateEventHandler(sPtr->view, WM_EVMASK_EXPOSURE | WM_EVMASK_STRUCTURE_NOTIFY, handleEvents, sPtr);
|
|
|
|
WMCreateEventHandler(sPtr->view, WM_EVMASK_BUTTON_PRESS | WM_EVMASK_BUTTON_RELEASE
|
|
| WM_EVMASK_ENTER_WINDOW | WM_EVMASK_LEAVE_WINDOW | WM_EVMASK_BUTTON_MOTION, handleActionEvents, sPtr);
|
|
|
|
W_ResizeView(sPtr->view, 100, 16);
|
|
sPtr->flags.vertical = 0;
|
|
sPtr->minValue = 0;
|
|
sPtr->maxValue = 100;
|
|
sPtr->value = 50;
|
|
|
|
sPtr->knobThickness = 20;
|
|
|
|
sPtr->flags.continuous = 1;
|
|
|
|
WMAddNotificationObserver(realizeObserver, sPtr, WMViewRealizedNotification, sPtr->view);
|
|
|
|
return sPtr;
|
|
}
|
|
|
|
void WMSetSliderImage(WMSlider * sPtr, WMPixmap * pixmap)
|
|
{
|
|
if (sPtr->backPixmap)
|
|
WMReleasePixmap(sPtr->backPixmap);
|
|
|
|
sPtr->backPixmap = WMRetainPixmap(pixmap);
|
|
|
|
if (sPtr->view->flags.mapped) {
|
|
paintSlider(sPtr);
|
|
}
|
|
}
|
|
|
|
void WMSetSliderKnobThickness(WMSlider * sPtr, int thickness)
|
|
{
|
|
assert(thickness > 0);
|
|
|
|
sPtr->knobThickness = thickness;
|
|
|
|
if (sPtr->knobPixmap) {
|
|
makeKnobPixmap(sPtr);
|
|
}
|
|
|
|
if (sPtr->view->flags.mapped) {
|
|
paintSlider(sPtr);
|
|
}
|
|
}
|
|
|
|
int WMGetSliderMinValue(WMSlider * slider)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
return slider->minValue;
|
|
}
|
|
|
|
int WMGetSliderMaxValue(WMSlider * slider)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
return slider->maxValue;
|
|
}
|
|
|
|
int WMGetSliderValue(WMSlider * slider)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
return slider->value;
|
|
}
|
|
|
|
void WMSetSliderMinValue(WMSlider * slider, int value)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
slider->minValue = value;
|
|
if (slider->value < value) {
|
|
slider->value = value;
|
|
if (slider->view->flags.mapped)
|
|
paintSlider(slider);
|
|
}
|
|
}
|
|
|
|
void WMSetSliderMaxValue(WMSlider * slider, int value)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
slider->maxValue = value;
|
|
if (slider->value > value) {
|
|
slider->value = value;
|
|
if (slider->view->flags.mapped)
|
|
paintSlider(slider);
|
|
}
|
|
}
|
|
|
|
void WMSetSliderValue(WMSlider * slider, int value)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
if (value < slider->minValue)
|
|
slider->value = slider->minValue;
|
|
else if (value > slider->maxValue)
|
|
slider->value = slider->maxValue;
|
|
else
|
|
slider->value = value;
|
|
|
|
if (slider->view->flags.mapped)
|
|
paintSlider(slider);
|
|
}
|
|
|
|
void WMSetSliderContinuous(WMSlider * slider, Bool flag)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
slider->flags.continuous = ((flag == 0) ? 0 : 1);
|
|
}
|
|
|
|
void WMSetSliderAction(WMSlider * slider, WMAction * action, void *data)
|
|
{
|
|
CHECK_CLASS(slider, WC_Slider);
|
|
|
|
slider->action = action;
|
|
slider->clientData = data;
|
|
}
|
|
|
|
static void makeKnobPixmap(Slider * sPtr)
|
|
{
|
|
WMScreen *scr = sPtr->view->screen;
|
|
const W_RenderBackend *R = scr->renderBackend;
|
|
void *rctx = scr->renderBackendCtx;
|
|
WMDrawable pix;
|
|
int w, h;
|
|
|
|
if (sPtr->flags.vertical) {
|
|
w = sPtr->view->size.width - 2;
|
|
h = sPtr->knobThickness;
|
|
} else {
|
|
w = sPtr->knobThickness;
|
|
h = sPtr->view->size.height - 2;
|
|
}
|
|
|
|
pix = R->pixmap_create(rctx, w, h, scr->depth);
|
|
R->fill_rect(rctx, pix, scr->gray, 0, 0, w, h);
|
|
|
|
if (sPtr->knobThickness < 10) {
|
|
W_DrawRelief(scr, pix, 0, 0, w, h, WRRaised);
|
|
} else if (sPtr->flags.vertical) {
|
|
R->draw_line(rctx, pix, scr->white, 0, 0, 0, h - 3);
|
|
R->draw_line(rctx, pix, scr->white, 1, 0, 1, h - 3);
|
|
R->draw_line(rctx, pix, scr->darkGray, w - 2, 1, w - 2, h / 2 - 2);
|
|
R->draw_line(rctx, pix, scr->darkGray, w - 2, h / 2, w - 2, h - 2);
|
|
|
|
R->draw_line(rctx, pix, scr->white, 0, 0, w - 2, 0);
|
|
R->draw_line(rctx, pix, scr->darkGray, 1, h / 2 - 2, w - 3, h / 2 - 2);
|
|
R->draw_line(rctx, pix, scr->white, 0, h / 2 - 1, w - 3, h / 2 - 1);
|
|
|
|
R->draw_line(rctx, pix, scr->black, w - 1, 0, w - 1, h - 2);
|
|
|
|
R->draw_line(rctx, pix, scr->darkGray, 0, h - 3, w - 2, h - 3);
|
|
R->draw_line(rctx, pix, scr->black, 0, h - 2, w - 1, h - 2);
|
|
R->draw_line(rctx, pix, scr->darkGray, 0, h - 1, w - 1, h - 1);
|
|
} else {
|
|
R->draw_line(rctx, pix, scr->white, 0, 0, w - 3, 0);
|
|
|
|
R->draw_line(rctx, pix, scr->white, 0, 0, 0, h - 2);
|
|
|
|
R->draw_line(rctx, pix, scr->white, 1, 0, 1, h - 3);
|
|
R->draw_line(rctx, pix, scr->darkGray, w / 2 - 2, 1, w / 2 - 2, h - 3);
|
|
R->draw_line(rctx, pix, scr->white, w / 2 - 1, 0, w / 2 - 1, h - 3);
|
|
|
|
R->draw_line(rctx, pix, scr->darkGray, w - 3, 0, w - 3, h - 2);
|
|
R->draw_line(rctx, pix, scr->black, w - 2, 0, w - 2, h - 2);
|
|
R->draw_line(rctx, pix, scr->darkGray, w - 1, 0, w - 1, h - 1);
|
|
|
|
R->draw_line(rctx, pix, scr->black, 1, h - 1, w / 2 + 1, h - 1);
|
|
R->draw_line(rctx, pix, scr->darkGray, 1, h - 2, w / 2 - 2, h - 2);
|
|
R->draw_line(rctx, pix, scr->darkGray, w / 2, h - 2, w - 3, h - 2);
|
|
|
|
R->draw_line(rctx, pix, scr->black, 0, h - 1, w - 2, h - 1);
|
|
}
|
|
|
|
if (sPtr->knobPixmap)
|
|
R->pixmap_free(rctx, (WMDrawable)sPtr->knobPixmap);
|
|
sPtr->knobPixmap = (WMNativePixmap)pix;
|
|
}
|
|
|
|
static void didResizeSlider(W_ViewDelegate * self, WMView * view)
|
|
{
|
|
Slider *sPtr = (Slider *) view->self;
|
|
int width = sPtr->view->size.width;
|
|
int height = sPtr->view->size.height;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) self;
|
|
|
|
assert(width > 0);
|
|
assert(height > 0);
|
|
|
|
if (width > height) {
|
|
if (sPtr->flags.vertical) {
|
|
sPtr->flags.vertical = 0;
|
|
if (sPtr->view->flags.realized)
|
|
makeKnobPixmap(sPtr);
|
|
}
|
|
} else {
|
|
if (!sPtr->flags.vertical) {
|
|
sPtr->flags.vertical = 1;
|
|
if (sPtr->view->flags.realized)
|
|
makeKnobPixmap(sPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void paintSlider(Slider * sPtr)
|
|
{
|
|
W_Screen *scr = sPtr->view->screen;
|
|
const W_RenderBackend *R = scr->renderBackend;
|
|
void *rctx = scr->renderBackendCtx;
|
|
WMSize size = sPtr->view->size;
|
|
int pos;
|
|
WMDrawable buffer;
|
|
|
|
#define MINV sPtr->minValue
|
|
#define MAXV sPtr->maxValue
|
|
#define POSV sPtr->value
|
|
|
|
buffer = R->pixmap_create(rctx, size.width, size.height, scr->depth);
|
|
|
|
if (sPtr->backPixmap) {
|
|
WMSize bsz = WMGetPixmapSize(sPtr->backPixmap);
|
|
|
|
R->pixmap_copy(rctx, (WMDrawable)WMGetPixmapXID(sPtr->backPixmap),
|
|
buffer, 0, 0, bsz.width, bsz.height, 1, 1);
|
|
} else {
|
|
R->fill_rect(rctx, buffer, scr->gray, 0, 0, size.width, size.height);
|
|
R->fill_stippled(rctx, buffer, scr->darkGray, 0, 0, size.width, size.height);
|
|
}
|
|
|
|
if (sPtr->flags.vertical) {
|
|
pos = (size.height - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV) + 1;
|
|
/* draw knob */
|
|
R->pixmap_copy(rctx, (WMDrawable)sPtr->knobPixmap, buffer,
|
|
0, 0, size.width - 2, sPtr->knobThickness, 1, pos);
|
|
} else {
|
|
pos = (size.width - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV) + 1;
|
|
/* draw knob */
|
|
R->pixmap_copy(rctx, (WMDrawable)sPtr->knobPixmap, buffer,
|
|
0, 0, sPtr->knobThickness, size.height, pos, 1);
|
|
}
|
|
|
|
R->draw_line(rctx, buffer, scr->black, 0, 0, 0, size.height - 1);
|
|
R->draw_line(rctx, buffer, scr->black, 0, 0, size.width, 0);
|
|
|
|
R->draw_line(rctx, buffer, scr->white, size.width - 1, 0, size.width - 1, size.height - 1);
|
|
R->draw_line(rctx, buffer, scr->white, 0, size.height - 1, size.width - 1, size.height - 1);
|
|
|
|
R->pixmap_copy(rctx, buffer, (WMDrawable)sPtr->view->window, 0, 0, size.width, size.height, 0, 0);
|
|
R->pixmap_free(rctx, buffer);
|
|
}
|
|
|
|
static void handleEvents(WMEvent * event, void *data)
|
|
{
|
|
Slider *sPtr = (Slider *) data;
|
|
|
|
CHECK_CLASS(data, WC_Slider);
|
|
|
|
switch (event->type) {
|
|
case WME_EXPOSE:
|
|
if (event->u.expose.count != 0)
|
|
break;
|
|
paintSlider(sPtr);
|
|
break;
|
|
|
|
case WME_DESTROY:
|
|
destroySlider(sPtr);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
#define DECR_PART 1
|
|
#define KNOB_PART 2
|
|
#define INCR_PART 3
|
|
|
|
static int getSliderPart(Slider * sPtr, int x, int y)
|
|
{
|
|
int p;
|
|
int pos;
|
|
WMSize size = sPtr->view->size;
|
|
|
|
if (sPtr->flags.vertical) {
|
|
p = y;
|
|
pos = (size.height - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV);
|
|
if (p < pos)
|
|
return INCR_PART;
|
|
if (p > pos + sPtr->knobThickness)
|
|
return DECR_PART;
|
|
return KNOB_PART;
|
|
} else {
|
|
p = x;
|
|
pos = (size.width - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV);
|
|
if (p < pos)
|
|
return DECR_PART;
|
|
if (p > pos + sPtr->knobThickness)
|
|
return INCR_PART;
|
|
return KNOB_PART;
|
|
}
|
|
}
|
|
|
|
static int valueForMousePoint(Slider * sPtr, int x, int y)
|
|
{
|
|
WMSize size = sPtr->view->size;
|
|
int f;
|
|
|
|
if (sPtr->flags.vertical) {
|
|
f = (y - sPtr->knobThickness / 2) * (MAXV - MINV)
|
|
/ ((int)size.height - 2 - sPtr->knobThickness);
|
|
} else {
|
|
f = (x - sPtr->knobThickness / 2) * (MAXV - MINV)
|
|
/ ((int)size.width - 2 - sPtr->knobThickness);
|
|
}
|
|
|
|
f += sPtr->minValue;
|
|
if (f < sPtr->minValue)
|
|
f = sPtr->minValue;
|
|
else if (f > sPtr->maxValue)
|
|
f = sPtr->maxValue;
|
|
|
|
return f;
|
|
}
|
|
|
|
static void handleActionEvents(WMEvent * event, void *data)
|
|
{
|
|
WMSlider *sPtr = (Slider *) data;
|
|
|
|
CHECK_CLASS(data, WC_Slider);
|
|
|
|
switch (event->type) {
|
|
case WME_BUTTON_PRESS:
|
|
if (event->u.button.button == WINGsConfiguration.mouseWheelDown && !sPtr->flags.dragging) {
|
|
/* Wheel down */
|
|
if (sPtr->value + 1 <= sPtr->maxValue) {
|
|
WMSetSliderValue(sPtr, sPtr->value + 1);
|
|
if (sPtr->flags.continuous && sPtr->action) {
|
|
(*sPtr->action) (sPtr, sPtr->clientData);
|
|
}
|
|
}
|
|
} else if (event->u.button.button == WINGsConfiguration.mouseWheelUp && !sPtr->flags.dragging) {
|
|
/* Wheel up */
|
|
if (sPtr->value - 1 >= sPtr->minValue) {
|
|
WMSetSliderValue(sPtr, sPtr->value - 1);
|
|
if (sPtr->flags.continuous && sPtr->action) {
|
|
(*sPtr->action) (sPtr, sPtr->clientData);
|
|
}
|
|
}
|
|
} else if (getSliderPart(sPtr, event->u.button.x, event->u.button.y)
|
|
== KNOB_PART)
|
|
sPtr->flags.dragging = 1;
|
|
else {
|
|
#ifdef STRICT_NEXT_BEHAVIOUR
|
|
sPtr->flags.dragging = 1;
|
|
|
|
sPtr->value = valueForMousePoint(sPtr, event->u.motion.x, event->u.motion.y);
|
|
paintSlider(sPtr);
|
|
#else
|
|
int tmp;
|
|
|
|
if (event->u.button.button == Button2) {
|
|
sPtr->flags.dragging = 1;
|
|
|
|
sPtr->value = valueForMousePoint(sPtr, event->u.motion.x, event->u.motion.y);
|
|
paintSlider(sPtr);
|
|
} else {
|
|
tmp = valueForMousePoint(sPtr, event->u.motion.x, event->u.motion.y);
|
|
if (tmp < sPtr->value)
|
|
tmp = sPtr->value - 1;
|
|
else
|
|
tmp = sPtr->value + 1;
|
|
WMSetSliderValue(sPtr, tmp);
|
|
}
|
|
#endif
|
|
|
|
if (sPtr->flags.continuous && sPtr->action) {
|
|
(*sPtr->action) (sPtr, sPtr->clientData);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WME_BUTTON_RELEASE:
|
|
if (!sPtr->flags.continuous && sPtr->action) {
|
|
(*sPtr->action) (sPtr, sPtr->clientData);
|
|
}
|
|
sPtr->flags.dragging = 0;
|
|
break;
|
|
|
|
case WME_MOTION:
|
|
if (sPtr->flags.dragging) {
|
|
sPtr->value = valueForMousePoint(sPtr, event->u.motion.x, event->u.motion.y);
|
|
paintSlider(sPtr);
|
|
|
|
if (sPtr->flags.continuous && sPtr->action) {
|
|
(*sPtr->action) (sPtr, sPtr->clientData);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void destroySlider(Slider * sPtr)
|
|
{
|
|
if (sPtr->knobPixmap) {
|
|
W_Screen *scr = sPtr->view->screen;
|
|
scr->renderBackend->pixmap_free(scr->renderBackendCtx, (WMDrawable)sPtr->knobPixmap);
|
|
}
|
|
|
|
if (sPtr->backPixmap)
|
|
WMReleasePixmap(sPtr->backPixmap);
|
|
|
|
WMRemoveNotificationObserver(sPtr);
|
|
|
|
wfree(sPtr);
|
|
}
|