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