xref: /freebsd-src/contrib/bsddialog/lib/textbox.c (revision c76f07938c44264c7ebd400c23f218e561960d23)
1*c76f0793SBaptiste Daroussin /*-
2*c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3*c76f0793SBaptiste Daroussin  *
4*c76f0793SBaptiste Daroussin  * Copyright (c) 2021 Alfonso Sabato Siciliano
5*c76f0793SBaptiste Daroussin  *
6*c76f0793SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7*c76f0793SBaptiste Daroussin  * modification, are permitted provided that the following conditions
8*c76f0793SBaptiste Daroussin  * are met:
9*c76f0793SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10*c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11*c76f0793SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12*c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13*c76f0793SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14*c76f0793SBaptiste Daroussin  *
15*c76f0793SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*c76f0793SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*c76f0793SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*c76f0793SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*c76f0793SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*c76f0793SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*c76f0793SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*c76f0793SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*c76f0793SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*c76f0793SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*c76f0793SBaptiste Daroussin  * SUCH DAMAGE.
26*c76f0793SBaptiste Daroussin  */
27*c76f0793SBaptiste Daroussin 
28*c76f0793SBaptiste Daroussin 
29*c76f0793SBaptiste Daroussin #include <sys/param.h>
30*c76f0793SBaptiste Daroussin 
31*c76f0793SBaptiste Daroussin #include <string.h>
32*c76f0793SBaptiste Daroussin 
33*c76f0793SBaptiste Daroussin #ifdef PORTNCURSES
34*c76f0793SBaptiste Daroussin #include <ncurses/curses.h>
35*c76f0793SBaptiste Daroussin #else
36*c76f0793SBaptiste Daroussin #include <curses.h>
37*c76f0793SBaptiste Daroussin #endif
38*c76f0793SBaptiste Daroussin 
39*c76f0793SBaptiste Daroussin #include "bsddialog.h"
40*c76f0793SBaptiste Daroussin #include "lib_util.h"
41*c76f0793SBaptiste Daroussin #include "bsddialog_theme.h"
42*c76f0793SBaptiste Daroussin 
43*c76f0793SBaptiste Daroussin /* "Text": tailbox - tailboxbg - textbox */
44*c76f0793SBaptiste Daroussin 
45*c76f0793SBaptiste Daroussin #define BUTTON_TEXTBOX "HELP"
46*c76f0793SBaptiste Daroussin 
47*c76f0793SBaptiste Daroussin extern struct bsddialog_theme t;
48*c76f0793SBaptiste Daroussin 
49*c76f0793SBaptiste Daroussin enum textmode { TAILMODE, TAILBGMODE, TEXTMODE};
50*c76f0793SBaptiste Daroussin 
51*c76f0793SBaptiste Daroussin static void
52*c76f0793SBaptiste Daroussin textbox_autosize(struct bsddialog_conf conf, int rows, int cols, int *h, int *w,
53*c76f0793SBaptiste Daroussin     int hpad, int wpad)
54*c76f0793SBaptiste Daroussin {
55*c76f0793SBaptiste Daroussin 
56*c76f0793SBaptiste Daroussin 	if (cols == BSDDIALOG_AUTOSIZE) {
57*c76f0793SBaptiste Daroussin 		*w = VBORDERS;
58*c76f0793SBaptiste Daroussin 		/* buttons size */
59*c76f0793SBaptiste Daroussin 		*w += strlen(BUTTON_TEXTBOX) + 2 /* text delims*/;
60*c76f0793SBaptiste Daroussin 		/* text size */
61*c76f0793SBaptiste Daroussin 		*w = MAX(*w, wpad + VBORDERS);
62*c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
63*c76f0793SBaptiste Daroussin 		*w = MIN(*w, widget_max_width(conf)-1); /* again -1, fix util.c */
64*c76f0793SBaptiste Daroussin 	}
65*c76f0793SBaptiste Daroussin 
66*c76f0793SBaptiste Daroussin 	if (rows == BSDDIALOG_AUTOSIZE) {
67*c76f0793SBaptiste Daroussin 		*h = hpad + 4; /* HBORDERS + button border */
68*c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
69*c76f0793SBaptiste Daroussin 		*h = MIN(*h, widget_max_height(conf));
70*c76f0793SBaptiste Daroussin 	}
71*c76f0793SBaptiste Daroussin }
72*c76f0793SBaptiste Daroussin 
73*c76f0793SBaptiste Daroussin static int textbox_checksize(int rows, int cols, int hpad, int wpad)
74*c76f0793SBaptiste Daroussin {
75*c76f0793SBaptiste Daroussin 	int mincols;
76*c76f0793SBaptiste Daroussin 
77*c76f0793SBaptiste Daroussin 	mincols = VBORDERS + strlen(BUTTON_TEXTBOX) + 2 /* text delims */;
78*c76f0793SBaptiste Daroussin 
79*c76f0793SBaptiste Daroussin 	if (cols < mincols)
80*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Few cols for the textbox");
81*c76f0793SBaptiste Daroussin 
82*c76f0793SBaptiste Daroussin 	if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0))
83*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Few rows for the textbox");
84*c76f0793SBaptiste Daroussin 
85*c76f0793SBaptiste Daroussin 	return 0;
86*c76f0793SBaptiste Daroussin }
87*c76f0793SBaptiste Daroussin 
88*c76f0793SBaptiste Daroussin static int
89*c76f0793SBaptiste Daroussin do_textbox(enum textmode mode, struct bsddialog_conf conf, char* path, int rows, int cols)
90*c76f0793SBaptiste Daroussin {
91*c76f0793SBaptiste Daroussin 	WINDOW *widget, *pad, *shadow;
92*c76f0793SBaptiste Daroussin 	int i, input, y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows;
93*c76f0793SBaptiste Daroussin 	char buf[BUFSIZ], *exitbutt;
94*c76f0793SBaptiste Daroussin 	FILE *fp;
95*c76f0793SBaptiste Daroussin 	bool loop;
96*c76f0793SBaptiste Daroussin 	int output;
97*c76f0793SBaptiste Daroussin 
98*c76f0793SBaptiste Daroussin 	if (mode == TAILMODE || mode == TAILBGMODE) {
99*c76f0793SBaptiste Daroussin 		bsddialog_msgbox(conf, "Tailbox and Tailboxbg unimplemented", rows, cols);
100*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Tailbox and Tailboxbg unimplemented");
101*c76f0793SBaptiste Daroussin 	}
102*c76f0793SBaptiste Daroussin 
103*c76f0793SBaptiste Daroussin 	if ((fp = fopen(path, "r")) == NULL)
104*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Cannot open file");
105*c76f0793SBaptiste Daroussin 	/*if (mode == TAILMODE) {
106*c76f0793SBaptiste Daroussin 		fseek (fp, 0, SEEK_END);
107*c76f0793SBaptiste Daroussin 		i = nlines = 0;
108*c76f0793SBaptiste Daroussin 		while (i < hpad) {
109*c76f0793SBaptiste Daroussin 			line = ;
110*c76f0793SBaptiste Daroussin 		}
111*c76f0793SBaptiste Daroussin 		for (i=hpad-1; i--; i>=0) {
112*c76f0793SBaptiste Daroussin 		}
113*c76f0793SBaptiste Daroussin 	}*/
114*c76f0793SBaptiste Daroussin 	hpad = 1;
115*c76f0793SBaptiste Daroussin 	wpad = 1;
116*c76f0793SBaptiste Daroussin 	pad = newpad(hpad, wpad);
117*c76f0793SBaptiste Daroussin 	wbkgd(pad, t.widgetcolor);
118*c76f0793SBaptiste Daroussin 	i = 0;
119*c76f0793SBaptiste Daroussin 	while(fgets(buf, BUFSIZ, fp) != NULL) {
120*c76f0793SBaptiste Daroussin 		if ((int) strlen(buf) > wpad) {
121*c76f0793SBaptiste Daroussin 			wpad = strlen(buf);
122*c76f0793SBaptiste Daroussin 			wresize(pad, hpad, wpad);
123*c76f0793SBaptiste Daroussin 		}
124*c76f0793SBaptiste Daroussin 		if (i > hpad-1) {
125*c76f0793SBaptiste Daroussin 			hpad++;
126*c76f0793SBaptiste Daroussin 			wresize(pad, hpad, wpad);
127*c76f0793SBaptiste Daroussin 		}
128*c76f0793SBaptiste Daroussin 		mvwaddstr(pad, i, 0, buf);
129*c76f0793SBaptiste Daroussin 		i++;
130*c76f0793SBaptiste Daroussin 	}
131*c76f0793SBaptiste Daroussin 	fclose(fp);
132*c76f0793SBaptiste Daroussin 
133*c76f0793SBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
134*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
135*c76f0793SBaptiste Daroussin 	textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
136*c76f0793SBaptiste Daroussin 	if (textbox_checksize(h, w, hpad, wpad) != 0)
137*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
138*c76f0793SBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
139*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
140*c76f0793SBaptiste Daroussin 
141*c76f0793SBaptiste Daroussin 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
142*c76f0793SBaptiste Daroussin 	    NULL, NULL, NULL, true) != 0)
143*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
144*c76f0793SBaptiste Daroussin 
145*c76f0793SBaptiste Daroussin 	exitbutt = conf.button.exit_label == NULL ? BUTTON_TEXTBOX : conf.button.exit_label;
146*c76f0793SBaptiste Daroussin 	draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, strlen(exitbutt)+2,
147*c76f0793SBaptiste Daroussin 	    exitbutt, true, true);
148*c76f0793SBaptiste Daroussin 
149*c76f0793SBaptiste Daroussin 	wrefresh(widget);
150*c76f0793SBaptiste Daroussin 
151*c76f0793SBaptiste Daroussin 	ys = y + 1;
152*c76f0793SBaptiste Daroussin 	xs = x + 1;
153*c76f0793SBaptiste Daroussin 	ye = ys + h - 5;
154*c76f0793SBaptiste Daroussin 	xe = xs + w - 3;
155*c76f0793SBaptiste Daroussin 	ypad = xpad = 0;
156*c76f0793SBaptiste Daroussin 	printrows = h-4;
157*c76f0793SBaptiste Daroussin 	loop = true;
158*c76f0793SBaptiste Daroussin 	while(loop) {
159*c76f0793SBaptiste Daroussin 		prefresh(pad, ypad, xpad, ys, xs, ye, xe);
160*c76f0793SBaptiste Daroussin 		input = getch();
161*c76f0793SBaptiste Daroussin 		switch(input) {
162*c76f0793SBaptiste Daroussin 		case KEY_ENTER:
163*c76f0793SBaptiste Daroussin 		case 10: /* Enter */
164*c76f0793SBaptiste Daroussin 			output = BSDDIALOG_YESOK;
165*c76f0793SBaptiste Daroussin 			loop = false;
166*c76f0793SBaptiste Daroussin 			break;
167*c76f0793SBaptiste Daroussin 		case 27: /* Esc */
168*c76f0793SBaptiste Daroussin 			output = BSDDIALOG_ESC;
169*c76f0793SBaptiste Daroussin 			loop = false;
170*c76f0793SBaptiste Daroussin 			break;
171*c76f0793SBaptiste Daroussin 		case KEY_HOME:
172*c76f0793SBaptiste Daroussin 			ypad = 0;
173*c76f0793SBaptiste Daroussin 			break;
174*c76f0793SBaptiste Daroussin 		case KEY_END:
175*c76f0793SBaptiste Daroussin 			ypad = hpad - printrows;
176*c76f0793SBaptiste Daroussin 			ypad = ypad < 0 ? 0 : ypad;
177*c76f0793SBaptiste Daroussin 			break;
178*c76f0793SBaptiste Daroussin 		case KEY_PPAGE:
179*c76f0793SBaptiste Daroussin 			ypad -= printrows;
180*c76f0793SBaptiste Daroussin 			ypad = ypad < 0 ? 0 : ypad;
181*c76f0793SBaptiste Daroussin 			break;
182*c76f0793SBaptiste Daroussin 		case KEY_NPAGE:
183*c76f0793SBaptiste Daroussin 			ypad += printrows;
184*c76f0793SBaptiste Daroussin 			ypad = ypad + printrows > hpad ? hpad - printrows : ypad;
185*c76f0793SBaptiste Daroussin 			break;
186*c76f0793SBaptiste Daroussin 		case '0':
187*c76f0793SBaptiste Daroussin 			xpad = 0;
188*c76f0793SBaptiste Daroussin 		case KEY_LEFT:
189*c76f0793SBaptiste Daroussin 		case 'h':
190*c76f0793SBaptiste Daroussin 			xpad = xpad > 0 ? xpad - 1 : 0;
191*c76f0793SBaptiste Daroussin 			break;
192*c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
193*c76f0793SBaptiste Daroussin 		case 'l':
194*c76f0793SBaptiste Daroussin 			xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad;
195*c76f0793SBaptiste Daroussin 			break;
196*c76f0793SBaptiste Daroussin 		case KEY_UP:
197*c76f0793SBaptiste Daroussin 		case 'k':
198*c76f0793SBaptiste Daroussin 			ypad = ypad > 0 ? ypad - 1 : 0;
199*c76f0793SBaptiste Daroussin 			break;
200*c76f0793SBaptiste Daroussin 		case KEY_DOWN:
201*c76f0793SBaptiste Daroussin 		case'j':
202*c76f0793SBaptiste Daroussin 			ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad;
203*c76f0793SBaptiste Daroussin 			break;
204*c76f0793SBaptiste Daroussin 		case KEY_F(1):
205*c76f0793SBaptiste Daroussin 			if (conf.hfile == NULL)
206*c76f0793SBaptiste Daroussin 				break;
207*c76f0793SBaptiste Daroussin 			if (f1help(conf) != 0)
208*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
209*c76f0793SBaptiste Daroussin 			/* No break! the terminal size can change */
210*c76f0793SBaptiste Daroussin 		case KEY_RESIZE:
211*c76f0793SBaptiste Daroussin 			hide_widget(y, x, h, w,conf.shadow);
212*c76f0793SBaptiste Daroussin 
213*c76f0793SBaptiste Daroussin 			/*
214*c76f0793SBaptiste Daroussin 			 * Unnecessary, but, when the columns decrease the
215*c76f0793SBaptiste Daroussin 			 * following "refresh" seem not work
216*c76f0793SBaptiste Daroussin 			 */
217*c76f0793SBaptiste Daroussin 			refresh();
218*c76f0793SBaptiste Daroussin 
219*c76f0793SBaptiste Daroussin 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
220*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
221*c76f0793SBaptiste Daroussin 			textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
222*c76f0793SBaptiste Daroussin 			if (textbox_checksize(h, w, hpad, wpad) != 0)
223*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
224*c76f0793SBaptiste Daroussin 			if (set_widget_position(conf, &y, &x, h, w) != 0)
225*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
226*c76f0793SBaptiste Daroussin 
227*c76f0793SBaptiste Daroussin 			wclear(shadow);
228*c76f0793SBaptiste Daroussin 			mvwin(shadow, y + t.shadowrows, x + t.shadowcols);
229*c76f0793SBaptiste Daroussin 			wresize(shadow, h, w);
230*c76f0793SBaptiste Daroussin 
231*c76f0793SBaptiste Daroussin 			wclear(widget);
232*c76f0793SBaptiste Daroussin 			mvwin(widget, y, x);
233*c76f0793SBaptiste Daroussin 			wresize(widget, h, w);
234*c76f0793SBaptiste Daroussin 
235*c76f0793SBaptiste Daroussin 			ys = y + 1;
236*c76f0793SBaptiste Daroussin 			xs = x + 1;
237*c76f0793SBaptiste Daroussin 			ye = ys + h - 5;
238*c76f0793SBaptiste Daroussin 			xe = xs + w - 3;
239*c76f0793SBaptiste Daroussin 			ypad = xpad = 0;
240*c76f0793SBaptiste Daroussin 			printrows = h - 4;
241*c76f0793SBaptiste Daroussin 
242*c76f0793SBaptiste Daroussin 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
243*c76f0793SBaptiste Daroussin 			    RAISED, NULL, NULL, NULL, true) != 0)
244*c76f0793SBaptiste Daroussin 			return BSDDIALOG_ERROR;
245*c76f0793SBaptiste Daroussin 
246*c76f0793SBaptiste Daroussin 			draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2,
247*c76f0793SBaptiste Daroussin 			    strlen(exitbutt)+2, exitbutt, true, true);
248*c76f0793SBaptiste Daroussin 
249*c76f0793SBaptiste Daroussin 			wrefresh(widget); /* for button */
250*c76f0793SBaptiste Daroussin 
251*c76f0793SBaptiste Daroussin 			/* Important to fix grey lines expanding screen */
252*c76f0793SBaptiste Daroussin 			refresh();
253*c76f0793SBaptiste Daroussin 			break;
254*c76f0793SBaptiste Daroussin 		}
255*c76f0793SBaptiste Daroussin 	}
256*c76f0793SBaptiste Daroussin 
257*c76f0793SBaptiste Daroussin 	end_widget_withtextpad(conf, widget, h, w, pad, shadow);
258*c76f0793SBaptiste Daroussin 
259*c76f0793SBaptiste Daroussin 	return output;
260*c76f0793SBaptiste Daroussin }
261*c76f0793SBaptiste Daroussin 
262*c76f0793SBaptiste Daroussin int bsddialog_tailbox(struct bsddialog_conf conf, char* text, int rows, int cols)
263*c76f0793SBaptiste Daroussin {
264*c76f0793SBaptiste Daroussin 
265*c76f0793SBaptiste Daroussin 	return (do_textbox(TAILMODE, conf, text, rows, cols));
266*c76f0793SBaptiste Daroussin }
267*c76f0793SBaptiste Daroussin 
268*c76f0793SBaptiste Daroussin int bsddialog_tailboxbg(struct bsddialog_conf conf, char* text, int rows, int cols)
269*c76f0793SBaptiste Daroussin {
270*c76f0793SBaptiste Daroussin 
271*c76f0793SBaptiste Daroussin 	return (do_textbox(TAILBGMODE, conf, text, rows, cols));
272*c76f0793SBaptiste Daroussin }
273*c76f0793SBaptiste Daroussin 
274*c76f0793SBaptiste Daroussin 
275*c76f0793SBaptiste Daroussin int bsddialog_textbox(struct bsddialog_conf conf, char* text, int rows, int cols)
276*c76f0793SBaptiste Daroussin {
277*c76f0793SBaptiste Daroussin 
278*c76f0793SBaptiste Daroussin 	return (do_textbox(TEXTMODE, conf, text, rows, cols));
279*c76f0793SBaptiste Daroussin }
280*c76f0793SBaptiste Daroussin 
281