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