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 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 (void)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 '-': 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 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 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 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