1
2 #include "WINGsP.h"
3
4 #undef STRICT_NEXT_BEHAVIOUR
5
6 typedef struct W_Slider {
7 W_Class widgetClass;
8 WMView *view;
9
10 int minValue;
11 int maxValue;
12
13 int value;
14
15 Pixmap knobPixmap;
16 WMPixmap *backPixmap;
17
18 WMAction *action;
19 void *clientData;
20
21 int knobThickness;
22
23 struct {
24 unsigned int continuous:1;
25
26 unsigned int vertical:1;
27 unsigned int dragging:1;
28 } flags;
29
30 } Slider;
31
32 static void didResizeSlider(W_ViewDelegate * self, WMView * view);
33
34 W_ViewDelegate _SliderViewDelegate = {
35 NULL,
36 NULL,
37 didResizeSlider,
38 NULL,
39 NULL
40 };
41
42 static void destroySlider(Slider * sPtr);
43 static void paintSlider(Slider * sPtr);
44
45 static void handleEvents(XEvent * event, void *data);
46 static void handleActionEvents(XEvent * event, void *data);
47
48 static void makeKnobPixmap(Slider * sPtr);
49
realizeObserver(void * self,WMNotification * not)50 static void realizeObserver(void *self, WMNotification * not)
51 {
52 /* Parameter not used, but tell the compiler that it is ok */
53 (void) not;
54
55 makeKnobPixmap(self);
56 }
57
WMCreateSlider(WMWidget * parent)58 WMSlider *WMCreateSlider(WMWidget * parent)
59 {
60 Slider *sPtr;
61
62 sPtr = wmalloc(sizeof(Slider));
63 sPtr->widgetClass = WC_Slider;
64
65 sPtr->view = W_CreateView(W_VIEW(parent));
66 if (!sPtr->view) {
67 wfree(sPtr);
68 return NULL;
69 }
70 sPtr->view->self = sPtr;
71
72 sPtr->view->delegate = &_SliderViewDelegate;
73
74 WMCreateEventHandler(sPtr->view, ExposureMask | StructureNotifyMask, handleEvents, sPtr);
75
76 WMCreateEventHandler(sPtr->view, ButtonPressMask | ButtonReleaseMask
77 | EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, sPtr);
78
79 W_ResizeView(sPtr->view, 100, 16);
80 sPtr->flags.vertical = 0;
81 sPtr->minValue = 0;
82 sPtr->maxValue = 100;
83 sPtr->value = 50;
84
85 sPtr->knobThickness = 20;
86
87 sPtr->flags.continuous = 1;
88
89 WMAddNotificationObserver(realizeObserver, sPtr, WMViewRealizedNotification, sPtr->view);
90
91 return sPtr;
92 }
93
WMSetSliderImage(WMSlider * sPtr,WMPixmap * pixmap)94 void WMSetSliderImage(WMSlider * sPtr, WMPixmap * pixmap)
95 {
96 if (sPtr->backPixmap)
97 WMReleasePixmap(sPtr->backPixmap);
98
99 sPtr->backPixmap = WMRetainPixmap(pixmap);
100
101 if (sPtr->view->flags.mapped) {
102 paintSlider(sPtr);
103 }
104 }
105
WMSetSliderKnobThickness(WMSlider * sPtr,int thickness)106 void WMSetSliderKnobThickness(WMSlider * sPtr, int thickness)
107 {
108 assert(thickness > 0);
109
110 sPtr->knobThickness = thickness;
111
112 if (sPtr->knobPixmap) {
113 makeKnobPixmap(sPtr);
114 }
115
116 if (sPtr->view->flags.mapped) {
117 paintSlider(sPtr);
118 }
119 }
120
WMGetSliderMinValue(WMSlider * slider)121 int WMGetSliderMinValue(WMSlider * slider)
122 {
123 CHECK_CLASS(slider, WC_Slider);
124
125 return slider->minValue;
126 }
127
WMGetSliderMaxValue(WMSlider * slider)128 int WMGetSliderMaxValue(WMSlider * slider)
129 {
130 CHECK_CLASS(slider, WC_Slider);
131
132 return slider->maxValue;
133 }
134
WMGetSliderValue(WMSlider * slider)135 int WMGetSliderValue(WMSlider * slider)
136 {
137 CHECK_CLASS(slider, WC_Slider);
138
139 return slider->value;
140 }
141
WMSetSliderMinValue(WMSlider * slider,int value)142 void WMSetSliderMinValue(WMSlider * slider, int value)
143 {
144 CHECK_CLASS(slider, WC_Slider);
145
146 slider->minValue = value;
147 if (slider->value < value) {
148 slider->value = value;
149 if (slider->view->flags.mapped)
150 paintSlider(slider);
151 }
152 }
153
WMSetSliderMaxValue(WMSlider * slider,int value)154 void WMSetSliderMaxValue(WMSlider * slider, int value)
155 {
156 CHECK_CLASS(slider, WC_Slider);
157
158 slider->maxValue = value;
159 if (slider->value > value) {
160 slider->value = value;
161 if (slider->view->flags.mapped)
162 paintSlider(slider);
163 }
164 }
165
WMSetSliderValue(WMSlider * slider,int value)166 void WMSetSliderValue(WMSlider * slider, int value)
167 {
168 CHECK_CLASS(slider, WC_Slider);
169
170 if (value < slider->minValue)
171 slider->value = slider->minValue;
172 else if (value > slider->maxValue)
173 slider->value = slider->maxValue;
174 else
175 slider->value = value;
176
177 if (slider->view->flags.mapped)
178 paintSlider(slider);
179 }
180
WMSetSliderContinuous(WMSlider * slider,Bool flag)181 void WMSetSliderContinuous(WMSlider * slider, Bool flag)
182 {
183 CHECK_CLASS(slider, WC_Slider);
184
185 slider->flags.continuous = ((flag == 0) ? 0 : 1);
186 }
187
WMSetSliderAction(WMSlider * slider,WMAction * action,void * data)188 void WMSetSliderAction(WMSlider * slider, WMAction * action, void *data)
189 {
190 CHECK_CLASS(slider, WC_Slider);
191
192 slider->action = action;
193 slider->clientData = data;
194 }
195
makeKnobPixmap(Slider * sPtr)196 static void makeKnobPixmap(Slider * sPtr)
197 {
198 Pixmap pix;
199 WMScreen *scr = sPtr->view->screen;
200 int w, h;
201
202 if (sPtr->flags.vertical) {
203 w = sPtr->view->size.width - 2;
204 h = sPtr->knobThickness;
205 } else {
206 w = sPtr->knobThickness;
207 h = sPtr->view->size.height - 2;
208 }
209
210 pix = XCreatePixmap(scr->display, sPtr->view->window, w, h, scr->depth);
211 XFillRectangle(scr->display, pix, WMColorGC(scr->gray), 0, 0, w, h);
212
213 if (sPtr->knobThickness < 10) {
214 W_DrawRelief(scr, pix, 0, 0, w, h, WRRaised);
215 } else if (sPtr->flags.vertical) {
216 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h - 3);
217 XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h - 3);
218 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w - 2, 1, w - 2, h / 2 - 2);
219 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w - 2, h / 2, w - 2, h - 2);
220
221 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w - 2, 0);
222 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h / 2 - 2, w - 3, h / 2 - 2);
223 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, h / 2 - 1, w - 3, h / 2 - 1);
224
225 XDrawLine(scr->display, pix, WMColorGC(scr->black), w - 1, 0, w - 1, h - 2);
226
227 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h - 3, w - 2, h - 3);
228 XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h - 2, w - 1, h - 2);
229 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 0, h - 1, w - 1, h - 1);
230 } else {
231 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, w - 3, 0);
232
233 XDrawLine(scr->display, pix, WMColorGC(scr->white), 0, 0, 0, h - 2);
234
235 XDrawLine(scr->display, pix, WMColorGC(scr->white), 1, 0, 1, h - 3);
236 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w / 2 - 2, 1, w / 2 - 2, h - 3);
237 XDrawLine(scr->display, pix, WMColorGC(scr->white), w / 2 - 1, 0, w / 2 - 1, h - 3);
238
239 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w - 3, 0, w - 3, h - 2);
240 XDrawLine(scr->display, pix, WMColorGC(scr->black), w - 2, 0, w - 2, h - 2);
241 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w - 1, 0, w - 1, h - 1);
242
243 XDrawLine(scr->display, pix, WMColorGC(scr->black), 1, h - 1, w / 2 + 1, h - 1);
244 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), 1, h - 2, w / 2 - 2, h - 2);
245 XDrawLine(scr->display, pix, WMColorGC(scr->darkGray), w / 2, h - 2, w - 3, h - 2);
246
247 XDrawLine(scr->display, pix, WMColorGC(scr->black), 0, h - 1, w - 2, h - 1);
248 }
249
250 if (sPtr->knobPixmap)
251 XFreePixmap(scr->display, sPtr->knobPixmap);
252 sPtr->knobPixmap = pix;
253 }
254
didResizeSlider(W_ViewDelegate * self,WMView * view)255 static void didResizeSlider(W_ViewDelegate * self, WMView * view)
256 {
257 Slider *sPtr = (Slider *) view->self;
258 int width = sPtr->view->size.width;
259 int height = sPtr->view->size.height;
260
261 /* Parameter not used, but tell the compiler that it is ok */
262 (void) self;
263
264 assert(width > 0);
265 assert(height > 0);
266
267 if (width > height) {
268 if (sPtr->flags.vertical) {
269 sPtr->flags.vertical = 0;
270 if (sPtr->view->flags.realized)
271 makeKnobPixmap(sPtr);
272 }
273 } else {
274 if (!sPtr->flags.vertical) {
275 sPtr->flags.vertical = 1;
276 if (sPtr->view->flags.realized)
277 makeKnobPixmap(sPtr);
278 }
279 }
280 }
281
paintSlider(Slider * sPtr)282 static void paintSlider(Slider * sPtr)
283 {
284 W_Screen *scr = sPtr->view->screen;
285 GC bgc;
286 GC wgc;
287 GC lgc;
288 WMSize size = sPtr->view->size;
289 int pos;
290 Pixmap buffer;
291
292 #define MINV sPtr->minValue
293 #define MAXV sPtr->maxValue
294 #define POSV sPtr->value
295
296 bgc = WMColorGC(scr->black);
297 wgc = WMColorGC(scr->white);
298 lgc = WMColorGC(scr->gray);
299
300 buffer = XCreatePixmap(scr->display, sPtr->view->window, size.width, size.height, scr->depth);
301
302 if (sPtr->backPixmap) {
303 WMSize size = WMGetPixmapSize(sPtr->backPixmap);
304
305 XCopyArea(scr->display, WMGetPixmapXID(sPtr->backPixmap),
306 buffer, scr->copyGC, 0, 0, size.width, size.height, 1, 1);
307 } else {
308 XFillRectangle(scr->display, buffer, lgc, 0, 0, size.width, size.height);
309 XFillRectangle(scr->display, buffer, scr->stippleGC, 0, 0, size.width, size.height);
310 }
311
312 if (sPtr->flags.vertical) {
313 pos = (size.height - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV) + 1;
314 /* draw knob */
315 XCopyArea(scr->display, sPtr->knobPixmap, buffer,
316 scr->copyGC, 0, 0, size.width - 2, sPtr->knobThickness, 1, pos);
317 } else {
318 pos = (size.width - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV) + 1;
319 /* draw knob */
320 XCopyArea(scr->display, sPtr->knobPixmap, buffer,
321 scr->copyGC, 0, 0, sPtr->knobThickness, size.height, pos, 1);
322 }
323
324 XDrawLine(scr->display, buffer, bgc, 0, 0, 0, size.height - 1);
325 XDrawLine(scr->display, buffer, bgc, 0, 0, size.width, 0);
326
327 XDrawLine(scr->display, buffer, wgc, size.width - 1, 0, size.width - 1, size.height - 1);
328 XDrawLine(scr->display, buffer, wgc, 0, size.height - 1, size.width - 1, size.height - 1);
329
330 XCopyArea(scr->display, buffer, sPtr->view->window, scr->copyGC, 0, 0, size.width, size.height, 0, 0);
331 XFreePixmap(scr->display, buffer);
332 }
333
handleEvents(XEvent * event,void * data)334 static void handleEvents(XEvent * event, void *data)
335 {
336 Slider *sPtr = (Slider *) data;
337
338 CHECK_CLASS(data, WC_Slider);
339
340 switch (event->type) {
341 case Expose:
342 if (event->xexpose.count != 0)
343 break;
344 paintSlider(sPtr);
345 break;
346
347 case DestroyNotify:
348 destroySlider(sPtr);
349 break;
350
351 }
352 }
353
354 #define DECR_PART 1
355 #define KNOB_PART 2
356 #define INCR_PART 3
357
getSliderPart(Slider * sPtr,int x,int y)358 static int getSliderPart(Slider * sPtr, int x, int y)
359 {
360 int p;
361 int pos;
362 WMSize size = sPtr->view->size;
363
364 if (sPtr->flags.vertical) {
365 p = y;
366 pos = (size.height - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV);
367 if (p < pos)
368 return INCR_PART;
369 if (p > pos + sPtr->knobThickness)
370 return DECR_PART;
371 return KNOB_PART;
372 } else {
373 p = x;
374 pos = (size.width - 2 - sPtr->knobThickness) * (POSV - MINV) / (MAXV - MINV);
375 if (p < pos)
376 return DECR_PART;
377 if (p > pos + sPtr->knobThickness)
378 return INCR_PART;
379 return KNOB_PART;
380 }
381 }
382
valueForMousePoint(Slider * sPtr,int x,int y)383 static int valueForMousePoint(Slider * sPtr, int x, int y)
384 {
385 WMSize size = sPtr->view->size;
386 int f;
387
388 if (sPtr->flags.vertical) {
389 f = (y - sPtr->knobThickness / 2) * (MAXV - MINV)
390 / ((int)size.height - 2 - sPtr->knobThickness);
391 } else {
392 f = (x - sPtr->knobThickness / 2) * (MAXV - MINV)
393 / ((int)size.width - 2 - sPtr->knobThickness);
394 }
395
396 f += sPtr->minValue;
397 if (f < sPtr->minValue)
398 f = sPtr->minValue;
399 else if (f > sPtr->maxValue)
400 f = sPtr->maxValue;
401
402 return f;
403 }
404
handleActionEvents(XEvent * event,void * data)405 static void handleActionEvents(XEvent * event, void *data)
406 {
407 WMSlider *sPtr = (Slider *) data;
408
409 CHECK_CLASS(data, WC_Slider);
410
411 switch (event->type) {
412 case ButtonPress:
413 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown && !sPtr->flags.dragging) {
414 /* Wheel down */
415 if (sPtr->value + 1 <= sPtr->maxValue) {
416 WMSetSliderValue(sPtr, sPtr->value + 1);
417 if (sPtr->flags.continuous && sPtr->action) {
418 (*sPtr->action) (sPtr, sPtr->clientData);
419 }
420 }
421 } else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp && !sPtr->flags.dragging) {
422 /* Wheel up */
423 if (sPtr->value - 1 >= sPtr->minValue) {
424 WMSetSliderValue(sPtr, sPtr->value - 1);
425 if (sPtr->flags.continuous && sPtr->action) {
426 (*sPtr->action) (sPtr, sPtr->clientData);
427 }
428 }
429 } else if (getSliderPart(sPtr, event->xbutton.x, event->xbutton.y)
430 == KNOB_PART)
431 sPtr->flags.dragging = 1;
432 else {
433 #ifdef STRICT_NEXT_BEHAVIOUR
434 sPtr->flags.dragging = 1;
435
436 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y);
437 paintSlider(sPtr);
438 #else
439 int tmp;
440
441 if (event->xbutton.button == Button2) {
442 sPtr->flags.dragging = 1;
443
444 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y);
445 paintSlider(sPtr);
446 } else {
447 tmp = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y);
448 if (tmp < sPtr->value)
449 tmp = sPtr->value - 1;
450 else
451 tmp = sPtr->value + 1;
452 WMSetSliderValue(sPtr, tmp);
453 }
454 #endif
455
456 if (sPtr->flags.continuous && sPtr->action) {
457 (*sPtr->action) (sPtr, sPtr->clientData);
458 }
459 }
460 break;
461
462 case ButtonRelease:
463 if (!sPtr->flags.continuous && sPtr->action) {
464 (*sPtr->action) (sPtr, sPtr->clientData);
465 }
466 sPtr->flags.dragging = 0;
467 break;
468
469 case MotionNotify:
470 if (sPtr->flags.dragging) {
471 sPtr->value = valueForMousePoint(sPtr, event->xmotion.x, event->xmotion.y);
472 paintSlider(sPtr);
473
474 if (sPtr->flags.continuous && sPtr->action) {
475 (*sPtr->action) (sPtr, sPtr->clientData);
476 }
477 }
478 break;
479 }
480 }
481
destroySlider(Slider * sPtr)482 static void destroySlider(Slider * sPtr)
483 {
484 if (sPtr->knobPixmap)
485 XFreePixmap(sPtr->view->screen->display, sPtr->knobPixmap);
486
487 if (sPtr->backPixmap)
488 WMReleasePixmap(sPtr->backPixmap);
489
490 WMRemoveNotificationObserver(sPtr);
491
492 wfree(sPtr);
493 }
494