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.
27 #include "common/tokenize.h"
36 /** The plot data structure. */
39 /** Graph title of the plot sections */
41 /** Represents a full graph */
43 /** Scale the graph */
47 /** Index of current (new) value */
49 /** Index of the actual maximum value */
51 /** Pointer to current maximum value itself */
53 /** Draw style of according index */
54 plot_style_t draw_style
;
55 /** Keeps the calculated values (line-length); */
61 /** Color at middle of graph */
62 color_t pcolor_center
;
63 /** Color at end of graph */
65 /** Create a vertical color gradient */
66 bool vertical_gradient
;
70 plot_delete(plot_t
*g
)
77 DO_ARRAY(plot_t
, plot
, plot_delete
)
79 /** The private graph data structure */
82 /** Width of the widget */
84 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
86 /** Height of the innerbox in pixels */
88 /** Size of lines-array (also innerbox-length) */
90 /** Background color */
94 /** Grow: Left or Right */
96 /** Preparation/tmp array for draw_graph(); */
98 /** Preparation/tmp array for draw_graph(); */
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.
110 graph_plot_add(graph_data_t
*d
, const char *title
)
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.
135 graph_plot_get(graph_data_t
*d
, const char *title
)
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
))
147 /* no plot found -> create one */
148 return graph_plot_add(d
, title
);
152 graph_geometry(widget_t
*widget
, int screen
)
155 graph_data_t
*d
= widget
->data
;
157 geometry
.x
= geometry
.y
= 0;
158 geometry
.height
= d
->width
;
159 geometry
.width
= d
->width
;
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.
179 graph_draw(widget_t
*widget
, draw_context_t
*ctx
,
180 area_t geometry
, wibox_t
*p
)
183 graph_data_t
*d
= widget
->data
;
185 vector_t color_gradient
;
190 /* box = the plot inside the rectangle */
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 */
211 color_gradient
.x
= rectangle
.x
+ rectangle
.width
;
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
)
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
;
230 color_gradient
.y_offset
= 0;
233 color_gradient
.x_offset
= - rectangle
.width
;
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
);
248 color_gradient
.y
= rectangle
.y
;
249 if(plot
->vertical_gradient
)
251 color_gradient
.x_offset
= 0;
252 color_gradient
.y_offset
= - rectangle
.height
;
256 color_gradient
.y_offset
= 0;
259 color_gradient
.x_offset
= - rectangle
.width
;
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
);
269 color_gradient
.y
= rectangle
.y
;
270 if(plot
->vertical_gradient
)
272 color_gradient
.x_offset
= 0;
273 color_gradient
.y_offset
= -rectangle
.height
;
277 color_gradient
.y_offset
= 0;
279 color_gradient
.x_offset
= - rectangle
.width
;
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
);
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.
303 * \lparam A plot name.
304 * \lparam A table with various properties set.
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
;
312 const char *title
, *buf
;
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
))
343 plot
->draw_style
= Bottom_Style
;
346 plot
->draw_style
= Line_Style
;
349 plot
->draw_style
= Top_Style
;
355 for(i
= 0; i
<= reqs_nbr
; i
++)
356 color_init_reply(reqs
[i
]);
358 widget_invalidate_bywidget(widget
);
363 /** Add data to a plot.
364 * \param l The Lua VM state.
365 * \return The number of elements pushed on stack.
368 * \lparam A plot name.
369 * \lparam A data value.
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
;
377 const char *title
= luaL_checkstring(L
, 2);
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 */
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
;
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
])
414 plot
->current_max
= MAX(plot
->values
[plot
->max_index
], plot
->max_value
);
417 for(i
= 0; i
< d
->size
; i
++)
418 plot
->lines
[i
] = round(plot
->values
[i
] * d
->box_height
/ plot
->current_max
);
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
);
428 plot
->lines
[plot
->index
] = d
->box_height
;
431 widget_invalidate_bywidget(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.
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.
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
;
457 case A_TK_PLOT_PROPERTIES_SET
:
458 lua_pushcfunction(L
, luaA_graph_plot_properties_set
);
460 case A_TK_PLOT_DATA_ADD
:
461 lua_pushcfunction(L
, luaA_graph_plot_data_add
);
464 lua_pushnumber(L
, d
->height
);
467 lua_pushnumber(L
, d
->width
);
469 case A_TK_BORDER_COLOR
:
470 luaA_pushcolor(L
, &d
->border_color
);
473 luaA_pushcolor(L
, &d
->bg
);
479 lua_pushliteral(L
, "left");
482 lua_pushliteral(L
, "right");
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.
501 luaA_graph_newindex(lua_State
*L
, awesome_token_t token
)
504 widget_t
*widget
= luaA_checkudata(L
, 1, &widget_class
);
505 graph_data_t
*d
= widget
->data
;
514 d
->height
= luaL_checknumber(L
, 3);
517 width
= luaL_checknumber(L
, 3);
518 if(width
>= 2 && width
!= d
->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
);
532 plot
->current_max
= 0;
540 if((buf
= luaL_checklstring(L
, 3, &len
)))
542 if(color_init_reply(color_init_unchecked(&color
, buf
, len
)))
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
;
558 buf
= luaL_checklstring(L
, 3, &len
);
559 switch((pos
= position_fromstr(buf
, len
)))
573 widget_invalidate_bywidget(widget
);
578 /** Destroy definitively a graph widget.
579 * \param widget Who slay.
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
);
592 /** Create a brand new graph.
593 * \param w The widget to initialize.
594 * \return The same widget.
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);
610 d
->size
= d
->width
- 2;
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
);
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