1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021-2024 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
textupdate(struct dialog * d,struct scroll * s)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
message_size_position(struct dialog * d,int * htext)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
message_draw(struct dialog * d,struct scroll * s)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 (void)unused; /* fix unused error */
91
92 return (0);
93 }
94
95 static int
do_message(struct bsddialog_conf * conf,const char * text,int rows,int cols,const char * oklabel,const char * cancellabel)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 '-':
142 case KEY_CTRL('p'):
143 case KEY_UP:
144 if (s.ypad > 0)
145 s.ypad--;
146 break;
147 case '+':
148 case KEY_CTRL('n'):
149 case KEY_DOWN:
150 if (s.ypad + s.printrows < s.htextpad)
151 s.ypad++;
152 break;
153 case KEY_HOME:
154 s.ypad = 0;
155 break;
156 case KEY_END:
157 s.ypad = MAX(s.htextpad - s.printrows, 0);
158 break;
159 case KEY_PPAGE:
160 s.ypad = MAX(s.ypad - s.printrows, 0);
161 break;
162 case KEY_NPAGE:
163 s.ypad += s.printrows;
164 if (s.ypad + s.printrows > s.htextpad)
165 s.ypad = s.htextpad - s.printrows;
166 break;
167 case KEY_F(1):
168 if (d.conf->key.f1_file == NULL &&
169 d.conf->key.f1_message == NULL)
170 break;
171 if (f1help_dialog(d.conf) != 0)
172 return (BSDDIALOG_ERROR);
173 if (message_draw(&d, &s) != 0)
174 return (BSDDIALOG_ERROR);
175 break;
176 case KEY_CTRL('l'):
177 case KEY_RESIZE:
178 if (message_draw(&d, &s) != 0)
179 return (BSDDIALOG_ERROR);
180 break;
181 default:
182 if (shortcut_buttons(input, &d.bs)) {
183 DRAW_BUTTONS(d);
184 doupdate();
185 retval = BUTTONVALUE(d.bs);
186 loop = false;
187 }
188 }
189 }
190
191 end_dialog(&d);
192
193 return (retval);
194 }
195
196 /* API */
197 int
bsddialog_msgbox(struct bsddialog_conf * conf,const char * text,int rows,int cols)198 bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows,
199 int cols)
200 {
201 return (do_message(conf, text, rows, cols, OK_LABEL, NULL));
202 }
203
204 int
bsddialog_yesno(struct bsddialog_conf * conf,const char * text,int rows,int cols)205 bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows,
206 int cols)
207 {
208 return (do_message(conf, text, rows, cols, "Yes", "No"));
209 }
210
211 int
bsddialog_infobox(struct bsddialog_conf * conf,const char * text,int rows,int cols)212 bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
213 int cols)
214 {
215 int htext;
216 struct dialog d;
217
218 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
219 return (BSDDIALOG_ERROR);
220 htext = -1;
221 if (message_size_position(&d, &htext) != 0)
222 return (BSDDIALOG_ERROR);
223 if (draw_dialog(&d) != 0)
224 return (BSDDIALOG_ERROR);
225 TEXTPAD(&d, 0);
226 doupdate();
227
228 end_dialog(&d);
229
230 return (BSDDIALOG_OK);
231 }
232