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