[doc] Update a bunch of documentation for uicb; set transparency to be between 0...
[awesome.git] / widgets / progressbar.c
1 /*
2 * progressbar.c - progressbar widget
3 *
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
5 * Copyright © 2007-2008 Marco Candrian <mac@calmar.ws>
6 *
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.
11 *
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.
16 *
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.
20 *
21 */
22
23 #include <string.h>
24 #include "widget.h"
25
26 #include "widgets/common.h"
27 #include "screen.h"
28 #include "common/util.h"
29 #include "common/configopts.h"
30
31
32 extern AwesomeConf globalconf;
33
34 typedef struct
35 {
36 /** Percent 0 to 100 */
37 int *percent;
38 /** data_title of the data */
39 char **data_title;
40 /** Width of the data_items */
41 int width;
42 /** Pixel between data items (bars) */
43 int gap;
44 /** border width in pixels */
45 int border_width;
46 /** padding between border and ticks/bar */
47 int border_padding;
48 /** gap/distance between the individual ticks */
49 int ticks_gap;
50 /** total number of ticks */
51 int ticks_count;
52 /** reverse drawing */
53 Bool *reverse;
54 /** 90 Degree's turned */
55 Bool vertical;
56 /** Number of data_items (bars) */
57 int data_items;
58 /** Height 0-1, where 1 is height of statusbar */
59 float height;
60 /** Foreground color */
61 XColor *fg;
62 /** Foreground color of turned-off ticks */
63 XColor *fg_off;
64 /** Foreground color when bar is half-full */
65 XColor **pfg_center;
66 /** Foreground color when bar is full */
67 XColor **pfg_end;
68 /** Background color */
69 XColor *bg;
70 /** Border color */
71 XColor *bordercolor;
72 } Data;
73
74 Bool check_settings(Data *, int);
75
76 Bool
77 check_settings(Data *d, int status_height)
78 {
79 int simple, h_total;
80
81 h_total = (int)(status_height * d->height + 0.5);
82
83 if(!d->vertical) /* horizontal */
84 {
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))
88 {
89 warn("progressbar's 'width' is too small for the given configuration options\n");
90 return False;
91 }
92 simple = h_total - d->data_items * (d->border_width + d->border_padding + 1) - (d->data_items - 1) * d->gap;
93 if(simple < 0)
94 {
95 warn("progressbar's 'height' is too small for the given configuration options\n");
96 return False;
97 }
98 }
99 else /* vertical / standing up */
100 {
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))
104 {
105 warn("progressbar's 'height' is too small for the given configuration options\n");
106 return False;
107 }
108 simple = d->width - d->data_items * (d->border_width + d->border_padding + 1) - (d->data_items - 1) * d->gap;
109 if(simple < 0)
110 {
111 warn("progressbar's 'width' is too small for the given configuration options\n");
112 return False;
113 }
114 }
115 return True;
116 }
117
118
119 static int
120 progressbar_draw(Widget *widget, DrawCtx *ctx, int offset,
121 int used __attribute__ ((unused)))
122 {
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 */
126 int border_offset;
127 area_t rectangle, pattern_rect;
128
129 Data *d = widget->data;
130
131 if (!d->data_items)
132 return 0;
133
134 if(!widget->user_supplied_x)
135 widget->area.x = widget_calculate_offset(widget->statusbar->width,
136 d->width,
137 offset,
138 widget->alignment);
139 if(!widget->user_supplied_y)
140 widget->area.y = 0;
141
142 /* for a 'reversed' progressbar:
143 * basic 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
147 * ticks special:
148 * round the values to a full tick accordingly
149 * 4. finally draw the gaps
150 */
151
152 pb_x = widget->area.x + 1;
153 border_offset = d->border_width / 2;
154 pb_offset = 0;
155
156 if(d->vertical)
157 {
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)
161 {
162 unit = (pb_height + d->ticks_gap) / d->ticks_count;
163 pb_height = unit * d->ticks_count - d->ticks_gap; /* rounded to match ticks... */
164 }
165
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);
168
169 pb_y = widget->area.y + ((int) (widget->statusbar->height * (1 - d->height)) / 2) + d->border_width + d->border_padding;
170
171 for(i = 0; i < d->data_items; i++)
172 {
173 if(d->ticks_count && d->ticks_gap)
174 {
175 percent_ticks = (int)(d->ticks_count * (float)d->percent[i] / 100 + 0.5);
176 if(percent_ticks)
177 pb_progress = percent_ticks * unit - d->ticks_gap;
178 else
179 pb_progress = 0;
180 }
181 else
182 pb_progress = (int)(pb_height * d->percent[i] / 100.0 + 0.5);
183
184 if(d->border_width)
185 {
186 /* border rectangle */
187 rectangle.x = pb_x + pb_offset - border_offset - d->border_padding - 1;
188
189 if(2 * border_offset == d->border_width)
190 rectangle.y = pb_y - border_offset - d->border_padding;
191 else
192 rectangle.y = pb_y - border_offset - d->border_padding - 1;
193
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]);
198 }
199
200 /* new value/progress in px + pattern setup */
201 if(!d->reverse[i])
202 {
203 /* bottom to top */
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;
208 }
209 else
210 {
211 /* top to bottom */
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;
217 }
218
219 /* bottom part */
220 if(pb_progress > 0)
221 {
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;
226
227 /* fg color */
228 if(!d->reverse[i])
229 draw_rectangle_gradient(ctx, rectangle, 1.0, True, pattern_rect,
230 &(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
231 else /*REV: bg */
232 draw_rectangle(ctx, rectangle, 1.0, True, d->fg_off[i]);
233 }
234
235 /* top part */
236 if(pb_height - pb_progress > 0) /* not filled area */
237 {
238 rectangle.x = pb_x + pb_offset;
239 rectangle.y = pb_y;
240 rectangle.width = pb_width;
241 rectangle.height = pb_height - pb_progress;
242
243 /* bg color */
244 if(!d->reverse[i])
245 draw_rectangle(ctx, rectangle, 1.0, True, d->fg_off[i]);
246 else /* REV: bg */
247 draw_rectangle_gradient(ctx, rectangle, 1.0, True, pattern_rect,
248 &(d->fg[i]), d->pfg_center[i], d->pfg_end[i]);
249 }
250 /* draw gaps TODO: improve e.g all in one */
251 if(d->ticks_count && d->ticks_gap)
252 {
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;
258 rectangle.y += unit)
259 {
260 draw_rectangle(ctx, rectangle, 1.0, True, d->bg[i]);
261 }
262 }
263 pb_offset += pb_width + d->gap + 2 * (d->border_width + d->border_padding);
264 }
265 }
266 else /* a horizontal progressbar */
267 {
268 pb_width = d->width - d->border_width - 2 * d->border_padding;
269 if(d->ticks_count && d->ticks_gap)
270 {
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... */
273 }
274
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;
278
279 for(i = 0; i < d->data_items; i++)
280 {
281 if(d->ticks_count && d->ticks_gap)
282 {
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);
285 if(percent_ticks)
286 pb_progress = (int)(percent_ticks * unit - d->ticks_gap + 0.5);
287 else
288 pb_progress = 0;
289 }
290 else
291 pb_progress = (int)(pb_width * d->percent[i] / 100.0 + 0.5);
292
293 if(d->border_width)
294 {
295 /* border rectangle */
296 rectangle.x = pb_x - border_offset - d->border_padding - 1;
297
298 if(2 * border_offset == d->border_width)
299 rectangle.y = pb_y + pb_offset - border_offset - d->border_padding;
300 else
301 rectangle.y = pb_y + pb_offset - border_offset - d->border_padding - 1;
302
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]);
307 }
308 /* new value/progress in px + pattern setup */
309 if(!d->reverse[i])
310 {
311 /* left to right */
312 pattern_rect.x = pb_x;
313 pattern_rect.y = pb_y;
314 pattern_rect.width = pb_width;
315 pattern_rect.height = 0;
316 }
317 else
318 {
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;
325 }
326
327 /* left part */
328 if(pb_progress > 0)
329 {
330 rectangle.x = pb_x;
331 rectangle.y = pb_y + pb_offset;
332 rectangle.width = pb_progress;
333 rectangle.height = pb_height;
334
335 /* fg color */
336 if(!d->reverse[i])
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]);
341 }
342
343 /* right part */
344 if(pb_width - pb_progress > 0)
345 {
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;
350
351 /* bg color */
352 if(!d->reverse[i])
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]);
357 }
358 /* draw gaps TODO: improve e.g all in one */
359 if(d->ticks_count && d->ticks_gap)
360 {
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;
366 rectangle.x += unit)
367 {
368 draw_rectangle(ctx, rectangle, 1.0, True, d->bg[i]);
369 }
370 }
371
372 pb_offset += pb_height + d->gap + 2 * (d->border_width + d->border_padding);
373 }
374 }
375
376 widget->area.width = d->width;
377 widget->area.height = widget->statusbar->height;
378 return widget->area.width;
379 }
380
381 static widget_tell_status_t
382 progressbar_tell(Widget *widget, char *property, char *command)
383 {
384 Data *d = widget->data;
385 int i = 0, percent, tmp;
386 char *title, *setting;
387 float tmpf;
388
389 if(!d->data_items)
390 return WIDGET_ERROR_CUSTOM; /* error already printed on _new */
391
392 if(!command)
393 return WIDGET_ERROR_NOVALUE;
394
395 if(!a_strcmp(property, "data"))
396 {
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]))
402 {
403 percent = atoi(setting);
404 d->percent[i] = (percent < 0 ? 0 : (percent > 100 ? 100 : percent));
405 return WIDGET_NOERROR;
406 }
407 return WIDGET_ERROR_FORMAT_SECTION;
408 }
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"))
424 {
425 tmp = d->width;
426 d->width = atoi(command);
427 if(!check_settings(d, widget->statusbar->height))
428 {
429 d->width = tmp; /* restore */
430 return WIDGET_ERROR_CUSTOM;
431 }
432 }
433 else if(!a_strcmp(property, "height"))
434 {
435 tmpf = d->height;
436 d->height = atof(command);
437 if(!check_settings(d, widget->statusbar->height))
438 {
439 d->height = tmpf; /* restore */
440 return WIDGET_ERROR_CUSTOM;
441 }
442 }
443 else
444 return WIDGET_ERROR;
445
446 return WIDGET_NOERROR;
447 }
448
449 Widget *
450 progressbar_new(Statusbar *statusbar, cfg_t *config)
451 {
452 Widget *w;
453 Data *d;
454 char *color;
455 int i;
456 cfg_t *cfg;
457
458
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);
464
465 d->data_items = 0; /* XXX: need to initialise or for sure 0? */
466
467 d->height = cfg_getfloat(config, "height");
468 d->width = cfg_getint(config, "width");
469
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");
474
475 if(!(d->vertical = cfg_getbool(config, "vertical")))
476 d->vertical = False;
477
478 if(!check_settings(d, statusbar->height))
479 return w;
480
481 d->gap = cfg_getint(config, "gap");
482
483 w->alignment = cfg_getalignment(config, "align");
484
485 if(!(d->data_items = cfg_size(config, "data")))
486 {
487 warn("progressbar widget needs at least one bar section\n");
488 return w;
489 }
490
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);
500
501 for(i = 0; i < d->data_items; i++)
502 {
503 cfg = cfg_getnsec(config, "data", i);
504
505 d->data_title[i] = a_strdup(cfg_title(cfg));
506
507 if(!(d->reverse[i] = cfg_getbool(cfg, "reverse")))
508 d->reverse[i] = False;
509
510 if((color = cfg_getstr(cfg, "fg")))
511 draw_color_new(globalconf.display, statusbar->phys_screen, color, &d->fg[i]);
512 else
513 d->fg[i] = globalconf.screens[statusbar->screen].styles.normal.fg;
514
515 if((color = cfg_getstr(cfg, "fg_center")))
516 {
517 d->pfg_center[i] = p_new(XColor, 1);
518 draw_color_new(globalconf.display, statusbar->phys_screen, color, d->pfg_center[i]);
519 }
520
521 if((color = cfg_getstr(cfg, "fg_end")))
522 {
523 d->pfg_end[i] = p_new(XColor, 1);
524 draw_color_new(globalconf.display, statusbar->phys_screen, color, d->pfg_end[i]);
525 }
526
527 if((color = cfg_getstr(cfg, "fg_off")))
528 draw_color_new(globalconf.display, statusbar->phys_screen, color, &d->fg_off[i]);
529 else
530 d->fg_off[i] = globalconf.screens[statusbar->screen].styles.normal.bg;
531
532 if((color = cfg_getstr(cfg, "bg")))
533 draw_color_new(globalconf.display, statusbar->phys_screen, color, &d->bg[i]);
534 else
535 d->bg[i] = globalconf.screens[statusbar->screen].styles.normal.bg;
536
537 if((color = cfg_getstr(cfg, "bordercolor")))
538 draw_color_new(globalconf.display, statusbar->phys_screen, color, &d->bordercolor[i]);
539 else
540 d->bordercolor[i] = d->fg[i];
541 }
542 return w;
543 }
544 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80