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