xref: /freebsd/contrib/bsddialog/lib/formbox.c (revision 52d973f52c07b94909a6487be373c269988dc151)
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 #include <stdlib.h>
29 
30 #ifdef PORTNCURSES
31 #include <ncurses/curses.h>
32 #include <ncurses/form.h>
33 #else
34 #include <curses.h>
35 #include <form.h>
36 #endif
37 
38 #include "bsddialog.h"
39 #include "lib_util.h"
40 #include "bsddialog_theme.h"
41 
42 /* "Form": inputbox - passwordbox - form - passwordform - mixedform */
43 
44 extern struct bsddialog_theme t;
45 
46 int bsddialog_inputmenu(struct bsddialog_conf conf, char* text, int rows, int cols)
47 {
48 	text = "Inputbox unimplemented";
49 	bsddialog_msgbox(conf, text, rows, cols);
50 	RETURN_ERROR(text);
51 }
52 
53 #define ITEMHIDDEN 0x1
54 #define ISITEMHIDDEN(item) (item.itemflags & 0x1)
55 #define ITEMREADONLY 0x2
56 #define ISITEMREADONLY(item) (item.itemflags & 0x2)
57 struct formitem {
58 	char *label;
59 	unsigned int ylabel;
60 	unsigned int xlabel;
61 	char *item;
62 	unsigned int yitem;
63 	unsigned int xitem;
64 	int itemlen;
65 	unsigned int inputlen;
66 	unsigned int itemflags;
67 };
68 
69 static int
70 mixedform_handler(WINDOW *widget, int y, int cols, struct buttons bs,
71     bool shortkey, WINDOW *entry, FORM *form, FIELD **field, int nitems
72     /*struct formitem *items*/)
73 {
74 	bool loop, buttupdate, inentry = true;
75 	int input, output;
76 
77 	curs_set(2);
78 	pos_form_cursor(form);
79 	loop = buttupdate = true;
80 	bs.curr = -1;
81 	while(loop) {
82 		if (buttupdate) {
83 			draw_buttons(widget, y, cols, bs, shortkey);
84 			wrefresh(widget);
85 			buttupdate = false;
86 		}
87 		wrefresh(entry);
88 		input = getch();
89 		switch(input) {
90 		case 10: // Enter
91 			if (inentry)
92 				break;
93 			output = bs.value[bs.curr]; // values -> buttvalues
94 			form_driver(form, REQ_NEXT_FIELD);
95 			form_driver(form, REQ_PREV_FIELD);
96 			/* add a struct for forms */
97 			/*for (i=0; i<nitems; i++) {
98 				bufp = field_buffer(field[i], 0);
99 				dprintf(fd, "\n+%s", bufp);
100 				bufp = field_buffer(field[i], 1);
101 				dprintf(fd, "-%s+", bufp);
102 			}*/
103 			loop = false;
104 			break;
105 		case 27: /* Esc */
106 			output = BSDDIALOG_ESC;
107 			loop = false;
108 			break;
109 		case '\t': // TAB
110 			if (inentry) {
111 				bs.curr = 0;
112 				inentry = false;
113 				curs_set(0);
114 			} else {
115 				bs.curr++;
116 				inentry = bs.curr >= (int) bs.nbuttons ? true : false;
117 				if (inentry) {
118 					curs_set(2);
119 					pos_form_cursor(form);
120 				}
121 			}
122 			buttupdate = true;
123 			break;
124 		case KEY_LEFT:
125 			if (inentry) {
126 				form_driver(form, REQ_PREV_CHAR);
127 			} else {
128 				if (bs.curr > 0) {
129 					bs.curr--;
130 					buttupdate = true;
131 				}
132 			}
133 			break;
134 		case KEY_RIGHT:
135 			if (inentry) {
136 				form_driver(form, REQ_NEXT_CHAR);
137 			} else {
138 				if (bs.curr < (int) bs.nbuttons - 1) {
139 					bs.curr++;
140 					buttupdate = true;
141 				}
142 			}
143 			break;
144 		case KEY_UP:
145 			if (nitems < 2)
146 				break;
147 			set_field_fore(current_field(form), t.fieldcolor);
148 			set_field_back(current_field(form), t.fieldcolor);
149 			form_driver(form, REQ_PREV_FIELD);
150 			form_driver(form, REQ_END_LINE);
151 			set_field_fore(current_field(form), t.currfieldcolor);
152 			set_field_back(current_field(form), t.currfieldcolor);
153 			break;
154 		case KEY_DOWN:
155 			if (nitems < 2)
156 				break;
157 			set_field_fore(current_field(form), t.fieldcolor);
158 			set_field_back(current_field(form), t.fieldcolor);
159 			form_driver(form, REQ_NEXT_FIELD);
160 			form_driver(form, REQ_END_LINE);
161 			set_field_fore(current_field(form), t.currfieldcolor);
162 			set_field_back(current_field(form), t.currfieldcolor);
163 			break;
164 		case KEY_BACKSPACE:
165 			form_driver(form, REQ_DEL_PREV);
166 			break;
167 		case KEY_DC:
168 			form_driver(form, REQ_DEL_CHAR);
169 			break;
170 		default:
171 			if (inentry) {
172 				form_driver(form, input);
173 			}
174 			break;
175 		}
176 	}
177 
178 	curs_set(0);
179 
180 	return output;
181 }
182 
183 static int
184 do_mixedform(struct bsddialog_conf conf, char* text, int rows, int cols,
185     int formheight, int nitems, struct formitem *items)
186 {
187 	WINDOW *widget, *entry, *shadow;
188 	int i, output, color, y, x;
189 	FIELD **field;
190 	FORM *form;
191 	struct buttons bs;
192 
193 	if (new_widget(conf, &widget, &y, &x, text, &rows, &cols, &shadow,
194 	    true) <0)
195 		return -1;
196 
197 	entry = new_boxed_window(conf, y + rows - 3 - formheight -2, x +1,
198 	    formheight+2, cols-2, LOWERED);
199 
200 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
201 	    BUTTONLABEL(cancel_label), BUTTONLABEL(help_label));
202 
203 	field = calloc(nitems + 1, sizeof(FIELD*));
204 	for (i=0; i < nitems; i++) {
205 		field[i] = new_field(1, items[i].itemlen, items[i].yitem-1, items[i].xitem-1, 0, 1);
206 		field_opts_off(field[i], O_STATIC);
207 		set_max_field(field[i], items[i].inputlen);
208 		set_field_buffer(field[i], 0, items[i].item);
209 		set_field_buffer(field[i], 1, items[i].item);
210 		field_opts_off(field[i], O_AUTOSKIP);
211 		field_opts_off(field[i], O_BLANK);
212 		//field_opts_off(field[i], O_BS_OVERLOAD);
213 
214 		if (ISITEMHIDDEN(items[i]))
215 			field_opts_off(field[i], O_PUBLIC);
216 
217 		if (ISITEMREADONLY(items[i])) {
218 			field_opts_off(field[i], O_EDIT);
219 			field_opts_off(field[i], O_ACTIVE);
220 			color = t.fieldreadonlycolor;
221 		} else {
222 			color = i == 0 ? t.currfieldcolor : t.fieldcolor;
223 		}
224 		set_field_fore(field[i], color);
225 		set_field_back(field[i], color);
226 	}
227 	field[i] = NULL;
228 
229 	if (nitems == 1) {// inputbox or passwordbox
230 		set_field_fore(field[0], t.widgetcolor);
231 		set_field_back(field[0], t.widgetcolor);
232 	}
233 
234 	form = new_form(field);
235 	set_form_win(form, entry);
236 	set_form_sub(form, derwin(entry, nitems, cols-4, 1, 1));
237 	post_form(form);
238 
239 	for (i=0; i < nitems; i++)
240 		mvwaddstr(entry, items[i].ylabel, items[i].xlabel, items[i].label);
241 
242 	wrefresh(entry);
243 
244 	output = mixedform_handler(widget, rows-2, cols, bs, true, entry, form,
245 	    field, nitems /*,items*/);
246 
247 	unpost_form(form);
248 	free_form(form);
249 	for (i=0; i < nitems; i++)
250 		free_field(field[i]);
251 	free(field);
252 
253 	delwin(entry);
254 	end_widget(conf, widget, rows, cols, shadow);
255 
256 	return output;
257 }
258 
259 int bsddialog_inputbox(struct bsddialog_conf conf, char* text, int rows, int cols)
260 {
261 	int output;
262 	struct formitem item;
263 
264 	item.label	= "";
265 	item.ylabel	= 0;
266 	item.xlabel	= 0;
267 	item.item	= ""; // TODO add argv
268 	item.yitem	= 1;
269 	item.xitem	= 1;
270 	item.itemlen	= cols-4;
271 	item.inputlen	= 2048; // todo conf.sizeinput
272 	item.itemflags	= 0;
273 
274 	output = do_mixedform(conf, text, rows, cols, 1, 1, &item);
275 
276 	return output;
277 }
278 
279 int bsddialog_passwordbox(struct bsddialog_conf conf, char* text, int rows, int cols)
280 {
281 	int output;
282 	struct formitem item;
283 
284 	item.label	= "";
285 	item.ylabel	= 0;
286 	item.xlabel	= 0;
287 	item.item	= ""; // TODO add argv
288 	item.yitem	= 1;
289 	item.xitem	= 1;
290 	item.itemlen	= cols-4;
291 	item.inputlen	= 2048; // todo conf.sizeinput
292 	item.itemflags	= ITEMHIDDEN;
293 
294 	output = do_mixedform(conf, text, rows, cols, 1, 1, &item);
295 
296 	return output;
297 }
298 
299 int
300 bsddialog_mixedform(struct bsddialog_conf conf, char* text, int rows, int cols,
301     int formheight, int argc, char **argv)
302 {
303 	int i, output, nitems;
304 	struct formitem items[128];
305 
306 	if ((argc % 9) != 0)
307 		return (-1);
308 
309 	nitems = argc / 9;
310 	for (i=0; i<nitems; i++) {
311 		items[i].label	   = argv[9*i];
312 		items[i].ylabel	   = atoi(argv[9*i+1]);
313 		items[i].xlabel	   = atoi(argv[9*i+2]);
314 		items[i].item	   = argv[9*i+3];
315 		items[i].yitem	   = atoi(argv[9*i+4]);
316 		items[i].xitem	   = atoi(argv[9*i+5]);
317 		items[i].itemlen   = atoi(argv[9*i+6]);
318 		items[i].inputlen  = atoi(argv[9*i+7]);
319 		items[i].itemflags = atoi(argv[9*i+8]);
320 	}
321 
322 	output = do_mixedform(conf, text, rows, cols, formheight, nitems, items);
323 
324 	return output;
325 }
326 
327 int
328 bsddialog_form(struct bsddialog_conf conf, char* text, int rows, int cols,
329     int formheight, int argc, char **argv)
330 {
331 	int i, output, nitems, itemlen, inputlen;
332 	unsigned int flags = 0;
333 	struct formitem items[128];
334 
335 	if ((argc % 8) != 0)
336 		return (-1);
337 
338 	nitems = argc / 8;
339 	for (i=0; i<nitems; i++) {
340 		items[i].label	   = argv[8*i];
341 		items[i].ylabel	   = atoi(argv[8*i+1]);
342 		items[i].xlabel	   = atoi(argv[8*i+2]);
343 		items[i].item	   = argv[8*i+3];
344 		items[i].yitem	   = atoi(argv[8*i+4]);
345 		items[i].xitem	   = atoi(argv[8*i+5]);
346 
347 		itemlen = atoi(argv[8*i+6]);
348 		items[i].itemlen   = abs(itemlen);
349 
350 		inputlen = atoi(argv[8*i+7]);
351 		items[i].inputlen = inputlen == 0 ? abs(itemlen) : inputlen;
352 
353 		flags = flags | (itemlen < 0 ? ITEMREADONLY : 0);
354 		items[i].itemflags = flags;
355 	}
356 
357 	output = do_mixedform(conf, text, rows, cols, formheight, nitems, items);
358 
359 	return output;
360 }
361 
362 int
363 bsddialog_passwordform(struct bsddialog_conf conf, char* text, int rows, int cols,
364     int formheight, int argc, char **argv)
365 {
366 	int i, output, nitems, itemlen, inputlen;
367 	unsigned int flags = ITEMHIDDEN;
368 	struct formitem items[128];
369 
370 	if ((argc % 8) != 0)
371 		return (-1);
372 
373 	nitems = argc / 8;
374 	for (i=0; i<nitems; i++) {
375 		items[i].label	   = argv[8*i];
376 		items[i].ylabel	   = atoi(argv[8*i+1]);
377 		items[i].xlabel	   = atoi(argv[8*i+2]);
378 		items[i].item	   = argv[8*i+3];
379 		items[i].yitem	   = atoi(argv[8*i+4]);
380 		items[i].xitem	   = atoi(argv[8*i+5]);
381 
382 		itemlen = atoi(argv[8*i+6]);
383 		items[i].itemlen   = abs(itemlen);
384 
385 		inputlen = atoi(argv[8*i+7]);
386 		items[i].inputlen = inputlen == 0 ? abs(itemlen) : inputlen;
387 
388 		flags = flags | (itemlen < 0 ? ITEMREADONLY : 0);
389 		items[i].itemflags = flags;
390 	}
391 
392 	output = do_mixedform(conf, text, rows, cols, formheight, nitems, items);
393 
394 	return output;
395 }
396 
397