screen: reset statusbar and windows properly on padding changes
[awesome.git] / widgets / graph.c
blobae56ef0a63816bd0a34c90e3260b4c455c79be30
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"
27 #include "common/draw.h"
29 extern awesome_t globalconf;
31 typedef enum
33 Bottom_Style = 0,
34 Top_Style,
35 Line_Style
36 } plot_style_t;
38 typedef struct plot_t plot_t;
40 /** The plot data structure. */
41 struct plot_t
43 /** Grapht title of the plot sections */
44 char *title;
45 /** Represents a full graph */
46 float max_value;
47 /** Scale the graph */
48 bool scale;
50 /* markers... */
51 /** Index of current (new) value */
52 int index;
53 /** Index of the actual maximum value */
54 int max_index;
55 /** Pointer to current maximum value itself */
56 float current_max;
57 /** Draw style of according index */
58 plot_style_t draw_style;
59 /** Keeps the calculated values (line-length); */
60 int *lines;
61 /** Actual values */
62 float *values;
63 /** Color of them */
64 xcolor_t color_start;
65 /** Color at middle of graph */
66 xcolor_t pcolor_center;
67 /** Color at end of graph */
68 xcolor_t pcolor_end;
69 /** Create a vertical color gradient */
70 bool vertical_gradient;
71 /** Next and previous graph */
72 plot_t *next, *prev;
75 static void
76 plot_delete(plot_t **g)
78 p_delete(&(*g)->title);
79 p_delete(&(*g)->lines);
80 p_delete(&(*g)->values);
81 p_delete(g);
84 DO_SLIST(plot_t, plot, plot_delete)
86 /** The private graph data structure */
87 typedef struct
89 /** Width of the widget */
90 int width;
91 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
92 float height;
93 /** Height of the innerbox in pixels */
94 int box_height;
95 /** Size of lines-array (also innerbox-length) */
96 int size;
97 /** Background color */
98 xcolor_t bg;
99 /** Border color */
100 xcolor_t border_color;
101 /** Grow: Left or Right */
102 position_t grow;
103 /** Preparation/tmp array for draw_graph(); */
104 int *draw_from;
105 /** Preparation/tmp array for draw_graph(); */
106 int *draw_to;
107 /** Graph list */
108 plot_t *plots;
109 } graph_data_t;
111 /** Add a plot to a graph.
112 * \param d The graph private data.
113 * \param title The plot title.
114 * \return A new plot.
116 static plot_t *
117 graph_plot_add(graph_data_t *d, const char *title)
119 plot_t *plot = p_new(plot_t, 1);
121 plot->title = a_strdup(title);
122 plot->values = p_new(float, d->size);
123 plot->lines = p_new(int, d->size);
124 plot->max_value = 100.0;
125 plot->color_start = globalconf.colors.fg;
126 plot->vertical_gradient = true;
128 plot_list_append(&d->plots, plot);
130 return plot;
133 /** Draw a graph widget.
134 * \param ctx The draw context.
135 * \param screen The screen number.
136 * \param w The widget node we are called from.
137 * \param offset The offset to draw at.
138 * \param used The already used width.
139 * \param p A pointer to the object we're drawing onto.
140 * \param type The object type.
141 * \return The widget width.
143 static int
144 graph_draw(draw_context_t *ctx,
145 int screen __attribute__ ((unused)),
146 widget_node_t *w,
147 int offset,
148 int used __attribute__ ((unused)),
149 void *p __attribute__ ((unused)),
150 awesome_type_t type)
152 int margin_top, y;
153 graph_data_t *d = w->widget->data;
154 area_t rectangle;
155 vector_t color_gradient;
156 plot_t *plot;
158 if(!d->plots)
159 return 0;
161 w->area.x = widget_calculate_offset(ctx->width,
162 d->width, offset,
163 w->widget->align);
164 w->area.y = 0;
166 /* box = the plot inside the rectangle */
167 if(!d->box_height)
168 d->box_height = round(ctx->height * d->height) - 2;
170 margin_top = round((ctx->height - (d->box_height + 2)) / 2) + w->area.y;
172 /* draw background */
173 rectangle.x = w->area.x + 1;
174 rectangle.y = margin_top + 1;
175 rectangle.width = d->size;
176 rectangle.height = d->box_height;
177 draw_rectangle(ctx, rectangle, 1.0, true, &d->bg);
179 /* for plot drawing */
180 rectangle.y = margin_top + d->box_height + 1; /* bottom left corner as starting point */
181 rectangle.width = d->size; /* rectangle.height is not used */
183 draw_graph_setup(ctx); /* setup some drawing options */
185 /* gradient begin either left or on the right of the rectangle */
186 if(d->grow == Right)
187 color_gradient.x = rectangle.x + rectangle.width;
188 else
189 color_gradient.x = rectangle.x;
191 for(plot = d->plots; plot; plot = plot->next)
192 switch(plot->draw_style)
194 case Top_Style:
195 color_gradient.y = rectangle.y - rectangle.height;
196 if(plot->vertical_gradient)
198 color_gradient.x_offset = 0;
199 color_gradient.y_offset = rectangle.height;
201 else
203 color_gradient.y_offset = 0;
205 if(d->grow == Right)
206 color_gradient.x_offset = - rectangle.width;
207 else
208 color_gradient.x_offset = rectangle.width;
211 for(y = 0; y < d->size; y++)
213 /* reverse values (because drawing from top) */
214 d->draw_from[y] = d->box_height; /* i.e. no smaller value -> from top of box */
215 d->draw_to[y] = d->box_height - plot->lines[y]; /* i.e. on full plot -> 0 = bottom */
217 draw_graph(ctx, rectangle , d->draw_from, d->draw_to, plot->index, d->grow, color_gradient,
218 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
219 break;
220 case Bottom_Style:
221 color_gradient.y = rectangle.y;
222 if(plot->vertical_gradient)
224 color_gradient.x_offset = 0;
225 color_gradient.y_offset = - rectangle.height;
227 else
229 color_gradient.y_offset = 0;
231 if(d->grow == Right)
232 color_gradient.x_offset = - rectangle.width;
233 else
234 color_gradient.x_offset = rectangle.width;
237 p_clear(d->draw_from, d->size);
238 draw_graph(ctx, rectangle, d->draw_from, plot->lines, plot->index, d->grow, color_gradient,
239 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
240 break;
241 case Line_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;
251 if(d->grow == Right)
252 color_gradient.x_offset = - rectangle.width;
253 else
254 color_gradient.x_offset = rectangle.width;
257 draw_graph_line(ctx, rectangle, plot->lines, plot->index, d->grow, color_gradient,
258 &plot->color_start, &plot->pcolor_center, &plot->pcolor_end);
259 break;
262 /* draw border (after line-drawing, what paints 0-values to the border) */
263 rectangle.x = w->area.x;
264 rectangle.y = margin_top;
265 rectangle.width = d->size + 2;
266 rectangle.height = d->box_height + 2;
267 draw_rectangle(ctx, rectangle, 1.0, false, &d->border_color);
269 w->area.width = d->width;
270 w->area.height = ctx->height;
271 return w->area.width;
274 /** Set various plot graph properties.
275 * \param L The Lua VM state.
276 * \return The number of elements pushed on stack.
277 * \luastack
278 * \lvalue A widget.
279 * \lparam A plot name.
280 * \lparam A table with various properties set.
282 static int
283 luaA_graph_plot_properties_set(lua_State *L)
285 widget_t **widget = luaA_checkudata(L, 1, "widget");
286 graph_data_t *d = (*widget)->data;
287 float max_value;
288 const char *title, *buf;
289 size_t len;
290 plot_t *plot;
291 xcolor_init_request_t reqs[3];
292 int8_t i, reqs_nbr = -1;
294 title = luaL_checkstring(L, 2);
295 luaA_checktable(L, 3);
297 for(plot = d->plots; plot; plot = plot->next)
298 if(!a_strcmp(title, plot->title))
299 break;
300 /* no plot found -> create one */
301 if(!plot)
302 plot = graph_plot_add(d, title);
304 if((buf = luaA_getopt_lstring(L, 3, "fg", NULL, &len)))
305 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
306 &plot->color_start,
307 globalconf.default_screen,
308 buf, len);
310 if((buf = luaA_getopt_lstring(L, 3, "fg_center", NULL, &len)))
311 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
312 &plot->pcolor_center,
313 globalconf.default_screen,
314 buf, len);
316 if((buf = luaA_getopt_lstring(L, 3, "fg_end", NULL, &len)))
317 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
318 &plot->pcolor_end,
319 globalconf.default_screen,
320 buf, len);
322 plot->vertical_gradient = luaA_getopt_boolean(L, 3, "vertical_gradient", plot->vertical_gradient);
323 plot->scale = luaA_getopt_boolean(L, 3, "scale", plot->scale);
325 max_value = luaA_getopt_number(L, 3, "max_value", plot->max_value);
326 if(max_value != plot->max_value)
327 plot->max_value = plot->current_max = max_value;
329 if((buf = luaA_getopt_lstring(L, 3, "style", NULL, &len)))
330 switch (a_tokenize(buf, len))
332 case A_TK_BOTTOM:
333 plot->draw_style = Bottom_Style;
334 break;
335 case A_TK_LINE:
336 plot->draw_style = Line_Style;
337 break;
338 case A_TK_TOP:
339 plot->draw_style = Top_Style;
340 break;
341 default:
342 break;
345 for(i = 0; i <= reqs_nbr; i++)
346 xcolor_init_reply(globalconf.connection, reqs[i]);
348 widget_invalidate_bywidget(*widget);
350 return 0;
353 /** Add data to a plot.
354 * \param l The Lua VM state.
355 * \return The number of elements pushed on stack.
356 * \luastack
357 * \lvalue A widget.
358 * \lparam A plot name.
359 * \lparam A data value.
361 static int
362 luaA_graph_plot_data_add(lua_State *L)
364 widget_t **widget = luaA_checkudata(L, 1, "widget");
365 graph_data_t *d = (*widget)->data;
366 plot_t *plot;
367 const char *title = luaL_checkstring(L, 2);
368 float value;
369 int i;
371 for(plot = d->plots; plot; plot = plot->next)
372 if(!a_strcmp(title, plot->title))
373 break;
375 /* no plot found -> create one */
376 if(!plot)
377 plot = graph_plot_add(d, title);
379 /* assign incoming value */
380 value = MAX(luaL_checknumber(L, 3), 0);
382 if(++plot->index >= d->size) /* cycle inside the array */
383 plot->index = 0;
385 if(plot->scale) /* scale option is true */
387 plot->values[plot->index] = value;
389 if(value > plot->current_max) /* a new maximum value found */
391 plot->max_index = plot->index;
392 plot->current_max = value;
394 /* recalculate */
395 for (i = 0; i < d->size; i++)
396 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
398 /* old max_index reached + current_max > normal, re-check/generate */
399 else if(plot->max_index == plot->index
400 && plot->current_max > plot->max_value)
402 /* find the new max */
403 for(i = 0; i < d->size; i++)
404 if(plot->values[i] > plot->values[plot->max_index])
405 plot->max_index = i;
407 plot->current_max = MAX(plot->values[plot->max_index], plot->max_value);
409 /* recalculate */
410 for(i = 0; i < d->size; i++)
411 plot->lines[i] = round(plot->values[i] * d->box_height / plot->current_max);
413 else
414 plot->lines[plot->index] = round(value * d->box_height / plot->current_max);
416 else /* scale option is false - limit to d->box_height */
418 if(value < plot->max_value)
419 plot->lines[plot->index] = round(value * d->box_height / plot->max_value);
420 else
421 plot->lines[plot->index] = d->box_height;
424 widget_invalidate_bywidget(*widget);
426 return 0;
429 /** Graph widget.
430 * \param L The Lua VM state.
431 * \param token The key token.
432 * \return The number of elements pushed on stack.
434 static int
435 luaA_graph_index(lua_State *L, awesome_token_t token)
437 widget_t **widget = luaA_checkudata(L, 1, "widget");
438 graph_data_t *d = (*widget)->data;
440 switch(token)
442 case A_TK_PLOT_PROPERTIES_SET:
443 lua_pushcfunction(L, luaA_graph_plot_properties_set);
444 break;
445 case A_TK_PLOT_DATA_ADD:
446 lua_pushcfunction(L, luaA_graph_plot_data_add);
447 break;
448 case A_TK_HEIGHT:
449 lua_pushnumber(L, d->height);
450 break;
451 case A_TK_WIDTH:
452 lua_pushnumber(L, d->width);
453 break;
454 case A_TK_BORDER_COLOR:
455 luaA_pushcolor(L, &d->border_color);
456 break;
457 case A_TK_BG:
458 luaA_pushcolor(L, &d->bg);
459 break;
460 case A_TK_GROW:
461 switch(d->grow)
463 case Left:
464 lua_pushliteral(L, "left");
465 break;
466 case Right:
467 lua_pushliteral(L, "right");
468 break;
469 default:
470 return 0;
472 break;
473 default:
474 return 0;
477 return 1;
480 /** Newindex function for graph widget.
481 * \param L The Lua VM state.
482 * \param token The key token.
483 * \return The number of elements pushed on stack.
485 static int
486 luaA_graph_newindex(lua_State *L, awesome_token_t token)
488 size_t len;
489 widget_t **widget = luaA_checkudata(L, 1, "widget");
490 graph_data_t *d = (*widget)->data;
491 const char *buf;
492 int width;
493 plot_t *plot;
494 position_t pos;
495 xcolor_t color;
497 switch(token)
499 case A_TK_HEIGHT:
500 d->height = luaL_checknumber(L, 3);
501 break;
502 case A_TK_WIDTH:
503 width = luaL_checknumber(L, 3);
504 if(width != d->width)
506 d->width = width;
507 d->size = d->width - 2;
508 for(plot = d->plots; plot; plot = plot->next)
510 p_realloc(&plot->values, d->size);
511 p_realloc(&plot->lines, d->size);
512 p_clear(plot->values, d->size);
513 p_clear(plot->lines, d->size);
514 plot->index = 0;
515 plot->current_max = 0;
516 plot->max_index = 0;
519 else
520 return 0;
521 break;
522 case A_TK_BG:
523 if((buf = luaL_checklstring(L, 3, &len)))
525 if(xcolor_init_reply(globalconf.connection,
526 xcolor_init_unchecked(globalconf.connection,
527 &color,
528 globalconf.default_screen,
529 buf, len)))
530 d->bg = color;
531 else
532 return 0;
534 break;
535 case A_TK_BORDER_COLOR:
536 if((buf = luaL_checklstring(L, 3, &len)))
538 if(xcolor_init_reply(globalconf.connection,
539 xcolor_init_unchecked(globalconf.connection,
540 &color,
541 globalconf.default_screen,
542 buf, len)))
543 d->border_color = color;
544 else
545 return 0;
547 break;
548 case A_TK_GROW:
549 buf = luaL_checklstring(L, 3, &len);
550 switch((pos = position_fromstr(buf, len)))
552 case Left:
553 case Right:
554 d->grow = pos;
555 break;
556 default:
557 return 0;
559 break;
560 default:
561 return 0;
564 widget_invalidate_bywidget(*widget);
566 return 0;
569 /** Destroy definitively a graph widget.
570 * \param widget Who slay.
572 static void
573 graph_destructor(widget_t *widget)
575 graph_data_t *d = widget->data;
577 plot_list_wipe(&d->plots);
578 p_delete(&d->draw_from);
579 p_delete(&d->draw_to);
580 p_delete(&d);
583 /** Create a brand new graph.
584 * \param align The widget alignment.
585 * \return A graph widget.
587 widget_t *
588 graph_new(alignment_t align)
590 widget_t *w;
591 graph_data_t *d;
593 w = p_new(widget_t, 1);
594 widget_common_new(w);
596 w->draw = graph_draw;
597 w->index = luaA_graph_index;
598 w->newindex = luaA_graph_newindex;
599 w->destructor = graph_destructor;
600 w->align = align;
601 d = w->data = p_new(graph_data_t, 1);
603 d->width = 80;
604 d->height = 0.80;
605 d->size = d->width - 2;
606 d->grow = Left;
607 d->draw_from = p_new(int, d->size);
608 d->draw_to = p_new(int, d->size);
610 d->bg = globalconf.colors.bg;
611 d->border_color = globalconf.colors.fg;
613 return w;
616 /* This is used for building documentation. */
617 static const struct luaL_reg awesome_graph_meta[] __attribute__ ((unused)) =
619 { "plot_properties_set", luaA_graph_plot_properties_set },
620 { "plot_data_add", luaA_graph_plot_data_add },
623 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80