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