xref: /freebsd/contrib/bsddialog/lib/messagebox.c (revision 1da7f3f6f72b2245e458fc7195733268ae4a1136)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2023 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 <curses.h>
29 
30 #include "bsddialog.h"
31 #include "bsddialog_theme.h"
32 #include "lib_util.h"
33 
34 struct scroll {
35 	int ypad;      /* y scrollable pad */
36 	int htext;     /* real h text to draw, to use with htextpad */
37 	int htextpad;  /* h textpad, draw_dialog() set at least 1 */
38 	int printrows; /* h - BORDER - HBUTTONS - BORDER */
39 };
40 
41 static void textupdate(struct dialog *d, struct scroll *s)
42 {
43 	if (s->htext > 0 && s->htextpad > s->printrows) {
44 		wattron(d->widget, t.dialog.arrowcolor);
45 		mvwprintw(d->widget, d->h - HBUTTONS - BORDER,
46 		    d->w - 4 - TEXTHMARGIN - BORDER,
47 		    "%3d%%", 100 * (s->ypad + s->printrows) / s->htextpad);
48 		wattroff(d->widget, t.dialog.arrowcolor);
49 		wnoutrefresh(d->widget);
50 	}
51 	rtextpad(d, s->ypad, 0, 0, HBUTTONS);
52 }
53 
54 static int message_size_position(struct dialog *d, int *htext)
55 {
56 	int minw;
57 
58 	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
59 		return (BSDDIALOG_ERROR);
60 	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
61 	    d->text, (*htext < 0) ? htext : NULL, &d->bs, 0, 0) != 0)
62 		return (BSDDIALOG_ERROR);
63 	minw = (*htext > 0) ? 1 + TEXTHMARGINS : 0 ;
64 	if (widget_checksize(d->h, d->w, &d->bs, MIN(*htext, 1), minw) != 0)
65 		return (BSDDIALOG_ERROR);
66 	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
67 		return (BSDDIALOG_ERROR);
68 
69 	return (0);
70 }
71 
72 static int message_draw(struct dialog *d, struct scroll *s)
73 {
74 	int unused;
75 
76 	if (d->built) {
77 		hide_dialog(d);
78 		refresh(); /* Important for decreasing screen */
79 	}
80 	if (message_size_position(d, &s->htext) != 0)
81 		return (BSDDIALOG_ERROR);
82 	if (draw_dialog(d) != 0)
83 		return (BSDDIALOG_ERROR);
84 	if (d->built)
85 		refresh(); /* Important to fix grey lines expanding screen */
86 
87 	s->printrows = d->h - BORDER - HBUTTONS - BORDER;
88 	s->ypad = 0;
89 	getmaxyx(d->textpad, s->htextpad, unused);
90 	unused++; /* fix unused error */
91 
92 	return (0);
93 }
94 
95 static int
96 do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols,
97     const char *oklabel, const char *cancellabel)
98 {
99 	bool loop;
100 	int retval;
101 	wint_t input;
102 	struct scroll s;
103 	struct dialog d;
104 
105 	if (prepare_dialog(conf, text, rows, cols, &d) != 0)
106 		return (BSDDIALOG_ERROR);
107 	set_buttons(&d, true, oklabel, cancellabel);
108 	s.htext = -1;
109 	if(message_draw(&d, &s) != 0)
110 		return (BSDDIALOG_ERROR);
111 
112 	loop = true;
113 	while (loop) {
114 		textupdate(&d, &s);
115 		doupdate();
116 		if (get_wch(&input) == ERR)
117 			continue;
118 		switch (input) {
119 		case KEY_ENTER:
120 		case 10: /* Enter */
121 			retval = BUTTONVALUE(d.bs);
122 			loop = false;
123 			break;
124 		case 27: /* Esc */
125 			if (d.conf->key.enable_esc) {
126 				retval = BSDDIALOG_ESC;
127 				loop = false;
128 			}
129 			break;
130 		case '\t': /* TAB */
131 		case KEY_RIGHT:
132 			d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
133 			DRAW_BUTTONS(d);
134 			break;
135 		case KEY_LEFT:
136 			d.bs.curr--;
137 			if (d.bs.curr < 0)
138 				 d.bs.curr = d.bs.nbuttons - 1;
139 			DRAW_BUTTONS(d);
140 			break;
141 		case KEY_UP:
142 			if (s.ypad > 0)
143 				s.ypad--;
144 			break;
145 		case KEY_DOWN:
146 			if (s.ypad + s.printrows < s.htextpad)
147 				s.ypad++;
148 			break;
149 		case KEY_HOME:
150 			s.ypad = 0;
151 			break;
152 		case KEY_END:
153 			s.ypad = MAX(s.htextpad - s.printrows, 0);
154 			break;
155 		case KEY_PPAGE:
156 			s.ypad = MAX(s.ypad - s.printrows, 0);
157 			break;
158 		case KEY_NPAGE:
159 			s.ypad += s.printrows;
160 			if (s.ypad + s.printrows > s.htextpad)
161 				s.ypad = s.htextpad - s.printrows;
162 			break;
163 		case KEY_F(1):
164 			if (d.conf->key.f1_file == NULL &&
165 			    d.conf->key.f1_message == NULL)
166 				break;
167 			if (f1help_dialog(d.conf) != 0)
168 				return (BSDDIALOG_ERROR);
169 			if(message_draw(&d, &s) != 0)
170 				return (BSDDIALOG_ERROR);
171 			break;
172 		case KEY_RESIZE:
173 			if(message_draw(&d, &s) != 0)
174 				return (BSDDIALOG_ERROR);
175 			break;
176 		default:
177 			if (shortcut_buttons(input, &d.bs)) {
178 				DRAW_BUTTONS(d);
179 				doupdate();
180 				retval = BUTTONVALUE(d.bs);
181 				loop = false;
182 			}
183 		}
184 	}
185 
186 	end_dialog(&d);
187 
188 	return (retval);
189 }
190 
191 /* API */
192 int
193 bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows,
194     int cols)
195 {
196 	return (do_message(conf, text, rows, cols, OK_LABEL, NULL));
197 }
198 
199 int
200 bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows,
201     int cols)
202 {
203 	return (do_message(conf, text, rows, cols, "Yes", "No"));
204 }
205 
206 int
207 bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
208     int cols)
209 {
210 	int htext;
211 	struct dialog d;
212 
213 	if (prepare_dialog(conf, text, rows, cols, &d) != 0)
214 		return (BSDDIALOG_ERROR);
215 	htext = -1;
216 	if (message_size_position(&d, &htext) != 0)
217 		return (BSDDIALOG_ERROR);
218 	if (draw_dialog(&d) != 0)
219 		return (BSDDIALOG_ERROR);
220 	TEXTPAD(&d, 0);
221 	doupdate();
222 
223 	end_dialog(&d);
224 
225 	return (BSDDIALOG_OK);
226 }
227