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