1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2022 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 <sys/param.h> 29 30 #include <curses.h> 31 #include <string.h> 32 33 #include "bsddialog.h" 34 #include "bsddialog_theme.h" 35 #include "lib_util.h" 36 37 extern struct bsddialog_theme t; 38 39 static int 40 message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, 41 int *w, const char *text, struct buttons bs) 42 { 43 int htext, wtext; 44 45 if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { 46 if (text_size(conf, rows, cols, text, &bs, 0, SCREENCOLS/2, 47 &htext, &wtext) != 0) 48 return (BSDDIALOG_ERROR); 49 } 50 51 if (cols == BSDDIALOG_AUTOSIZE) 52 *w = widget_min_width(conf, wtext, 0, &bs); 53 54 if (rows == BSDDIALOG_AUTOSIZE) 55 *h = widget_min_height(conf, htext, 0, true); 56 57 return (0); 58 } 59 60 static int message_checksize(int rows, int cols, struct buttons bs) 61 { 62 int mincols; 63 64 mincols = VBORDERS; 65 mincols += bs.nbuttons * bs.sizebutton; 66 mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; 67 68 if (cols < mincols) 69 RETURN_ERROR("Few cols, Msgbox and Yesno need at least width " 70 "for borders, buttons and spaces between buttons"); 71 72 if (rows < HBORDERS + 2 /*buttons*/) 73 RETURN_ERROR("Msgbox and Yesno need at least height 4"); 74 75 return (0); 76 } 77 78 static void 79 textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad) 80 { 81 int y, x, h, w; 82 83 getbegyx(widget, y, x); 84 getmaxyx(widget, h, w); 85 86 if (htextpad > h - 4) { 87 mvwprintw(widget, h-3, w-6, "%3d%%", 88 100 * (ytextpad+h-4)/ htextpad); 89 wnoutrefresh(widget); 90 } 91 92 pnoutrefresh(textpad, ytextpad, 0, y+1, x+2, y+h-4, x+w-2); 93 } 94 95 static int 96 do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, 97 struct buttons bs) 98 { 99 bool loop; 100 int y, x, h, w, input, output, ytextpad, htextpad, unused; 101 WINDOW *widget, *textpad, *shadow; 102 103 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 104 return (BSDDIALOG_ERROR); 105 if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) 106 return (BSDDIALOG_ERROR); 107 if (message_checksize(h, w, bs) != 0) 108 return (BSDDIALOG_ERROR); 109 if (set_widget_position(conf, &y, &x, h, w) != 0) 110 return (BSDDIALOG_ERROR); 111 112 if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, 113 true) != 0) 114 return (BSDDIALOG_ERROR); 115 116 ytextpad = 0; 117 getmaxyx(textpad, htextpad, unused); 118 unused++; /* fix unused error */ 119 textupdate(widget, textpad, htextpad, ytextpad); 120 loop = true; 121 while (loop) { 122 doupdate(); 123 input = getch(); 124 switch (input) { 125 case KEY_ENTER: 126 case 10: /* Enter */ 127 output = bs.value[bs.curr]; 128 loop = false; 129 break; 130 case 27: /* Esc */ 131 if (conf->key.enable_esc) { 132 output = BSDDIALOG_ESC; 133 loop = false; 134 } 135 break; 136 case '\t': /* TAB */ 137 bs.curr = (bs.curr + 1) % bs.nbuttons; 138 draw_buttons(widget, bs, true); 139 wnoutrefresh(widget); 140 break; 141 case KEY_LEFT: 142 if (bs.curr > 0) { 143 bs.curr--; 144 draw_buttons(widget, bs, true); 145 wnoutrefresh(widget); 146 } 147 break; 148 case KEY_RIGHT: 149 if (bs.curr < (int)bs.nbuttons - 1) { 150 bs.curr++; 151 draw_buttons(widget, bs, true); 152 wnoutrefresh(widget); 153 } 154 break; 155 case KEY_F(1): 156 if (conf->f1_file == NULL && conf->f1_message == NULL) 157 break; 158 if (f1help(conf) != 0) 159 return (BSDDIALOG_ERROR); 160 /* No break, screen size can change */ 161 case KEY_RESIZE: 162 /* Important for decreasing screen */ 163 hide_widget(y, x, h, w, conf->shadow); 164 refresh(); 165 166 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 167 return (BSDDIALOG_ERROR); 168 if (message_autosize(conf, rows, cols, &h, &w, text, 169 bs) != 0) 170 return (BSDDIALOG_ERROR); 171 if (message_checksize(h, w, bs) != 0) 172 return (BSDDIALOG_ERROR); 173 if (set_widget_position(conf, &y, &x, h, w) != 0) 174 return (BSDDIALOG_ERROR); 175 176 if (update_dialog(conf, shadow, widget, y, x, h, w, 177 textpad, text, &bs, true) != 0) 178 return (BSDDIALOG_ERROR); 179 180 getmaxyx(textpad, htextpad, unused); 181 textupdate(widget, textpad, htextpad, ytextpad); 182 183 /* Important to fix grey lines expanding screen */ 184 refresh(); 185 break; 186 case KEY_UP: 187 if (ytextpad == 0) 188 break; 189 ytextpad--; 190 textupdate(widget, textpad, htextpad, ytextpad); 191 break; 192 case KEY_DOWN: 193 if (ytextpad + h - 4 >= htextpad) 194 break; 195 ytextpad++; 196 textupdate(widget, textpad, htextpad, ytextpad); 197 break; 198 default: 199 if (shortcut_buttons(input, &bs)) { 200 output = bs.value[bs.curr]; 201 loop = false; 202 } 203 } 204 } 205 206 end_dialog(conf, shadow, widget, textpad); 207 208 return (output); 209 } 210 211 /* API */ 212 int 213 bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, 214 int cols) 215 { 216 struct buttons bs; 217 218 get_buttons(conf, &bs, BUTTON_OK_LABEL, NULL); 219 220 return (do_message(conf, text, rows, cols, bs)); 221 } 222 223 int 224 bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows, 225 int cols) 226 { 227 struct buttons bs; 228 229 get_buttons(conf, &bs, "Yes", "No"); 230 231 return (do_message(conf, text, rows, cols, bs)); 232 }