xref: /freebsd/contrib/bsddialog/lib/textbox.c (revision 7e1d3eefd410ca0fbae5a217422821244c3eeee4)
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 
29 #include <sys/param.h>
30 
31 #include <string.h>
32 
33 #ifdef PORTNCURSES
34 #include <ncurses/curses.h>
35 #else
36 #include <curses.h>
37 #endif
38 
39 #include "bsddialog.h"
40 #include "lib_util.h"
41 #include "bsddialog_theme.h"
42 
43 /* "Text": tailbox - tailboxbg - textbox */
44 
45 #define BUTTON_TEXTBOX "HELP"
46 
47 extern struct bsddialog_theme t;
48 
49 enum textmode { TAILMODE, TAILBGMODE, TEXTMODE};
50 
51 static void
52 textbox_autosize(struct bsddialog_conf conf, int rows, int cols, int *h, int *w,
53     int hpad, int wpad)
54 {
55 
56 	if (cols == BSDDIALOG_AUTOSIZE) {
57 		*w = VBORDERS;
58 		/* buttons size */
59 		*w += strlen(BUTTON_TEXTBOX) + 2 /* text delims*/;
60 		/* text size */
61 		*w = MAX(*w, wpad + VBORDERS);
62 		/* avoid terminal overflow */
63 		*w = MIN(*w, widget_max_width(conf)-1); /* again -1, fix util.c */
64 	}
65 
66 	if (rows == BSDDIALOG_AUTOSIZE) {
67 		*h = hpad + 4; /* HBORDERS + button border */
68 		/* avoid terminal overflow */
69 		*h = MIN(*h, widget_max_height(conf));
70 	}
71 }
72 
73 static int textbox_checksize(int rows, int cols, int hpad, int wpad)
74 {
75 	int mincols;
76 
77 	mincols = VBORDERS + strlen(BUTTON_TEXTBOX) + 2 /* text delims */;
78 
79 	if (cols < mincols)
80 		RETURN_ERROR("Few cols for the textbox");
81 
82 	if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0))
83 		RETURN_ERROR("Few rows for the textbox");
84 
85 	return 0;
86 }
87 
88 static int
89 do_textbox(enum textmode mode, struct bsddialog_conf conf, char* path, int rows, int cols)
90 {
91 	WINDOW *widget, *pad, *shadow;
92 	int i, input, y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows;
93 	char buf[BUFSIZ], *exitbutt;
94 	FILE *fp;
95 	bool loop;
96 	int output;
97 
98 	if (mode == TAILMODE || mode == TAILBGMODE) {
99 		bsddialog_msgbox(conf, "Tailbox and Tailboxbg unimplemented", rows, cols);
100 		RETURN_ERROR("Tailbox and Tailboxbg unimplemented");
101 	}
102 
103 	if ((fp = fopen(path, "r")) == NULL)
104 		RETURN_ERROR("Cannot open file");
105 	/*if (mode == TAILMODE) {
106 		fseek (fp, 0, SEEK_END);
107 		i = nlines = 0;
108 		while (i < hpad) {
109 			line = ;
110 		}
111 		for (i=hpad-1; i--; i>=0) {
112 		}
113 	}*/
114 	hpad = 1;
115 	wpad = 1;
116 	pad = newpad(hpad, wpad);
117 	wbkgd(pad, t.widgetcolor);
118 	i = 0;
119 	while(fgets(buf, BUFSIZ, fp) != NULL) {
120 		if ((int) strlen(buf) > wpad) {
121 			wpad = strlen(buf);
122 			wresize(pad, hpad, wpad);
123 		}
124 		if (i > hpad-1) {
125 			hpad++;
126 			wresize(pad, hpad, wpad);
127 		}
128 		mvwaddstr(pad, i, 0, buf);
129 		i++;
130 	}
131 	fclose(fp);
132 
133 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
134 		return BSDDIALOG_ERROR;
135 	textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
136 	if (textbox_checksize(h, w, hpad, wpad) != 0)
137 		return BSDDIALOG_ERROR;
138 	if (set_widget_position(conf, &y, &x, h, w) != 0)
139 		return BSDDIALOG_ERROR;
140 
141 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
142 	    NULL, NULL, NULL, true) != 0)
143 		return BSDDIALOG_ERROR;
144 
145 	exitbutt = conf.button.exit_label == NULL ? BUTTON_TEXTBOX : conf.button.exit_label;
146 	draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, strlen(exitbutt)+2,
147 	    exitbutt, true, true);
148 
149 	wrefresh(widget);
150 
151 	ys = y + 1;
152 	xs = x + 1;
153 	ye = ys + h - 5;
154 	xe = xs + w - 3;
155 	ypad = xpad = 0;
156 	printrows = h-4;
157 	loop = true;
158 	while(loop) {
159 		prefresh(pad, ypad, xpad, ys, xs, ye, xe);
160 		input = getch();
161 		switch(input) {
162 		case KEY_ENTER:
163 		case 10: /* Enter */
164 			output = BSDDIALOG_YESOK;
165 			loop = false;
166 			break;
167 		case 27: /* Esc */
168 			output = BSDDIALOG_ESC;
169 			loop = false;
170 			break;
171 		case KEY_HOME:
172 			ypad = 0;
173 			break;
174 		case KEY_END:
175 			ypad = hpad - printrows;
176 			ypad = ypad < 0 ? 0 : ypad;
177 			break;
178 		case KEY_PPAGE:
179 			ypad -= printrows;
180 			ypad = ypad < 0 ? 0 : ypad;
181 			break;
182 		case KEY_NPAGE:
183 			ypad += printrows;
184 			ypad = ypad + printrows > hpad ? hpad - printrows : ypad;
185 			break;
186 		case '0':
187 			xpad = 0;
188 		case KEY_LEFT:
189 		case 'h':
190 			xpad = xpad > 0 ? xpad - 1 : 0;
191 			break;
192 		case KEY_RIGHT:
193 		case 'l':
194 			xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad;
195 			break;
196 		case KEY_UP:
197 		case 'k':
198 			ypad = ypad > 0 ? ypad - 1 : 0;
199 			break;
200 		case KEY_DOWN:
201 		case'j':
202 			ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad;
203 			break;
204 		case KEY_F(1):
205 			if (conf.hfile == NULL)
206 				break;
207 			if (f1help(conf) != 0)
208 				return BSDDIALOG_ERROR;
209 			/* No break! the terminal size can change */
210 		case KEY_RESIZE:
211 			hide_widget(y, x, h, w,conf.shadow);
212 
213 			/*
214 			 * Unnecessary, but, when the columns decrease the
215 			 * following "refresh" seem not work
216 			 */
217 			refresh();
218 
219 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
220 				return BSDDIALOG_ERROR;
221 			textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
222 			if (textbox_checksize(h, w, hpad, wpad) != 0)
223 				return BSDDIALOG_ERROR;
224 			if (set_widget_position(conf, &y, &x, h, w) != 0)
225 				return BSDDIALOG_ERROR;
226 
227 			wclear(shadow);
228 			mvwin(shadow, y + t.shadowrows, x + t.shadowcols);
229 			wresize(shadow, h, w);
230 
231 			wclear(widget);
232 			mvwin(widget, y, x);
233 			wresize(widget, h, w);
234 
235 			ys = y + 1;
236 			xs = x + 1;
237 			ye = ys + h - 5;
238 			xe = xs + w - 3;
239 			ypad = xpad = 0;
240 			printrows = h - 4;
241 
242 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
243 			    RAISED, NULL, NULL, NULL, true) != 0)
244 			return BSDDIALOG_ERROR;
245 
246 			draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2,
247 			    strlen(exitbutt)+2, exitbutt, true, true);
248 
249 			wrefresh(widget); /* for button */
250 
251 			/* Important to fix grey lines expanding screen */
252 			refresh();
253 			break;
254 		}
255 	}
256 
257 	end_widget_withtextpad(conf, widget, h, w, pad, shadow);
258 
259 	return output;
260 }
261 
262 int bsddialog_tailbox(struct bsddialog_conf conf, char* text, int rows, int cols)
263 {
264 
265 	return (do_textbox(TAILMODE, conf, text, rows, cols));
266 }
267 
268 int bsddialog_tailboxbg(struct bsddialog_conf conf, char* text, int rows, int cols)
269 {
270 
271 	return (do_textbox(TAILBGMODE, conf, text, rows, cols));
272 }
273 
274 
275 int bsddialog_textbox(struct bsddialog_conf conf, char* text, int rows, int cols)
276 {
277 
278 	return (do_textbox(TEXTMODE, conf, text, rows, cols));
279 }
280 
281