#include "WINGsP.h" #undef STRICT_NEXT_BEHAVIOUR typedef struct W_Slider { W_Class widgetClass; WMView *view; int minValue; int maxValue; int value; Pixmap 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 resizeSlider(); W_ViewProcedureTable _SliderViewProcedures = { NULL, resizeSlider, NULL }; static void destroySlider(Slider *sPtr); static void paintSlider(Slider *sPtr); static void realizeSlider(Slider *sPtr); static void handleEvents(XEvent *event, void *data); static void handleActionEvents(XEvent *event, void *data); static void makeKnobPixmap(Slider *sPtr); static void realizeObserver(void *self, WMNotification *not) { realizeSlider(self); } WMSlider* WMCreateSlider(WMWidget *parent) { Slider *sPtr; sPtr = wmalloc(sizeof(Slider)); memset(sPtr, 0, sizeof(Slider)); sPtr->widgetClass = WC_Slider; sPtr->view = W_CreateView(W_VIEW(parent)); if (!sPtr->view) { free(sPtr); return NULL; } sPtr->view->self = sPtr; WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask, handleEvents, sPtr); WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask |EnterWindowMask|LeaveWindowMask|ButtonMotionMask, 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; } void WMSetSliderAction(WMSlider *slider, WMAction *action, void *data) { CHECK_CLASS(slider, WC_Slider); slider->action = action; slider->clientData = data; } static void makeKnobPixmap(Slider *sPtr) { Pixmap pix; WMScreen *scr = sPtr->view->screen; 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 = XCreatePixmap(scr->display, sPtr->view->window, w, h, scr->depth); XFillRectangle(scr->display, pix, WMColorGC(scr->gray), 0, 0, w, h); if (sPtr->knobThickness < 10) { W_DrawRelief(scr, pix, 0, 0, w, h, WRRaised); } else if (sPtr->flags.vertical) { XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-2, 1, w-2, h/2-2); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-2, h/2, w-2, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w-2, 0); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h/2-2, w-3, h/2-2); XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, h/2-1, w-3, h/2-1); XDrawLine(scr->display, pix, WMColorGC(scr->black), w-1, 0, w-1, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h-3, w-2, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h-2, w-1, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h-1, w-1,h-1); } else { XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w-3, 0); XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w/2-2, 1, w/2-2, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->white), w/2-1, 0, w/2-1, h-3); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-3, 0, w-3, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->black), w-2, 0, w-2, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-1, 0, w-1, h-1); XDrawLine(scr->display, pix, WMColorGC(scr->black), 1, h-1, w/2+1, h-1); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h-2, w/2-2, h-2); XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w/2, h-2, w-3,h-2); XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h-1, w-2, h-1); } if (sPtr->knobPixmap) XFreePixmap(scr->display, sPtr->knobPixmap); sPtr->knobPixmap = pix; } static void realizeSlider(Slider *sPtr) { W_RealizeView(sPtr->view); makeKnobPixmap(sPtr); } static void resizeSlider(Slider *sPtr, unsigned int width, unsigned int height) { assert(width > 0); assert(height > 0); W_ResizeView(sPtr->view, width, height); 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; GC bgc; GC wgc; GC lgc; WMSize size = sPtr->view->size; int pos; Pixmap buffer; #define MINV sPtr->minValue #define MAXV sPtr->maxValue #define POSV sPtr->value bgc = WMColorGC(scr->black); wgc = WMColorGC(scr->white); lgc = WMColorGC(scr->gray); buffer = XCreatePixmap(scr->display, sPtr->view->window, size.width, size.height, scr->depth); if (sPtr->backPixmap) { WMSize size = WMGetPixmapSize(sPtr->backPixmap); XCopyArea(scr->display, WMGetPixmapXID(sPtr->backPixmap), buffer, scr->copyGC, 0, 0, size.width, size.height, 1, 1); } else { XFillRectangle(scr->display, buffer, lgc, 0, 0, size.width, size.height); XFillRectangle(scr->display, buffer, scr->stippleGC, 0, 0, size.width, size.height); } if (sPtr->flags.vertical) { pos = (size.height-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV)+1; /* draw knob */ XCopyArea(scr->display, sPtr->knobPixmap, buffer, scr->copyGC, 0, 0, size.width-2, sPtr->knobThickness, 1, pos); } else { pos = (size.width-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV)+1; /* draw knob */ XCopyArea(scr->display, sPtr->knobPixmap, buffer, scr->copyGC, 0, 0, sPtr->knobThickness, size.height, pos, 1); } XDrawLine(scr->display, buffer, bgc, 0, 0, 0, size.height-1); XDrawLine(scr->display, buffer, bgc, 0, 0, size.width, 0); XDrawLine(scr->display, buffer, wgc, size.width-1, 0, size.width-1, size.height-1); XDrawLine(scr->display, buffer, wgc, 0, size.height-1, size.width-1, size.height-1); XCopyArea(scr->display, buffer, sPtr->view->window, scr->copyGC, 0, 0, size.width, size.height, 0, 0); XFreePixmap(scr->display, buffer); } static void handleEvents(XEvent *event, void *data) { Slider *sPtr = (Slider*)data; CHECK_CLASS(data, WC_Slider); switch (event->type) { case Expose: if (event->xexpose.count!=0) break; paintSlider(sPtr); break; case DestroyNotify: 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(XEvent *event, void *data) { WMSlider *sPtr = (Slider*)data; int ovalue = sPtr->value; CHECK_CLASS(data, WC_Slider); switch (event->type) { case ButtonPress: if (getSliderPart(sPtr, event->xbutton.x, event->xbutton.y)==KNOB_PART) sPtr->flags.dragging = 1; else { #ifdef STRICT_NEXT_BEHAVIOUR sPtr->flags.dragging = 1; sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y); paintSlider(sPtr); #else int tmp; if (event->xbutton.button == Button2) { sPtr->flags.dragging = 1; sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y); paintSlider(sPtr); } else { tmp = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.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 ButtonRelease: if (!sPtr->flags.continuous && ovalue != sPtr->value && sPtr->action) { (*sPtr->action)(sPtr, sPtr->clientData); } sPtr->flags.dragging = 0; break; case MotionNotify: if (sPtr->flags.dragging) { sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y); paintSlider(sPtr); if (sPtr->flags.continuous && sPtr->action) { (*sPtr->action)(sPtr, sPtr->clientData); } } break; } } static void destroySlider(Slider *sPtr) { if (sPtr->knobPixmap) XFreePixmap(sPtr->view->screen->display, sPtr->knobPixmap); if (sPtr->backPixmap) WMReleasePixmap(sPtr->backPixmap); WMRemoveNotificationObserver(sPtr); free(sPtr); }