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.
26 #include "common/tokenize.h"
28 extern awesome_t globalconf
;
37 /** The plot data structure. */
40 /** Grapht title of the plot sections */
42 /** Represents a full graph */
44 /** Scale the graph */
48 /** Index of current (new) value */
50 /** Index of the actual maximum value */
52 /** Pointer to current maximum value itself */
54 /** Draw style of according index */
55 plot_style_t draw_style
;
56 /** Keeps the calculated values (line-length); */
62 /** Color at middle of graph */
63 xcolor_t pcolor_center
;
64 /** Color at end of graph */
66 /** Create a vertical color gradient */
67 bool vertical_gradient
;
71 plot_delete(plot_t
*g
)
78 DO_ARRAY(plot_t
, plot
, plot_delete
)
80 /** The private graph data structure */
83 /** Width of the widget */
85 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
87 /** Height of the innerbox in pixels */
89 /** Size of lines-array (also innerbox-length) */
91 /** Background color */
94 xcolor_t border_color
;
95 /** Grow: Left or Right */
97 /** Preparation/tmp array for draw_graph(); */
99 /** Preparation/tmp array for draw_graph(); */
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.
111 graph_plot_add(graph_data_t
*d
, const char *title
)
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.
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
, int height
, int width
)
155 graph_data_t
*d
= widget
->data
;
157 geometry
.height
= height
;
158 geometry
.width
= d
->width
;
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.
173 graph_draw(widget_t
*widget
, draw_context_t
*ctx
,
174 area_t geometry
, int screen
, wibox_t
*p
)
177 graph_data_t
*d
= widget
->data
;
179 vector_t color_gradient
;
184 /* box = the plot inside the rectangle */
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 */
205 color_gradient
.x
= rectangle
.x
+ rectangle
.width
;
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
)
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
;
224 color_gradient
.y_offset
= 0;
227 color_gradient
.x_offset
= - rectangle
.width
;
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
);
242 color_gradient
.y
= rectangle
.y
;
243 if(plot
->vertical_gradient
)
245 color_gradient
.x_offset
= 0;
246 color_gradient
.y_offset
= - rectangle
.height
;
250 color_gradient
.y_offset
= 0;
253 color_gradient
.x_offset
= - rectangle
.width
;
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
);
263 color_gradient
.y
= rectangle
.y
;
264 if(plot
->vertical_gradient
)
266 color_gradient
.x_offset
= 0;
267 color_gradient
.y_offset
= -rectangle
.height
;
271 color_gradient
.y_offset
= 0;
273 color_gradient
.x_offset
= - rectangle
.width
;
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
);
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.
297 * \lparam A plot name.
298 * \lparam A table with various properties set.
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
;
306 const char *title
, *buf
;
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
))
337 plot
->draw_style
= Bottom_Style
;
340 plot
->draw_style
= Line_Style
;
343 plot
->draw_style
= Top_Style
;
349 for(i
= 0; i
<= reqs_nbr
; i
++)
350 xcolor_init_reply(reqs
[i
]);
352 widget_invalidate_bywidget(*widget
);
357 /** Add data to a plot.
358 * \param l The Lua VM state.
359 * \return The number of elements pushed on stack.
362 * \lparam A plot name.
363 * \lparam A data value.
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
;
371 const char *title
= luaL_checkstring(L
, 2);
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 */
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
;
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
])
405 plot
->current_max
= MAX(plot
->values
[plot
->max_index
], plot
->max_value
);
408 for(i
= 0; i
< d
->size
; i
++)
409 plot
->lines
[i
] = round(plot
->values
[i
] * d
->box_height
/ plot
->current_max
);
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
);
419 plot
->lines
[plot
->index
] = d
->box_height
;
422 widget_invalidate_bywidget(*widget
);
428 * \param L The Lua VM state.
429 * \param token The key token.
430 * \return The number of elements pushed on stack.
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.
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
;
447 case A_TK_PLOT_PROPERTIES_SET
:
448 lua_pushcfunction(L
, luaA_graph_plot_properties_set
);
450 case A_TK_PLOT_DATA_ADD
:
451 lua_pushcfunction(L
, luaA_graph_plot_data_add
);
454 lua_pushnumber(L
, d
->height
);
457 lua_pushnumber(L
, d
->width
);
459 case A_TK_BORDER_COLOR
:
460 luaA_pushcolor(L
, &d
->border_color
);
463 luaA_pushcolor(L
, &d
->bg
);
469 lua_pushliteral(L
, "left");
472 lua_pushliteral(L
, "right");
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.
491 luaA_graph_newindex(lua_State
*L
, awesome_token_t token
)
494 widget_t
**widget
= luaA_checkudata(L
, 1, "widget");
495 graph_data_t
*d
= (*widget
)->data
;
504 d
->height
= luaL_checknumber(L
, 3);
507 width
= luaL_checknumber(L
, 3);
508 if(width
!= d
->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
);
520 plot
->current_max
= 0;
528 if((buf
= luaL_checklstring(L
, 3, &len
)))
530 if(xcolor_init_reply(xcolor_init_unchecked(&color
, buf
, len
)))
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
;
546 buf
= luaL_checklstring(L
, 3, &len
);
547 switch((pos
= position_fromstr(buf
, len
)))
561 widget_invalidate_bywidget(*widget
);
566 /** Destroy definitively a graph widget.
567 * \param widget Who slay.
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
);
580 /** Create a brand new graph.
581 * \param align The widget alignment.
582 * \return A graph widget.
585 graph_new(alignment_t align
)
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
;
598 w
->geometry
= graph_geometry
;
599 d
= w
->data
= p_new(graph_data_t
, 1);
603 d
->size
= d
->width
- 2;
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
;
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