client_unmanage: Update WM_STATE later
[awesome.git] / widgets / graph.c
blob2dbe6a3e2e189e7536ca318da1948e3669149eac
1 /*
2 * graph.c - a graph widget
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
5 * Copyright © 2007-2008 Marco Candrian <mac@calmar.ws>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <math.h>
25 #include "widget.h"
26 #include "luaa.h"
27 #include "common/tokenize.h"
29 typedef enum
31 Bottom_Style = 0,
32 Top_Style,
33 Line_Style
34 } plot_style_t;
36 /** The plot data structure. */
37 typedef struct
39 /** Graph title of the plot sections */
40 char *title;
41 /** Represents a full graph */
42 float max_value;
43 /** Scale the graph */
44 bool scale;
46 /* markers... */
47 /** Index of current (new) value */
48 int index;
49 /** Index of the actual maximum value */
50 int max_index;
51 /** Pointer to current maximum value itself */
52 float current_max;
53 /** Draw style of according index */
54 plot_style_t draw_style;
55 /** Keeps the calculated values (line-length); */
56 int *lines;
57 /** Actual values */
58 float *values;
59 /** Color of them */
60 color_t color_start;
61 /** Color at middle of graph */
62 color_t pcolor_center;
63 /** Color at end of graph */
64 color_t pcolor_end;
65 /** Create a vertical color gradient */
66 bool vertical_gradient;
67 } plot_t;
69 static void
70 plot_delete(plot_t *g)
72 p_delete(&g->title);
73 p_delete(&g->lines);
74 p_delete(&g->values);
77 DO_ARRAY(plot_t, plot, plot_delete)
79 /** The private graph data structure */
80 typedef struct
82 /** Width of the widget */
83 int width;
84 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
85 float height;
86 /** Height of the innerbox in pixels */
87 int box_height;
88 /** Size of lines-array (also innerbox-length) */
89 int size;
90 /** Background color */
91 color_t bg;
92 /** Border color */
93 color_t border_color;
94 /** Grow: Left or Right */
95 position_t grow;
96 /** Preparation/tmp array for draw_graph(); */
97 int *draw_from;
98 /** Preparation/tmp array for draw_graph(); */
99 int *draw_to;
100 /** Graph list */
101 plot_array_t plots;
102 } graph_data_t;
104 /** Add a plot to a graph.
105 * \param d The graph private data.
106 * \param title The plot title.
107 * \return A new plot.
109 static plot_t *
110 graph_plot_add(graph_data_t *d, const char *title)
112 plot_t plot;
114 p_clear(&plot, 1);
116 plot.title = a_strdup(title);
117 plot.values = p_new(float, d->size);
118 plot.lines = p_new(int, d->size);
119 plot.max_value = 100.0;
120 plot.vertical_gradient = true;
122 xcolor_to_color(&globalconf.colors.fg, &plot.color_start);
124 plot_array_append(&d->plots, plot);
126 return &d->plots.tab[d->plots.len - 1];
129 /** Get the plot, and create one if it does not exist.
130 * \param d The graph private data.
131 * \param title The plot title.
132 * \return A maybe new plot.
134 static plot_t *
135 graph_plot_get(graph_data_t *d, const char *title)
137 plot_t *plot;
139 /* check if this section is defined already */
140 for(int j = 0; j < d->plots.len; j++)
142 plot = &d->plots.tab[j];
143 if(!a_strcmp(title, plot->title))
144 return plot;
147 /* no plot found -> create one */
148 return graph_plot_add(d, title);
151 static area_t
152 graph_geometry(widget_t *widget, int screen)
154 area_t geometry;
155 graph_data_t *d = widget->data;
157 geometry.x = geometry.y = 0;
158 geometry.height = d->width;
159 geometry.width = d->width;
161 return geometry;
164 static area_t
165 graph_extents(lua_State *L, widget_t *widget)
167 return graph_geometry(widget, 0);
170 /** Draw a graph widget.
171 * \param ctx The draw context.
172 * \param w The widget node we are called from.
173 * \param offset The offset to draw at.
174 * \param used The already used width.
175 * \param p A pointer to the object we're drawing onto.
176 * \return The widget width.
178 static void
179 graph_draw(widget_t *widget, draw_context_t *ctx,
180 area_t geometry, wibox_t *p)
182 int margin_top, y;
183 graph_data_t *d = widget->data;
184 area_t rectangle;
185 vector_t color_gradient;
187 if(!d->plots.len)
188 return;
190 /* box = the plot inside the rectangle */
191 if(!d->box_height)
192 d->box_height = round(ctx->height * d->height) - 2;
194 margin_top = round((ctx->height - (d->box_height + 2)) / 2) + geometry.y;
196 /* draw background */
197 rectangle.x = geometry.x + 1;
198 rectangle.y = margin_top + 1;
199 rectangle.width = d->size;
200 rectangle.height = d->box_height;
201 draw_rectangle(ctx, rectangle, 1.0, true, &d->bg);
203 /* for plot drawing */
204 rectangle.y = margin_top + d->box_height + 1; /* bottom left corner as starting point */
205 rectangle.width = d->size; /* rectangle.height is not used */
207 draw_graph_setup(ctx); /* setup some drawing options */
209 /* gradient begin either left or on the right of the rectangle */
210 if(d->grow == Right)
211 color_gradient.x = rectangle.x + rectangle.width;
212 else
213 color_gradient.x = rectangle.x;
215 for(int i = 0; i < d->plots.len; i++)
217 plot_t *plot = &d->plots.tab[i];
219 switch(plot->draw_style)
221 case Top_Style:
222 color_gradient.y = rectangle.y - rectangle.height;
223 if(plot->vertical_gradient)
225 color_gradient.x_offset = 0;
226 color_gradient.y_offset = rectangle.height;
228 else
230 color_gradient.y_offset = 0;
232 if(d->grow == Right)
233 color_gradient.x_offset = - rectangle.width;
234 else
235 color_gradient.x_offset = rectangle.width;
238 for(y = 0; y < d->size; y++)
240 /* reverse values (because drawing from top) */
241 d->draw_from[y] = d->box_height; /* i.e. no smaller value -> from top of box */
242 d->draw_to[y] = d->box_height - plot->lines[y]; /* i.e. on full plot -> 0 = bottom */
244 draw_graph(ctx, rectangle , d->draw_from, d->draw_to, plot->index, d->grow, color_gradient,
245 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
246 break;
247 case Bottom_Style:
248 color_gradient.y = rectangle.y;
249 if(plot->vertical_gradient)
251 color_gradient.x_offset = 0;
252 color_gradient.y_offset = - rectangle.height;
254 else
256 color_gradient.y_offset = 0;
258 if(d->grow == Right)
259 color_gradient.x_offset = - rectangle.width;
260 else
261 color_gradient.x_offset = rectangle.width;
264 p_clear(d->draw_from, d->size);
265 draw_graph(ctx, rectangle, d->draw_from, plot->lines, plot->index, d->grow, color_gradient,
266 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
267 break;
268 case Line_Style:
269 color_gradient.y = rectangle.y;
270 if(plot->vertical_gradient)
272 color_gradient.x_offset = 0;
273 color_gradient.y_offset = -rectangle.height;
275 else
277 color_gradient.y_offset = 0;
278 if(d->grow == Right)
279 color_gradient.x_offset = - rectangle.width;
280 else
281 color_gradient.x_offset = rectangle.width;
284 draw_graph_line(ctx, rectangle, plot->lines, plot->index, d->grow, color_gradient,
285 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
286 break;
290 /* draw border (after line-drawing, what paints 0-values to the border) */
291 rectangle.x = geometry.x;
292 rectangle.y = margin_top;
293 rectangle.width = d->size + 2;
294 rectangle.height = d->box_height + 2;
295 draw_rectangle(ctx, rectangle, 1.0, false, &d->border_color);
298 /** Set various plot graph properties.
299 * \param L The Lua VM state.
300 * \return The number of elements pushed on stack.
301 * \luastack
302 * \lvalue A widget.
303 * \lparam A plot name.
304 * \lparam A table with various properties set.
306 static int
307 luaA_graph_plot_properties_set(lua_State *L)
309 widget_t *widget = luaA_checkudata(L, 1, &widget_class);
310 graph_data_t *d = widget->data;
311 float max_value;
312 const char *title, *buf;
313 size_t len;
314 plot_t *plot = NULL;
315 color_init_cookie_t reqs[3];
316 int i, reqs_nbr = -1;
318 title = luaL_checkstring(L, 2);
319 luaA_checktable(L, 3);
321 plot = graph_plot_get(d, title);
323 if((buf = luaA_getopt_lstring(L, 3, "fg", NULL, &len)))
324 reqs[++reqs_nbr] = color_init_unchecked(&plot->color_start, buf, len);
326 if((buf = luaA_getopt_lstring(L, 3, "fg_center", NULL, &len)))
327 reqs[++reqs_nbr] = color_init_unchecked(&plot->pcolor_center, buf, len);
329 if((buf = luaA_getopt_lstring(L, 3, "fg_end", NULL, &len)))
330 reqs[++reqs_nbr] = color_init_unchecked(&plot->pcolor_end, buf, len);
332 plot->vertical_gradient = luaA_getopt_boolean(L, 3, "vertical_gradient", plot->vertical_gradient);
333 plot->scale = luaA_getopt_boolean(L, 3, "scale", plot->scale);
335 max_value = luaA_getopt_number(L, 3, "max_value", plot->max_value);
336 if(max_value != plot->max_value)
337 plot->max_value = plot->current_max = max_value;
339 if((buf = luaA_getopt_lstring(L, 3, "style", NULL, &len)))
340 switch (a_tokenize(buf, len))
342 case A_TK_BOTTOM:
343 plot->draw_style = Bottom_Style;
344 break;
345 case A_TK_LINE:
346 plot->draw_style = Line_Style;
347 break;
348 case A_TK_TOP:
349 plot->draw_style = Top_Style;
350 break;
351 default:
352 break;
355 for(i = 0; i <= reqs_nbr; i++)
356 color_init_reply(reqs[i]);
358 widget_invalidate_bywidget(widget);
360 return 0;
363 /** Add data to a plot.
364 * \param l The Lua VM state.
365 * \return The number of elements pushed on stack.
366 * \luastack
367 * \lvalue A widget.
368 * \lparam A plot name.
369 * \lparam A data value.
371 static int
372 luaA_graph_plot_data_add(lua_State *L)
374 widget_t *widget = luaA_checkudata(L, 1, &widget_class);
375 graph_data_t *d = widget->data;
376 plot_t *plot = NULL;
377 const char *title = luaL_checkstring(L, 2);
378 float value;
379 int i;
381 if(!d->size)
382 return 0;
384 plot = graph_plot_get(d, title);
386 /* assign incoming value */
387 value = MAX(luaL_checknumber(L, 3), 0);
389 if(++plot->index >= d->size) /* cycle inside the array */
390 plot->index = 0;
392 if(plot->scale) /* scale option is true */
394 plot->values[plot->index] = value;
396 if(value > plot->current_max) /* a new maximum value found */
398 plot->max_index = plot->index;
399 plot->current_max = value;
401 /* recalculate */
402 for (i = 0; i < d->size; i++)
403 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
405 /* old max_index reached + current_max > normal, re-check/generate */
406 else if(plot->max_index == plot->index
407 && plot->current_max > plot->max_value)
409 /* find the new max */
410 for(i = 0; i < d->size; i++)
411 if(plot->values[i] > plot->values[plot->max_index])
412 plot->max_index = i;
414 plot->current_max = MAX(plot->values[plot->max_index], plot->max_value);
416 /* recalculate */
417 for(i = 0; i < d->size; i++)
418 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
420 else
421 plot->lines[plot->index] = round(value * d->box_height / plot->current_max);
423 else /* scale option is false - limit to d->box_height */
425 if(value < plot->max_value)
426 plot->lines[plot->index] = round(value * d->box_height / plot->max_value);
427 else
428 plot->lines[plot->index] = d->box_height;
431 widget_invalidate_bywidget(widget);
433 return 0;
436 /** Graph widget.
437 * DEPRECATED, see awful.widget.graph.
438 * \param L The Lua VM state.
439 * \param token The key token.
440 * \return The number of elements pushed on stack.
441 * \luastack
442 * \lfield plot_properties_set A function to set plot properties.
443 * \lfield plot_data_add A function to add data to a plot.
444 * \lfield height Graph height.
445 * \lfield widget Graph width.
446 * \lfield bg Background color.
447 * \lfield grow Direction to grow: left or right.
449 static int
450 luaA_graph_index(lua_State *L, awesome_token_t token)
452 widget_t *widget = luaA_checkudata(L, 1, &widget_class);
453 graph_data_t *d = widget->data;
455 switch(token)
457 case A_TK_PLOT_PROPERTIES_SET:
458 lua_pushcfunction(L, luaA_graph_plot_properties_set);
459 break;
460 case A_TK_PLOT_DATA_ADD:
461 lua_pushcfunction(L, luaA_graph_plot_data_add);
462 break;
463 case A_TK_HEIGHT:
464 lua_pushnumber(L, d->height);
465 break;
466 case A_TK_WIDTH:
467 lua_pushnumber(L, d->width);
468 break;
469 case A_TK_BORDER_COLOR:
470 luaA_pushcolor(L, &d->border_color);
471 break;
472 case A_TK_BG:
473 luaA_pushcolor(L, &d->bg);
474 break;
475 case A_TK_GROW:
476 switch(d->grow)
478 case Left:
479 lua_pushliteral(L, "left");
480 break;
481 case Right:
482 lua_pushliteral(L, "right");
483 break;
484 default:
485 return 0;
487 break;
488 default:
489 return 0;
492 return 1;
495 /** Newindex function for graph widget.
496 * \param L The Lua VM state.
497 * \param token The key token.
498 * \return The number of elements pushed on stack.
500 static int
501 luaA_graph_newindex(lua_State *L, awesome_token_t token)
503 size_t len;
504 widget_t *widget = luaA_checkudata(L, 1, &widget_class);
505 graph_data_t *d = widget->data;
506 const char *buf;
507 int width;
508 position_t pos;
509 color_t color;
511 switch(token)
513 case A_TK_HEIGHT:
514 d->height = luaL_checknumber(L, 3);
515 break;
516 case A_TK_WIDTH:
517 width = luaL_checknumber(L, 3);
518 if(width >= 2 && width != d->width)
520 d->width = width;
521 d->size = d->width - 2;
522 p_realloc(&d->draw_from, d->size);
523 p_realloc(&d->draw_to, d->size);
524 for(int i = 0; i < d->plots.len; i++)
526 plot_t *plot = &d->plots.tab[i];
527 p_realloc(&plot->values, d->size);
528 p_realloc(&plot->lines, d->size);
529 p_clear(plot->values, d->size);
530 p_clear(plot->lines, d->size);
531 plot->index = 0;
532 plot->current_max = 0;
533 plot->max_index = 0;
536 else
537 return 0;
538 break;
539 case A_TK_BG:
540 if((buf = luaL_checklstring(L, 3, &len)))
542 if(color_init_reply(color_init_unchecked(&color, buf, len)))
543 d->bg = color;
544 else
545 return 0;
547 break;
548 case A_TK_BORDER_COLOR:
549 if((buf = luaL_checklstring(L, 3, &len)))
551 if(color_init_reply(color_init_unchecked(&color, buf, len)))
552 d->border_color = color;
553 else
554 return 0;
556 break;
557 case A_TK_GROW:
558 buf = luaL_checklstring(L, 3, &len);
559 switch((pos = position_fromstr(buf, len)))
561 case Left:
562 case Right:
563 d->grow = pos;
564 break;
565 default:
566 return 0;
568 break;
569 default:
570 return 0;
573 widget_invalidate_bywidget(widget);
575 return 0;
578 /** Destroy definitively a graph widget.
579 * \param widget Who slay.
581 static void
582 graph_destructor(widget_t *widget)
584 graph_data_t *d = widget->data;
586 plot_array_wipe(&d->plots);
587 p_delete(&d->draw_from);
588 p_delete(&d->draw_to);
589 p_delete(&d);
592 /** Create a brand new graph.
593 * \param w The widget to initialize.
594 * \return The same widget.
596 widget_t *
597 widget_graph(widget_t *w)
599 luaA_deprecate(globalconf.L, "awful.widget.graph");
600 w->draw = graph_draw;
601 w->index = luaA_graph_index;
602 w->newindex = luaA_graph_newindex;
603 w->destructor = graph_destructor;
604 w->extents = graph_extents;
606 graph_data_t *d = w->data = p_new(graph_data_t, 1);
608 d->width = 80;
609 d->height = 0.80;
610 d->size = d->width - 2;
611 d->grow = Left;
612 d->draw_from = p_new(int, d->size);
613 d->draw_to = p_new(int, d->size);
615 xcolor_to_color(&globalconf.colors.bg, &d->bg);
616 xcolor_to_color(&globalconf.colors.fg, &d->border_color);
618 return w;
621 /* This is used for building documentation. */
622 static const struct luaL_reg awesome_graph_meta[] __attribute__ ((unused)) =
624 { "plot_properties_set", luaA_graph_plot_properties_set },
625 { "plot_data_add", luaA_graph_plot_data_add },
628 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80