xref: /freebsd-src/contrib/bsddialog/lib/messagebox.c (revision c76f07938c44264c7ebd400c23f218e561960d23)
1*c76f0793SBaptiste Daroussin /*-
2*c76f0793SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3*c76f0793SBaptiste Daroussin  *
4*c76f0793SBaptiste Daroussin  * Copyright (c) 2021 Alfonso Sabato Siciliano
5*c76f0793SBaptiste Daroussin  *
6*c76f0793SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7*c76f0793SBaptiste Daroussin  * modification, are permitted provided that the following conditions
8*c76f0793SBaptiste Daroussin  * are met:
9*c76f0793SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10*c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11*c76f0793SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12*c76f0793SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13*c76f0793SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14*c76f0793SBaptiste Daroussin  *
15*c76f0793SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*c76f0793SBaptiste Daroussin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*c76f0793SBaptiste Daroussin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*c76f0793SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*c76f0793SBaptiste Daroussin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*c76f0793SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*c76f0793SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*c76f0793SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*c76f0793SBaptiste Daroussin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*c76f0793SBaptiste Daroussin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*c76f0793SBaptiste Daroussin  * SUCH DAMAGE.
26*c76f0793SBaptiste Daroussin  */
27*c76f0793SBaptiste Daroussin 
28*c76f0793SBaptiste Daroussin #include <sys/param.h>
29*c76f0793SBaptiste Daroussin 
30*c76f0793SBaptiste Daroussin #include <ctype.h>
31*c76f0793SBaptiste Daroussin #include <string.h>
32*c76f0793SBaptiste Daroussin 
33*c76f0793SBaptiste Daroussin #ifdef PORTNCURSES
34*c76f0793SBaptiste Daroussin #include <ncurses/curses.h>
35*c76f0793SBaptiste Daroussin #else
36*c76f0793SBaptiste Daroussin #include <curses.h>
37*c76f0793SBaptiste Daroussin #endif
38*c76f0793SBaptiste Daroussin 
39*c76f0793SBaptiste Daroussin #include "bsddialog.h"
40*c76f0793SBaptiste Daroussin #include "lib_util.h"
41*c76f0793SBaptiste Daroussin #include "bsddialog_theme.h"
42*c76f0793SBaptiste Daroussin 
43*c76f0793SBaptiste Daroussin /* "Message": msgbox - yesno */
44*c76f0793SBaptiste Daroussin 
45*c76f0793SBaptiste Daroussin #define AUTO_WIDTH	(COLS / 3U)
46*c76f0793SBaptiste Daroussin /*
47*c76f0793SBaptiste Daroussin  * Min height = 5: 2 up & down borders + 2 label & up border buttons + 1 line
48*c76f0793SBaptiste Daroussin  * for text, at least 1 line is important for widget_withtextpad_init() to avoid
49*c76f0793SBaptiste Daroussin  * "Cannot build the pad window for text".
50*c76f0793SBaptiste Daroussin  */
51*c76f0793SBaptiste Daroussin #define MIN_HEIGHT	5
52*c76f0793SBaptiste Daroussin 
53*c76f0793SBaptiste Daroussin extern struct bsddialog_theme t;
54*c76f0793SBaptiste Daroussin 
55*c76f0793SBaptiste Daroussin static int
56*c76f0793SBaptiste Daroussin message_autosize(struct bsddialog_conf conf, int rows, int cols, int *h, int *w,
57*c76f0793SBaptiste Daroussin     char *text, struct buttons bs)
58*c76f0793SBaptiste Daroussin {
59*c76f0793SBaptiste Daroussin 	int maxword, maxline, nlines, line;
60*c76f0793SBaptiste Daroussin 
61*c76f0793SBaptiste Daroussin 	if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0)
62*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
63*c76f0793SBaptiste Daroussin 
64*c76f0793SBaptiste Daroussin 	if (cols == BSDDIALOG_AUTOSIZE) {
65*c76f0793SBaptiste Daroussin 		*w = VBORDERS;
66*c76f0793SBaptiste Daroussin 		/* buttons size */
67*c76f0793SBaptiste Daroussin 		*w += bs.nbuttons * bs.sizebutton;
68*c76f0793SBaptiste Daroussin 		*w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.buttonspace : 0;
69*c76f0793SBaptiste Daroussin 		/* text size */
70*c76f0793SBaptiste Daroussin 		line = MIN(maxline + VBORDERS + t.texthmargin * 2, AUTO_WIDTH);
71*c76f0793SBaptiste Daroussin 		line = MAX(line, (int) (maxword + VBORDERS + t.texthmargin * 2));
72*c76f0793SBaptiste Daroussin 		*w = MAX(*w, line);
73*c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
74*c76f0793SBaptiste Daroussin 		*w = MIN(*w, widget_max_width(conf));
75*c76f0793SBaptiste Daroussin 	}
76*c76f0793SBaptiste Daroussin 
77*c76f0793SBaptiste Daroussin 	if (rows == BSDDIALOG_AUTOSIZE) {
78*c76f0793SBaptiste Daroussin 		*h = MIN_HEIGHT - 1;
79*c76f0793SBaptiste Daroussin 		if (maxword > 0)
80*c76f0793SBaptiste Daroussin 			*h += MAX(nlines, (*w / GET_ASPECT_RATIO(conf)));
81*c76f0793SBaptiste Daroussin 		*h = MAX(*h, MIN_HEIGHT);
82*c76f0793SBaptiste Daroussin 		/* avoid terminal overflow */
83*c76f0793SBaptiste Daroussin 		*h = MIN(*h, widget_max_height(conf));
84*c76f0793SBaptiste Daroussin 	}
85*c76f0793SBaptiste Daroussin 
86*c76f0793SBaptiste Daroussin 	return 0;
87*c76f0793SBaptiste Daroussin }
88*c76f0793SBaptiste Daroussin 
89*c76f0793SBaptiste Daroussin static int message_checksize(int rows, int cols, struct buttons bs)
90*c76f0793SBaptiste Daroussin {
91*c76f0793SBaptiste Daroussin 	int mincols;
92*c76f0793SBaptiste Daroussin 
93*c76f0793SBaptiste Daroussin 	mincols = VBORDERS;
94*c76f0793SBaptiste Daroussin 	mincols += bs.nbuttons * bs.sizebutton;
95*c76f0793SBaptiste Daroussin 	mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.buttonspace : 0;
96*c76f0793SBaptiste Daroussin 
97*c76f0793SBaptiste Daroussin 	if (cols < mincols)
98*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Few cols, Msgbox and Yesno need at least width "\
99*c76f0793SBaptiste Daroussin 		    "for borders, buttons and spaces between buttons");
100*c76f0793SBaptiste Daroussin 
101*c76f0793SBaptiste Daroussin 	if (rows < MIN_HEIGHT)
102*c76f0793SBaptiste Daroussin 		RETURN_ERROR("Msgbox and Yesno need at least height 5");
103*c76f0793SBaptiste Daroussin 
104*c76f0793SBaptiste Daroussin 	return 0;
105*c76f0793SBaptiste Daroussin }
106*c76f0793SBaptiste Daroussin 
107*c76f0793SBaptiste Daroussin static void
108*c76f0793SBaptiste Daroussin buttonsupdate(WINDOW *widget, int h, int w, struct buttons bs, bool shortkey)
109*c76f0793SBaptiste Daroussin {
110*c76f0793SBaptiste Daroussin 	draw_buttons(widget, h-2, w, bs, shortkey);
111*c76f0793SBaptiste Daroussin 	wnoutrefresh(widget);
112*c76f0793SBaptiste Daroussin }
113*c76f0793SBaptiste Daroussin 
114*c76f0793SBaptiste Daroussin static void
115*c76f0793SBaptiste Daroussin textupdate(WINDOW *widget, int y, int x, int h, int w, WINDOW *textpad,
116*c76f0793SBaptiste Daroussin     int htextpad, int textrow)
117*c76f0793SBaptiste Daroussin {
118*c76f0793SBaptiste Daroussin 
119*c76f0793SBaptiste Daroussin 	if (htextpad > h - 4) {
120*c76f0793SBaptiste Daroussin 		mvwprintw(widget, h-3, w-6, "%3d%%",
121*c76f0793SBaptiste Daroussin 		    100 * (textrow+h-4)/ htextpad);
122*c76f0793SBaptiste Daroussin 		wnoutrefresh(widget);
123*c76f0793SBaptiste Daroussin 	}
124*c76f0793SBaptiste Daroussin 
125*c76f0793SBaptiste Daroussin 	pnoutrefresh(textpad, textrow, 0, y+1, x+2, y+h-4, x+w-2);
126*c76f0793SBaptiste Daroussin }
127*c76f0793SBaptiste Daroussin 
128*c76f0793SBaptiste Daroussin static int
129*c76f0793SBaptiste Daroussin do_widget(struct bsddialog_conf conf, char *text, int rows, int cols,
130*c76f0793SBaptiste Daroussin     struct buttons bs, bool shortkey)
131*c76f0793SBaptiste Daroussin {
132*c76f0793SBaptiste Daroussin 	WINDOW *widget, *textpad, *shadow;
133*c76f0793SBaptiste Daroussin 	bool loop;
134*c76f0793SBaptiste Daroussin 	int i, y, x, h, w, input, output, htextpad, textrow;
135*c76f0793SBaptiste Daroussin 
136*c76f0793SBaptiste Daroussin 	if (set_widget_size(conf, rows, cols, &h, &w) != 0)
137*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
138*c76f0793SBaptiste Daroussin 	if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0)
139*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
140*c76f0793SBaptiste Daroussin 	if (message_checksize(h, w, bs) != 0)
141*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
142*c76f0793SBaptiste Daroussin 	if (set_widget_position(conf, &y, &x, h, w) != 0)
143*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
144*c76f0793SBaptiste Daroussin 
145*c76f0793SBaptiste Daroussin 	if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED,
146*c76f0793SBaptiste Daroussin 	    &textpad, &htextpad, text, true) != 0)
147*c76f0793SBaptiste Daroussin 		return BSDDIALOG_ERROR;
148*c76f0793SBaptiste Daroussin 
149*c76f0793SBaptiste Daroussin 	textrow = 0;
150*c76f0793SBaptiste Daroussin 	loop = true;
151*c76f0793SBaptiste Daroussin 	buttonsupdate(widget, h, w, bs, shortkey);
152*c76f0793SBaptiste Daroussin 	textupdate(widget, y, x, h, w, textpad, htextpad, textrow);
153*c76f0793SBaptiste Daroussin 	while(loop) {
154*c76f0793SBaptiste Daroussin 		doupdate();
155*c76f0793SBaptiste Daroussin 		input = getch();
156*c76f0793SBaptiste Daroussin 		switch (input) {
157*c76f0793SBaptiste Daroussin 		case 10: /* Enter */
158*c76f0793SBaptiste Daroussin 			output = bs.value[bs.curr];
159*c76f0793SBaptiste Daroussin 			loop = false;
160*c76f0793SBaptiste Daroussin 			break;
161*c76f0793SBaptiste Daroussin 		case 27: /* Esc */
162*c76f0793SBaptiste Daroussin 			output = BSDDIALOG_ESC;
163*c76f0793SBaptiste Daroussin 			loop = false;
164*c76f0793SBaptiste Daroussin 			break;
165*c76f0793SBaptiste Daroussin 		case '\t': /* TAB */
166*c76f0793SBaptiste Daroussin 			bs.curr = (bs.curr + 1) % bs.nbuttons;
167*c76f0793SBaptiste Daroussin 			buttonsupdate(widget, h, w, bs, shortkey);
168*c76f0793SBaptiste Daroussin 			break;
169*c76f0793SBaptiste Daroussin 		case KEY_F(1):
170*c76f0793SBaptiste Daroussin 			if (conf.hfile == NULL)
171*c76f0793SBaptiste Daroussin 				break;
172*c76f0793SBaptiste Daroussin 			if (f1help(conf) != 0)
173*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
174*c76f0793SBaptiste Daroussin 			/* No break! the terminal size can change */
175*c76f0793SBaptiste Daroussin 		case KEY_RESIZE:
176*c76f0793SBaptiste Daroussin 			hide_widget(y, x, h, w,conf.shadow);
177*c76f0793SBaptiste Daroussin 
178*c76f0793SBaptiste Daroussin 			/*
179*c76f0793SBaptiste Daroussin 			 * Unnecessary, but, when the columns decrease the
180*c76f0793SBaptiste Daroussin 			 * following "refresh" seem not work
181*c76f0793SBaptiste Daroussin 			 */
182*c76f0793SBaptiste Daroussin 			refresh();
183*c76f0793SBaptiste Daroussin 
184*c76f0793SBaptiste Daroussin 			if (set_widget_size(conf, rows, cols, &h, &w) != 0)
185*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
186*c76f0793SBaptiste Daroussin 			if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0)
187*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
188*c76f0793SBaptiste Daroussin 			if (message_checksize(h, w, bs) != 0)
189*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
190*c76f0793SBaptiste Daroussin 			if (set_widget_position(conf, &y, &x, h, w) != 0)
191*c76f0793SBaptiste Daroussin 				return BSDDIALOG_ERROR;
192*c76f0793SBaptiste Daroussin 
193*c76f0793SBaptiste Daroussin 			wclear(shadow);
194*c76f0793SBaptiste Daroussin 			mvwin(shadow, y + t.shadowrows, x + t.shadowcols);
195*c76f0793SBaptiste Daroussin 			wresize(shadow, h, w);
196*c76f0793SBaptiste Daroussin 
197*c76f0793SBaptiste Daroussin 			wclear(widget);
198*c76f0793SBaptiste Daroussin 			mvwin(widget, y, x);
199*c76f0793SBaptiste Daroussin 			wresize(widget, h, w);
200*c76f0793SBaptiste Daroussin 
201*c76f0793SBaptiste Daroussin 			htextpad = 1;
202*c76f0793SBaptiste Daroussin 			wclear(textpad);
203*c76f0793SBaptiste Daroussin 			wresize(textpad, 1, w - HBORDERS - t.texthmargin * 2);
204*c76f0793SBaptiste Daroussin 
205*c76f0793SBaptiste Daroussin 			if(update_widget_withtextpad(conf, shadow, widget, h, w,
206*c76f0793SBaptiste Daroussin 			    RAISED, textpad, &htextpad, text, true) != 0)
207*c76f0793SBaptiste Daroussin 			return BSDDIALOG_ERROR;
208*c76f0793SBaptiste Daroussin 
209*c76f0793SBaptiste Daroussin 			buttonsupdate(widget, h, w, bs, shortkey);
210*c76f0793SBaptiste Daroussin 			textupdate(widget, y, x, h, w, textpad, htextpad, textrow);
211*c76f0793SBaptiste Daroussin 
212*c76f0793SBaptiste Daroussin 			/* Important to fix grey lines expanding screen */
213*c76f0793SBaptiste Daroussin 			refresh();
214*c76f0793SBaptiste Daroussin 			break;
215*c76f0793SBaptiste Daroussin 		case KEY_UP:
216*c76f0793SBaptiste Daroussin 			if (textrow == 0)
217*c76f0793SBaptiste Daroussin 				break;
218*c76f0793SBaptiste Daroussin 			textrow--;
219*c76f0793SBaptiste Daroussin 			textupdate(widget, y, x, h, w, textpad, htextpad, textrow);
220*c76f0793SBaptiste Daroussin 			break;
221*c76f0793SBaptiste Daroussin 		case KEY_DOWN:
222*c76f0793SBaptiste Daroussin 			if (textrow + h - 4 >= htextpad)
223*c76f0793SBaptiste Daroussin 				break;
224*c76f0793SBaptiste Daroussin 			textrow++;
225*c76f0793SBaptiste Daroussin 			textupdate(widget, y, x, h, w, textpad, htextpad, textrow);
226*c76f0793SBaptiste Daroussin 			break;
227*c76f0793SBaptiste Daroussin 		case KEY_LEFT:
228*c76f0793SBaptiste Daroussin 			if (bs.curr > 0) {
229*c76f0793SBaptiste Daroussin 				bs.curr--;
230*c76f0793SBaptiste Daroussin 				buttonsupdate(widget, h, w, bs, shortkey);
231*c76f0793SBaptiste Daroussin 			}
232*c76f0793SBaptiste Daroussin 			break;
233*c76f0793SBaptiste Daroussin 		case KEY_RIGHT:
234*c76f0793SBaptiste Daroussin 			if (bs.curr < (int) bs.nbuttons - 1) {
235*c76f0793SBaptiste Daroussin 				bs.curr++;
236*c76f0793SBaptiste Daroussin 				buttonsupdate(widget, h, w, bs, shortkey);
237*c76f0793SBaptiste Daroussin 			}
238*c76f0793SBaptiste Daroussin 			break;
239*c76f0793SBaptiste Daroussin 		default:
240*c76f0793SBaptiste Daroussin 			if (shortkey == false)
241*c76f0793SBaptiste Daroussin 				break;
242*c76f0793SBaptiste Daroussin 
243*c76f0793SBaptiste Daroussin 			for (i = 0; i < (int) bs.nbuttons; i++)
244*c76f0793SBaptiste Daroussin 				if (tolower(input) == tolower((bs.label[i])[0])) {
245*c76f0793SBaptiste Daroussin 					output = bs.value[i];
246*c76f0793SBaptiste Daroussin 					loop = false;
247*c76f0793SBaptiste Daroussin 			}
248*c76f0793SBaptiste Daroussin 		}
249*c76f0793SBaptiste Daroussin 	}
250*c76f0793SBaptiste Daroussin 
251*c76f0793SBaptiste Daroussin 	end_widget_withtextpad(conf, widget, h, w, textpad, shadow);
252*c76f0793SBaptiste Daroussin 
253*c76f0793SBaptiste Daroussin 	return output;
254*c76f0793SBaptiste Daroussin }
255*c76f0793SBaptiste Daroussin 
256*c76f0793SBaptiste Daroussin /* API */
257*c76f0793SBaptiste Daroussin 
258*c76f0793SBaptiste Daroussin int
259*c76f0793SBaptiste Daroussin bsddialog_msgbox(struct bsddialog_conf conf, char* text, int rows, int cols)
260*c76f0793SBaptiste Daroussin {
261*c76f0793SBaptiste Daroussin 	struct buttons bs;
262*c76f0793SBaptiste Daroussin 
263*c76f0793SBaptiste Daroussin 	get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label),
264*c76f0793SBaptiste Daroussin 	    NULL /* nocancel */, BUTTONLABEL(help_label));
265*c76f0793SBaptiste Daroussin 
266*c76f0793SBaptiste Daroussin 	return (do_widget(conf, text, rows, cols, bs, true));
267*c76f0793SBaptiste Daroussin }
268*c76f0793SBaptiste Daroussin 
269*c76f0793SBaptiste Daroussin int
270*c76f0793SBaptiste Daroussin bsddialog_yesno(struct bsddialog_conf conf, char* text, int rows, int cols)
271*c76f0793SBaptiste Daroussin {
272*c76f0793SBaptiste Daroussin 	struct buttons bs;
273*c76f0793SBaptiste Daroussin 
274*c76f0793SBaptiste Daroussin 	get_buttons(conf, &bs, BUTTONLABEL(yes_label), BUTTONLABEL(extra_label),
275*c76f0793SBaptiste Daroussin 	    BUTTONLABEL(no_label), BUTTONLABEL(help_label));
276*c76f0793SBaptiste Daroussin 
277*c76f0793SBaptiste Daroussin 	return (do_widget(conf, text, rows, cols, bs, true));
278*c76f0793SBaptiste Daroussin }
279