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"
27 #include "common/draw.h"
29 extern awesome_t globalconf
;
38 typedef struct plot_t plot_t
;
40 /** The plot data structure. */
43 /** Grapht title of the plot sections */
45 /** Represents a full graph */
47 /** Scale the graph */
51 /** Index of current (new) value */
53 /** Index of the actual maximum value */
55 /** Pointer to current maximum value itself */
57 /** Draw style of according index */
58 plot_style_t draw_style
;
59 /** Keeps the calculated values (line-length); */
65 /** Color at middle of graph */
66 xcolor_t pcolor_center
;
67 /** Color at end of graph */
69 /** Create a vertical color gradient */
70 bool vertical_gradient
;
71 /** Next and previous graph */
76 plot_delete(plot_t
**g
)
78 p_delete(&(*g
)->title
);
79 p_delete(&(*g
)->lines
);
80 p_delete(&(*g
)->values
);
84 DO_SLIST(plot_t
, plot
, plot_delete
)
86 /** The private graph data structure */
89 /** Width of the widget */
91 /** Height of graph (0.0-1.0; 1.0 = height of bar) */
93 /** Height of the innerbox in pixels */
95 /** Size of lines-array (also innerbox-length) */
97 /** Background color */
100 xcolor_t border_color
;
101 /** Grow: Left or Right */
103 /** Preparation/tmp array for draw_graph(); */
105 /** Preparation/tmp array for draw_graph(); */
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.
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
);
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.
144 graph_draw(draw_context_t
*ctx
,
145 int screen
__attribute__ ((unused
)),
148 int used
__attribute__ ((unused
)),
149 void *p
__attribute__ ((unused
)),
153 graph_data_t
*d
= w
->widget
->data
;
155 vector_t color_gradient
;
161 w
->area
.x
= widget_calculate_offset(ctx
->width
,
166 /* box = the plot inside the rectangle */
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 */
187 color_gradient
.x
= rectangle
.x
+ rectangle
.width
;
189 color_gradient
.x
= rectangle
.x
;
191 for(plot
= d
->plots
; plot
; plot
= plot
->next
)
192 switch(plot
->draw_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
;
203 color_gradient
.y_offset
= 0;
206 color_gradient
.x_offset
= - rectangle
.width
;
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
);
221 color_gradient
.y
= rectangle
.y
;
222 if(plot
->vertical_gradient
)
224 color_gradient
.x_offset
= 0;
225 color_gradient
.y_offset
= - rectangle
.height
;
229 color_gradient
.y_offset
= 0;
232 color_gradient
.x_offset
= - rectangle
.width
;
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
);
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;
252 color_gradient
.x_offset
= - rectangle
.width
;
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
);
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.
279 * \lparam A plot name.
280 * \lparam A table with various properties set.
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
;
288 const char *title
, *buf
;
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
))
300 /* no plot found -> create one */
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
,
307 globalconf
.default_screen
,
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
,
316 if((buf
= luaA_getopt_lstring(L
, 3, "fg_end", NULL
, &len
)))
317 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
319 globalconf
.default_screen
,
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
))
333 plot
->draw_style
= Bottom_Style
;
336 plot
->draw_style
= Line_Style
;
339 plot
->draw_style
= Top_Style
;
345 for(i
= 0; i
<= reqs_nbr
; i
++)
346 xcolor_init_reply(globalconf
.connection
, reqs
[i
]);
348 widget_invalidate_bywidget(*widget
);
353 /** Add data to a plot.
354 * \param l The Lua VM state.
355 * \return The number of elements pushed on stack.
358 * \lparam A plot name.
359 * \lparam A data value.
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
;
367 const char *title
= luaL_checkstring(L
, 2);
371 for(plot
= d
->plots
; plot
; plot
= plot
->next
)
372 if(!a_strcmp(title
, plot
->title
))
375 /* no plot found -> create one */
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 */
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
;
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
])
407 plot
->current_max
= MAX(plot
->values
[plot
->max_index
], plot
->max_value
);
410 for(i
= 0; i
< d
->size
; i
++)
411 plot
->lines
[i
] = round(plot
->values
[i
] * d
->box_height
/ plot
->current_max
);
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
);
421 plot
->lines
[plot
->index
] = d
->box_height
;
424 widget_invalidate_bywidget(*widget
);
430 * \param L The Lua VM state.
431 * \param token The key token.
432 * \return The number of elements pushed on stack.
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
;
442 case A_TK_PLOT_PROPERTIES_SET
:
443 lua_pushcfunction(L
, luaA_graph_plot_properties_set
);
445 case A_TK_PLOT_DATA_ADD
:
446 lua_pushcfunction(L
, luaA_graph_plot_data_add
);
449 lua_pushnumber(L
, d
->height
);
452 lua_pushnumber(L
, d
->width
);
454 case A_TK_BORDER_COLOR
:
455 luaA_pushcolor(L
, &d
->border_color
);
458 luaA_pushcolor(L
, &d
->bg
);
464 lua_pushliteral(L
, "left");
467 lua_pushliteral(L
, "right");
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.
486 luaA_graph_newindex(lua_State
*L
, awesome_token_t token
)
489 widget_t
**widget
= luaA_checkudata(L
, 1, "widget");
490 graph_data_t
*d
= (*widget
)->data
;
500 d
->height
= luaL_checknumber(L
, 3);
503 width
= luaL_checknumber(L
, 3);
504 if(width
!= d
->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
);
515 plot
->current_max
= 0;
523 if((buf
= luaL_checklstring(L
, 3, &len
)))
525 if(xcolor_init_reply(globalconf
.connection
,
526 xcolor_init_unchecked(globalconf
.connection
,
528 globalconf
.default_screen
,
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
,
541 globalconf
.default_screen
,
543 d
->border_color
= color
;
549 buf
= luaL_checklstring(L
, 3, &len
);
550 switch((pos
= position_fromstr(buf
, len
)))
564 widget_invalidate_bywidget(*widget
);
569 /** Destroy definitively a graph widget.
570 * \param widget Who slay.
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
);
583 /** Create a brand new graph.
584 * \param align The widget alignment.
585 * \return A graph widget.
588 graph_new(alignment_t align
)
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
;
601 d
= w
->data
= p_new(graph_data_t
, 1);
605 d
->size
= d
->width
- 2;
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
;
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