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 
get_error_string(void)51 const char *get_error_string(void)
52 {
53 	return errorbuffer;
54 }
55 
set_error_string(char * str)56 void set_error_string(char *str)
57 {
58 
59 	strncpy(errorbuffer, str, ERRBUFLEN-1);
60 }
61 
62 /* cleaner */
hide_widget(int y,int x,int h,int w,bool withshadow)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.shadow.h, x + t.shadow.w)) == NULL)
69 		RETURN_ERROR("Cannot hide the widget");
70 	wbkgd(clear, t.terminal.color);
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 */
f1help(struct bsddialog_conf * conf)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
draw_button(WINDOW * window,int y,int x,int size,char * text,bool selected,bool shortkey)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.button.f_delimcolor;
114 		color_shortkey = t.button.f_shortcutcolor;
115 		color_button = t.button.f_color;
116 	} else {
117 		color_arrows = t.button.delimcolor;
118 		color_shortkey = t.button.shortcutcolor;
119 		color_button = t.button.color;
120 	}
121 
122 	wattron(window, color_arrows);
123 	mvwaddch(window, y, x, t.button.leftch);
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.button.rightch);
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
draw_buttons(WINDOW * window,int y,int cols,struct buttons bs,bool shortkey)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.button.space;
151 	start_x = cols/2 - start_x/2;
152 
153 	for (i = 0; i < (int) bs.nbuttons; i++) {
154 		x = i * (bs.sizebutton + t.button.space);
155 		draw_button(window, y, start_x + x, bs.sizebutton, bs.label[i],
156 		    i == bs.curr, shortkey);
157 	}
158 }
159 
160 void
get_buttons(struct bsddialog_conf * conf,struct buttons * bs,char * yesoklabel,char * extralabel,char * nocancellabel,char * helplabel)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 (conf->button.generic1_label != NULL) {
201 		bs->label[bs->nbuttons] = conf->button.generic1_label;
202 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1;
203 		bs->nbuttons += 1;
204 	}
205 
206 	if (conf->button.generic2_label != NULL) {
207 		bs->label[bs->nbuttons] = conf->button.generic2_label;
208 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2;
209 		bs->nbuttons += 1;
210 	}
211 
212 	if (bs->nbuttons == 0) {
213 		bs->label[0] = DEFAULT_BUTTON_LABEL;
214 		bs->value[0] = DEFAULT_BUTTON_VALUE;
215 		bs->nbuttons = 1;
216 	}
217 
218 	if (conf->button.default_label != NULL) {
219 		for (i=0; i<(int)bs->nbuttons; i++) {
220 			if (strcmp(conf->button.default_label, bs->label[i]) == 0)
221 				bs->curr = i;
222 		}
223 	}
224 
225 	bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0]));
226 	for (i=1; i < (int) bs->nbuttons; i++)
227 		bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i]));
228 	bs->sizebutton += 2;
229 }
230 
231 /* Text */
is_ncurses_attr(char * text)232 static bool is_ncurses_attr(char *text)
233 {
234 
235 	if (strnlen(text, 3) < 3)
236 		return false;
237 
238 	if (text[0] != '\\' || text[1] != 'Z')
239 		return false;
240 
241 	return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true);
242 }
243 
check_set_ncurses_attr(WINDOW * win,char * text)244 static bool check_set_ncurses_attr(WINDOW *win, char *text)
245 {
246 
247 	if (is_ncurses_attr(text) == false)
248 		return false;
249 
250 	if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) {
251 		wattron(win, bsddialog_color( text[2] - '0', COLOR_WHITE) );
252 		return true;
253 	}
254 
255 	switch (text[2]) {
256 	case 'n':
257 		wattrset(win, A_NORMAL);
258 		break;
259 	case 'b':
260 		wattron(win, A_BOLD);
261 		break;
262 	case 'B':
263 		wattroff(win, A_BOLD);
264 		break;
265 	case 'r':
266 		wattron(win, A_REVERSE);
267 		break;
268 	case 'R':
269 		wattroff(win, A_REVERSE);
270 		break;
271 	case 'u':
272 		wattron(win, A_UNDERLINE);
273 		break;
274 	case 'U':
275 		wattroff(win, A_UNDERLINE);
276 		break;
277 	}
278 
279 	return true;
280 }
281 
282 static void
print_str(WINDOW * win,int * rows,int * y,int * x,int cols,char * str,bool color)283 print_str(WINDOW *win, int *rows, int *y, int *x, int cols, char *str, bool color)
284 {
285 	int i, j, len, reallen;
286 
287 	if(strlen(str) == 0)
288 		return;
289 
290 	len = reallen = strlen(str);
291 	if (color) {
292 		i=0;
293 		while (i < len) {
294 			if (is_ncurses_attr(str+i))
295 				reallen -= 3;
296 			i++;
297 		}
298 	}
299 
300 	i = 0;
301 	while (i < len) {
302 		if (*x + reallen > cols) {
303 			*y = (*x != 0 ? *y+1 : *y);
304 			if (*y >= *rows) {
305 				*rows = *y + 1;
306 				wresize(win, *rows, cols);
307 			}
308 			*x = 0;
309 		}
310 		j = *x;
311 		while (j < cols && i < len) {
312 			if (color && check_set_ncurses_attr(win, str+i)) {
313 				i += 3;
314 			} else {
315 				mvwaddch(win, *y, j, str[i]);
316 				i++;
317 				reallen--;
318 				j++;
319 				*x = j;
320 			}
321 		}
322 	}
323 }
324 
prepare_text(struct bsddialog_conf * conf,char * text,char * buf)325 static void prepare_text(struct bsddialog_conf *conf, char *text, char *buf)
326 {
327 	int i, j;
328 
329 	i = j = 0;
330 	while (text[i] != '\0') {
331 		switch (text[i]) {
332 		case '\\':
333 			buf[j] = '\\';
334 			switch (text[i+1]) {
335 			case '\\':
336 				i++;
337 				break;
338 			case 'n':
339 				if (conf->text.no_nl_expand) {
340 					j++;
341 					buf[j] = 'n';
342 				} else
343 					buf[j] = '\n';
344 				i++;
345 				break;
346 			case 't':
347 				if (conf->text.no_collapse) {
348 					j++;
349 					buf[j] = 't';
350 				} else
351 					buf[j] = '\t';
352 				i++;
353 				break;
354 			}
355 			break;
356 		case '\n':
357 			buf[j] = conf->text.cr_wrap ? ' ' : '\n';
358 			break;
359 		case '\t':
360 			buf[j] = conf->text.no_collapse ? '\t' : ' ';
361 			break;
362 		default:
363 			buf[j] = text[i];
364 		}
365 		i++;
366 		j += (buf[j] == ' ' && conf->text.trim && j > 0 && buf[j-1] == ' ') ?
367 		    0 : 1;
368 	}
369 	buf[j] = '\0';
370 }
371 
372 int
get_text_properties(struct bsddialog_conf * conf,char * text,int * maxword,int * maxline,int * nlines)373 get_text_properties(struct bsddialog_conf *conf, char *text, int *maxword,
374     int *maxline, int *nlines)
375 {
376 	char *buf;
377 	int i, buflen, wordlen, linelen;
378 
379 	if ((buf = malloc(strlen(text) + 1)) == NULL)
380 		RETURN_ERROR("Cannot building a buffer to find the properties "\
381 		    "of the text properties");
382 
383 	prepare_text(conf, text, buf);
384 
385 	buflen = strlen(buf) + 1;
386 	*maxword = 0;
387 	wordlen = 0;
388 	for (i=0; i < buflen; i++) {
389 		if (buf[i] == '\t' || buf[i] == '\n' || buf[i] == ' ' || buf[i] == '\0')
390 			if (wordlen != 0) {
391 				*maxword = MAX(*maxword, wordlen);
392 				wordlen = 0;
393 				continue;
394 			}
395 		if (conf->text.colors && is_ncurses_attr(buf + i))
396 			i += 3;
397 		else
398 			wordlen++;
399 	}
400 
401 	*maxline = linelen = 0;
402 	*nlines = 1;
403 	for (i=0; i < buflen; i++) {
404 		switch (buf[i]) {
405 		case '\n':
406 			*nlines = *nlines + 1;
407 		case '\0':
408 			*maxline = MAX(*maxline, linelen);
409 			linelen = 0;
410 			break;
411 		default:
412 			if (conf->text.colors && is_ncurses_attr(buf + i))
413 				i += 3;
414 			else
415 				linelen++;
416 		}
417 	}
418 	if (*nlines == 1 && *maxline == 0)
419 		*nlines = 0;
420 
421 	free(buf);
422 
423 	return 0;
424 }
425 
426 int
print_textpad(struct bsddialog_conf * conf,WINDOW * pad,int * rows,int cols,char * text)427 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, int *rows, int cols,
428     char *text)
429 {
430 	char *buf, *string;
431 	int i, j, x, y;
432 	bool loop;
433 
434 	if ((buf = malloc(strlen(text) + 1)) == NULL)
435 		RETURN_ERROR("Cannot build (analyze) text");
436 
437 	prepare_text(conf, text, buf);
438 
439 	if ((string = malloc(strlen(text) + 1)) == NULL) {
440 		free(buf);
441 		RETURN_ERROR("Cannot build (analyze) text");
442 	}
443 	i = j = x = y = 0;
444 	loop = true;
445 	while (loop) {
446 		string[j] = buf[i];
447 
448 		if (string[j] == '\0' || string[j] == '\n' ||
449 		    string[j] == '\t' || string[j] == ' ') {
450 			if (j != 0) {
451 				string[j] = '\0';
452 				print_str(pad, rows, &y, &x, cols, string, conf->text.colors);
453 			}
454 		}
455 
456 		switch (buf[i]) {
457 		case '\0':
458 			loop = false;
459 			break;
460 		case '\n':
461 			j = -1;
462 			x = 0;
463 			y++;
464 			break;
465 		case '\t':
466 			for (j=0; j<4 /*tablen*/; j++) {
467 				x++;
468 				if (x >= cols) {
469 					x = 0;
470 					y++;
471 				}
472 			}
473 			j = -1;
474 			break;
475 		case ' ':
476 			x++;
477 			if (x >= cols) {
478 				x = 0;
479 				y++;
480 			}
481 			j = -1;
482 		}
483 
484 		if (y >= *rows) { /* check for whitespaces */
485 			*rows = y + 1;
486 			wresize(pad, *rows, cols);
487 		}
488 
489 		j++;
490 		i++;
491 	}
492 
493 	free(string);
494 	free(buf);
495 
496 	return 0;
497 }
498 
499 /* autosize */
500 
501 /*
502  * max y, that is from 0 to LINES - 1 - t.shadowrows,
503  * could not be max height but avoids problems with checksize
504  */
widget_max_height(struct bsddialog_conf * conf)505 int widget_max_height(struct bsddialog_conf *conf)
506 {
507 	int maxheight;
508 
509 	if ((maxheight = conf->shadow ? LINES - 1 - t.shadow.h : LINES - 1) <= 0)
510 		RETURN_ERROR("Terminal too small, LINES - shadow <= 0");
511 
512 	if (conf->y > 0)
513 		if ((maxheight -= conf->y) <=0)
514 			RETURN_ERROR("Terminal too small, LINES - shadow - y <= 0");
515 
516 	return maxheight;
517 }
518 
519 /*
520  * max x, that is from 0 to COLS - 1 - t.shadowcols,
521  *  * could not be max height but avoids problems with checksize
522  */
widget_max_width(struct bsddialog_conf * conf)523 int widget_max_width(struct bsddialog_conf *conf)
524 {
525 	int maxwidth;
526 
527 	if ((maxwidth = conf->shadow ? COLS - 1 - t.shadow.w : COLS - 1)  <= 0)
528 		RETURN_ERROR("Terminal too small, COLS - shadow <= 0");
529 	if (conf->x > 0)
530 		if ((maxwidth -= conf->x) <=0)
531 			RETURN_ERROR("Terminal too small, COLS - shadow - x <= 0");
532 
533 	return maxwidth;
534 }
535 
536 int
set_widget_size(struct bsddialog_conf * conf,int rows,int cols,int * h,int * w)537 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
538 {
539 	int maxheight, maxwidth;
540 
541 	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
542 		return BSDDIALOG_ERROR;
543 
544 	if (rows == BSDDIALOG_FULLSCREEN)
545 		*h = maxheight;
546 	else if (rows < BSDDIALOG_FULLSCREEN)
547 		RETURN_ERROR("Negative (less than -1) height");
548 	else if (rows > BSDDIALOG_AUTOSIZE) {
549 		if ((*h = rows) > maxheight)
550 			RETURN_ERROR("Height too big (> terminal height - "\
551 			    "shadow");
552 	}
553 	/* rows == AUTOSIZE: each widget has to set its size */
554 
555 	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
556 		return BSDDIALOG_ERROR;
557 
558 	if (cols == BSDDIALOG_FULLSCREEN)
559 		*w = maxwidth;
560 	else if (cols < BSDDIALOG_FULLSCREEN)
561 		RETURN_ERROR("Negative (less than -1) width");
562 	else if (cols > BSDDIALOG_AUTOSIZE) {
563 		if ((*w = cols) > maxwidth)
564 			RETURN_ERROR("Width too big (> terminal width - shadow)");
565 	}
566 	/* cols == AUTOSIZE: each widget has to set its size */
567 
568 	return 0;
569 }
570 
571 int
set_widget_position(struct bsddialog_conf * conf,int * y,int * x,int h,int w)572 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
573 {
574 
575 	if (conf->y == BSDDIALOG_CENTER)
576 		*y = LINES/2 - h/2;
577 	else if (conf->y < BSDDIALOG_CENTER)
578 		RETURN_ERROR("Negative begin y (less than -1)");
579 	else if (conf->y >= LINES)
580 		RETURN_ERROR("Begin Y under the terminal");
581 	else
582 		*y = conf->y;
583 
584 	if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > LINES)
585 		RETURN_ERROR("The lower of the box under the terminal "\
586 		    "(begin Y + height (+ shadow) > terminal lines)");
587 
588 
589 	if (conf->x == BSDDIALOG_CENTER)
590 		*x = COLS/2 - w/2;
591 	else if (conf->x < BSDDIALOG_CENTER)
592 		RETURN_ERROR("Negative begin x (less than -1)");
593 	else if (conf->x >= COLS)
594 		RETURN_ERROR("Begin X over the right of the terminal");
595 	else
596 		*x = conf->x;
597 
598 	if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > COLS)
599 		RETURN_ERROR("The right of the box over the terminal "\
600 		    "(begin X + width (+ shadow) > terminal cols)");
601 
602 	return 0;
603 }
604 
605 /* Widgets builders */
606 void
draw_borders(struct bsddialog_conf * conf,WINDOW * win,int rows,int cols,enum elevation elev)607 draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols,
608     enum elevation elev)
609 {
610 	int leftcolor, rightcolor;
611 	int ls, rs, ts, bs, tl, tr, bl, br;
612 	int ltee, rtee;
613 
614 	ls = rs = ACS_VLINE;
615 	ts = bs = ACS_HLINE;
616 	tl = ACS_ULCORNER;
617 	tr = ACS_URCORNER;
618 	bl = ACS_LLCORNER;
619 	br = ACS_LRCORNER;
620 	ltee = ACS_LTEE;
621 	rtee = ACS_RTEE;
622 
623 	if (conf->no_lines == false) {
624 		if (conf->ascii_lines) {
625 			ls = rs = '|';
626 			ts = bs = '-';
627 			tl = tr = bl = br = ltee = rtee = '+';
628 		}
629 		leftcolor  = elev == RAISED ? t.widget.lineraisecolor : t.widget.linelowercolor;
630 		rightcolor = elev == RAISED ? t.widget.linelowercolor : t.widget.lineraisecolor;
631 		wattron(win, leftcolor);
632 		wborder(win, ls, rs, ts, bs, tl, tr, bl, br);
633 		wattroff(win, leftcolor);
634 
635 		wattron(win, rightcolor);
636 		mvwaddch(win, 0, cols-1, tr);
637 		mvwvline(win, 1, cols-1, rs, rows-2);
638 		mvwaddch(win, rows-1, cols-1, br);
639 		mvwhline(win, rows-1, 1, bs, cols-2);
640 		wattroff(win, rightcolor);
641 	}
642 }
643 
644 WINDOW *
new_boxed_window(struct bsddialog_conf * conf,int y,int x,int rows,int cols,enum elevation elev)645 new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols,
646     enum elevation elev)
647 {
648 	WINDOW *win;
649 
650 	if ((win = newwin(rows, cols, y, x)) == NULL) {
651 		set_error_string("Cannot build boxed window");
652 		return NULL;
653 	}
654 
655 	wbkgd(win, t.widget.color);
656 
657 	draw_borders(conf, win, rows, cols, elev);
658 
659 	return win;
660 }
661 
662 /*
663  * `enum elevation elev` could be useless because it should be always RAISED,
664  * to check at the end.
665  */
666 static int
draw_widget_withtextpad(struct bsddialog_conf * conf,WINDOW * shadow,WINDOW * widget,int h,int w,enum elevation elev,WINDOW * textpad,int * htextpad,char * text,bool buttons)667 draw_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow,
668     WINDOW *widget, int h, int w, enum elevation elev,
669     WINDOW *textpad, int *htextpad, char *text, bool buttons)
670 {
671 	int ts, ltee, rtee;
672 	int colorsurroundtitle;
673 
674 	ts = conf->ascii_lines ? '-' : ACS_HLINE;
675 	ltee = conf->ascii_lines ? '+' : ACS_LTEE;
676 	rtee = conf->ascii_lines ? '+' : ACS_RTEE;
677 	colorsurroundtitle = elev == RAISED ? t.widget.lineraisecolor : t.widget.linelowercolor;
678 
679 	if (shadow != NULL)
680 		wnoutrefresh(shadow);
681 
682 	// move / resize now or the caller?
683 	draw_borders(conf, widget, h, w, elev);
684 
685 	if (conf->title != NULL) {
686 		if (t.widget.delimtitle && conf->no_lines == false) {
687 			wattron(widget, colorsurroundtitle);
688 			mvwaddch(widget, 0, w/2 - strlen(conf->title)/2 - 1, rtee);
689 			wattroff(widget, colorsurroundtitle);
690 		}
691 		wattron(widget, t.widget.titlecolor);
692 		mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title);
693 		wattroff(widget, t.widget.titlecolor);
694 		if (t.widget.delimtitle && conf->no_lines == false) {
695 			wattron(widget, colorsurroundtitle);
696 			waddch(widget, ltee);
697 			wattroff(widget, colorsurroundtitle);
698 		}
699 	}
700 
701 	if (conf->hline != NULL) {
702 		wattron(widget, t.widget.bottomtitlecolor);
703 		wmove(widget, h - 1, w/2 - strlen(conf->hline)/2 - 1);
704 		waddch(widget, '[');
705 		waddstr(widget, conf->hline);
706 		waddch(widget, ']');
707 		wattroff(widget, t.widget.bottomtitlecolor);
708 	}
709 
710 	//if (textpad == NULL && text != NULL) /* no pad, text null for textbox */
711 	//	print_text(conf, widget, 1, 2, w-3, text);
712 
713 	if (buttons && conf->no_lines == false) {
714 		wattron(widget, t.widget.lineraisecolor);
715 		mvwaddch(widget, h-3, 0, ltee);
716 		mvwhline(widget, h-3, 1, ts, w-2);
717 		wattroff(widget, t.widget.lineraisecolor);
718 
719 		wattron(widget, t.widget.linelowercolor);
720 		mvwaddch(widget, h-3, w-1, rtee);
721 		wattroff(widget, t.widget.linelowercolor);
722 	}
723 
724 	wnoutrefresh(widget);
725 
726 	if (textpad == NULL)
727 		return 0; /* widget_init() ends */
728 
729 	if (text != NULL) /* programbox etc */
730 		if (print_textpad(conf, textpad, htextpad,
731 		    w - HBORDERS - t.text.hmargin * 2, text) !=0)
732 			return BSDDIALOG_ERROR;
733 
734 	return 0;
735 }
736 
737 /*
738  * `enum elevation elev` could be useless because it should be always RAISED,
739  * to check at the end.
740  */
741 int
update_widget_withtextpad(struct bsddialog_conf * conf,WINDOW * shadow,WINDOW * widget,int h,int w,enum elevation elev,WINDOW * textpad,int * htextpad,char * text,bool buttons)742 update_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow,
743     WINDOW *widget, int h, int w, enum elevation elev,
744     WINDOW *textpad, int *htextpad, char *text, bool buttons)
745 {
746 	int error;
747 
748 	/* nothing for now */
749 
750 	error =  draw_widget_withtextpad(conf, shadow, widget, h, w,
751 	    elev, textpad, htextpad, text, buttons);
752 
753 	return error;
754 }
755 
756 /*
757  * `enum elevation elev` could be useless because it should be always RAISED,
758  * to check at the end.
759  */
760 int
new_widget_withtextpad(struct bsddialog_conf * conf,WINDOW ** shadow,WINDOW ** widget,int y,int x,int h,int w,enum elevation elev,WINDOW ** textpad,int * htextpad,char * text,bool buttons)761 new_widget_withtextpad(struct bsddialog_conf *conf, WINDOW **shadow,
762     WINDOW **widget, int y, int x, int h, int w, enum elevation elev,
763     WINDOW **textpad, int *htextpad, char *text, bool buttons)
764 {
765 	int error;
766 
767 	if (conf->shadow) {
768 		*shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w);
769 		if (*shadow == NULL)
770 			RETURN_ERROR("Cannot build shadow");
771 		wbkgd(*shadow, t.shadow.color);
772 	}
773 
774 	if ((*widget = new_boxed_window(conf, y, x, h, w, elev)) == NULL) {
775 		if (conf->shadow)
776 			delwin(*shadow);
777 		return BSDDIALOG_ERROR;
778 	}
779 
780 	if (textpad == NULL) { /* widget_init() */
781 		error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w,
782 		    elev, NULL, NULL, text, buttons);
783 		return error;
784 	}
785 
786 	if (text != NULL) { /* programbox etc */
787 		*htextpad = 1;
788 		*textpad = newpad(*htextpad, w - HBORDERS - t.text.hmargin * 2);
789 		if (*textpad == NULL) {
790 			delwin(*textpad);
791 			if (conf->shadow)
792 				delwin(*shadow);
793 			RETURN_ERROR("Cannot build the pad window for text");
794 		}
795 		wbkgd(*textpad, t.widget.color);
796 	}
797 
798 	error =  draw_widget_withtextpad(conf, *shadow, *widget, h, w, elev,
799 	    *textpad, htextpad, text, buttons);
800 
801 	return error;
802 }
803 
804 void
end_widget_withtextpad(struct bsddialog_conf * conf,WINDOW * window,int h,int w,WINDOW * textpad,WINDOW * shadow)805 end_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *window, int h, int w,
806     WINDOW *textpad, WINDOW *shadow)
807 {
808 	int y, x;
809 
810 	getbegyx(window, y, x); /* for clear, add y & x to args? */
811 
812 	if (conf->sleep > 0)
813 		sleep(conf->sleep);
814 
815 	if (textpad != NULL)
816 		delwin(textpad);
817 
818 	delwin(window);
819 
820 	if (conf->shadow)
821 		delwin(shadow);
822 
823 	if (conf->clear)
824 		hide_widget(y, x, h, w, shadow != NULL);
825 
826 	if (conf->get_height != NULL)
827 		*conf->get_height = h;
828 	if (conf->get_width != NULL)
829 		*conf->get_width = w;
830 }
831