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