xref: /freebsd/contrib/bsddialog/lib/lib_util.c (revision 44682688f038edbf34591b25ce36412a7f2d6d07)
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 #ifdef PORTNCURSES
31 #include <ncurses/ncurses.h>
32 #else
33 #include <ncurses.h>
34 #endif
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "bsddialog.h"
40 #include "lib_util.h"
41 #include "bsddialog_theme.h"
42 
43 extern struct bsddialog_theme t;
44 
45 /* Error buffer */
46 
47 #define ERRBUFLEN 1024
48 static char errorbuffer[ERRBUFLEN];
49 
50 const char *get_error_string(void)
51 {
52 	return errorbuffer;
53 }
54 
55 void set_error_string(char *str)
56 {
57 
58 	strncpy(errorbuffer, str, ERRBUFLEN-1);
59 }
60 
61 /* cleaner */
62 int hide_widget(int y, int x, int h, int w, bool withshadow)
63 {
64 	WINDOW *clear;
65 
66 	/* no check: y, x, h and w are checked by the builders */
67 	if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL)
68 		RETURN_ERROR("Cannot hide the widget");
69 	wbkgd(clear, t.terminal.color);
70 
71 	if (withshadow)
72 		wrefresh(clear);
73 
74 	mvwin(clear, y, x);
75 	wrefresh(clear);
76 
77 	delwin(clear);
78 
79 	return 0;
80 }
81 
82 /* F1 help */
83 int f1help(struct bsddialog_conf *conf)
84 {
85 	int output;
86 	struct bsddialog_conf hconf;
87 
88 	//memcpy(&hconf, conf, sizeof(struct bsddialog_conf));
89 	bsddialog_initconf(&hconf);
90 	hconf.title = "HELP";
91 	hconf.button.ok_label = "EXIT";
92 	hconf.clear = true;
93 	hconf.ascii_lines = conf->ascii_lines;
94 	hconf.no_lines = conf->no_lines;
95 	hconf.shadow = conf->shadow;
96 	hconf.text.colors = conf->text.colors;
97 
98 	output = BSDDIALOG_OK;
99 	if (conf->f1_message != NULL)
100 		output = bsddialog_msgbox(&hconf, conf->f1_message, 0, 0);
101 
102 	if (output != BSDDIALOG_ERROR && conf->f1_file != NULL)
103 		output = bsddialog_textbox(&hconf, conf->f1_file, 0, 0);
104 
105 	return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0);
106 }
107 
108 /* Buttons */
109 void
110 draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected,
111     bool shortkey)
112 {
113 	int i, color_arrows, color_shortkey, color_button;
114 
115 	if (selected) {
116 		color_arrows = t.button.f_delimcolor;
117 		color_shortkey = t.button.f_shortcutcolor;
118 		color_button = t.button.f_color;
119 	} else {
120 		color_arrows = t.button.delimcolor;
121 		color_shortkey = t.button.shortcutcolor;
122 		color_button = t.button.color;
123 	}
124 
125 	wattron(window, color_arrows);
126 	mvwaddch(window, y, x, t.button.leftch);
127 	wattroff(window, color_arrows);
128 	wattron(window, color_button);
129 	for(i = 1; i < size - 1; i++)
130 		waddch(window, ' ');
131 	wattroff(window, color_button);
132 	wattron(window, color_arrows);
133 	mvwaddch(window, y, x + i, t.button.rightch);
134 	wattroff(window, color_arrows);
135 
136 	x = x + 1 + ((size - 2 - strlen(text))/2);
137 	wattron(window, color_button);
138 	mvwaddstr(window, y, x, text);
139 	wattroff(window, color_button);
140 
141 	if (shortkey) {
142 		wattron(window, color_shortkey);
143 		mvwaddch(window, y, x, text[0]);
144 		wattroff(window, color_shortkey);
145 	}
146 }
147 
148 void
149 draw_buttons(WINDOW *window, int y, int cols, struct buttons bs, bool shortkey)
150 {
151 	int i, x, start_x;
152 
153 	start_x = bs.sizebutton * bs.nbuttons + (bs.nbuttons - 1) * t.button.space;
154 	start_x = cols/2 - start_x/2;
155 
156 	for (i = 0; i < (int) bs.nbuttons; i++) {
157 		x = i * (bs.sizebutton + t.button.space);
158 		draw_button(window, y, start_x + x, bs.sizebutton, bs.label[i],
159 		    i == bs.curr, shortkey);
160 	}
161 }
162 
163 void
164 get_buttons(struct bsddialog_conf *conf, struct buttons *bs, char *yesoklabel,
165     char *extralabel, char *nocancellabel, char *helplabel)
166 {
167 	int i;
168 #define SIZEBUTTON  8
169 #define DEFAULT_BUTTON_LABEL	LABEL_ok_label
170 #define DEFAULT_BUTTON_VALUE	BSDDIALOG_OK
171 
172 
173 	bs->nbuttons = 0;
174 	bs->curr = 0;
175 	bs->sizebutton = 0;
176 
177 	if (yesoklabel != NULL && conf->button.without_ok == false) {
178 		bs->label[0] = yesoklabel;
179 		bs->value[0] = BSDDIALOG_OK;
180 		bs->nbuttons += 1;
181 	}
182 
183 	if (extralabel != NULL && conf->button.with_extra) {
184 		bs->label[bs->nbuttons] = extralabel;
185 		bs->value[bs->nbuttons] = BSDDIALOG_EXTRA;
186 		bs->nbuttons += 1;
187 	}
188 
189 	if (nocancellabel != NULL && conf->button.without_cancel == false) {
190 		bs->label[bs->nbuttons] = nocancellabel;
191 		bs->value[bs->nbuttons] = BSDDIALOG_CANCEL;
192 		if (conf->button.default_cancel)
193 			bs->curr = bs->nbuttons;
194 		bs->nbuttons += 1;
195 	}
196 
197 	if (helplabel != NULL && conf->button.with_help) {
198 		bs->label[bs->nbuttons] = helplabel;
199 		bs->value[bs->nbuttons] = BSDDIALOG_HELP;
200 		bs->nbuttons += 1;
201 	}
202 
203 	if (conf->button.generic1_label != NULL) {
204 		bs->label[bs->nbuttons] = conf->button.generic1_label;
205 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1;
206 		bs->nbuttons += 1;
207 	}
208 
209 	if (conf->button.generic2_label != NULL) {
210 		bs->label[bs->nbuttons] = conf->button.generic2_label;
211 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2;
212 		bs->nbuttons += 1;
213 	}
214 
215 	if (bs->nbuttons == 0) {
216 		bs->label[0] = DEFAULT_BUTTON_LABEL;
217 		bs->value[0] = DEFAULT_BUTTON_VALUE;
218 		bs->nbuttons = 1;
219 	}
220 
221 	if (conf->button.default_label != NULL) {
222 		for (i=0; i<(int)bs->nbuttons; i++) {
223 			if (strcmp(conf->button.default_label, bs->label[i]) == 0)
224 				bs->curr = i;
225 		}
226 	}
227 
228 	bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0]));
229 	for (i=1; i < (int) bs->nbuttons; i++)
230 		bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i]));
231 	bs->sizebutton += 2;
232 }
233 
234 /* Text */
235 static bool is_ncurses_attr(char *text)
236 {
237 
238 	if (strnlen(text, 3) < 3)
239 		return false;
240 
241 	if (text[0] != '\\' || text[1] != 'Z')
242 		return false;
243 
244 	return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true);
245 }
246 
247 static bool check_set_ncurses_attr(WINDOW *win, char *text)
248 {
249 
250 	if (is_ncurses_attr(text) == false)
251 		return false;
252 
253 	if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) {
254 		wattron(win, bsddialog_color( text[2] - '0', COLOR_WHITE, 0));
255 		return true;
256 	}
257 
258 	switch (text[2]) {
259 	case 'n':
260 		wattrset(win, A_NORMAL);
261 		break;
262 	case 'b':
263 		wattron(win, A_BOLD);
264 		break;
265 	case 'B':
266 		wattroff(win, A_BOLD);
267 		break;
268 	case 'r':
269 		wattron(win, A_REVERSE);
270 		break;
271 	case 'R':
272 		wattroff(win, A_REVERSE);
273 		break;
274 	case 'u':
275 		wattron(win, A_UNDERLINE);
276 		break;
277 	case 'U':
278 		wattroff(win, A_UNDERLINE);
279 		break;
280 	}
281 
282 	return true;
283 }
284 
285 static void
286 print_str(WINDOW *win, int *rows, int *y, int *x, int cols, char *str, bool color)
287 {
288 	int i, j, len, reallen;
289 
290 	if(strlen(str) == 0)
291 		return;
292 
293 	len = reallen = strlen(str);
294 	if (color) {
295 		i=0;
296 		while (i < len) {
297 			if (is_ncurses_attr(str+i))
298 				reallen -= 3;
299 			i++;
300 		}
301 	}
302 
303 	i = 0;
304 	while (i < len) {
305 		if (*x + reallen > cols) {
306 			*y = (*x != 0 ? *y+1 : *y);
307 			if (*y >= *rows) {
308 				*rows = *y + 1;
309 				wresize(win, *rows, cols);
310 			}
311 			*x = 0;
312 		}
313 		j = *x;
314 		while (j < cols && i < len) {
315 			if (color && check_set_ncurses_attr(win, str+i)) {
316 				i += 3;
317 			} else {
318 				mvwaddch(win, *y, j, str[i]);
319 				i++;
320 				reallen--;
321 				j++;
322 				*x = j;
323 			}
324 		}
325 	}
326 }
327 
328 int
329 get_text_properties(struct bsddialog_conf *conf, char *text, int *maxword,
330     int *maxline, int *nlines)
331 {
332 	int i, buflen, wordlen, linelen;
333 
334 
335 	buflen = strlen(text) + 1;
336 	*maxword = 0;
337 	wordlen = 0;
338 	for (i=0; i < buflen; i++) {
339 		if (text[i] == '\t' || text[i] == '\n' || text[i] == ' ' || text[i] == '\0')
340 			if (wordlen != 0) {
341 				*maxword = MAX(*maxword, wordlen);
342 				wordlen = 0;
343 				continue;
344 			}
345 		if (conf->text.colors && is_ncurses_attr(text + i))
346 			i += 3;
347 		else
348 			wordlen++;
349 	}
350 
351 	*maxline = linelen = 0;
352 	*nlines = 1;
353 	for (i=0; i < buflen; i++) {
354 		switch (text[i]) {
355 		case '\n':
356 			*nlines = *nlines + 1;
357 		case '\0':
358 			*maxline = MAX(*maxline, linelen);
359 			linelen = 0;
360 			break;
361 		default:
362 			if (conf->text.colors && is_ncurses_attr(text + i))
363 				i += 3;
364 			else
365 				linelen++;
366 		}
367 	}
368 	if (*nlines == 1 && *maxline == 0)
369 		*nlines = 0;
370 
371 	//free(buf);
372 
373 	return 0;
374 }
375 
376 int
377 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, int *rows, int cols,
378     char *text)
379 {
380 	char *string;
381 	int i, j, x, y;
382 	bool loop;
383 
384 	if ((string = malloc(strlen(text) + 1)) == NULL)
385 		RETURN_ERROR("Cannot build (analyze) text");
386 
387 	i = j = x = y = 0;
388 	loop = true;
389 	while (loop) {
390 		string[j] = text[i];
391 
392 		if (string[j] == '\0' || string[j] == '\n' ||
393 		    string[j] == '\t' || string[j] == ' ') {
394 			if (j != 0) {
395 				string[j] = '\0';
396 				print_str(pad, rows, &y, &x, cols, string,
397 				    conf->text.colors);
398 			}
399 		}
400 
401 		switch (text[i]) {
402 		case '\0':
403 			loop = false;
404 			break;
405 		case '\n':
406 			j = -1;
407 			x = 0;
408 			y++;
409 			break;
410 		case '\t':
411 			for (j=0; j<4 /*tablen*/; j++) {
412 				x++;
413 				if (x >= cols) {
414 					x = 0;
415 					y++;
416 				}
417 			}
418 			j = -1;
419 			break;
420 		case ' ':
421 			x++;
422 			if (x >= cols) {
423 				x = 0;
424 				y++;
425 			}
426 			j = -1;
427 		}
428 
429 		if (y >= *rows) { /* check for whitespaces */
430 			*rows = y + 1;
431 			wresize(pad, *rows, cols);
432 		}
433 
434 		j++;
435 		i++;
436 	}
437 
438 	free(string);
439 
440 	return 0;
441 }
442 
443 /* autosize */
444 
445 /*
446  * max y, that is from 0 to LINES - 1 - t.shadowrows,
447  * could not be max height but avoids problems with checksize
448  */
449 int widget_max_height(struct bsddialog_conf *conf)
450 {
451 	int maxheight;
452 
453 	if ((maxheight = conf->shadow ? LINES - 1 - t.shadow.h : LINES - 1) <= 0)
454 		RETURN_ERROR("Terminal too small, LINES - shadow <= 0");
455 
456 	if (conf->y > 0)
457 		if ((maxheight -= conf->y) <=0)
458 			RETURN_ERROR("Terminal too small, LINES - shadow - y <= 0");
459 
460 	return maxheight;
461 }
462 
463 /*
464  * max x, that is from 0 to COLS - 1 - t.shadowcols,
465  *  * could not be max height but avoids problems with checksize
466  */
467 int widget_max_width(struct bsddialog_conf *conf)
468 {
469 	int maxwidth;
470 
471 	if ((maxwidth = conf->shadow ? COLS - 1 - t.shadow.w : COLS - 1)  <= 0)
472 		RETURN_ERROR("Terminal too small, COLS - shadow <= 0");
473 	if (conf->x > 0)
474 		if ((maxwidth -= conf->x) <=0)
475 			RETURN_ERROR("Terminal too small, COLS - shadow - x <= 0");
476 
477 	return maxwidth;
478 }
479 
480 int
481 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
482 {
483 	int maxheight, maxwidth;
484 
485 	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
486 		return BSDDIALOG_ERROR;
487 
488 	if (rows == BSDDIALOG_FULLSCREEN)
489 		*h = maxheight;
490 	else if (rows < BSDDIALOG_FULLSCREEN)
491 		RETURN_ERROR("Negative (less than -1) height");
492 	else if (rows > BSDDIALOG_AUTOSIZE) {
493 		if ((*h = rows) > maxheight)
494 			RETURN_ERROR("Height too big (> terminal height - "\
495 			    "shadow");
496 	}
497 	/* rows == AUTOSIZE: each widget has to set its size */
498 
499 	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
500 		return BSDDIALOG_ERROR;
501 
502 	if (cols == BSDDIALOG_FULLSCREEN)
503 		*w = maxwidth;
504 	else if (cols < BSDDIALOG_FULLSCREEN)
505 		RETURN_ERROR("Negative (less than -1) width");
506 	else if (cols > BSDDIALOG_AUTOSIZE) {
507 		if ((*w = cols) > maxwidth)
508 			RETURN_ERROR("Width too big (> terminal width - shadow)");
509 	}
510 	/* cols == AUTOSIZE: each widget has to set its size */
511 
512 	return 0;
513 }
514 
515 int
516 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
517 {
518 
519 	if (conf->y == BSDDIALOG_CENTER)
520 		*y = LINES/2 - h/2;
521 	else if (conf->y < BSDDIALOG_CENTER)
522 		RETURN_ERROR("Negative begin y (less than -1)");
523 	else if (conf->y >= LINES)
524 		RETURN_ERROR("Begin Y under the terminal");
525 	else
526 		*y = conf->y;
527 
528 	if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > LINES)
529 		RETURN_ERROR("The lower of the box under the terminal "\
530 		    "(begin Y + height (+ shadow) > terminal lines)");
531 
532 
533 	if (conf->x == BSDDIALOG_CENTER)
534 		*x = COLS/2 - w/2;
535 	else if (conf->x < BSDDIALOG_CENTER)
536 		RETURN_ERROR("Negative begin x (less than -1)");
537 	else if (conf->x >= COLS)
538 		RETURN_ERROR("Begin X over the right of the terminal");
539 	else
540 		*x = conf->x;
541 
542 	if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > COLS)
543 		RETURN_ERROR("The right of the box over the terminal "\
544 		    "(begin X + width (+ shadow) > terminal cols)");
545 
546 	return 0;
547 }
548 
549 /* Widgets builders */
550 void
551 draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols,
552     enum elevation elev)
553 {
554 	int leftcolor, rightcolor;
555 	int ls, rs, ts, bs, tl, tr, bl, br;
556 	int ltee, rtee;
557 
558 	ls = rs = ACS_VLINE;
559 	ts = bs = ACS_HLINE;
560 	tl = ACS_ULCORNER;
561 	tr = ACS_URCORNER;
562 	bl = ACS_LLCORNER;
563 	br = ACS_LRCORNER;
564 	ltee = ACS_LTEE;
565 	rtee = ACS_RTEE;
566 
567 	if (conf->no_lines == false) {
568 		if (conf->ascii_lines) {
569 			ls = rs = '|';
570 			ts = bs = '-';
571 			tl = tr = bl = br = ltee = rtee = '+';
572 		}
573 		leftcolor  = elev == RAISED ?
574 		    t.dialog.lineraisecolor : t.dialog.linelowercolor;
575 		rightcolor = elev == RAISED ?
576 		    t.dialog.linelowercolor : t.dialog.lineraisecolor;
577 		wattron(win, leftcolor);
578 		wborder(win, ls, rs, ts, bs, tl, tr, bl, br);
579 		wattroff(win, leftcolor);
580 
581 		wattron(win, rightcolor);
582 		mvwaddch(win, 0, cols-1, tr);
583 		mvwvline(win, 1, cols-1, rs, rows-2);
584 		mvwaddch(win, rows-1, cols-1, br);
585 		mvwhline(win, rows-1, 1, bs, cols-2);
586 		wattroff(win, rightcolor);
587 	}
588 }
589 
590 WINDOW *
591 new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols,
592     enum elevation elev)
593 {
594 	WINDOW *win;
595 
596 	if ((win = newwin(rows, cols, y, x)) == NULL) {
597 		set_error_string("Cannot build boxed window");
598 		return NULL;
599 	}
600 
601 	wbkgd(win, t.dialog.color);
602 
603 	draw_borders(conf, win, rows, cols, elev);
604 
605 	return win;
606 }
607 
608 /*
609  * `enum elevation elev` could be useless because it should be always RAISED,
610  * to check at the end.
611  */
612 static int
613 draw_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow,
614     WINDOW *widget, int h, int w, enum elevation elev,
615     WINDOW *textpad, int *htextpad, char *text, bool buttons)
616 {
617 	int ts, ltee, rtee;
618 	int colordelimtitle;
619 
620 	ts = conf->ascii_lines ? '-' : ACS_HLINE;
621 	ltee = conf->ascii_lines ? '+' : ACS_LTEE;
622 	rtee = conf->ascii_lines ? '+' : ACS_RTEE;
623 	colordelimtitle = elev == RAISED ?
624 	    t.dialog.lineraisecolor : t.dialog.linelowercolor;
625 
626 	if (shadow != NULL)
627 		wnoutrefresh(shadow);
628 
629 	// move / resize now or the caller?
630 	draw_borders(conf, widget, h, w, elev);
631 
632 	if (conf->title != NULL) {
633 		if (t.dialog.delimtitle && conf->no_lines == false) {
634 			wattron(widget, colordelimtitle);
635 			mvwaddch(widget, 0, w/2 - strlen(conf->title)/2 - 1, rtee);
636 			wattroff(widget, colordelimtitle);
637 		}
638 		wattron(widget, t.dialog.titlecolor);
639 		mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title);
640 		wattroff(widget, t.dialog.titlecolor);
641 		if (t.dialog.delimtitle && conf->no_lines == false) {
642 			wattron(widget, colordelimtitle);
643 			waddch(widget, ltee);
644 			wattroff(widget, colordelimtitle);
645 		}
646 	}
647 
648 	if (conf->bottomtitle != NULL) {
649 		wattron(widget, t.dialog.bottomtitlecolor);
650 		wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1);
651 		waddch(widget, '[');
652 		waddstr(widget, conf->bottomtitle);
653 		waddch(widget, ']');
654 		wattroff(widget, t.dialog.bottomtitlecolor);
655 	}
656 
657 	//if (textpad == NULL && text != NULL) /* no pad, text null for textbox */
658 	//	print_text(conf, widget, 1, 2, w-3, text);
659 
660 	if (buttons && conf->no_lines == false) {
661 		wattron(widget, t.dialog.lineraisecolor);
662 		mvwaddch(widget, h-3, 0, ltee);
663 		mvwhline(widget, h-3, 1, ts, w-2);
664 		wattroff(widget, t.dialog.lineraisecolor);
665 
666 		wattron(widget, t.dialog.linelowercolor);
667 		mvwaddch(widget, h-3, w-1, rtee);
668 		wattroff(widget, t.dialog.linelowercolor);
669 	}
670 
671 	wnoutrefresh(widget);
672 
673 	if (textpad == NULL)
674 		return 0; /* widget_init() ends */
675 
676 	if (text != NULL) /* programbox etc */
677 		if (print_textpad(conf, textpad, htextpad,
678 		    w - HBORDERS - t.text.hmargin * 2, text) !=0)
679 			return BSDDIALOG_ERROR;
680 
681 	return 0;
682 }
683 
684 /*
685  * `enum elevation elev` could be useless because it should be always RAISED,
686  * to check at the end.
687  */
688 int
689 update_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow,
690     WINDOW *widget, int h, int w, enum elevation elev,
691     WINDOW *textpad, int *htextpad, char *text, bool buttons)
692 {
693 	int error;
694 
695 	/* nothing for now */
696 
697 	error =  draw_widget_withtextpad(conf, shadow, widget, h, w,
698 	    elev, textpad, htextpad, text, buttons);
699 
700 	return error;
701 }
702 
703 /*
704  * `enum elevation elev` could be useless because it should be always RAISED,
705  * to check at the end.
706  */
707 int
708 new_widget_withtextpad(struct bsddialog_conf *conf, WINDOW **shadow,
709     WINDOW **widget, int y, int x, int h, int w, enum elevation elev,
710     WINDOW **textpad, int *htextpad, char *text, bool buttons)
711 {
712 	int error;
713 
714 	if (conf->shadow) {
715 		*shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w);
716 		if (*shadow == NULL)
717 			RETURN_ERROR("Cannot build shadow");
718 		wbkgd(*shadow, t.shadow.color);
719 	}
720 
721 	if ((*widget = new_boxed_window(conf, y, x, h, w, elev)) == NULL) {
722 		if (conf->shadow)
723 			delwin(*shadow);
724 		return BSDDIALOG_ERROR;
725 	}
726 
727 	if (textpad == NULL) { /* widget_init() */
728 		error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w,
729 		    elev, NULL, NULL, text, buttons);
730 		return error;
731 	}
732 
733 	if (text != NULL) { /* programbox etc */
734 		*htextpad = 1;
735 		*textpad = newpad(*htextpad, w - HBORDERS - t.text.hmargin * 2);
736 		if (*textpad == NULL) {
737 			delwin(*textpad);
738 			if (conf->shadow)
739 				delwin(*shadow);
740 			RETURN_ERROR("Cannot build the pad window for text");
741 		}
742 		wbkgd(*textpad, t.dialog.color);
743 	}
744 
745 	error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w, elev,
746 	    *textpad, htextpad, text, buttons);
747 
748 	return error;
749 }
750 
751 void
752 end_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *window, int h, int w,
753     WINDOW *textpad, WINDOW *shadow)
754 {
755 	int y, x;
756 
757 	getbegyx(window, y, x); /* for clear, add y & x to args? */
758 
759 	if (conf->sleep > 0)
760 		sleep(conf->sleep);
761 
762 	if (textpad != NULL)
763 		delwin(textpad);
764 
765 	delwin(window);
766 
767 	if (conf->shadow)
768 		delwin(shadow);
769 
770 	if (conf->clear)
771 		hide_widget(y, x, h, w, shadow != NULL);
772 
773 	if (conf->get_height != NULL)
774 		*conf->get_height = h;
775 	if (conf->get_width != NULL)
776 		*conf->get_width = w;
777 }
778