screen: reset statusbar and windows properly on padding changes
[awesome.git] / widgets / progressbar.c
blob6d8eb9a7e594c0976109902b5cd14d2d6f879a0b
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 typedef struct bar_t bar_t;
30 /** Progressbar bar data structure */
31 struct bar_t
33 /** Title of the data/bar */
34 char *title;
35 /** These or lower values won't fill the bar at all*/
36 float min_value;
37 /** These or higher values fill the bar fully */
38 float max_value;
39 /** Pointer to value */
40 float value;
41 /** Reverse filling */
42 bool reverse;
43 /** Foreground color */
44 xcolor_t fg;
45 /** Foreground color of turned-off ticks */
46 xcolor_t fg_off;
47 /** Foreground color when bar is half-full */
48 xcolor_t fg_center;
49 /** Foreground color when bar is full */
50 xcolor_t fg_end;
51 /** Background color */
52 xcolor_t bg;
53 /** Border color */
54 xcolor_t border_color;
55 /** The next and previous bar in the list */
56 bar_t *next, *prev;
59 /** Delete a bar.
60 * \param bar The bar to annihilate.
62 static void
63 bar_delete(bar_t **bar)
65 p_delete(&(*bar)->title);
66 p_delete(bar);
69 DO_SLIST(bar_t, bar, bar_delete)
71 /** Progressbar private data structure */
72 typedef struct
74 /** Width of the data_items */
75 int width;
76 /** Pixel between data items (bars) */
77 int gap;
78 /** Border width in pixels */
79 int border_width;
80 /** Padding between border and ticks/bar */
81 int border_padding;
82 /** Gap/distance between the individual ticks */
83 int ticks_gap;
84 /** Total number of ticks */
85 int ticks_count;
86 /** 90 Degree's turned */
87 bool vertical;
88 /** Height 0-1, where 1.0 is height of bar */
89 float height;
90 /** The bars */
91 bar_t *bars;
92 } progressbar_data_t;
94 /** Add a new bar to the progressbar private data structure.
95 * \param d The private data structure.
96 * \param title The graph title.
98 static bar_t *
99 progressbar_bar_add(progressbar_data_t *d, const char *title)
101 bar_t *bar = p_new(bar_t, 1);
103 bar->title = a_strdup(title);
104 bar->fg = globalconf.colors.fg;
105 bar->fg_off = globalconf.colors.bg;
106 bar->bg = globalconf.colors.bg;
107 bar->border_color = globalconf.colors.fg;
108 bar->max_value = 100.0;
110 /* append the bar in the list */
111 bar_list_append(&d->bars, bar);
113 return bar;
116 /** Draw a progressbar.
117 * \param ctx The draw context.
118 * \param screen The screen we're drawing for.
119 * \param w The widget node we're drawing for.
120 * \param offset Offset to draw at.
121 * \param used Space already used.
122 * \param object The object pointer we're drawing onto.
123 * \param type The object type.
124 * \return The width used.
126 static int
127 progressbar_draw(draw_context_t *ctx,
128 int screen __attribute__ ((unused)),
129 widget_node_t *w,
130 int offset,
131 int used __attribute__ ((unused)),
132 void *p __attribute__ ((unused)),
133 awesome_type_t type)
135 /* pb_.. values points to the widget inside a potential border */
136 int values_ticks, pb_x, pb_y, pb_height, pb_width, pb_progress, pb_offset;
137 int unit = 0, nbbars = 0; /* tick + gap */
138 area_t rectangle;
139 vector_t color_gradient;
140 progressbar_data_t *d = w->widget->data;
141 bar_t *bar;
143 if(!d->bars)
144 return 0;
146 for(bar = d->bars; bar; bar = bar->next)
147 nbbars++;
149 if(d->vertical)
151 pb_width = (int) ((d->width - 2 * (d->border_width + d->border_padding) * nbbars
152 - d->gap * (nbbars - 1)) / nbbars);
153 w->area.width = nbbars
154 * (pb_width + 2 * (d->border_width + d->border_padding)
155 + d->gap) - d->gap;
157 else
159 pb_width = d->width - 2 * (d->border_width + d->border_padding);
160 if(d->ticks_count && d->ticks_gap)
162 unit = (pb_width + d->ticks_gap) / d->ticks_count;
163 pb_width = unit * d->ticks_count - d->ticks_gap; /* rounded to match ticks... */
165 w->area.width = pb_width + 2 * (d->border_width + d->border_padding);
168 w->area.x = widget_calculate_offset(ctx->width,
169 w->area.width,
170 offset,
171 w->widget->align);
172 w->area.y = 0;
174 /* for a 'reversed' progressbar:
175 * basic progressbar:
176 * 1. the full space gets the size of the formerly empty one
177 * 2. the pattern must be mirrored
178 * 3. the formerly 'empty' side gets drawed with fg colors, the 'full' with bg-color
180 * ticks:
181 * 1. round the values to a full tick accordingly
182 * 2. finally draw the gaps
185 pb_x = w->area.x + d->border_width + d->border_padding;
186 pb_offset = 0;
188 if(d->vertical)
190 /** \todo maybe prevent to calculate that stuff below over and over again
191 * (->use static-values) */
192 pb_height = (int) (ctx->height * d->height + 0.5)
193 - 2 * (d->border_width + d->border_padding);
194 if(d->ticks_count && d->ticks_gap)
196 /* '+ d->ticks_gap' because a unit includes a ticks + ticks_gap */
197 unit = (pb_height + d->ticks_gap) / d->ticks_count;
198 pb_height = unit * d->ticks_count - d->ticks_gap;
201 pb_y = w->area.y + ((int) (ctx->height * (1 - d->height)) / 2)
202 + d->border_width + d->border_padding;
204 for(bar = d->bars; bar; bar = bar->next)
206 if(d->ticks_count && d->ticks_gap)
208 values_ticks = (int)(d->ticks_count * (bar->value - bar->min_value)
209 / (bar->max_value - bar->min_value) + 0.5);
210 if(values_ticks)
211 pb_progress = values_ticks * unit - d->ticks_gap;
212 else
213 pb_progress = 0;
215 else
216 /* e.g.: min = 50; max = 56; 53 should show 50% graph
217 * (53(val) - 50(min) / (56(max) - 50(min) = 3 / 5 = 0.5 = 50%
218 * round that ( + 0.5 and (int)) and finally multiply with height
220 pb_progress = (int) (pb_height * (bar->value - bar->min_value)
221 / (bar->max_value - bar->min_value) + 0.5);
223 if(d->border_width)
225 /* border rectangle */
226 rectangle.x = pb_x + pb_offset - d->border_width - d->border_padding;
227 rectangle.y = pb_y - d->border_width - d->border_padding;
228 rectangle.width = pb_width + 2 * (d->border_padding + d->border_width);
229 rectangle.height = pb_height + 2 * (d->border_padding + d->border_width);
231 if(d->border_padding)
232 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
233 draw_rectangle(ctx, rectangle, d->border_width, false, &bar->border_color);
236 color_gradient.x = pb_x;
237 color_gradient.x_offset = 0;
238 color_gradient.y = pb_y;
240 /* new value/progress in px + pattern setup */
241 if(bar->reverse)
243 /* invert: top with bottom part */
244 pb_progress = pb_height - pb_progress;
245 color_gradient.y_offset = pb_height;
247 else
249 /* bottom to top */
250 color_gradient.y += pb_height;
251 color_gradient.y_offset = - pb_height;
254 /* bottom part */
255 if(pb_progress > 0)
257 rectangle.x = pb_x + pb_offset;
258 rectangle.y = pb_y + pb_height - pb_progress;
259 rectangle.width = pb_width;
260 rectangle.height = pb_progress;
262 /* fg color */
263 if(bar->reverse)
264 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
265 else
266 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
267 &bar->fg, &bar->fg_center, &bar->fg_end);
270 /* top part */
271 if(pb_height - pb_progress > 0) /* not filled area */
273 rectangle.x = pb_x + pb_offset;
274 rectangle.y = pb_y;
275 rectangle.width = pb_width;
276 rectangle.height = pb_height - pb_progress;
278 /* bg color */
279 if(bar->reverse)
280 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
281 &bar->fg, &bar->fg_center, &bar->fg_end);
282 else
283 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
285 /* draw gaps TODO: improve e.g all in one */
286 if(d->ticks_count && d->ticks_gap)
288 rectangle.width = pb_width;
289 rectangle.height = d->ticks_gap;
290 rectangle.x = pb_x + pb_offset;
291 for(rectangle.y = pb_y + (unit - d->ticks_gap);
292 pb_y + pb_height - d->ticks_gap >= rectangle.y;
293 rectangle.y += unit)
294 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
296 pb_offset += pb_width + d->gap + 2 * (d->border_width + d->border_padding);
299 else /* a horizontal progressbar */
301 pb_height = (int) ((ctx->height * d->height
302 - nbbars * 2 * (d->border_width + d->border_padding)
303 - (d->gap * (nbbars - 1))) / nbbars + 0.5);
304 pb_y = w->area.y + ((int) (ctx->height * (1 - d->height)) / 2)
305 + d->border_width + d->border_padding;
307 for(bar = d->bars; bar; bar = bar->next)
309 if(d->ticks_count && d->ticks_gap)
311 /* +0.5 rounds up ticks -> turn on a tick when half of it is reached */
312 values_ticks = (int)(d->ticks_count * (bar->value - bar->min_value)
313 / (bar->max_value - bar->min_value) + 0.5);
314 if(values_ticks)
315 pb_progress = values_ticks * unit - d->ticks_gap;
316 else
317 pb_progress = 0;
319 else
320 pb_progress = (int) (pb_width * (bar->value - bar->min_value)
321 / (bar->max_value - bar->min_value) + 0.5);
323 if(d->border_width)
325 /* border rectangle */
326 rectangle.x = pb_x - d->border_width - d->border_padding;
327 rectangle.y = pb_y + pb_offset - d->border_width - d->border_padding;
328 rectangle.width = pb_width + 2 * (d->border_padding + d->border_width);
329 rectangle.height = pb_height + 2 * (d->border_padding + d->border_width);
331 if(d->border_padding)
332 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
333 draw_rectangle(ctx, rectangle, d->border_width, false, &bar->border_color);
336 color_gradient.y = pb_y;
337 color_gradient.y_offset = 0;
338 color_gradient.x = pb_x;
340 /* new value/progress in px + pattern setup */
341 if(bar->reverse)
343 /* reverse: right to left */
344 pb_progress = pb_width - pb_progress;
345 color_gradient.x += pb_width;
346 color_gradient.x_offset = - pb_width;
348 else
349 /* left to right */
350 color_gradient.x_offset = pb_width;
352 /* left part */
353 if(pb_progress > 0)
355 rectangle.x = pb_x;
356 rectangle.y = pb_y + pb_offset;
357 rectangle.width = pb_progress;
358 rectangle.height = pb_height;
360 /* fg color */
361 if(bar->reverse)
362 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
363 else
364 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
365 &bar->fg, &bar->fg_center, &bar->fg_end);
368 /* right part */
369 if(pb_width - pb_progress > 0)
371 rectangle.x = pb_x + pb_progress;
372 rectangle.y = pb_y + pb_offset;
373 rectangle.width = pb_width - pb_progress;
374 rectangle.height = pb_height;
376 /* bg color */
377 if(bar->reverse)
378 draw_rectangle_gradient(ctx, rectangle, 1.0, true, color_gradient,
379 &bar->fg, &bar->fg_center, &bar->fg_end);
380 else
381 draw_rectangle(ctx, rectangle, 1.0, true, &bar->fg_off);
383 /* draw gaps TODO: improve e.g all in one */
384 if(d->ticks_count && d->ticks_gap)
386 rectangle.width = d->ticks_gap;
387 rectangle.height = pb_height;
388 rectangle.y = pb_y + pb_offset;
389 for(rectangle.x = pb_x + (unit - d->ticks_gap);
390 pb_x + pb_width - d->ticks_gap >= rectangle.x;
391 rectangle.x += unit)
392 draw_rectangle(ctx, rectangle, 1.0, true, &bar->bg);
395 pb_offset += pb_height + d->gap + 2 * (d->border_width + d->border_padding);
399 w->area.height = ctx->height;
400 return w->area.width;
403 /** Set various progressbar bars properties:
404 * \param L The Lua VM state.
405 * \return The number of elements pushed on the stack.
406 * \luastack
407 * \lvalue A widget.
408 * \lparam A bar name.
409 * \lparam A table with keys as properties names.
411 static int
412 luaA_progressbar_bar_properties_set(lua_State *L)
414 size_t len;
415 widget_t **widget = luaA_checkudata(L, 1, "widget");
416 const char *buf, *title = luaL_checkstring(L, 2);
417 bar_t *bar;
418 progressbar_data_t *d = (*widget)->data;
419 xcolor_init_request_t reqs[6];
420 int8_t i, reqs_nbr = -1;
422 luaA_checktable(L, 3);
424 /* check if this section is defined already */
425 for(bar = d->bars; bar; bar = bar->next)
426 if(!a_strcmp(title, bar->title))
427 break;
429 /* no bar found -> create one */
430 if(!bar)
431 bar = progressbar_bar_add(d, title);
433 if((buf = luaA_getopt_lstring(L, 3, "fg", NULL, &len)))
434 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
435 &bar->fg,
436 globalconf.default_screen,
437 buf, len);
439 if((buf = luaA_getopt_lstring(L, 3, "fg_off", NULL, &len)))
440 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
441 &bar->fg_off,
442 globalconf.default_screen,
443 buf, len);
445 if((buf = luaA_getopt_lstring(L, 3, "bg", NULL, &len)))
446 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
447 &bar->bg,
448 globalconf.default_screen,
449 buf, len);
451 if((buf = luaA_getopt_lstring(L, 3, "border_color", NULL, &len)))
452 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
453 &bar->border_color,
454 globalconf.default_screen,
455 buf, len);
457 if((buf = luaA_getopt_lstring(L, 3, "fg_center", NULL, &len)))
458 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
459 &bar->fg_center,
460 globalconf.default_screen,
461 buf, len);
463 if((buf = luaA_getopt_lstring(L, 3, "fg_end", NULL, &len)))
464 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
465 &bar->fg_end,
466 globalconf.default_screen,
467 buf, len);
469 bar->min_value = luaA_getopt_number(L, 3, "min_value", bar->min_value);
470 /* hack to prevent max_value beeing less than min_value
471 * and also preventing a division by zero when both are equal */
472 if(bar->max_value <= bar->min_value)
473 bar->max_value = bar->max_value + 0.0001;
474 /* force a actual value into the newly possible range */
475 if(bar->value < bar->min_value)
476 bar->value = bar->min_value;
478 bar->max_value = luaA_getopt_number(L, 3, "max_value", bar->max_value);
479 if(bar->min_value >= bar->max_value)
480 bar->min_value = bar->max_value - 0.0001;
481 if(bar->value > bar->max_value)
482 bar->value = bar->max_value;
484 bar->reverse = luaA_getopt_boolean(L, 3, "reverse", bar->reverse);
486 for(i = 0; i <= reqs_nbr; i++)
487 xcolor_init_reply(globalconf.connection, reqs[i]);
489 widget_invalidate_bywidget(*widget);
491 return 0;
494 /** Add a value to a progressbar bar.
495 * \param L The Lua VM state.
496 * \return The number of elements pushed on the stack.
497 * \luastack
498 * \lvalue A widget.
499 * \lparam A bar name.
500 * \lparam A data value.
502 static int
503 luaA_progressbar_bar_data_add(lua_State *L)
505 widget_t **widget = luaA_checkudata(L, 1, "widget");
506 const char *title = luaL_checkstring(L, 2);
507 progressbar_data_t *d = (*widget)->data;
508 bar_t *bar;
510 /* check if this section is defined already */
511 for(bar = d->bars; bar; bar = bar->next)
512 if(!a_strcmp(title, bar->title))
513 break;
515 /* no bar found -> create one */
516 if(!bar)
517 bar = progressbar_bar_add(d, title);
519 bar->value = luaL_checknumber(L, 3);
520 bar->value = MAX(bar->min_value, MIN(bar->max_value, bar->value));
522 widget_invalidate_bywidget(*widget);
524 return 0;
527 /** Progressbar widget.
528 * \param L The Lua VM state.
529 * \param token The key token.
530 * \return The number of elements pushed on the stack.
532 static int
533 luaA_progressbar_index(lua_State *L, awesome_token_t token)
535 widget_t **widget = luaA_checkudata(L, 1, "widget");
536 progressbar_data_t *d = (*widget)->data;
538 switch(token)
540 case A_TK_BAR_PROPERTIES_SET:
541 lua_pushcfunction(L, luaA_progressbar_bar_properties_set);
542 break;
543 case A_TK_BAR_DATA_ADD:
544 lua_pushcfunction(L, luaA_progressbar_bar_data_add);
545 break;
546 case A_TK_GAP:
547 lua_pushnumber(L, d->gap);
548 break;
549 case A_TK_TICKS_GAP:
550 lua_pushnumber(L, d->ticks_gap);
551 break;
552 case A_TK_TICKS_COUNT:
553 lua_pushnumber(L, d->ticks_count);
554 break;
555 case A_TK_BORDER_PADDING:
556 lua_pushnumber(L, d->border_padding);
557 break;
558 case A_TK_BORDER_WIDTH:
559 lua_pushnumber(L, d->border_width);
560 break;
561 case A_TK_WIDTH:
562 lua_pushnumber(L, d->width);
563 break;
564 case A_TK_HEIGHT:
565 lua_pushnumber(L, d->height);
566 break;
567 case A_TK_VERTICAL:
568 lua_pushnumber(L, d->vertical);
569 break;
570 default:
571 return 0;
574 return 1;
577 /** Newindex function for progressbar.
578 * \param L The Lua VM state.
579 * \param token The key token.
580 * \return The number of elements pushed on the stack.
582 static int
583 luaA_progressbar_newindex(lua_State *L, awesome_token_t token)
585 widget_t **widget = luaA_checkudata(L, 1, "widget");
586 progressbar_data_t *d = (*widget)->data;
588 switch(token)
590 case A_TK_GAP:
591 d->gap = luaL_checknumber(L, 3);
592 break;
593 case A_TK_TICKS_COUNT:
594 d->ticks_count = luaL_checknumber(L, 3);
595 break;
596 case A_TK_TICKS_GAP:
597 d->ticks_gap = luaL_checknumber(L, 3);
598 break;
599 case A_TK_BORDER_PADDING:
600 d->border_padding = luaL_checknumber(L, 3);
601 break;
602 case A_TK_BORDER_WIDTH:
603 d->border_width = luaL_checknumber(L, 3);
604 break;
605 case A_TK_WIDTH:
606 d->width = luaL_checknumber(L, 3);
607 break;
608 case A_TK_HEIGHT:
609 d->height = luaL_checknumber(L, 3);
610 break;
611 case A_TK_VERTICAL:
612 d->vertical = luaA_checkboolean(L, 3);
613 break;
614 default:
615 return 0;
618 widget_invalidate_bywidget(*widget);
620 return 0;
623 /** Destroy a progressbar.
624 * \param widget The widget to kill.
626 static void
627 progressbar_destructor(widget_t *widget)
629 progressbar_data_t *d = widget->data;
631 bar_list_wipe(&d->bars);
632 p_delete(&d);
635 /** Create a new progressbar.
636 * \param align Alignment of the widget.
637 * \return A brand new progressbar.
639 widget_t *
640 progressbar_new(alignment_t align)
642 widget_t *w;
643 progressbar_data_t *d;
645 w = p_new(widget_t, 1);
646 widget_common_new(w);
647 w->align = align;
648 w->draw = progressbar_draw;
649 w->index = luaA_progressbar_index;
650 w->newindex = luaA_progressbar_newindex;
651 w->destructor = progressbar_destructor;
652 d = w->data = p_new(progressbar_data_t, 1);
654 d->height = 0.80;
655 d->width = 80;
657 d->ticks_gap = 1;
658 d->border_width = 1;
659 d->gap = 2;
661 return w;
664 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80