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