1 /*
2  *                           0BSD
3  *
4  *                    BSD Zero Clause License
5  *
6  *  Copyright (c) 2019 Hermann Meyer
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted.
10 
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  *
19  */
20 
21 
22 #include "xbutton_private.h"
23 
24 
_rounded_rectangle(cairo_t * cr,float x,float y,float width,float height)25 void _rounded_rectangle(cairo_t *cr,float x, float y, float width, float height) {
26 	cairo_new_path (cr);
27 	cairo_move_to  (cr, x, (y + height)/2);
28 	cairo_curve_to (cr, x ,y, x, y, (x + width)/2, y);
29 	cairo_curve_to (cr, width, y, width, y, width, (y + height)/2);
30 	cairo_curve_to (cr, width, height, width, height, (width + x)/2, height);
31 	cairo_curve_to (cr, x, height, x, height, x, (y + height)/2);
32 	cairo_close_path (cr);
33 }
34 
_pattern_out(Widget_t * w,Color_state st,int height)35 void _pattern_out(Widget_t *w, Color_state st, int height) {
36     Colors *c = get_color_scheme(w->app,st);
37     if (!c) return;
38     cairo_pattern_t *pat = cairo_pattern_create_linear (2, 2, 2, height);
39     cairo_pattern_add_color_stop_rgba(pat, 0.0, c->light[0],  c->light[1], c->light[2],  c->light[3]);
40     cairo_pattern_add_color_stop_rgba(pat, 0.5, 0.0, 0.0, 0.0, 0.0);
41     cairo_pattern_add_color_stop_rgba(pat, 1.0, c->light[0],  c->light[1], c->light[2],  c->light[3]);
42     cairo_set_source(w->crb, pat);
43     cairo_pattern_destroy (pat);
44 }
45 
_pattern_in(Widget_t * w,Color_state st,int height)46 void _pattern_in(Widget_t *w, Color_state st, int height) {
47     Colors *c = get_color_scheme(w->app,st);
48     if (!c) return;
49     cairo_pattern_t *pat = cairo_pattern_create_linear (2, 2, 2, height);
50     cairo_pattern_add_color_stop_rgba(pat, 0.0, 0.0, 0.0, 0.0, 0.0);
51     cairo_pattern_add_color_stop_rgba(pat, 0.5, c->light[0],  c->light[1], c->light[2],  c->light[3]);
52     cairo_pattern_add_color_stop_rgba(pat, 1.0, 0.0, 0.0, 0.0, 0.0);
53     cairo_set_source(w->crb, pat);
54     cairo_pattern_destroy (pat);
55 }
56 
_draw_image_button(Widget_t * w,int width_t,int height_t,float offset)57 void _draw_image_button(Widget_t *w, int width_t, int height_t, float offset) {
58     int width = cairo_xlib_surface_get_width(w->image);
59     int height = cairo_xlib_surface_get_height(w->image);
60     double half_width = (width/height >=2) ? width*0.5 : width;
61     double x = (double)width_t/(double)(half_width);
62     double y = (double)height_t/(double)height;
63     double x1 = (double)height/(double)height_t;
64     double y1 = (double)(half_width)/(double)width_t;
65     double off_set = offset*x1;
66     double buttonstate = adj_get_state(w->adj);
67     int findex = (int)(((width/height)-1) * buttonstate) * (width/height >=2);
68     cairo_scale(w->crb, x,y);
69     cairo_set_source_surface (w->crb, w->image, -height*findex+off_set, off_set);
70     cairo_rectangle(w->crb,0, 0, height, height);
71     cairo_fill(w->crb);
72     cairo_scale(w->crb, x1,y1);
73 }
74 
_draw_image_button_with_label(Widget_t * w,int width_t,int height_t)75 void _draw_image_button_with_label(Widget_t *w, int width_t, int height_t) {
76     int width = cairo_xlib_surface_get_width(w->image);
77     int height = cairo_xlib_surface_get_height(w->image);
78     double x = (double)width_t/(double)height;
79     double y = (double)height/(double)width_t;
80     double buttonstate = adj_get_state(w->adj);
81     int findex = (int)(((width/height)-1) * buttonstate);
82     cairo_scale(w->crb, x,x);
83     cairo_set_source_surface (w->crb, w->image, -height*findex, 0);
84     cairo_rectangle(w->crb,0, 0, height, height);
85     cairo_fill(w->crb);
86     cairo_scale(w->crb, y,y);
87     cairo_text_extents_t extents;
88     if(w->state==0) {
89         use_fg_color_scheme(w, NORMAL_);
90     } else if(w->state==1 && ! (int)w->adj_y->value) {
91         use_fg_color_scheme(w, PRELIGHT_);
92     } else if(w->state==1) {
93         use_fg_color_scheme(w, ACTIVE_);
94     } else if(w->state==2) {
95         use_fg_color_scheme(w, SELECTED_);
96     } else if(w->state==3) {
97         use_fg_color_scheme(w, ACTIVE_);
98     }
99 
100     use_text_color_scheme(w, get_color_state(w));
101     cairo_set_font_size (w->crb,w->app->normal_font/w->scale.ascale);
102     if ((int)adj_get_value(w->adj) && strlen(w->input_label)) {
103         cairo_text_extents(w->crb,w->input_label , &extents);
104         cairo_move_to (w->crb, (width_t*0.5)-(extents.width/2), height_t-(extents.height/4));
105         cairo_show_text(w->crb, w->input_label);
106     } else {
107         cairo_text_extents(w->crb,w->label , &extents);
108         cairo_move_to (w->crb, (width_t*0.5)-(extents.width/2), height_t-(extents.height/4));
109         cairo_show_text(w->crb, w->label);
110     }
111     cairo_new_path (w->crb);
112 }
113 
_draw_switch_image_button(void * w_,void * user_data)114 void _draw_switch_image_button(void *w_, void* user_data) {
115     Widget_t *w = (Widget_t*)w_;
116     if (!w) return;
117     XWindowAttributes attrs;
118     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
119     int width = attrs.width-2;
120     int height = attrs.height-2;
121     if (attrs.map_state != IsViewable) return;
122     if(strlen(w->label)) {
123         _draw_image_button_with_label(w, width, height);
124     } else {
125         _draw_image_button(w, width, height,0.0);
126     }
127 }
128 
_draw_button_base(Widget_t * w,int width,int height)129 void _draw_button_base(Widget_t *w, int width, int height) {
130     if (!w->state && (int)w->adj_y->value) {
131         w->state = 3;
132     } else if (w->state == 3 && !(int)w->adj_y->value) {
133         w->state = 0;
134     }
135 
136     _rounded_rectangle(w->crb,2.0, 2.0, width, height);
137 
138     if(w->state==0) {
139         cairo_set_line_width(w->crb, 1.0);
140         _pattern_out(w, NORMAL_, height);
141         cairo_fill_preserve(w->crb);
142         use_frame_color_scheme(w, PRELIGHT_);
143     } else if(w->state==1) {
144         _pattern_out(w, PRELIGHT_, height);
145         cairo_fill_preserve(w->crb);
146         cairo_set_line_width(w->crb, 1.5);
147         use_frame_color_scheme(w, PRELIGHT_);
148     } else if(w->state==2) {
149         _pattern_in(w, SELECTED_, height);
150         cairo_fill_preserve(w->crb);
151         cairo_set_line_width(w->crb, 1.0);
152         use_frame_color_scheme(w, PRELIGHT_);
153     } else if(w->state==3) {
154         _pattern_in(w, ACTIVE_, height);
155         cairo_fill_preserve(w->crb);
156         cairo_set_line_width(w->crb, 1.0);
157         use_frame_color_scheme(w, PRELIGHT_);
158     }
159     cairo_stroke(w->crb);
160 
161     if(w->state==2) {
162         _rounded_rectangle(w->crb,4.0, 4.0, width, height);
163         cairo_stroke(w->crb);
164         _rounded_rectangle(w->crb,3.0, 3.0, width, height);
165         cairo_stroke(w->crb);
166     } else if (w->state==3) {
167         _rounded_rectangle(w->crb,3.0, 3.0, width, height);
168         cairo_stroke(w->crb);
169     }
170 }
171 
_draw_button(void * w_,void * user_data)172 void _draw_button(void *w_, void* user_data) {
173     Widget_t *w = (Widget_t*)w_;
174     if (!w) return;
175     XWindowAttributes attrs;
176     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
177     int width = attrs.width-2;
178     int height = attrs.height-2;
179     if (attrs.map_state != IsViewable) return;
180     _draw_button_base(w, width, height);
181 
182     float offset = 0.0;
183     if(w->state==1 && ! (int)w->adj_y->value) {
184         offset = 1.0;
185     } else if(w->state==1) {
186         offset = 2.0;
187     } else if(w->state==2) {
188         offset = 2.0;
189     } else if(w->state==3) {
190         offset = 1.0;
191     }
192     if (w->image) {
193         if(strlen(w->label)) {
194             _draw_image_button_with_label(w, width, height);
195         } else {
196             _draw_image_button(w, width, height,offset);
197         }
198     } else {
199 
200         cairo_text_extents_t extents;
201         use_text_color_scheme(w, get_color_state(w));
202         cairo_set_font_size (w->crb, w->app->normal_font/w->scale.ascale);
203         cairo_text_extents(w->crb,w->label , &extents);
204         if(IS_UTF8(w->label[0])) {
205             cairo_set_font_size (w->crb, w->app->normal_font/w->scale.ascale);
206             cairo_text_extents(w->crb,w->label , &extents);
207         }
208 
209         cairo_move_to (w->crb, (width-extents.width)*0.5 +offset, (height+extents.height)*0.5 +offset);
210         cairo_show_text(w->crb, w->label);
211         cairo_new_path (w->crb);
212     }
213 }
214 
_draw_on_off_button(void * w_,void * user_data)215 void _draw_on_off_button(void *w_, void* user_data) {
216     Widget_t *w = (Widget_t*)w_;
217     if (!w) return;
218     XWindowAttributes attrs;
219     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
220     int width = attrs.width-2;
221     int height = attrs.height-2;
222     if (attrs.map_state != IsViewable) return;
223 
224     _draw_button_base(w, width, height);
225 
226     float offset = 0.0;
227     cairo_text_extents_t extents;
228     if(w->state==1 && ! (int)w->adj_y->value) {
229         offset = 1.0;
230     } else if(w->state==1) {
231         offset = 2.0;
232     } else if(w->state==2) {
233         offset = 2.0;
234     } else if(w->state==3) {
235         offset = 1.0;
236     }
237     if((int)w->adj_y->value) {
238         w->label = "On";
239     } else {
240         w->label = "Off";
241     }
242 
243     use_text_color_scheme(w, get_color_state(w));
244     cairo_set_font_size (w->crb, w->app->normal_font/w->scale.ascale);
245     cairo_text_extents(w->crb,w->label , &extents);
246     if(IS_UTF8(w->label[0])) {
247         cairo_set_font_size (w->crb, w->app->normal_font/w->scale.ascale);
248         cairo_text_extents(w->crb,w->label , &extents);
249     }
250 
251     cairo_move_to (w->crb, (width-extents.width)*0.5 +offset, (height+extents.height)*0.5 +offset);
252     cairo_show_text(w->crb, w->label);
253     cairo_new_path (w->crb);
254 
255 }
256 
_draw_ti_button(void * w_,void * user_data)257 void _draw_ti_button(void *w_, void* user_data) {
258     Widget_t *w = (Widget_t*)w_;
259     if (!w) return;
260     XWindowAttributes attrs;
261     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
262     int width = attrs.width-2;
263     int height = attrs.height-2;
264     if (attrs.map_state != IsViewable) return;
265     _draw_button_base(w, width, height);
266     if (w->image) {
267         float offset = 0.0;
268         if(w->state==1 && ! (int)w->adj_y->value) {
269             offset = 1.0;
270         } else if(w->state==1) {
271             offset = 2.0;
272         } else if(w->state==2) {
273             offset = 2.0;
274         } else if(w->state==3) {
275             offset = 1.0;
276         }
277 
278        _draw_image_button(w, width, height,offset);
279    }
280 }
281 
_draw_check_button(void * w_,void * user_data)282 void _draw_check_button(void *w_, void* user_data) {
283     Widget_t *w = (Widget_t*)w_;
284     if (!w) return;
285     XWindowAttributes attrs;
286     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
287     int width = attrs.width-2;
288     int height = attrs.height-2;
289     if (attrs.map_state != IsViewable) return;
290     if (w->image) {
291         _draw_image_button(w, width, height,0.0);
292     } else {
293         _draw_button_base(w, width, height);
294 
295         if(w->state==3) {
296             use_fg_color_scheme(w, get_color_state(w));
297             float offset = 1.0;
298             int wa = width/1.3;
299             int h = height/2.2;
300             int wa1 = width/2.2;
301             int h1 = height/1.3;
302             int wa2 = width/2.8;
303 
304             cairo_set_line_width(w->crb, 2.5);
305             cairo_move_to(w->crb, wa+offset, h+offset);
306             cairo_line_to(w->crb, wa1+offset, h1+offset);
307             cairo_line_to(w->crb, wa2+offset, h+offset);
308             cairo_stroke(w->crb);
309         }
310 
311         cairo_new_path (w->crb);
312     }
313 }
314 
_draw_check_box(void * w_,void * user_data)315 void _draw_check_box(void *w_, void* user_data) {
316     Widget_t *w = (Widget_t*)w_;
317     if (!w) return;
318     XWindowAttributes attrs;
319     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
320     int height = attrs.height-2;
321     if (attrs.map_state != IsViewable) return;
322     if (w->image) {
323         _draw_image_button(w, height, height,0.0);
324     } else {
325         _draw_button_base(w, height, height);
326 
327         if(adj_get_value(w->adj)) {
328             use_fg_color_scheme(w, get_color_state(w));
329             float offset = 1.0;
330             int wa = height/1.3;
331             int h = height/2.2;
332             int wa1 = height/2.2;
333             int h1 = height/1.3;
334             int wa2 = height/2.8;
335 
336             cairo_set_line_width(w->crb, 2.5);
337             cairo_move_to(w->crb, wa+offset, h+offset);
338             cairo_line_to(w->crb, wa1+offset, h1+offset);
339             cairo_line_to(w->crb, wa2+offset, h+offset);
340             cairo_stroke(w->crb);
341         }
342 
343         cairo_new_path (w->crb);
344 
345         cairo_text_extents_t extents;
346         use_text_color_scheme(w, get_color_state(w));
347         cairo_set_font_size (w->crb, w->app->normal_font/w->scale.ascale);
348         cairo_text_extents(w->crb,w->label , &extents);
349         cairo_move_to (w->crb, height+5 , (height+extents.height)*0.5 );
350         cairo_show_text(w->crb, w->label);
351         cairo_new_path (w->crb);
352     }
353 }
354 
355 /*---------------------------------------------------------------------
356 -----------------------------------------------------------------------
357                             button
358 -----------------------------------------------------------------------
359 ----------------------------------------------------------------------*/
360 
_button_pressed(void * w_,void * button,void * user_data)361 void _button_pressed(void *w_, void* button, void* user_data) {
362     Widget_t *w = (Widget_t*)w_;
363     adj_set_value(w->adj_y, 1.0);
364 }
365 
_button_released(void * w_,void * button_,void * user_data)366 void _button_released(void *w_, void* button_, void* user_data) {
367     Widget_t *w = (Widget_t*)w_;
368     if (w->flags & HAS_POINTER) w->state=1;
369     adj_set_value(w->adj_y, 0.0);
370 }
371 
372 /*---------------------------------------------------------------------
373 -----------------------------------------------------------------------
374                         toggle button
375 -----------------------------------------------------------------------
376 ----------------------------------------------------------------------*/
377 
_toggle_button_pressed(void * w_,void * button,void * user_data)378 void _toggle_button_pressed(void *w_, void* button, void* user_data) {
379     Widget_t *w = (Widget_t*)w_;
380     expose_widget(w);
381 }
382 
_toggle_button_released(void * w_,void * button_,void * user_data)383 void _toggle_button_released(void *w_, void* button_, void* user_data) {
384     Widget_t *w = (Widget_t*)w_;
385     XButtonEvent *xbutton = (XButtonEvent*)button_;
386     if (w->flags & HAS_POINTER) {
387         float value = w->adj->value;
388         if (xbutton->button == Button1) value = value ?
389                 w->adj->min_value : w->adj->max_value;
390         if (xbutton->button == Button4) value = w->adj->max_value;
391         if (xbutton->button == Button5) value = w->adj->min_value;
392         adj_set_value(w->adj, value);
393         w->state = (int) w->adj->value ? 3 : 1;
394     } else {
395         w->state = (int) w->adj->value ? 3 : 0;
396     }
397     expose_widget(w);
398 }
399