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.
25 #include "common/util.h"
26 #include "common/draw.h"
27 #include "common/configopts.h"
29 extern AwesomeConf globalconf
;
35 char **data_title
; /** data_title of the data sections */
36 float *max
; /** Represents a full graph */
37 int width
; /** Width of the widget */
38 float height
; /** Height of graph (0-1; 1 = height of statusbar) */
39 int box_height
; /** Height of the innerbox in pixels */
40 int padding_left
; /** Left padding */
41 int size
; /** Size of lines-array (also innerbox-lenght) */
42 XColor bg
; /** Background color */
43 XColor bordercolor
; /** Border color */
44 Position grow
; /** grow: Left or Right */
47 int *index
; /** Index of current (new) value */
48 int *max_index
; /** Index of the actual maximum value */
49 float *current_max
; /** Pointer to current maximum value itself */
51 /* all data is stored here */
52 int data_items
; /** Number of data-input items */
53 int **lines
; /** Keeps the calculated values (line-length); */
54 float **values
; /** Actual values */
56 /* additional data + a pointer to **lines accordingly */
57 int **fillbottom
; /** Data array pointer (like *lines) */
58 int **fillbottom_index
; /** Points to some index[i] */
59 int fillbottom_total
; /** Total of them */
60 Bool
*fillbottom_vertical_grad
; /** Create a vertical color gradient */
61 XColor
*fillbottom_color
; /** Color of them */
62 XColor
**fillbottom_pcolor_center
; /** Color at middle of graph */
63 XColor
**fillbottom_pcolor_end
; /** Color at end of graph */
64 int **filltop
; /** Data array pointer (like *lines) */
65 int **filltop_index
; /** Points to some index[i] */
66 int filltop_total
; /** Total of them */
67 Bool
*filltop_vertical_grad
; /** Create a vertical color gradient */
68 XColor
*filltop_color
; /** Color of them */
69 XColor
**filltop_pcolor_center
; /** Color at center of graph */
70 XColor
**filltop_pcolor_end
; /** Color at end of graph */
71 int **drawline
; /** Data array pointer (like *lines) */
72 int **drawline_index
; /** Points to some index[i] */
73 int drawline_total
; /** Total of them */
74 Bool
*drawline_vertical_grad
; /** Create a vertical color gradient */
75 XColor
*drawline_color
; /** Color of them */
76 XColor
**drawline_pcolor_center
; /** Color at middle of graph */
77 XColor
**drawline_pcolor_end
; /** Color at end of graph */
79 int *draw_from
; /** Preparation/tmp array for draw_graph(); */
80 int *draw_to
; /** Preparation/tmp array for draw_graph(); */
85 graph_draw(Widget
*widget
, DrawCtx
*ctx
, int offset
,
86 int used
__attribute__ ((unused
)))
88 int margin_top
, left_offset
;
89 int z
, y
, x
, tmp
, cur_index
, test_index
;
90 Data
*d
= widget
->data
;
91 area_t rectangle
, pattern_area
;
96 if(!widget
->user_supplied_x
)
97 widget
->area
.x
= widget_calculate_offset(widget
->statusbar
->width
,
101 if(!widget
->user_supplied_y
)
104 left_offset
= widget
->area
.x
+ d
->padding_left
;
106 /* box = the graph inside the rectangle */
108 d
->box_height
= (int) (widget
->statusbar
->height
* d
->height
+ 0.5) - 2;
110 margin_top
= (int)((widget
->statusbar
->height
- (d
->box_height
+ 2)) / 2 + 0.5) + widget
->area
.y
;
112 rectangle
.x
= left_offset
;
113 rectangle
.y
= margin_top
;
114 rectangle
.width
= d
->size
+ 2;
115 rectangle
.height
= d
->box_height
+ 2;
116 draw_rectangle(ctx
, rectangle
, 1.0, False
, d
->bordercolor
);
120 rectangle
.width
= d
->size
;
121 rectangle
.height
-= 2;
122 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->bg
);
124 /* for graph drawing */
125 rectangle
.x
= left_offset
+ 2;
126 rectangle
.y
= margin_top
+ d
->box_height
+ 1; /* bottom left corner as starting point */
127 rectangle
.width
= d
->size
; /* rectangle.height is not used */
129 draw_graph_setup(ctx
); /* setup some drawing options */
133 /* all these filltop's have the same setting */
134 pattern_area
.x
= rectangle
.x
;
135 pattern_area
.y
= rectangle
.y
- rectangle
.height
;
136 if(d
->filltop_vertical_grad
[0])
138 pattern_area
.width
= 0;
139 pattern_area
.height
= rectangle
.height
;
143 pattern_area
.width
= rectangle
.width
;
144 pattern_area
.height
= 0;
147 /* draw style = top */
148 for(z
= 0; z
< d
->filltop_total
; z
++)
150 cur_index
= *(d
->filltop_index
[z
]);
152 for(y
= 0; y
< d
->size
; y
++)
154 /* draw this filltop data thing [z]. But figure out the part
155 * what shall be visible. Therefore find the next smaller value
156 * on this index (draw_from) - might be 0 (then draw from start) */
157 for(tmp
= 0, x
= 0; x
< d
->filltop_total
; x
++)
159 if (x
== z
) /* no need to compare with itself */
162 /* current index's can be different (widget_tell might shift
163 * some with a different frequenzy), so calculate
164 * offset and compare accordingly finally */
165 test_index
= cur_index
+ (*(d
->filltop_index
[x
]) - *(d
->filltop_index
[z
]));
168 test_index
= d
->size
+ test_index
; /* text_index is minus, since < 0 */
169 else if(test_index
>= d
->size
)
170 test_index
-= d
->size
;
172 /* ... (test_)index to test for a smaller value found. */
174 /* if such a smaller value (to not overdraw!) is there, store into 'tmp' */
175 if(d
->filltop
[x
][test_index
] > tmp
&& d
->filltop
[x
][test_index
] < d
->filltop
[z
][cur_index
])
176 tmp
= d
->filltop
[x
][test_index
];
179 /* reverse values (because drawing from top) */
180 d
->draw_from
[cur_index
] = d
->box_height
- tmp
; /* i.e. no smaller value -> from top of box */
181 d
->draw_to
[cur_index
] = d
->box_height
- d
->filltop
[z
][cur_index
]; /* i.e. on full graph -> 0 = bottom */
183 if (--cur_index
< 0) /* next index to compare to other values */
184 cur_index
= d
->size
- 1;
186 draw_graph(ctx
, rectangle
, d
->draw_from
, d
->draw_to
, *(d
->filltop_index
[z
]), d
->grow
, pattern_area
,
187 &(d
->filltop_color
[z
]), d
->filltop_pcolor_center
[z
], d
->filltop_pcolor_end
[z
]);
191 pattern_area
.x
= rectangle
.x
;
192 pattern_area
.y
= rectangle
.y
;
194 if(d
->fillbottom_total
)
196 /* all these fillbottom's have the same setting */
197 if(d
->fillbottom_vertical_grad
[0])
199 pattern_area
.width
= 0;
200 pattern_area
.height
= -rectangle
.height
;
204 pattern_area
.width
= rectangle
.width
;
205 pattern_area
.height
= 0;
208 /* draw style = bottom */
209 for(z
= 0; z
< d
->fillbottom_total
; z
++)
211 cur_index
= *(d
->fillbottom_index
[z
]);
213 for(y
= 0; y
< d
->size
; y
++)
215 for(tmp
= 0, x
= 0; x
< d
->fillbottom_total
; x
++)
220 test_index
= cur_index
+ (*(d
->fillbottom_index
[x
]) - *(d
->fillbottom_index
[z
]));
223 test_index
= d
->size
+ test_index
;
224 else if(test_index
>= d
->size
)
225 test_index
-= d
->size
;
227 if(d
->fillbottom
[x
][test_index
] > tmp
&& d
->fillbottom
[x
][test_index
] < d
->fillbottom
[z
][cur_index
])
228 tmp
= d
->fillbottom
[x
][test_index
];
230 d
->draw_from
[cur_index
] = tmp
;
232 cur_index
= d
->size
- 1;
234 draw_graph(ctx
, rectangle
, d
->draw_from
, d
->fillbottom
[z
], *(d
->fillbottom_index
[z
]), d
->grow
,
235 pattern_area
, &(d
->fillbottom_color
[z
]), d
->fillbottom_pcolor_center
[z
], d
->fillbottom_pcolor_end
[z
]);
239 if(d
->drawline_total
)
241 /* all these drawline's have the same setting */
242 if(d
->drawline_vertical_grad
[0])
244 pattern_area
.width
= 0;
245 pattern_area
.height
= -rectangle
.height
;
249 pattern_area
.width
= rectangle
.width
;
250 pattern_area
.height
= 0;
253 /* draw style = line */
254 for(z
= 0; z
< d
->drawline_total
; z
++)
256 draw_graph_line(ctx
, rectangle
, d
->drawline
[z
], *(d
->drawline_index
[z
]), d
->grow
, pattern_area
,
257 &(d
->drawline_color
[z
]), d
->drawline_pcolor_center
[z
], d
->drawline_pcolor_end
[z
]);
261 widget
->area
.width
= d
->width
;
262 widget
->area
.height
= widget
->statusbar
->height
;
263 return widget
->area
.width
;
266 static widget_tell_status_t
267 graph_tell(Widget
*widget
, char *property
, char *command
)
269 Data
*d
= widget
->data
;
272 char *title
, *setting
;
275 return WIDGET_ERROR_CUSTOM
; /* error already printed on _new */
278 return WIDGET_ERROR_NOVALUE
;
280 if(!a_strcmp(property
, "data"))
282 title
= strtok(command
, " ");
283 if(!(setting
= strtok(NULL
, " ")))
284 return WIDGET_ERROR_NOVALUE
;
286 for(i
= 0; i
< d
->data_items
; i
++)
288 if(!a_strcmp(title
, d
->data_title
[i
]))
290 value
= MAX(atof(setting
), 0);
292 if(++d
->index
[i
] >= d
->size
) /* cycle inside the array */
295 if(d
->values
[i
]) /* scale option is true */
297 d
->values
[i
][d
->index
[i
]] = value
;
299 if(value
> d
->current_max
[i
]) /* a new maximum value found */
301 d
->max_index
[i
] = d
->index
[i
];
302 d
->current_max
[i
] = value
;
305 for (u
= 0; u
< d
->size
; u
++)
306 d
->lines
[i
][u
] = (int) (d
->values
[i
][u
] * (d
->box_height
) / d
->current_max
[i
] + 0.5);
308 /* old max_index reached + current_max > normal, re-check/generate */
309 else if(d
->max_index
[i
] == d
->index
[i
] && d
->current_max
[i
] > d
->max
[i
])
311 /* find the new max */
312 for (u
= 0; u
< d
->size
; u
++)
313 if (d
->values
[i
][u
] > d
->values
[i
][d
->max_index
[i
]])
316 d
->current_max
[i
] = MAX(d
->values
[i
][d
->max_index
[i
]], d
->max
[i
]);
319 for (u
= 0; u
< d
->size
; u
++)
320 d
->lines
[i
][u
] = (int) (d
->values
[i
][u
] * d
->box_height
/ d
->current_max
[i
] + 0.5);
323 d
->lines
[i
][d
->index
[i
]] = (int) (value
* d
->box_height
/ d
->current_max
[i
] + 0.5);
325 else /* scale option is false - limit to d->box_height */
327 if (value
< d
->current_max
[i
])
328 d
->lines
[i
][d
->index
[i
]] = (int) (value
* d
->box_height
/ d
->current_max
[i
] + 0.5);
330 d
->lines
[i
][d
->index
[i
]] = d
->box_height
;
332 return WIDGET_NOERROR
;
335 return WIDGET_ERROR_FORMAT_SECTION
;
337 else if(!a_strcmp(property
, "height"))
338 d
->height
= atof(command
);
339 else if(!a_strcmp(property
, "bg"))
341 if(!draw_color_new(globalconf
.display
,
342 widget
->statusbar
->phys_screen
,
344 return WIDGET_ERROR_FORMAT_COLOR
;
346 else if(!a_strcmp(property
, "bordercolor"))
348 if(!draw_color_new(globalconf
.display
,
349 widget
->statusbar
->phys_screen
,
350 command
, &d
->bordercolor
))
351 return WIDGET_ERROR_FORMAT_COLOR
;
353 else if(!a_strcmp(property
, "grow"))
354 switch((d
->grow
= position_get_from_str(command
)))
360 warn("error changing property %s of widget %s, must be 'left' or 'right'\n",
361 property
, widget
->name
);
362 return WIDGET_ERROR_CUSTOM
;
367 return WIDGET_NOERROR
;
371 graph_new(Statusbar
*statusbar
, cfg_t
*config
)
379 XColor tmp_color
= { 0, 0, 0, 0, 0, 0 };
380 XColor
*ptmp_color_center
;
381 XColor
*ptmp_color_end
;
383 w
= p_new(Widget
, 1);
384 widget_common_new(w
, statusbar
, config
);
386 w
->draw
= graph_draw
;
387 w
->tell
= graph_tell
;
388 w
->alignment
= cfg_getalignment(config
, "align");
389 d
= w
->data
= p_new(Data
, 1);
391 d
->width
= cfg_getint(config
, "width");
392 d
->height
= cfg_getfloat(config
, "height");
393 d
->padding_left
= cfg_getint(config
, "padding_left");
394 d
->size
= d
->width
- d
->padding_left
- 2;
398 warn("graph widget needs: (width - padding_left) >= 3\n");
402 if(!(d
->data_items
= cfg_size(config
, "data")))
404 warn("graph widget needs at least one data section\n");
408 d
->grow
= cfg_getposition(config
, "grow");
409 if(d
->grow
!= Left
&& d
->grow
!= Right
)
411 warn("graph widget: 'grow' argument must be 'left' or 'right'\n");
412 d
->data_items
= 0; /* disable widget drawing */
416 d
->draw_from
= p_new(int, d
->size
);
417 d
->draw_to
= p_new(int, d
->size
);
419 d
->fillbottom
= p_new(int *, d
->size
);
420 d
->fillbottom_index
= p_new(int *, d
->size
);
421 d
->filltop
= p_new(int *, d
->size
);
422 d
->filltop_index
= p_new(int *, d
->size
);
423 d
->drawline
= p_new(int *, d
->size
);
424 d
->drawline_index
= p_new(int *, d
->size
);
426 d
->data_title
= p_new(char *, d
->data_items
);
427 d
->values
= p_new(float *, d
->data_items
);
428 d
->lines
= p_new(int *, d
->data_items
);
430 d
->filltop_color
= p_new(XColor
, d
->data_items
);
431 d
->filltop_pcolor_center
= p_new(XColor
*, d
->data_items
);
432 d
->filltop_pcolor_end
= p_new(XColor
*, d
->data_items
);
433 d
->filltop_vertical_grad
= p_new(Bool
, d
->data_items
);
434 d
->fillbottom_color
= p_new(XColor
, d
->data_items
);
435 d
->fillbottom_pcolor_center
= p_new(XColor
*, d
->data_items
);
436 d
->fillbottom_pcolor_end
= p_new(XColor
*, d
->data_items
);
437 d
->fillbottom_vertical_grad
= p_new(Bool
, d
->data_items
);
438 d
->drawline_color
= p_new(XColor
, d
->data_items
);
439 d
->drawline_pcolor_center
= p_new(XColor
*, d
->data_items
);
440 d
->drawline_pcolor_end
= p_new(XColor
*, d
->data_items
);
441 d
->drawline_vertical_grad
= p_new(Bool
, d
->data_items
);
443 d
->max_index
= p_new(int, d
->data_items
);
444 d
->index
= p_new(int, d
->data_items
);
446 d
->current_max
= p_new(float, d
->data_items
);
447 d
->max
= p_new(float, d
->data_items
);
449 for(i
= 0; i
< d
->data_items
; i
++)
451 ptmp_color_center
= ptmp_color_end
= NULL
;
453 cfg
= cfg_getnsec(config
, "data", i
);
455 d
->data_title
[i
] = a_strdup(cfg_title(cfg
));
457 if((color
= cfg_getstr(cfg
, "fg")))
458 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &tmp_color
);
460 tmp_color
= globalconf
.screens
[statusbar
->screen
].styles
.normal
.fg
;
462 if((color
= cfg_getstr(cfg
, "fg_center")))
464 ptmp_color_center
= p_new(XColor
, 1);
465 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, ptmp_color_center
);
468 if((color
= cfg_getstr(cfg
, "fg_end")))
470 ptmp_color_end
= p_new(XColor
, 1);
471 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, ptmp_color_end
);
474 if (cfg_getbool(cfg
, "scale"))
475 d
->values
[i
] = p_new(float, d
->size
); /* not null -> scale = true */
477 /* prevent: division by zero */
478 d
->current_max
[i
] = d
->max
[i
] = cfg_getfloat(cfg
, "max");
481 warn("all graph widget needs a 'max' value greater than zero\n");
486 d
->lines
[i
] = p_new(int, d
->size
);
488 /* filter each style-typ into it's own array (for easy looping later)*/
490 if ((type
= cfg_getstr(cfg
, "draw_style")))
492 if(!a_strncmp(type
, "bottom", sizeof("bottom")))
494 d
->fillbottom
[d
->fillbottom_total
] = d
->lines
[i
];
495 d
->fillbottom_index
[d
->fillbottom_total
] = &d
->index
[i
];
496 d
->fillbottom_color
[d
->fillbottom_total
] = tmp_color
;
497 d
->fillbottom_pcolor_center
[d
->fillbottom_total
] = ptmp_color_center
;
498 d
->fillbottom_pcolor_end
[d
->fillbottom_total
] = ptmp_color_end
;
499 d
->fillbottom_vertical_grad
[d
->fillbottom_total
] = cfg_getbool(cfg
, "vertical_gradient");
500 d
->fillbottom_total
++;
502 else if (!a_strncmp(type
, "top", sizeof("top")))
504 d
->filltop
[d
->filltop_total
] = d
->lines
[i
];
505 d
->filltop_index
[d
->filltop_total
] = &d
->index
[i
];
506 d
->filltop_color
[d
->filltop_total
] = tmp_color
;
507 d
->filltop_pcolor_center
[d
->filltop_total
] = ptmp_color_center
;
508 d
->filltop_pcolor_end
[d
->filltop_total
] = ptmp_color_end
;
509 d
->filltop_vertical_grad
[d
->filltop_total
] = cfg_getbool(cfg
, "vertical_gradient");
512 else if (!a_strncmp(type
, "line", sizeof("line")))
514 d
->drawline
[d
->drawline_total
] = d
->lines
[i
];
515 d
->drawline_index
[d
->drawline_total
] = &d
->index
[i
];
516 d
->drawline_color
[d
->drawline_total
] = tmp_color
;
517 d
->drawline_pcolor_center
[d
->drawline_total
] = ptmp_color_center
;
518 d
->drawline_pcolor_end
[d
->drawline_total
] = ptmp_color_end
;
519 d
->drawline_vertical_grad
[d
->drawline_total
] = cfg_getbool(cfg
, "vertical_gradient");
525 if((color
= cfg_getstr(config
, "bg")))
526 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->bg
);
528 d
->bg
= globalconf
.screens
[statusbar
->screen
].styles
.normal
.bg
;
530 if((color
= cfg_getstr(config
, "bordercolor")))
531 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->bordercolor
);
533 d
->bordercolor
= tmp_color
;
537 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80