awful.prompt: new arg 'selectall' in add()
[awesome.git] / widgets / graph.c
blobd0cbc5c09e11e9b3dd1623fef8b16e3b6af5b403
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 "common/tokenize.h"
28 extern awesome_t globalconf;
30 typedef enum
32 Bottom_Style = 0,
33 Top_Style,
34 Line_Style
35 } plot_style_t;
37 /** The plot data structure. */
38 typedef struct
40 /** Grapht title of the plot sections */
41 char *title;
42 /** Represents a full graph */
43 float max_value;
44 /** Scale the graph */
45 bool scale;
47 /* markers... */
48 /** Index of current (new) value */
49 int index;
50 /** Index of the actual maximum value */
51 int max_index;
52 /** Pointer to current maximum value itself */
53 float current_max;
54 /** Draw style of according index */
55 plot_style_t draw_style;
56 /** Keeps the calculated values (line-length); */
57 int *lines;
58 /** Actual values */
59 float *values;
60 /** Color of them */
61 xcolor_t color_start;
62 /** Color at middle of graph */
63 xcolor_t pcolor_center;
64 /** Color at end of graph */
65 xcolor_t pcolor_end;
66 /** Create a vertical color gradient */
67 bool vertical_gradient;
68 } plot_t;
70 static void
71 plot_delete(plot_t *g)
73 p_delete(&g->title);
74 p_delete(&g->lines);
75 p_delete(&g->values);
78 DO_ARRAY(plot_t, plot, plot_delete)
80 /** The private graph data structure */
81 typedef struct
83 /** Width of the widget */
84 int width;
85 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
86 float height;
87 /** Height of the innerbox in pixels */
88 int box_height;
89 /** Size of lines-array (also innerbox-length) */
90 int size;
91 /** Background color */
92 xcolor_t bg;
93 /** Border color */
94 xcolor_t border_color;
95 /** Grow: Left or Right */
96 position_t grow;
97 /** Preparation/tmp array for draw_graph(); */
98 int *draw_from;
99 /** Preparation/tmp array for draw_graph(); */
100 int *draw_to;
101 /** Graph list */
102 plot_array_t plots;
103 } graph_data_t;
105 /** Add a plot to a graph.
106 * \param d The graph private data.
107 * \param title The plot title.
108 * \return A new plot.
110 static plot_t *
111 graph_plot_add(graph_data_t *d, const char *title)
113 plot_t plot;
115 p_clear(&plot, 1);
117 plot.title = a_strdup(title);
118 plot.values = p_new(float, d->size);
119 plot.lines = p_new(int, d->size);
120 plot.max_value = 100.0;
121 plot.color_start = globalconf.colors.fg;
122 plot.vertical_gradient = true;
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, int height, int width)
154 area_t geometry;
155 graph_data_t *d = widget->data;
157 geometry.height = height;
158 geometry.width = d->width;
160 return geometry;
163 /** Draw a graph widget.
164 * \param ctx The draw context.
165 * \param screen The screen number.
166 * \param w The widget node we are called from.
167 * \param offset The offset to draw at.
168 * \param used The already used width.
169 * \param p A pointer to the object we're drawing onto.
170 * \return The widget width.
172 static void
173 graph_draw(widget_t *widget, draw_context_t *ctx,
174 area_t geometry, int screen, wibox_t *p)
176 int margin_top, y;
177 graph_data_t *d = widget->data;
178 area_t rectangle;
179 vector_t color_gradient;
181 if(!d->plots.len)
182 return;
184 /* box = the plot inside the rectangle */
185 if(!d->box_height)
186 d->box_height = round(ctx->height * d->height) - 2;
188 margin_top = round((ctx->height - (d->box_height + 2)) / 2) + geometry.y;
190 /* draw background */
191 rectangle.x = geometry.x + 1;
192 rectangle.y = margin_top + 1;
193 rectangle.width = d->size;
194 rectangle.height = d->box_height;
195 draw_rectangle(ctx, rectangle, 1.0, true, &d->bg);
197 /* for plot drawing */
198 rectangle.y = margin_top + d->box_height + 1; /* bottom left corner as starting point */
199 rectangle.width = d->size; /* rectangle.height is not used */
201 draw_graph_setup(ctx); /* setup some drawing options */
203 /* gradient begin either left or on the right of the rectangle */
204 if(d->grow == Right)
205 color_gradient.x = rectangle.x + rectangle.width;
206 else
207 color_gradient.x = rectangle.x;
209 for(int i = 0; i < d->plots.len; i++)
211 plot_t *plot = &d->plots.tab[i];
213 switch(plot->draw_style)
215 case Top_Style:
216 color_gradient.y = rectangle.y - rectangle.height;
217 if(plot->vertical_gradient)
219 color_gradient.x_offset = 0;
220 color_gradient.y_offset = rectangle.height;
222 else
224 color_gradient.y_offset = 0;
226 if(d->grow == Right)
227 color_gradient.x_offset = - rectangle.width;
228 else
229 color_gradient.x_offset = rectangle.width;
232 for(y = 0; y < d->size; y++)
234 /* reverse values (because drawing from top) */
235 d->draw_from[y] = d->box_height; /* i.e. no smaller value -> from top of box */
236 d->draw_to[y] = d->box_height - plot->lines[y]; /* i.e. on full plot -> 0 = bottom */
238 draw_graph(ctx, rectangle , d->draw_from, d->draw_to, plot->index, d->grow, color_gradient,
239 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
240 break;
241 case Bottom_Style:
242 color_gradient.y = rectangle.y;
243 if(plot->vertical_gradient)
245 color_gradient.x_offset = 0;
246 color_gradient.y_offset = - rectangle.height;
248 else
250 color_gradient.y_offset = 0;
252 if(d->grow == Right)
253 color_gradient.x_offset = - rectangle.width;
254 else
255 color_gradient.x_offset = rectangle.width;
258 p_clear(d->draw_from, d->size);
259 draw_graph(ctx, rectangle, d->draw_from, plot->lines, plot->index, d->grow, color_gradient,
260 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
261 break;
262 case Line_Style:
263 color_gradient.y = rectangle.y;
264 if(plot->vertical_gradient)
266 color_gradient.x_offset = 0;
267 color_gradient.y_offset = -rectangle.height;
269 else
271 color_gradient.y_offset = 0;
272 if(d->grow == Right)
273 color_gradient.x_offset = - rectangle.width;
274 else
275 color_gradient.x_offset = rectangle.width;
278 draw_graph_line(ctx, rectangle, plot->lines, plot->index, d->grow, color_gradient,
279 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
280 break;
284 /* draw border (after line-drawing, what paints 0-values to the border) */
285 rectangle.x = geometry.x;
286 rectangle.y = margin_top;
287 rectangle.width = d->size + 2;
288 rectangle.height = d->box_height + 2;
289 draw_rectangle(ctx, rectangle, 1.0, false, &d->border_color);
292 /** Set various plot graph properties.
293 * \param L The Lua VM state.
294 * \return The number of elements pushed on stack.
295 * \luastack
296 * \lvalue A widget.
297 * \lparam A plot name.
298 * \lparam A table with various properties set.
300 static int
301 luaA_graph_plot_properties_set(lua_State *L)
303 widget_t **widget = luaA_checkudata(L, 1, "widget");
304 graph_data_t *d = (*widget)->data;
305 float max_value;
306 const char *title, *buf;
307 size_t len;
308 plot_t *plot = NULL;
309 xcolor_init_request_t reqs[3];
310 int8_t i, reqs_nbr = -1;
312 title = luaL_checkstring(L, 2);
313 luaA_checktable(L, 3);
315 plot = graph_plot_get(d, title);
317 if((buf = luaA_getopt_lstring(L, 3, "fg", NULL, &len)))
318 reqs[++reqs_nbr] = xcolor_init_unchecked(&plot->color_start, buf, len);
320 if((buf = luaA_getopt_lstring(L, 3, "fg_center", NULL, &len)))
321 reqs[++reqs_nbr] = xcolor_init_unchecked(&plot->pcolor_center, buf, len);
323 if((buf = luaA_getopt_lstring(L, 3, "fg_end", NULL, &len)))
324 reqs[++reqs_nbr] = xcolor_init_unchecked(&plot->pcolor_end, buf, len);
326 plot->vertical_gradient = luaA_getopt_boolean(L, 3, "vertical_gradient", plot->vertical_gradient);
327 plot->scale = luaA_getopt_boolean(L, 3, "scale", plot->scale);
329 max_value = luaA_getopt_number(L, 3, "max_value", plot->max_value);
330 if(max_value != plot->max_value)
331 plot->max_value = plot->current_max = max_value;
333 if((buf = luaA_getopt_lstring(L, 3, "style", NULL, &len)))
334 switch (a_tokenize(buf, len))
336 case A_TK_BOTTOM:
337 plot->draw_style = Bottom_Style;
338 break;
339 case A_TK_LINE:
340 plot->draw_style = Line_Style;
341 break;
342 case A_TK_TOP:
343 plot->draw_style = Top_Style;
344 break;
345 default:
346 break;
349 for(i = 0; i <= reqs_nbr; i++)
350 xcolor_init_reply(reqs[i]);
352 widget_invalidate_bywidget(*widget);
354 return 0;
357 /** Add data to a plot.
358 * \param l The Lua VM state.
359 * \return The number of elements pushed on stack.
360 * \luastack
361 * \lvalue A widget.
362 * \lparam A plot name.
363 * \lparam A data value.
365 static int
366 luaA_graph_plot_data_add(lua_State *L)
368 widget_t **widget = luaA_checkudata(L, 1, "widget");
369 graph_data_t *d = (*widget)->data;
370 plot_t *plot = NULL;
371 const char *title = luaL_checkstring(L, 2);
372 float value;
373 int i;
375 plot = graph_plot_get(d, title);
377 /* assign incoming value */
378 value = MAX(luaL_checknumber(L, 3), 0);
380 if(++plot->index >= d->size) /* cycle inside the array */
381 plot->index = 0;
383 if(plot->scale) /* scale option is true */
385 plot->values[plot->index] = value;
387 if(value > plot->current_max) /* a new maximum value found */
389 plot->max_index = plot->index;
390 plot->current_max = value;
392 /* recalculate */
393 for (i = 0; i < d->size; i++)
394 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
396 /* old max_index reached + current_max > normal, re-check/generate */
397 else if(plot->max_index == plot->index
398 && plot->current_max > plot->max_value)
400 /* find the new max */
401 for(i = 0; i < d->size; i++)
402 if(plot->values[i] > plot->values[plot->max_index])
403 plot->max_index = i;
405 plot->current_max = MAX(plot->values[plot->max_index], plot->max_value);
407 /* recalculate */
408 for(i = 0; i < d->size; i++)
409 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
411 else
412 plot->lines[plot->index] = round(value * d->box_height / plot->current_max);
414 else /* scale option is false - limit to d->box_height */
416 if(value < plot->max_value)
417 plot->lines[plot->index] = round(value * d->box_height / plot->max_value);
418 else
419 plot->lines[plot->index] = d->box_height;
422 widget_invalidate_bywidget(*widget);
424 return 0;
427 /** Graph widget.
428 * \param L The Lua VM state.
429 * \param token The key token.
430 * \return The number of elements pushed on stack.
431 * \luastack
432 * \lfield plot_properties_set A function to set plot properties.
433 * \lfield plot_data_add A function to add data to a plot.
434 * \lfield height Graph height.
435 * \lfield widget Graph width.
436 * \lfield bg Background color.
437 * \lfield grow Direction to grow: left or right.
439 static int
440 luaA_graph_index(lua_State *L, awesome_token_t token)
442 widget_t **widget = luaA_checkudata(L, 1, "widget");
443 graph_data_t *d = (*widget)->data;
445 switch(token)
447 case A_TK_PLOT_PROPERTIES_SET:
448 lua_pushcfunction(L, luaA_graph_plot_properties_set);
449 break;
450 case A_TK_PLOT_DATA_ADD:
451 lua_pushcfunction(L, luaA_graph_plot_data_add);
452 break;
453 case A_TK_HEIGHT:
454 lua_pushnumber(L, d->height);
455 break;
456 case A_TK_WIDTH:
457 lua_pushnumber(L, d->width);
458 break;
459 case A_TK_BORDER_COLOR:
460 luaA_pushcolor(L, &d->border_color);
461 break;
462 case A_TK_BG:
463 luaA_pushcolor(L, &d->bg);
464 break;
465 case A_TK_GROW:
466 switch(d->grow)
468 case Left:
469 lua_pushliteral(L, "left");
470 break;
471 case Right:
472 lua_pushliteral(L, "right");
473 break;
474 default:
475 return 0;
477 break;
478 default:
479 return 0;
482 return 1;
485 /** Newindex function for graph widget.
486 * \param L The Lua VM state.
487 * \param token The key token.
488 * \return The number of elements pushed on stack.
490 static int
491 luaA_graph_newindex(lua_State *L, awesome_token_t token)
493 size_t len;
494 widget_t **widget = luaA_checkudata(L, 1, "widget");
495 graph_data_t *d = (*widget)->data;
496 const char *buf;
497 int width;
498 position_t pos;
499 xcolor_t color;
501 switch(token)
503 case A_TK_HEIGHT:
504 d->height = luaL_checknumber(L, 3);
505 break;
506 case A_TK_WIDTH:
507 width = luaL_checknumber(L, 3);
508 if(width != d->width)
510 d->width = width;
511 d->size = d->width - 2;
512 for(int i = 0; i < d->plots.len; i++)
514 plot_t *plot = &d->plots.tab[i];
515 p_realloc(&plot->values, d->size);
516 p_realloc(&plot->lines, d->size);
517 p_clear(plot->values, d->size);
518 p_clear(plot->lines, d->size);
519 plot->index = 0;
520 plot->current_max = 0;
521 plot->max_index = 0;
524 else
525 return 0;
526 break;
527 case A_TK_BG:
528 if((buf = luaL_checklstring(L, 3, &len)))
530 if(xcolor_init_reply(xcolor_init_unchecked(&color, buf, len)))
531 d->bg = color;
532 else
533 return 0;
535 break;
536 case A_TK_BORDER_COLOR:
537 if((buf = luaL_checklstring(L, 3, &len)))
539 if(xcolor_init_reply(xcolor_init_unchecked(&color, buf, len)))
540 d->border_color = color;
541 else
542 return 0;
544 break;
545 case A_TK_GROW:
546 buf = luaL_checklstring(L, 3, &len);
547 switch((pos = position_fromstr(buf, len)))
549 case Left:
550 case Right:
551 d->grow = pos;
552 break;
553 default:
554 return 0;
556 break;
557 default:
558 return 0;
561 widget_invalidate_bywidget(*widget);
563 return 0;
566 /** Destroy definitively a graph widget.
567 * \param widget Who slay.
569 static void
570 graph_destructor(widget_t *widget)
572 graph_data_t *d = widget->data;
574 plot_array_wipe(&d->plots);
575 p_delete(&d->draw_from);
576 p_delete(&d->draw_to);
577 p_delete(&d);
580 /** Create a brand new graph.
581 * \param align The widget alignment.
582 * \return A graph widget.
584 widget_t *
585 graph_new(alignment_t align)
587 widget_t *w;
588 graph_data_t *d;
590 w = p_new(widget_t, 1);
591 widget_common_new(w);
593 w->draw = graph_draw;
594 w->index = luaA_graph_index;
595 w->newindex = luaA_graph_newindex;
596 w->destructor = graph_destructor;
597 w->align = align;
598 w->geometry = graph_geometry;
599 d = w->data = p_new(graph_data_t, 1);
601 d->width = 80;
602 d->height = 0.80;
603 d->size = d->width - 2;
604 d->grow = Left;
605 d->draw_from = p_new(int, d->size);
606 d->draw_to = p_new(int, d->size);
608 d->bg = globalconf.colors.bg;
609 d->border_color = globalconf.colors.fg;
611 return w;
614 /* This is used for building documentation. */
615 static const struct luaL_reg awesome_graph_meta[] __attribute__ ((unused)) =
617 { "plot_properties_set", luaA_graph_plot_properties_set },
618 { "plot_data_add", luaA_graph_plot_data_add },
621 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80