xref: /freebsd/contrib/bsddialog/lib/textbox.c (revision 29fc4075e69fd27de0cded313ac6000165d99f8b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 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 #include <sys/param.h>
29 
30 #include <curses.h>
31 #include <string.h>
32 
33 #include "bsddialog.h"
34 #include "bsddialog_theme.h"
35 #include "lib_util.h"
36 
37 static void
38 textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
39     int *w, int hpad, int wpad, struct buttons bs)
40 {
41 	if (cols == BSDDIALOG_AUTOSIZE)
42 		*w = widget_min_width(conf, 0, wpad, &bs);
43 
44 	if (rows == BSDDIALOG_AUTOSIZE)
45 		*h = widget_min_height(conf, 0, hpad, true);
46 }
47 
48 static int
49 textbox_checksize(int rows, int cols, int hpad, struct buttons bs)
50 {
51 	int mincols;
52 
53 	mincols = VBORDERS + bs.sizebutton;
54 
55 	if (cols < mincols)
56 		RETURN_ERROR("Few cols for the textbox");
57 
58 	if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0))
59 		RETURN_ERROR("Few rows for the textbox");
60 
61 	return (0);
62 }
63 
64 /* API */
65 int
66 bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows,
67     int cols)
68 {
69 	bool loop;
70 	int i, output, input;
71 	int y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows;
72 	char buf[BUFSIZ];
73 	FILE *fp;
74 	struct buttons bs;
75 	WINDOW *shadow, *widget, *pad;
76 
77 	if ((fp = fopen(file, "r")) == NULL)
78 		RETURN_ERROR("Cannot open file");
79 
80 	hpad = 1;
81 	wpad = 1;
82 	pad = newpad(hpad, wpad);
83 	wbkgd(pad, t.dialog.color);
84 	i = 0;
85 	while (fgets(buf, BUFSIZ, fp) != NULL) {
86 		if ((int) strlen(buf) > wpad) {
87 			wpad = strlen(buf);
88 			wresize(pad, hpad, wpad);
89 		}
90 		if (i > hpad-1) {
91 			hpad++;
92 			wresize(pad, hpad, wpad);
93 		}
94 		mvwaddstr(pad, i, 0, buf);
95 		i++;
96 	}
97 	fclose(fp);
98 
99 	bs.nbuttons = 1;
100 	bs.label[0] = "EXIT";
101 	if (conf->button.ok_label != NULL)
102 		bs.label[0] = conf->button.ok_label;
103 	bs.value[0] = BSDDIALOG_OK;
104 	bs.curr = 0;
105 	bs.sizebutton = strlen(bs.label[0]) + 2;
106 
107 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
108 		return (BSDDIALOG_ERROR);
109 	textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, bs);
110 	if (textbox_checksize(h, w, hpad, bs) != 0)
111 		return (BSDDIALOG_ERROR);
112 	if (set_widget_position(conf, &y, &x, h, w) != 0)
113 		return (BSDDIALOG_ERROR);
114 
115 	if (new_dialog(conf, &shadow, &widget, y, x, h, w, NULL, NULL, &bs,
116 	    true) != 0)
117 		return (BSDDIALOG_ERROR);
118 
119 	ys = y + 1;
120 	xs = x + 1;
121 	ye = ys + h - 5;
122 	xe = xs + w - 3;
123 	ypad = xpad = 0;
124 	printrows = h-4;
125 	loop = true;
126 	while (loop) {
127 		wnoutrefresh(widget);
128 		pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe);
129 		doupdate();
130 		input = getch();
131 		switch(input) {
132 		case KEY_ENTER:
133 		case 10: /* Enter */
134 			output = BSDDIALOG_OK;
135 			loop = false;
136 			break;
137 		case 27: /* Esc */
138 			if (conf->key.enable_esc) {
139 				output = BSDDIALOG_ESC;
140 				loop = false;
141 			}
142 			break;
143 		case KEY_HOME:
144 			ypad = 0;
145 			break;
146 		case KEY_END:
147 			ypad = hpad - printrows;
148 			ypad = ypad < 0 ? 0 : ypad;
149 			break;
150 		case KEY_PPAGE:
151 			ypad -= printrows;
152 			ypad = ypad < 0 ? 0 : ypad;
153 			break;
154 		case KEY_NPAGE:
155 			ypad += printrows;
156 			if (ypad + printrows > hpad)
157 				ypad = hpad - printrows;
158 			break;
159 		case '0':
160 			xpad = 0;
161 		case KEY_LEFT:
162 		case 'h':
163 			xpad = xpad > 0 ? xpad - 1 : 0;
164 			break;
165 		case KEY_RIGHT:
166 		case 'l':
167 			xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad;
168 			break;
169 		case KEY_UP:
170 		case 'k':
171 			ypad = ypad > 0 ? ypad - 1 : 0;
172 			break;
173 		case KEY_DOWN:
174 		case'j':
175 			ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad;
176 			break;
177 		case KEY_F(1):
178 			if (conf->key.f1_file == NULL &&
179 			    conf->key.f1_message == NULL)
180 				break;
181 			if (f1help(conf) != 0)
182 				return (BSDDIALOG_ERROR);
183 			/* No break, screen size can change */
184 		case KEY_RESIZE:
185 			/* Important for decreasing screen */
186 			hide_widget(y, x, h, w, conf->shadow);
187 			refresh();
188 
189 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
190 				return (BSDDIALOG_ERROR);
191 			textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad,
192 			    bs);
193 			if (textbox_checksize(h, w, hpad, bs) != 0)
194 				return (BSDDIALOG_ERROR);
195 			if (set_widget_position(conf, &y, &x, h, w) != 0)
196 				return (BSDDIALOG_ERROR);
197 
198 			ys = y + 1;
199 			xs = x + 1;
200 			ye = ys + h - 5;
201 			xe = xs + w - 3;
202 			ypad = xpad = 0;
203 			printrows = h - 4;
204 
205 			if (update_dialog(conf, shadow, widget, y, x, h, w,
206 			    NULL, NULL, &bs, true) != 0)
207 				return (BSDDIALOG_ERROR);
208 
209 			/* Important to fix grey lines expanding screen */
210 			refresh();
211 			break;
212 		default:
213 			if (shortcut_buttons(input, &bs)) {
214 				output = bs.value[bs.curr];
215 				loop = false;
216 			}
217 		}
218 	}
219 
220 	end_dialog(conf, shadow, widget, pad);
221 
222 	return (output);
223 }