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 "xcombobox.h"
22 #include "xcombobox_private.h"
23 #include "xtooltip.h"
24 #include "xslider.h"
25 
26 
combobox_set_active_entry(Widget_t * w,int active)27 void combobox_set_active_entry(Widget_t *w, int active) {
28     float value = (float)active;
29     if (value>w->adj->max_value) value = w->adj->max_value;
30     else if (value<w->adj->min_value) value = w->adj->min_value;
31     adj_set_value(w->adj, value);
32 }
33 
combobox_mem_free(void * w_,void * user_data)34 void combobox_mem_free(void *w_, void* user_data) {
35     Widget_t *w = (Widget_t*)w_;
36     ComboBox_t *comboboxlist = (ComboBox_t*)w->parent_struct;
37     unsigned int j = 0;
38     for(; j<comboboxlist->list_size;j++) {
39         free(comboboxlist->list_names[j]);
40         comboboxlist->list_names[j] = NULL;
41     }
42     free(comboboxlist);
43 }
44 
combobox_delete_entrys(Widget_t * combobox)45 void combobox_delete_entrys(Widget_t *combobox) {
46     Widget_t * menu = combobox->childlist->childs[1];
47     Widget_t* view_port =  menu->childlist->childs[0];
48     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
49     unsigned int j = 0;
50     for(; j<comboboxlist->list_size;j++) {
51         free(comboboxlist->list_names[j]);
52         comboboxlist->list_names[j] = NULL;
53     }
54     comboboxlist->list_size = 0;
55     set_adjustment(combobox->adj,0.0, 0.0, 0.0, -1.0,1.0, CL_ENUM);
56     set_adjustment(view_port->adj,0.0, 0.0, 0.0, -6.0,1.0, CL_ENUM);
57     set_adjustment(comboboxlist->slider->adj,0.0, 0.0, 0.0, 1.0,0.0085, CL_VIEWPORTSLIDER);
58 }
59 
pop_combobox_menu_show(Widget_t * parent,Widget_t * menu,int elem,bool above)60 void pop_combobox_menu_show(Widget_t *parent, Widget_t *menu, int elem, bool above) {
61     if (!childlist_has_child(menu->childlist)) return;
62     Widget_t* view_port =  menu->childlist->childs[0];
63     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
64     if (!comboboxlist->list_size) return;
65     _configure_combobox_menu(parent, menu, elem, above);
66     pop_widget_show_all(menu);
67     int err = XGrabPointer(menu->app->dpy, DefaultRootWindow(parent->app->dpy), True,
68                  ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
69                  GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
70     menu->app->hold_grab = menu;
71 
72     if (err) debug_print("Error grap pointer\n");
73 }
74 
75 
create_combobox_viewport(Widget_t * parent,int elem,int width,int height)76 Widget_t* create_combobox_viewport(Widget_t *parent, int elem, int width, int height) {
77     Widget_t *wid = create_widget(parent->app, parent, 0, 0, width, height);
78     XSelectInput(wid->app->dpy, wid->widget,StructureNotifyMask|ExposureMask|KeyPressMask
79                     |EnterWindowMask|LeaveWindowMask|ButtonReleaseMask
80                     |ButtonPressMask|Button1MotionMask|PointerMotionMask);
81     wid->scale.gravity = CENTER;
82     ComboBox_t *comboboxlist;
83     comboboxlist = (ComboBox_t*)malloc(sizeof(ComboBox_t));
84     comboboxlist->show_items = elem;
85     comboboxlist->list_names = NULL;
86     comboboxlist->list_size = 0;
87     wid->flags |= HAS_MEM;
88     wid->parent_struct = comboboxlist;
89     float max_value = -elem;
90     wid->adj_y = add_adjustment(wid,0.0, 0.0, 0.0, max_value,1.0, CL_VIEWPORT);
91     wid->adj = wid->adj_y;
92     wid->func.adj_callback = _set_combobox_viewpoint;
93     wid->func.motion_callback = _combobox_motion;
94     wid->func.leave_callback = _leave_combobox;
95     wid->func.button_release_callback = _combobox_entry_released;
96     wid->func.key_press_callback = _combobox_key_pressed;
97     wid->func.expose_callback = _draw_combobox_entrys;
98     wid->func.configure_notify_callback = _reconfigure_combobox_viewport;
99     wid->func.mem_free_callback = combobox_mem_free;
100     return wid;
101 }
102 
create_combobox_menu(Widget_t * parent,int height)103 Widget_t* create_combobox_menu(Widget_t *parent, int height) {
104 
105     int x1, y1;
106     Window child;
107     XTranslateCoordinates( parent->app->dpy, parent->widget, DefaultRootWindow(parent->app->dpy), 0, 0, &x1, &y1, &child );
108     Widget_t *wid = create_window(parent->app, DefaultRootWindow(parent->app->dpy), x1, y1, 10, height);
109     Widget_t *viewport = create_combobox_viewport(wid, 6, 10, 5*height);
110     ComboBox_t *comboboxlist = (ComboBox_t*)viewport->parent_struct;
111     comboboxlist->combobox = parent;
112 
113     XSetWindowAttributes attributes;
114     attributes.override_redirect = True;
115     XChangeWindowAttributes(parent->app->dpy, wid->widget, CWOverrideRedirect, &attributes);
116 
117     Atom window_type = XInternAtom(wid->app->dpy, "_NET_WM_WINDOW_TYPE", False);
118     Atom window_type_popup = XInternAtom(wid->app->dpy, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False);
119     XChangeProperty(wid->app->dpy, wid->widget, window_type,
120         XA_ATOM, 32, PropModeReplace, (unsigned char *) &window_type_popup,1 );
121 
122     Atom window_state = XInternAtom(wid->app->dpy, "_NET_WM_STATE", False);
123     Atom window_state_modal = XInternAtom(wid->app->dpy, "_NET_WM_STATE_MODAL", False);
124     XChangeProperty(wid->app->dpy, wid->widget, window_state,
125         XA_ATOM, 32, PropModeReplace, (unsigned char *) &window_state_modal, 1);
126 
127     XSetTransientForHint(parent->app->dpy,wid->widget,parent->widget);
128     wid->func.expose_callback = _draw_combobox_menu;
129     wid->flags |= IS_POPUP;
130     wid->scale.gravity = NONE;
131     childlist_add_child(parent->childlist, wid);
132 
133     comboboxlist->slider = add_vslider(wid, "", 0, 0, 10, height);
134     comboboxlist->slider->func.expose_callback = _draw_combobox_menu_slider;
135     comboboxlist->slider->adj_y = add_adjustment(comboboxlist->slider,0.0, 0.0, 0.0, 1.0,0.0085, CL_VIEWPORTSLIDER);
136     comboboxlist->slider->adj = comboboxlist->slider->adj_y;
137     comboboxlist->slider->func.value_changed_callback = _set_combobox_menu_viewport;
138     comboboxlist->slider->scale.gravity = NORTHWEST;
139     comboboxlist->slider->flags &= ~USE_TRANSPARENCY;
140     comboboxlist->slider->flags |= NO_AUTOREPEAT | NO_PROPAGATE;
141     comboboxlist->slider->parent_struct = viewport;
142 
143     return wid;
144 }
145 
add_combobox(Widget_t * parent,const char * label,int x,int y,int width,int height)146 Widget_t* add_combobox(Widget_t *parent, const char  * label, int x, int y, int width, int height) {
147 
148     Widget_t *wid = create_widget(parent->app, parent, x, y, width, height);
149     wid->label = label;
150     wid->scale.gravity = CENTER;
151     wid->adj_y = add_adjustment(wid,0.0, 0.0, 0.0, -1.0,1.0, CL_ENUM);
152     wid->adj = wid->adj_y;
153     wid->func.adj_callback = _set_entry;
154     wid->func.expose_callback = _draw_combobox;
155     wid->func.enter_callback = transparent_draw;
156     wid->func.leave_callback = transparent_draw;
157     wid->func.button_release_callback = _combobox_button_released;
158 
159     Widget_t* button = add_button(wid, "", width-20, 0, 20, height);
160     button->func.expose_callback = _draw_combobox_button;
161     button->func.button_release_callback = _button_combobox_released;
162 
163     Widget_t* menu = create_combobox_menu(wid, 25);
164     menu->func.button_release_callback = _entry_released;
165 
166     return wid;
167 }
168 
combobox_add_entry(Widget_t * wid,const char * label)169 void combobox_add_entry(Widget_t *wid, const char  * label) {
170     Widget_t *menu = wid->childlist->childs[1];
171     Widget_t* view_port =  menu->childlist->childs[0];
172     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
173     comboboxlist->list_names = (char **)realloc(comboboxlist->list_names,
174       (comboboxlist->list_size + 1) * sizeof(char *));
175     asprintf(&comboboxlist->list_names[comboboxlist->list_size++],"%s",label);
176     assert(comboboxlist->list_names != NULL);
177     float max_value = wid->adj->max_value+1.0;
178     set_adjustment(wid->adj,0.0, max_value, 0.0, max_value,1.0, CL_ENUM);
179 
180 }
181 
combobox_add_numeric_entrys(Widget_t * wid,int imin,int imax)182 void combobox_add_numeric_entrys(Widget_t *wid, int imin, int imax) {
183     int i = imin;
184     int o = imax+1;
185     for (;i<o;i++) {
186         char s[32];
187         snprintf(s, 31, "%i",i);
188         combobox_add_entry(wid, s);
189     }
190 }
191