magnifier: try harder to find parent of dialog
[awesome.git] / widget.c
blobd25fc750b0f70ead9d6e64e3df4ea57f75c3f991
1 /*
2 * widget.c - widget managing
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <math.h>
24 #include <xcb/xcb.h>
25 #include <xcb/xcb_atom.h>
27 #include "mouse.h"
28 #include "widget.h"
29 #include "wibox.h"
30 #include "common/atoms.h"
32 extern awesome_t globalconf;
34 DO_LUA_NEW(extern, widget_t, widget, "widget", widget_ref)
35 DO_LUA_GC(widget_t, widget, "widget", widget_unref)
36 DO_LUA_EQ(widget_t, widget, "widget")
38 #include "widgetgen.h"
40 /** Delete a widget structure.
41 * \param widget The widget to destroy.
43 void
44 widget_delete(widget_t **widget)
46 if((*widget)->destructor)
47 (*widget)->destructor(*widget);
48 button_array_wipe(&(*widget)->buttons);
49 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*widget)->mouse_enter);
50 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*widget)->mouse_leave);
51 p_delete(&(*widget)->name);
52 p_delete(widget);
55 /** Compute offset for drawing the first pixel of a widget.
56 * \param barwidth The wibox width.
57 * \param widgetwidth The widget width.
58 * \param alignment The widget alignment on wibox.
59 * \return The x coordinate to draw at.
61 int
62 widget_calculate_offset(int barwidth, int widgetwidth, int offset, int alignment)
64 switch(alignment)
66 case AlignLeft:
67 case AlignFlex:
68 return offset;
70 return barwidth - offset - widgetwidth;
73 /** Common function for button press event on widget.
74 * It will look into configuration to find the callback function to call.
75 * \param w The widget node.
76 * \param ev The button press event the widget received.
77 * \param screen The screen number.
78 * \param p The object where user clicked.
80 static void
81 widget_common_button(widget_node_t *w,
82 xcb_button_press_event_t *ev,
83 int screen __attribute__ ((unused)),
84 wibox_t *p)
86 button_array_t *b = &w->widget->buttons;
88 for(int i = 0; i < b->len; i++)
89 if(ev->detail == b->tab[i]->button
90 && XUTIL_MASK_CLEAN(ev->state) == b->tab[i]->mod)
91 switch(ev->response_type)
93 case XCB_BUTTON_PRESS:
94 if(b->tab[i]->press != LUA_REFNIL)
96 luaA_wibox_userdata_new(globalconf.L, p);
97 luaA_dofunction(globalconf.L, b->tab[i]->press, 1, 0);
99 break;
100 case XCB_BUTTON_RELEASE:
101 if(b->tab[i]->release != LUA_REFNIL)
103 luaA_wibox_userdata_new(globalconf.L, p);
104 luaA_dofunction(globalconf.L, b->tab[i]->release, 1, 0);
106 break;
110 /** Convert a Lua table to a list of widget nodet.
111 * \param L The Lua VM state.
112 * \param widgets The linked list of widget node.
114 void
115 luaA_table2widgets(lua_State *L, widget_node_array_t *widgets)
117 if(lua_istable(L, -1))
119 lua_pushnil(L);
120 while(luaA_next(L, -2))
122 luaA_table2widgets(L, widgets);
123 lua_pop(L, 1); /* remove value */
126 else
128 widget_t **widget = luaA_toudata(L, -1, "widget");
129 if(widget)
131 widget_node_t w;
132 p_clear(&w, 1);
133 w.widget = widget_ref(widget);
134 widget_node_array_append(widgets, w);
139 /** Render a list of widgets.
140 * \param wnode The list of widgets.
141 * \param ctx The draw context where to render.
142 * \param rotate_px The rotate pixmap: where to rotate and render the final
143 * pixmap when the object oritation is not east.
144 * \param screen The logical screen used to render.
145 * \param orientation The object orientation.
146 * \param x The x coordinates of the object.
147 * \param y The y coordinates of the object.
148 * \param wibox The wibox.
149 * \todo Remove GC.
151 void
152 widget_render(widget_node_array_t *widgets, draw_context_t *ctx, xcb_gcontext_t gc, xcb_pixmap_t rotate_px,
153 int screen, orientation_t orientation,
154 int x, int y, wibox_t *wibox)
156 int left = 0, right = 0;
157 area_t rectangle = { 0, 0, 0, 0 };
159 rectangle.width = ctx->width;
160 rectangle.height = ctx->height;
162 if(ctx->bg.alpha != 0xffff)
164 xcb_get_property_reply_t *prop_r;
165 char *data;
166 xcb_pixmap_t rootpix;
167 xcb_get_property_cookie_t prop_c;
168 xcb_screen_t *s = xutil_screen_get(globalconf.connection, ctx->phys_screen);
169 prop_c = xcb_get_property_unchecked(globalconf.connection, false, s->root, _XROOTPMAP_ID,
170 PIXMAP, 0, 1);
171 if((prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL)))
173 if(prop_r->value_len
174 && (data = xcb_get_property_value(prop_r))
175 && (rootpix = *(xcb_pixmap_t *) data))
176 switch(orientation)
178 case North:
179 draw_rotate(ctx,
180 rootpix, ctx->pixmap,
181 s->width_in_pixels, s->height_in_pixels,
182 ctx->width, ctx->height,
183 M_PI_2,
184 y + ctx->width,
185 - x);
186 break;
187 case South:
188 draw_rotate(ctx,
189 rootpix, ctx->pixmap,
190 s->width_in_pixels, s->height_in_pixels,
191 ctx->width, ctx->height,
192 - M_PI_2,
193 - y,
194 x + ctx->height);
195 break;
196 case East:
197 xcb_copy_area(globalconf.connection, rootpix,
198 rotate_px, gc,
199 x, y,
200 0, 0,
201 ctx->width, ctx->height);
202 break;
204 p_delete(&prop_r);
208 /* compute geometry */
209 for(int i = 0; i < widgets->len; i++)
210 if(widgets->tab[i].widget->align == AlignLeft && widgets->tab[i].widget->isvisible)
212 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
213 screen, ctx->height,
214 ctx->width - (left + right));
215 widgets->tab[i].geometry.x = left;
216 left += widgets->tab[i].geometry.width;
219 for(int i = widgets->len - 1; i >= 0; i--)
220 if(widgets->tab[i].widget->align == AlignRight && widgets->tab[i].widget->isvisible)
222 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
223 screen, ctx->height,
224 ctx->width - (left + right));
225 right += widgets->tab[i].geometry.width;
226 widgets->tab[i].geometry.x = ctx->width - right;
229 /* save left value */
230 int fake_left = left;
232 /* compute width of flex aligned widgets that does not support it */
233 int flex = 0;
234 for(int i = 0; i < widgets->len; i++)
235 if(widgets->tab[i].widget->align == AlignFlex
236 && widgets->tab[i].widget->isvisible)
238 if(widgets->tab[i].widget->align_supported & AlignFlex)
239 flex++;
240 else
241 fake_left += widgets->tab[i].widget->geometry(widgets->tab[i].widget,
242 screen, ctx->height,
243 ctx->width - (fake_left + right)).width;
246 /* now compute everybody together! */
247 int flex_rendered = 0;
248 for(int i = 0; i < widgets->len; i++)
249 if(widgets->tab[i].widget->align == AlignFlex
250 && widgets->tab[i].widget->isvisible)
252 if(widgets->tab[i].widget->align_supported & AlignFlex)
254 int width = (ctx->width - (right + fake_left)) / flex;
255 /* give last pixels to last flex to be rendered */
256 if(flex_rendered == flex - 1)
257 width += (ctx->width - (right + fake_left)) % flex;
258 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
259 screen, ctx->height,
260 width);
261 flex_rendered++;
263 else
264 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
265 screen, ctx->height,
266 ctx->width - (left + right));
267 widgets->tab[i].geometry.x = left;
268 left += widgets->tab[i].geometry.width;
271 /* draw everything! */
272 draw_rectangle(ctx, rectangle, 1.0, true, &ctx->bg);
274 for(int i = 0; i < widgets->len; i++)
275 if(widgets->tab[i].widget->isvisible)
277 widgets->tab[i].geometry.y = 0;
278 widgets->tab[i].widget->draw(widgets->tab[i].widget,
279 ctx, widgets->tab[i].geometry,
280 screen, wibox);
283 switch(orientation)
285 case South:
286 draw_rotate(ctx, ctx->pixmap, rotate_px,
287 ctx->width, ctx->height,
288 ctx->height, ctx->width,
289 M_PI_2, ctx->height, 0);
290 break;
291 case North:
292 draw_rotate(ctx, ctx->pixmap, rotate_px,
293 ctx->width, ctx->height,
294 ctx->height, ctx->width,
295 - M_PI_2, 0, ctx->width);
296 break;
297 case East:
298 break;
302 /** Common function for creating a widget.
303 * \param widget The allocated widget.
305 void
306 widget_common_new(widget_t *widget)
308 widget->button = widget_common_button;
309 widget->align_supported = AlignLeft | AlignRight;
312 /** Invalidate widgets which should be refresh upon
313 * external modifications. widget_t who watch flags will
314 * be set to be refreshed.
315 * \param screen Virtual screen number.
316 * \param flags Cache flags to invalidate.
318 void
319 widget_invalidate_cache(int screen, int flags)
321 for(int i = 0; i < globalconf.screens[screen].wiboxes.len; i++)
323 wibox_t *wibox = globalconf.screens[screen].wiboxes.tab[i];
325 for(int j = 0; j < wibox->widgets.len; j++)
326 if(wibox->widgets.tab[j].widget->cache_flags & flags)
328 wibox->need_update = true;
329 break;
334 /** Set a wibox needs update because it has widget, or redraw a titlebar.
335 * \todo Probably needs more optimization.
336 * \param widget The widget to look for.
338 void
339 widget_invalidate_bywidget(widget_t *widget)
341 for(int screen = 0; screen < globalconf.nscreen; screen++)
342 for(int i = 0; i < globalconf.screens[screen].wiboxes.len; i++)
344 wibox_t *wibox = globalconf.screens[screen].wiboxes.tab[i];
345 if(!wibox->need_update)
346 for(int j = 0; j < wibox->widgets.len; j++)
347 if(wibox->widgets.tab[j].widget == widget)
349 wibox->need_update = true;
350 break;
354 for(client_t *c = globalconf.clients; c; c = c->next)
355 if(c->titlebar && !c->titlebar->need_update)
356 for(int j = 0; j < c->titlebar->widgets.len; j++)
357 if(c->titlebar->widgets.tab[j].widget == widget)
359 c->titlebar->need_update = true;
360 break;
364 /** Deprecated function to add mouse bindings.
365 * \param L The Lua VM state.
366 * \return The number of elements pushed on stack.
368 static int
369 luaA_widget_mouse_add(lua_State *L)
371 deprecate(L, "widget:buttons()");
372 return 0;
375 /** Create a new widget.
376 * \param L The Lua VM state.
378 * \luastack
379 * \lparam A table with at least a name and a type value. Optional attributes
380 * are: align.
381 * \lreturn A brand new widget.
383 static int
384 luaA_widget_new(lua_State *L)
386 const char *buf, *type;
387 widget_t *w = NULL;
388 widget_constructor_t *wc;
389 alignment_t align;
390 size_t len;
392 luaA_checktable(L, 2);
394 buf = luaA_getopt_lstring(L, 2, "align", "left", &len);
395 align = draw_align_fromstr(buf, len);
397 buf = luaA_getopt_string(L, 2, "name", NULL);
399 type = luaA_getopt_string(L, 2, "type", NULL);
401 /* deprecated, compatibility code */
402 if(!a_strcmp(type, "tasklist")
403 || !a_strcmp(type, "taglist"))
405 deprecate(L, "awful.widget.tasklist and awful.widget.taglist");
406 luaA_warn(L, "requesting old widgets, return a table to allow smooth execution.\n");
407 lua_newtable(L);
408 lua_pushcfunction(L, luaA_widget_mouse_add);
409 lua_setfield(L, -2, "mouse_add");
410 return 1;
412 else if((wc = name_func_lookup(type, WidgetList)))
413 w = wc(align);
414 else
416 luaA_warn(L, "unkown widget type: %s", type);
417 return 0;
420 w->type = wc;
422 /* Set visible by default. */
423 w->isvisible = true;
425 w->mouse_enter = w->mouse_leave = LUA_REFNIL;
427 w->name = a_strdup(buf);
429 return luaA_widget_userdata_new(L, w);
432 /** Get or set mouse buttons bindings to a widget.
433 * \param L The Lua VM state.
435 * \luastack
436 * \lvalue A widget.
437 * \lparam An array of mouse button bindings objects, or nothing.
438 * \return The array of mouse button bindings objects of this widget.
440 static int
441 luaA_widget_buttons(lua_State *L)
443 widget_t **widget = luaA_checkudata(L, 1, "widget");
444 button_array_t *buttons = &(*widget)->buttons;
446 if(lua_gettop(L) == 2)
448 luaA_button_array_set(L, 2, buttons);
449 return 1;
452 return luaA_button_array_get(L, buttons);
455 /** Generic widget.
456 * \param L The Lua VM state.
457 * \return The number of elements pushed on stack.
458 * \luastack
459 * \lfield visible The widget visibility.
460 * \lfield name The widget name.
461 * \lfield mouse_enter A function to execute when the mouse enter the widget.
462 * \lfield mouse_leave A function to execute when the mouse leave the widget.
464 static int
465 luaA_widget_index(lua_State *L)
467 size_t len;
468 widget_t **widget = luaA_checkudata(L, 1, "widget");
469 const char *buf = luaL_checklstring(L, 2, &len);
470 awesome_token_t token;
472 if(luaA_usemetatable(L, 1, 2))
473 return 1;
475 switch((token = a_tokenize(buf, len)))
477 case A_TK_VISIBLE:
478 lua_pushboolean(L, (*widget)->isvisible);
479 return 1;
480 case A_TK_NAME:
481 if((*widget)->name)
482 lua_pushstring(L, (*widget)->name);
483 else
484 lua_pushnil(L);
485 return 1;
486 case A_TK_MOUSE_ENTER:
487 if((*widget)->mouse_enter != LUA_REFNIL)
488 lua_rawgeti(L, LUA_REGISTRYINDEX, (*widget)->mouse_enter);
489 else
490 return 0;
491 return 1;
492 case A_TK_MOUSE_LEAVE:
493 if((*widget)->mouse_leave != LUA_REFNIL)
494 lua_rawgeti(L, LUA_REGISTRYINDEX, (*widget)->mouse_leave);
495 else
496 return 0;
497 return 1;
498 default:
499 break;
502 return (*widget)->index ? (*widget)->index(L, token) : 0;
505 /** Generic widget newindex.
506 * \param L The Lua VM state.
507 * \return The number of elements pushed on stack.
509 static int
510 luaA_widget_newindex(lua_State *L)
512 size_t len;
513 widget_t **widget = luaA_checkudata(L, 1, "widget");
514 const char *buf = luaL_checklstring(L, 2, &len);
515 awesome_token_t token;
517 switch((token = a_tokenize(buf, len)))
519 case A_TK_VISIBLE:
520 (*widget)->isvisible = luaA_checkboolean(L, 3);
521 break;
522 case A_TK_MOUSE_ENTER:
523 luaA_registerfct(L, 3, &(*widget)->mouse_enter);
524 break;
525 case A_TK_MOUSE_LEAVE:
526 luaA_registerfct(L, 3, &(*widget)->mouse_leave);
527 break;
528 default:
529 return (*widget)->newindex ? (*widget)->newindex(L, token) : 0;
532 widget_invalidate_bywidget(*widget);
534 return 0;
537 const struct luaL_reg awesome_widget_methods[] =
539 { "__call", luaA_widget_new },
540 { NULL, NULL }
542 const struct luaL_reg awesome_widget_meta[] =
544 { "buttons", luaA_widget_buttons },
545 { "mouse_add", luaA_widget_mouse_add },
546 { "__index", luaA_widget_index },
547 { "__newindex", luaA_widget_newindex },
548 { "__gc", luaA_widget_gc },
549 { "__eq", luaA_widget_eq },
550 { "__tostring", luaA_widget_tostring },
551 { NULL, NULL }
554 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80