xref: /freebsd-src/contrib/bsddialog/lib/textbox.c (revision d93b4d32034df7cd70e80b496e8fe8c1bc57c629)
1c76f0793SBaptiste Daroussin /*-
2c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3c76f0793SBaptiste Daroussin  *
4c76f0793SBaptiste Daroussin  * Copyright (c) 2021 Alfonso Sabato Siciliano
5c76f0793SBaptiste Daroussin  *
6c76f0793SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7c76f0793SBaptiste Daroussin  * modification, are permitted provided that the following conditions
8c76f0793SBaptiste Daroussin  * are met:
9c76f0793SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11c76f0793SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13c76f0793SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14c76f0793SBaptiste Daroussin  *
15c76f0793SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c76f0793SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c76f0793SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c76f0793SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c76f0793SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c76f0793SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c76f0793SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c76f0793SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c76f0793SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c76f0793SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c76f0793SBaptiste Daroussin  * SUCH DAMAGE.
26c76f0793SBaptiste Daroussin  */
27c76f0793SBaptiste Daroussin 
28c76f0793SBaptiste Daroussin 
29c76f0793SBaptiste Daroussin #include <sys/param.h>
30c76f0793SBaptiste Daroussin 
31c76f0793SBaptiste Daroussin #ifdef PORTNCURSES
328c4f4028SBaptiste Daroussin #include <ncurses/ncurses.h>
33c76f0793SBaptiste Daroussin #else
348c4f4028SBaptiste Daroussin #include <ncurses.h>
35c76f0793SBaptiste Daroussin #endif
36*d93b4d32SBaptiste Daroussin #include <string.h>
37c76f0793SBaptiste Daroussin 
38c76f0793SBaptiste Daroussin #include "bsddialog.h"
39c76f0793SBaptiste Daroussin #include "lib_util.h"
40c76f0793SBaptiste Daroussin #include "bsddialog_theme.h"
41c76f0793SBaptiste Daroussin 
42f499134dSBaptiste Daroussin /* "Text": textbox */
43c76f0793SBaptiste Daroussin 
448c4f4028SBaptiste Daroussin #define BUTTON_TEXTBOX "EXIT"
45c76f0793SBaptiste Daroussin 
46c76f0793SBaptiste Daroussin extern struct bsddialog_theme t;
47c76f0793SBaptiste Daroussin 
48c76f0793SBaptiste Daroussin static void
49f499134dSBaptiste Daroussin textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
50c76f0793SBaptiste Daroussin     int hpad, int wpad)
51c76f0793SBaptiste Daroussin {
52c76f0793SBaptiste Daroussin 
53c76f0793SBaptiste Daroussin 	if (cols == BSDDIALOG_AUTOSIZE) {
54c76f0793SBaptiste Daroussin 		*w = VBORDERS;
55c76f0793SBaptiste Daroussin 		/* buttons size */
56c76f0793SBaptiste Daroussin 		*w += strlen(BUTTON_TEXTBOX) + 2 /* text delims*/;
57c76f0793SBaptiste Daroussin 		/* text size */
58c76f0793SBaptiste Daroussin 		*w = MAX(*w, wpad + VBORDERS);
598c4f4028SBaptiste Daroussin 		/* conf.auto_minwidth */
608c4f4028SBaptiste Daroussin 		*w = MAX(*w, (int)conf->auto_minwidth);
61c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
62c76f0793SBaptiste Daroussin 		*w = MIN(*w, widget_max_width(conf)-1); /* again -1, fix util.c */
63c76f0793SBaptiste Daroussin 	}
64c76f0793SBaptiste Daroussin 
65c76f0793SBaptiste Daroussin 	if (rows == BSDDIALOG_AUTOSIZE) {
66c76f0793SBaptiste Daroussin 		*h = hpad + 4; /* HBORDERS + button border */
678c4f4028SBaptiste Daroussin 		/* conf.auto_minheight */
688c4f4028SBaptiste Daroussin 		*h = MAX(*h, (int)conf->auto_minheight);
69c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
70c76f0793SBaptiste Daroussin 		*h = MIN(*h, widget_max_height(conf));
71c76f0793SBaptiste Daroussin 	}
72c76f0793SBaptiste Daroussin }
73c76f0793SBaptiste Daroussin 
74f499134dSBaptiste Daroussin static int textbox_checksize(int rows, int cols, int hpad)
75c76f0793SBaptiste Daroussin {
76c76f0793SBaptiste Daroussin 	int mincols;
77c76f0793SBaptiste Daroussin 
78c76f0793SBaptiste Daroussin 	mincols = VBORDERS + strlen(BUTTON_TEXTBOX) + 2 /* text delims */;
79c76f0793SBaptiste Daroussin 
80c76f0793SBaptiste Daroussin 	if (cols < mincols)
81c76f0793SBaptiste Daroussin 		RETURN_ERROR("Few cols for the textbox");
82c76f0793SBaptiste Daroussin 
83c76f0793SBaptiste Daroussin 	if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0))
84c76f0793SBaptiste Daroussin 		RETURN_ERROR("Few rows for the textbox");
85c76f0793SBaptiste Daroussin 
86c76f0793SBaptiste Daroussin 	return 0;
87c76f0793SBaptiste Daroussin }
88c76f0793SBaptiste Daroussin 
89f499134dSBaptiste Daroussin int
90f499134dSBaptiste Daroussin bsddialog_textbox(struct bsddialog_conf *conf, char* file, int rows, int cols)
91c76f0793SBaptiste Daroussin {
92c76f0793SBaptiste Daroussin 	WINDOW *widget, *pad, *shadow;
93c76f0793SBaptiste Daroussin 	int i, input, y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows;
94c76f0793SBaptiste Daroussin 	char buf[BUFSIZ], *exitbutt;
95c76f0793SBaptiste Daroussin 	FILE *fp;
96c76f0793SBaptiste Daroussin 	bool loop;
97c76f0793SBaptiste Daroussin 	int output;
98c76f0793SBaptiste Daroussin 
99f499134dSBaptiste Daroussin 	if ((fp = fopen(file, "r")) == NULL)
100c76f0793SBaptiste Daroussin 		RETURN_ERROR("Cannot open file");
101f499134dSBaptiste Daroussin 
102c76f0793SBaptiste Daroussin 	hpad = 1;
103c76f0793SBaptiste Daroussin 	wpad = 1;
104c76f0793SBaptiste Daroussin 	pad = newpad(hpad, wpad);
1058c4f4028SBaptiste Daroussin 	wbkgd(pad, t.dialog.color);
106c76f0793SBaptiste Daroussin 	i = 0;
107c76f0793SBaptiste Daroussin 	while(fgets(buf, BUFSIZ, fp) != NULL) {
108c76f0793SBaptiste Daroussin 		if ((int) strlen(buf) > wpad) {
109c76f0793SBaptiste Daroussin 			wpad = strlen(buf);
110c76f0793SBaptiste Daroussin 			wresize(pad, hpad, wpad);
111c76f0793SBaptiste Daroussin 		}
112c76f0793SBaptiste Daroussin 		if (i > hpad-1) {
113c76f0793SBaptiste Daroussin 			hpad++;
114c76f0793SBaptiste Daroussin 			wresize(pad, hpad, wpad);
115c76f0793SBaptiste Daroussin 		}
116c76f0793SBaptiste Daroussin 		mvwaddstr(pad, i, 0, buf);
117c76f0793SBaptiste Daroussin 		i++;
118c76f0793SBaptiste Daroussin 	}
119c76f0793SBaptiste Daroussin 	fclose(fp);
120c76f0793SBaptiste Daroussin 
121c76f0793SBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
122c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
123c76f0793SBaptiste Daroussin 	textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
124f499134dSBaptiste Daroussin 	if (textbox_checksize(h, w, hpad) != 0)
125c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
126c76f0793SBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
127c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
128c76f0793SBaptiste Daroussin 
129c76f0793SBaptiste Daroussin 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
130c76f0793SBaptiste Daroussin 	    NULL, NULL, NULL, true) != 0)
131c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
132c76f0793SBaptiste Daroussin 
133f499134dSBaptiste Daroussin 	exitbutt = conf->button.exit_label == NULL ? BUTTON_TEXTBOX : conf->button.exit_label;
134c76f0793SBaptiste Daroussin 	draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, strlen(exitbutt)+2,
135f499134dSBaptiste Daroussin 	    exitbutt, true, false);
136c76f0793SBaptiste Daroussin 
137c76f0793SBaptiste Daroussin 	wrefresh(widget);
138c76f0793SBaptiste Daroussin 
139c76f0793SBaptiste Daroussin 	ys = y + 1;
140c76f0793SBaptiste Daroussin 	xs = x + 1;
141c76f0793SBaptiste Daroussin 	ye = ys + h - 5;
142c76f0793SBaptiste Daroussin 	xe = xs + w - 3;
143c76f0793SBaptiste Daroussin 	ypad = xpad = 0;
144c76f0793SBaptiste Daroussin 	printrows = h-4;
145c76f0793SBaptiste Daroussin 	loop = true;
146c76f0793SBaptiste Daroussin 	while(loop) {
147c76f0793SBaptiste Daroussin 		prefresh(pad, ypad, xpad, ys, xs, ye, xe);
148c76f0793SBaptiste Daroussin 		input = getch();
149c76f0793SBaptiste Daroussin 		switch(input) {
150c76f0793SBaptiste Daroussin 		case KEY_ENTER:
151c76f0793SBaptiste Daroussin 		case 10: /* Enter */
1528c4f4028SBaptiste Daroussin 			output = BSDDIALOG_OK;
153c76f0793SBaptiste Daroussin 			loop = false;
154c76f0793SBaptiste Daroussin 			break;
155c76f0793SBaptiste Daroussin 		case 27: /* Esc */
156c76f0793SBaptiste Daroussin 			output = BSDDIALOG_ESC;
157c76f0793SBaptiste Daroussin 			loop = false;
158c76f0793SBaptiste Daroussin 			break;
159c76f0793SBaptiste Daroussin 		case KEY_HOME:
160c76f0793SBaptiste Daroussin 			ypad = 0;
161c76f0793SBaptiste Daroussin 			break;
162c76f0793SBaptiste Daroussin 		case KEY_END:
163c76f0793SBaptiste Daroussin 			ypad = hpad - printrows;
164c76f0793SBaptiste Daroussin 			ypad = ypad < 0 ? 0 : ypad;
165c76f0793SBaptiste Daroussin 			break;
166c76f0793SBaptiste Daroussin 		case KEY_PPAGE:
167c76f0793SBaptiste Daroussin 			ypad -= printrows;
168c76f0793SBaptiste Daroussin 			ypad = ypad < 0 ? 0 : ypad;
169c76f0793SBaptiste Daroussin 			break;
170c76f0793SBaptiste Daroussin 		case KEY_NPAGE:
171c76f0793SBaptiste Daroussin 			ypad += printrows;
172c76f0793SBaptiste Daroussin 			ypad = ypad + printrows > hpad ? hpad - printrows : ypad;
173c76f0793SBaptiste Daroussin 			break;
174c76f0793SBaptiste Daroussin 		case '0':
175c76f0793SBaptiste Daroussin 			xpad = 0;
176c76f0793SBaptiste Daroussin 		case KEY_LEFT:
177c76f0793SBaptiste Daroussin 		case 'h':
178c76f0793SBaptiste Daroussin 			xpad = xpad > 0 ? xpad - 1 : 0;
179c76f0793SBaptiste Daroussin 			break;
180c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
181c76f0793SBaptiste Daroussin 		case 'l':
182c76f0793SBaptiste Daroussin 			xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad;
183c76f0793SBaptiste Daroussin 			break;
184c76f0793SBaptiste Daroussin 		case KEY_UP:
185c76f0793SBaptiste Daroussin 		case 'k':
186c76f0793SBaptiste Daroussin 			ypad = ypad > 0 ? ypad - 1 : 0;
187c76f0793SBaptiste Daroussin 			break;
188c76f0793SBaptiste Daroussin 		case KEY_DOWN:
189c76f0793SBaptiste Daroussin 		case'j':
190c76f0793SBaptiste Daroussin 			ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad;
191c76f0793SBaptiste Daroussin 			break;
192c76f0793SBaptiste Daroussin 		case KEY_F(1):
1938c4f4028SBaptiste Daroussin 			if (conf->f1_file == NULL && conf->f1_message == NULL)
194c76f0793SBaptiste Daroussin 				break;
195c76f0793SBaptiste Daroussin 			if (f1help(conf) != 0)
196c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
197c76f0793SBaptiste Daroussin 			/* No break! the terminal size can change */
198c76f0793SBaptiste Daroussin 		case KEY_RESIZE:
199f499134dSBaptiste Daroussin 			hide_widget(y, x, h, w,conf->shadow);
200c76f0793SBaptiste Daroussin 
201c76f0793SBaptiste Daroussin 			/*
202c76f0793SBaptiste Daroussin 			 * Unnecessary, but, when the columns decrease the
203c76f0793SBaptiste Daroussin 			 * following "refresh" seem not work
204c76f0793SBaptiste Daroussin 			 */
205c76f0793SBaptiste Daroussin 			refresh();
206c76f0793SBaptiste Daroussin 
207c76f0793SBaptiste Daroussin 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
208c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
209c76f0793SBaptiste Daroussin 			textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad);
210f499134dSBaptiste Daroussin 			if (textbox_checksize(h, w, hpad) != 0)
211c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
212c76f0793SBaptiste Daroussin 			if (set_widget_position(conf, &y, &x, h, w) != 0)
213c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
214c76f0793SBaptiste Daroussin 
215c76f0793SBaptiste Daroussin 			wclear(shadow);
216f499134dSBaptiste Daroussin 			mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
217c76f0793SBaptiste Daroussin 			wresize(shadow, h, w);
218c76f0793SBaptiste Daroussin 
219c76f0793SBaptiste Daroussin 			wclear(widget);
220c76f0793SBaptiste Daroussin 			mvwin(widget, y, x);
221c76f0793SBaptiste Daroussin 			wresize(widget, h, w);
222c76f0793SBaptiste Daroussin 
223c76f0793SBaptiste Daroussin 			ys = y + 1;
224c76f0793SBaptiste Daroussin 			xs = x + 1;
225c76f0793SBaptiste Daroussin 			ye = ys + h - 5;
226c76f0793SBaptiste Daroussin 			xe = xs + w - 3;
227c76f0793SBaptiste Daroussin 			ypad = xpad = 0;
228c76f0793SBaptiste Daroussin 			printrows = h - 4;
229c76f0793SBaptiste Daroussin 
230c76f0793SBaptiste Daroussin 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
231c76f0793SBaptiste Daroussin 			    RAISED, NULL, NULL, NULL, true) != 0)
232c76f0793SBaptiste Daroussin 			return BSDDIALOG_ERROR;
233c76f0793SBaptiste Daroussin 
234c76f0793SBaptiste Daroussin 			draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2,
235f499134dSBaptiste Daroussin 			    strlen(exitbutt)+2, exitbutt, true, false);
236c76f0793SBaptiste Daroussin 
237c76f0793SBaptiste Daroussin 			wrefresh(widget); /* for button */
238c76f0793SBaptiste Daroussin 
239c76f0793SBaptiste Daroussin 			/* Important to fix grey lines expanding screen */
240c76f0793SBaptiste Daroussin 			refresh();
241c76f0793SBaptiste Daroussin 			break;
242c76f0793SBaptiste Daroussin 		}
243c76f0793SBaptiste Daroussin 	}
244c76f0793SBaptiste Daroussin 
245c76f0793SBaptiste Daroussin 	end_widget_withtextpad(conf, widget, h, w, pad, shadow);
246c76f0793SBaptiste Daroussin 
247c76f0793SBaptiste Daroussin 	return output;
248c76f0793SBaptiste Daroussin }
249