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 #include "xwidget_private.h"
22 
23 
_scroll_event(Widget_t * wid,int direction)24 void _scroll_event(Widget_t * wid, int direction) {
25     Adjustment_t *adj = NULL;
26     if (wid->adj_y) {
27         adj = wid->adj_y;
28     } else if(wid->adj_x) {
29         adj = wid->adj_x;
30     }
31     if (adj) {
32         float value = adj->value;
33         switch(adj->type) {
34             case (CL_LOGSCALE):
35             case (CL_LOGARITHMIC):
36             case (CL_CONTINUOS):
37                 value = min(adj->max_value,max(adj->min_value,
38                                 adj->value + (adj->step * direction)));
39             break;
40             case (CL_VIEWPORT):
41             case (CL_VIEWPORTSLIDER):
42             case (CL_ENUM):
43                 value = min(adj->max_value,max(adj->min_value,
44                                 adj->value + (adj->step * -direction)));
45             break;
46             case (CL_TOGGLE):
47                // value = adj->value ? 1.0 : 0.0;
48             break;
49             default:
50             break;
51         }
52         check_value_changed(adj, &value);
53     }
54 }
55 
_toggle_event(Widget_t * wid)56 void _toggle_event(Widget_t * wid) {
57     Adjustment_t *adj = NULL;
58     if (wid->adj_y) {
59         adj = wid->adj_y;
60     } else if(wid->adj_x) {
61         adj = wid->adj_x;
62     }
63     if (adj && adj->type != CL_TOGGLE) {
64         adj_set_start_value(wid);
65     }
66 }
67 
_check_enum(Widget_t * wid,XButtonEvent * xbutton)68 void _check_enum(Widget_t * wid, XButtonEvent *xbutton) {
69     if (wid->flags & HAS_POINTER && xbutton->button == Button1) {
70         Adjustment_t *adj = NULL;
71         if (wid->adj_y) {
72             adj = wid->adj_y;
73         } else if(wid->adj_x) {
74             adj = wid->adj_x;
75         }
76         if (adj && adj->type == CL_ENUM) {
77             float value = adj->value;
78             value = adj->value + 1.0;
79             if (value>adj->max_value) value = adj->min_value;
80             check_value_changed(adj, &value);
81         }
82     }
83 }
84 
_button_press(Widget_t * wid,XButtonEvent * xbutton,void * user_data)85 void _button_press(Widget_t * wid, XButtonEvent *xbutton, void* user_data) {
86     switch(xbutton->button) {
87         case Button1:
88             wid->state = 2;
89             _has_pointer(wid, xbutton);
90             wid->pos_x = xbutton->x;
91             wid->pos_y = xbutton->y;
92             _toggle_event(wid);
93             wid->func.button_press_callback(wid, xbutton, user_data);
94         break;
95         case Button2:
96             debug_print("Button2 \n");
97             _has_pointer(wid, xbutton);
98             wid->func.button_press_callback(wid, xbutton, user_data);
99         break;
100         case Button3:
101             debug_print("Button3 \n");
102             _has_pointer(wid, xbutton);
103             wid->func.button_press_callback(wid, xbutton, user_data);
104         break;
105         case  Button4:
106             _scroll_event(wid, 1);
107         break;
108         case Button5:
109             _scroll_event(wid, -1);
110         break;
111         default:
112         break;
113     }
114 }
115 
_check_grab(Widget_t * wid,XButtonEvent * xbutton,Xputty * main)116 void _check_grab(Widget_t * wid, XButtonEvent *xbutton, Xputty *main) {
117     if(main->hold_grab != NULL) {
118         if (childlist_has_child(main->hold_grab->childlist)) {
119             Widget_t *slider = main->hold_grab->childlist->childs[1];
120             if (xbutton->window == slider->widget) {
121                 return;
122             }
123         }
124         Widget_t *view_port = main->hold_grab->childlist->childs[0];
125         if(xbutton->button == Button1) {
126             //if (xbutton->window == view_port->widget) return;
127             XUngrabPointer(main->dpy,CurrentTime);
128             int i = view_port->childlist->elem-1;
129             for(;i>-1;i--) {
130                 Widget_t *w = view_port->childlist->childs[i];
131                 if (xbutton->window == w->widget) {
132                     const char *l = view_port->childlist->childs[i]->label;
133                     main->hold_grab->func.button_release_callback
134                         (main->hold_grab, &i, &l);
135                     break;
136                 }
137             }
138             widget_hide(main->hold_grab);
139             main->hold_grab = NULL;
140 
141         } /*else if(xbutton->button == Button4) {
142             _scroll_event(view_port, 1);
143         } else if(xbutton->button == Button5) {
144             _scroll_event(view_port, -1);
145         }*/ // done in  _button_press() anyway
146     }
147 }
148 
_check_submenu(Widget_t * wid,XButtonEvent * xbutton,Xputty * main)149 void _check_submenu(Widget_t * wid, XButtonEvent *xbutton, Xputty *main) {
150     if(main->submenu != NULL) {
151         Widget_t *view_port = main->submenu->childlist->childs[0];
152         if(xbutton->button == Button1) {
153             //if (xbutton->window == view_port->widget) return;
154             int i = view_port->childlist->elem-1;
155             for(;i>-1;i--) {
156                 Widget_t *w = view_port->childlist->childs[i];
157                 if (xbutton->window == w->widget) {
158                     const char *l = view_port->childlist->childs[i]->label;
159                     main->submenu->func.button_release_callback
160                         (main->submenu, &i, &l);
161                     break;
162                 }
163             }
164             widget_hide(main->submenu);
165             main->submenu = NULL;
166 
167         } else if(xbutton->button == Button4) {
168             _scroll_event(view_port, 1);
169         } else if(xbutton->button == Button5) {
170             _scroll_event(view_port, -1);
171         }
172     }
173 }
174 
_propagate_child_expose(Widget_t * wid)175 void _propagate_child_expose(Widget_t *wid) {
176 
177     if (childlist_has_child(wid->childlist)) {
178         int i = 0;
179         for(;i<wid->childlist->elem;i++) {
180             Widget_t *w = wid->childlist->childs[i];
181             if ( w->flags & NO_PROPAGATE) continue;
182             if (w->flags & USE_TRANSPARENCY) {
183                 if(w->flags & FAST_REDRAW)
184                     transparent_draw(w, NULL);
185                 else expose_widget(w);
186             }
187         }
188     }
189 }
190 
_check_keymap(void * w_,XKeyEvent xkey)191 void _check_keymap (void *w_ ,XKeyEvent xkey) {
192     Widget_t *wid = (Widget_t*)w_;
193     int n = 1;
194     int i = 0;
195     for(;i<wid->childlist->elem;i++) {
196         Widget_t *w = wid->childlist->childs[i];
197         if(w->flags & HAS_FOCUS && w->state != 4) {
198              wid=w;
199             break;
200         }
201     }
202     if(wid->app->hold_grab != NULL) {
203         wid = wid->app->hold_grab->childlist->childs[0];
204         n = -1;
205     }
206     int nk = key_mapping(wid->app->dpy, &xkey);
207     if (nk) {
208         switch (nk) {
209             case 3: _set_adj_value(wid, false, 1*n);
210             break;
211             case 4: _set_adj_value(wid, true, 1*n);
212             break;
213             case 5: _set_adj_value(wid, false, -1*n);
214             break;
215             case 6: _set_adj_value(wid, true, -1*n);
216             break;
217             case 10:
218             {
219                 int i = 0;
220                 for(;i<wid->childlist->elem;i++) {
221                     Widget_t *w = wid->childlist->childs[i];
222                     if(w->flags & HAS_FOCUS && w->state != 4) {
223                          wid=w;
224                         break;
225                     }
226                 }
227                 send_button_press_event(wid);
228                 send_button_release_event(wid);
229             }
230             break;
231             default:
232             break;
233         }
234     }
235 }
236 
_hide_all_tooltips(Widget_t * wid)237 void _hide_all_tooltips(Widget_t *wid) {
238     int i = 0;
239     for(;i<wid->app->childlist->elem;i++) {
240         Widget_t *w = wid->app->childlist->childs[i];
241         if (w->flags & IS_TOOLTIP) {
242             widget_hide(w);
243         }
244     }
245 }
246 
_has_pointer(Widget_t * w,XButtonEvent * button)247 void _has_pointer(Widget_t *w, XButtonEvent *button) {
248     XWindowAttributes attrs;
249     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
250 
251     if ((button->x<attrs.width && button->y<attrs.height) &&
252                                 (button->x>0 && button->y>0)){
253         w->flags |= HAS_POINTER;
254     } else {
255         w->flags &= ~HAS_POINTER;
256     }
257 }
258 
_set_adj_value(void * w_,bool x,int direction)259 void _set_adj_value(void *w_, bool x, int direction) {
260     Widget_t *wid = (Widget_t*)w_;
261     Adjustment_t *adj = NULL;
262     if (x && wid->adj_x) {
263         adj = wid->adj_x;
264     } else if (!x && wid->adj_y) {
265         adj = wid->adj_y;
266     }
267     if (adj) {
268         float value = adj->value;
269         switch(adj->type) {
270             case (CL_VIEWPORT):
271             case (CL_VIEWPORTSLIDER):
272                 value = min(adj->max_value,max(adj->min_value,
273                     adj->value + (adj->step * -direction)));
274             break;
275             default:
276                 value = min(adj->max_value,max(adj->min_value,
277                     adj->value + (adj->step * direction)));
278             break;
279         }
280         check_value_changed(adj, &value);
281     }
282 }
283 
_dummy1_callback(void * w_,void * _data,void * user_data)284 void _dummy1_callback(void *w_, void* _data, void* user_data) {
285     debug_print("Widget_t _dummy callback\n");
286 }
287 
_dummy_callback(void * w_,void * user_data)288 void _dummy_callback(void *w_, void* user_data) {
289     debug_print("Widget_t _dummy callback\n");
290 }
291 
_resize_surface(Widget_t * wid,int width,int height)292 void _resize_surface(Widget_t *wid, int width, int height) {
293     wid->width = width;
294     wid->height = height;
295     cairo_xlib_surface_set_size( wid->surface, wid->width, wid->height);
296     cairo_font_face_t *ff = cairo_get_font_face(wid->crb);
297     cairo_destroy(wid->crb);
298     cairo_surface_destroy(wid->buffer);
299     wid->buffer = cairo_surface_create_similar (wid->surface,
300                         CAIRO_CONTENT_COLOR_ALPHA, width, height);
301     assert(cairo_surface_status(wid->buffer) == CAIRO_STATUS_SUCCESS);
302     wid->crb = cairo_create (wid->buffer);
303     cairo_set_font_face(wid->crb, ff);
304 }
305 
_resize_childs(Widget_t * wid)306 void _resize_childs(Widget_t *wid) {
307     if(!childlist_has_child(wid->childlist)) return;
308     int i = 0;
309     for(;i<wid->childlist->elem;i++) {
310         Widget_t *w = wid->childlist->childs[i];
311         switch(w->scale.gravity) {
312             case(NORTHWEST):
313                 XResizeWindow (wid->app->dpy, w->widget, max(1,
314                     w->scale.init_width - (wid->scale.scale_x)),
315                     max(1,w->scale.init_height - (wid->scale.scale_y)));
316             break;
317             case(NORTHEAST):
318                 XResizeWindow (wid->app->dpy, w->widget, max(1,
319                     w->scale.init_width - (wid->scale.scale_x)), w->height);
320             break;
321             case(SOUTHWEST):
322                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x-wid->scale.scale_x,
323                                         w->scale.init_y-wid->scale.scale_y);
324             break;
325             case(SOUTHEAST):
326                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x,
327                                             w->scale.init_y-wid->scale.scale_y);
328             case(SOUTHCENTER):
329                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x,
330                                             w->scale.init_y / wid->scale.cscale_y);
331                 XResizeWindow (wid->app->dpy, w->widget, max(1,
332                     w->scale.init_width - (wid->scale.scale_x)),
333                     max(1,w->scale.init_height / (wid->scale.cscale_y)));
334             break;
335             case(EASTWEST):
336                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x,
337                                 w->scale.init_y-wid->scale.scale_y);
338             break;
339             case(EASTNORTH):
340                 XResizeWindow (wid->app->dpy, w->widget, w->scale.init_width,
341                     max(1,w->scale.init_height - (wid->scale.scale_y)));
342             break;
343             case(WESTNORTH):
344                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x-wid->scale.scale_x,
345                                                                     w->scale.init_y);
346             break;
347             case(WESTSOUTH):
348                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x-wid->scale.scale_x,
349                                                                     w->scale.init_y);
350                 XResizeWindow (wid->app->dpy, w->widget, w->scale.init_width,
351                     max(1,w->scale.init_height - (wid->scale.scale_y)));
352             break;
353             case(CENTER):
354                 XMoveWindow(wid->app->dpy,w->widget,w->scale.init_x /
355                     wid->scale.cscale_x,w->scale.init_y / wid->scale.cscale_y);
356                 XResizeWindow (wid->app->dpy, w->widget, max(1,
357                     w->scale.init_width / (wid->scale.cscale_x)),
358                     max(1,w->scale.init_height / (wid->scale.cscale_y)));
359             break;
360             case(ASPECT):
361                 XMoveWindow(wid->app->dpy,w->widget,(
362                     (w->scale.init_x + w->scale.init_width*0.5) /
363                     wid->scale.cscale_x) - w->width*0.5,
364                     ((w->scale.init_y + w->scale.init_height*0.5) /
365                     wid->scale.cscale_y)- w->height*0.5) ;
366                 XResizeWindow (wid->app->dpy, w->widget, max(1,
367                     w->scale.init_width / (wid->scale.ascale)),
368                     max(1,w->scale.init_height / (wid->scale.ascale)));
369             break;
370             case(FIXEDSIZE):
371                 XMoveWindow(wid->app->dpy,w->widget,(
372                     (w->scale.init_x + w->scale.init_width*0.5) /
373                     wid->scale.cscale_x) - w->width*0.5,
374                     ((w->scale.init_y + w->scale.init_height*0.5) /
375                     wid->scale.cscale_y)- w->height*0.5) ;
376             break;
377             case(MENUITEM):
378                 XResizeWindow (wid->app->dpy, w->widget, max(1,
379                     w->scale.init_width - (wid->scale.scale_x)-5), w->scale.init_height);
380             break;
381             case(NONE):
382             break;
383             default:
384             break;
385         }
386         w->func.configure_notify_callback(w,NULL);
387     }
388 }
389