stop raising like dumb
[awesome.git] / widgets / graph.c
blob44b08f5ce1939da49171049dd5e328cbbd96bf1d
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 "common/draw.h"
24 #include "widget.h"
25 #include "screen.h"
26 #include "common/util.h"
28 extern AwesomeConf globalconf;
30 typedef struct
32 /* general layout */
33 float *max; /** Represents a full graph */
34 int width; /** Width of the widget */
35 float height; /** Height of graph (0-1; 1 = height of statusbar) */
36 int box_height; /** Height of the innerbox in pixels */
37 int padding_left; /** Left padding */
38 int size; /** Size of lines-array (also innerbox-lenght) */
39 XColor bg; /** Background color */
40 XColor bordercolor; /** Border color */
42 /* markers... */
43 int index; /** Index of current (new) value */
44 int *max_index; /** Index of the actual maximum value */
45 float *current_max; /** Pointer to current maximum value itself */
47 /* all data is stored here */
48 int data_items; /** Number of data-input items */
49 int **lines; /** Keeps the calculated values (line-length); */
50 float **values; /** Actual values */
52 /* additional data + a pointer to **lines accordingly */
53 int **fillbottom; /** Datatypes holder (data equal to **lines) */
54 int fillbottom_total; /** Total of them */
55 XColor *fillbottom_color; /** Color of them */
56 XColor **fillbottom_pcolor_center; /** Color at middle of graph */
57 XColor **fillbottom_pcolor_end; /** Color at end of graph */
58 int **filltop; /** Datatypes holder */
59 int filltop_total; /** Total of them */
60 XColor *filltop_color; /** Color of them */
61 XColor **filltop_pcolor_center; /** Color at center of graph */
62 XColor **filltop_pcolor_end; /** Color at end of graph */
63 int **drawline; /** Datatypes holder */
64 int drawline_total; /** Total of them */
65 XColor *drawline_color; /** Color of them */
66 XColor **drawline_pcolor_center; /** Color at middle of graph */
67 XColor **drawline_pcolor_end; /** Color at end of graph */
69 int *draw_from; /** Preparation/tmp array for draw_graph(); */
70 int *draw_to; /** Preparation/tmp array for draw_graph(); */
72 } Data;
74 static int
75 graph_draw(Widget *widget, DrawCtx *ctx, int offset,
76 int used __attribute__ ((unused)))
78 int margin_top, left_offset;
79 int z, y, x, tmp;
80 Data *d = widget->data;
81 Area rectangle;
83 if(d->width < 1 || !d->data_items)
84 return 0;
86 if(!widget->user_supplied_x)
87 widget->area.x = widget_calculate_offset(widget->statusbar->width,
88 d->width,
89 offset,
90 widget->alignment);
91 if(!widget->user_supplied_y)
92 widget->area.y = 0;
94 margin_top = (int) (widget->statusbar->height * (1 - d->height)) / 2 + 0.5 + widget->area.y;
95 left_offset = widget->area.x + d->padding_left;
97 if(!(d->box_height))
98 d->box_height = (int) (widget->statusbar->height * d->height + 0.5) - 2;
100 rectangle.x = left_offset;
101 rectangle.y = margin_top;
102 rectangle.width = d->size + 2;
103 rectangle.height = d->box_height + 2;
104 draw_rectangle(ctx, rectangle, False, d->bordercolor);
106 rectangle.x++;
107 rectangle.y++;
108 rectangle.width -= 2;
109 rectangle.height -= 2;
110 draw_rectangle(ctx, rectangle, True, d->bg);
112 draw_graph_setup(ctx); /* setup some drawing options */
114 /* draw style = top */
115 for(z = 0; z < d->filltop_total; z++)
117 for(y = 0; y < d->size; y++)
119 for(tmp = 0, x = 0; x < d->filltop_total; x++) /* find largest smaller value */
121 if (x == z)
122 continue;
124 if(d->filltop[x][y] > tmp && d->filltop[x][y] < d->filltop[z][y])
125 tmp = d->filltop[x][y];
127 d->draw_from[y] = d->box_height - tmp;
128 d->draw_to[y] = d->box_height - d->filltop[z][y];
130 draw_graph(ctx,
131 left_offset + 2, margin_top + d->box_height + 1,
132 d->size, d->draw_from, d->draw_to, d->index,
133 d->filltop_color[z], d->filltop_pcolor_center[z], d->filltop_pcolor_end[z]);
136 /* draw style = bottom */
137 for(z = 0; z < d->fillbottom_total; z++)
139 for(y = 0; y < d->size; y++)
141 for(tmp = 0, x = 0; x < d->fillbottom_total; x++) /* find largest smaller value */
143 if (x == z)
144 continue;
146 if(d->fillbottom[x][y] > tmp && d->fillbottom[x][y] < d->fillbottom[z][y])
147 tmp = d->fillbottom[x][y];
149 d->draw_from[y] = tmp;
152 draw_graph(ctx,
153 left_offset + 2, margin_top + d->box_height + 1,
154 d->size, d->draw_from, d->fillbottom[z], d->index,
155 d->fillbottom_color[z], d->fillbottom_pcolor_center[z], d->fillbottom_pcolor_end[z]);
158 /* draw style = line */
159 for(z = 0; z < d->drawline_total; z++)
161 draw_graph_line(ctx,
162 left_offset + 2, margin_top + d->box_height + 1,
163 d->size, d->drawline[z], d->index,
164 d->drawline_color[z], d->drawline_pcolor_center[z], d->drawline_pcolor_end[z]);
167 widget->area.width = d->width;
168 widget->area.height = widget->statusbar->height;
169 return widget->area.width;
172 static void
173 graph_tell(Widget *widget, char *command)
175 Data *d = widget->data;
176 int i, z;
177 float *value;
178 char *tok;
180 if(!command || d->width < 1 || !(d->data_items > 0))
181 return;
183 value = p_new(float, d->data_items);
185 for (i = 0, tok = strtok(command, ","); tok && i < d->data_items; tok = strtok(NULL, ","), i++)
186 value[i] = MAX(atof(tok), 0);
188 if(++d->index >= d->size) /* cycle inside the arrays (all-in-one) */
189 d->index = 0;
191 /* add according values and to-draw-line-lenghts to the according data_items */
192 for(z = 0; z < d->data_items; z++)
194 if(d->values[z]) /* scale option is true */
196 d->values[z][d->index] = value[z];
198 if(value[z] > d->current_max[z]) /* a new maximum value found */
200 d->max_index[z] = d->index;
201 d->current_max[z] = value[z];
203 /* recalculate */
204 for (i = 0; i < d->size; i++)
205 d->lines[z][i] = (int) (d->values[z][i] * (d->box_height) / d->current_max[z] + 0.5);
207 else if(d->max_index[z] == d->index) /* old max_index reached, re-check/generate */
209 /* find the new max */
210 for (i = 0; i < d->size; i++)
211 if (d->values[z][i] > d->values[z][d->max_index[z]])
212 d->max_index[z] = i;
214 d->current_max[z] = MAX(d->values[z][d->max_index[z]], d->max[z]);
216 /* recalculate */
217 for (i = 0; i < d->size; i++)
218 d->lines[z][i] = (int) (d->values[z][i] * d->box_height / d->current_max[z] + 0.5);
220 else
221 d->lines[z][d->index] = (int) (value[z] * d->box_height / d->current_max[z] + 0.5);
224 else /* scale option is false - limit to d->box_height */
226 if (value[z] < d->current_max[z])
227 d->lines[z][d->index] = (int) (value[z] * d->box_height / d->current_max[z] + 0.5);
228 else
229 d->lines[z][d->index] = d->box_height;
234 Widget *
235 graph_new(Statusbar *statusbar, cfg_t *config)
237 Widget *w;
238 Data *d;
239 cfg_t *cfg;
240 char *color;
241 int phys_screen = get_phys_screen(statusbar->screen);
242 int i;
243 char *type;
244 XColor tmp_color = { 0, 0, 0, 0, 0, 0 };
245 XColor *ptmp_color_center;
246 XColor *ptmp_color_end;
248 w = p_new(Widget, 1);
249 widget_common_new(w, statusbar, config);
251 w->draw = graph_draw;
252 w->tell = graph_tell;
253 d = w->data = p_new(Data, 1);
255 d->width = cfg_getint(config, "width");
256 d->height = cfg_getfloat(config, "height");
257 d->padding_left = cfg_getint(config, "padding_left");
258 d->size = d->width - d->padding_left - 2;
260 if(d->size < 1)
262 warn("graph widget needs: (width - padding_left) >= 3\n");
263 return w;
266 if(!(d->data_items = cfg_size(config, "data")))
268 warn("graph widget needs at least one data section\n");
269 return w;
272 d->draw_from = p_new(int, d->size);
273 d->draw_to = p_new(int, d->size);
275 d->fillbottom = p_new(int *, d->size);
276 d->filltop = p_new(int *, d->size);
277 d->drawline = p_new(int *, d->size);
279 d->values = p_new(float *, d->data_items);
280 d->lines = p_new(int *, d->data_items);
282 d->filltop_color = p_new(XColor, d->data_items);
283 d->filltop_pcolor_center = p_new(XColor *, d->data_items);
284 d->filltop_pcolor_end = p_new(XColor *, d->data_items);
285 d->fillbottom_color = p_new(XColor, d->data_items);
286 d->fillbottom_pcolor_center = p_new(XColor *, d->data_items);
287 d->fillbottom_pcolor_end = p_new(XColor *, d->data_items);
288 d->drawline_color = p_new(XColor, d->data_items);
289 d->drawline_pcolor_center = p_new(XColor *, d->data_items);
290 d->drawline_pcolor_end = p_new(XColor *, d->data_items);
292 d->max_index = p_new(int, d->data_items);
294 d->current_max = p_new(float, d->data_items);
295 d->max = p_new(float, d->data_items);
297 for(i = 0; i < d->data_items; i++)
299 ptmp_color_center = ptmp_color_end = NULL;
301 cfg = cfg_getnsec(config, "data", i);
303 if((color = cfg_getstr(cfg, "fg")))
304 tmp_color = draw_color_new(globalconf.display, phys_screen, color);
305 else
306 tmp_color = globalconf.screens[statusbar->screen].colors_normal[ColFG];
308 if((color = cfg_getstr(cfg, "fg_center")))
310 ptmp_color_center = p_new(XColor, 1);
311 *ptmp_color_center = draw_color_new(globalconf.display, phys_screen, color);
314 if((color = cfg_getstr(cfg, "fg_end")))
316 ptmp_color_end = p_new(XColor, 1);
317 *ptmp_color_end = draw_color_new(globalconf.display, phys_screen, color);
320 if (cfg_getbool(cfg, "scale"))
321 d->values[i] = p_new(float, d->size); /* not null -> scale = true */
323 /* prevent: division by zero */
324 d->current_max[i] = d->max[i] = cfg_getfloat(cfg, "max");
325 if(!(d->max[i] > 0))
327 warn("all graph widget needs a 'max' value greater than zero\n");
328 d->data_items = 0;
329 return w;
332 d->lines[i] = p_new(int, d->size);
334 /* filter each style-typ into it's own array (for easy looping later)*/
336 if ((type = cfg_getstr(cfg, "style")))
338 if(!a_strncmp(type, "bottom", sizeof("bottom")))
340 d->fillbottom[d->fillbottom_total] = d->lines[i];
341 d->fillbottom_color[d->fillbottom_total] = tmp_color;
342 d->fillbottom_pcolor_center[d->fillbottom_total] = ptmp_color_center;
343 d->fillbottom_pcolor_end[d->fillbottom_total] = ptmp_color_end;
344 d->fillbottom_total++;
346 else if (!a_strncmp(type, "top", sizeof("top")))
348 d->filltop[d->filltop_total] = d->lines[i];
349 d->filltop_color[d->filltop_total] = tmp_color;
350 d->filltop_pcolor_center[d->fillbottom_total] = ptmp_color_center;
351 d->filltop_pcolor_end[d->fillbottom_total] = ptmp_color_end;
352 d->filltop_total++;
354 else if (!a_strncmp(type, "line", sizeof("line")))
356 d->drawline[d->drawline_total] = d->lines[i];
357 d->drawline_color[d->drawline_total] = tmp_color;
358 d->drawline_pcolor_center[d->fillbottom_total] = ptmp_color_center;
359 d->drawline_pcolor_end[d->fillbottom_total] = ptmp_color_end;
360 d->drawline_total++;
365 if((color = cfg_getstr(config, "bg")))
366 d->bg = draw_color_new(globalconf.display, phys_screen, color);
367 else
368 d->bg = globalconf.screens[statusbar->screen].colors_normal[ColBG];
370 if((color = cfg_getstr(config, "bordercolor")))
371 d->bordercolor = draw_color_new(globalconf.display, phys_screen, color);
372 else
373 d->bordercolor = tmp_color;
375 return w;
377 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80