xref: /freebsd/contrib/bsddialog/lib/barbox.c (revision e92ffd9b626833ebdbf2742c8ffddc6cd94b963e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 
30 #include <ctype.h>
31 #ifdef PORTNCURSES
32 #include <ncurses/ncurses.h>
33 #else
34 #include <ncurses.h>
35 #endif
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 
40 #include "bsddialog.h"
41 #include "bsddialog_progressview.h"
42 #include "lib_util.h"
43 #include "bsddialog_theme.h"
44 
45 #define BARMARGIN	3
46 #define MINBARWIDTH	10
47 #define MINWIDTH	(VBORDERS + MINBARWIDTH + BARMARGIN * 2)
48 #define MINHEIGHT	7 /* without text */
49 
50 /* "Bar": gauge - mixedgauge - rangebox - pause - progressview */
51 
52 bool bsddialog_interruptprogview;
53 bool bsddialog_abortprogview;
54 int  bsddialog_total_progview;
55 
56 extern struct bsddialog_theme t;
57 
58 static void
59 draw_perc_bar(WINDOW *win, int y, int x, int size, int perc, bool withlabel,
60     int label)
61 {
62 	char labelstr[128];
63 	int i, blue_x, color;
64 
65 	blue_x = (int)((perc*(size))/100);
66 
67 	wmove(win, y, x);
68 	for (i = 0; i < size; i++) {
69 		color = (i <= blue_x) ? t.bar.f_color : t.bar.color;
70 		wattron(win, color);
71 		waddch(win, ' ');
72 		wattroff(win, color);
73 	}
74 
75 	if (withlabel)
76 		sprintf(labelstr, "%d", label);
77 	else
78 		sprintf(labelstr, "%3d%%", perc);
79 	wmove(win, y, x + size/2 - 2);
80 	for (i=0; i < (int) strlen(labelstr); i++) {
81 		color = (blue_x + 1 <= size/2 - (int)strlen(labelstr)/2 + i ) ?
82 		    t.bar.color : t.bar.f_color;
83 		wattron(win, color);
84 		waddch(win, labelstr[i]);
85 		wattroff(win, color);
86 	}
87 }
88 
89 static int
90 bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
91     char *text, struct buttons *bs)
92 {
93 	int maxword, maxline, nlines, buttonswidth;
94 
95 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
96 		return BSDDIALOG_ERROR;
97 
98 	buttonswidth = 0;
99 	if (bs != NULL) { /* gauge has not buttons */
100 		buttonswidth= bs->nbuttons * bs->sizebutton;
101 		if (bs->nbuttons > 0)
102 			buttonswidth += (bs->nbuttons-1) * t.button.space;
103 	}
104 
105 	if (cols == BSDDIALOG_AUTOSIZE) {
106 		*w = VBORDERS;
107 		/* buttons size */
108 		*w += buttonswidth;
109 		/* bar size */
110 		*w = MAX(*w, MINWIDTH);
111 		/* text size*/
112 		*w = MAX((int)(maxline + VBORDERS + t.text.hmargin * 2), *w);
113 		/* conf.auto_minwidth */
114 		*w = MAX(*w, (int)conf->auto_minwidth);
115 		/* avoid terminal overflow */
116 		*w = MIN(*w, widget_max_width(conf));
117 	}
118 
119 	if (rows == BSDDIALOG_AUTOSIZE) {
120 		*h = MINHEIGHT;
121 		if (maxword > 0)
122 			*h += 1;
123 		/* conf.auto_minheight */
124 		*h = MAX(*h, (int)conf->auto_minheight);
125 		/* avoid terminal overflow */
126 		*h = MIN(*h, widget_max_height(conf));
127 	}
128 
129 	return (0);
130 }
131 
132 static int
133 bar_checksize(char *text, int rows, int cols, struct buttons *bs)
134 {
135 	int minheight, minwidth;
136 
137 	minwidth = 0;
138 	if (bs != NULL) { /* gauge has not buttons */
139 		minwidth = bs->nbuttons * bs->sizebutton;
140 		if (bs->nbuttons > 0)
141 			minwidth += (bs->nbuttons-1) * t.button.space;
142 	}
143 	minwidth = MAX(minwidth + VBORDERS, MINBARWIDTH);
144 
145 	if (cols< minwidth)
146 		RETURN_ERROR("Few cols for this widget");
147 
148 	minheight = MINHEIGHT + ((text != NULL && strlen(text) > 0) ? 1 : 0);
149 	if (rows < minheight)
150 		RETURN_ERROR("Few rows for this mixedgauge");
151 
152 	return 0;
153 }
154 
155 int
156 bsddialog_gauge(struct bsddialog_conf *conf, char* text, int rows, int cols,
157     unsigned int perc)
158 {
159 	WINDOW *widget, *textpad, *bar, *shadow;
160 	char input[2048], ntext[2048], *pntext;
161 	int y, x, h, w, htextpad;
162 	bool mainloop;
163 
164 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
165 		return BSDDIALOG_ERROR;
166 	if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0)
167 		return BSDDIALOG_ERROR;
168 	if (bar_checksize(text, h, w, NULL) != 0)
169 		return BSDDIALOG_ERROR;
170 	if (set_widget_position(conf, &y, &x, h, w) != 0)
171 		return BSDDIALOG_ERROR;
172 
173 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
174 	    &textpad, &htextpad, text, false) != 0)
175 		return BSDDIALOG_ERROR;
176 
177 	bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED);
178 
179 	mainloop = true;
180 	while (mainloop) {
181 		wrefresh(widget);
182 		prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-4,
183 		    x+w-1-t.text.hmargin);
184 		draw_perc_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/);
185 		wrefresh(bar);
186 
187 		while (true) {
188 			scanf("%s", input);
189 			if (strcmp(input,"EOF") == 0) {
190 				mainloop = false;
191 				break;
192 			}
193 			if (strcmp(input,"XXX") == 0)
194 				break;
195 		}
196 		scanf("%d", &perc);
197 		perc = perc < 0 ? 0 : perc;
198 		perc = perc > 100 ? 100 : perc;
199 		htextpad = 1;
200 		wclear(textpad);
201 		pntext = &ntext[0];
202 		ntext[0] = '\0';
203 		while (true) {
204 			scanf("%s", input);
205 			if (strcmp(input,"EOF") == 0) {
206 				mainloop = false;
207 				break;
208 			}
209 			if (strcmp(input,"XXX") == 0)
210 				break;
211 			pntext[0] = ' ';
212 			pntext++;
213 			strcpy(pntext, input);
214 			pntext += strlen(input);
215 		}
216 		print_textpad(conf, textpad, &htextpad, w-2-t.text.hmargin*2,
217 		    ntext);
218 	}
219 
220 	delwin(bar);
221 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
222 
223 	return BSDDIALOG_OK;
224 }
225 
226 
227 /* Mixedgauge */
228 static int
229 mixedgauge(struct bsddialog_conf *conf, char* text, int rows, int cols,
230     unsigned int mainperc, unsigned int nminibars, char **minilabels,
231     int *minipercs, bool color)
232 {
233 	WINDOW *widget, *textpad, *bar, *shadow;
234 	int i, output, miniperc, y, x, h, w, max_minbarlen;
235 	int maxword, maxline, nlines, htextpad, ypad;
236 	int colorperc, red, green;
237 	char states[12][14] = {
238 	    "  Succeeded  ", /*  0  */
239 	    "   Failed    ", /*  1  */
240 	    "   Passed    ", /*  2  */
241 	    "  Completed  ", /*  3  */
242 	    "   Checked   ", /*  4  */
243 	    "    Done     ", /*  5  */
244 	    "   Skipped   ", /*  6  */
245 	    " In Progress ", /*  7  */
246 	    "(blank)      ", /*  8  */
247 	    "     N/A     ", /*  9  */
248 	    "   Pending   ", /* 10  */
249 	    "   UNKNOWN   ", /* 10+ */
250 	};
251 
252 	red   = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED,  BSDDIALOG_BOLD);
253 	green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
254 
255 	max_minbarlen = 0;
256 	for (i=0; i < (int)nminibars; i++)
257 		max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i]));
258 	max_minbarlen += 3 + 16 /* seps + [...] or mainbar */;
259 
260 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
261 		return BSDDIALOG_ERROR;
262 
263 	/* mixedgauge autosize */
264 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
265 		return BSDDIALOG_ERROR;
266 
267 	if (cols == BSDDIALOG_AUTOSIZE) {
268 		w = max_minbarlen + HBORDERS;
269 		w = MAX(w, maxline + 4);
270 		w = MAX(w, (int)conf->auto_minwidth);
271 		w = MIN(w, widget_max_width(conf) - 1);
272 	}
273 	if (rows == BSDDIALOG_AUTOSIZE) {
274 		h = 5; /* borders + mainbar */
275 		h += nminibars;
276 		h += (strlen(text) > 0 ? 3 : 0);
277 		h = MAX(h, (int)conf->auto_minheight);
278 		h = MIN(h, widget_max_height(conf) -1);
279 	}
280 
281 	/* mixedgauge checksize */
282 	if (w < max_minbarlen + 2)
283 		RETURN_ERROR("Few cols for this mixedgauge");
284 	if (h < 5 + (int)nminibars + (strlen(text) > 0 ? 1 : 0))
285 		RETURN_ERROR("Few rows for this mixedgauge");
286 
287 	if (set_widget_position(conf, &y, &x, h, w) != 0)
288 		return BSDDIALOG_ERROR;
289 
290 	output = new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w,
291 	    RAISED, &textpad, &htextpad, text, false);
292 	if (output == BSDDIALOG_ERROR)
293 		return output;
294 
295 	/* mini bars */
296 	for (i=0; i < (int)nminibars; i++) {
297 		miniperc = minipercs[i];
298 		if (miniperc == 8)
299 			continue;
300 		/* label */
301 		if (color && (miniperc == 7 || miniperc < 0))
302 			wattron(widget, A_BOLD);
303 		mvwaddstr(widget, i+1, 2, minilabels[i]);
304 			wattroff(widget, A_BOLD);
305 		/* perc */
306 		if (miniperc > 10)
307 			mvwaddstr(widget, i+1, w-2-15, states[11]);
308 		else if (miniperc >= 0 && miniperc <= 10) {
309 			mvwaddstr(widget, i+1, w-2-15, "[             ]");
310 			if (color && miniperc == 1) /* Failed */
311 				colorperc = red;
312 			if (color && miniperc == 5) /* Done */
313 				colorperc = green;
314 			if (color && (miniperc == 1 || miniperc == 5))
315 				wattron(widget, colorperc);
316 			mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]);
317 			if (color && (miniperc == 1 || miniperc == 5))
318 				wattroff(widget, colorperc);
319 		}
320 		else { /* miniperc < 0 */
321 			miniperc = abs(miniperc);
322 			mvwaddstr(widget, i+1, w-2-15, "[             ]");
323 			draw_perc_bar(widget, i+1, 1+w-2-15, 13, miniperc,
324 			    false, -1 /*unused*/);
325 		}
326 	}
327 
328 	wrefresh(widget);
329 	ypad =  y + h - 5 - htextpad;
330 	ypad = ypad < y+(int)nminibars ? y+nminibars : ypad;
331 	prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2);
332 
333 	/* main bar */
334 	bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED);
335 
336 	draw_perc_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/);
337 
338 	wattron(bar, t.bar.color);
339 	mvwaddstr(bar, 0, 2, "Overall Progress");
340 	wattroff(bar, t.bar.color);
341 
342 	wrefresh(bar);
343 
344 	/* getch(); port ncurses shows nothing */
345 
346 	delwin(bar);
347 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
348 
349 	return BSDDIALOG_OK;
350 }
351 
352 int
353 bsddialog_mixedgauge(struct bsddialog_conf *conf, char* text, int rows,
354     int cols, unsigned int mainperc, unsigned int nminibars, char **minilabels,
355     int *minipercs)
356 {
357 	int output;
358 
359 	output = mixedgauge(conf, text, rows, cols, mainperc, nminibars,
360 	    minilabels, minipercs, false);
361 
362 	return (output);
363 }
364 
365 int
366 bsddialog_progressview (struct bsddialog_conf *conf, char * text, int rows,
367     int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
368     struct bsddialog_fileminibar *minibar)
369 {
370 	int perc, output;
371 	int *minipercs;
372 	unsigned int i;
373 	char **minilabels;
374 	unsigned int mainperc, totaltodo;
375 	time_t tstart, told, tnew, refresh;
376 	bool update;
377 	float readforsec;
378 
379 	if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL)
380 		RETURN_ERROR("Cannot allocate memory for minilabels\n");
381 	if ((minipercs = calloc(nminibar, sizeof(int))) == NULL)
382 		RETURN_ERROR("Cannot allocate memory for minipercs\n");
383 
384 	totaltodo = 0;
385 	for(i=0; i<nminibar; i++) {
386 		totaltodo     += minibar[i].size;
387 		minilabels[i] = minibar[i].label;
388 		minipercs[i]  = 10; /*Pending*/
389 	}
390 
391 	refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1;
392 	output = BSDDIALOG_OK;
393 	i = 0;
394 	update = true;
395 	time(&told);
396 	tstart = told;
397 	while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) {
398 		if (bsddialog_total_progview == 0 || totaltodo == 0)
399 			mainperc = 0;
400 		else
401 			mainperc = (bsddialog_total_progview * 100) / totaltodo;
402 
403 		time(&tnew);
404 		if (update || tnew > told + refresh) {
405 			output = mixedgauge(conf, text, rows, cols, mainperc,
406 			    nminibar, minilabels, minipercs, true);
407 			if (output == BSDDIALOG_ERROR)
408 				return BSDDIALOG_ERROR;
409 
410 			move(LINES-1, 2);
411 			clrtoeol();
412 			readforsec = ((tnew - tstart) == 0) ?
413 			    0 : bsddialog_total_progview / (float)(tnew - tstart);
414 			printw(pvconf->fmtbottomstr, bsddialog_total_progview,
415 			    readforsec);
416 			refresh();
417 
418 			time(&told);
419 			update = false;
420 		}
421 
422 		if (i >= nminibar)
423 			break;
424 		if (minibar[i].status == 1) /* Failed*/
425 			break;
426 
427 		perc = pvconf->callback(&minibar[i]);
428 
429 		if (minibar[i].status == 5) {/* ||prec >= 100) Done */
430 			minipercs[i] = 5;
431 			update = true;
432 			i++;
433 		} else if (minibar[i].status == 1 || perc < 0) { /* Failed */
434 			minipercs[i] = 1;
435 			update = true;
436 		} else if (perc == 0)
437 			minipercs[i] = 7; /* In progress */
438 		else /* perc > 0 */
439 			minipercs[i] = -(perc);
440 	}
441 
442 	free(minilabels);
443 	free(minipercs);
444 	return (output);
445 }
446 
447 int
448 bsddialog_rangebox(struct bsddialog_conf *conf, char* text, int rows, int cols,
449     int min, int max, int *value)
450 {
451 	WINDOW *widget, *textpad, *bar, *shadow;
452 	int i, y, x, h, w, htextpad;
453 	bool loop, buttupdate, barupdate;
454 	int input, currvalue, output, sizebar, bigchange, positions;
455 	float perc;
456 	struct buttons bs;
457 
458 	if (value == NULL)
459 		RETURN_ERROR("*value cannot be NULL");
460 
461 	if (min >= max)
462 		RETURN_ERROR("min >= max");
463 
464 	currvalue = *value;
465 	positions = max - min + 1;
466 
467 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
468 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
469 
470 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
471 		return BSDDIALOG_ERROR;
472 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
473 		return BSDDIALOG_ERROR;
474 	if (bar_checksize(text, h, w, &bs) != 0)
475 		return BSDDIALOG_ERROR;
476 	if (set_widget_position(conf, &y, &x, h, w) != 0)
477 		return BSDDIALOG_ERROR;
478 
479 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
480 	    &textpad, &htextpad, text, true) != 0)
481 		return BSDDIALOG_ERROR;
482 
483 	prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
484 			    x+w-1-t.text.hmargin);
485 
486 	sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
487 	bigchange = MAX(1, sizebar/10);
488 
489 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3,
490 	    sizebar + 2, RAISED);
491 
492 	loop = buttupdate = barupdate = true;
493 	while(loop) {
494 		if (buttupdate) {
495 			draw_buttons(widget, h-2, w, bs, true);
496 			wrefresh(widget);
497 			buttupdate = false;
498 		}
499 		if (barupdate) {
500 			perc = ((float)(currvalue - min)*100) / (positions-1);
501 			draw_perc_bar(bar, 1, 1, sizebar, perc, true, currvalue);
502 			barupdate = false;
503 			wrefresh(bar);
504 		}
505 
506 		input = getch();
507 		switch(input) {
508 		case KEY_ENTER:
509 		case 10: /* Enter */
510 			output = bs.value[bs.curr];
511 			*value = currvalue;
512 			loop = false;
513 			break;
514 		case 27: /* Esc */
515 			output = BSDDIALOG_ESC;
516 			loop = false;
517 			break;
518 		case '\t': /* TAB */
519 			bs.curr = (bs.curr + 1) % bs.nbuttons;
520 			buttupdate = true;
521 			break;
522 		case KEY_LEFT:
523 			if (bs.curr > 0) {
524 				bs.curr--;
525 				buttupdate = true;
526 			}
527 			break;
528 		case KEY_RIGHT:
529 			if (bs.curr < (int) bs.nbuttons - 1) {
530 				bs.curr++;
531 				buttupdate = true;
532 			}
533 			break;
534 		case KEY_HOME:
535 			currvalue = max;
536 			barupdate = true;
537 			break;
538 		case KEY_END:
539 			currvalue = min;
540 			barupdate = true;
541 			break;
542 		case KEY_NPAGE:
543 			currvalue -= bigchange;
544 			if (currvalue < min)
545 				currvalue = min;
546 			barupdate = true;
547 			break;
548 		case KEY_PPAGE:
549 			currvalue += bigchange;
550 			if (currvalue > max)
551 				currvalue = max;
552 			barupdate = true;
553 			break;
554 		case KEY_UP:
555 			if (currvalue < max) {
556 				currvalue++;
557 				barupdate = true;
558 			}
559 			break;
560 		case KEY_DOWN:
561 			if (currvalue > min) {
562 				currvalue--;
563 				barupdate = true;
564 			}
565 			break;
566 		case KEY_F(1):
567 			if (conf->f1_file == NULL && conf->f1_message == NULL)
568 				break;
569 			if (f1help(conf) != 0)
570 				return BSDDIALOG_ERROR;
571 			/* No break! the terminal size can change */
572 		case KEY_RESIZE:
573 			hide_widget(y, x, h, w,conf->shadow);
574 
575 			/*
576 			 * Unnecessary, but, when the columns decrease the
577 			 * following "refresh" seem not work
578 			 */
579 			refresh();
580 
581 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
582 				return BSDDIALOG_ERROR;
583 			if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
584 				return BSDDIALOG_ERROR;
585 			if (bar_checksize(text, h, w, &bs) != 0)
586 				return BSDDIALOG_ERROR;
587 			if (set_widget_position(conf, &y, &x, h, w) != 0)
588 				return BSDDIALOG_ERROR;
589 
590 			wclear(shadow);
591 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
592 			wresize(shadow, h, w);
593 
594 			wclear(widget);
595 			mvwin(widget, y, x);
596 			wresize(widget, h, w);
597 
598 			htextpad = 1;
599 			wclear(textpad);
600 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
601 
602 			sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
603 			bigchange = MAX(1, sizebar/10);
604 			wclear(bar);
605 			mvwin(bar, y + h - 6, x + 1 + BARMARGIN);
606 			wresize(bar, 3, sizebar + 2);
607 
608 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
609 			    RAISED, textpad, &htextpad, text, true) != 0)
610 				return BSDDIALOG_ERROR;
611 
612 			prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
613 			    x+w-1-t.text.hmargin);
614 
615 			draw_borders(conf, bar, 3, sizebar + 2, RAISED);
616 
617 			barupdate = true;
618 			buttupdate = true;
619 			break;
620 		default:
621 			for (i = 0; i < (int) bs.nbuttons; i++)
622 				if (tolower(input) == tolower((bs.label[i])[0])) {
623 					output = bs.value[i];
624 					loop = false;
625 			}
626 		}
627 	}
628 
629 	delwin(bar);
630 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
631 
632 	return output;
633 }
634 
635 int
636 bsddialog_pause(struct bsddialog_conf *conf, char* text, int rows, int cols,
637     unsigned int sec)
638 {
639 	WINDOW *widget, *textpad, *bar, *shadow;
640 	int i, output, y, x, h, w, htextpad;
641 	bool loop, buttupdate, barupdate;
642 	int input, tout, sizebar;
643 	float perc;
644 	struct buttons bs;
645 
646 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
647 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
648 
649 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
650 		return BSDDIALOG_ERROR;
651 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
652 		return BSDDIALOG_ERROR;
653 	if (bar_checksize(text, h, w, &bs) != 0)
654 		return BSDDIALOG_ERROR;
655 	if (set_widget_position(conf, &y, &x, h, w) != 0)
656 		return BSDDIALOG_ERROR;
657 
658 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
659 	    &textpad, &htextpad, text, true) != 0)
660 		return BSDDIALOG_ERROR;
661 
662 	prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
663 	    x+w-1-t.text.hmargin);
664 
665 	sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
666 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3,
667 	    sizebar + 2, RAISED);
668 
669 	tout = sec;
670 	nodelay(stdscr, TRUE);
671 	timeout(1000);
672 	loop = buttupdate = barupdate = true;
673 	while(loop) {
674 		if (barupdate) {
675 			perc = (float)tout * 100 / sec;
676 			draw_perc_bar(bar, 1, 1, sizebar, perc, true, tout);
677 			barupdate = false;
678 			wrefresh(bar);
679 		}
680 
681 		if (buttupdate) {
682 			draw_buttons(widget, h-2, w, bs, true);
683 			wrefresh(widget);
684 			buttupdate = false;
685 		}
686 
687 		input = getch();
688 		if(input < 0) { /* timeout */
689 			tout--;
690 			if (tout < 0) {
691 				output = BSDDIALOG_TIMEOUT;
692 				break;
693 			}
694 			else {
695 				barupdate = true;
696 				continue;
697 			}
698 		}
699 		switch(input) {
700 		case KEY_ENTER:
701 		case 10: /* Enter */
702 			output = bs.value[bs.curr];
703 			loop = false;
704 			break;
705 		case 27: /* Esc */
706 			output = BSDDIALOG_ESC;
707 			loop = false;
708 			break;
709 		case '\t': /* TAB */
710 			bs.curr = (bs.curr + 1) % bs.nbuttons;
711 			buttupdate = true;
712 			break;
713 		case KEY_LEFT:
714 			if (bs.curr > 0) {
715 				bs.curr--;
716 				buttupdate = true;
717 			}
718 			break;
719 		case KEY_RIGHT:
720 			if (bs.curr < (int) bs.nbuttons - 1) {
721 				bs.curr++;
722 				buttupdate = true;
723 			}
724 			break;
725 		case KEY_F(1):
726 			if (conf->f1_file == NULL && conf->f1_message == NULL)
727 				break;
728 			if (f1help(conf) != 0)
729 				return BSDDIALOG_ERROR;
730 			/* No break! the terminal size can change */
731 		case KEY_RESIZE:
732 			hide_widget(y, x, h, w,conf->shadow);
733 
734 			/*
735 			 * Unnecessary, but, when the columns decrease the
736 			 * following "refresh" seem not work
737 			 */
738 			refresh();
739 
740 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
741 				return BSDDIALOG_ERROR;
742 			if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
743 				return BSDDIALOG_ERROR;
744 			if (bar_checksize(text, h, w, &bs) != 0)
745 				return BSDDIALOG_ERROR;
746 			if (set_widget_position(conf, &y, &x, h, w) != 0)
747 				return BSDDIALOG_ERROR;
748 
749 			wclear(shadow);
750 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
751 			wresize(shadow, h, w);
752 
753 			wclear(widget);
754 			mvwin(widget, y, x);
755 			wresize(widget, h, w);
756 
757 			htextpad = 1;
758 			wclear(textpad);
759 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
760 
761 			sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
762 			wclear(bar);
763 			mvwin(bar, y + h - 6, x + 1 + BARMARGIN);
764 			wresize(bar, 3, sizebar + 2);
765 
766 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
767 			    RAISED, textpad, &htextpad, text, true) != 0)
768 				return BSDDIALOG_ERROR;
769 
770 			prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
771 			    x+w-1-t.text.hmargin);
772 
773 			draw_borders(conf, bar, 3, sizebar + 2, RAISED);
774 
775 			barupdate = true;
776 			buttupdate = true;
777 			break;
778 		default:
779 			for (i = 0; i < (int) bs.nbuttons; i++)
780 				if (tolower(input) == tolower((bs.label[i])[0])) {
781 					output = bs.value[i];
782 					loop = false;
783 			}
784 		}
785 	}
786 
787 	nodelay(stdscr, FALSE);
788 
789 	delwin(bar);
790 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
791 
792 	return output;
793 }
794