xref: /freebsd-src/contrib/bsddialog/lib/textbox.c (revision a6d8be451f62d425b71a4874f7d4e133b9fb393c)
1c76f0793SBaptiste Daroussin /*-
2c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3c76f0793SBaptiste Daroussin  *
4*a6d8be45SAlfonso S. Siciliano  * Copyright (c) 2021-2024 Alfonso Sabato Siciliano
5c76f0793SBaptiste Daroussin  *
6c76f0793SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7c76f0793SBaptiste Daroussin  * modification, are permitted provided that the following conditions
8c76f0793SBaptiste Daroussin  * are met:
9c76f0793SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11c76f0793SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13c76f0793SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14c76f0793SBaptiste Daroussin  *
15c76f0793SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c76f0793SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c76f0793SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c76f0793SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c76f0793SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c76f0793SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c76f0793SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c76f0793SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c76f0793SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c76f0793SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c76f0793SBaptiste Daroussin  * SUCH DAMAGE.
26c76f0793SBaptiste Daroussin  */
27c76f0793SBaptiste Daroussin 
28263660c0SAlfonso Siciliano #include <curses.h>
29c76f0793SBaptiste Daroussin 
30c76f0793SBaptiste Daroussin #include "bsddialog.h"
31c76f0793SBaptiste Daroussin #include "bsddialog_theme.h"
32263660c0SAlfonso Siciliano #include "lib_util.h"
33c76f0793SBaptiste Daroussin 
3461ba55bcSBaptiste Daroussin struct scrolltext {
3561ba55bcSBaptiste Daroussin 	WINDOW *pad;
3661ba55bcSBaptiste Daroussin 	int ypad;
3761ba55bcSBaptiste Daroussin 	int xpad;
3861ba55bcSBaptiste Daroussin 	int ys;
3961ba55bcSBaptiste Daroussin 	int ye;
4061ba55bcSBaptiste Daroussin 	int xs;
4161ba55bcSBaptiste Daroussin 	int xe;
4261ba55bcSBaptiste Daroussin 	int hpad;
4361ba55bcSBaptiste Daroussin 	int wpad;
4461ba55bcSBaptiste Daroussin 	int margin;    /* 2 with multicolumn char, 0 otherwise */
4561ba55bcSBaptiste Daroussin 	int printrows; /* d.h - BORDERS - HBUTTONS */
4661ba55bcSBaptiste Daroussin };
4761ba55bcSBaptiste Daroussin 
updateborders(struct dialog * d,struct scrolltext * st)4861ba55bcSBaptiste Daroussin static void updateborders(struct dialog *d, struct scrolltext *st)
49b319d934SAlfonso S. Siciliano {
50*a6d8be45SAlfonso S. Siciliano 	chtype arrowch;
51*a6d8be45SAlfonso S. Siciliano 	cchar_t borderch;
52b319d934SAlfonso S. Siciliano 
5361ba55bcSBaptiste Daroussin 	if (d->conf->no_lines)
54*a6d8be45SAlfonso S. Siciliano 		setcchar(&borderch, L" ", 0, 0, NULL);
5561ba55bcSBaptiste Daroussin 	else if (d->conf->ascii_lines)
56*a6d8be45SAlfonso S. Siciliano 		setcchar(&borderch, L"|", 0, 0, NULL);
57b319d934SAlfonso S. Siciliano 	else
58*a6d8be45SAlfonso S. Siciliano 		borderch = *WACS_VLINE;
59b319d934SAlfonso S. Siciliano 
6061ba55bcSBaptiste Daroussin 	if (st->xpad > 0) {
61*a6d8be45SAlfonso S. Siciliano 		arrowch = LARROW(d->conf) | t.dialog.arrowcolor;
6261ba55bcSBaptiste Daroussin 		mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4);
63*a6d8be45SAlfonso S. Siciliano 	} else {
64*a6d8be45SAlfonso S. Siciliano 		wattron(d->widget, t.dialog.lineraisecolor);
65*a6d8be45SAlfonso S. Siciliano 		mvwvline_set(d->widget, (d->h / 2) - 2, 0, &borderch, 4);
66*a6d8be45SAlfonso S. Siciliano 		wattroff(d->widget, t.dialog.lineraisecolor);
67*a6d8be45SAlfonso S. Siciliano 	}
68b319d934SAlfonso S. Siciliano 
6961ba55bcSBaptiste Daroussin 	if (st->xpad + d->w - 2 - st->margin < st->wpad) {
70*a6d8be45SAlfonso S. Siciliano 		arrowch = RARROW(d->conf) | t.dialog.arrowcolor;
7161ba55bcSBaptiste Daroussin 		mvwvline(d->widget, (d->h / 2) - 2, d->w - 1, arrowch, 4);
72*a6d8be45SAlfonso S. Siciliano 	} else {
73*a6d8be45SAlfonso S. Siciliano 		wattron(d->widget, t.dialog.linelowercolor);
74*a6d8be45SAlfonso S. Siciliano 		mvwvline_set(d->widget, (d->h / 2) - 2, d->w - 1, &borderch, 4);
75*a6d8be45SAlfonso S. Siciliano 		wattroff(d->widget, t.dialog.linelowercolor);
76*a6d8be45SAlfonso S. Siciliano 	}
77b319d934SAlfonso S. Siciliano 
7861ba55bcSBaptiste Daroussin 	if (st->hpad > d->h - 4) {
7961ba55bcSBaptiste Daroussin 		wattron(d->widget, t.dialog.arrowcolor);
8061ba55bcSBaptiste Daroussin 		mvwprintw(d->widget, d->h - 3, d->w - 6,
8161ba55bcSBaptiste Daroussin 		    "%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad);
8261ba55bcSBaptiste Daroussin 		wattroff(d->widget, t.dialog.arrowcolor);
83b319d934SAlfonso S. Siciliano 	}
84b319d934SAlfonso S. Siciliano }
85b319d934SAlfonso S. Siciliano 
textbox_size_position(struct dialog * d,struct scrolltext * st)8661ba55bcSBaptiste Daroussin static int textbox_size_position(struct dialog *d, struct scrolltext *st)
87c76f0793SBaptiste Daroussin {
8861ba55bcSBaptiste Daroussin 	int minw;
89c76f0793SBaptiste Daroussin 
9061ba55bcSBaptiste Daroussin 	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
9161ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
9261ba55bcSBaptiste Daroussin 	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
9361ba55bcSBaptiste Daroussin 	    d->text, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0)
9461ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
9561ba55bcSBaptiste Daroussin 	minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ;
9661ba55bcSBaptiste Daroussin 	if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0)
9761ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
9861ba55bcSBaptiste Daroussin 	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
9961ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
10061ba55bcSBaptiste Daroussin 
10161ba55bcSBaptiste Daroussin 	return (0);
102c76f0793SBaptiste Daroussin }
103c76f0793SBaptiste Daroussin 
textbox_draw(struct dialog * d,struct scrolltext * st)10461ba55bcSBaptiste Daroussin static int textbox_draw(struct dialog *d, struct scrolltext *st)
105c76f0793SBaptiste Daroussin {
10661ba55bcSBaptiste Daroussin 	if (d->built) {
10761ba55bcSBaptiste Daroussin 		hide_dialog(d);
10861ba55bcSBaptiste Daroussin 		refresh(); /* Important for decreasing screen */
10961ba55bcSBaptiste Daroussin 	}
11061ba55bcSBaptiste Daroussin 	if (textbox_size_position(d, st) != 0)
11161ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
11261ba55bcSBaptiste Daroussin 	if (draw_dialog(d) != 0)
11361ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
11461ba55bcSBaptiste Daroussin 	if (d->built)
11561ba55bcSBaptiste Daroussin 		refresh(); /* Important to fix grey lines expanding screen */
116c76f0793SBaptiste Daroussin 
11761ba55bcSBaptiste Daroussin 	st->ys = d->y + 1;
11861ba55bcSBaptiste Daroussin 	st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2;
11961ba55bcSBaptiste Daroussin 	st->ye = st->ys + d->h - 5;
12061ba55bcSBaptiste Daroussin 	st->xe = st->xs + d->w - 3 - st->margin;
12161ba55bcSBaptiste Daroussin 	st->ypad = st->xpad = 0;
12261ba55bcSBaptiste Daroussin 	st->printrows = d->h-4;
123c76f0793SBaptiste Daroussin 
124263660c0SAlfonso Siciliano 	return (0);
125c76f0793SBaptiste Daroussin }
126c76f0793SBaptiste Daroussin 
127263660c0SAlfonso Siciliano /* API */
128f499134dSBaptiste Daroussin int
bsddialog_textbox(struct bsddialog_conf * conf,const char * file,int rows,int cols)129263660c0SAlfonso Siciliano bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows,
130263660c0SAlfonso Siciliano     int cols)
131c76f0793SBaptiste Daroussin {
13261ba55bcSBaptiste Daroussin 	bool loop, has_multicol_ch;
13361ba55bcSBaptiste Daroussin 	int i, retval;
134b319d934SAlfonso S. Siciliano 	unsigned int defaulttablen, linecols;
135b319d934SAlfonso S. Siciliano 	wint_t input;
136263660c0SAlfonso Siciliano 	char buf[BUFSIZ];
137263660c0SAlfonso Siciliano 	FILE *fp;
13861ba55bcSBaptiste Daroussin 	struct scrolltext st;
13961ba55bcSBaptiste Daroussin 	struct dialog d;
140c76f0793SBaptiste Daroussin 
14161ba55bcSBaptiste Daroussin 	if (file == NULL)
14261ba55bcSBaptiste Daroussin 		RETURN_ERROR("*file is NULL");
143f499134dSBaptiste Daroussin 	if ((fp = fopen(file, "r")) == NULL)
14461ba55bcSBaptiste Daroussin 		RETURN_FMTERROR("Cannot open file \"%s\"", file);
14561ba55bcSBaptiste Daroussin 
14661ba55bcSBaptiste Daroussin 	if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0)
14761ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
14861ba55bcSBaptiste Daroussin 	set_buttons(&d, true, "EXIT", NULL);
149f499134dSBaptiste Daroussin 
150b319d934SAlfonso S. Siciliano 	defaulttablen = TABSIZE;
15161ba55bcSBaptiste Daroussin 	if (conf->text.tablen > 0)
15261ba55bcSBaptiste Daroussin 		set_tabsize(conf->text.tablen);
15361ba55bcSBaptiste Daroussin 	st.hpad = 1;
15461ba55bcSBaptiste Daroussin 	st.wpad = 1;
15561ba55bcSBaptiste Daroussin 	st.pad = newpad(st.hpad, st.wpad);
15661ba55bcSBaptiste Daroussin 	wbkgd(st.pad, t.dialog.color);
15761ba55bcSBaptiste Daroussin 	st.margin = 0;
158c76f0793SBaptiste Daroussin 	i = 0;
159c76f0793SBaptiste Daroussin 	while (fgets(buf, BUFSIZ, fp) != NULL) {
16061ba55bcSBaptiste Daroussin 		if (str_props(buf, &linecols, &has_multicol_ch) != 0)
161b319d934SAlfonso S. Siciliano 			continue;
16261ba55bcSBaptiste Daroussin 		if ((int)linecols > st.wpad) {
16361ba55bcSBaptiste Daroussin 			st.wpad = linecols;
16461ba55bcSBaptiste Daroussin 			wresize(st.pad, st.hpad, st.wpad);
165c76f0793SBaptiste Daroussin 		}
16661ba55bcSBaptiste Daroussin 		if (i > st.hpad-1) {
16761ba55bcSBaptiste Daroussin 			st.hpad++;
16861ba55bcSBaptiste Daroussin 			wresize(st.pad, st.hpad, st.wpad);
169c76f0793SBaptiste Daroussin 		}
17061ba55bcSBaptiste Daroussin 		mvwaddstr(st.pad, i, 0, buf);
171c76f0793SBaptiste Daroussin 		i++;
17261ba55bcSBaptiste Daroussin 		if (has_multicol_ch)
17361ba55bcSBaptiste Daroussin 			st.margin = 2;
174c76f0793SBaptiste Daroussin 	}
175c76f0793SBaptiste Daroussin 	fclose(fp);
17661ba55bcSBaptiste Daroussin 	set_tabsize(defaulttablen); /* reset because it is curses global */
177c76f0793SBaptiste Daroussin 
17861ba55bcSBaptiste Daroussin 	if (textbox_draw(&d, &st) != 0)
179263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
180c76f0793SBaptiste Daroussin 
181c76f0793SBaptiste Daroussin 	loop = true;
182c76f0793SBaptiste Daroussin 	while (loop) {
18361ba55bcSBaptiste Daroussin 		updateborders(&d, &st);
184b319d934SAlfonso S. Siciliano 		/*
185*a6d8be45SAlfonso S. Siciliano 		 * Trick, overflow multicolumn charchter right border:
186b319d934SAlfonso S. Siciliano 		 * wnoutrefresh(widget);
187b319d934SAlfonso S. Siciliano 		 * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe);
188b319d934SAlfonso S. Siciliano 		 * doupdate();
189b319d934SAlfonso S. Siciliano 		 */
19061ba55bcSBaptiste Daroussin 		wrefresh(d.widget);
19161ba55bcSBaptiste Daroussin 		prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe);
192b319d934SAlfonso S. Siciliano 		if (get_wch(&input) == ERR)
193b319d934SAlfonso S. Siciliano 			continue;
19461ba55bcSBaptiste Daroussin 		if (shortcut_buttons(input, &d.bs)) {
19561ba55bcSBaptiste Daroussin 			DRAW_BUTTONS(d);
19661ba55bcSBaptiste Daroussin 			doupdate();
19761ba55bcSBaptiste Daroussin 			retval = BUTTONVALUE(d.bs);
19861ba55bcSBaptiste Daroussin 			break; /* loop */
19961ba55bcSBaptiste Daroussin 		}
200c76f0793SBaptiste Daroussin 		switch(input) {
201c76f0793SBaptiste Daroussin 		case KEY_ENTER:
202c76f0793SBaptiste Daroussin 		case 10: /* Enter */
203b319d934SAlfonso S. Siciliano 			retval = BSDDIALOG_OK;
204c76f0793SBaptiste Daroussin 			loop = false;
205c76f0793SBaptiste Daroussin 			break;
206c76f0793SBaptiste Daroussin 		case 27: /* Esc */
207263660c0SAlfonso Siciliano 			if (conf->key.enable_esc) {
208b319d934SAlfonso S. Siciliano 				retval = BSDDIALOG_ESC;
209c76f0793SBaptiste Daroussin 				loop = false;
210263660c0SAlfonso Siciliano 			}
211c76f0793SBaptiste Daroussin 			break;
21261ba55bcSBaptiste Daroussin 		case '\t': /* TAB */
21361ba55bcSBaptiste Daroussin 			d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
21461ba55bcSBaptiste Daroussin 			DRAW_BUTTONS(d);
21561ba55bcSBaptiste Daroussin 			break;
216c76f0793SBaptiste Daroussin 		case KEY_HOME:
21761ba55bcSBaptiste Daroussin 			st.ypad = 0;
218c76f0793SBaptiste Daroussin 			break;
219c76f0793SBaptiste Daroussin 		case KEY_END:
22061ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.hpad - st.printrows, 0);
221c76f0793SBaptiste Daroussin 			break;
222c76f0793SBaptiste Daroussin 		case KEY_PPAGE:
22361ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.ypad - st.printrows, 0);
224c76f0793SBaptiste Daroussin 			break;
225c76f0793SBaptiste Daroussin 		case KEY_NPAGE:
22661ba55bcSBaptiste Daroussin 			st.ypad += st.printrows;
22761ba55bcSBaptiste Daroussin 			if (st.ypad + st.printrows > st.hpad)
22861ba55bcSBaptiste Daroussin 				st.ypad = st.hpad - st.printrows;
229c76f0793SBaptiste Daroussin 			break;
230c76f0793SBaptiste Daroussin 		case '0':
23161ba55bcSBaptiste Daroussin 			st.xpad = 0;
23261ba55bcSBaptiste Daroussin 			break;
233c76f0793SBaptiste Daroussin 		case KEY_LEFT:
234c76f0793SBaptiste Daroussin 		case 'h':
23561ba55bcSBaptiste Daroussin 			st.xpad = MAX(st.xpad - 1, 0);
236c76f0793SBaptiste Daroussin 			break;
237c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
238c76f0793SBaptiste Daroussin 		case 'l':
23961ba55bcSBaptiste Daroussin 			if (st.xpad + d.w - 2 - st.margin < st.wpad)
24061ba55bcSBaptiste Daroussin 				st.xpad++;
241c76f0793SBaptiste Daroussin 			break;
242c76f0793SBaptiste Daroussin 		case KEY_UP:
243c76f0793SBaptiste Daroussin 		case 'k':
24461ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.ypad - 1, 0);
245c76f0793SBaptiste Daroussin 			break;
246c76f0793SBaptiste Daroussin 		case KEY_DOWN:
247c76f0793SBaptiste Daroussin 		case'j':
24861ba55bcSBaptiste Daroussin 			if (st.ypad + st.printrows <= st.hpad -1)
24961ba55bcSBaptiste Daroussin 				st.ypad++;
250c76f0793SBaptiste Daroussin 			break;
251c76f0793SBaptiste Daroussin 		case KEY_F(1):
252bce40c02SAlfonso S. Siciliano 			if (conf->key.f1_file == NULL &&
253bce40c02SAlfonso S. Siciliano 			    conf->key.f1_message == NULL)
254c76f0793SBaptiste Daroussin 				break;
25561ba55bcSBaptiste Daroussin 			if (f1help_dialog(conf) != 0)
256263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
25761ba55bcSBaptiste Daroussin 			if (textbox_draw(&d, &st) != 0)
258263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
259c76f0793SBaptiste Daroussin 			break;
260*a6d8be45SAlfonso S. Siciliano 		case KEY_CTRL('l'):
26161ba55bcSBaptiste Daroussin 		case KEY_RESIZE:
26261ba55bcSBaptiste Daroussin 			if (textbox_draw(&d, &st) != 0)
26361ba55bcSBaptiste Daroussin 				return (BSDDIALOG_ERROR);
26461ba55bcSBaptiste Daroussin 			break;
265c76f0793SBaptiste Daroussin 		}
266c76f0793SBaptiste Daroussin 	}
267c76f0793SBaptiste Daroussin 
26861ba55bcSBaptiste Daroussin 	delwin(st.pad);
26961ba55bcSBaptiste Daroussin 	end_dialog(&d);
270c76f0793SBaptiste Daroussin 
271b319d934SAlfonso S. Siciliano 	return (retval);
272c76f0793SBaptiste Daroussin }
273