xref: /freebsd/contrib/bsddialog/lib/barbox.c (revision 263660c0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 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 #include <curses.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include "bsddialog.h"
38 #include "bsddialog_progressview.h"
39 #include "bsddialog_theme.h"
40 #include "lib_util.h"
41 
42 #define BARPADDING    3
43 #define MINBARWIDTH   (15 + BARPADDING * 2)
44 
45 bool bsddialog_interruptprogview;
46 bool bsddialog_abortprogview;
47 int  bsddialog_total_progview;
48 
49 extern struct bsddialog_theme t;
50 
51 static void
52 draw_bar(WINDOW *win, int y, int x, int size, int perc, bool withlabel,
53     int label)
54 {
55 	int i, blue_x, color;
56 	char labelstr[128];
57 
58 	blue_x = (int)((perc*(size))/100);
59 
60 	wmove(win, y, x);
61 	for (i = 0; i < size; i++) {
62 		color = (i <= blue_x) ? t.bar.f_color : t.bar.color;
63 		wattron(win, color);
64 		waddch(win, ' ');
65 		wattroff(win, color);
66 	}
67 
68 	if (withlabel)
69 		sprintf(labelstr, "%d", label);
70 	else
71 		sprintf(labelstr, "%3d%%", perc);
72 	wmove(win, y, x + size/2 - 2);
73 	for (i = 0; i < (int)strlen(labelstr); i++) {
74 		color = (blue_x + 1 <= size/2 - (int)strlen(labelstr)/2 + i ) ?
75 		    t.bar.color : t.bar.f_color;
76 		wattron(win, color);
77 		waddch(win, labelstr[i]);
78 		wattroff(win, color);
79 	}
80 }
81 
82 static int
83 bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
84     const char *text, struct buttons *bs)
85 {
86 	int htext, wtext;
87 
88 	if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
89 		if (text_size(conf, rows, cols, text, bs, 3, MINBARWIDTH,
90 		    &htext, &wtext) != 0)
91 			return (BSDDIALOG_ERROR);
92 	}
93 
94 	if (cols == BSDDIALOG_AUTOSIZE)
95 		*w = widget_min_width(conf, wtext, MINBARWIDTH, bs);
96 
97 	if (rows == BSDDIALOG_AUTOSIZE)
98 		*h = widget_min_height(conf, htext, 3 /* bar */, bs != NULL);
99 
100 	return (0);
101 }
102 
103 static int
104 bar_checksize(int rows, int cols, struct buttons *bs)
105 {
106 	int minheight, minwidth;
107 
108 	minwidth = 0;
109 	if (bs != NULL) { /* gauge has not buttons */
110 		minwidth = bs->nbuttons * bs->sizebutton;
111 		if (bs->nbuttons > 0)
112 			minwidth += (bs->nbuttons-1) * t.button.space;
113 	}
114 	minwidth = MAX(minwidth, MINBARWIDTH);
115 	minwidth += VBORDERS;
116 
117 	if (cols < minwidth)
118 		RETURN_ERROR("Few cols to draw bar and/or buttons");
119 
120 	minheight = HBORDERS + 3;
121 	if (bs != NULL)
122 		minheight += 2;
123 	if (rows < minheight)
124 		RETURN_ERROR("Few rows to draw bar");
125 
126 	return (0);
127 }
128 
129 int
130 bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
131     int cols, unsigned int perc, int fd, const char *sep)
132 {
133 	bool mainloop;
134 	int y, x, h, w, fd2;
135 	FILE *input;
136 	WINDOW *widget, *textpad, *bar, *shadow;
137 	char inputbuf[2048], ntext[2048], *pntext;
138 
139 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
140 		return (BSDDIALOG_ERROR);
141 	if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0)
142 		return (BSDDIALOG_ERROR);
143 	if (bar_checksize(h, w, NULL) != 0)
144 		return (BSDDIALOG_ERROR);
145 	if (set_widget_position(conf, &y, &x, h, w) != 0)
146 		return (BSDDIALOG_ERROR);
147 
148 	if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL,
149 	    false) != 0)
150 		return (BSDDIALOG_ERROR);
151 
152 	bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED);
153 
154 	mainloop = (fd < 0) ? false : true;
155 
156 	if (mainloop) {
157 		fd2 = dup(fd);
158 		input = fdopen(fd2, "r");
159 		if (input == NULL)
160 			RETURN_ERROR("Cannot build FILE* from fd");
161 	} else
162 		input = NULL;
163 
164 	while (mainloop) {
165 		wrefresh(widget);
166 		prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4,
167 		    x+w-1-TEXTHMARGIN);
168 		draw_borders(conf, bar, 3, w-6, RAISED);
169 		draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/);
170 		wrefresh(bar);
171 
172 		while (true) {
173 			fscanf(input, "%s", inputbuf);
174 			if (strcmp(inputbuf,"EOF") == 0) {
175 				mainloop = false;
176 				break;
177 			}
178 			if (strcmp(inputbuf, sep) == 0)
179 				break;
180 		}
181 		if (mainloop == false)
182 			break;
183 		fscanf(input, "%d", &perc);
184 		perc = perc < 0 ? 0 : perc;
185 		perc = perc > 100 ? 100 : perc;
186 		pntext = &ntext[0];
187 		ntext[0] = '\0';
188 		while (true) {
189 			fscanf(input, "%s", inputbuf);
190 			if (strcmp(inputbuf,"EOF") == 0) {
191 				mainloop = false;
192 				break;
193 			}
194 			if (strcmp(inputbuf, sep) == 0)
195 				break;
196 			strcpy(pntext, inputbuf);
197 			pntext += strlen(inputbuf);
198 			pntext[0] = ' ';
199 			pntext++;
200 		}
201 		if (update_dialog(conf, shadow, widget, y, x, h, w, textpad,
202 		    ntext, NULL, false) != 0)
203 			return (BSDDIALOG_ERROR);
204 	}
205 
206 	if (input != NULL)
207 		fclose(input);
208 	delwin(bar);
209 	end_dialog(conf, shadow, widget, textpad);
210 
211 	return (BSDDIALOG_OK);
212 }
213 
214 /* Mixedgauge */
215 static int
216 do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols,
217     unsigned int mainperc, unsigned int nminibars, const char **minilabels,
218     int *minipercs, bool color)
219 {
220 	int i, output, miniperc, y, x, h, w, ypad, max_minbarlen;
221 	int htextpad, htext, wtext;
222 	int colorperc, red, green;
223 	WINDOW *widget, *textpad, *bar, *shadow;
224 	char states[12][14] = {
225 		"  Succeeded  ", /* -1  */
226 		"   Failed    ", /* -2  */
227 		"   Passed    ", /* -3  */
228 		"  Completed  ", /* -4  */
229 		"   Checked   ", /* -5  */
230 		"    Done     ", /* -6  */
231 		"   Skipped   ", /* -7  */
232 		" In Progress ", /* -8  */
233 		"(blank)      ", /* -9  */
234 		"     N/A     ", /* -10 */
235 		"   Pending   ", /* -11 */
236 		"   UNKNOWN   ", /* < -11, no API */
237 	};
238 
239 	red   = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED,  BSDDIALOG_BOLD);
240 	green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
241 
242 	max_minbarlen = 0;
243 	for (i = 0; i < (int)nminibars; i++)
244 		max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i]));
245 	max_minbarlen += 3 + 16 /* seps + [...] or mainbar */;
246 
247 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
248 		return (BSDDIALOG_ERROR);
249 
250 	/* mixedgauge autosize */
251 	if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
252 		if (text_size(conf, rows, cols, text, NULL, nminibars + 3,
253 		    max_minbarlen, &htext, &wtext) != 0)
254 			return (BSDDIALOG_ERROR);
255 	}
256 	if (cols == BSDDIALOG_AUTOSIZE)
257 		w = widget_min_width(conf, wtext, max_minbarlen, NULL);
258 	if (rows == BSDDIALOG_AUTOSIZE)
259 		h = widget_min_height(conf, htext, nminibars + 3, false);
260 
261 	/* mixedgauge checksize */
262 	if (w < max_minbarlen + 2)
263 		RETURN_ERROR("Few cols for this mixedgauge");
264 	if (h < 5 + (int)nminibars)
265 		RETURN_ERROR("Few rows for this mixedgauge");
266 
267 	if (set_widget_position(conf, &y, &x, h, w) != 0)
268 		return (BSDDIALOG_ERROR);
269 
270 	output = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text,
271 	    NULL, false);
272 	if (output == BSDDIALOG_ERROR)
273 		return (output);
274 
275 	/* mini bars */
276 	for (i = 0; i < (int)nminibars; i++) {
277 		miniperc = minipercs[i];
278 		if (miniperc == BSDDIALOG_MG_BLANK)
279 			continue;
280 		/* label */
281 		if (color && (miniperc >= 0))
282 			wattron(widget, A_BOLD);
283 		mvwaddstr(widget, i+1, 2, minilabels[i]);
284 			wattroff(widget, A_BOLD);
285 		/* perc */
286 		if (miniperc < -11)
287 			mvwaddstr(widget, i+1, w-2-15, states[11]);
288 		else if (miniperc < 0) {
289 			mvwaddstr(widget, i+1, w-2-15, "[             ]");
290 			colorperc = -1;
291 			if (color && miniperc == BSDDIALOG_MG_FAILED)
292 				colorperc = red;
293 			if (color && miniperc == BSDDIALOG_MG_DONE)
294 				colorperc = green;
295 			if (colorperc != -1)
296 				wattron(widget, colorperc);
297 			miniperc = abs(miniperc + 1);
298 			mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]);
299 			if (colorperc != -1)
300 				wattroff(widget, colorperc);
301 		}
302 		else { /* miniperc >= 0 */
303 			if (miniperc > 100)
304 				miniperc = 100;
305 			mvwaddstr(widget, i+1, w-2-15, "[             ]");
306 			draw_bar(widget, i+1, 1+w-2-15, 13, miniperc, false,
307 			    -1 /*unused*/);
308 		}
309 	}
310 
311 	wrefresh(widget);
312 	getmaxyx(textpad, htextpad, i /* unused */);
313 	ypad =  y + h - 4 - htextpad;
314 	ypad = ypad < y+(int)nminibars ? y+nminibars : ypad;
315 	prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2);
316 
317 	/* main bar */
318 	bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED);
319 
320 	draw_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/);
321 
322 	wattron(bar, t.bar.color);
323 	mvwaddstr(bar, 0, 2, "Overall Progress");
324 	wattroff(bar, t.bar.color);
325 
326 	wrefresh(bar);
327 
328 	/* getch(); port ncurses shows nothing */
329 
330 	delwin(bar);
331 	end_dialog(conf, shadow, widget, textpad);
332 
333 	return (BSDDIALOG_OK);
334 }
335 
336 int
337 bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows,
338     int cols, unsigned int mainperc, unsigned int nminibars,
339     const char **minilabels, int *minipercs)
340 {
341 	int output;
342 
343 	output = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars,
344 	    minilabels, minipercs, false);
345 
346 	return (output);
347 }
348 
349 int
350 bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
351     int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
352     struct bsddialog_fileminibar *minibar)
353 {
354 	bool update;
355 	int perc, output, *minipercs;
356 	unsigned int i, mainperc, totaltodo;
357 	float readforsec;
358 	const char **minilabels;
359 	time_t tstart, told, tnew, refresh;
360 
361 	if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL)
362 		RETURN_ERROR("Cannot allocate memory for minilabels");
363 	if ((minipercs = calloc(nminibar, sizeof(int))) == NULL)
364 		RETURN_ERROR("Cannot allocate memory for minipercs");
365 
366 	totaltodo = 0;
367 	for (i = 0; i < nminibar; i++) {
368 		totaltodo += minibar[i].size;
369 		minilabels[i] = minibar[i].label;
370 		minipercs[i] = minibar[i].status;
371 	}
372 
373 	refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1;
374 	output = BSDDIALOG_OK;
375 	i = 0;
376 	update = true;
377 	time(&told);
378 	tstart = told;
379 	while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) {
380 		if (bsddialog_total_progview == 0 || totaltodo == 0)
381 			mainperc = 0;
382 		else
383 			mainperc = (bsddialog_total_progview * 100) / totaltodo;
384 
385 		time(&tnew);
386 		if (update || tnew > told + refresh) {
387 			output = do_mixedgauge(conf, text, rows, cols, mainperc,
388 			    nminibar, minilabels, minipercs, true);
389 			if (output == BSDDIALOG_ERROR)
390 				return (BSDDIALOG_ERROR);
391 
392 			move(SCREENLINES - 1, 2);
393 			clrtoeol();
394 			readforsec = ((tnew - tstart) == 0) ? 0 :
395 			    bsddialog_total_progview / (float)(tnew - tstart);
396 			printw(pvconf->fmtbottomstr, bsddialog_total_progview,
397 			    readforsec);
398 			refresh();
399 
400 			time(&told);
401 			update = false;
402 		}
403 
404 		if (i >= nminibar)
405 			break;
406 		if (minibar[i].status == BSDDIALOG_MG_FAILED)
407 			break;
408 
409 		perc = pvconf->callback(&minibar[i]);
410 
411 		if (minibar[i].status == BSDDIALOG_MG_DONE) { /*||perc >= 100)*/
412 			minipercs[i] = BSDDIALOG_MG_DONE;
413 			update = true;
414 			i++;
415 		} else if (minibar[i].status == BSDDIALOG_MG_FAILED || perc < 0) {
416 			minipercs[i] = BSDDIALOG_MG_FAILED;
417 			update = true;
418 		} else /* perc >= 0 */
419 			minipercs[i] = perc;
420 	}
421 
422 	free(minilabels);
423 	free(minipercs);
424 	return (output);
425 }
426 
427 int
428 bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
429     int cols, int min, int max, int *value)
430 {
431 	bool loop, buttupdate, barupdate;
432 	int y, x, h, w;
433 	int input, currvalue, output, sizebar, bigchange, positions;
434 	float perc;
435 	WINDOW *widget, *textpad, *bar, *shadow;
436 	struct buttons bs;
437 
438 	if (value == NULL)
439 		RETURN_ERROR("*value cannot be NULL");
440 
441 	if (min >= max)
442 		RETURN_ERROR("min >= max");
443 
444 	currvalue = *value;
445 	positions = max - min + 1;
446 
447 	get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
448 
449 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
450 		return (BSDDIALOG_ERROR);
451 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
452 		return (BSDDIALOG_ERROR);
453 	if (bar_checksize(h, w, &bs) != 0)
454 		return (BSDDIALOG_ERROR);
455 	if (set_widget_position(conf, &y, &x, h, w) != 0)
456 		return (BSDDIALOG_ERROR);
457 
458 	if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
459 	    true) != 0)
460 		return (BSDDIALOG_ERROR);
461 
462 	doupdate();
463 
464 	prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
465 
466 	sizebar = w - HBORDERS - 2 - BARPADDING * 2;
467 	bigchange = MAX(1, sizebar/10);
468 
469 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
470 	    sizebar + 2, RAISED);
471 
472 	loop = buttupdate = barupdate = true;
473 	while (loop) {
474 		if (buttupdate) {
475 			draw_buttons(widget, bs, true);
476 			wrefresh(widget);
477 			buttupdate = false;
478 		}
479 		if (barupdate) {
480 			perc = ((float)(currvalue - min)*100) / (positions-1);
481 			draw_bar(bar, 1, 1, sizebar, perc, true, currvalue);
482 			barupdate = false;
483 			wrefresh(bar);
484 		}
485 
486 		input = getch();
487 		switch(input) {
488 		case KEY_ENTER:
489 		case 10: /* Enter */
490 			output = bs.value[bs.curr];
491 			*value = currvalue;
492 			loop = false;
493 			break;
494 		case 27: /* Esc */
495 			if (conf->key.enable_esc) {
496 				output = BSDDIALOG_ESC;
497 				loop = false;
498 			}
499 			break;
500 		case '\t': /* TAB */
501 			bs.curr = (bs.curr + 1) % bs.nbuttons;
502 			buttupdate = true;
503 			break;
504 		case KEY_LEFT:
505 			if (bs.curr > 0) {
506 				bs.curr--;
507 				buttupdate = true;
508 			}
509 			break;
510 		case KEY_RIGHT:
511 			if (bs.curr < (int) bs.nbuttons - 1) {
512 				bs.curr++;
513 				buttupdate = true;
514 			}
515 			break;
516 		case KEY_HOME:
517 			currvalue = max;
518 			barupdate = true;
519 			break;
520 		case KEY_END:
521 			currvalue = min;
522 			barupdate = true;
523 			break;
524 		case KEY_NPAGE:
525 			currvalue -= bigchange;
526 			if (currvalue < min)
527 				currvalue = min;
528 			barupdate = true;
529 			break;
530 		case KEY_PPAGE:
531 			currvalue += bigchange;
532 			if (currvalue > max)
533 				currvalue = max;
534 			barupdate = true;
535 			break;
536 		case KEY_UP:
537 			if (currvalue < max) {
538 				currvalue++;
539 				barupdate = true;
540 			}
541 			break;
542 		case KEY_DOWN:
543 			if (currvalue > min) {
544 				currvalue--;
545 				barupdate = true;
546 			}
547 			break;
548 		case KEY_F(1):
549 			if (conf->f1_file == NULL && conf->f1_message == NULL)
550 				break;
551 			if (f1help(conf) != 0)
552 				return (BSDDIALOG_ERROR);
553 			/* No break, screen size can change */
554 		case KEY_RESIZE:
555 			/* Important for decreasing screen */
556 			hide_widget(y, x, h, w, conf->shadow);
557 			refresh();
558 
559 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
560 				return (BSDDIALOG_ERROR);
561 			if (bar_autosize(conf, rows, cols, &h, &w, text,
562 			    &bs) != 0)
563 				return (BSDDIALOG_ERROR);
564 			if (bar_checksize(h, w, &bs) != 0)
565 				return (BSDDIALOG_ERROR);
566 			if (set_widget_position(conf, &y, &x, h, w) != 0)
567 				return (BSDDIALOG_ERROR);
568 
569 			if (update_dialog(conf, shadow, widget,y, x, h, w,
570 			    textpad, text, &bs, true) != 0)
571 				return (BSDDIALOG_ERROR);
572 
573 			doupdate();
574 
575 			sizebar = w - HBORDERS - 2 - BARPADDING * 2;
576 			bigchange = MAX(1, sizebar/10);
577 			wclear(bar);
578 			mvwin(bar, y + h - 6, x + 1 + BARPADDING);
579 			wresize(bar, 3, sizebar + 2);
580 			draw_borders(conf, bar, 3, sizebar+2, RAISED);
581 
582 			prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
583 			    x+w-1-TEXTHMARGIN);
584 
585 			barupdate = true;
586 			break;
587 		default:
588 			if (shortcut_buttons(input, &bs)) {
589 				output = bs.value[bs.curr];
590 				loop = false;
591 			}
592 		}
593 	}
594 
595 	delwin(bar);
596 	end_dialog(conf, shadow, widget, textpad);
597 
598 	return (output);
599 }
600 
601 int
602 bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
603     int cols, unsigned int sec)
604 {
605 	bool loop, buttupdate, barupdate;
606 	int output, y, x, h, w, input, tout, sizebar;
607 	float perc;
608 	WINDOW *widget, *textpad, *bar, *shadow;
609 	struct buttons bs;
610 
611 	get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
612 
613 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
614 		return (BSDDIALOG_ERROR);
615 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
616 		return (BSDDIALOG_ERROR);
617 	if (bar_checksize(h, w, &bs) != 0)
618 		return (BSDDIALOG_ERROR);
619 	if (set_widget_position(conf, &y, &x, h, w) != 0)
620 		return (BSDDIALOG_ERROR);
621 
622 	if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
623 	    true) != 0)
624 		return (BSDDIALOG_ERROR);
625 
626 	doupdate();
627 
628 	prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
629 
630 	sizebar = w - HBORDERS - 2 - BARPADDING * 2;
631 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
632 	    sizebar + 2, RAISED);
633 
634 	tout = sec;
635 	nodelay(stdscr, TRUE);
636 	timeout(1000);
637 	loop = buttupdate = barupdate = true;
638 	while (loop) {
639 		if (barupdate) {
640 			perc = (float)tout * 100 / sec;
641 			draw_bar(bar, 1, 1, sizebar, perc, true, tout);
642 			barupdate = false;
643 			wrefresh(bar);
644 		}
645 
646 		if (buttupdate) {
647 			draw_buttons(widget, bs, true);
648 			wrefresh(widget);
649 			buttupdate = false;
650 		}
651 
652 		input = getch();
653 		if (input < 0) { /* timeout */
654 			tout--;
655 			if (tout < 0) {
656 				output = BSDDIALOG_TIMEOUT;
657 				break;
658 			}
659 			else {
660 				barupdate = true;
661 				continue;
662 			}
663 		}
664 		switch(input) {
665 		case KEY_ENTER:
666 		case 10: /* Enter */
667 			output = bs.value[bs.curr];
668 			loop = false;
669 			break;
670 		case 27: /* Esc */
671 			if (conf->key.enable_esc) {
672 				output = BSDDIALOG_ESC;
673 				loop = false;
674 			}
675 			break;
676 		case '\t': /* TAB */
677 			bs.curr = (bs.curr + 1) % bs.nbuttons;
678 			buttupdate = true;
679 			break;
680 		case KEY_LEFT:
681 			if (bs.curr > 0) {
682 				bs.curr--;
683 				buttupdate = true;
684 			}
685 			break;
686 		case KEY_RIGHT:
687 			if (bs.curr < (int) bs.nbuttons - 1) {
688 				bs.curr++;
689 				buttupdate = true;
690 			}
691 			break;
692 		case KEY_F(1):
693 			if (conf->f1_file == NULL && conf->f1_message == NULL)
694 				break;
695 			if (f1help(conf) != 0)
696 				return (BSDDIALOG_ERROR);
697 			/* No break, screen size can change */
698 		case KEY_RESIZE:
699 			/* Important for decreasing screen */
700 			hide_widget(y, x, h, w, conf->shadow);
701 			refresh();
702 
703 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
704 				return (BSDDIALOG_ERROR);
705 			if (bar_autosize(conf, rows, cols, &h, &w, text,
706 			    &bs) != 0)
707 				return (BSDDIALOG_ERROR);
708 			if (bar_checksize(h, w, &bs) != 0)
709 				return (BSDDIALOG_ERROR);
710 			if (set_widget_position(conf, &y, &x, h, w) != 0)
711 				return (BSDDIALOG_ERROR);
712 
713 			if (update_dialog(conf, shadow, widget,y, x, h, w,
714 			    textpad, text, &bs, true) != 0)
715 				return (BSDDIALOG_ERROR);
716 
717 			doupdate();
718 
719 			sizebar = w - HBORDERS - 2 - BARPADDING * 2;
720 			wclear(bar);
721 			mvwin(bar, y + h - 6, x + 1 + BARPADDING);
722 			wresize(bar, 3, sizebar + 2);
723 			draw_borders(conf, bar, 3, sizebar+2, LOWERED);
724 
725 			prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
726 			    x+w-1-TEXTHMARGIN);
727 
728 			barupdate = true;
729 			break;
730 		default:
731 			if (shortcut_buttons(input, &bs)) {
732 				output = bs.value[bs.curr];
733 				loop = false;
734 			}
735 		}
736 	}
737 
738 	nodelay(stdscr, FALSE);
739 
740 	delwin(bar);
741 	end_dialog(conf, shadow, widget, textpad);
742 
743 	return (output);
744 }