reverted to KeepInsideScreen
[wmaker-crm.git] / WINGs / wslider.c
blobdb41dec6547357d9d9f386eb3e62b7b3fcbcce05
5 #include "WINGsP.h"
8 #undef STRICT_NEXT_BEHAVIOUR
11 typedef struct W_Slider {
12 W_Class widgetClass;
13 WMView *view;
15 int minValue;
16 int maxValue;
18 int value;
20 Pixmap knobPixmap;
21 WMPixmap *backPixmap;
23 WMAction *action;
24 void *clientData;
26 int knobThickness;
28 struct {
29 unsigned int continuous:1;
31 unsigned int vertical:1;
32 unsigned int dragging:1;
33 } flags;
35 } Slider;
40 static void didResizeSlider();
43 W_ViewDelegate _SliderViewDelegate = {
44 NULL,
45 NULL,
46 didResizeSlider,
47 NULL,
48 NULL
53 static void destroySlider(Slider *sPtr);
54 static void paintSlider(Slider *sPtr);
55 static void realizeSlider(Slider *sPtr);
57 static void handleEvents(XEvent *event, void *data);
58 static void handleActionEvents(XEvent *event, void *data);
60 static void makeKnobPixmap(Slider *sPtr);
62 static void
63 realizeObserver(void *self, WMNotification *not)
65 realizeSlider(self);
70 WMSlider*
71 WMCreateSlider(WMWidget *parent)
73 Slider *sPtr;
75 sPtr = wmalloc(sizeof(Slider));
76 memset(sPtr, 0, sizeof(Slider));
78 sPtr->widgetClass = WC_Slider;
80 sPtr->view = W_CreateView(W_VIEW(parent));
81 if (!sPtr->view) {
82 free(sPtr);
83 return NULL;
85 sPtr->view->self = sPtr;
87 sPtr->view->delegate = &_SliderViewDelegate;
89 WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask,
90 handleEvents, sPtr);
93 WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
94 |EnterWindowMask|LeaveWindowMask|ButtonMotionMask,
95 handleActionEvents, sPtr);
97 W_ResizeView(sPtr->view, 100, 16);
98 sPtr->flags.vertical = 0;
99 sPtr->minValue = 0;
100 sPtr->maxValue = 100;
101 sPtr->value = 50;
103 sPtr->knobThickness = 20;
105 sPtr->flags.continuous = 1;
107 WMAddNotificationObserver(realizeObserver, sPtr,
108 WMViewRealizedNotification, sPtr->view);
110 return sPtr;
114 void
115 WMSetSliderImage(WMSlider *sPtr, WMPixmap *pixmap)
117 if (sPtr->backPixmap)
118 WMReleasePixmap(sPtr->backPixmap);
120 sPtr->backPixmap = WMRetainPixmap(pixmap);
122 if (sPtr->view->flags.mapped) {
123 paintSlider(sPtr);
128 void
129 WMSetSliderKnobThickness(WMSlider *sPtr, int thickness)
131 assert(thickness > 0);
133 sPtr->knobThickness = thickness;
135 if (sPtr->knobPixmap) {
136 makeKnobPixmap(sPtr);
139 if (sPtr->view->flags.mapped) {
140 paintSlider(sPtr);
146 WMGetSliderMinValue(WMSlider *slider)
148 CHECK_CLASS(slider, WC_Slider);
150 return slider->minValue;
155 WMGetSliderMaxValue(WMSlider *slider)
157 CHECK_CLASS(slider, WC_Slider);
159 return slider->maxValue;
164 WMGetSliderValue(WMSlider *slider)
166 CHECK_CLASS(slider, WC_Slider);
168 return slider->value;
172 void
173 WMSetSliderMinValue(WMSlider *slider, int value)
175 CHECK_CLASS(slider, WC_Slider);
177 slider->minValue = value;
178 if (slider->value < value) {
179 slider->value = value;
180 if (slider->view->flags.mapped)
181 paintSlider(slider);
186 void
187 WMSetSliderMaxValue(WMSlider *slider, int value)
189 CHECK_CLASS(slider, WC_Slider);
191 slider->maxValue = value;
192 if (slider->value > value) {
193 slider->value = value;
194 if (slider->view->flags.mapped)
195 paintSlider(slider);
200 void
201 WMSetSliderValue(WMSlider *slider, int value)
203 CHECK_CLASS(slider, WC_Slider);
205 if (value < slider->minValue)
206 slider->value = slider->minValue;
207 else if (value > slider->maxValue)
208 slider->value = slider->maxValue;
209 else
210 slider->value = value;
212 if (slider->view->flags.mapped)
213 paintSlider(slider);
217 void
218 WMSetSliderContinuous(WMSlider *slider, Bool flag)
220 CHECK_CLASS(slider, WC_Slider);
222 slider->flags.continuous = flag;
226 void
227 WMSetSliderAction(WMSlider *slider, WMAction *action, void *data)
229 CHECK_CLASS(slider, WC_Slider);
231 slider->action = action;
232 slider->clientData = data;
236 static void
237 makeKnobPixmap(Slider *sPtr)
239 Pixmap pix;
240 WMScreen *scr = sPtr->view->screen;
241 int w, h;
243 if (sPtr->flags.vertical) {
244 w = sPtr->view->size.width-2;
245 h = sPtr->knobThickness;
246 } else {
247 w = sPtr->knobThickness;
248 h = sPtr->view->size.height-2;
251 pix = XCreatePixmap(scr->display, sPtr->view->window, w, h, scr->depth);
252 XFillRectangle(scr->display, pix, WMColorGC(scr->gray), 0, 0, w, h);
254 if (sPtr->knobThickness < 10) {
255 W_DrawRelief(scr, pix, 0, 0, w, h, WRRaised);
256 } else if (sPtr->flags.vertical) {
257 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h-3);
258 XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h-3);
259 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-2, 1, w-2, h/2-2);
260 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-2, h/2, w-2, h-2);
262 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w-2, 0);
263 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h/2-2, w-3, h/2-2);
264 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, h/2-1, w-3, h/2-1);
266 XDrawLine(scr->display, pix, WMColorGC(scr->black), w-1, 0, w-1, h-2);
268 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h-3, w-2, h-3);
269 XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h-2, w-1, h-2);
270 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h-1, w-1,h-1);
271 } else {
272 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w-3, 0);
274 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h-2);
276 XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h-3);
277 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w/2-2, 1, w/2-2, h-3);
278 XDrawLine(scr->display, pix, WMColorGC(scr->white), w/2-1, 0, w/2-1, h-3);
280 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-3, 0, w-3, h-2);
281 XDrawLine(scr->display, pix, WMColorGC(scr->black), w-2, 0, w-2, h-2);
282 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w-1, 0, w-1, h-1);
284 XDrawLine(scr->display, pix, WMColorGC(scr->black), 1, h-1, w/2+1, h-1);
285 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h-2, w/2-2, h-2);
286 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w/2, h-2, w-3,h-2);
288 XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h-1, w-2, h-1);
291 if (sPtr->knobPixmap)
292 XFreePixmap(scr->display, sPtr->knobPixmap);
293 sPtr->knobPixmap = pix;
297 static void
298 realizeSlider(Slider *sPtr)
300 W_RealizeView(sPtr->view);
302 makeKnobPixmap(sPtr);
306 static void
307 didResizeSlider(W_ViewDelegate *self, WMView *view)
309 Slider *sPtr = (Slider*)view->self;
310 int width = sPtr->view->size.width;
311 int height = sPtr->view->size.height;
313 assert(width > 0);
314 assert(height > 0);
316 if (width > height) {
317 if (sPtr->flags.vertical) {
318 sPtr->flags.vertical = 0;
319 if (sPtr->view->flags.realized)
320 makeKnobPixmap(sPtr);
322 } else {
323 if (!sPtr->flags.vertical) {
324 sPtr->flags.vertical = 1;
325 if (sPtr->view->flags.realized)
326 makeKnobPixmap(sPtr);
333 static void
334 paintSlider(Slider *sPtr)
336 W_Screen *scr = sPtr->view->screen;
337 GC bgc;
338 GC wgc;
339 GC lgc;
340 WMSize size = sPtr->view->size;
341 int pos;
342 Pixmap buffer;
344 #define MINV sPtr->minValue
345 #define MAXV sPtr->maxValue
346 #define POSV sPtr->value
348 bgc = WMColorGC(scr->black);
349 wgc = WMColorGC(scr->white);
350 lgc = WMColorGC(scr->gray);
352 buffer = XCreatePixmap(scr->display, sPtr->view->window,
353 size.width, size.height, scr->depth);
355 if (sPtr->backPixmap) {
356 WMSize size = WMGetPixmapSize(sPtr->backPixmap);
358 XCopyArea(scr->display, WMGetPixmapXID(sPtr->backPixmap),
359 buffer, scr->copyGC, 0, 0, size.width, size.height, 1, 1);
360 } else {
361 XFillRectangle(scr->display, buffer, lgc, 0, 0, size.width,
362 size.height);
363 XFillRectangle(scr->display, buffer, scr->stippleGC, 0, 0, size.width,
364 size.height);
367 if (sPtr->flags.vertical) {
368 pos = (size.height-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV)+1;
369 /* draw knob */
370 XCopyArea(scr->display, sPtr->knobPixmap, buffer,
371 scr->copyGC, 0, 0, size.width-2, sPtr->knobThickness,
372 1, pos);
373 } else {
374 pos = (size.width-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV)+1;
375 /* draw knob */
376 XCopyArea(scr->display, sPtr->knobPixmap, buffer,
377 scr->copyGC, 0, 0, sPtr->knobThickness, size.height, pos, 1);
380 XDrawLine(scr->display, buffer, bgc, 0, 0, 0, size.height-1);
381 XDrawLine(scr->display, buffer, bgc, 0, 0, size.width, 0);
383 XDrawLine(scr->display, buffer, wgc, size.width-1, 0,
384 size.width-1, size.height-1);
385 XDrawLine(scr->display, buffer, wgc, 0, size.height-1,
386 size.width-1, size.height-1);
388 XCopyArea(scr->display, buffer, sPtr->view->window, scr->copyGC, 0, 0,
389 size.width, size.height, 0, 0);
390 XFreePixmap(scr->display, buffer);
395 static void
396 handleEvents(XEvent *event, void *data)
398 Slider *sPtr = (Slider*)data;
400 CHECK_CLASS(data, WC_Slider);
403 switch (event->type) {
404 case Expose:
405 if (event->xexpose.count!=0)
406 break;
407 paintSlider(sPtr);
408 break;
410 case DestroyNotify:
411 destroySlider(sPtr);
412 break;
418 #define DECR_PART 1
419 #define KNOB_PART 2
420 #define INCR_PART 3
422 static int
423 getSliderPart(Slider *sPtr, int x, int y)
425 int p;
426 int pos;
427 WMSize size = sPtr->view->size;
430 if (sPtr->flags.vertical) {
431 p = y;
432 pos = (size.height-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV);
433 if (p < pos)
434 return INCR_PART;
435 if (p > pos + sPtr->knobThickness)
436 return DECR_PART;
437 return KNOB_PART;
438 } else {
439 p = x;
440 pos = (size.width-2-sPtr->knobThickness)*(POSV-MINV)/(MAXV-MINV);
441 if (p < pos)
442 return DECR_PART;
443 if (p > pos + sPtr->knobThickness)
444 return INCR_PART;
445 return KNOB_PART;
450 static int
451 valueForMousePoint(Slider *sPtr, int x, int y)
453 WMSize size = sPtr->view->size;
454 int f;
456 if (sPtr->flags.vertical) {
457 f = (y-sPtr->knobThickness/2)*(MAXV-MINV)
458 / ((int)size.height-2-sPtr->knobThickness);
459 } else {
460 f = (x-sPtr->knobThickness/2)*(MAXV-MINV)
461 / ((int)size.width-2-sPtr->knobThickness);
464 f += sPtr->minValue;
465 if (f < sPtr->minValue)
466 f = sPtr->minValue;
467 else if (f > sPtr->maxValue)
468 f = sPtr->maxValue;
470 return f;
474 static void
475 handleActionEvents(XEvent *event, void *data)
477 WMSlider *sPtr = (Slider*)data;
479 CHECK_CLASS(data, WC_Slider);
482 switch (event->type) {
483 case ButtonPress:
484 if (getSliderPart(sPtr, event->xbutton.x, event->xbutton.y)==KNOB_PART)
485 sPtr->flags.dragging = 1;
486 else {
487 #ifdef STRICT_NEXT_BEHAVIOUR
488 sPtr->flags.dragging = 1;
490 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x,
491 event->xmotion.y);
492 paintSlider(sPtr);
493 #else
494 int tmp;
496 if (event->xbutton.button == Button2) {
497 sPtr->flags.dragging = 1;
499 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x,
500 event->xmotion.y);
501 paintSlider(sPtr);
502 } else {
503 tmp = valueForMousePoint(sPtr, event->xmotion.x,
504 event->xmotion.y);
505 if (tmp < sPtr->value)
506 tmp = sPtr->value-1;
507 else
508 tmp = sPtr->value+1;
509 WMSetSliderValue(sPtr, tmp);
511 #endif
513 if (sPtr->flags.continuous && sPtr->action) {
514 (*sPtr->action)(sPtr, sPtr->clientData);
517 break;
519 case ButtonRelease:
520 if (!sPtr->flags.continuous && sPtr->action) {
521 (*sPtr->action)(sPtr, sPtr->clientData);
523 sPtr->flags.dragging = 0;
524 break;
526 case MotionNotify:
527 if (sPtr->flags.dragging) {
528 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x,
529 event->xmotion.y);
530 paintSlider(sPtr);
532 if (sPtr->flags.continuous && sPtr->action) {
533 (*sPtr->action)(sPtr, sPtr->clientData);
536 break;
542 static void
543 destroySlider(Slider *sPtr)
545 if (sPtr->knobPixmap)
546 XFreePixmap(sPtr->view->screen->display, sPtr->knobPixmap);
548 if (sPtr->backPixmap)
549 WMReleasePixmap(sPtr->backPixmap);
551 WMRemoveNotificationObserver(sPtr);
553 free(sPtr);