xref: /freebsd/contrib/bsddialog/lib/lib_util.c (revision 52d973f52c07b94909a6487be373c269988dc151)
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 <stdlib.h>
31 #include <string.h>
32 #include <unistd.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 extern struct bsddialog_theme t;
45 
46 /* Error buffer */
47 
48 #define ERRBUFLEN 1024
49 static char errorbuffer[ERRBUFLEN];
50 
51 const char *get_error_string(void)
52 {
53 	return errorbuffer;
54 }
55 
56 void set_error_string(char *str)
57 {
58 
59 	strncpy(errorbuffer, str, ERRBUFLEN-1);
60 }
61 
62 /* cleaner */
63 int hide_widget(int y, int x, int h, int w, bool withshadow)
64 {
65 	WINDOW *clear;
66 
67 	/* no check: y, x, h and w are checked by the builders */
68 	if ((clear = newwin(h, w, y + t.shadowrows, x + t.shadowcols)) == NULL)
69 		RETURN_ERROR("Cannot hide the widget");
70 	wbkgd(clear, t.backgroundcolor);
71 
72 	if (withshadow)
73 		wrefresh(clear);
74 
75 	mvwin(clear, y, x);
76 	wrefresh(clear);
77 
78 	delwin(clear);
79 
80 	return 0;
81 }
82 
83 /* F1 help */
84 int f1help(struct bsddialog_conf conf)
85 {
86 	char *file = conf.hfile;
87 	char *title = conf.title;
88 	int output;
89 
90 	conf.hfile = NULL;
91 	conf.clear = true;
92 	conf.y = BSDDIALOG_CENTER;
93 	conf.x = BSDDIALOG_CENTER;
94 	conf.title = "HELP";
95 	conf.sleep = 0;
96 
97 	output = bsddialog_textbox(conf, file, BSDDIALOG_AUTOSIZE,
98 	    BSDDIALOG_AUTOSIZE);
99 	conf.hfile = file;
100 	conf.title = title;
101 
102 	return output;
103 }
104 
105 /* Buttons */
106 void
107 draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected,
108     bool shortkey)
109 {
110 	int i, color_arrows, color_shortkey, color_button;
111 
112 	if (selected) {
113 		color_arrows = t.currbuttdelimcolor;
114 		color_shortkey = t.currshortkeycolor;
115 		color_button = t.currbuttoncolor;
116 	} else {
117 		color_arrows = t.buttdelimcolor;
118 		color_shortkey = t.shortkeycolor;
119 		color_button = t.buttoncolor;
120 	}
121 
122 	wattron(window, color_arrows);
123 	mvwaddch(window, y, x, t.buttleftch);
124 	wattroff(window, color_arrows);
125 	wattron(window, color_button);
126 	for(i = 1; i < size - 1; i++)
127 		waddch(window, ' ');
128 	wattroff(window, color_button);
129 	wattron(window, color_arrows);
130 	mvwaddch(window, y, x + i, t.buttrightchar);
131 	wattroff(window, color_arrows);
132 
133 	x = x + 1 + ((size - 2 - strlen(text))/2);
134 	wattron(window, color_button);
135 	mvwaddstr(window, y, x, text);
136 	wattroff(window, color_button);
137 
138 	if (shortkey) {
139 		wattron(window, color_shortkey);
140 		mvwaddch(window, y, x, text[0]);
141 		wattroff(window, color_shortkey);
142 	}
143 }
144 
145 void
146 draw_buttons(WINDOW *window, int y, int cols, struct buttons bs, bool shortkey)
147 {
148 	int i, x, start_x;
149 
150 	start_x = bs.sizebutton * bs.nbuttons + (bs.nbuttons - 1) * t.buttonspace;
151 	start_x = cols/2 - start_x/2;
152 
153 	for (i = 0; i < (int) bs.nbuttons; i++) {
154 		x = i * (bs.sizebutton + t.buttonspace);
155 		draw_button(window, y, start_x + x, bs.sizebutton, bs.label[i],
156 		    i == bs.curr, shortkey);
157 	}
158 }
159 
160 void
161 get_buttons(struct bsddialog_conf conf, struct buttons *bs, char *yesoklabel,
162     char *extralabel, char *nocancellabel, char *helplabel)
163 {
164 	int i;
165 #define SIZEBUTTON  8
166 #define DEFAULT_BUTTON_LABEL	LABEL_ok_label
167 #define DEFAULT_BUTTON_VALUE	BSDDIALOG_YESOK
168 
169 
170 	bs->nbuttons = 0;
171 	bs->curr = 0;
172 	bs->sizebutton = 0;
173 
174 	if (yesoklabel != NULL && conf.button.no_ok == false) {
175 		bs->label[0] = yesoklabel;
176 		bs->value[0] = BSDDIALOG_YESOK;
177 		bs->nbuttons += 1;
178 	}
179 
180 	if (extralabel != NULL && conf.button.extra_button) {
181 		bs->label[bs->nbuttons] = extralabel;
182 		bs->value[bs->nbuttons] = BSDDIALOG_EXTRA;
183 		bs->nbuttons += 1;
184 	}
185 
186 	if (nocancellabel != NULL && conf.button.no_cancel == false) {
187 		bs->label[bs->nbuttons] = nocancellabel;
188 		bs->value[bs->nbuttons] = BSDDIALOG_NOCANCEL;
189 		if (conf.button.defaultno)
190 			bs->curr = bs->nbuttons;
191 		bs->nbuttons += 1;
192 	}
193 
194 	if (helplabel != NULL && conf.button.help_button) {
195 		bs->label[bs->nbuttons] = helplabel;
196 		bs->value[bs->nbuttons] = BSDDIALOG_HELP;
197 		bs->nbuttons += 1;
198 	}
199 
200 	if (bs->nbuttons == 0) {
201 		bs->label[0] = DEFAULT_BUTTON_LABEL;
202 		bs->value[0] = DEFAULT_BUTTON_VALUE;
203 		bs->nbuttons = 1;
204 	}
205 
206 	if (conf.button.default_label != NULL) {
207 		for (i=0; i<(int)bs->nbuttons; i++) {
208 			if (strcmp(conf.button.default_label, bs->label[i]) == 0)
209 				bs->curr = i;
210 		}
211 	}
212 
213 	bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0]));
214 	for (i=1; i < (int) bs->nbuttons; i++)
215 		bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i]));
216 	bs->sizebutton += 2;
217 }
218 
219 /* Text */
220 
221 // old text, to delete in the future
222 enum token { TEXT, WS, END };
223 
224 static bool check_set_ncurses_attr(WINDOW *win, char *text)
225 {
226 	bool isattr;
227 	int colors[8] = {
228 	    COLOR_BLACK,
229 	    COLOR_RED,
230 	    COLOR_GREEN,
231 	    COLOR_YELLOW,
232 	    COLOR_BLUE,
233 	    COLOR_MAGENTA,
234 	    COLOR_CYAN,
235 	    COLOR_WHITE
236 	};
237 
238 	if (text[0] == '\0' || text[0] != '\\')
239 		return false;
240 	if (text[1] == '\0' || text[1] != 'Z')
241 		return false;
242 	if (text[2] == '\0')
243 		return false;
244 
245 	if ((text[2] - 48) >= 0 && (text[2] - 48) < 8) {
246 		// tocheck: import BSD_COLOR
247 		// tofix color background
248 		wattron(win, COLOR_PAIR(colors[text[2] - 48] * 8 + COLOR_WHITE + 1));
249 		return true;
250 	}
251 
252 	isattr = true;
253 	switch (text[2]) {
254 	case 'n':
255 		wattrset(win, A_NORMAL);
256 		break;
257 	case 'b':
258 		wattron(win, A_BOLD);
259 		break;
260 	case 'B':
261 		wattroff(win, A_BOLD);
262 		break;
263 	case 'r':
264 		wattron(win, A_REVERSE);
265 		break;
266 	case 'R':
267 		wattroff(win, A_REVERSE);
268 		break;
269 	case 'u':
270 		wattron(win, A_UNDERLINE);
271 		break;
272 	case 'U':
273 		wattroff(win, A_UNDERLINE);
274 		break;
275 	default:
276 		isattr = false;
277 	}
278 
279 	return isattr;
280 }
281 
282 static bool isws(int ch)
283 {
284 
285 	return (ch == ' ' || ch == '\t' || ch == '\n');
286 }
287 
288 static int
289 next_token(char *text, char *valuestr)
290 {
291 	int i, j;
292 	enum token tok;
293 
294 	i = j = 0;
295 
296 	if (text[0] == '\0')
297 		return END;
298 
299 	while (text[i] != '\0') {
300 		if (isws(text[i])) {
301 			if (i == 0) {
302 				valuestr[0] = text[i];
303 				valuestr[1] = '\0';
304 				tok = WS;
305 			}
306 			break;
307 		}
308 
309 		valuestr[j] = text[i];
310 		j++;
311 		valuestr[j] = '\0';
312 		i++;
313 		tok = TEXT;
314 	}
315 
316 	return tok;
317 }
318 
319 static void
320 print_string(WINDOW *win, int *y, int *x, int minx, int maxx, char *str, bool color)
321 {
322 	int i, j, len, reallen;
323 
324 	if(strlen(str) == 0)
325 		return;
326 
327 	len = reallen = strlen(str);
328 	if (color) {
329 		i=0;
330 		while (i < len) {
331 			if (check_set_ncurses_attr(win, str+i))
332 				reallen -= 3;
333 			i++;
334 		}
335 	}
336 
337 	i = 0;
338 	while (i < len) {
339 		if (*x + reallen > maxx) {
340 			*y = (*x != minx ? *y+1 : *y);
341 			*x = minx;
342 		}
343 		j = *x;
344 		while (j < maxx && i < len) {
345 			if (color && check_set_ncurses_attr(win, str+i)) {
346 				i += 3;
347 			} else {
348 				mvwaddch(win, *y, j, str[i]);
349 				i++;
350 				reallen--;
351 				j++;
352 				*x = j;
353 			}
354 		}
355 	}
356 }
357 
358 void
359 print_text(struct bsddialog_conf conf, WINDOW *pad, int starty, int minx, int maxx,
360     char *text)
361 {
362 	char *valuestr;
363 	int x, y;
364 	bool loop;
365 	enum token tok;
366 
367 	valuestr = malloc(strlen(text) + 1);
368 
369 	x = minx;
370 	y = starty;
371 	loop = true;
372 	while (loop) {
373 		tok = next_token(text, valuestr);
374 		switch (tok) {
375 		case END:
376 			loop = false;
377 			break;
378 		case WS:
379 			text += strlen(valuestr);
380 			print_string(pad, &y, &x, minx, maxx, valuestr, false /*useless*/);
381 			break;
382 		case TEXT:
383 			text += strlen(valuestr);
384 			print_string(pad, &y, &x, minx, maxx, valuestr, conf.text.colors);
385 			break;
386 		}
387 	}
388 
389 	free(valuestr);
390 }
391 
392 // new text funcs
393 
394 static bool is_ncurses_attr(char *text)
395 {
396 	bool isattr;
397 
398 	if (strnlen(text, 3) < 3)
399 		return false;
400 
401 	if (text[0] != '\\' || text[1] != 'Z')
402 		return false;
403 
404 	if ((text[2] - '0') >= 0 && (text[2] - '0') < 8)
405 		return true;
406 
407 	isattr = text[2] == 'n' || text[2] == 'b' || text[2] == 'B' ||
408 	    text[2] == 'r' || text[2] == 'R' || text[2] == 'u' ||
409 	    text[2] == 'U';
410 
411 	return isattr;
412 }
413 
414 static void
415 print_str(WINDOW *win, int *rows, int *y, int *x, int cols, char *str, bool color)
416 {
417 	int i, j, len, reallen;
418 
419 	if(strlen(str) == 0)
420 		return;
421 
422 	len = reallen = strlen(str);
423 	if (color) {
424 		i=0;
425 		while (i < len) {
426 			if (is_ncurses_attr(str+i))
427 				reallen -= 3;
428 			i++;
429 		}
430 	}
431 
432 	i = 0;
433 	while (i < len) {
434 		if (*x + reallen > cols) {
435 			*y = (*x != 0 ? *y+1 : *y);
436 			if (*y >= *rows) {
437 				*rows = *y + 1;
438 				wresize(win, *rows, cols);
439 			}
440 			*x = 0;
441 		}
442 		j = *x;
443 		while (j < cols && i < len) {
444 			if (color && check_set_ncurses_attr(win, str+i)) {
445 				i += 3;
446 			} else {
447 				mvwaddch(win, *y, j, str[i]);
448 				i++;
449 				reallen--;
450 				j++;
451 				*x = j;
452 			}
453 		}
454 	}
455 }
456 
457 static void prepare_text(struct bsddialog_conf conf, char *text, char *buf)
458 {
459 	int i, j;
460 
461 	i = j = 0;
462 	while (text[i] != '\0') {
463 		switch (text[i]) {
464 		case '\\':
465 			buf[j] = '\\';
466 			switch (text[i+1]) {
467 			case '\\':
468 				i++;
469 				break;
470 			case 'n':
471 				if (conf.text.no_nl_expand) {
472 					j++;
473 					buf[j] = 'n';
474 				} else
475 					buf[j] = '\n';
476 				i++;
477 				break;
478 			case 't':
479 				if (conf.text.no_collapse) {
480 					j++;
481 					buf[j] = 't';
482 				} else
483 					buf[j] = '\t';
484 				i++;
485 				break;
486 			}
487 			break;
488 		case '\n':
489 			buf[j] = conf.text.cr_wrap ? ' ' : '\n';
490 			break;
491 		case '\t':
492 			buf[j] = conf.text.no_collapse ? '\t' : ' ';
493 			break;
494 		default:
495 			buf[j] = text[i];
496 		}
497 		i++;
498 		j += (buf[j] == ' ' && conf.text.trim && j > 0 && buf[j-1] == ' ') ?
499 		    0 : 1;
500 	}
501 	buf[j] = '\0';
502 }
503 
504 int
505 get_text_properties(struct bsddialog_conf conf, char *text, int *maxword,
506     int *maxline, int *nlines)
507 {
508 	char *buf;
509 	int i, buflen, wordlen, linelen;
510 
511 	if ((buf = malloc(strlen(text) + 1)) == NULL)
512 		RETURN_ERROR("Cannot building a buffer to find the properties "\
513 		    "of the text properties");
514 
515 	prepare_text(conf, text, buf);
516 
517 	buflen = strlen(buf) + 1;
518 	*maxword = 0;
519 	wordlen = 0;
520 	for (i=0; i < buflen; i++) {
521 		if (buf[i] == '\t' || buf[i] == '\n' || buf[i] == ' ' || buf[i] == '\0')
522 			if (wordlen != 0) {
523 				*maxword = MAX(*maxword, wordlen);
524 				wordlen = 0;
525 				continue;
526 			}
527 		if (conf.text.colors && is_ncurses_attr(buf + i))
528 			i += 3;
529 		else
530 			wordlen++;
531 	}
532 
533 	*maxline = linelen = 0;
534 	*nlines = 1;
535 	for (i=0; i < buflen; i++) {
536 		switch (buf[i]) {
537 		case '\n':
538 			*nlines = *nlines + 1;
539 		case '\0':
540 			*maxline = MAX(*maxline, linelen);
541 			linelen = 0;
542 			break;
543 		default:
544 			if (conf.text.colors && is_ncurses_attr(buf + i))
545 				i += 3;
546 			else
547 				linelen++;
548 		}
549 	}
550 	if (*nlines == 1 && *maxline == 0)
551 		*nlines = 0;
552 
553 	free(buf);
554 
555 	return 0;
556 }
557 
558 static int
559 print_textpad(struct bsddialog_conf conf, WINDOW *pad, int *rows, int cols, char *text)
560 {
561 	char *buf, *string;
562 	int i, j, x, y;
563 	bool loop;
564 
565 	if ((buf = malloc(strlen(text) + 1)) == NULL)
566 		RETURN_ERROR("Cannot build (analyze) text");
567 
568 	prepare_text(conf, text, buf);
569 
570 	if ((string = malloc(strlen(text) + 1)) == NULL) {
571 		free(buf);
572 		RETURN_ERROR("Cannot build (analyze) text");
573 	}
574 	i = j = x = y = 0;
575 	loop = true;
576 	while (loop) {
577 		string[j] = buf[i];
578 
579 		if (string[j] == '\0' || string[j] == '\n' ||
580 		    string[j] == '\t' || string[j] == ' ') {
581 			if (j != 0) {
582 				string[j] = '\0';
583 				print_str(pad, rows, &y, &x, cols, string, conf.text.colors);
584 			}
585 		}
586 
587 		switch (buf[i]) {
588 		case '\0':
589 			loop = false;
590 			break;
591 		case '\n':
592 			j = -1;
593 			x = 0;
594 			y++;
595 			break;
596 		case '\t':
597 			for (j=0; j<4 /*tablen*/; j++) {
598 				x++;
599 				if (x >= cols) {
600 					x = 0;
601 					y++;
602 				}
603 			}
604 			j = -1;
605 			break;
606 		case ' ':
607 			x++;
608 			if (x >= cols) {
609 				x = 0;
610 				y++;
611 			}
612 			j = -1;
613 		}
614 
615 		if (y >= *rows) { /* check for whitespaces */
616 			*rows = y + 1;
617 			wresize(pad, *rows, cols);
618 		}
619 
620 		j++;
621 		i++;
622 	}
623 
624 	free(string);
625 	free(buf);
626 
627 	return 0;
628 }
629 
630 /* autosize */
631 
632 /*
633  * max y, that is from 0 to LINES - 1 - t.shadowrows,
634  * could not be max height but avoids problems with checksize
635  */
636 int widget_max_height(struct bsddialog_conf conf)
637 {
638 	int maxheight;
639 
640 	if ((maxheight = conf.shadow ? LINES - 1 - t.shadowrows : LINES - 1) <= 0)
641 		RETURN_ERROR("Terminal too small, LINES - shadow <= 0");
642 
643 	if (conf.y > 0)
644 		if ((maxheight -= conf.y) <=0)
645 			RETURN_ERROR("Terminal too small, LINES - shadow - y <= 0");
646 
647 	return maxheight;
648 }
649 
650 /*
651  * max x, that is from 0 to COLS - 1 - t.shadowcols,
652  *  * could not be max height but avoids problems with checksize
653  */
654 int widget_max_width(struct bsddialog_conf conf)
655 {
656 	int maxwidth;
657 
658 	if ((maxwidth = conf.shadow ? COLS - 1 - t.shadowcols : COLS - 1)  <= 0)
659 		RETURN_ERROR("Terminal too small, COLS - shadow <= 0");
660 	if (conf.x > 0)
661 		if ((maxwidth -= conf.x) <=0)
662 			RETURN_ERROR("Terminal too small, COLS - shadow - x <= 0");
663 
664 	return maxwidth;
665 }
666 
667 int
668 set_widget_size(struct bsddialog_conf conf, int rows, int cols, int *h, int *w)
669 {
670 	int maxheight, maxwidth;
671 
672 	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
673 		return BSDDIALOG_ERROR;
674 
675 	if (rows == BSDDIALOG_FULLSCREEN)
676 		*h = maxheight;
677 	else if (rows < BSDDIALOG_FULLSCREEN)
678 		RETURN_ERROR("Negative (less than -1) height");
679 	else if (rows > BSDDIALOG_AUTOSIZE) {
680 		if ((*h = rows) > maxheight)
681 			RETURN_ERROR("Height too big (> terminal height - "\
682 			    "shadow");
683 	}
684 	/* rows == AUTOSIZE: each widget has to set its size */
685 
686 	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
687 		return BSDDIALOG_ERROR;
688 
689 	if (cols == BSDDIALOG_FULLSCREEN)
690 		*w = maxwidth;
691 	else if (cols < BSDDIALOG_FULLSCREEN)
692 		RETURN_ERROR("Negative (less than -1) width");
693 	else if (cols > BSDDIALOG_AUTOSIZE) {
694 		if ((*w = cols) > maxwidth)
695 			RETURN_ERROR("Width too big (> terminal width - shadow)");
696 	}
697 	/* cols == AUTOSIZE: each widget has to set its size */
698 
699 	return 0;
700 }
701 
702 int
703 set_widget_position(struct bsddialog_conf conf, int *y, int *x, int h, int w)
704 {
705 
706 	if (conf.y == BSDDIALOG_CENTER)
707 		*y = LINES/2 - h/2;
708 	else if (conf.y < BSDDIALOG_CENTER)
709 		RETURN_ERROR("Negative begin y (less than -1)");
710 	else if (conf.y >= LINES)
711 		RETURN_ERROR("Begin Y under the terminal");
712 	else
713 		*y = conf.y;
714 
715 	if ((*y + h + (conf.shadow ? (int) t.shadowrows : 0)) > LINES)
716 		RETURN_ERROR("The lower of the box under the terminal "\
717 		    "(begin Y + height (+ shadow) > terminal lines)");
718 
719 
720 	if (conf.x == BSDDIALOG_CENTER)
721 		*x = COLS/2 - w/2;
722 	else if (conf.x < BSDDIALOG_CENTER)
723 		RETURN_ERROR("Negative begin x (less than -1)");
724 	else if (conf.x >= COLS)
725 		RETURN_ERROR("Begin X over the right of the terminal");
726 	else
727 		*x = conf.x;
728 
729 	if ((*x + w + (conf.shadow ? (int) t.shadowcols : 0)) > COLS)
730 		RETURN_ERROR("The right of the box over the terminal "\
731 		    "(begin X + width (+ shadow) > terminal cols)");
732 
733 	return 0;
734 }
735 
736 /* Widgets builders */
737 void
738 draw_borders(struct bsddialog_conf conf, WINDOW *win, int rows, int cols,
739     enum elevation elev)
740 {
741 	int leftcolor, rightcolor;
742 	int ls, rs, ts, bs, tl, tr, bl, br;
743 	int ltee, rtee;
744 
745 	ls = rs = ACS_VLINE;
746 	ts = bs = ACS_HLINE;
747 	tl = ACS_ULCORNER;
748 	tr = ACS_URCORNER;
749 	bl = ACS_LLCORNER;
750 	br = ACS_LRCORNER;
751 	ltee = ACS_LTEE;
752 	rtee = ACS_RTEE;
753 
754 	if (conf.no_lines == false) {
755 		if (conf.ascii_lines) {
756 			ls = rs = '|';
757 			ts = bs = '-';
758 			tl = tr = bl = br = ltee = rtee = '+';
759 		}
760 		leftcolor  = elev == RAISED ? t.lineraisecolor : t.linelowercolor;
761 		rightcolor = elev == RAISED ? t.linelowercolor : t.lineraisecolor;
762 		wattron(win, leftcolor);
763 		wborder(win, ls, rs, ts, bs, tl, tr, bl, br);
764 		wattroff(win, leftcolor);
765 
766 		wattron(win, rightcolor);
767 		mvwaddch(win, 0, cols-1, tr);
768 		mvwvline(win, 1, cols-1, rs, rows-2);
769 		mvwaddch(win, rows-1, cols-1, br);
770 		mvwhline(win, rows-1, 1, bs, cols-2);
771 		wattroff(win, rightcolor);
772 	}
773 }
774 
775 WINDOW *
776 new_boxed_window(struct bsddialog_conf conf, int y, int x, int rows, int cols,
777     enum elevation elev)
778 {
779 	WINDOW *win;
780 
781 	if ((win = newwin(rows, cols, y, x)) == NULL) {
782 		set_error_string("Cannot build boxed window");
783 		return NULL;
784 	}
785 
786 	wbkgd(win, t.widgetcolor);
787 
788 	draw_borders(conf, win, rows, cols, elev);
789 
790 	return win;
791 }
792 
793 /*
794  * `enum elevation elev` could be useless because it should be always RAISED,
795  * to check at the end.
796  */
797 static int
798 draw_widget_withtextpad(struct bsddialog_conf conf, WINDOW *shadow,
799     WINDOW *widget, int h, int w, enum elevation elev,
800     WINDOW *textpad, int *htextpad, char *text, bool buttons)
801 {
802 	int ts, ltee, rtee;
803 	int colorsurroundtitle;
804 
805 	ts = conf.ascii_lines ? '-' : ACS_HLINE;
806 	ltee = conf.ascii_lines ? '+' : ACS_LTEE;
807 	rtee = conf.ascii_lines ? '+' : ACS_RTEE;
808 	colorsurroundtitle = elev == RAISED ? t.lineraisecolor : t.linelowercolor;
809 
810 	if (shadow != NULL)
811 		wnoutrefresh(shadow);
812 
813 	// move / resize now or the caller?
814 	draw_borders(conf, widget, h, w, elev);
815 
816 	if (conf.title != NULL) {
817 		if (t.surroundtitle && conf.no_lines == false) {
818 			wattron(widget, colorsurroundtitle);
819 			mvwaddch(widget, 0, w/2 - strlen(conf.title)/2 - 1, rtee);
820 			wattroff(widget, colorsurroundtitle);
821 		}
822 		wattron(widget, t.titlecolor);
823 		mvwaddstr(widget, 0, w/2 - strlen(conf.title)/2, conf.title);
824 		wattroff(widget, t.titlecolor);
825 		if (t.surroundtitle && conf.no_lines == false) {
826 			wattron(widget, colorsurroundtitle);
827 			waddch(widget, ltee);
828 			wattroff(widget, colorsurroundtitle);
829 		}
830 	}
831 
832 	if (conf.hline != NULL) {
833 		wattron(widget, t.bottomtitlecolor);
834 		wmove(widget, h - 1, w/2 - strlen(conf.hline)/2 - 1);
835 		waddch(widget, '[');
836 		waddstr(widget, conf.hline);
837 		waddch(widget, ']');
838 		wattroff(widget, t.bottomtitlecolor);
839 	}
840 
841 	if (textpad == NULL && text != NULL) /* no pad, text null for textbox */
842 		print_text(conf, widget, 1, 2, w-3, text);
843 
844 	if (buttons && conf.no_lines == false) {
845 		wattron(widget, t.lineraisecolor);
846 		mvwaddch(widget, h-3, 0, ltee);
847 		mvwhline(widget, h-3, 1, ts, w-2);
848 		wattroff(widget, t.lineraisecolor);
849 
850 		wattron(widget, t.linelowercolor);
851 		mvwaddch(widget, h-3, w-1, rtee);
852 		wattroff(widget, t.linelowercolor);
853 	}
854 
855 	wnoutrefresh(widget);
856 
857 	if (textpad == NULL)
858 		return 0; /* widget_init() ends */
859 
860 	if (text != NULL) /* programbox etc */
861 		if (print_textpad(conf, textpad, htextpad,
862 		    w - HBORDERS - t.texthmargin * 2, text) !=0)
863 			return BSDDIALOG_ERROR;
864 
865 	return 0;
866 }
867 
868 /*
869  * `enum elevation elev` could be useless because it should be always RAISED,
870  * to check at the end.
871  */
872 int
873 update_widget_withtextpad(struct bsddialog_conf conf, WINDOW *shadow,
874     WINDOW *widget, int h, int w, enum elevation elev,
875     WINDOW *textpad, int *htextpad, char *text, bool buttons)
876 {
877 	int error;
878 
879 	/* nothing for now */
880 
881 	error =  draw_widget_withtextpad(conf, shadow, widget, h, w,
882 	    elev, textpad, htextpad, text, buttons);
883 
884 	return error;
885 }
886 
887 /*
888  * `enum elevation elev` could be useless because it should be always RAISED,
889  * to check at the end.
890  */
891 int
892 new_widget_withtextpad(struct bsddialog_conf conf, WINDOW **shadow,
893     WINDOW **widget, int y, int x, int h, int w, enum elevation elev,
894     WINDOW **textpad, int *htextpad, char *text, bool buttons)
895 {
896 	int error;
897 
898 	if (conf.shadow) {
899 		*shadow = newwin(h, w, y + t.shadowrows, x + t.shadowcols);
900 		if (*shadow == NULL)
901 			RETURN_ERROR("Cannot build shadow");
902 		wbkgd(*shadow, t.shadowcolor);
903 	}
904 
905 	if ((*widget = new_boxed_window(conf, y, x, h, w, elev)) == NULL) {
906 		if (conf.shadow)
907 			delwin(*shadow);
908 		return BSDDIALOG_ERROR;
909 	}
910 
911 	if (textpad == NULL) { /* widget_init() */
912 		error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w,
913 		    elev, NULL, NULL, text, buttons);
914 		return error;
915 	}
916 
917 	if (text != NULL) { /* programbox etc */
918 		*htextpad = 1;
919 		*textpad = newpad(*htextpad, w - HBORDERS - t.texthmargin * 2);
920 		if (*textpad == NULL) {
921 			delwin(*textpad);
922 			if (conf.shadow)
923 				delwin(*shadow);
924 			RETURN_ERROR("Cannot build the pad window for text");
925 		}
926 		wbkgd(*textpad, t.widgetcolor);
927 	}
928 
929 	error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w, elev,
930 	    *textpad, htextpad, text, buttons);
931 
932 	return error;
933 }
934 
935 int
936 new_widget(struct bsddialog_conf conf, WINDOW **widget, int *y, int *x,
937     char *text, int *h, int *w, WINDOW **shadow, bool buttons)
938 {
939 
940 	// to delete (each widget has to check its x,y,h,w)
941 	if (*h <= 0)
942 		; /* todo */
943 
944 	if (*w <= 0)
945 		; /* todo */
946 
947 	*y = (conf.y < 0) ? (LINES/2 - *h/2) : conf.y;
948 	*x = (conf.x < 0) ? (COLS/2 - *w/2) : conf.x;
949 
950 	if (new_widget_withtextpad(conf, shadow, widget, *y, *x, *h, *w, RAISED,
951 	    NULL, NULL, text, buttons) != 0)
952 		return BSDDIALOG_ERROR;
953 
954 	if (conf.shadow)
955 		wrefresh(*shadow);
956 
957 	wrefresh(*widget);
958 
959 	return 0;
960 }
961 
962 void
963 end_widget_withtextpad(struct bsddialog_conf conf, WINDOW *window, int h, int w,
964     WINDOW *textpad, WINDOW *shadow)
965 {
966 	int y, x;
967 
968 	getbegyx(window, y, x); /* for clear, add y & x to args? */
969 
970 	if (conf.sleep > 0)
971 		sleep(conf.sleep);
972 
973 	if (textpad != NULL)
974 		delwin(textpad);
975 
976 	delwin(window);
977 
978 	if (conf.shadow)
979 		delwin(shadow);
980 
981 	if (conf.clear)
982 		hide_widget(y, x, h, w, shadow != NULL);
983 
984 	if (conf.get_height != NULL)
985 		*conf.get_height = h;
986 	if (conf.get_width != NULL)
987 		*conf.get_width = w;
988 }
989 
990 void
991 end_widget(struct bsddialog_conf conf, WINDOW *window, int h, int w,
992     WINDOW *shadow)
993 {
994 
995 	end_widget_withtextpad(conf, window, h, w, NULL, shadow);
996 }
997