#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); W_RedisplayView(slider->view); } } } 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); W_RedisplayView(slider->view); } } } 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); W_RedisplayView(slider->view); } } 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.button.x, event->u.button.y); paintSlider(sPtr); #else int tmp; if (event->u.button.button == Button2) { sPtr->flags.dragging = 1; sPtr->value = valueForMousePoint(sPtr, event->u.button.x, event->u.button.y); paintSlider(sPtr); } else { tmp = valueForMousePoint(sPtr, event->u.button.x, event->u.button.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.button.x, event->u.button.y); paintSlider(sPtr); if (sPtr->flags.continuous && sPtr->action) { (*sPtr->action) (sPtr, sPtr->clientData); } } break; } if (sPtr->flags.dragging || event->type == WME_BUTTON_RELEASE) W_RedisplayView(sPtr->view); } 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); }