client: make `ontop' windows under fullscreen
[awesome.git] / widgets / textbox.c
blobc19887e0d9958f1d66f9dd2ac87d3ffbf4512b5a
1 /*
2 * textbox.c - text box widget
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 "widget.h"
23 #include "common/tokenize.h"
25 extern awesome_t globalconf;
27 /** The textbox private data structure */
28 typedef struct
30 draw_text_context_t data;
31 /** Textbox width */
32 int width;
33 /** Extents */
34 area_t extents;
35 PangoEllipsizeMode ellip;
36 PangoWrapMode wrap;
37 /** Border */
38 struct
40 int width;
41 xcolor_t color;
42 } border;
43 /** Text alignment */
44 alignment_t align;
45 /** Margin */
46 padding_t margin;
47 /** Background color */
48 xcolor_t bg;
49 /** Background image */
50 image_t *bg_image;
51 /** Background resize to wibox height. */
52 bool bg_resize;
53 /** Background alignment */
54 alignment_t bg_align;
55 } textbox_data_t;
57 static area_t
58 textbox_geometry(widget_t *widget, int screen, int height, int width)
60 area_t geometry;
61 textbox_data_t *d = widget->data;
63 geometry.height = height;
65 if(d->width)
66 geometry.width = d->width;
67 else if(widget->align == AlignFlex)
68 geometry.width = width;
69 else if(d->bg_image)
71 double ratio = d->bg_resize ? (double) geometry.height / d->bg_image->height : 1;
72 geometry.width = MIN(width, MAX(d->extents.width + d->margin.left + d->margin.right, MAX(d->width, d->bg_image->width * ratio)));
74 else
75 geometry.width = MIN(d->extents.width + d->margin.left + d->margin.right, width);
77 return geometry;
80 /** Draw a textbox widget.
81 * \param widget The widget.
82 * \param ctx The draw context.
83 * \param screen The screen.
84 * \param p A pointer to the object we're draw onto.
86 static void
87 textbox_draw(widget_t *widget, draw_context_t *ctx, area_t geometry,
88 int screen, wibox_t *p)
90 textbox_data_t *d = widget->data;
92 if(d->bg.initialized)
93 draw_rectangle(ctx, geometry, 1.0, true, &d->bg);
95 if(d->border.width > 0)
96 draw_rectangle(ctx, geometry, d->border.width, false, &d->border.color);
98 if(d->bg_image)
100 double ratio = d->bg_resize ? (double) geometry.height / d->bg_image->height : 1;
101 /* check there is enough space to draw the image */
102 if(ratio * d->bg_image->width <= geometry.width)
104 int x = geometry.x;
105 int y = geometry.y;
106 switch(d->bg_align)
108 case AlignCenter:
109 x += (geometry.width - d->bg_image->width * ratio) / 2;
110 break;
111 case AlignRight:
112 x += geometry.width - d->bg_image->width * ratio;
113 break;
114 default:
115 break;
117 draw_image(ctx, x, y, ratio, d->bg_image);
121 draw_text(ctx, &d->data, d->ellip, d->wrap, d->align, &d->margin, geometry, &d->extents);
124 /** Delete a textbox widget.
125 * \param w The widget to destroy.
127 static void
128 textbox_destructor(widget_t *w)
130 textbox_data_t *d = w->data;
131 draw_text_context_wipe(&d->data);
132 p_delete(&d);
135 static int
136 luaA_textbox_margin(lua_State *L)
138 widget_t **widget = luaA_checkudata(L, 1, "widget");
139 textbox_data_t *d = (*widget)->data;
141 if(lua_gettop(L) == 2)
143 d->margin = luaA_getopt_padding(L, 3, &d->margin);
144 widget_invalidate_bywidget(*widget);
147 return luaA_pushpadding(L, &d->margin);
150 /** Textbox widget.
151 * \param L The Lua VM state.
152 * \param token The key token.
153 * \return The number of elements pushed on stack.
154 * \luastack
155 * \lfield text The text to display.
156 * \lfield width The width of the textbox. Set to 0 for auto.
157 * \lfield wrap The wrap mode: word, char, word_char.
158 * \lfield ellipsize The ellipsize mode: start, middle or end.
159 * \lfield border_width The border width to draw around.
160 * \lfield border_color The border color.
161 * \lfield align Text alignment, left, center or right.
162 * \lfield margin Method to pass text margin: a table with top, left, right and bottom keys.
163 * \lfield bg Background color.
164 * \lfield bg_image Background image.
165 * \lfield bg_align Background image alignment.
166 * \lfield bg_resize Background resize.
168 static int
169 luaA_textbox_index(lua_State *L, awesome_token_t token)
171 widget_t **widget = luaA_checkudata(L, 1, "widget");
172 textbox_data_t *d = (*widget)->data;
174 switch(token)
176 case A_TK_BG_RESIZE:
177 lua_pushboolean(L, d->bg_resize);
178 return 1;
179 case A_TK_BG_ALIGN:
180 lua_pushstring(L, draw_align_tostr(d->bg_align));
181 return 1;
182 case A_TK_BG_IMAGE:
183 if(d->bg_image)
184 return luaA_image_userdata_new(L, d->bg_image);
185 else
186 return 0;
187 case A_TK_BG:
188 return luaA_pushcolor(L, &d->bg);
189 case A_TK_MARGIN:
190 lua_pushcfunction(L, luaA_textbox_margin);
191 return 1;
192 case A_TK_ALIGN:
193 lua_pushstring(L, draw_align_tostr(d->align));
194 return 1;
195 case A_TK_BORDER_WIDTH:
196 lua_pushnumber(L, d->border.width);
197 return 1;
198 case A_TK_BORDER_COLOR:
199 luaA_pushcolor(L, &d->border.color);
200 return 1;
201 case A_TK_TEXT:
202 if(d->data.len > 0)
204 lua_pushlstring(L, d->data.text, d->data.len);
205 return 1;
207 return 0;
208 case A_TK_WIDTH:
209 lua_pushnumber(L, d->width);
210 return 1;
211 case A_TK_WRAP:
212 switch(d->wrap)
214 default:
215 lua_pushliteral(L, "word");
216 break;
217 case A_TK_CHAR:
218 lua_pushliteral(L, "char");
219 break;
220 case A_TK_WORD_CHAR:
221 lua_pushliteral(L, "word_char");
222 break;
224 return 1;
225 case A_TK_ELLIPSIZE:
226 switch(d->ellip)
228 case A_TK_START:
229 lua_pushliteral(L, "start");
230 break;
231 case A_TK_MIDDLE:
232 lua_pushliteral(L, "middle");
233 break;
234 default:
235 lua_pushliteral(L, "end");
236 break;
238 return 1;
239 default:
240 return 0;
244 /** The __newindex method for a textbox object.
245 * \param L The Lua VM state.
246 * \param token The key token.
247 * \return The number of elements pushed on stack.
249 static int
250 luaA_textbox_newindex(lua_State *L, awesome_token_t token)
252 size_t len = 0;
253 widget_t **widget = luaA_checkudata(L, 1, "widget");
254 const char *buf = NULL;
255 textbox_data_t *d = (*widget)->data;
256 image_t **image = NULL;
258 switch(token)
260 case A_TK_BG_ALIGN:
261 buf = luaL_checklstring(L, 3, &len);
262 d->bg_align = draw_align_fromstr(buf, len);
263 break;
264 case A_TK_BG_RESIZE:
265 d->bg_resize = luaA_checkboolean(L, 3);
266 break;
267 case A_TK_BG_IMAGE:
268 if(lua_isnil(L, 3)
269 || (image = luaA_checkudata(L, 3, "image")))
271 /* unref image */
272 image_unref(&d->bg_image);
273 if(image)
274 d->bg_image = image_ref(image);
275 else
276 d->bg_image = NULL;
278 break;
279 case A_TK_BG:
280 if(lua_isnil(L, 3))
281 p_clear(&d->bg, 1);
282 else if((buf = luaL_checklstring(L, 3, &len)))
283 xcolor_init_reply(xcolor_init_unchecked(&d->bg, buf, len));
284 break;
285 case A_TK_ALIGN:
286 if((buf = luaL_checklstring(L, 3, &len)))
287 d->align = draw_align_fromstr(buf, len);
288 break;
289 case A_TK_BORDER_COLOR:
290 if((buf = luaL_checklstring(L, 3, &len)))
291 xcolor_init_reply(xcolor_init_unchecked(&d->border.color, buf, len));
292 break;
293 case A_TK_BORDER_WIDTH:
294 d->border.width = luaL_checknumber(L, 3);
295 break;
296 case A_TK_TEXT:
297 if(lua_isnil(L, 3)
298 || (buf = luaL_checklstring(L, 3, &len)))
300 /* delete */
301 draw_text_context_wipe(&d->data);
302 p_clear(&d->data, 1);
304 if(buf)
306 char *text;
307 ssize_t tlen;
308 /* if text has been converted to UTF-8 */
309 if(draw_iso2utf8(buf, len, &text, &tlen))
311 draw_text_context_init(&d->data, text, tlen);
312 p_delete(&text);
314 else
315 draw_text_context_init(&d->data, buf, len);
317 d->extents = draw_text_extents(&d->data);
319 else
320 p_clear(&d->extents, 1);
322 break;
323 case A_TK_WIDTH:
324 d->width = luaL_checknumber(L, 3);
325 break;
326 case A_TK_WRAP:
327 if((buf = luaL_checklstring(L, 3, &len)))
328 switch(a_tokenize(buf, len))
330 case A_TK_WORD:
331 d->wrap = PANGO_WRAP_WORD;
332 break;
333 case A_TK_CHAR:
334 d->wrap = PANGO_WRAP_CHAR;
335 break;
336 case A_TK_WORD_CHAR:
337 d->wrap = PANGO_WRAP_WORD_CHAR;
338 break;
339 default:
340 break;
342 break;
343 case A_TK_ELLIPSIZE:
344 if((buf = luaL_checklstring(L, 3, &len)))
345 switch(a_tokenize(buf, len))
347 case A_TK_START:
348 d->ellip = PANGO_ELLIPSIZE_START;
349 break;
350 case A_TK_MIDDLE:
351 d->ellip = PANGO_ELLIPSIZE_MIDDLE;
352 break;
353 case A_TK_END:
354 d->ellip = PANGO_ELLIPSIZE_END;
355 break;
356 default:
357 break;
359 break;
360 default:
361 return 0;
364 widget_invalidate_bywidget(*widget);
366 return 0;
369 /** Create a new textbox widget.
370 * \param w The widget to initialize.
371 * \return A brand new widget.
373 widget_t *
374 widget_textbox(widget_t *w)
376 w->align_supported |= AlignFlex;
377 w->draw = textbox_draw;
378 w->index = luaA_textbox_index;
379 w->newindex = luaA_textbox_newindex;
380 w->destructor = textbox_destructor;
381 w->geometry = textbox_geometry;
383 textbox_data_t *d = w->data = p_new(textbox_data_t, 1);
384 d->ellip = PANGO_ELLIPSIZE_END;
386 return w;
389 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80