xref: /freebsd/contrib/bsddialog/lib/messagebox.c (revision 84823cc70824c8d842f503d8c2e6d7b0c2d95b61)
1c76f0793SBaptiste Daroussin /*-
2c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3c76f0793SBaptiste Daroussin  *
4263660c0SAlfonso Siciliano  * Copyright (c) 2021-2022 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 #include <sys/param.h>
29c76f0793SBaptiste Daroussin 
30263660c0SAlfonso Siciliano #include <curses.h>
31d93b4d32SBaptiste Daroussin #include <string.h>
32c76f0793SBaptiste Daroussin 
33c76f0793SBaptiste Daroussin #include "bsddialog.h"
34b319d934SAlfonso S. Siciliano #include "bsddialog_theme.h"
35263660c0SAlfonso Siciliano #include "lib_util.h"
36c76f0793SBaptiste Daroussin 
37c76f0793SBaptiste Daroussin static int
38d93b4d32SBaptiste Daroussin message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
39b319d934SAlfonso S. Siciliano     int *w, const char *text, bool *hastext, struct buttons bs)
40c76f0793SBaptiste Daroussin {
41263660c0SAlfonso Siciliano 	int htext, wtext;
42c76f0793SBaptiste Daroussin 
43b319d934SAlfonso S. Siciliano 	if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE ||
44b319d934SAlfonso S. Siciliano 	    hastext != NULL) {
45b319d934SAlfonso S. Siciliano 		if (text_size(conf, rows, cols, text, &bs, 0, 1, &htext,
46b319d934SAlfonso S. Siciliano 		    &wtext) != 0)
47263660c0SAlfonso Siciliano 			return (BSDDIALOG_ERROR);
48b319d934SAlfonso S. Siciliano 		if (hastext != NULL)
49b319d934SAlfonso S. Siciliano 			*hastext = htext > 0 ? true : false;
50c76f0793SBaptiste Daroussin 	}
51c76f0793SBaptiste Daroussin 
52263660c0SAlfonso Siciliano 	if (cols == BSDDIALOG_AUTOSIZE)
53263660c0SAlfonso Siciliano 		*w = widget_min_width(conf, wtext, 0, &bs);
54c76f0793SBaptiste Daroussin 
55263660c0SAlfonso Siciliano 	if (rows == BSDDIALOG_AUTOSIZE)
56263660c0SAlfonso Siciliano 		*h = widget_min_height(conf, htext, 0, true);
57263660c0SAlfonso Siciliano 
58263660c0SAlfonso Siciliano 	return (0);
59c76f0793SBaptiste Daroussin }
60c76f0793SBaptiste Daroussin 
61b319d934SAlfonso S. Siciliano static int
62b319d934SAlfonso S. Siciliano message_checksize(int rows, int cols, bool hastext, struct buttons bs)
63c76f0793SBaptiste Daroussin {
64c76f0793SBaptiste Daroussin 	int mincols;
65c76f0793SBaptiste Daroussin 
66c76f0793SBaptiste Daroussin 	mincols = VBORDERS;
67b319d934SAlfonso S. Siciliano 	mincols += buttons_min_width(bs);
68c76f0793SBaptiste Daroussin 
69c76f0793SBaptiste Daroussin 	if (cols < mincols)
70263660c0SAlfonso Siciliano 		RETURN_ERROR("Few cols, Msgbox and Yesno need at least width "
71c76f0793SBaptiste Daroussin 		    "for borders, buttons and spaces between buttons");
72c76f0793SBaptiste Daroussin 
73263660c0SAlfonso Siciliano 	if (rows < HBORDERS + 2 /* buttons */)
74b319d934SAlfonso S. Siciliano 		RETURN_ERROR("Msgbox and Yesno need at least 4 rows");
75b319d934SAlfonso S. Siciliano 	if (hastext && rows < HBORDERS + 2 /*buttons*/ + 1 /* text row */)
76b319d934SAlfonso S. Siciliano 		RETURN_ERROR("Msgbox and Yesno with text need at least 5 rows");
77c76f0793SBaptiste Daroussin 
78263660c0SAlfonso Siciliano 	return (0);
79c76f0793SBaptiste Daroussin }
80c76f0793SBaptiste Daroussin 
81c76f0793SBaptiste Daroussin static void
82b319d934SAlfonso S. Siciliano textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad,
83b319d934SAlfonso S. Siciliano     bool hastext)
84c76f0793SBaptiste Daroussin {
85263660c0SAlfonso Siciliano 	int y, x, h, w;
86c76f0793SBaptiste Daroussin 
87263660c0SAlfonso Siciliano 	getbegyx(widget, y, x);
88263660c0SAlfonso Siciliano 	getmaxyx(widget, h, w);
89263660c0SAlfonso Siciliano 
90b319d934SAlfonso S. Siciliano 	if (hastext && htextpad > h - 4) {
91b319d934SAlfonso S. Siciliano 		wattron(widget, t.dialog.arrowcolor);
92c76f0793SBaptiste Daroussin 		mvwprintw(widget, h-3, w-6, "%3d%%",
93263660c0SAlfonso Siciliano 		    100 * (ytextpad+h-4)/ htextpad);
94b319d934SAlfonso S. Siciliano 		wattroff(widget, t.dialog.arrowcolor);
95c76f0793SBaptiste Daroussin 		wnoutrefresh(widget);
96c76f0793SBaptiste Daroussin 	}
97c76f0793SBaptiste Daroussin 
98263660c0SAlfonso Siciliano 	pnoutrefresh(textpad, ytextpad, 0, y+1, x+2, y+h-4, x+w-2);
99c76f0793SBaptiste Daroussin }
100c76f0793SBaptiste Daroussin 
101c76f0793SBaptiste Daroussin static int
102263660c0SAlfonso Siciliano do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols,
103d93b4d32SBaptiste Daroussin     struct buttons bs)
104c76f0793SBaptiste Daroussin {
105b319d934SAlfonso S. Siciliano 	bool hastext, loop;
106*84823cc7SAlfonso S. Siciliano 	int y, x, h, w, retval, ytextpad, htextpad, printrows, unused;
107263660c0SAlfonso Siciliano 	WINDOW *widget, *textpad, *shadow;
108b319d934SAlfonso S. Siciliano 	wint_t input;
109c76f0793SBaptiste Daroussin 
110c76f0793SBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
111263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
112b319d934SAlfonso S. Siciliano 	if (message_autosize(conf, rows, cols, &h, &w, text, &hastext, bs) != 0)
113263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
114b319d934SAlfonso S. Siciliano 	if (message_checksize(h, w, hastext, bs) != 0)
115263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
116c76f0793SBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
117263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
118c76f0793SBaptiste Daroussin 
119263660c0SAlfonso Siciliano 	if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
120263660c0SAlfonso Siciliano 	    true) != 0)
121263660c0SAlfonso Siciliano 		return (BSDDIALOG_ERROR);
122c76f0793SBaptiste Daroussin 
123*84823cc7SAlfonso S. Siciliano 	printrows = h - 4;
124263660c0SAlfonso Siciliano 	ytextpad = 0;
125263660c0SAlfonso Siciliano 	getmaxyx(textpad, htextpad, unused);
126263660c0SAlfonso Siciliano 	unused++; /* fix unused error */
127c76f0793SBaptiste Daroussin 	loop = true;
128c76f0793SBaptiste Daroussin 	while (loop) {
129*84823cc7SAlfonso S. Siciliano 		textupdate(widget, textpad, htextpad, ytextpad, hastext);
130c76f0793SBaptiste Daroussin 		doupdate();
131b319d934SAlfonso S. Siciliano 		if (get_wch(&input) == ERR)
132b319d934SAlfonso S. Siciliano 			continue;
133c76f0793SBaptiste Daroussin 		switch (input) {
134263660c0SAlfonso Siciliano 		case KEY_ENTER:
135c76f0793SBaptiste Daroussin 		case 10: /* Enter */
136b319d934SAlfonso S. Siciliano 			retval = bs.value[bs.curr];
137c76f0793SBaptiste Daroussin 			loop = false;
138c76f0793SBaptiste Daroussin 			break;
139c76f0793SBaptiste Daroussin 		case 27: /* Esc */
140263660c0SAlfonso Siciliano 			if (conf->key.enable_esc) {
141b319d934SAlfonso S. Siciliano 				retval = BSDDIALOG_ESC;
142c76f0793SBaptiste Daroussin 				loop = false;
143263660c0SAlfonso Siciliano 			}
144c76f0793SBaptiste Daroussin 			break;
145c76f0793SBaptiste Daroussin 		case '\t': /* TAB */
146c76f0793SBaptiste Daroussin 			bs.curr = (bs.curr + 1) % bs.nbuttons;
147263660c0SAlfonso Siciliano 			draw_buttons(widget, bs, true);
148263660c0SAlfonso Siciliano 			wnoutrefresh(widget);
149c76f0793SBaptiste Daroussin 			break;
150c76f0793SBaptiste Daroussin 		case KEY_LEFT:
151c76f0793SBaptiste Daroussin 			if (bs.curr > 0) {
152c76f0793SBaptiste Daroussin 				bs.curr--;
153263660c0SAlfonso Siciliano 				draw_buttons(widget, bs, true);
154263660c0SAlfonso Siciliano 				wnoutrefresh(widget);
155c76f0793SBaptiste Daroussin 			}
156c76f0793SBaptiste Daroussin 			break;
157c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
158c76f0793SBaptiste Daroussin 			if (bs.curr < (int)bs.nbuttons - 1) {
159c76f0793SBaptiste Daroussin 				bs.curr++;
160263660c0SAlfonso Siciliano 				draw_buttons(widget, bs, true);
161263660c0SAlfonso Siciliano 				wnoutrefresh(widget);
162c76f0793SBaptiste Daroussin 			}
163c76f0793SBaptiste Daroussin 			break;
164*84823cc7SAlfonso S. Siciliano 		case KEY_UP:
165*84823cc7SAlfonso S. Siciliano 			if (ytextpad > 0)
166*84823cc7SAlfonso S. Siciliano 				ytextpad--;
167*84823cc7SAlfonso S. Siciliano 			break;
168*84823cc7SAlfonso S. Siciliano 		case KEY_DOWN:
169*84823cc7SAlfonso S. Siciliano 			if (ytextpad + printrows < htextpad)
170*84823cc7SAlfonso S. Siciliano 				ytextpad++;
171*84823cc7SAlfonso S. Siciliano 			break;
172*84823cc7SAlfonso S. Siciliano 		case KEY_HOME:
173*84823cc7SAlfonso S. Siciliano 			ytextpad = 0;
174*84823cc7SAlfonso S. Siciliano 			break;
175*84823cc7SAlfonso S. Siciliano 		case KEY_END:
176*84823cc7SAlfonso S. Siciliano 			ytextpad = htextpad - printrows;
177*84823cc7SAlfonso S. Siciliano 			ytextpad = ytextpad < 0 ? 0 : ytextpad;
178*84823cc7SAlfonso S. Siciliano 			break;
179*84823cc7SAlfonso S. Siciliano 		case KEY_PPAGE:
180*84823cc7SAlfonso S. Siciliano 			ytextpad -= printrows;
181*84823cc7SAlfonso S. Siciliano 			ytextpad = ytextpad < 0 ? 0 : ytextpad;
182*84823cc7SAlfonso S. Siciliano 			break;
183*84823cc7SAlfonso S. Siciliano 		case KEY_NPAGE:
184*84823cc7SAlfonso S. Siciliano 			ytextpad += printrows;
185*84823cc7SAlfonso S. Siciliano 			if (ytextpad + printrows > htextpad)
186*84823cc7SAlfonso S. Siciliano 				ytextpad = htextpad - printrows;
187*84823cc7SAlfonso S. Siciliano 			break;
188263660c0SAlfonso Siciliano 		case KEY_F(1):
189bce40c02SAlfonso S. Siciliano 			if (conf->key.f1_file == NULL &&
190bce40c02SAlfonso S. Siciliano 			    conf->key.f1_message == NULL)
191263660c0SAlfonso Siciliano 				break;
192263660c0SAlfonso Siciliano 			if (f1help(conf) != 0)
193263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
194263660c0SAlfonso Siciliano 			/* No break, screen size can change */
195263660c0SAlfonso Siciliano 		case KEY_RESIZE:
196263660c0SAlfonso Siciliano 			/* Important for decreasing screen */
197263660c0SAlfonso Siciliano 			hide_widget(y, x, h, w, conf->shadow);
198263660c0SAlfonso Siciliano 			refresh();
199263660c0SAlfonso Siciliano 
200263660c0SAlfonso Siciliano 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
201263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
202263660c0SAlfonso Siciliano 			if (message_autosize(conf, rows, cols, &h, &w, text,
203b319d934SAlfonso S. Siciliano 			    NULL, bs) != 0)
204263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
205b319d934SAlfonso S. Siciliano 			if (message_checksize(h, w, hastext, bs) != 0)
206263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
207263660c0SAlfonso Siciliano 			if (set_widget_position(conf, &y, &x, h, w) != 0)
208263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
209263660c0SAlfonso Siciliano 
210263660c0SAlfonso Siciliano 			if (update_dialog(conf, shadow, widget, y, x, h, w,
211263660c0SAlfonso Siciliano 			    textpad, text, &bs, true) != 0)
212263660c0SAlfonso Siciliano 				return (BSDDIALOG_ERROR);
213263660c0SAlfonso Siciliano 
214*84823cc7SAlfonso S. Siciliano 			printrows = h - 4;
215263660c0SAlfonso Siciliano 			getmaxyx(textpad, htextpad, unused);
216b319d934SAlfonso S. Siciliano 			ytextpad = 0;
217b319d934SAlfonso S. Siciliano 			textupdate(widget, textpad, htextpad, ytextpad, hastext);
218263660c0SAlfonso Siciliano 
219263660c0SAlfonso Siciliano 			/* Important to fix grey lines expanding screen */
220263660c0SAlfonso Siciliano 			refresh();
221263660c0SAlfonso Siciliano 			break;
222c76f0793SBaptiste Daroussin 		default:
223263660c0SAlfonso Siciliano 			if (shortcut_buttons(input, &bs)) {
224b319d934SAlfonso S. Siciliano 				retval = bs.value[bs.curr];
225c76f0793SBaptiste Daroussin 				loop = false;
226c76f0793SBaptiste Daroussin 			}
227c76f0793SBaptiste Daroussin 		}
228c76f0793SBaptiste Daroussin 	}
229c76f0793SBaptiste Daroussin 
230263660c0SAlfonso Siciliano 	end_dialog(conf, shadow, widget, textpad);
231c76f0793SBaptiste Daroussin 
232b319d934SAlfonso S. Siciliano 	return (retval);
233c76f0793SBaptiste Daroussin }
234c76f0793SBaptiste Daroussin 
235c76f0793SBaptiste Daroussin /* API */
236c76f0793SBaptiste Daroussin int
237263660c0SAlfonso Siciliano bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows,
238263660c0SAlfonso Siciliano     int cols)
239c76f0793SBaptiste Daroussin {
240c76f0793SBaptiste Daroussin 	struct buttons bs;
241c76f0793SBaptiste Daroussin 
242263660c0SAlfonso Siciliano 	get_buttons(conf, &bs, BUTTON_OK_LABEL, NULL);
243c76f0793SBaptiste Daroussin 
244263660c0SAlfonso Siciliano 	return (do_message(conf, text, rows, cols, bs));
245c76f0793SBaptiste Daroussin }
246c76f0793SBaptiste Daroussin 
247c76f0793SBaptiste Daroussin int
248263660c0SAlfonso Siciliano bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows,
249263660c0SAlfonso Siciliano     int cols)
250c76f0793SBaptiste Daroussin {
251c76f0793SBaptiste Daroussin 	struct buttons bs;
252c76f0793SBaptiste Daroussin 
253263660c0SAlfonso Siciliano 	get_buttons(conf, &bs, "Yes", "No");
254c76f0793SBaptiste Daroussin 
255263660c0SAlfonso Siciliano 	return (do_message(conf, text, rows, cols, bs));
256c76f0793SBaptiste Daroussin }