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"
26 #include "common/util.h"
28 extern AwesomeConf globalconf
;
34 char **data_title
; /** data_title of the data sections */
35 float *max
; /** Represents a full graph */
36 int width
; /** Width of the widget */
37 float height
; /** Height of graph (0-1; 1 = height of statusbar) */
38 int box_height
; /** Height of the innerbox in pixels */
39 int padding_left
; /** Left padding */
40 int size
; /** Size of lines-array (also innerbox-lenght) */
41 XColor bg
; /** Background color */
42 XColor bordercolor
; /** Border color */
45 int *index
; /** Index of current (new) value */
46 int *max_index
; /** Index of the actual maximum value */
47 float *current_max
; /** Pointer to current maximum value itself */
49 /* all data is stored here */
50 int data_items
; /** Number of data-input items */
51 int **lines
; /** Keeps the calculated values (line-length); */
52 float **values
; /** Actual values */
54 /* additional data + a pointer to **lines accordingly */
55 int **fillbottom
; /** Data array pointer (like *lines) */
56 int **fillbottom_index
; /** Points to some index[i] */
57 int fillbottom_total
; /** Total of them */
58 XColor
*fillbottom_color
; /** Color of them */
59 XColor
**fillbottom_pcolor_center
; /** Color at middle of graph */
60 XColor
**fillbottom_pcolor_end
; /** Color at end of graph */
61 int **filltop
; /** Data array pointer (like *lines) */
62 int **filltop_index
; /** Points to some index[i] */
63 int filltop_total
; /** Total of them */
64 XColor
*filltop_color
; /** Color of them */
65 XColor
**filltop_pcolor_center
; /** Color at center of graph */
66 XColor
**filltop_pcolor_end
; /** Color at end of graph */
67 int **drawline
; /** Data array pointer (like *lines) */
68 int **drawline_index
; /** Points to some index[i] */
69 int drawline_total
; /** Total of them */
70 XColor
*drawline_color
; /** Color of them */
71 XColor
**drawline_pcolor_center
; /** Color at middle of graph */
72 XColor
**drawline_pcolor_end
; /** Color at end of graph */
74 int *draw_from
; /** Preparation/tmp array for draw_graph(); */
75 int *draw_to
; /** Preparation/tmp array for draw_graph(); */
80 graph_draw(Widget
*widget
, DrawCtx
*ctx
, int offset
,
81 int used
__attribute__ ((unused
)))
83 int margin_top
, left_offset
;
85 Data
*d
= widget
->data
;
88 if(d
->width
< 1 || !d
->data_items
)
91 if(!widget
->user_supplied_x
)
92 widget
->area
.x
= widget_calculate_offset(widget
->statusbar
->width
,
96 if(!widget
->user_supplied_y
)
99 margin_top
= (int) (widget
->statusbar
->height
* (1 - d
->height
)) / 2 + 0.5 + widget
->area
.y
;
100 left_offset
= widget
->area
.x
+ d
->padding_left
;
103 d
->box_height
= (int) (widget
->statusbar
->height
* d
->height
+ 0.5) - 2;
105 rectangle
.x
= left_offset
;
106 rectangle
.y
= margin_top
;
107 rectangle
.width
= d
->size
+ 2;
108 rectangle
.height
= d
->box_height
+ 2;
109 draw_rectangle(ctx
, rectangle
, False
, d
->bordercolor
);
113 rectangle
.width
-= 2;
114 rectangle
.height
-= 2;
115 draw_rectangle(ctx
, rectangle
, True
, d
->bg
);
117 draw_graph_setup(ctx
); /* setup some drawing options */
119 /* draw style = top */
120 for(z
= 0; z
< d
->filltop_total
; z
++)
122 for(y
= 0; y
< d
->size
; y
++)
124 for(tmp
= 0, x
= 0; x
< d
->filltop_total
; x
++) /* find largest smaller value */
129 if(d
->filltop
[x
][y
] > tmp
&& d
->filltop
[x
][y
] < d
->filltop
[z
][y
])
130 tmp
= d
->filltop
[x
][y
];
132 d
->draw_from
[y
] = d
->box_height
- tmp
;
133 d
->draw_to
[y
] = d
->box_height
- d
->filltop
[z
][y
];
136 left_offset
+ 2, margin_top
+ d
->box_height
+ 1,
137 d
->size
, d
->draw_from
, d
->draw_to
, *(d
->filltop_index
[z
]),
138 d
->filltop_color
[z
], d
->filltop_pcolor_center
[z
], d
->filltop_pcolor_end
[z
]);
141 /* draw style = bottom */
142 for(z
= 0; z
< d
->fillbottom_total
; z
++)
144 for(y
= 0; y
< d
->size
; y
++)
146 for(tmp
= 0, x
= 0; x
< d
->fillbottom_total
; x
++) /* find largest smaller value */
151 if(d
->fillbottom
[x
][y
] > tmp
&& d
->fillbottom
[x
][y
] < d
->fillbottom
[z
][y
])
152 tmp
= d
->fillbottom
[x
][y
];
154 d
->draw_from
[y
] = tmp
;
158 left_offset
+ 2, margin_top
+ d
->box_height
+ 1,
159 d
->size
, d
->draw_from
, d
->fillbottom
[z
], *(d
->fillbottom_index
[z
]),
160 d
->fillbottom_color
[z
], d
->fillbottom_pcolor_center
[z
], d
->fillbottom_pcolor_end
[z
]);
163 /* draw style = line */
164 for(z
= 0; z
< d
->drawline_total
; z
++)
167 left_offset
+ 2, margin_top
+ d
->box_height
+ 1,
168 d
->size
, d
->drawline
[z
], *(d
->drawline_index
[z
]),
169 d
->drawline_color
[z
], d
->drawline_pcolor_center
[z
], d
->drawline_pcolor_end
[z
]);
172 widget
->area
.width
= d
->width
;
173 widget
->area
.height
= widget
->statusbar
->height
;
174 return widget
->area
.width
;
177 static widget_tell_status_t
178 graph_tell(Widget
*widget
, char *property
, char *command
)
180 Data
*d
= widget
->data
;
183 char *title
, *setting
;
185 if(!property
|| !command
|| d
->width
< 1 || !(d
->data_items
> 0))
188 if(!a_strcmp(property
, "data"))
190 title
= strtok(command
, " ");
191 setting
= strtok(NULL
, " ");
193 for(i
= 0; i
< d
->data_items
; i
++)
195 if(!a_strcmp(title
, d
->data_title
[i
]))
197 value
= MAX(atof(setting
), 0);
199 if(++d
->index
[i
] >= d
->size
) /* cycle inside the array */
202 if(d
->values
[i
]) /* scale option is true */
204 d
->values
[i
][d
->index
[i
]] = value
;
206 if(value
> d
->current_max
[i
]) /* a new maximum value found */
208 d
->max_index
[i
] = d
->index
[i
];
209 d
->current_max
[i
] = value
;
212 for (u
= 0; u
< d
->size
; u
++)
213 d
->lines
[i
][u
] = (int) (d
->values
[i
][u
] * (d
->box_height
) / d
->current_max
[i
] + 0.5);
215 /* old max_index reached + current_max > normal, re-check/generate */
216 else if(d
->max_index
[i
] == d
->index
[i
] && d
->current_max
[i
] > d
->max
[i
])
218 /* find the new max */
219 for (u
= 0; u
< d
->size
; u
++)
220 if (d
->values
[i
][u
] > d
->values
[i
][d
->max_index
[i
]])
223 d
->current_max
[i
] = MAX(d
->values
[i
][d
->max_index
[i
]], d
->max
[i
]);
226 for (u
= 0; u
< d
->size
; u
++)
227 d
->lines
[i
][u
] = (int) (d
->values
[i
][u
] * d
->box_height
/ d
->current_max
[i
] + 0.5);
230 d
->lines
[i
][d
->index
[i
]] = (int) (value
* d
->box_height
/ d
->current_max
[i
] + 0.5);
233 else /* scale option is false - limit to d->box_height */
235 if (value
< d
->current_max
[i
])
236 d
->lines
[i
][d
->index
[i
]] = (int) (value
* d
->box_height
/ d
->current_max
[i
] + 0.5);
238 d
->lines
[i
][d
->index
[i
]] = d
->box_height
;
240 return WIDGET_NOERROR
;
243 warn("no such data-section title: %s\n", title
);
246 else if(!a_strcmp(property
, "width"))
247 d
->width
= atoi(command
);
248 else if(!a_strcmp(property
, "height"))
249 d
->height
= atof(command
);
250 else if(!a_strcmp(property
, "padding_left"))
251 d
->padding_left
= atoi(command
);
252 else if(!a_strcmp(property
, "bg"))
253 draw_color_new(globalconf
.display
, get_phys_screen(widget
->statusbar
->screen
),
255 else if(!a_strcmp(property
, "bordercolor"))
256 draw_color_new(globalconf
.display
, get_phys_screen(widget
->statusbar
->screen
),
257 command
, &d
->bordercolor
);
261 return WIDGET_NOERROR
;;
265 graph_new(Statusbar
*statusbar
, cfg_t
*config
)
271 int phys_screen
= get_phys_screen(statusbar
->screen
);
274 XColor tmp_color
= { 0, 0, 0, 0, 0, 0 };
275 XColor
*ptmp_color_center
;
276 XColor
*ptmp_color_end
;
278 w
= p_new(Widget
, 1);
279 widget_common_new(w
, statusbar
, config
);
281 w
->draw
= graph_draw
;
282 w
->tell
= graph_tell
;
283 w
->alignment
= draw_get_align(cfg_getstr(config
, "align"));
284 d
= w
->data
= p_new(Data
, 1);
286 d
->width
= cfg_getint(config
, "width");
287 d
->height
= cfg_getfloat(config
, "height");
288 d
->padding_left
= cfg_getint(config
, "padding_left");
289 d
->size
= d
->width
- d
->padding_left
- 2;
293 warn("graph widget needs: (width - padding_left) >= 3\n");
297 if(!(d
->data_items
= cfg_size(config
, "data")))
299 warn("graph widget needs at least one data section\n");
303 d
->draw_from
= p_new(int, d
->size
);
304 d
->draw_to
= p_new(int, d
->size
);
306 d
->fillbottom
= p_new(int *, d
->size
);
307 d
->fillbottom_index
= p_new(int *, d
->size
);
308 d
->filltop
= p_new(int *, d
->size
);
309 d
->filltop_index
= p_new(int *, d
->size
);
310 d
->drawline
= p_new(int *, d
->size
);
311 d
->drawline_index
= p_new(int *, d
->size
);
313 d
->data_title
= p_new(char *, d
->data_items
);
314 d
->values
= p_new(float *, d
->data_items
);
315 d
->lines
= p_new(int *, d
->data_items
);
317 d
->filltop_color
= p_new(XColor
, d
->data_items
);
318 d
->filltop_pcolor_center
= p_new(XColor
*, d
->data_items
);
319 d
->filltop_pcolor_end
= p_new(XColor
*, d
->data_items
);
320 d
->fillbottom_color
= p_new(XColor
, d
->data_items
);
321 d
->fillbottom_pcolor_center
= p_new(XColor
*, d
->data_items
);
322 d
->fillbottom_pcolor_end
= p_new(XColor
*, d
->data_items
);
323 d
->drawline_color
= p_new(XColor
, d
->data_items
);
324 d
->drawline_pcolor_center
= p_new(XColor
*, d
->data_items
);
325 d
->drawline_pcolor_end
= p_new(XColor
*, d
->data_items
);
327 d
->max_index
= p_new(int, d
->data_items
);
328 d
->index
= p_new(int, d
->data_items
);
330 d
->current_max
= p_new(float, d
->data_items
);
331 d
->max
= p_new(float, d
->data_items
);
333 for(i
= 0; i
< d
->data_items
; i
++)
335 ptmp_color_center
= ptmp_color_end
= NULL
;
337 cfg
= cfg_getnsec(config
, "data", i
);
339 d
->data_title
[i
] = a_strdup(cfg_title(cfg
));
341 if((color
= cfg_getstr(cfg
, "fg")))
342 draw_color_new(globalconf
.display
, phys_screen
, color
, &tmp_color
);
344 tmp_color
= globalconf
.screens
[statusbar
->screen
].colors_normal
[ColFG
];
346 if((color
= cfg_getstr(cfg
, "fg_center")))
348 ptmp_color_center
= p_new(XColor
, 1);
349 draw_color_new(globalconf
.display
, phys_screen
, color
, ptmp_color_center
);
352 if((color
= cfg_getstr(cfg
, "fg_end")))
354 ptmp_color_end
= p_new(XColor
, 1);
355 draw_color_new(globalconf
.display
, phys_screen
, color
, ptmp_color_end
);
358 if (cfg_getbool(cfg
, "scale"))
359 d
->values
[i
] = p_new(float, d
->size
); /* not null -> scale = true */
361 /* prevent: division by zero */
362 d
->current_max
[i
] = d
->max
[i
] = cfg_getfloat(cfg
, "max");
365 warn("all graph widget needs a 'max' value greater than zero\n");
370 d
->lines
[i
] = p_new(int, d
->size
);
372 /* filter each style-typ into it's own array (for easy looping later)*/
374 if ((type
= cfg_getstr(cfg
, "style")))
376 if(!a_strncmp(type
, "bottom", sizeof("bottom")))
378 d
->fillbottom
[d
->fillbottom_total
] = d
->lines
[i
];
379 d
->fillbottom_index
[d
->fillbottom_total
] = &d
->index
[i
];
380 d
->fillbottom_color
[d
->fillbottom_total
] = tmp_color
;
381 d
->fillbottom_pcolor_center
[d
->fillbottom_total
] = ptmp_color_center
;
382 d
->fillbottom_pcolor_end
[d
->fillbottom_total
] = ptmp_color_end
;
383 d
->fillbottom_total
++;
385 else if (!a_strncmp(type
, "top", sizeof("top")))
387 d
->filltop
[d
->filltop_total
] = d
->lines
[i
];
388 d
->filltop_index
[d
->filltop_total
] = &d
->index
[i
];
389 d
->filltop_color
[d
->filltop_total
] = tmp_color
;
390 d
->filltop_pcolor_center
[d
->filltop_total
] = ptmp_color_center
;
391 d
->filltop_pcolor_end
[d
->filltop_total
] = ptmp_color_end
;
394 else if (!a_strncmp(type
, "line", sizeof("line")))
396 d
->drawline
[d
->drawline_total
] = d
->lines
[i
];
397 d
->drawline_index
[d
->drawline_total
] = &d
->index
[i
];
398 d
->drawline_color
[d
->drawline_total
] = tmp_color
;
399 d
->drawline_pcolor_center
[d
->drawline_total
] = ptmp_color_center
;
400 d
->drawline_pcolor_end
[d
->drawline_total
] = ptmp_color_end
;
406 if((color
= cfg_getstr(config
, "bg")))
407 draw_color_new(globalconf
.display
, phys_screen
, color
, &d
->bg
);
409 d
->bg
= globalconf
.screens
[statusbar
->screen
].colors_normal
[ColBG
];
411 if((color
= cfg_getstr(config
, "bordercolor")))
412 draw_color_new(globalconf
.display
, phys_screen
, color
, &d
->bordercolor
);
414 d
->bordercolor
= tmp_color
;
418 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80