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 typedef struct bar_t bar_t
;
30 /** Progressbar bar data structure */
33 /** Title of the data/bar */
35 /** These or lower values won't fill the bar at all*/
37 /** These or higher values fill the bar fully */
39 /** Pointer to value */
41 /** Reverse filling */
43 /** Foreground color */
45 /** Foreground color of turned-off ticks */
47 /** Foreground color when bar is half-full */
49 /** Foreground color when bar is full */
51 /** Background color */
54 xcolor_t border_color
;
55 /** The next and previous bar in the list */
60 * \param bar The bar to annihilate.
63 bar_delete(bar_t
**bar
)
65 p_delete(&(*bar
)->title
);
69 DO_SLIST(bar_t
, bar
, bar_delete
)
71 /** Progressbar private data structure */
74 /** Width of the data_items */
76 /** Pixel between data items (bars) */
78 /** Border width in pixels */
80 /** Padding between border and ticks/bar */
82 /** Gap/distance between the individual ticks */
84 /** Total number of ticks */
86 /** 90 Degree's turned */
88 /** Height 0-1, where 1.0 is height of bar */
94 /** Add a new bar to the progressbar private data structure.
95 * \param d The private data structure.
96 * \param title The graph title.
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
);
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.
127 progressbar_draw(draw_context_t
*ctx
,
128 int screen
__attribute__ ((unused
)),
131 int used
__attribute__ ((unused
)),
132 void *p
__attribute__ ((unused
)),
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 */
139 vector_t color_gradient
;
140 progressbar_data_t
*d
= w
->widget
->data
;
146 for(bar
= d
->bars
; bar
; bar
= bar
->next
)
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
)
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
,
174 /* for a 'reversed' 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
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
;
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);
211 pb_progress
= values_ticks
* unit
- d
->ticks_gap
;
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);
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 */
243 /* invert: top with bottom part */
244 pb_progress
= pb_height
- pb_progress
;
245 color_gradient
.y_offset
= pb_height
;
250 color_gradient
.y
+= pb_height
;
251 color_gradient
.y_offset
= - pb_height
;
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
;
264 draw_rectangle(ctx
, rectangle
, 1.0, true, &bar
->fg_off
);
266 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
267 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
271 if(pb_height
- pb_progress
> 0) /* not filled area */
273 rectangle
.x
= pb_x
+ pb_offset
;
275 rectangle
.width
= pb_width
;
276 rectangle
.height
= pb_height
- pb_progress
;
280 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
281 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
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);
315 pb_progress
= values_ticks
* unit
- d
->ticks_gap
;
320 pb_progress
= (int) (pb_width
* (bar
->value
- bar
->min_value
)
321 / (bar
->max_value
- bar
->min_value
) + 0.5);
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 */
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
;
350 color_gradient
.x_offset
= pb_width
;
356 rectangle
.y
= pb_y
+ pb_offset
;
357 rectangle
.width
= pb_progress
;
358 rectangle
.height
= pb_height
;
362 draw_rectangle(ctx
, rectangle
, 1.0, true, &bar
->fg_off
);
364 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
365 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
378 draw_rectangle_gradient(ctx
, rectangle
, 1.0, true, color_gradient
,
379 &bar
->fg
, &bar
->fg_center
, &bar
->fg_end
);
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
;
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.
408 * \lparam A bar name.
409 * \lparam A table with keys as properties names.
412 luaA_progressbar_bar_properties_set(lua_State
*L
)
415 widget_t
**widget
= luaA_checkudata(L
, 1, "widget");
416 const char *buf
, *title
= luaL_checkstring(L
, 2);
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
))
429 /* no bar found -> create one */
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
,
436 globalconf
.default_screen
,
439 if((buf
= luaA_getopt_lstring(L
, 3, "fg_off", NULL
, &len
)))
440 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
442 globalconf
.default_screen
,
445 if((buf
= luaA_getopt_lstring(L
, 3, "bg", NULL
, &len
)))
446 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
448 globalconf
.default_screen
,
451 if((buf
= luaA_getopt_lstring(L
, 3, "border_color", NULL
, &len
)))
452 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
454 globalconf
.default_screen
,
457 if((buf
= luaA_getopt_lstring(L
, 3, "fg_center", NULL
, &len
)))
458 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
460 globalconf
.default_screen
,
463 if((buf
= luaA_getopt_lstring(L
, 3, "fg_end", NULL
, &len
)))
464 reqs
[++reqs_nbr
] = xcolor_init_unchecked(globalconf
.connection
,
466 globalconf
.default_screen
,
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
);
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.
499 * \lparam A bar name.
500 * \lparam A data value.
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
;
510 /* check if this section is defined already */
511 for(bar
= d
->bars
; bar
; bar
= bar
->next
)
512 if(!a_strcmp(title
, bar
->title
))
515 /* no bar found -> create one */
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
);
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.
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
;
540 case A_TK_BAR_PROPERTIES_SET
:
541 lua_pushcfunction(L
, luaA_progressbar_bar_properties_set
);
543 case A_TK_BAR_DATA_ADD
:
544 lua_pushcfunction(L
, luaA_progressbar_bar_data_add
);
547 lua_pushnumber(L
, d
->gap
);
550 lua_pushnumber(L
, d
->ticks_gap
);
552 case A_TK_TICKS_COUNT
:
553 lua_pushnumber(L
, d
->ticks_count
);
555 case A_TK_BORDER_PADDING
:
556 lua_pushnumber(L
, d
->border_padding
);
558 case A_TK_BORDER_WIDTH
:
559 lua_pushnumber(L
, d
->border_width
);
562 lua_pushnumber(L
, d
->width
);
565 lua_pushnumber(L
, d
->height
);
568 lua_pushnumber(L
, d
->vertical
);
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.
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
;
591 d
->gap
= luaL_checknumber(L
, 3);
593 case A_TK_TICKS_COUNT
:
594 d
->ticks_count
= luaL_checknumber(L
, 3);
597 d
->ticks_gap
= luaL_checknumber(L
, 3);
599 case A_TK_BORDER_PADDING
:
600 d
->border_padding
= luaL_checknumber(L
, 3);
602 case A_TK_BORDER_WIDTH
:
603 d
->border_width
= luaL_checknumber(L
, 3);
606 d
->width
= luaL_checknumber(L
, 3);
609 d
->height
= luaL_checknumber(L
, 3);
612 d
->vertical
= luaA_checkboolean(L
, 3);
618 widget_invalidate_bywidget(*widget
);
623 /** Destroy a progressbar.
624 * \param widget The widget to kill.
627 progressbar_destructor(widget_t
*widget
)
629 progressbar_data_t
*d
= widget
->data
;
631 bar_list_wipe(&d
->bars
);
635 /** Create a new progressbar.
636 * \param align Alignment of the widget.
637 * \return A brand new progressbar.
640 progressbar_new(alignment_t align
)
643 progressbar_data_t
*d
;
645 w
= p_new(widget_t
, 1);
646 widget_common_new(w
);
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);
664 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80