11590Srgrimes/*-
21590Srgrimes * SPDX-License-Identifier: BSD-2-Clause
31590Srgrimes *
41590Srgrimes * Copyright (c) 2021-2023 Alfonso Sabato Siciliano
51590Srgrimes *
61590Srgrimes * Redistribution and use in source and binary forms, with or without
71590Srgrimes * modification, are permitted provided that the following conditions
81590Srgrimes * are met:
91590Srgrimes * 1. Redistributions of source code must retain the above copyright
101590Srgrimes *    notice, this list of conditions and the following disclaimer.
111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer in the
131590Srgrimes *    documentation and/or other materials provided with the distribution.
141590Srgrimes *
151590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251590Srgrimes * SUCH DAMAGE.
261590Srgrimes */
271590Srgrimes
281590Srgrimes#include <curses.h>
291590Srgrimes#include <stdlib.h>
301590Srgrimes#include <string.h>
311590Srgrimes#include <unistd.h>
321590Srgrimes#include <wctype.h>
332537Spst
342537Spst#include "bsddialog.h"
352537Spst#include "bsddialog_theme.h"
362537Spst#include "lib_util.h"
372537Spst
382537Spst/*
392537Spst * -1- Error and diagnostic
402537Spst *
412537Spst *	get_error_string();
422537Spst *	set_error_string();
431590Srgrimes *	set_fmt_error_string();
4467467Sru *
451590Srgrimes * ----------------------------------------------------
461590Srgrimes * -2- (Unicode) Multicolumn character strings
471590Srgrimes *
481590Srgrimes *	alloc_mbstows();
4987628Sdwmalone *	mvwaddwch();
501590Srgrimes *	str_props();
5187628Sdwmalone *	strcols();
5272109Scharnier *
5387628Sdwmalone * ----------------------------------------------------
541590Srgrimes * -3- Buttons
5587628Sdwmalone *
5687628Sdwmalone * [static] buttons_min_width();
5787628Sdwmalone * [static] draw_button();
581590Srgrimes *          draw_buttons();
591590Srgrimes *          set_buttons(); (to call 1 time after prepare_dialog()).
601590Srgrimes *          shortcut_buttons();
611590Srgrimes *
621590Srgrimes * ----------------------------------------------------
631590Srgrimes * -4- (Auto) Sizing and (Auto) Position
641590Srgrimes *
651590Srgrimes * [static] widget_max_height(conf);
662537Spst * [static] widget_max_width(struct bsddialog_conf *conf)
672537Spst * [static] is_wtext_attr();
682537Spst * [static] text_properties();
692537Spst * [static] text_autosize();
701590Srgrimes * [static] text_size();
711590Srgrimes * [static] widget_min_height(conf, htext, hnotext, bool buttons);
72100521Sume * [static] widget_min_width(conf, wtext, minw, buttons);
73100521Sume *          set_widget_size();
7423693Speter *          set_widget_autosize();  (not for all dialogs).
7523693Speter *          widget_checksize();     (not for all dialogs).
761590Srgrimes *          set_widget_position();
771590Srgrimes *          dialog_size_position(struct dialog); (not for all dialogs).
781590Srgrimes *
791590Srgrimes * ----------------------------------------------------
8023693Speter * -5- (Dialog) Widget components and utils
8123693Speter *
82202191Sed *	hide_dialog(struct dialog);
8311759Sache *	f1help_dialog(conf);
8423693Speter *	draw_borders(conf, win, elev);
851590Srgrimes *	update_box(conf, win, y, x, h, w, elev);
8664775Sbrian *	rtextpad(); (helper for pnoutrefresh(textpad)).
871590Srgrimes *
881590Srgrimes * ----------------------------------------------------
891590Srgrimes * -6- Dialog init/build, update/draw, destroy
90168632Sdes *
91106250Ssobomax *          end_dialog(struct dialog);
9274586Sache * [static] check_set_wtext_attr();
931590Srgrimes * [static] print_string(); (word wrapping).
94150316Sdds * [static] print_textpad();
951590Srgrimes *          draw_dialog(struct dialog);
9692920Simp *          prepare_dialog(struct dialog);
9792920Simp */
9892920Simp
9992920Simp/*
1001590Srgrimes * -1- Error and diagnostic
10187229Smarkm */
102102944Sdwmalone#define ERRBUFLEN    1024
1031590Srgrimes
1041590Srgrimesstatic char errorbuffer[ERRBUFLEN];
1051590Srgrimes
1062589Spstconst char *get_error_string(void)
1072537Spst{
108168635Sdes	return (errorbuffer);
1091590Srgrimes}
110100521Sume
111100521Sumevoid set_error_string(const char *str)
112100521Sume{
113100521Sume	strncpy(errorbuffer, str, ERRBUFLEN-1);
114100521Sume}
115100521Sume
11699249Sminivoid set_fmt_error_string(const char *fmt, ...)
11799249Smini{
11899249Smini   va_list arg_ptr;
119117010Sjmallett
120117010Sjmallett   va_start(arg_ptr, fmt);
121117010Sjmallett   vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr);
1221590Srgrimes   va_end(arg_ptr);
1231590Srgrimes}
1241590Srgrimes
1251590Srgrimes/*
1261590Srgrimes * -2- (Unicode) Multicolumn character strings
1271590Srgrimes */
1281590Srgrimeswchar_t* alloc_mbstows(const char *mbstring)
1291590Srgrimes{
1301590Srgrimes	size_t charlen, nchar;
1311590Srgrimes	mbstate_t mbs;
1321590Srgrimes	const char *pmbstring;
1331590Srgrimes	wchar_t *wstring;
1342537Spst
1352537Spst	nchar = 1;
1362537Spst	pmbstring = mbstring;
1372537Spst	memset(&mbs, 0, sizeof(mbs));
1382537Spst	while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 &&
1392537Spst	    charlen != (size_t)-1 && charlen != (size_t)-2) {
1401590Srgrimes		pmbstring += charlen;
1411590Srgrimes		nchar++;
14227169Scharnier	}
1431590Srgrimes
1441590Srgrimes	if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL)
1452589Spst		return (NULL);
1462589Spst	mbstowcs(wstring, mbstring, nchar);
1472589Spst
14827169Scharnier	return (wstring);
149102944Sdwmalone}
15027169Scharnier
151146466Sruvoid mvwaddwch(WINDOW *w, int y, int x, wchar_t wch)
152168635Sdes{
15327169Scharnier	wchar_t ws[2];
15427169Scharnier
15527169Scharnier	ws[0] = wch;
15627169Scharnier	ws[1] = L'\0';
157102944Sdwmalone	mvwaddwstr(w, y, x, ws);
1582589Spst}
15927169Scharnier
1602589Spstint str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col)
16146662Sobrien{
16287229Smarkm	bool multicol;
1632589Spst	int w;
16446662Sobrien	unsigned int ncol;
165150316Sdds	size_t charlen, mb_cur_max;
16646662Sobrien	wchar_t wch;
167220971Ssimon	mbstate_t mbs;
168220971Ssimon
169220971Ssimon	multicol = false;
170220971Ssimon	mb_cur_max = MB_CUR_MAX;
17146662Sobrien	ncol = 0;
172220971Ssimon	memset(&mbs, 0, sizeof(mbs));
173220971Ssimon	while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
174220971Ssimon	    charlen != (size_t)-1 && charlen != (size_t)-2) {
175220971Ssimon		if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
17646662Sobrien			return (-1);
17746662Sobrien		w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
17846662Sobrien		ncol += (w < 0) ? 0 : w;
17911811Sache		if (w > 1 && wch != L'\t')
18011759Sache			multicol = true;
1812589Spst		mbstring += charlen;
1822589Spst	}
1832589Spst
1842589Spst	if (cols != NULL)
1852589Spst		*cols = ncol;
1862589Spst	if (has_multi_col != NULL)
1872589Spst		*has_multi_col = multicol;
1882589Spst
18987229Smarkm	return (0);
1902589Spst}
1912589Spst
1922589Spstunsigned int strcols(const char *mbstring)
1932589Spst{
1942589Spst	int w;
1952589Spst	unsigned int ncol;
1962589Spst	size_t charlen, mb_cur_max;
1972589Spst	wchar_t wch;
1981590Srgrimes	mbstate_t mbs;
1991590Srgrimes
2001590Srgrimes	mb_cur_max = MB_CUR_MAX;
2011590Srgrimes	ncol = 0;
2021590Srgrimes	memset(&mbs, 0, sizeof(mbs));
2031590Srgrimes	while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
2041590Srgrimes	    charlen != (size_t)-1 && charlen != (size_t)-2) {
2051590Srgrimes		if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
2061590Srgrimes			return (0);
2071590Srgrimes		w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
2081590Srgrimes		ncol += (w < 0) ? 0 : w;
2091590Srgrimes		mbstring += charlen;
2101590Srgrimes	}
2111590Srgrimes
2121590Srgrimes	return (ncol);
2131590Srgrimes}
2141590Srgrimes
2151590Srgrimes/*
2161590Srgrimes * -3- Buttons
2171590Srgrimes */
2181590Srgrimesstatic int buttons_min_width(struct buttons *bs)
2191590Srgrimes{
2201590Srgrimes	unsigned int width;
22148566Sbillf
2221590Srgrimes	width = bs->nbuttons * bs->sizebutton;
2231590Srgrimes	if (bs->nbuttons > 0)
2241590Srgrimes		width += (bs->nbuttons - 1) * t.button.minmargin;
2251590Srgrimes
22648566Sbillf	return (width);
22723693Speter}
2281590Srgrimes
2291590Srgrimesstatic void
2301590Srgrimesdraw_button(WINDOW *window, int y, int x, int size, const char *text,
231102944Sdwmalone    wchar_t first, bool selected, bool shortcut)
2321590Srgrimes{
23387229Smarkm	int i, color_arrows, color_shortkey, color_button;
2341590Srgrimes
2351590Srgrimes	if (selected) {
236201140Sed		color_arrows = t.button.f_delimcolor;
23787229Smarkm		color_shortkey = t.button.f_shortcutcolor;
2381590Srgrimes		color_button = t.button.f_color;
239117010Sjmallett	} else {
240117010Sjmallett		color_arrows = t.button.delimcolor;
241117010Sjmallett		color_shortkey = t.button.shortcutcolor;
242201140Sed		color_button = t.button.color;
243201140Sed	}
244201140Sed
2451590Srgrimes	wattron(window, color_arrows);
246201140Sed	mvwaddch(window, y, x, t.button.leftdelim);
247201140Sed	wattroff(window, color_arrows);
2481590Srgrimes	wattron(window, color_button);
2495369Sjkh	for (i = 1; i < size - 1; i++)
2505369Sjkh		waddch(window, ' ');
2511590Srgrimes	wattroff(window, color_button);
2521590Srgrimes	wattron(window, color_arrows);
253201140Sed	mvwaddch(window, y, x + i, t.button.rightdelim);
2541590Srgrimes	wattroff(window, color_arrows);
255201140Sed
2561590Srgrimes	x = x + 1 + ((size - 2 - strcols(text))/2);
25787229Smarkm	wattron(window, color_button);
25823693Speter	mvwaddstr(window, y, x, text);
25923693Speter	wattroff(window, color_button);
26087229Smarkm
2611590Srgrimes	if (shortcut) {
26223693Speter		wattron(window, color_shortkey);
2631590Srgrimes		mvwaddwch(window, y, x, first);
2641590Srgrimes		wattroff(window, color_shortkey);
26523693Speter	}
26623693Speter}
2671590Srgrimes
2681590Srgrimesvoid draw_buttons(struct dialog *d)
2691590Srgrimes{
2701590Srgrimes	int i, x, startx, y;
271102944Sdwmalone	unsigned int newmargin, margin, wbuttons;
2721590Srgrimes
27387229Smarkm	y = d->h - 2;
2741590Srgrimes
275201140Sed	newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton);
2761590Srgrimes	newmargin /= (d->bs.nbuttons + 1);
27787229Smarkm	newmargin = MIN(newmargin, t.button.maxmargin);
2781590Srgrimes	if (newmargin == 0) {
27964775Sbrian		margin = t.button.minmargin;
28064775Sbrian		wbuttons = buttons_min_width(&d->bs);
28164775Sbrian	} else {
28264775Sbrian		margin = newmargin;
2831590Srgrimes		wbuttons = d->bs.nbuttons * d->bs.sizebutton;
2841590Srgrimes		wbuttons += (d->bs.nbuttons + 1) * margin;
2851590Srgrimes	}
28623693Speter
2871590Srgrimes	startx = d->w/2 - wbuttons/2 + newmargin;
2881590Srgrimes	for (i = 0; i < (int)d->bs.nbuttons; i++) {
2891590Srgrimes		x = i * (d->bs.sizebutton + margin);
290229403Sed		draw_button(d->widget, y, startx + x, d->bs.sizebutton,
2911590Srgrimes		    d->bs.label[i], d->bs.first[i],  i == d->bs.curr,
2921590Srgrimes		    d->bs.shortcut);
2931590Srgrimes	}
2941590Srgrimes}
2951590Srgrimes
2961590Srgrimesvoid
2971590Srgrimesset_buttons(struct dialog *d, bool shortcut, const char *oklabel,
2981590Srgrimes    const char *cancellabel)
2991590Srgrimes{
3001590Srgrimes	int i;
3011590Srgrimes#define SIZEBUTTON              8
302228992Suqs#define DEFAULT_BUTTON_LABEL	OK_LABEL
303228992Suqs#define DEFAULT_BUTTON_VALUE	BSDDIALOG_OK
30466563Sbrian	wchar_t first;
30566563Sbrian
30666563Sbrian	d->bs.nbuttons = 0;
30766563Sbrian	d->bs.curr = 0;
30866563Sbrian	d->bs.sizebutton = 0;
30966563Sbrian	d->bs.shortcut = shortcut;
31066563Sbrian
31166563Sbrian	if (d->conf->button.left1_label != NULL) {
31264775Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label;
31364775Sbrian		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1;
31464775Sbrian		d->bs.nbuttons += 1;
31564775Sbrian	}
31664775Sbrian
31764775Sbrian	if (d->conf->button.left2_label != NULL) {
31864775Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label;
31964775Sbrian		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2;
32064775Sbrian		d->bs.nbuttons += 1;
32164775Sbrian	}
32264775Sbrian
32364775Sbrian	if (d->conf->button.left3_label != NULL) {
32464775Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label;
325126960Sbde		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3;
32664775Sbrian		d->bs.nbuttons += 1;
32764775Sbrian	}
32864775Sbrian
32964775Sbrian	if (oklabel != NULL && d->conf->button.without_ok == false) {
33064775Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ?
33164775Sbrian		    d->conf->button.ok_label : oklabel;
33264775Sbrian		d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK;
33364775Sbrian		d->bs.nbuttons += 1;
33464775Sbrian	}
33564775Sbrian
3361590Srgrimes	if (d->conf->button.with_extra) {
33765064Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ?
33865064Sbrian		    d->conf->button.extra_label : "Extra";
33965064Sbrian		d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA;
3401590Srgrimes		d->bs.nbuttons += 1;
3411590Srgrimes	}
34266675Sru
34366675Sru	if (cancellabel != NULL && d->conf->button.without_cancel == false) {
34465064Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ?
34565064Sbrian		    d->conf->button.cancel_label : cancellabel;
34666675Sru		d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL;
34765064Sbrian		if (d->conf->button.default_cancel)
34865064Sbrian			d->bs.curr = d->bs.nbuttons;
34965064Sbrian		d->bs.nbuttons += 1;
3501590Srgrimes	}
35165787Sbrian
3521590Srgrimes	if (d->conf->button.with_help) {
35365064Sbrian		d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ?
35465787Sbrian		    d->conf->button.help_label : "Help";
35565064Sbrian		d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP;
35665787Sbrian		d->bs.nbuttons += 1;
3571590Srgrimes	}
3581590Srgrimes
3591590Srgrimes	if (d->conf->button.right1_label != NULL) {
3605369Sjkh		d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label;
3611590Srgrimes		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1;
3621590Srgrimes		d->bs.nbuttons += 1;
36327169Scharnier	}
3641590Srgrimes
3651590Srgrimes	if (d->conf->button.right2_label != NULL) {
3661590Srgrimes		d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label;
3674991Spst		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2;
3681590Srgrimes		d->bs.nbuttons += 1;
3694991Spst	}
3704991Spst
3714991Spst	if (d->conf->button.right3_label != NULL) {
3721590Srgrimes		d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label;
373200793Sdelphij		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3;
3741590Srgrimes		d->bs.nbuttons += 1;
3751590Srgrimes	}
3761590Srgrimes
377117010Sjmallett	if (d->bs.nbuttons == 0) {
378117010Sjmallett		d->bs.label[0] = DEFAULT_BUTTON_LABEL;
379117010Sjmallett		d->bs.value[0] = DEFAULT_BUTTON_VALUE;
3801590Srgrimes		d->bs.nbuttons = 1;
3811590Srgrimes	}
3821590Srgrimes
3831590Srgrimes	for (i = 0; i < (int)d->bs.nbuttons; i++) {
384201140Sed		mbtowc(&first, d->bs.label[i], MB_CUR_MAX);
385201140Sed		d->bs.first[i] = first;
386201140Sed	}
3871590Srgrimes
388201140Sed	if (d->conf->button.default_label != NULL) {
3891590Srgrimes		for (i = 0; i < (int)d->bs.nbuttons; i++) {
390201140Sed			if (strcmp(d->conf->button.default_label,
3911590Srgrimes			    d->bs.label[i]) == 0)
392201140Sed				d->bs.curr = i;
3931590Srgrimes		}
39487229Smarkm	}
39523693Speter
39623693Speter	d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0]));
39787229Smarkm	for (i = 1; i < (int)d->bs.nbuttons; i++)
3981590Srgrimes		d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i]));
39923693Speter	d->bs.sizebutton += 2;
4001590Srgrimes}
4011590Srgrimes
40223693Speterbool shortcut_buttons(wint_t key, struct buttons *bs)
40323693Speter{
4041590Srgrimes	bool match;
4051590Srgrimes	unsigned int i;
406
407	match = false;
408	for (i = 0; i < bs->nbuttons; i++) {
409		if (towlower(key) == towlower(bs->first[i])) {
410			bs->curr = i;
411			match = true;
412			break;
413		}
414	}
415
416	return (match);
417}
418
419/*
420 * -4- (Auto) Sizing and (Auto) Position
421 */
422static int widget_max_height(struct bsddialog_conf *conf)
423{
424	int maxheight;
425
426	maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES;
427	if (maxheight <= 0)
428		RETURN_ERROR("Terminal too small, screen lines - shadow <= 0");
429
430	if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0)
431		RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0");
432	else if (conf->y == BSDDIALOG_CENTER) {
433		maxheight -= conf->auto_topmargin;
434		if (maxheight <= 0)
435			RETURN_ERROR("Terminal too small, screen lines - top "
436			    "margins <= 0");
437	} else if (conf->y > 0) {
438		maxheight -= conf->y;
439		if (maxheight <= 0)
440			RETURN_ERROR("Terminal too small, screen lines - "
441			    "shadow - y <= 0");
442	}
443
444	maxheight -= conf->auto_downmargin;
445	if (maxheight <= 0)
446		RETURN_ERROR("Terminal too small, screen lines - Down margins "
447		    "<= 0");
448
449	return (maxheight);
450}
451
452static int widget_max_width(struct bsddialog_conf *conf)
453{
454	int maxwidth;
455
456	maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS;
457	if (maxwidth <= 0)
458		RETURN_ERROR("Terminal too small, screen cols - shadow <= 0");
459
460	if (conf->x > 0) {
461		maxwidth -= conf->x;
462		if (maxwidth <= 0)
463			RETURN_ERROR("Terminal too small, screen cols - shadow "
464			    "- x <= 0");
465	}
466
467	return (maxwidth);
468}
469
470static bool is_wtext_attr(const wchar_t *wtext)
471{
472	bool att;
473
474	if (wcsnlen(wtext, 3) < 3)
475		return (false);
476	if (wtext[0] != L'\\' || wtext[1] != L'Z')
477		return (false);
478
479	att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true;
480
481	return (att);
482}
483
484#define NL  -1
485#define WS  -2
486#define TB  -3
487
488struct textproperties {
489	int nword;
490	int *words;
491	uint8_t *wletters;
492	int maxwordcols;
493	int maxline;
494	bool hasnewline;
495};
496
497static int
498text_properties(struct bsddialog_conf *conf, const char *text,
499    struct textproperties *tp)
500{
501	int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols;
502	wchar_t *wtext;
503
504	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
505
506	maxwords = 1024;
507	if ((tp->words = calloc(maxwords, sizeof(int))) == NULL)
508		RETURN_ERROR("Cannot alloc memory for text autosize");
509
510	if ((wtext = alloc_mbstows(text)) == NULL)
511		RETURN_ERROR("Cannot allocate/autosize text in wchar_t*");
512	wtextlen = wcslen(wtext);
513	if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL)
514		RETURN_ERROR("Cannot allocate wletters for text autosizing");
515
516	tp->nword = 0;
517	tp->maxline = 0;
518	tp->maxwordcols = 0;
519	tp->hasnewline = false;
520	currlinecols = 0;
521	wordcols = 0;
522	l = 0;
523	for (i = 0; i < wtextlen; i++) {
524		if (conf->text.escape && is_wtext_attr(wtext + i)) {
525			i += 2; /* +1 for update statement */
526			continue;
527		}
528
529		if (tp->nword + 1 >= maxwords) {
530			maxwords += 1024;
531			tp->words = realloc(tp->words, maxwords * sizeof(int));
532			if (tp->words == NULL)
533				RETURN_ERROR("Cannot realloc memory for text "
534				    "autosize");
535		}
536
537		if (wcschr(L"\t\n  ", wtext[i]) != NULL) {
538			tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
539
540			if (wordcols != 0) {
541				/* line */
542				currlinecols += wordcols;
543				/* word */
544				tp->words[tp->nword] = wordcols;
545				tp->nword += 1;
546				wordcols = 0;
547			}
548
549			switch (wtext[i]) {
550			case L'\t':
551				/* line */
552				currlinecols += tablen;
553				/* word */
554				tp->words[tp->nword] = TB;
555				break;
556			case L'\n':
557				/* line */
558				tp->hasnewline = true;
559				tp->maxline = MAX(tp->maxline, currlinecols);
560				currlinecols = 0;
561				/* word */
562				tp->words[tp->nword] = NL;
563				break;
564			case L' ':
565				/* line */
566				currlinecols += 1;
567				/* word */
568				tp->words[tp->nword] = WS;
569				break;
570			}
571			tp->nword += 1;
572		} else {
573			tp->wletters[l] = wcwidth(wtext[i]);
574			wordcols += tp->wletters[l];
575			l++;
576		}
577	}
578	/* word */
579	if (wordcols != 0) {
580		tp->words[tp->nword] = wordcols;
581		tp->nword += 1;
582		tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
583	}
584	/* line */
585	tp->maxline = MAX(tp->maxline, currlinecols);
586
587	free(wtext);
588
589	return (0);
590}
591
592static int
593text_autosize(struct bsddialog_conf *conf, struct textproperties *tp,
594    int maxrows, int mincols, bool increasecols, int *h, int *w)
595{
596	int i, j, x, y, z, l, line, maxwidth, tablen;
597
598	maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
599	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
600
601	if (increasecols) {
602		mincols = MAX(mincols, tp->maxwordcols);
603		mincols = MAX(mincols,
604		    (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS);
605		mincols = MIN(mincols, maxwidth);
606	}
607
608	while (true) {
609		x = 0;
610		y = 1;
611		line=0;
612		l = 0;
613		for (i = 0; i < tp->nword; i++) {
614			switch (tp->words[i]) {
615			case TB:
616				for (j = 0; j < tablen; j++) {
617					if (x >= mincols) {
618						x = 0;
619						y++;
620					}
621				x++;
622				}
623				break;
624			case NL:
625				y++;
626				x = 0;
627				break;
628			case WS:
629				x++;
630				if (x >= mincols) {
631					x = 0;
632					y++;
633				}
634				break;
635			default:
636				if (tp->words[i] + x <= mincols) {
637					x += tp->words[i];
638					for (z = 0 ; z != tp->words[i]; l++ )
639						z += tp->wletters[l];
640				} else if (tp->words[i] <= mincols) {
641					y++;
642					x = tp->words[i];
643					for (z = 0 ; z != tp->words[i]; l++ )
644						z += tp->wletters[l];
645				} else {
646					for (j = tp->words[i]; j > 0; ) {
647						y = (x == 0) ? y : y + 1;
648						z = 0;
649						while (z != j && z < mincols) {
650							z += tp->wletters[l];
651							l++;
652						}
653						x = z;
654						line = MAX(line, x);
655						j -= z;
656					}
657				}
658			}
659			line = MAX(line, x);
660		}
661
662		if (increasecols == false)
663			break;
664		if (mincols >= maxwidth)
665			break;
666		if (line >= y * (int)conf->text.cols_per_row && y <= maxrows)
667			break;
668		mincols++;
669	}
670
671	*h = (tp->nword == 0) ? 0 : y;
672	*w = MIN(mincols, line); /* wtext can be less than mincols */
673
674	return (0);
675}
676
677static int
678text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text,
679    struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext)
680{
681	bool changewtext;
682	int wbuttons, maxhtext;
683	struct textproperties tp;
684
685	wbuttons = 0;
686	if (bs->nbuttons > 0)
687		wbuttons = buttons_min_width(bs);
688
689	/* Rows */
690	if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) {
691		maxhtext = widget_max_height(conf) - BORDERS - rowsnotext;
692	} else { /* fixed */
693		maxhtext = rows - BORDERS - rowsnotext;
694	}
695	if (bs->nbuttons > 0)
696		maxhtext -= 2;
697	if (maxhtext <= 0)
698		maxhtext = 1; /* text_autosize() computes always htext */
699
700	/* Cols */
701	if (cols == BSDDIALOG_AUTOSIZE) {
702		startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS);
703		changewtext = true;
704	} else if (cols == BSDDIALOG_FULLSCREEN) {
705		startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
706		changewtext = false;
707	} else { /* fixed */
708		startwtext = cols - BORDERS - TEXTHMARGINS;
709		changewtext = false;
710	}
711
712	if (startwtext <= 0 && changewtext)
713		startwtext = 1;
714
715	/* Sizing calculation */
716	if (text_properties(conf, text, &tp) != 0)
717		return (BSDDIALOG_ERROR);
718	if (tp.nword > 0 && startwtext <= 0)
719		RETURN_FMTERROR("(fixed cols or fullscreen) "
720		    "needed at least %d cols to draw text",
721		    BORDERS + TEXTHMARGINS + 1);
722	if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext,
723	    wtext) != 0)
724		return (BSDDIALOG_ERROR);
725
726	free(tp.words);
727	free(tp.wletters);
728
729	return (0);
730}
731
732static int
733widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext,
734    bool withbuttons)
735{
736	int min;
737
738	/* dialog borders */
739	min = BORDERS;
740
741	/* text */
742	min += htext;
743
744	/* specific widget lines without text */
745	min += hnotext;
746
747	/* buttons */
748	if (withbuttons)
749		min += HBUTTONS; /* buttons and their up-border */
750
751	/* conf.auto_minheight */
752	min = MAX(min, (int)conf->auto_minheight);
753
754	return (min);
755}
756
757static int
758widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget,
759    struct buttons *bs)
760
761{
762	int min, delimtitle, wbottomtitle, wtitle;
763
764	min = 0;
765
766	/* buttons */
767	if (bs->nbuttons > 0)
768		min += buttons_min_width(bs);
769
770	/* text */
771	if (wtext > 0)
772		min = MAX(min, wtext + TEXTHMARGINS);
773
774	/* specific widget min width */
775	min = MAX(min, minwidget);
776
777	/* title */
778	if (conf->title != NULL) {
779		delimtitle = t.dialog.delimtitle ? 2 : 0;
780		wtitle = strcols(conf->title);
781		min = MAX(min, wtitle + 2 + delimtitle);
782	}
783
784	/* bottom title */
785	if (conf->bottomtitle != NULL) {
786		wbottomtitle = strcols(conf->bottomtitle);
787		min = MAX(min, wbottomtitle + 4);
788	}
789
790	/* dialog borders */
791	min += BORDERS;
792	/* conf.auto_minwidth */
793	min = MAX(min, (int)conf->auto_minwidth);
794
795	return (min);
796}
797
798int
799set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
800{
801	int maxheight, maxwidth;
802
803	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
804		return (BSDDIALOG_ERROR);
805
806	if (rows == BSDDIALOG_FULLSCREEN)
807		*h = maxheight;
808	else if (rows < BSDDIALOG_FULLSCREEN)
809		RETURN_ERROR("Negative (less than -1) height");
810	else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */
811		*h = MIN(rows, maxheight); /* rows is at most maxheight */
812	/* rows == AUTOSIZE: each widget has to set its size */
813
814	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
815		return (BSDDIALOG_ERROR);
816
817	if (cols == BSDDIALOG_FULLSCREEN)
818		*w = maxwidth;
819	else if (cols < BSDDIALOG_FULLSCREEN)
820		RETURN_ERROR("Negative (less than -1) width");
821	else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */
822		*w = MIN(cols, maxwidth); /* cols is at most maxwidth */
823	/* cols == AUTOSIZE: each widget has to set its size */
824
825	return (0);
826}
827
828int
829set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
830    int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext,
831    int minw)
832{
833	int htext, wtext;
834
835	if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE ||
836	    rowstext != NULL) {
837		if (text_size(conf, rows, cols, text, bs, hnotext, minw,
838		    &htext, &wtext) != 0)
839			return (BSDDIALOG_ERROR);
840		if (rowstext != NULL)
841			*rowstext = htext;
842	}
843
844	if (rows == BSDDIALOG_AUTOSIZE) {
845		*h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0);
846		*h = MIN(*h, widget_max_height(conf));
847	}
848
849	if (cols == BSDDIALOG_AUTOSIZE) {
850		*w = widget_min_width(conf, wtext, minw, bs);
851		*w = MIN(*w, widget_max_width(conf));
852	}
853
854	return (0);
855}
856
857int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw)
858{
859	int minheight, minwidth;
860
861	minheight = BORDERS + hnotext;
862	if (bs->nbuttons > 0)
863		minheight += HBUTTONS;
864	if (h < minheight)
865		RETURN_FMTERROR("Current rows: %d, needed at least: %d",
866		    h, minheight);
867
868	minwidth = 0;
869	if (bs->nbuttons > 0)
870		minwidth = buttons_min_width(bs);
871	minwidth = MAX(minwidth, minw);
872	minwidth += BORDERS;
873	if (w < minwidth)
874		RETURN_FMTERROR("Current cols: %d, nedeed at least %d",
875		    w, minwidth);
876
877	return (0);
878}
879
880int
881set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
882{
883	int hshadow = conf->shadow ? (int)t.shadow.y : 0;
884	int wshadow = conf->shadow ? (int)t.shadow.x : 0;
885
886	if (conf->y == BSDDIALOG_CENTER) {
887		*y = SCREENLINES/2 - (h + hshadow)/2;
888		if (*y < (int)conf->auto_topmargin)
889			*y = conf->auto_topmargin;
890		if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin)
891			*y = SCREENLINES - h - hshadow - conf->auto_downmargin;
892	}
893	else if (conf->y < BSDDIALOG_CENTER)
894		RETURN_ERROR("Negative begin y (less than -1)");
895	else if (conf->y >= SCREENLINES)
896		RETURN_ERROR("Begin Y under the terminal");
897	else
898		*y = conf->y;
899
900	if (*y + h + hshadow > SCREENLINES)
901		RETURN_ERROR("The lower of the box under the terminal "
902		    "(begin Y + height (+ shadow) > terminal lines)");
903
904
905	if (conf->x == BSDDIALOG_CENTER)
906		*x = SCREENCOLS/2 - (w + wshadow)/2;
907	else if (conf->x < BSDDIALOG_CENTER)
908		RETURN_ERROR("Negative begin x (less than -1)");
909	else if (conf->x >= SCREENCOLS)
910		RETURN_ERROR("Begin X over the right of the terminal");
911	else
912		*x = conf->x;
913
914	if ((*x + w + wshadow) > SCREENCOLS)
915		RETURN_ERROR("The right of the box over the terminal "
916		    "(begin X + width (+ shadow) > terminal cols)");
917
918	return (0);
919}
920
921int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext)
922{
923	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
924		return (BSDDIALOG_ERROR);
925	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
926	    d->text, htext, &d->bs, hnotext, minw) != 0)
927		return (BSDDIALOG_ERROR);
928	if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0)
929		return (BSDDIALOG_ERROR);
930	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
931		return (BSDDIALOG_ERROR);
932
933	return (0);
934}
935
936/*
937 * -5- Widget components and utilities
938 */
939int hide_dialog(struct dialog *d)
940{
941	WINDOW *clear;
942
943	if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL)
944		RETURN_ERROR("Cannot hide the widget");
945	wbkgd(clear, t.screen.color);
946	wrefresh(clear);
947
948	if (d->conf->shadow) {
949		mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x);
950		wrefresh(clear);
951	}
952
953	delwin(clear);
954
955	return (0);
956}
957
958int f1help_dialog(struct bsddialog_conf *conf)
959{
960	int output;
961	struct bsddialog_conf hconf;
962
963	bsddialog_initconf(&hconf);
964	hconf.title           = "HELP";
965	hconf.button.ok_label = "EXIT";
966	hconf.clear           = true;
967	hconf.ascii_lines     = conf->ascii_lines;
968	hconf.no_lines        = conf->no_lines;
969	hconf.shadow          = conf->shadow;
970	hconf.text.escape     = conf->text.escape;
971
972	output = BSDDIALOG_OK;
973	if (conf->key.f1_message != NULL)
974		output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0);
975
976	if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL)
977		output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0);
978
979	return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0);
980}
981
982void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev)
983{
984	int h, w;
985	int leftcolor, rightcolor;
986	cchar_t *ls, *rs, *ts, *bs, *tl, *tr, *bl, *br;
987	cchar_t hline, vline, corner;
988
989	if (conf->no_lines)
990		return;
991
992	if (conf->ascii_lines) {
993		setcchar(&hline, L"|", 0, 0, NULL);
994		ls = rs = &hline;
995		setcchar(&vline, L"-", 0, 0, NULL);
996		ts = bs = &vline;
997		setcchar(&corner, L"+", 0, 0, NULL);
998		tl = tr = bl = br = &corner;
999	} else {
1000		ls = rs = WACS_VLINE;
1001		ts = bs = WACS_HLINE;
1002		tl = WACS_ULCORNER;
1003		tr = WACS_URCORNER;
1004		bl = WACS_LLCORNER;
1005		br = WACS_LRCORNER;
1006	}
1007
1008	getmaxyx(win, h, w);
1009	leftcolor = (elev == RAISED) ?
1010	    t.dialog.lineraisecolor : t.dialog.linelowercolor;
1011	rightcolor = (elev == RAISED) ?
1012	    t.dialog.linelowercolor : t.dialog.lineraisecolor;
1013
1014	wattron(win, leftcolor);
1015	wborder_set(win, ls, rs, ts, bs, tl, tr, bl, br);
1016	wattroff(win, leftcolor);
1017
1018	wattron(win, rightcolor);
1019	mvwadd_wch(win, 0, w-1, tr);
1020	mvwvline_set(win, 1, w-1, rs, h-2);
1021	mvwadd_wch(win, h-1, w-1, br);
1022	mvwhline_set(win, h-1, 1, bs, w-2);
1023	wattroff(win, rightcolor);
1024}
1025
1026void
1027update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w,
1028    enum elevation elev)
1029{
1030	wclear(win);
1031	wresize(win, h, w);
1032	mvwin(win, y, x);
1033	draw_borders(conf, win, elev);
1034}
1035
1036void
1037rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext)
1038{
1039	pnoutrefresh(d->textpad, ytext, xtext,
1040	    d->y + BORDER + upnotext,
1041	    d->x + BORDER + TEXTHMARGIN,
1042	    d->y + d->h - 1 - downnotext - BORDER,
1043	    d->x + d->w - TEXTHMARGIN - BORDER);
1044}
1045
1046/*
1047 * -6- Dialog init/build, update/draw, destroy
1048 */
1049void end_dialog(struct dialog *d)
1050{
1051	if (d->conf->sleep > 0)
1052		sleep(d->conf->sleep);
1053
1054	delwin(d->textpad);
1055	delwin(d->widget);
1056	if (d->conf->shadow)
1057		delwin(d->shadow);
1058
1059	if (d->conf->clear)
1060		hide_dialog(d);
1061
1062	if (d->conf->get_height != NULL)
1063		*d->conf->get_height = d->h;
1064	if (d->conf->get_width != NULL)
1065		*d->conf->get_width = d->w;
1066}
1067
1068static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext)
1069{
1070	enum bsddialog_color bg;
1071
1072	if (is_wtext_attr(wtext) == false)
1073		return (false);
1074
1075	if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) {
1076		bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL);
1077		wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0));
1078		return (true);
1079	}
1080
1081	switch (wtext[2]) {
1082	case L'n':
1083		wattron(win, t.dialog.color);
1084		wattrset(win, A_NORMAL);
1085		break;
1086	case L'b':
1087		wattron(win, A_BOLD);
1088		break;
1089	case L'B':
1090		wattroff(win, A_BOLD);
1091		break;
1092	case L'd':
1093		wattron(win, A_DIM);
1094		break;
1095	case L'D':
1096		wattroff(win, A_DIM);
1097		break;
1098	case L'k':
1099		wattron(win, A_BLINK);
1100		break;
1101	case L'K':
1102		wattroff(win, A_BLINK);
1103		break;
1104	case L'r':
1105		wattron(win, A_REVERSE);
1106		break;
1107	case L'R':
1108		wattroff(win, A_REVERSE);
1109		break;
1110	case L's':
1111		wattron(win, A_STANDOUT);
1112		break;
1113	case L'S':
1114		wattroff(win, A_STANDOUT);
1115		break;
1116	case L'u':
1117		wattron(win, A_UNDERLINE);
1118		break;
1119	case L'U':
1120		wattroff(win, A_UNDERLINE);
1121		break;
1122	}
1123
1124	return (true);
1125}
1126
1127static void
1128print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str,
1129    bool color)
1130{
1131	int charwidth, i, j, strlen, strwidth;
1132	wchar_t ws[2];
1133
1134	ws[1] = L'\0';
1135
1136	strlen = wcslen(str);
1137	if (color) {
1138		strwidth = 0;
1139		i=0;
1140		while (i < strlen) {
1141			if (is_wtext_attr(str+i) == false) {
1142				strwidth += wcwidth(str[i]);
1143				i++;
1144			} else {
1145				i += 3;
1146			}
1147		}
1148	} else
1149		strwidth = wcswidth(str, strlen);
1150
1151	i = 0;
1152	while (i < strlen) {
1153		if (*x + strwidth > cols) {
1154			if (*x != 0)
1155				*y = *y + 1;
1156			if (*y >= *rows) {
1157				*rows = *y + 1;
1158				wresize(win, *rows, cols);
1159			}
1160			*x = 0;
1161		}
1162		j = *x;
1163		while (i < strlen) {
1164			if (color && check_set_wtext_attr(win, str+i)) {
1165				i += 3;
1166				continue;
1167			}
1168
1169			charwidth = wcwidth(str[i]);
1170			if (j + wcwidth(str[i]) > cols)
1171				break;
1172			/* inline mvwaddwch() for efficiency */
1173			ws[0] = str[i];
1174			mvwaddwstr(win, *y, j, ws);
1175			strwidth -= charwidth;
1176			j += charwidth;
1177			*x = j;
1178			i++;
1179		}
1180	}
1181}
1182
1183static int
1184print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text)
1185{
1186	bool loop;
1187	int i, j, z, rows, cols, x, y, tablen;
1188	wchar_t *wtext, *string;
1189
1190	if ((wtext = alloc_mbstows(text)) == NULL)
1191		RETURN_ERROR("Cannot allocate/print text in wchar_t*");
1192
1193	if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL)
1194		RETURN_ERROR("Cannot build (analyze) text");
1195
1196	getmaxyx(pad, rows, cols);
1197	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
1198
1199	i = j = x = y = 0;
1200	loop = true;
1201	while (loop) {
1202		string[j] = wtext[i];
1203
1204		if (wcschr(L"\n\t  ", string[j]) != NULL || string[j] == L'\0') {
1205			string[j] = L'\0';
1206			print_string(pad, &rows, cols, &y, &x, string,
1207			    conf->text.escape);
1208		}
1209
1210		switch (wtext[i]) {
1211		case L'\0':
1212			loop = false;
1213			break;
1214		case L'\n':
1215			x = 0;
1216			y++;
1217			j = -1;
1218			break;
1219		case L'\t':
1220			for (z = 0; z < tablen; z++) {
1221				if (x >= cols) {
1222					x = 0;
1223					y++;
1224				}
1225				x++;
1226			}
1227			j = -1;
1228			break;
1229		case L' ':
1230			x++;
1231			if (x >= cols) {
1232				x = 0;
1233				y++;
1234			}
1235			j = -1;
1236		}
1237
1238		if (y >= rows) {
1239			rows = y + 1;
1240			wresize(pad, rows, cols);
1241		}
1242
1243		j++;
1244		i++;
1245	}
1246
1247	free(wtext);
1248	free(string);
1249
1250	return (0);
1251}
1252
1253int draw_dialog(struct dialog *d)
1254{
1255	int wtitle, wbottomtitle;
1256	cchar_t ts, ltee, rtee;
1257
1258	if (d->conf->ascii_lines) {
1259		setcchar(&ts, L"-", 0, 0, NULL);
1260		setcchar(&ltee, L"+", 0, 0,NULL);
1261		setcchar(&rtee, L"+", 0, 0, NULL);
1262	} else {
1263		ts = *WACS_HLINE;
1264		ltee = *WACS_LTEE;
1265		rtee = *WACS_RTEE;
1266	}
1267
1268	if (d->conf->shadow) {
1269		wclear(d->shadow);
1270		wresize(d->shadow, d->h, d->w);
1271		mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x);
1272		wnoutrefresh(d->shadow);
1273	}
1274
1275	wclear(d->widget);
1276	wresize(d->widget, d->h, d->w);
1277	mvwin(d->widget, d->y, d->x);
1278	draw_borders(d->conf, d->widget, RAISED);
1279
1280	if (d->conf->title != NULL) {
1281		if ((wtitle = strcols(d->conf->title)) < 0)
1282			return (BSDDIALOG_ERROR);
1283		if (t.dialog.delimtitle && d->conf->no_lines == false) {
1284			wattron(d->widget, t.dialog.lineraisecolor);
1285			mvwadd_wch(d->widget, 0, d->w/2 - wtitle/2 -1, &rtee);
1286			wattroff(d->widget, t.dialog.lineraisecolor);
1287		}
1288		wattron(d->widget, t.dialog.titlecolor);
1289		mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title);
1290		wattroff(d->widget, t.dialog.titlecolor);
1291		if (t.dialog.delimtitle && d->conf->no_lines == false) {
1292			wattron(d->widget, t.dialog.lineraisecolor);
1293			wadd_wch(d->widget, &ltee);
1294			wattroff(d->widget, t.dialog.lineraisecolor);
1295		}
1296	}
1297
1298	if (d->bs.nbuttons > 0) {
1299		if (d->conf->no_lines == false) {
1300			wattron(d->widget, t.dialog.lineraisecolor);
1301			mvwadd_wch(d->widget, d->h-3, 0, &ltee);
1302			mvwhline_set(d->widget, d->h-3, 1, &ts, d->w-2);
1303			wattroff(d->widget, t.dialog.lineraisecolor);
1304
1305			wattron(d->widget, t.dialog.linelowercolor);
1306			mvwadd_wch(d->widget, d->h-3, d->w-1, &rtee);
1307			wattroff(d->widget, t.dialog.linelowercolor);
1308		}
1309		draw_buttons(d);
1310	}
1311
1312	if (d->conf->bottomtitle != NULL) {
1313		if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0)
1314			return (BSDDIALOG_ERROR);
1315		wattron(d->widget, t.dialog.bottomtitlecolor);
1316		wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1);
1317		waddch(d->widget, ' ');
1318		waddstr(d->widget, d->conf->bottomtitle);
1319		waddch(d->widget, ' ');
1320		wattroff(d->widget, t.dialog.bottomtitlecolor);
1321	}
1322
1323	wnoutrefresh(d->widget);
1324
1325	wclear(d->textpad);
1326	/* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */
1327	wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS);
1328
1329	if (print_textpad(d->conf, d->textpad, d->text) != 0)
1330		return (BSDDIALOG_ERROR);
1331
1332	d->built = true;
1333
1334	return (0);
1335}
1336
1337int
1338prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows,
1339    int cols, struct dialog *d)
1340{
1341	CHECK_PTR(conf);
1342
1343	d->built = false;
1344	d->conf = conf;
1345	d->rows = rows;
1346	d->cols = cols;
1347	d->text = CHECK_STR(text);
1348	d->bs.nbuttons = 0;
1349
1350	if (d->conf->shadow) {
1351		if ((d->shadow = newwin(1, 1, 1, 1)) == NULL)
1352			RETURN_ERROR("Cannot build WINDOW shadow");
1353		wbkgd(d->shadow, t.shadow.color);
1354	}
1355
1356	if ((d->widget = newwin(1, 1, 1, 1)) == NULL)
1357		RETURN_ERROR("Cannot build WINDOW widget");
1358	wbkgd(d->widget, t.dialog.color);
1359
1360	/* fake for textpad */
1361	if ((d->textpad = newpad(1, 1)) == NULL)
1362		RETURN_ERROR("Cannot build the pad WINDOW for text");
1363	wbkgd(d->textpad, t.dialog.color);
1364
1365	return (0);
1366}