2 * progressbar.c - progressbar 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 "widgets/common.h"
28 #include "common/util.h"
29 #include "common/configopts.h"
32 extern AwesomeConf globalconf
;
36 /** Percent 0 to 100 */
38 /** data_title of the data */
40 /** Width of the data_items */
42 /** Pixel between data items (bars) */
44 /** border width in pixels */
46 /** padding between border and ticks/bar */
48 /** gap/distance between the individual ticks */
50 /** total number of ticks */
52 /** reverse drawing */
54 /** 90 Degree's turned */
56 /** Number of data_items (bars) */
58 /** Height 0-1, where 1 is height of statusbar */
60 /** Foreground color */
62 /** Foreground color of turned-off ticks */
64 /** Foreground color when bar is half-full */
66 /** Foreground color when bar is full */
68 /** Background color */
74 Bool
check_settings(Data
*, int);
77 check_settings(Data
*d
, int status_height
)
81 h_total
= (int)(status_height
* d
->height
+ 0.5);
83 if(!d
->vertical
) /* horizontal */
85 simple
= d
->width
- 2 * (d
->border_width
+ d
->border_padding
) - 1;
86 if((d
->ticks_count
&& simple
- (d
->ticks_count
- 1) * d
->ticks_gap
- d
->ticks_count
+ 1 < 0) ||
87 (!d
->ticks_count
&& simple
< 0))
89 warn("progressbar's 'width' is too small for the given configuration options\n");
92 simple
= h_total
- d
->data_items
* (d
->border_width
+ d
->border_padding
+ 1) - (d
->data_items
- 1) * d
->gap
;
95 warn("progressbar's 'height' is too small for the given configuration options\n");
99 else /* vertical / standing up */
101 simple
= h_total
- 2 * (d
->border_width
+ d
->border_padding
) - 1;
102 if((d
->ticks_count
&& simple
- (d
->ticks_count
- 1) * d
->ticks_gap
- d
->ticks_count
+ 1 < 0) ||
103 (!d
->ticks_count
&& simple
< 0))
105 warn("progressbar's 'height' is too small for the given configuration options\n");
108 simple
= d
->width
- d
->data_items
* (d
->border_width
+ d
->border_padding
+ 1) - (d
->data_items
- 1) * d
->gap
;
111 warn("progressbar's 'width' is too small for the given configuration options\n");
120 progressbar_draw(Widget
*widget
, DrawCtx
*ctx
, int offset
,
121 int used
__attribute__ ((unused
)))
123 /* pb_.. values points to the widget inside a potential border */
124 int i
, percent_ticks
, pb_x
, pb_y
, pb_height
, pb_width
, pb_progress
, pb_offset
;
125 int unit
= 0; /* tick + gap */
127 area_t rectangle
, pattern_rect
;
129 Data
*d
= widget
->data
;
134 if(!widget
->user_supplied_x
)
135 widget
->area
.x
= widget_calculate_offset(widget
->statusbar
->width
,
139 if(!widget
->user_supplied_y
)
142 /* for a 'reversed' progressbar:
144 * 1. the full space gets the size of the formerly empty one
145 * 2. the pattern must be mirrored
146 * 3. the formerly 'empty' side gets drawed with fg colors, the 'full' with bg-color
148 * round the values to a full tick accordingly
149 * 4. finally draw the gaps
152 pb_x
= widget
->area
.x
+ 1;
153 border_offset
= d
->border_width
/ 2;
158 /* TODO: maybe prevent to calculate that stuff below over and over again (->use Data-values) */
159 pb_height
= (int) (widget
->statusbar
->height
* d
->height
+ 0.5) - 2 * (d
->border_width
+ d
->border_padding
);
160 if(d
->ticks_count
&& d
->ticks_gap
)
162 unit
= (pb_height
+ d
->ticks_gap
) / d
->ticks_count
;
163 pb_height
= unit
* d
->ticks_count
- d
->ticks_gap
; /* rounded to match ticks... */
166 pb_width
= (int) ((d
->width
- 2 * (d
->border_width
+ d
->border_padding
) * d
->data_items
-
167 d
->gap
* (d
->data_items
- 1)) / d
->data_items
);
169 pb_y
= widget
->area
.y
+ ((int) (widget
->statusbar
->height
* (1 - d
->height
)) / 2) + d
->border_width
+ d
->border_padding
;
171 for(i
= 0; i
< d
->data_items
; i
++)
173 if(d
->ticks_count
&& d
->ticks_gap
)
175 percent_ticks
= (int)(d
->ticks_count
* (float)d
->percent
[i
] / 100 + 0.5);
177 pb_progress
= percent_ticks
* unit
- d
->ticks_gap
;
182 pb_progress
= (int)(pb_height
* d
->percent
[i
] / 100.0 + 0.5);
186 /* border rectangle */
187 rectangle
.x
= pb_x
+ pb_offset
- border_offset
- d
->border_padding
- 1;
189 if(2 * border_offset
== d
->border_width
)
190 rectangle
.y
= pb_y
- border_offset
- d
->border_padding
;
192 rectangle
.y
= pb_y
- border_offset
- d
->border_padding
- 1;
194 rectangle
.width
= pb_width
+ d
->border_width
+ 2 * d
->border_padding
- 1 + 2;
195 rectangle
.height
= pb_height
+ d
->border_width
+ 2 * d
->border_padding
+ 1;
196 draw_rectangle(ctx
, rectangle
, d
->border_width
, True
, d
->bg
[i
]);
197 draw_rectangle(ctx
, rectangle
, d
->border_width
, False
, d
->bordercolor
[i
]);
200 /* new value/progress in px + pattern setup */
204 pattern_rect
.x
= pb_x
;
205 pattern_rect
.y
= pb_y
+ pb_height
;
206 pattern_rect
.width
= 0;
207 pattern_rect
.height
= -pb_height
;
212 pb_progress
= pb_width
- pb_progress
;
213 pattern_rect
.x
= pb_x
;
214 pattern_rect
.y
= pb_y
;
215 pattern_rect
.width
= 0;
216 pattern_rect
.height
= pb_height
;
222 rectangle
.x
= pb_x
+ pb_offset
;
223 rectangle
.y
= pb_y
+ pb_height
- pb_progress
;
224 rectangle
.width
= pb_width
;
225 rectangle
.height
= pb_progress
;
229 draw_rectangle_gradient(ctx
, rectangle
, 1.0, True
, pattern_rect
,
230 &(d
->fg
[i
]), d
->pfg_center
[i
], d
->pfg_end
[i
]);
232 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->fg_off
[i
]);
236 if(pb_height
- pb_progress
> 0) /* not filled area */
238 rectangle
.x
= pb_x
+ pb_offset
;
240 rectangle
.width
= pb_width
;
241 rectangle
.height
= pb_height
- pb_progress
;
245 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->fg_off
[i
]);
247 draw_rectangle_gradient(ctx
, rectangle
, 1.0, True
, pattern_rect
,
248 &(d
->fg
[i
]), d
->pfg_center
[i
], d
->pfg_end
[i
]);
250 /* draw gaps TODO: improve e.g all in one */
251 if(d
->ticks_count
&& d
->ticks_gap
)
253 rectangle
.width
= pb_width
;
254 rectangle
.height
= d
->ticks_gap
;
255 rectangle
.x
= pb_x
+ pb_offset
;
256 for(rectangle
.y
= pb_y
+ (unit
- d
->ticks_gap
);
257 pb_y
+ pb_height
- d
->ticks_gap
>= rectangle
.y
;
260 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->bg
[i
]);
263 pb_offset
+= pb_width
+ d
->gap
+ 2 * (d
->border_width
+ d
->border_padding
);
266 else /* a horizontal progressbar */
268 pb_width
= d
->width
- d
->border_width
- 2 * d
->border_padding
;
269 if(d
->ticks_count
&& d
->ticks_gap
)
271 unit
= (pb_width
+ d
->ticks_gap
) / d
->ticks_count
;
272 pb_width
= (int)unit
* d
->ticks_count
- d
->ticks_gap
; /* rounded to match ticks... */
275 pb_height
= (int) ((widget
->statusbar
->height
* d
->height
- d
->data_items
* 2 * (d
->border_width
+ d
->border_padding
) -
276 (d
->gap
* (d
->data_items
- 1))) / d
->data_items
+ 0.5);
277 pb_y
= widget
->area
.y
+ ((int) (widget
->statusbar
->height
* (1 - d
->height
)) / 2) + d
->border_width
+ d
->border_padding
;
279 for(i
= 0; i
< d
->data_items
; i
++)
281 if(d
->ticks_count
&& d
->ticks_gap
)
283 /* +0.5 rounds up ticks -> turn on a tick when half of it is reached */
284 percent_ticks
= (int)(d
->ticks_count
* (float)d
->percent
[i
] / 100 + 0.5);
286 pb_progress
= (int)(percent_ticks
* unit
- d
->ticks_gap
+ 0.5);
291 pb_progress
= (int)(pb_width
* d
->percent
[i
] / 100.0 + 0.5);
295 /* border rectangle */
296 rectangle
.x
= pb_x
- border_offset
- d
->border_padding
- 1;
298 if(2 * border_offset
== d
->border_width
)
299 rectangle
.y
= pb_y
+ pb_offset
- border_offset
- d
->border_padding
;
301 rectangle
.y
= pb_y
+ pb_offset
- border_offset
- d
->border_padding
- 1;
303 rectangle
.width
= pb_width
+ d
->border_width
+ 2 * d
->border_padding
- 1 + 2;
304 rectangle
.height
= pb_height
+ d
->border_width
+ 2 * d
->border_padding
+ 1;
305 draw_rectangle(ctx
, rectangle
, d
->border_width
, True
, d
->bg
[i
]);
306 draw_rectangle(ctx
, rectangle
, d
->border_width
, False
, d
->bordercolor
[i
]);
308 /* new value/progress in px + pattern setup */
312 pattern_rect
.x
= pb_x
;
313 pattern_rect
.y
= pb_y
;
314 pattern_rect
.width
= pb_width
;
315 pattern_rect
.height
= 0;
319 /* reverse: right to left */
320 pb_progress
= pb_width
- pb_progress
;
321 pattern_rect
.x
= pb_x
+ pb_width
;
322 pattern_rect
.y
= pb_y
;
323 pattern_rect
.width
= -pb_width
;
324 pattern_rect
.height
= 0;
331 rectangle
.y
= pb_y
+ pb_offset
;
332 rectangle
.width
= pb_progress
;
333 rectangle
.height
= pb_height
;
337 draw_rectangle_gradient(ctx
, rectangle
, 1.0, True
, pattern_rect
,
338 &(d
->fg
[i
]), d
->pfg_center
[i
], d
->pfg_end
[i
]);
339 else /* reverse: bg */
340 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->fg_off
[i
]);
344 if(pb_width
- pb_progress
> 0)
346 rectangle
.x
= pb_x
+ pb_progress
;
347 rectangle
.y
= pb_y
+ pb_offset
;
348 rectangle
.width
= pb_width
- pb_progress
;
349 rectangle
.height
= pb_height
;
353 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->fg_off
[i
]);
354 else /* reverse: fg */
355 draw_rectangle_gradient(ctx
, rectangle
, 1.0, True
, pattern_rect
,
356 &(d
->fg
[i
]), d
->pfg_center
[i
], d
->pfg_end
[i
]);
358 /* draw gaps TODO: improve e.g all in one */
359 if(d
->ticks_count
&& d
->ticks_gap
)
361 rectangle
.width
= d
->ticks_gap
;
362 rectangle
.height
= pb_height
;
363 rectangle
.y
= pb_y
+ pb_offset
;
364 for(rectangle
.x
= pb_x
+ (unit
- d
->ticks_gap
);
365 pb_x
+ pb_width
- d
->ticks_gap
>= rectangle
.x
;
368 draw_rectangle(ctx
, rectangle
, 1.0, True
, d
->bg
[i
]);
372 pb_offset
+= pb_height
+ d
->gap
+ 2 * (d
->border_width
+ d
->border_padding
);
376 widget
->area
.width
= d
->width
;
377 widget
->area
.height
= widget
->statusbar
->height
;
378 return widget
->area
.width
;
381 static widget_tell_status_t
382 progressbar_tell(Widget
*widget
, char *property
, char *command
)
384 Data
*d
= widget
->data
;
385 int i
= 0, percent
, tmp
;
386 char *title
, *setting
;
390 return WIDGET_ERROR_CUSTOM
; /* error already printed on _new */
393 return WIDGET_ERROR_NOVALUE
;
395 if(!a_strcmp(property
, "data"))
397 title
= strtok(command
, " ");
398 if(!(setting
= strtok(NULL
, " ")))
399 return WIDGET_ERROR_NOVALUE
;
400 for(i
= 0; i
< d
->data_items
; i
++)
401 if(!a_strcmp(title
, d
->data_title
[i
]))
403 percent
= atoi(setting
);
404 d
->percent
[i
] = (percent
< 0 ?
0 : (percent
> 100 ?
100 : percent
));
405 return WIDGET_NOERROR
;
407 return WIDGET_ERROR_FORMAT_SECTION
;
409 else if(!a_strcmp(property
, "fg"))
410 return widget_set_color_for_data(widget
, d
->fg
, command
, d
->data_items
, d
->data_title
);
411 else if(!a_strcmp(property
, "fg_off"))
412 return widget_set_color_for_data(widget
, d
->fg_off
, command
, d
->data_items
, d
->data_title
);
413 else if(!a_strcmp(property
, "bg"))
414 return widget_set_color_for_data(widget
, d
->bg
, command
, d
->data_items
, d
->data_title
);
415 else if(!a_strcmp(property
, "bordercolor"))
416 return widget_set_color_for_data(widget
, d
->bordercolor
, command
, d
->data_items
, d
->data_title
);
417 else if(!a_strcmp(property
, "fg_center"))
418 return widget_set_color_pointer_for_data(widget
, d
->pfg_center
, command
, d
->data_items
, d
->data_title
);
419 else if(!a_strcmp(property
, "fg_end"))
420 return widget_set_color_pointer_for_data(widget
, d
->pfg_end
, command
, d
->data_items
, d
->data_title
);
421 else if(!a_strcmp(property
, "gap"))
422 d
->gap
= atoi(command
);
423 else if(!a_strcmp(property
, "width"))
426 d
->width
= atoi(command
);
427 if(!check_settings(d
, widget
->statusbar
->height
))
429 d
->width
= tmp
; /* restore */
430 return WIDGET_ERROR_CUSTOM
;
433 else if(!a_strcmp(property
, "height"))
436 d
->height
= atof(command
);
437 if(!check_settings(d
, widget
->statusbar
->height
))
439 d
->height
= tmpf
; /* restore */
440 return WIDGET_ERROR_CUSTOM
;
446 return WIDGET_NOERROR
;
450 progressbar_new(Statusbar
*statusbar
, cfg_t
*config
)
459 w
= p_new(Widget
, 1);
460 widget_common_new(w
, statusbar
, config
);
461 w
->draw
= progressbar_draw
;
462 w
->tell
= progressbar_tell
;
463 d
= w
->data
= p_new(Data
, 1);
465 d
->data_items
= 0; /* XXX: need to initialise or for sure 0? */
467 d
->height
= cfg_getfloat(config
, "height");
468 d
->width
= cfg_getint(config
, "width");
470 d
->border_padding
= cfg_getint(config
, "border_padding");
471 d
->ticks_gap
= cfg_getint(config
, "ticks_gap");
472 d
->ticks_count
= cfg_getint(config
, "ticks_count");
473 d
->border_width
= cfg_getint(config
, "border_width");
475 if(!(d
->vertical
= cfg_getbool(config
, "vertical")))
478 if(!check_settings(d
, statusbar
->height
))
481 d
->gap
= cfg_getint(config
, "gap");
483 w
->alignment
= cfg_getalignment(config
, "align");
485 if(!(d
->data_items
= cfg_size(config
, "data")))
487 warn("progressbar widget needs at least one bar section\n");
491 d
->fg
= p_new(XColor
, d
->data_items
);
492 d
->pfg_end
= p_new(XColor
*, d
->data_items
);
493 d
->pfg_center
= p_new(XColor
*, d
->data_items
);
494 d
->fg_off
= p_new(XColor
, d
->data_items
);
495 d
->bg
= p_new(XColor
, d
->data_items
);
496 d
->bordercolor
= p_new(XColor
, d
->data_items
);
497 d
->percent
= p_new(int, d
->data_items
);
498 d
->reverse
= p_new(Bool
, d
->data_items
);
499 d
->data_title
= p_new(char *, d
->data_items
);
501 for(i
= 0; i
< d
->data_items
; i
++)
503 cfg
= cfg_getnsec(config
, "data", i
);
505 d
->data_title
[i
] = a_strdup(cfg_title(cfg
));
507 if(!(d
->reverse
[i
] = cfg_getbool(cfg
, "reverse")))
508 d
->reverse
[i
] = False
;
510 if((color
= cfg_getstr(cfg
, "fg")))
511 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->fg
[i
]);
513 d
->fg
[i
] = globalconf
.screens
[statusbar
->screen
].styles
.normal
.fg
;
515 if((color
= cfg_getstr(cfg
, "fg_center")))
517 d
->pfg_center
[i
] = p_new(XColor
, 1);
518 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, d
->pfg_center
[i
]);
521 if((color
= cfg_getstr(cfg
, "fg_end")))
523 d
->pfg_end
[i
] = p_new(XColor
, 1);
524 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, d
->pfg_end
[i
]);
527 if((color
= cfg_getstr(cfg
, "fg_off")))
528 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->fg_off
[i
]);
530 d
->fg_off
[i
] = globalconf
.screens
[statusbar
->screen
].styles
.normal
.bg
;
532 if((color
= cfg_getstr(cfg
, "bg")))
533 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->bg
[i
]);
535 d
->bg
[i
] = globalconf
.screens
[statusbar
->screen
].styles
.normal
.bg
;
537 if((color
= cfg_getstr(cfg
, "bordercolor")))
538 draw_color_new(globalconf
.display
, statusbar
->phys_screen
, color
, &d
->bordercolor
[i
]);
540 d
->bordercolor
[i
] = d
->fg
[i
];
544 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80