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"
26 extern awesome_t globalconf
;
28 /** Progressbar bar data structure */
31 /** Title of the data/bar */
33 /** These or lower values won't fill the bar at all*/
35 /** These or higher values fill the bar fully */
37 /** Pointer to value */
39 /** Reverse filling */
41 /** Foreground color */
43 /** Foreground color of turned-off ticks */
45 /** Foreground color when bar is half-full */
47 /** Foreground color when bar is full */
49 /** Background color */
52 xcolor_t border_color
;
56 * \param bar The bar to annihilate.
59 bar_delete(bar_t
*bar
)
61 p_delete(&bar
->title
);
64 DO_ARRAY(bar_t
, bar
, bar_delete
)
66 /** Progressbar private data structure */
69 /** Width of the data_items */
71 /** Pixel between data items (bars) */
73 /** Border width in pixels */
75 /** Padding between border and ticks/bar */
77 /** Gap/distance between the individual ticks */
79 /** Total number of ticks */
81 /** 90 Degree's turned */
83 /** Height 0-1, where 1.0 is height of bar */
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.
95 progressbar_bar_add(bar_array_t
*bars
, const char *title
)
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.
120 progressbar_bar_get(bar_array_t
*bars
, const char *title
)
124 /* check if this section is defined already */
125 for(int j
= 0; j
< bars
->len
; j
++)
128 if(!a_strcmp(title
, bar
->title
))
132 /* no bar found -> create one */
133 return progressbar_bar_add(bars
, title
);
137 progressbar_geometry(widget_t
*widget
, int screen
, int height
, int width
)
140 progressbar_data_t
*d
= widget
->data
;
142 geometry
.height
= height
;
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
)
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
);
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.
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 */
182 vector_t color_gradient
;
183 progressbar_data_t
*d
= widget
->data
;
188 /* for a 'reversed' 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
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
;
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);
229 pb_progress
= values_ticks
* unit
- d
->ticks_gap
;
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);
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 */
261 /* invert: top with bottom part */
262 pb_progress
= pb_height
- pb_progress
;
263 color_gradient
.y_offset
= pb_height
;
268 color_gradient
.y
+= pb_height
;
269 color_gradient
.y_offset
= - pb_height
;
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
;
282 draw_rectangle(ctx
, rectangle
, 1.0, true, &bar
->fg_off
);
284 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
285 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
289 if(pb_height
- pb_progress
> 0) /* not filled area */
291 rectangle
.x
= pb_x
+ pb_offset
;
293 rectangle
.width
= pb_width
;
294 rectangle
.height
= pb_height
- pb_progress
;
298 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
299 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
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);
342 pb_progress
= values_ticks
* unit
- d
->ticks_gap
;
347 pb_progress
= (int) (pb_width
* (bar
->value
- bar
->min_value
)
348 / (bar
->max_value
- bar
->min_value
) + 0.5);
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 */
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
;
377 color_gradient
.x_offset
= pb_width
;
383 rectangle
.y
= pb_y
+ pb_offset
;
384 rectangle
.width
= pb_progress
;
385 rectangle
.height
= pb_height
;
389 draw_rectangle(ctx
, rectangle
, 1.0, true, &bar
->fg_off
);
391 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
392 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
405 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
406 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
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.
432 * \lparam A bar name.
433 * \lparam A table with keys as properties names.
436 luaA_progressbar_bar_properties_set(lua_State
*L
)
439 widget_t
**widget
= luaA_checkudata(L
, 1, "widget");
440 const char *buf
, *title
= luaL_checkstring(L
, 2);
442 progressbar_data_t
*d
= (*widget
)->data
;
443 xcolor_init_request_t reqs
[6];
444 int 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
);
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.
498 * \lparam A bar name.
499 * \lparam A data value.
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
;
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
);
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.
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.
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
;
543 case A_TK_BAR_PROPERTIES_SET
:
544 lua_pushcfunction(L
, luaA_progressbar_bar_properties_set
);
546 case A_TK_BAR_DATA_ADD
:
547 lua_pushcfunction(L
, luaA_progressbar_bar_data_add
);
550 lua_pushnumber(L
, d
->gap
);
553 lua_pushnumber(L
, d
->ticks_gap
);
555 case A_TK_TICKS_COUNT
:
556 lua_pushnumber(L
, d
->ticks_count
);
558 case A_TK_BORDER_PADDING
:
559 lua_pushnumber(L
, d
->border_padding
);
561 case A_TK_BORDER_WIDTH
:
562 lua_pushnumber(L
, d
->border_width
);
565 lua_pushnumber(L
, d
->width
);
568 lua_pushnumber(L
, d
->height
);
571 lua_pushboolean(L
, d
->vertical
);
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.
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
;
594 d
->gap
= luaL_checknumber(L
, 3);
596 case A_TK_TICKS_COUNT
:
597 d
->ticks_count
= luaL_checknumber(L
, 3);
600 d
->ticks_gap
= luaL_checknumber(L
, 3);
602 case A_TK_BORDER_PADDING
:
603 d
->border_padding
= luaL_checknumber(L
, 3);
605 case A_TK_BORDER_WIDTH
:
606 d
->border_width
= luaL_checknumber(L
, 3);
609 d
->width
= luaL_checknumber(L
, 3);
612 d
->height
= luaL_checknumber(L
, 3);
615 d
->vertical
= luaA_checkboolean(L
, 3);
621 widget_invalidate_bywidget(*widget
);
626 /** Destroy a progressbar.
627 * \param widget The widget to kill.
630 progressbar_destructor(widget_t
*widget
)
632 progressbar_data_t
*d
= widget
->data
;
634 bar_array_wipe(&d
->bars
);
638 /** Create a new progressbar.
639 * \param w The widget to initialize.
640 * \return A brand new progressbar.
643 widget_progressbar(widget_t
*w
)
645 w
->draw
= progressbar_draw
;
646 w
->index
= luaA_progressbar_index
;
647 w
->newindex
= luaA_progressbar_newindex
;
648 w
->destructor
= progressbar_destructor
;
649 w
->geometry
= progressbar_geometry
;
651 progressbar_data_t
*d
= w
->data
= p_new(progressbar_data_t
, 1);
663 /* This is used for building documentation. */
664 static const struct luaL_reg awesome_progressbar_meta
[] __attribute__ ((unused
)) =
666 { "bar_properties_set", luaA_progressbar_bar_properties_set
},
667 { "bar_data_add", luaA_progressbar_bar_data_add
},
670 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80