naughty: icon_size added to config and notify()
[awesome.git] / widgets / progressbar.c
blob3a5546f6737364972434126767c8acb7157b36f8
1 /*
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.
23 #include "common/tokenize.h"
24 #include "widget.h"
26 extern awesome_t globalconf;
28 /** Progressbar bar data structure */
29 typedef struct
31 /** Title of the data/bar */
32 char *title;
33 /** These or lower values won't fill the bar at all*/
34 float min_value;
35 /** These or higher values fill the bar fully */
36 float max_value;
37 /** Pointer to value */
38 float value;
39 /** Reverse filling */
40 bool reverse;
41 /** Foreground color */
42 xcolor_t fg;
43 /** Foreground color of turned-off ticks */
44 xcolor_t fg_off;
45 /** Foreground color when bar is half-full */
46 xcolor_t fg_center;
47 /** Foreground color when bar is full */
48 xcolor_t fg_end;
49 /** Background color */
50 xcolor_t bg;
51 /** Border color */
52 xcolor_t border_color;
53 } bar_t;
55 /** Delete a bar.
56 * \param bar The bar to annihilate.
58 static void
59 bar_delete(bar_t *bar)
61 p_delete(&bar->title);
64 DO_ARRAY(bar_t, bar, bar_delete)
66 /** Progressbar private data structure */
67 typedef struct
69 /** Width of the data_items */
70 int width;
71 /** Pixel between data items (bars) */
72 int gap;
73 /** Border width in pixels */
74 int border_width;
75 /** Padding between border and ticks/bar */
76 int border_padding;
77 /** Gap/distance between the individual ticks */
78 int ticks_gap;
79 /** Total number of ticks */
80 int ticks_count;
81 /** 90 Degree's turned */
82 bool vertical;
83 /** Height 0-1, where 1.0 is height of bar */
84 float height;
85 /** The bars */
86 bar_array_t bars;
87 } progressbar_data_t;
89 /** Add a new bar to the progressbar private data structure.
90 * \param bars The bar array.
91 * \param title The bar title.
92 * \return The new bar.
94 static bar_t *
95 progressbar_bar_add(bar_array_t *bars, const char *title)
97 bar_t bar;
99 p_clear(&bar, 1);
101 bar.title = a_strdup(title);
102 bar.fg = globalconf.colors.fg;
103 bar.fg_off = globalconf.colors.bg;
104 bar.bg = globalconf.colors.bg;
105 bar.border_color = globalconf.colors.fg;
106 bar.max_value = 100.0;
108 /* append the bar in the list */
109 bar_array_append(bars, bar);
111 return &bars->tab[bars->len - 1];
114 /** Get the bar, and create one if it does not exist.
115 * \param bars The bar array.
116 * \param title The bar title.
117 * \return A maybe new bar.
119 static bar_t *
120 progressbar_bar_get(bar_array_t *bars, const char *title)
122 bar_t *bar;
124 /* check if this section is defined already */
125 for(int j = 0; j < bars->len; j++)
127 bar = &bars->tab[j];
128 if(!a_strcmp(title, bar->title))
129 return bar;
132 /* no bar found -> create one */
133 return progressbar_bar_add(bars, title);
136 static area_t
137 progressbar_geometry(widget_t *widget, int screen, int height, int width)
139 area_t geometry;
140 progressbar_data_t *d = widget->data;
142 geometry.height = height;
144 if(d->vertical)
146 int pb_width = (int) ((d->width - 2 * (d->border_width + d->border_padding) * d->bars.len
147 - d->gap * (d->bars.len - 1)) / d->bars.len);
148 geometry.width = d->bars.len * (pb_width + 2 * (d->border_width + d->border_padding)
149 + d->gap) - d->gap;
151 else
153 int pb_width = d->width - 2 * (d->border_width + d->border_padding);
154 if(d->ticks_count && d->ticks_gap)
156 int unit = (pb_width + d->ticks_gap) / d->ticks_count;
157 pb_width = unit * d->ticks_count - d->ticks_gap; /* rounded to match ticks... */
159 geometry.width = pb_width + 2 * (d->border_width + d->border_padding);
162 return geometry;
165 /** Draw a progressbar.
166 * \param ctx The draw context.
167 * \param screen The screen we're drawing for.
168 * \param w The widget node we're drawing for.
169 * \param offset Offset to draw at.
170 * \param used Space already used.
171 * \param object The object pointer we're drawing onto.
172 * \return The width used.
174 static void
175 progressbar_draw(widget_t *widget, draw_context_t *ctx, area_t geometry,
176 int screen, wibox_t *p)
178 /* pb_.. values points to the widget inside a potential border */
179 int values_ticks, pb_x, pb_y, pb_height, pb_width, pb_progress, pb_offset;
180 int unit = 0; /* tick + gap */
181 area_t rectangle;
182 vector_t color_gradient;
183 progressbar_data_t *d = widget->data;
185 if(!d->bars.len)
186 return;
188 /* for a 'reversed' progressbar:
189 * basic progressbar:
190 * 1. the full space gets the size of the formerly empty one
191 * 2. the pattern must be mirrored
192 * 3. the formerly 'empty' side gets drawed with fg colors, the 'full' with bg-color
194 * ticks:
195 * 1. round the values to a full tick accordingly
196 * 2. finally draw the gaps
199 pb_x = geometry.x + d->border_width + d->border_padding;
200 pb_offset = 0;
202 if(d->vertical)
204 pb_width = (int) ((d->width - 2 * (d->border_width + d->border_padding) * d->bars.len
205 - d->gap * (d->bars.len - 1)) / d->bars.len);
207 /** \todo maybe prevent to calculate that stuff below over and over again
208 * (->use static-values) */
209 pb_height = (int) (ctx->height * d->height + 0.5)
210 - 2 * (d->border_width + d->border_padding);
211 if(d->ticks_count && d->ticks_gap)
213 /* '+ d->ticks_gap' because a unit includes a ticks + ticks_gap */
214 unit = (pb_height + d->ticks_gap) / d->ticks_count;
215 pb_height = unit * d->ticks_count - d->ticks_gap;
218 pb_y = geometry.y + ((int) (ctx->height * (1 - d->height)) / 2)
219 + d->border_width + d->border_padding;
221 for(int i = 0; i < d->bars.len; i++)
223 bar_t *bar = &d->bars.tab[i];
224 if(d->ticks_count && d->ticks_gap)
226 values_ticks = (int)(d->ticks_count * (bar->value - bar->min_value)
227 / (bar->max_value - bar->min_value) + 0.5);
228 if(values_ticks)
229 pb_progress = values_ticks * unit - d->ticks_gap;
230 else
231 pb_progress = 0;
233 else
234 /* e.g.: min = 50; max = 56; 53 should show 50% graph
235 * (53(val) - 50(min) / (56(max) - 50(min) = 3 / 5 = 0.5 = 50%
236 * round that ( + 0.5 and (int)) and finally multiply with height
238 pb_progress = (int) (pb_height * (bar->value - bar->min_value)
239 / (bar->max_value - bar->min_value) + 0.5);
241 if(d->border_width)
243 /* border rectangle */
244 rectangle.x = pb_x + pb_offset - d->border_width - d->border_padding;
245 rectangle.y = pb_y - d->border_width - d->border_padding;
246 rectangle.width = pb_width + 2 * (d->border_padding + d->border_width);
247 rectangle.height = pb_height + 2 * (d->border_padding + d->border_width);
249 if(d->border_padding)
250 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
251 draw_rectangle(ctx, rectangle, d->border_width, false, &bar->border_color);
254 color_gradient.x = pb_x;
255 color_gradient.x_offset = 0;
256 color_gradient.y = pb_y;
258 /* new value/progress in px + pattern setup */
259 if(bar->reverse)
261 /* invert: top with bottom part */
262 pb_progress = pb_height - pb_progress;
263 color_gradient.y_offset = pb_height;
265 else
267 /* bottom to top */
268 color_gradient.y += pb_height;
269 color_gradient.y_offset = - pb_height;
272 /* bottom part */
273 if(pb_progress > 0)
275 rectangle.x = pb_x + pb_offset;
276 rectangle.y = pb_y + pb_height - pb_progress;
277 rectangle.width = pb_width;
278 rectangle.height = pb_progress;
280 /* fg color */
281 if(bar->reverse)
282 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
283 else
284 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
285 &bar->fg, &bar->fg_center, &bar->fg_end);
288 /* top part */
289 if(pb_height - pb_progress > 0) /* not filled area */
291 rectangle.x = pb_x + pb_offset;
292 rectangle.y = pb_y;
293 rectangle.width = pb_width;
294 rectangle.height = pb_height - pb_progress;
296 /* bg color */
297 if(bar->reverse)
298 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
299 &bar->fg, &bar->fg_center, &bar->fg_end);
300 else
301 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
303 /* draw gaps TODO: improve e.g all in one */
304 if(d->ticks_count && d->ticks_gap)
306 rectangle.width = pb_width;
307 rectangle.height = d->ticks_gap;
308 rectangle.x = pb_x + pb_offset;
309 for(rectangle.y = pb_y + (unit - d->ticks_gap);
310 pb_y + pb_height - d->ticks_gap >= rectangle.y;
311 rectangle.y += unit)
312 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
314 pb_offset += pb_width + d->gap + 2 * (d->border_width + d->border_padding);
317 else /* a horizontal progressbar */
319 pb_width = d->width - 2 * (d->border_width + d->border_padding);
321 if(d->ticks_count && d->ticks_gap)
323 unit = (pb_width + d->ticks_gap) / d->ticks_count;
324 pb_width = unit * d->ticks_count - d->ticks_gap; /* rounded to match ticks... */
327 pb_height = (int) ((ctx->height * d->height
328 - d->bars.len * 2 * (d->border_width + d->border_padding)
329 - (d->gap * (d->bars.len - 1))) / d->bars.len + 0.5);
330 pb_y = geometry.y + ((int) (ctx->height * (1 - d->height)) / 2)
331 + d->border_width + d->border_padding;
333 for(int i = 0; i < d->bars.len; i++)
335 bar_t *bar = &d->bars.tab[i];
336 if(d->ticks_count && d->ticks_gap)
338 /* +0.5 rounds up ticks -> turn on a tick when half of it is reached */
339 values_ticks = (int)(d->ticks_count * (bar->value - bar->min_value)
340 / (bar->max_value - bar->min_value) + 0.5);
341 if(values_ticks)
342 pb_progress = values_ticks * unit - d->ticks_gap;
343 else
344 pb_progress = 0;
346 else
347 pb_progress = (int) (pb_width * (bar->value - bar->min_value)
348 / (bar->max_value - bar->min_value) + 0.5);
350 if(d->border_width)
352 /* border rectangle */
353 rectangle.x = pb_x - d->border_width - d->border_padding;
354 rectangle.y = pb_y + pb_offset - d->border_width - d->border_padding;
355 rectangle.width = pb_width + 2 * (d->border_padding + d->border_width);
356 rectangle.height = pb_height + 2 * (d->border_padding + d->border_width);
358 if(d->border_padding)
359 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
360 draw_rectangle(ctx, rectangle, d->border_width, false, &bar->border_color);
363 color_gradient.y = pb_y;
364 color_gradient.y_offset = 0;
365 color_gradient.x = pb_x;
367 /* new value/progress in px + pattern setup */
368 if(bar->reverse)
370 /* reverse: right to left */
371 pb_progress = pb_width - pb_progress;
372 color_gradient.x += pb_width;
373 color_gradient.x_offset = - pb_width;
375 else
376 /* left to right */
377 color_gradient.x_offset = pb_width;
379 /* left part */
380 if(pb_progress > 0)
382 rectangle.x = pb_x;
383 rectangle.y = pb_y + pb_offset;
384 rectangle.width = pb_progress;
385 rectangle.height = pb_height;
387 /* fg color */
388 if(bar->reverse)
389 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
390 else
391 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
392 &bar->fg, &bar->fg_center, &bar->fg_end);
395 /* right part */
396 if(pb_width - pb_progress > 0)
398 rectangle.x = pb_x + pb_progress;
399 rectangle.y = pb_y + pb_offset;
400 rectangle.width = pb_width - pb_progress;
401 rectangle.height = pb_height;
403 /* bg color */
404 if(bar->reverse)
405 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
406 &bar->fg, &bar->fg_center, &bar->fg_end);
407 else
408 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
410 /* draw gaps TODO: improve e.g all in one */
411 if(d->ticks_count && d->ticks_gap)
413 rectangle.width = d->ticks_gap;
414 rectangle.height = pb_height;
415 rectangle.y = pb_y + pb_offset;
416 for(rectangle.x = pb_x + (unit - d->ticks_gap);
417 pb_x + pb_width - d->ticks_gap >= rectangle.x;
418 rectangle.x += unit)
419 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
422 pb_offset += pb_height + d->gap + 2 * (d->border_width + d->border_padding);
427 /** Set various progressbar bars properties:
428 * \param L The Lua VM state.
429 * \return The number of elements pushed on the stack.
430 * \luastack
431 * \lvalue A widget.
432 * \lparam A bar name.
433 * \lparam A table with keys as properties names.
435 static int
436 luaA_progressbar_bar_properties_set(lua_State *L)
438 size_t len;
439 widget_t **widget = luaA_checkudata(L, 1, "widget");
440 const char *buf, *title = luaL_checkstring(L, 2);
441 bar_t *bar;
442 progressbar_data_t *d = (*widget)->data;
443 xcolor_init_request_t reqs[6];
444 int8_t i, reqs_nbr = -1;
446 luaA_checktable(L, 3);
448 bar = progressbar_bar_get(&d->bars, title);
450 if((buf = luaA_getopt_lstring(L, 3, "fg", NULL, &len)))
451 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->fg, buf, len);
453 if((buf = luaA_getopt_lstring(L, 3, "fg_off", NULL, &len)))
454 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->fg_off, buf, len);
456 if((buf = luaA_getopt_lstring(L, 3, "bg", NULL, &len)))
457 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->bg, buf, len);
459 if((buf = luaA_getopt_lstring(L, 3, "border_color", NULL, &len)))
460 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->border_color, buf, len);
462 if((buf = luaA_getopt_lstring(L, 3, "fg_center", NULL, &len)))
463 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->fg_center, buf, len);
465 if((buf = luaA_getopt_lstring(L, 3, "fg_end", NULL, &len)))
466 reqs[++reqs_nbr] = xcolor_init_unchecked(&bar->fg_end, buf, len);
468 bar->min_value = luaA_getopt_number(L, 3, "min_value", bar->min_value);
469 /* hack to prevent max_value beeing less than min_value
470 * and also preventing a division by zero when both are equal */
471 if(bar->max_value <= bar->min_value)
472 bar->max_value = bar->max_value + 0.0001;
473 /* force a actual value into the newly possible range */
474 if(bar->value < bar->min_value)
475 bar->value = bar->min_value;
477 bar->max_value = luaA_getopt_number(L, 3, "max_value", bar->max_value);
478 if(bar->min_value >= bar->max_value)
479 bar->min_value = bar->max_value - 0.0001;
480 if(bar->value > bar->max_value)
481 bar->value = bar->max_value;
483 bar->reverse = luaA_getopt_boolean(L, 3, "reverse", bar->reverse);
485 for(i = 0; i <= reqs_nbr; i++)
486 xcolor_init_reply(reqs[i]);
488 widget_invalidate_bywidget(*widget);
490 return 0;
493 /** Add a value to a progressbar bar.
494 * \param L The Lua VM state.
495 * \return The number of elements pushed on the stack.
496 * \luastack
497 * \lvalue A widget.
498 * \lparam A bar name.
499 * \lparam A data value.
501 static int
502 luaA_progressbar_bar_data_add(lua_State *L)
504 widget_t **widget = luaA_checkudata(L, 1, "widget");
505 const char *title = luaL_checkstring(L, 2);
506 progressbar_data_t *d = (*widget)->data;
507 bar_t *bar;
509 bar = progressbar_bar_get(&d->bars, title);
511 bar->value = luaL_checknumber(L, 3);
512 bar->value = MAX(bar->min_value, MIN(bar->max_value, bar->value));
514 widget_invalidate_bywidget(*widget);
516 return 0;
519 /** Progressbar widget.
520 * \param L The Lua VM state.
521 * \param token The key token.
522 * \return The number of elements pushed on the stack.
523 * \luastack
524 * \lfield bar_properties_set Set the properties of a bar.
525 * \lfield bar_data_add Add data to a bar.
526 * \lfield gap Gap betweens bars.
527 * \lfield ticks_gap Gap between ticks.
528 * \lfield ticks_count Number of ticks.
529 * \lfield border_padding Border padding.
530 * \lfield border_width Border width.
531 * \lfield width Bars width.
532 * \lfield height Bars height.
533 * \lfield vertical True: draw bar vertically, false: horizontally.
535 static int
536 luaA_progressbar_index(lua_State *L, awesome_token_t token)
538 widget_t **widget = luaA_checkudata(L, 1, "widget");
539 progressbar_data_t *d = (*widget)->data;
541 switch(token)
543 case A_TK_BAR_PROPERTIES_SET:
544 lua_pushcfunction(L, luaA_progressbar_bar_properties_set);
545 break;
546 case A_TK_BAR_DATA_ADD:
547 lua_pushcfunction(L, luaA_progressbar_bar_data_add);
548 break;
549 case A_TK_GAP:
550 lua_pushnumber(L, d->gap);
551 break;
552 case A_TK_TICKS_GAP:
553 lua_pushnumber(L, d->ticks_gap);
554 break;
555 case A_TK_TICKS_COUNT:
556 lua_pushnumber(L, d->ticks_count);
557 break;
558 case A_TK_BORDER_PADDING:
559 lua_pushnumber(L, d->border_padding);
560 break;
561 case A_TK_BORDER_WIDTH:
562 lua_pushnumber(L, d->border_width);
563 break;
564 case A_TK_WIDTH:
565 lua_pushnumber(L, d->width);
566 break;
567 case A_TK_HEIGHT:
568 lua_pushnumber(L, d->height);
569 break;
570 case A_TK_VERTICAL:
571 lua_pushboolean(L, d->vertical);
572 break;
573 default:
574 return 0;
577 return 1;
580 /** Newindex function for progressbar.
581 * \param L The Lua VM state.
582 * \param token The key token.
583 * \return The number of elements pushed on the stack.
585 static int
586 luaA_progressbar_newindex(lua_State *L, awesome_token_t token)
588 widget_t **widget = luaA_checkudata(L, 1, "widget");
589 progressbar_data_t *d = (*widget)->data;
591 switch(token)
593 case A_TK_GAP:
594 d->gap = luaL_checknumber(L, 3);
595 break;
596 case A_TK_TICKS_COUNT:
597 d->ticks_count = luaL_checknumber(L, 3);
598 break;
599 case A_TK_TICKS_GAP:
600 d->ticks_gap = luaL_checknumber(L, 3);
601 break;
602 case A_TK_BORDER_PADDING:
603 d->border_padding = luaL_checknumber(L, 3);
604 break;
605 case A_TK_BORDER_WIDTH:
606 d->border_width = luaL_checknumber(L, 3);
607 break;
608 case A_TK_WIDTH:
609 d->width = luaL_checknumber(L, 3);
610 break;
611 case A_TK_HEIGHT:
612 d->height = luaL_checknumber(L, 3);
613 break;
614 case A_TK_VERTICAL:
615 d->vertical = luaA_checkboolean(L, 3);
616 break;
617 default:
618 return 0;
621 widget_invalidate_bywidget(*widget);
623 return 0;
626 /** Destroy a progressbar.
627 * \param widget The widget to kill.
629 static void
630 progressbar_destructor(widget_t *widget)
632 progressbar_data_t *d = widget->data;
634 bar_array_wipe(&d->bars);
635 p_delete(&d);
638 /** Create a new progressbar.
639 * \param align Alignment of the widget.
640 * \return A brand new progressbar.
642 widget_t *
643 progressbar_new(alignment_t align)
645 widget_t *w;
646 progressbar_data_t *d;
648 w = p_new(widget_t, 1);
649 widget_common_new(w);
650 w->align = align;
651 w->draw = progressbar_draw;
652 w->index = luaA_progressbar_index;
653 w->newindex = luaA_progressbar_newindex;
654 w->destructor = progressbar_destructor;
655 w->geometry = progressbar_geometry;
656 d = w->data = p_new(progressbar_data_t, 1);
658 d->height = 0.80;
659 d->width = 80;
661 d->ticks_gap = 1;
662 d->border_width = 1;
663 d->gap = 2;
665 return w;
668 /* This is used for building documentation. */
669 static const struct luaL_reg awesome_progressbar_meta[] __attribute__ ((unused)) =
671 { "bar_properties_set", luaA_progressbar_bar_properties_set },
672 { "bar_data_add", luaA_progressbar_bar_data_add },
675 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80