1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 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 29 #include <sys/param.h> 30 31 #include <string.h> 32 33 #ifdef PORTNCURSES 34 #include <ncurses/curses.h> 35 #else 36 #include <curses.h> 37 #endif 38 39 #include "bsddialog.h" 40 #include "lib_util.h" 41 #include "bsddialog_theme.h" 42 43 /* "Text": tailbox - tailboxbg - textbox */ 44 45 #define BUTTON_TEXTBOX "HELP" 46 47 extern struct bsddialog_theme t; 48 49 enum textmode { TAILMODE, TAILBGMODE, TEXTMODE}; 50 51 static void 52 textbox_autosize(struct bsddialog_conf conf, int rows, int cols, int *h, int *w, 53 int hpad, int wpad) 54 { 55 56 if (cols == BSDDIALOG_AUTOSIZE) { 57 *w = VBORDERS; 58 /* buttons size */ 59 *w += strlen(BUTTON_TEXTBOX) + 2 /* text delims*/; 60 /* text size */ 61 *w = MAX(*w, wpad + VBORDERS); 62 /* avoid terminal overflow */ 63 *w = MIN(*w, widget_max_width(conf)-1); /* again -1, fix util.c */ 64 } 65 66 if (rows == BSDDIALOG_AUTOSIZE) { 67 *h = hpad + 4; /* HBORDERS + button border */ 68 /* avoid terminal overflow */ 69 *h = MIN(*h, widget_max_height(conf)); 70 } 71 } 72 73 static int textbox_checksize(int rows, int cols, int hpad, int wpad) 74 { 75 int mincols; 76 77 mincols = VBORDERS + strlen(BUTTON_TEXTBOX) + 2 /* text delims */; 78 79 if (cols < mincols) 80 RETURN_ERROR("Few cols for the textbox"); 81 82 if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0)) 83 RETURN_ERROR("Few rows for the textbox"); 84 85 return 0; 86 } 87 88 static int 89 do_textbox(enum textmode mode, struct bsddialog_conf conf, char* path, int rows, int cols) 90 { 91 WINDOW *widget, *pad, *shadow; 92 int i, input, y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows; 93 char buf[BUFSIZ], *exitbutt; 94 FILE *fp; 95 bool loop; 96 int output; 97 98 if (mode == TAILMODE || mode == TAILBGMODE) { 99 bsddialog_msgbox(conf, "Tailbox and Tailboxbg unimplemented", rows, cols); 100 RETURN_ERROR("Tailbox and Tailboxbg unimplemented"); 101 } 102 103 if ((fp = fopen(path, "r")) == NULL) 104 RETURN_ERROR("Cannot open file"); 105 /*if (mode == TAILMODE) { 106 fseek (fp, 0, SEEK_END); 107 i = nlines = 0; 108 while (i < hpad) { 109 line = ; 110 } 111 for (i=hpad-1; i--; i>=0) { 112 } 113 }*/ 114 hpad = 1; 115 wpad = 1; 116 pad = newpad(hpad, wpad); 117 wbkgd(pad, t.widgetcolor); 118 i = 0; 119 while(fgets(buf, BUFSIZ, fp) != NULL) { 120 if ((int) strlen(buf) > wpad) { 121 wpad = strlen(buf); 122 wresize(pad, hpad, wpad); 123 } 124 if (i > hpad-1) { 125 hpad++; 126 wresize(pad, hpad, wpad); 127 } 128 mvwaddstr(pad, i, 0, buf); 129 i++; 130 } 131 fclose(fp); 132 133 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 134 return BSDDIALOG_ERROR; 135 textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad); 136 if (textbox_checksize(h, w, hpad, wpad) != 0) 137 return BSDDIALOG_ERROR; 138 if (set_widget_position(conf, &y, &x, h, w) != 0) 139 return BSDDIALOG_ERROR; 140 141 if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 142 NULL, NULL, NULL, true) != 0) 143 return BSDDIALOG_ERROR; 144 145 exitbutt = conf.button.exit_label == NULL ? BUTTON_TEXTBOX : conf.button.exit_label; 146 draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, strlen(exitbutt)+2, 147 exitbutt, true, true); 148 149 wrefresh(widget); 150 151 ys = y + 1; 152 xs = x + 1; 153 ye = ys + h - 5; 154 xe = xs + w - 3; 155 ypad = xpad = 0; 156 printrows = h-4; 157 loop = true; 158 while(loop) { 159 prefresh(pad, ypad, xpad, ys, xs, ye, xe); 160 input = getch(); 161 switch(input) { 162 case KEY_ENTER: 163 case 10: /* Enter */ 164 output = BSDDIALOG_YESOK; 165 loop = false; 166 break; 167 case 27: /* Esc */ 168 output = BSDDIALOG_ESC; 169 loop = false; 170 break; 171 case KEY_HOME: 172 ypad = 0; 173 break; 174 case KEY_END: 175 ypad = hpad - printrows; 176 ypad = ypad < 0 ? 0 : ypad; 177 break; 178 case KEY_PPAGE: 179 ypad -= printrows; 180 ypad = ypad < 0 ? 0 : ypad; 181 break; 182 case KEY_NPAGE: 183 ypad += printrows; 184 ypad = ypad + printrows > hpad ? hpad - printrows : ypad; 185 break; 186 case '0': 187 xpad = 0; 188 case KEY_LEFT: 189 case 'h': 190 xpad = xpad > 0 ? xpad - 1 : 0; 191 break; 192 case KEY_RIGHT: 193 case 'l': 194 xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad; 195 break; 196 case KEY_UP: 197 case 'k': 198 ypad = ypad > 0 ? ypad - 1 : 0; 199 break; 200 case KEY_DOWN: 201 case'j': 202 ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad; 203 break; 204 case KEY_F(1): 205 if (conf.hfile == NULL) 206 break; 207 if (f1help(conf) != 0) 208 return BSDDIALOG_ERROR; 209 /* No break! the terminal size can change */ 210 case KEY_RESIZE: 211 hide_widget(y, x, h, w,conf.shadow); 212 213 /* 214 * Unnecessary, but, when the columns decrease the 215 * following "refresh" seem not work 216 */ 217 refresh(); 218 219 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 220 return BSDDIALOG_ERROR; 221 textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad); 222 if (textbox_checksize(h, w, hpad, wpad) != 0) 223 return BSDDIALOG_ERROR; 224 if (set_widget_position(conf, &y, &x, h, w) != 0) 225 return BSDDIALOG_ERROR; 226 227 wclear(shadow); 228 mvwin(shadow, y + t.shadowrows, x + t.shadowcols); 229 wresize(shadow, h, w); 230 231 wclear(widget); 232 mvwin(widget, y, x); 233 wresize(widget, h, w); 234 235 ys = y + 1; 236 xs = x + 1; 237 ye = ys + h - 5; 238 xe = xs + w - 3; 239 ypad = xpad = 0; 240 printrows = h - 4; 241 242 if(update_widget_withtextpad(conf, shadow, widget, h, w, 243 RAISED, NULL, NULL, NULL, true) != 0) 244 return BSDDIALOG_ERROR; 245 246 draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, 247 strlen(exitbutt)+2, exitbutt, true, true); 248 249 wrefresh(widget); /* for button */ 250 251 /* Important to fix grey lines expanding screen */ 252 refresh(); 253 break; 254 } 255 } 256 257 end_widget_withtextpad(conf, widget, h, w, pad, shadow); 258 259 return output; 260 } 261 262 int bsddialog_tailbox(struct bsddialog_conf conf, char* text, int rows, int cols) 263 { 264 265 return (do_textbox(TAILMODE, conf, text, rows, cols)); 266 } 267 268 int bsddialog_tailboxbg(struct bsddialog_conf conf, char* text, int rows, int cols) 269 { 270 271 return (do_textbox(TAILBGMODE, conf, text, rows, cols)); 272 } 273 274 275 int bsddialog_textbox(struct bsddialog_conf conf, char* text, int rows, int cols) 276 { 277 278 return (do_textbox(TEXTMODE, conf, text, rows, cols)); 279 } 280 281