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