xref: /freebsd-src/contrib/bsddialog/lib/textbox.c (revision 61ba55bcf70f2340f9c943c9571113b3fd8eda69)
1c76f0793SBaptiste Daroussin /*-
2c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3c76f0793SBaptiste Daroussin  *
4*61ba55bcSBaptiste Daroussin  * Copyright (c) 2021-2023 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 
34*61ba55bcSBaptiste Daroussin struct scrolltext {
35*61ba55bcSBaptiste Daroussin 	WINDOW *pad;
36*61ba55bcSBaptiste Daroussin 	int ypad;
37*61ba55bcSBaptiste Daroussin 	int xpad;
38*61ba55bcSBaptiste Daroussin 	int ys;
39*61ba55bcSBaptiste Daroussin 	int ye;
40*61ba55bcSBaptiste Daroussin 	int xs;
41*61ba55bcSBaptiste Daroussin 	int xe;
42*61ba55bcSBaptiste Daroussin 	int hpad;
43*61ba55bcSBaptiste Daroussin 	int wpad;
44*61ba55bcSBaptiste Daroussin 	int margin;    /* 2 with multicolumn char, 0 otherwise */
45*61ba55bcSBaptiste Daroussin 	int printrows; /* d.h - BORDERS - HBUTTONS */
46*61ba55bcSBaptiste Daroussin };
47*61ba55bcSBaptiste Daroussin 
48*61ba55bcSBaptiste Daroussin static void updateborders(struct dialog *d, struct scrolltext *st)
49b319d934SAlfonso S. Siciliano {
50b319d934SAlfonso S. Siciliano 	chtype arrowch, borderch;
51b319d934SAlfonso S. Siciliano 
52*61ba55bcSBaptiste Daroussin 	if (d->conf->no_lines)
53b319d934SAlfonso S. Siciliano 		borderch = ' ';
54*61ba55bcSBaptiste Daroussin 	else if (d->conf->ascii_lines)
55b319d934SAlfonso S. Siciliano 		borderch = '|';
56b319d934SAlfonso S. Siciliano 	else
57b319d934SAlfonso S. Siciliano 		borderch = ACS_VLINE;
58b319d934SAlfonso S. Siciliano 
59*61ba55bcSBaptiste Daroussin 	if (st->xpad > 0) {
60*61ba55bcSBaptiste Daroussin 		arrowch = d->conf->ascii_lines ? '<' : ACS_LARROW;
61*61ba55bcSBaptiste Daroussin 		arrowch |= t.dialog.arrowcolor;
62b319d934SAlfonso S. Siciliano 	} else {
63b319d934SAlfonso S. Siciliano 		arrowch = borderch;
64*61ba55bcSBaptiste Daroussin 		arrowch |= t.dialog.lineraisecolor;
65b319d934SAlfonso S. Siciliano 	}
66*61ba55bcSBaptiste Daroussin 	mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4);
67b319d934SAlfonso S. Siciliano 
68*61ba55bcSBaptiste Daroussin 	if (st->xpad + d->w - 2 - st->margin < st->wpad) {
69*61ba55bcSBaptiste Daroussin 		arrowch = d->conf->ascii_lines ? '>' : ACS_RARROW;
70*61ba55bcSBaptiste Daroussin 		arrowch |= t.dialog.arrowcolor;
71b319d934SAlfonso S. Siciliano 	} else {
72b319d934SAlfonso S. Siciliano 		arrowch = borderch;
73*61ba55bcSBaptiste Daroussin 		arrowch |= t.dialog.linelowercolor;
74b319d934SAlfonso S. Siciliano 	}
75*61ba55bcSBaptiste Daroussin 	mvwvline(d->widget, (d->h / 2) - 2, d->w - 1, arrowch, 4);
76b319d934SAlfonso S. Siciliano 
77*61ba55bcSBaptiste Daroussin 	if (st->hpad > d->h - 4) {
78*61ba55bcSBaptiste Daroussin 		wattron(d->widget, t.dialog.arrowcolor);
79*61ba55bcSBaptiste Daroussin 		mvwprintw(d->widget, d->h - 3, d->w - 6,
80*61ba55bcSBaptiste Daroussin 		    "%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad);
81*61ba55bcSBaptiste Daroussin 		wattroff(d->widget, t.dialog.arrowcolor);
82b319d934SAlfonso S. Siciliano 	}
83b319d934SAlfonso S. Siciliano }
84b319d934SAlfonso S. Siciliano 
85*61ba55bcSBaptiste Daroussin static int textbox_size_position(struct dialog *d, struct scrolltext *st)
86c76f0793SBaptiste Daroussin {
87*61ba55bcSBaptiste Daroussin 	int minw;
88c76f0793SBaptiste Daroussin 
89*61ba55bcSBaptiste Daroussin 	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
90*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
91*61ba55bcSBaptiste Daroussin 	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
92*61ba55bcSBaptiste Daroussin 	    d->text, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0)
93*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
94*61ba55bcSBaptiste Daroussin 	minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ;
95*61ba55bcSBaptiste Daroussin 	if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0)
96*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
97*61ba55bcSBaptiste Daroussin 	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
98*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
99*61ba55bcSBaptiste Daroussin 
100*61ba55bcSBaptiste Daroussin 	return (0);
101c76f0793SBaptiste Daroussin }
102c76f0793SBaptiste Daroussin 
103*61ba55bcSBaptiste Daroussin static int textbox_draw(struct dialog *d, struct scrolltext *st)
104c76f0793SBaptiste Daroussin {
105*61ba55bcSBaptiste Daroussin 	if (d->built) {
106*61ba55bcSBaptiste Daroussin 		hide_dialog(d);
107*61ba55bcSBaptiste Daroussin 		refresh(); /* Important for decreasing screen */
108*61ba55bcSBaptiste Daroussin 	}
109*61ba55bcSBaptiste Daroussin 	if (textbox_size_position(d, st) != 0)
110*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
111*61ba55bcSBaptiste Daroussin 	if (draw_dialog(d) != 0)
112*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
113*61ba55bcSBaptiste Daroussin 	if (d->built)
114*61ba55bcSBaptiste Daroussin 		refresh(); /* Important to fix grey lines expanding screen */
115c76f0793SBaptiste Daroussin 
116*61ba55bcSBaptiste Daroussin 	st->ys = d->y + 1;
117*61ba55bcSBaptiste Daroussin 	st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2;
118*61ba55bcSBaptiste Daroussin 	st->ye = st->ys + d->h - 5;
119*61ba55bcSBaptiste Daroussin 	st->xe = st->xs + d->w - 3 - st->margin;
120*61ba55bcSBaptiste Daroussin 	st->ypad = st->xpad = 0;
121*61ba55bcSBaptiste Daroussin 	st->printrows = d->h-4;
122c76f0793SBaptiste Daroussin 
123263660c0SAlfonso Siciliano 	return (0);
124c76f0793SBaptiste Daroussin }
125c76f0793SBaptiste Daroussin 
126263660c0SAlfonso Siciliano /* API */
127f499134dSBaptiste Daroussin int
128263660c0SAlfonso Siciliano bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows,
129263660c0SAlfonso Siciliano     int cols)
130c76f0793SBaptiste Daroussin {
131*61ba55bcSBaptiste Daroussin 	bool loop, has_multicol_ch;
132*61ba55bcSBaptiste Daroussin 	int i, retval;
133b319d934SAlfonso S. Siciliano 	unsigned int defaulttablen, linecols;
134b319d934SAlfonso S. Siciliano 	wint_t input;
135263660c0SAlfonso Siciliano 	char buf[BUFSIZ];
136263660c0SAlfonso Siciliano 	FILE *fp;
137*61ba55bcSBaptiste Daroussin 	struct scrolltext st;
138*61ba55bcSBaptiste Daroussin 	struct dialog d;
139c76f0793SBaptiste Daroussin 
140*61ba55bcSBaptiste Daroussin 	if (file == NULL)
141*61ba55bcSBaptiste Daroussin 		RETURN_ERROR("*file is NULL");
142f499134dSBaptiste Daroussin 	if ((fp = fopen(file, "r")) == NULL)
143*61ba55bcSBaptiste Daroussin 		RETURN_FMTERROR("Cannot open file \"%s\"", file);
144*61ba55bcSBaptiste Daroussin 
145*61ba55bcSBaptiste Daroussin 	if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0)
146*61ba55bcSBaptiste Daroussin 		return (BSDDIALOG_ERROR);
147*61ba55bcSBaptiste Daroussin 	set_buttons(&d, true, "EXIT", NULL);
148f499134dSBaptiste Daroussin 
149b319d934SAlfonso S. Siciliano 	defaulttablen = TABSIZE;
150*61ba55bcSBaptiste Daroussin 	if (conf->text.tablen > 0)
151*61ba55bcSBaptiste Daroussin 		set_tabsize(conf->text.tablen);
152*61ba55bcSBaptiste Daroussin 	st.hpad = 1;
153*61ba55bcSBaptiste Daroussin 	st.wpad = 1;
154*61ba55bcSBaptiste Daroussin 	st.pad = newpad(st.hpad, st.wpad);
155*61ba55bcSBaptiste Daroussin 	wbkgd(st.pad, t.dialog.color);
156*61ba55bcSBaptiste Daroussin 	st.margin = 0;
157c76f0793SBaptiste Daroussin 	i = 0;
158c76f0793SBaptiste Daroussin 	while (fgets(buf, BUFSIZ, fp) != NULL) {
159*61ba55bcSBaptiste Daroussin 		if (str_props(buf, &linecols, &has_multicol_ch) != 0)
160b319d934SAlfonso S. Siciliano 			continue;
161*61ba55bcSBaptiste Daroussin 		if ((int)linecols > st.wpad) {
162*61ba55bcSBaptiste Daroussin 			st.wpad = linecols;
163*61ba55bcSBaptiste Daroussin 			wresize(st.pad, st.hpad, st.wpad);
164c76f0793SBaptiste Daroussin 		}
165*61ba55bcSBaptiste Daroussin 		if (i > st.hpad-1) {
166*61ba55bcSBaptiste Daroussin 			st.hpad++;
167*61ba55bcSBaptiste Daroussin 			wresize(st.pad, st.hpad, st.wpad);
168c76f0793SBaptiste Daroussin 		}
169*61ba55bcSBaptiste Daroussin 		mvwaddstr(st.pad, i, 0, buf);
170c76f0793SBaptiste Daroussin 		i++;
171*61ba55bcSBaptiste Daroussin 		if (has_multicol_ch)
172*61ba55bcSBaptiste Daroussin 			st.margin = 2;
173c76f0793SBaptiste Daroussin 	}
174c76f0793SBaptiste Daroussin 	fclose(fp);
175*61ba55bcSBaptiste Daroussin 	set_tabsize(defaulttablen); /* reset because it is curses global */
176c76f0793SBaptiste Daroussin 
177*61ba55bcSBaptiste Daroussin 	if (textbox_draw(&d, &st) != 0)
178263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
179c76f0793SBaptiste Daroussin 
180c76f0793SBaptiste Daroussin 	loop = true;
181c76f0793SBaptiste Daroussin 	while (loop) {
182*61ba55bcSBaptiste Daroussin 		updateborders(&d, &st);
183b319d934SAlfonso S. Siciliano 		/*
184b319d934SAlfonso S. Siciliano 		 * Overflow multicolumn charchter right border:
185b319d934SAlfonso S. Siciliano 		 * wnoutrefresh(widget);
186b319d934SAlfonso S. Siciliano 		 * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe);
187b319d934SAlfonso S. Siciliano 		 * doupdate();
188b319d934SAlfonso S. Siciliano 		 */
189*61ba55bcSBaptiste Daroussin 		wrefresh(d.widget);
190*61ba55bcSBaptiste Daroussin 		prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe);
191b319d934SAlfonso S. Siciliano 		if (get_wch(&input) == ERR)
192b319d934SAlfonso S. Siciliano 			continue;
193*61ba55bcSBaptiste Daroussin 		if (shortcut_buttons(input, &d.bs)) {
194*61ba55bcSBaptiste Daroussin 			DRAW_BUTTONS(d);
195*61ba55bcSBaptiste Daroussin 			doupdate();
196*61ba55bcSBaptiste Daroussin 			retval = BUTTONVALUE(d.bs);
197*61ba55bcSBaptiste Daroussin 			break; /* loop */
198*61ba55bcSBaptiste Daroussin 		}
199c76f0793SBaptiste Daroussin 		switch(input) {
200c76f0793SBaptiste Daroussin 		case KEY_ENTER:
201c76f0793SBaptiste Daroussin 		case 10: /* Enter */
202b319d934SAlfonso S. Siciliano 			retval = BSDDIALOG_OK;
203c76f0793SBaptiste Daroussin 			loop = false;
204c76f0793SBaptiste Daroussin 			break;
205c76f0793SBaptiste Daroussin 		case 27: /* Esc */
206263660c0SAlfonso Siciliano 			if (conf->key.enable_esc) {
207b319d934SAlfonso S. Siciliano 				retval = BSDDIALOG_ESC;
208c76f0793SBaptiste Daroussin 				loop = false;
209263660c0SAlfonso Siciliano 			}
210c76f0793SBaptiste Daroussin 			break;
211*61ba55bcSBaptiste Daroussin 		case '\t': /* TAB */
212*61ba55bcSBaptiste Daroussin 			d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
213*61ba55bcSBaptiste Daroussin 			DRAW_BUTTONS(d);
214*61ba55bcSBaptiste Daroussin 			break;
215c76f0793SBaptiste Daroussin 		case KEY_HOME:
216*61ba55bcSBaptiste Daroussin 			st.ypad = 0;
217c76f0793SBaptiste Daroussin 			break;
218c76f0793SBaptiste Daroussin 		case KEY_END:
219*61ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.hpad - st.printrows, 0);
220c76f0793SBaptiste Daroussin 			break;
221c76f0793SBaptiste Daroussin 		case KEY_PPAGE:
222*61ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.ypad - st.printrows, 0);
223c76f0793SBaptiste Daroussin 			break;
224c76f0793SBaptiste Daroussin 		case KEY_NPAGE:
225*61ba55bcSBaptiste Daroussin 			st.ypad += st.printrows;
226*61ba55bcSBaptiste Daroussin 			if (st.ypad + st.printrows > st.hpad)
227*61ba55bcSBaptiste Daroussin 				st.ypad = st.hpad - st.printrows;
228c76f0793SBaptiste Daroussin 			break;
229c76f0793SBaptiste Daroussin 		case '0':
230*61ba55bcSBaptiste Daroussin 			st.xpad = 0;
231*61ba55bcSBaptiste Daroussin 			break;
232c76f0793SBaptiste Daroussin 		case KEY_LEFT:
233c76f0793SBaptiste Daroussin 		case 'h':
234*61ba55bcSBaptiste Daroussin 			st.xpad = MAX(st.xpad - 1, 0);
235c76f0793SBaptiste Daroussin 			break;
236c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
237c76f0793SBaptiste Daroussin 		case 'l':
238*61ba55bcSBaptiste Daroussin 			if (st.xpad + d.w - 2 - st.margin < st.wpad)
239*61ba55bcSBaptiste Daroussin 				st.xpad++;
240c76f0793SBaptiste Daroussin 			break;
241c76f0793SBaptiste Daroussin 		case KEY_UP:
242c76f0793SBaptiste Daroussin 		case 'k':
243*61ba55bcSBaptiste Daroussin 			st.ypad = MAX(st.ypad - 1, 0);
244c76f0793SBaptiste Daroussin 			break;
245c76f0793SBaptiste Daroussin 		case KEY_DOWN:
246c76f0793SBaptiste Daroussin 		case'j':
247*61ba55bcSBaptiste Daroussin 			if (st.ypad + st.printrows <= st.hpad -1)
248*61ba55bcSBaptiste Daroussin 				st.ypad++;
249c76f0793SBaptiste Daroussin 			break;
250c76f0793SBaptiste Daroussin 		case KEY_F(1):
251bce40c02SAlfonso S. Siciliano 			if (conf->key.f1_file == NULL &&
252bce40c02SAlfonso S. Siciliano 			    conf->key.f1_message == NULL)
253c76f0793SBaptiste Daroussin 				break;
254*61ba55bcSBaptiste Daroussin 			if (f1help_dialog(conf) != 0)
255263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
256*61ba55bcSBaptiste Daroussin 			if (textbox_draw(&d, &st) != 0)
257263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
258c76f0793SBaptiste Daroussin 			break;
259*61ba55bcSBaptiste Daroussin 		case KEY_RESIZE:
260*61ba55bcSBaptiste Daroussin 			if (textbox_draw(&d, &st) != 0)
261*61ba55bcSBaptiste Daroussin 				return (BSDDIALOG_ERROR);
262*61ba55bcSBaptiste Daroussin 			break;
263c76f0793SBaptiste Daroussin 		}
264c76f0793SBaptiste Daroussin 	}
265c76f0793SBaptiste Daroussin 
266*61ba55bcSBaptiste Daroussin 	delwin(st.pad);
267*61ba55bcSBaptiste Daroussin 	end_dialog(&d);
268c76f0793SBaptiste Daroussin 
269b319d934SAlfonso S. Siciliano 	return (retval);
270c76f0793SBaptiste Daroussin }
271