1 /*
2 * widgets.c
3 *
4 * Copyright (C) Johan Malm 2017-2019
5 *
6 * A very simple widget implementation
7 *
8 * We read lines beginning with '@' from jgmenu flavoured CSV file and parses in
9 * accordance with the following syntax:
10 *
11 * @type,action,x,y,w,h,r,halign,valign,fgcol,bgcol,content
12 *
13 * where
14 * - action = what to do when clicked
15 * - (x, y) = margin
16 * - (w, h) = size
17 * - r = corner radius
18 * - content = icon_path or text
19 * note
20 * - For RECT, a 1px thick border will be drawn using fgcol
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "util.h"
28 #include "list.h"
29 #include "argv-buf.h"
30 #include "align.h"
31 #include "x11-ui.h"
32 #include "config.h"
33 #include "geometry.h"
34 #include "sbuf.h"
35 #include "filter.h"
36 #include "icon.h"
37 #include "x11-ui.h"
38 #include "widgets.h"
39 #include "banned.h"
40
41 enum widget_type { WIDGET_ERROR, ICON, RECT, TEXT, SEARCH };
42
43 static LIST_HEAD(widgets);
44
45 /* indicates if mouse is over any of the widgets */
46 static int mouseover;
47
48 /* "widgets" has control of keyboard movement keys */
49 static int keyboard_grabbed;
50
51 struct widget {
52 char *buf;
53 enum widget_type type;
54 char *action;
55 int x;
56 int y;
57 int w;
58 int h;
59 int r;
60 enum alignment halign;
61 enum alignment valign;
62 double fgcol[4];
63 double bgcol[4];
64 char *content;
65 cairo_surface_t *surface;
66 struct list_head list;
67 };
68
69 static struct widget *selection;
70
parse_type(const char * field)71 static enum widget_type parse_type(const char *field)
72 {
73 if (!field || !field[0])
74 return WIDGET_ERROR;
75 if (!strcmp(field, "icon"))
76 return ICON;
77 if (!strcmp(field, "rect"))
78 return RECT;
79 if (!strcmp(field, "search"))
80 return SEARCH;
81 if (!strcmp(field, "text"))
82 return TEXT;
83 return WIDGET_ERROR;
84 }
85
draw_icon(struct widget ** w)86 static void draw_icon(struct widget **w)
87 {
88 if (!(*w)->surface)
89 (*w)->surface = load_cairo_icon((*w)->content, (*w)->w);
90 if (!(*w)->surface)
91 return;
92 ui_insert_image((*w)->surface, (*w)->x, (*w)->y, (*w)->w, 1.0);
93 }
94
draw_rect(struct widget ** w)95 static void draw_rect(struct widget **w)
96 {
97 ui_draw_rectangle((*w)->x, (*w)->y, (*w)->w, (*w)->h, (*w)->r,
98 0.0, 1, (*w)->bgcol);
99 ui_draw_rectangle((*w)->x, (*w)->y, (*w)->w, (*w)->h, (*w)->r,
100 1.0, 0, (*w)->fgcol);
101 }
102
draw_search(struct widget ** w)103 static void draw_search(struct widget **w)
104 {
105 char *t;
106 int padding_left = 4;
107
108 if (filter_needle_length())
109 t = filter_strdup_needle();
110 else
111 t = xstrdup((*w)->content);
112 ui_insert_text(t, (*w)->x + padding_left, (*w)->y, (*w)->h, (*w)->w,
113 (*w)->fgcol, LEFT);
114 xfree(t);
115 }
116
draw_text(struct widget ** w)117 static void draw_text(struct widget **w)
118 {
119 ui_insert_text((*w)->content, (*w)->x, (*w)->y, (*w)->h, (*w)->w,
120 (*w)->fgcol, LEFT);
121 }
122
draw_selection(struct widget ** w)123 static void draw_selection(struct widget **w)
124 {
125 if (!(*w)->action || (*w)->action[0] == '\0')
126 return;
127 ui_draw_rectangle((*w)->x, (*w)->y, (*w)->w, (*w)->h, (*w)->r,
128 0.0, 1, config.color_sel_bg);
129 ui_draw_rectangle((*w)->x, (*w)->y, (*w)->w, (*w)->h, (*w)->r,
130 1.0, 0, config.color_sel_border);
131 }
132
widgets_select(const char * ksym)133 void widgets_select(const char *ksym)
134 {
135 struct widget *w;
136 enum direction {UNKNOWN, NEXT, PREV};
137 enum direction direction = UNKNOWN;
138
139 /*
140 * 'selection' is set when widgets are initiates
141 * if selection == 0, there are no selectable widgets
142 */
143 if (!selection)
144 return;
145 w = selection;
146 if (!strcmp(ksym, "XK_Down"))
147 direction = NEXT;
148 else if (!strcmp(ksym, "XK_Up"))
149 direction = PREV;
150 if (direction == UNKNOWN)
151 warn("unknown string passed to %s", __func__);
152 for (;;) {
153 switch (direction) {
154 case NEXT:
155 w = container_of(w->list.next, struct widget, list);
156 break;
157 case PREV:
158 w = container_of(w->list.prev, struct widget, list);
159 break;
160 case UNKNOWN:
161 return;
162 }
163 if (!w->action || w->action[0] == '\0')
164 continue;
165 selection = w;
166 break;
167 }
168 }
169
widgets_get_kb_grabbed(void)170 int widgets_get_kb_grabbed(void)
171 {
172 return keyboard_grabbed;
173 }
174
widgets_toggle_kb_grabbed(void)175 void widgets_toggle_kb_grabbed(void)
176 {
177 keyboard_grabbed = keyboard_grabbed ? 0 : 1;
178 }
179
widgets_mouseover(void)180 int widgets_mouseover(void)
181 {
182 return mouseover;
183 }
184
widgets_set_pointer_position(int x,int y)185 void widgets_set_pointer_position(int x, int y)
186 {
187 struct widget *w;
188 struct area widget_area;
189 struct point pointer;
190
191 mouseover = 0;
192 pointer.x = x;
193 pointer.y = y;
194 list_for_each_entry(w, &widgets, list) {
195 widget_area.x = w->x;
196 widget_area.y = w->y;
197 widget_area.w = w->w;
198 widget_area.h = w->h;
199 if (ui_is_point_in_area(pointer, widget_area)) {
200 selection = w;
201 mouseover = 1;
202 break;
203 }
204 }
205 }
206
207 /*
208 * widgets_set_point_position() should be run just before calling
209 * this function
210 */
widgets_get_selection_action(void)211 char *widgets_get_selection_action(void)
212 {
213 struct widget *w;
214
215 list_for_each_entry(w, &widgets, list) {
216 if (selection == w) {
217 if (!w->action || w->action[0] == '\0')
218 continue;
219 return w->action;
220 }
221 }
222 return NULL;
223 }
224
widgets_draw(void)225 void widgets_draw(void)
226 {
227 struct widget *w;
228
229 if (list_empty(&widgets))
230 return;
231 list_for_each_entry(w, &widgets, list) {
232 if (selection == w)
233 draw_selection(&w);
234 if (w->type == ICON)
235 draw_icon(&w);
236 else if (w->type == RECT)
237 draw_rect(&w);
238 else if (w->type == SEARCH)
239 draw_search(&w);
240 else if (w->type == TEXT)
241 draw_text(&w);
242 else
243 warn("widget type not recognised");
244 }
245 }
246
color_copy(double to[4],double from[4])247 static void color_copy(double to[4], double from[4])
248 {
249 int i;
250
251 for (i = 0; i < 4; i++)
252 to[i] = from[i];
253 }
254
widgets_add(const char * s)255 void widgets_add(const char *s)
256 {
257 struct argv_buf argv_buf;
258 struct widget *w;
259
260 w = xmalloc(sizeof(struct widget));
261 argv_init(&argv_buf);
262 argv_set_delim(&argv_buf, ',');
263 argv_strdup(&argv_buf, s);
264 argv_parse(&argv_buf);
265 if (argv_buf.argc != 12)
266 warn("widget did not contain 12 fields");
267 w->buf = argv_buf.buf;
268 w->type = parse_type(argv_buf.argv[0] + 1);
269 w->action = argv_buf.argv[1];
270 remove_caret_markup_closing_bracket(w->action);
271 xatoi(&w->x, argv_buf.argv[2], XATOI_NONNEG, "w->x");
272 xatoi(&w->y, argv_buf.argv[3], XATOI_NONNEG, "w->y");
273 xatoi(&w->w, argv_buf.argv[4], XATOI_NONNEG, "w->w");
274 xatoi(&w->h, argv_buf.argv[5], XATOI_NONNEG, "w->h");
275 xatoi(&w->r, argv_buf.argv[6], XATOI_NONNEG, "w->r");
276 /* enum alignment halign; */
277 /* enum alignment valign; */
278 if (!strcasecmp(argv_buf.argv[9], "auto"))
279 color_copy(w->fgcol, config.color_norm_fg);
280 else
281 parse_hexstr(argv_buf.argv[9], w->fgcol);
282 parse_hexstr(argv_buf.argv[10], w->bgcol);
283 w->content = argv_buf.argv[11];
284 w->surface = NULL;
285 list_add_tail(&w->list, &widgets);
286 if (selection)
287 return;
288 if (w->action && w->action[0] != '\0')
289 selection = w;
290 }
291
widgets_cleanup(void)292 void widgets_cleanup(void)
293 {
294 struct widget *w, *tmp_w;
295
296 list_for_each_entry_safe(w, tmp_w, &widgets, list) {
297 xfree(w->buf);
298 list_del(&w->list);
299 xfree(w);
300 }
301 }
302