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 <stdlib.h> 32 #include <string.h> 33 #include <wchar.h> 34 35 #include "bsddialog.h" 36 #include "bsddialog_theme.h" 37 #include "lib_util.h" 38 39 static void 40 updateborders(struct bsddialog_conf *conf, WINDOW *widget, int padmargin, 41 int hpad, int wpad, int ypad, int xpad) 42 { 43 int h, w; 44 chtype arrowch, borderch; 45 46 getmaxyx(widget, h, w); 47 48 if (conf->no_lines) 49 borderch = ' '; 50 else if (conf->ascii_lines) 51 borderch = '|'; 52 else 53 borderch = ACS_VLINE; 54 55 if (xpad > 0) { 56 arrowch = conf->ascii_lines ? '<' : ACS_LARROW; 57 arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor; 58 } else { 59 arrowch = borderch; 60 arrowch |= A_ATTRIBUTES & t.dialog.lineraisecolor; 61 } 62 mvwvline(widget, (h / 2) - 2, 0, arrowch, 4); 63 64 if (xpad + w-2-padmargin < wpad) { 65 arrowch = conf->ascii_lines ? '>' : ACS_RARROW; 66 arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor; 67 } else { 68 arrowch = borderch; 69 arrowch |= A_ATTRIBUTES & t.dialog.linelowercolor; 70 } 71 mvwvline(widget, (h / 2) - 2, w - 1, arrowch, 4); 72 73 if (hpad > h - 4) { 74 wattron(widget, t.dialog.arrowcolor); 75 mvwprintw(widget, h-3, w-6, "%3d%%", 100 * (ypad+h-4)/ hpad); 76 wattroff(widget, t.dialog.arrowcolor); 77 } 78 } 79 80 static void 81 textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, 82 int *w, int hpad, int wpad, int padmargin, struct buttons bs) 83 { 84 if (cols == BSDDIALOG_AUTOSIZE) 85 *w = widget_min_width(conf, 0, wpad + padmargin, &bs); 86 87 if (rows == BSDDIALOG_AUTOSIZE) 88 *h = widget_min_height(conf, 0, hpad, true); 89 } 90 91 static int 92 textbox_checksize(int rows, int cols, int hpad, struct buttons bs) 93 { 94 int mincols; 95 96 mincols = VBORDERS; 97 mincols += buttons_min_width(bs); 98 99 if (cols < mincols) 100 RETURN_ERROR("Few cols for the textbox"); 101 102 if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0)) 103 RETURN_ERROR("Few rows for the textbox"); 104 105 return (0); 106 } 107 108 /* API */ 109 int 110 bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, 111 int cols) 112 { 113 bool loop, has_multi_col; 114 int i, retval, y, x, h, w; 115 int hpad, wpad, ypad, xpad, ys, ye, xs, xe, padmargin, printrows; 116 unsigned int defaulttablen, linecols; 117 wint_t input; 118 char buf[BUFSIZ]; 119 FILE *fp; 120 struct buttons bs; 121 WINDOW *shadow, *widget, *pad; 122 123 if ((fp = fopen(file, "r")) == NULL) 124 RETURN_ERROR("Cannot open file"); 125 126 defaulttablen = TABSIZE; 127 set_tabsize((conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen); 128 hpad = 1; 129 wpad = 1; 130 pad = newpad(hpad, wpad); 131 wbkgd(pad, t.dialog.color); 132 padmargin = 0; 133 i = 0; 134 while (fgets(buf, BUFSIZ, fp) != NULL) { 135 if (str_props(buf, &linecols, &has_multi_col) != 0) 136 continue; 137 if ((int)linecols > wpad) { 138 wpad = linecols; 139 wresize(pad, hpad, wpad); 140 } 141 if (i > hpad-1) { 142 hpad++; 143 wresize(pad, hpad, wpad); 144 } 145 mvwaddstr(pad, i, 0, buf); 146 i++; 147 if (has_multi_col) 148 padmargin = 2; 149 } 150 fclose(fp); 151 set_tabsize(defaulttablen); 152 153 get_buttons(conf, &bs, "EXIT", NULL); 154 bs.curr = 0; 155 bs.nbuttons = 1; 156 157 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 158 return (BSDDIALOG_ERROR); 159 textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, padmargin, bs); 160 if (textbox_checksize(h, w, hpad, bs) != 0) 161 return (BSDDIALOG_ERROR); 162 if (set_widget_position(conf, &y, &x, h, w) != 0) 163 return (BSDDIALOG_ERROR); 164 165 if (new_dialog(conf, &shadow, &widget, y, x, h, w, NULL, NULL, &bs, 166 true) != 0) 167 return (BSDDIALOG_ERROR); 168 169 ys = y + 1; 170 xs = (padmargin == 0) ? x + 1 : x + 2; 171 ye = ys + h - 5; 172 xe = xs + w - 3 - padmargin; 173 ypad = xpad = 0; 174 printrows = h-4; 175 loop = true; 176 while (loop) { 177 updateborders(conf, widget, padmargin, hpad, wpad, ypad, xpad); 178 /* 179 * Overflow multicolumn charchter right border: 180 * wnoutrefresh(widget); 181 * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); 182 * doupdate(); 183 */ 184 wrefresh(widget); 185 prefresh(pad, ypad, xpad, ys, xs, ye, xe); 186 if (get_wch(&input) == ERR) 187 continue; 188 switch(input) { 189 case KEY_ENTER: 190 case 10: /* Enter */ 191 retval = BSDDIALOG_OK; 192 loop = false; 193 break; 194 case 27: /* Esc */ 195 if (conf->key.enable_esc) { 196 retval = BSDDIALOG_ESC; 197 loop = false; 198 } 199 break; 200 case KEY_HOME: 201 ypad = 0; 202 break; 203 case KEY_END: 204 ypad = hpad - printrows; 205 ypad = ypad < 0 ? 0 : ypad; 206 break; 207 case KEY_PPAGE: 208 ypad -= printrows; 209 ypad = ypad < 0 ? 0 : ypad; 210 break; 211 case KEY_NPAGE: 212 ypad += printrows; 213 if (ypad + printrows > hpad) 214 ypad = hpad - printrows; 215 break; 216 case '0': 217 xpad = 0; 218 case KEY_LEFT: 219 case 'h': 220 xpad = xpad > 0 ? xpad - 1 : 0; 221 break; 222 case KEY_RIGHT: 223 case 'l': 224 xpad = (xpad + w-2-padmargin) < wpad ? xpad + 1 : xpad; 225 break; 226 case KEY_UP: 227 case 'k': 228 ypad = ypad > 0 ? ypad - 1 : 0; 229 break; 230 case KEY_DOWN: 231 case'j': 232 ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad; 233 break; 234 case KEY_F(1): 235 if (conf->key.f1_file == NULL && 236 conf->key.f1_message == NULL) 237 break; 238 if (f1help(conf) != 0) 239 return (BSDDIALOG_ERROR); 240 /* No break, screen size can change */ 241 case KEY_RESIZE: 242 /* Important for decreasing screen */ 243 hide_widget(y, x, h, w, conf->shadow); 244 refresh(); 245 246 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 247 return (BSDDIALOG_ERROR); 248 textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, 249 padmargin, bs); 250 if (textbox_checksize(h, w, hpad, bs) != 0) 251 return (BSDDIALOG_ERROR); 252 if (set_widget_position(conf, &y, &x, h, w) != 0) 253 return (BSDDIALOG_ERROR); 254 255 ys = y + 1; 256 xs = (padmargin == 0) ? x + 1 : x + 2; 257 ye = ys + h - 5; 258 xe = xs + w - 3 - padmargin; 259 ypad = xpad = 0; 260 printrows = h - 4; 261 262 if (update_dialog(conf, shadow, widget, y, x, h, w, 263 NULL, NULL, &bs, true) != 0) 264 return (BSDDIALOG_ERROR); 265 266 /* Important to fix grey lines expanding screen */ 267 refresh(); 268 break; 269 default: 270 if (shortcut_buttons(input, &bs)) { 271 retval = bs.value[bs.curr]; 272 loop = false; 273 } 274 } 275 } 276 277 end_dialog(conf, shadow, widget, pad); 278 279 return (retval); 280 } 281