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