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 #include <stdlib.h>
32 #include <string.h>
33 
34 #ifdef PORTNCURSES
35 #include <ncurses/curses.h>
36 #else
37 #include <curses.h>
38 #endif
39 
40 #include "bsddialog.h"
41 #include "lib_util.h"
42 #include "bsddialog_theme.h"
43 
44 #define BARMARGIN	3
45 #define MINBARWIDTH	10
46 #define MINWIDTH	(VBORDERS + MINBARWIDTH + BARMARGIN * 2)
47 #define MINHEIGHT	7 /* without text */
48 
49 /* "Bar": gauge - mixedgauge - rangebox - pause */
50 
51 extern struct bsddialog_theme t;
52 
53 static void
draw_perc_bar(WINDOW * win,int y,int x,int size,int perc,bool withlabel,int label)54 draw_perc_bar(WINDOW *win, int y, int x, int size, int perc, bool withlabel,
55     int label)
56 {
57 	char labelstr[128];
58 	int i, blue_x, color;
59 
60 	blue_x = (int)((perc*(size))/100);
61 
62 	wmove(win, y, x);
63 	for (i = 0; i < size; i++) {
64 		color = (i <= blue_x) ? t.bar.f_color : t.bar.color;
65 		wattron(win, color);
66 		waddch(win, ' ');
67 		wattroff(win, color);
68 	}
69 
70 	if (withlabel)
71 		sprintf(labelstr, "%d", label);
72 	else
73 		sprintf(labelstr, "%3d%%", perc);
74 	wmove(win, y, x + size/2 - 2);
75 	for (i=0; i < (int) strlen(labelstr); i++) {
76 		color = (blue_x + 1 <= size/2 - (int)strlen(labelstr)/2 + i ) ?
77 		    t.bar.color : t.bar.f_color;
78 		wattron(win, color);
79 		waddch(win, labelstr[i]);
80 		wattroff(win, color);
81 	}
82 }
83 
84 static int
bar_autosize(struct bsddialog_conf * conf,int rows,int cols,int * h,int * w,char * text,struct buttons * bs)85 bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
86     char *text, struct buttons *bs)
87 {
88 	int maxword, maxline, nlines, buttonswidth;
89 
90 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
91 		return BSDDIALOG_ERROR;
92 
93 	buttonswidth = 0;
94 	if (bs != NULL) { /* gauge has not buttons */
95 		buttonswidth= bs->nbuttons * bs->sizebutton;
96 		if (bs->nbuttons > 0)
97 			buttonswidth += (bs->nbuttons-1) * t.button.space;
98 	}
99 
100 	if (cols == BSDDIALOG_AUTOSIZE) {
101 		*w = VBORDERS;
102 		/* buttons size */
103 		*w += buttonswidth;
104 		/* bar size */
105 		*w = MAX(*w, MINWIDTH);
106 		/* text size*/
107 		*w = MAX((int)(maxline + VBORDERS + t.text.hmargin * 2), *w);
108 		/* avoid terminal overflow */
109 		*w = MIN(*w, widget_max_width(conf));
110 	}
111 
112 	if (rows == BSDDIALOG_AUTOSIZE) {
113 		*h = MINHEIGHT;
114 		if (maxword > 0)
115 			*h += 1;
116 		/* avoid terminal overflow */
117 		*h = MIN(*h, widget_max_height(conf));
118 	}
119 
120 	return (0);
121 }
122 
123 static int
bar_checksize(char * text,int rows,int cols,struct buttons * bs)124 bar_checksize(char *text, int rows, int cols, struct buttons *bs)
125 {
126 	int minheight, minwidth;
127 
128 	minwidth = 0;
129 	if (bs != NULL) { /* gauge has not buttons */
130 		minwidth = bs->nbuttons * bs->sizebutton;
131 		if (bs->nbuttons > 0)
132 			minwidth += (bs->nbuttons-1) * t.button.space;
133 	}
134 	minwidth = MAX(minwidth + VBORDERS, MINBARWIDTH);
135 
136 	if (cols< minwidth)
137 		RETURN_ERROR("Few cols for this widget");
138 
139 	minheight = MINHEIGHT + ((text != NULL && strlen(text) > 0) ? 1 : 0);
140 	if (rows < minheight)
141 		RETURN_ERROR("Few rows for this mixedgauge");
142 
143 	return 0;
144 }
145 
146 int
bsddialog_gauge(struct bsddialog_conf * conf,char * text,int rows,int cols,unsigned int perc)147 bsddialog_gauge(struct bsddialog_conf *conf, char* text, int rows, int cols,
148     unsigned int perc)
149 {
150 	WINDOW *widget, *textpad, *bar, *shadow;
151 	char input[2048], ntext[2048], *pntext;
152 	int y, x, h, w, htextpad;
153 	bool mainloop;
154 
155 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
156 		return BSDDIALOG_ERROR;
157 	if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0)
158 		return BSDDIALOG_ERROR;
159 	if (bar_checksize(text, h, w, NULL) != 0)
160 		return BSDDIALOG_ERROR;
161 	if (set_widget_position(conf, &y, &x, h, w) != 0)
162 		return BSDDIALOG_ERROR;
163 
164 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
165 	    &textpad, &htextpad, text, false) != 0)
166 		return BSDDIALOG_ERROR;
167 
168 	bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED);
169 
170 	mainloop = true;
171 	while (mainloop) {
172 		wrefresh(widget);
173 		prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-4,
174 		    x+w-1-t.text.hmargin);
175 		draw_perc_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/);
176 		wrefresh(bar);
177 
178 		while (true) {
179 			scanf("%s", input);
180 			if (strcmp(input,"EOF") == 0) {
181 				mainloop = false;
182 				break;
183 			}
184 			if (strcmp(input,"XXX") == 0)
185 				break;
186 		}
187 		scanf("%d", &perc);
188 		perc = perc < 0 ? 0 : perc;
189 		perc = perc > 100 ? 100 : perc;
190 		htextpad = 1;
191 		wclear(textpad);
192 		pntext = &ntext[0];
193 		ntext[0] = '\0';
194 		while (true) {
195 			scanf("%s", input);
196 			if (strcmp(input,"EOF") == 0) {
197 				mainloop = false;
198 				break;
199 			}
200 			if (strcmp(input,"XXX") == 0)
201 				break;
202 			pntext[0] = ' ';
203 			pntext++;
204 			strcpy(pntext, input);
205 			pntext += strlen(input);
206 		}
207 		print_textpad(conf, textpad, &htextpad, w-2-t.text.hmargin*2,
208 		    ntext);
209 	}
210 
211 	delwin(bar);
212 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
213 
214 	return BSDDIALOG_YESOK;
215 }
216 
217 int
bsddialog_mixedgauge(struct bsddialog_conf * conf,char * text,int rows,int cols,unsigned int mainperc,unsigned int nminbars,char ** minibars)218 bsddialog_mixedgauge(struct bsddialog_conf *conf, char* text, int rows, int cols,
219     unsigned int mainperc, unsigned int nminbars, char **minibars)
220 {
221 	WINDOW *widget, *textpad, *bar, *shadow;
222 	int i, output, miniperc, y, x, h, w, max_minbarlen;
223 	int maxword, maxline, nlines, htextpad, ypad;
224 	char states[11][16] = {
225 	    "[  Succeeded  ]",
226 	    "[   Failed    ]",
227 	    "[   Passed    ]",
228 	    "[  Completed  ]",
229 	    "[   Checked   ]",
230 	    "[    Done     ]",
231 	    "[   Skipped   ]",
232 	    "[ In Progress ]",
233 	    "!!!  BLANK  !!!",
234 	    "[     N/A     ]",
235 	    "[   UNKNOWN   ]",
236 	};
237 
238 	if (nminbars % 2 !=0)
239 		RETURN_ERROR("Mixedgauge wants a pair name/perc");
240 
241 	max_minbarlen = 0;
242 	for (i=0; i < (int)(nminbars/2); i++)
243 		max_minbarlen = MAX(max_minbarlen, (int) strlen(minibars[i*2]));
244 	max_minbarlen += 3 + 16 /* seps + [...] or mainbar */;
245 
246 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
247 		return BSDDIALOG_ERROR;
248 
249 	/* mixedgauge autosize */
250 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
251 		return BSDDIALOG_ERROR;
252 
253 	if (cols == BSDDIALOG_AUTOSIZE) {
254 		w = max_minbarlen + HBORDERS;
255 		w = MAX(max_minbarlen, maxline + 4);
256 		w = MIN(w, widget_max_width(conf) - 1);
257 	}
258 	if (rows == BSDDIALOG_AUTOSIZE) {
259 		h = 5; /* borders + mainbar */
260 		h += nminbars/2;
261 		h += (strlen(text) > 0 ? 3 : 0);
262 		h = MIN(h, widget_max_height(conf) -1);
263 	}
264 
265 	/* mixedgauge checksize */
266 	if (w < max_minbarlen + 2)
267 		RETURN_ERROR("Few cols for this mixedgauge");
268 	if (h < 5 + (int)nminbars/2 + (strlen(text) > 0 ? 1 : 0))
269 		RETURN_ERROR("Few rows for this mixedgauge");
270 
271 	if (set_widget_position(conf, &y, &x, h, w) != 0)
272 		return BSDDIALOG_ERROR;
273 
274 	output = new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w,
275 	    RAISED, &textpad, &htextpad, text, false);
276 	if (output == BSDDIALOG_ERROR)
277 		return output;
278 
279 	/* mini bars */
280 	for (i=0; i < (int)nminbars/2; i++) {
281 		miniperc = atol(minibars[i*2 + 1]);
282 		if (miniperc == 8)
283 			continue;
284 		mvwaddstr(widget, i+1, 2, minibars[i*2]);
285 		if (miniperc > 9)
286 			mvwaddstr(widget, i+1, w-2-15, states[10]);
287 		else if (miniperc >= 0 && miniperc <= 9)
288 			mvwaddstr(widget, i+1, w-2-15, states[miniperc]);
289 		else { //miniperc < 0
290 			miniperc = abs(miniperc);
291 			mvwaddstr(widget, i+1, w-2-15, "[             ]");
292 			draw_perc_bar(widget, i+1, 1+w-2-15, 13, miniperc,
293 			    false, -1 /*unused*/);
294 		}
295 	}
296 
297 	wrefresh(widget);
298 	ypad =  y + h - 5 - htextpad;
299 	ypad = ypad < y+(int)nminbars/2 ? y+nminbars/2 : ypad;
300 	prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2);
301 
302 	/* main bar */
303 	bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED);
304 
305 	draw_perc_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/);
306 
307 	wattron(bar, t.bar.color);
308 	mvwaddstr(bar, 0, 2, "Overall Progress");
309 	wattroff(bar, t.bar.color);
310 
311 	wrefresh(bar);
312 
313 	/* getch(); port ncurses shows nothing */
314 
315 	delwin(bar);
316 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
317 
318 	return BSDDIALOG_YESOK;
319 }
320 
321 int
bsddialog_rangebox(struct bsddialog_conf * conf,char * text,int rows,int cols,int min,int max,int * value)322 bsddialog_rangebox(struct bsddialog_conf *conf, char* text, int rows, int cols,
323     int min, int max, int *value)
324 {
325 	WINDOW *widget, *textpad, *bar, *shadow;
326 	int i, y, x, h, w, htextpad;
327 	bool loop, buttupdate, barupdate;
328 	int input, currvalue, output, sizebar, bigchange, positions;
329 	float perc;
330 	struct buttons bs;
331 
332 	if (value == NULL)
333 		RETURN_ERROR("*value cannot be NULL");
334 
335 	if (min >= max)
336 		RETURN_ERROR("min >= max");
337 
338 	currvalue = *value;
339 	positions = max - min + 1;
340 
341 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
342 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
343 
344 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
345 		return BSDDIALOG_ERROR;
346 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
347 		return BSDDIALOG_ERROR;
348 	if (bar_checksize(text, h, w, &bs) != 0)
349 		return BSDDIALOG_ERROR;
350 	if (set_widget_position(conf, &y, &x, h, w) != 0)
351 		return BSDDIALOG_ERROR;
352 
353 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
354 	    &textpad, &htextpad, text, true) != 0)
355 		return BSDDIALOG_ERROR;
356 
357 	prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
358 			    x+w-1-t.text.hmargin);
359 
360 	sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
361 	bigchange = MAX(1, sizebar/10);
362 
363 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3,
364 	    sizebar + 2, RAISED);
365 
366 	loop = buttupdate = barupdate = true;
367 	while(loop) {
368 		if (buttupdate) {
369 			draw_buttons(widget, h-2, w, bs, true);
370 			wrefresh(widget);
371 			buttupdate = false;
372 		}
373 		if (barupdate) {
374 			perc = ((float)(currvalue - min)*100) / (positions-1);
375 			draw_perc_bar(bar, 1, 1, sizebar, perc, true, currvalue);
376 			barupdate = false;
377 			wrefresh(bar);
378 		}
379 
380 		input = getch();
381 		switch(input) {
382 		case KEY_ENTER:
383 		case 10: /* Enter */
384 			output = bs.value[bs.curr];
385 			*value = currvalue;
386 			loop = false;
387 			break;
388 		case 27: /* Esc */
389 			output = BSDDIALOG_ESC;
390 			loop = false;
391 			break;
392 		case '\t': /* TAB */
393 			bs.curr = (bs.curr + 1) % bs.nbuttons;
394 			buttupdate = true;
395 			break;
396 		case KEY_LEFT:
397 			if (bs.curr > 0) {
398 				bs.curr--;
399 				buttupdate = true;
400 			}
401 			break;
402 		case KEY_RIGHT:
403 			if (bs.curr < (int) bs.nbuttons - 1) {
404 				bs.curr++;
405 				buttupdate = true;
406 			}
407 			break;
408 		case KEY_HOME:
409 			currvalue = max;
410 			barupdate = true;
411 			break;
412 		case KEY_END:
413 			currvalue = min;
414 			barupdate = true;
415 			break;
416 		case KEY_NPAGE:
417 			currvalue -= bigchange;
418 			if (currvalue < min)
419 				currvalue = min;
420 			barupdate = true;
421 			break;
422 		case KEY_PPAGE:
423 			currvalue += bigchange;
424 			if (currvalue > max)
425 				currvalue = max;
426 			barupdate = true;
427 			break;
428 		case KEY_UP:
429 			if (currvalue < max) {
430 				currvalue++;
431 				barupdate = true;
432 			}
433 			break;
434 		case KEY_DOWN:
435 			if (currvalue > min) {
436 				currvalue--;
437 				barupdate = true;
438 			}
439 			break;
440 		case KEY_F(1):
441 			if (conf->hfile == NULL)
442 				break;
443 			if (f1help(conf) != 0)
444 				return BSDDIALOG_ERROR;
445 			/* No break! the terminal size can change */
446 		case KEY_RESIZE:
447 			hide_widget(y, x, h, w,conf->shadow);
448 
449 			/*
450 			 * Unnecessary, but, when the columns decrease the
451 			 * following "refresh" seem not work
452 			 */
453 			refresh();
454 
455 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
456 				return BSDDIALOG_ERROR;
457 			if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
458 				return BSDDIALOG_ERROR;
459 			if (bar_checksize(text, h, w, &bs) != 0)
460 				return BSDDIALOG_ERROR;
461 			if (set_widget_position(conf, &y, &x, h, w) != 0)
462 				return BSDDIALOG_ERROR;
463 
464 			wclear(shadow);
465 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
466 			wresize(shadow, h, w);
467 
468 			wclear(widget);
469 			mvwin(widget, y, x);
470 			wresize(widget, h, w);
471 
472 			htextpad = 1;
473 			wclear(textpad);
474 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
475 
476 			sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
477 			bigchange = MAX(1, sizebar/10);
478 			wclear(bar);
479 			mvwin(bar, y + h - 6, x + 1 + BARMARGIN);
480 			wresize(bar, 3, sizebar + 2);
481 
482 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
483 			    RAISED, textpad, &htextpad, text, true) != 0)
484 				return BSDDIALOG_ERROR;
485 
486 			prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
487 			    x+w-1-t.text.hmargin);
488 
489 			draw_borders(conf, bar, 3, sizebar + 2, RAISED);
490 
491 			barupdate = true;
492 			buttupdate = true;
493 			break;
494 		default:
495 			for (i = 0; i < (int) bs.nbuttons; i++)
496 				if (tolower(input) == tolower((bs.label[i])[0])) {
497 					output = bs.value[i];
498 					loop = false;
499 			}
500 		}
501 	}
502 
503 	delwin(bar);
504 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
505 
506 	return output;
507 }
508 
509 int
bsddialog_pause(struct bsddialog_conf * conf,char * text,int rows,int cols,unsigned int sec)510 bsddialog_pause(struct bsddialog_conf *conf, char* text, int rows, int cols,
511     unsigned int sec)
512 {
513 	WINDOW *widget, *textpad, *bar, *shadow;
514 	int i, output, y, x, h, w, htextpad;
515 	bool loop, buttupdate, barupdate;
516 	int input, tout, sizebar;
517 	float perc;
518 	struct buttons bs;
519 
520 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
521 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
522 
523 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
524 		return BSDDIALOG_ERROR;
525 	if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
526 		return BSDDIALOG_ERROR;
527 	if (bar_checksize(text, h, w, &bs) != 0)
528 		return BSDDIALOG_ERROR;
529 	if (set_widget_position(conf, &y, &x, h, w) != 0)
530 		return BSDDIALOG_ERROR;
531 
532 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
533 	    &textpad, &htextpad, text, true) != 0)
534 		return BSDDIALOG_ERROR;
535 
536 	prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
537 	    x+w-1-t.text.hmargin);
538 
539 	sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
540 	bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3,
541 	    sizebar + 2, RAISED);
542 
543 	tout = sec;
544 	nodelay(stdscr, TRUE);
545 	timeout(1000);
546 	loop = buttupdate = barupdate = true;
547 	while(loop) {
548 		if (barupdate) {
549 			perc = (float)tout * 100 / sec;
550 			draw_perc_bar(bar, 1, 1, sizebar, perc, true, tout);
551 			barupdate = false;
552 			wrefresh(bar);
553 		}
554 
555 		if (buttupdate) {
556 			draw_buttons(widget, h-2, w, bs, true);
557 			wrefresh(widget);
558 			buttupdate = false;
559 		}
560 
561 		input = getch();
562 		if(input < 0) { /* timeout */
563 			tout--;
564 			if (tout < 0) {
565 				output = BSDDIALOG_TIMEOUT;
566 				break;
567 			}
568 			else {
569 				barupdate = true;
570 				continue;
571 			}
572 		}
573 		switch(input) {
574 		case KEY_ENTER:
575 		case 10: /* Enter */
576 			output = bs.value[bs.curr];
577 			loop = false;
578 			break;
579 		case 27: /* Esc */
580 			output = BSDDIALOG_ESC;
581 			loop = false;
582 			break;
583 		case '\t': /* TAB */
584 			bs.curr = (bs.curr + 1) % bs.nbuttons;
585 			buttupdate = true;
586 			break;
587 		case KEY_LEFT:
588 			if (bs.curr > 0) {
589 				bs.curr--;
590 				buttupdate = true;
591 			}
592 			break;
593 		case KEY_RIGHT:
594 			if (bs.curr < (int) bs.nbuttons - 1) {
595 				bs.curr++;
596 				buttupdate = true;
597 			}
598 			break;
599 		case KEY_F(1):
600 			if (conf->hfile == NULL)
601 				break;
602 			if (f1help(conf) != 0)
603 				return BSDDIALOG_ERROR;
604 			/* No break! the terminal size can change */
605 		case KEY_RESIZE:
606 			hide_widget(y, x, h, w,conf->shadow);
607 
608 			/*
609 			 * Unnecessary, but, when the columns decrease the
610 			 * following "refresh" seem not work
611 			 */
612 			refresh();
613 
614 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
615 				return BSDDIALOG_ERROR;
616 			if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
617 				return BSDDIALOG_ERROR;
618 			if (bar_checksize(text, h, w, &bs) != 0)
619 				return BSDDIALOG_ERROR;
620 			if (set_widget_position(conf, &y, &x, h, w) != 0)
621 				return BSDDIALOG_ERROR;
622 
623 			wclear(shadow);
624 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
625 			wresize(shadow, h, w);
626 
627 			wclear(widget);
628 			mvwin(widget, y, x);
629 			wresize(widget, h, w);
630 
631 			htextpad = 1;
632 			wclear(textpad);
633 			wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2);
634 
635 			sizebar = w - HBORDERS - 2 - BARMARGIN * 2;
636 			wclear(bar);
637 			mvwin(bar, y + h - 6, x + 1 + BARMARGIN);
638 			wresize(bar, 3, sizebar + 2);
639 
640 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
641 			    RAISED, textpad, &htextpad, text, true) != 0)
642 				return BSDDIALOG_ERROR;
643 
644 			prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7,
645 			    x+w-1-t.text.hmargin);
646 
647 			draw_borders(conf, bar, 3, sizebar + 2, RAISED);
648 
649 			barupdate = true;
650 			buttupdate = true;
651 			break;
652 		default:
653 			for (i = 0; i < (int) bs.nbuttons; i++)
654 				if (tolower(input) == tolower((bs.label[i])[0])) {
655 					output = bs.value[i];
656 					loop = false;
657 			}
658 		}
659 	}
660 
661 	nodelay(stdscr, FALSE);
662 
663 	delwin(bar);
664 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
665 
666 	return output;
667 }
668