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 #include <stdlib.h> 29 30 #ifdef PORTNCURSES 31 #include <ncurses/curses.h> 32 #include <ncurses/form.h> 33 #else 34 #include <curses.h> 35 #include <form.h> 36 #endif 37 38 #include "bsddialog.h" 39 #include "lib_util.h" 40 #include "bsddialog_theme.h" 41 42 /* "Form": inputbox - passwordbox - form - passwordform - mixedform */ 43 44 extern struct bsddialog_theme t; 45 46 int bsddialog_inputmenu(struct bsddialog_conf conf, char* text, int rows, int cols) 47 { 48 text = "Inputbox unimplemented"; 49 bsddialog_msgbox(conf, text, rows, cols); 50 RETURN_ERROR(text); 51 } 52 53 #define ITEMHIDDEN 0x1 54 #define ISITEMHIDDEN(item) (item.itemflags & 0x1) 55 #define ITEMREADONLY 0x2 56 #define ISITEMREADONLY(item) (item.itemflags & 0x2) 57 struct formitem { 58 char *label; 59 unsigned int ylabel; 60 unsigned int xlabel; 61 char *item; 62 unsigned int yitem; 63 unsigned int xitem; 64 int itemlen; 65 unsigned int inputlen; 66 unsigned int itemflags; 67 }; 68 69 static int 70 mixedform_handler(WINDOW *widget, int y, int cols, struct buttons bs, 71 bool shortkey, WINDOW *entry, FORM *form, FIELD **field, int nitems 72 /*struct formitem *items*/) 73 { 74 bool loop, buttupdate, inentry = true; 75 int input, output; 76 77 curs_set(2); 78 pos_form_cursor(form); 79 loop = buttupdate = true; 80 bs.curr = -1; 81 while(loop) { 82 if (buttupdate) { 83 draw_buttons(widget, y, cols, bs, shortkey); 84 wrefresh(widget); 85 buttupdate = false; 86 } 87 wrefresh(entry); 88 input = getch(); 89 switch(input) { 90 case 10: // Enter 91 if (inentry) 92 break; 93 output = bs.value[bs.curr]; // values -> buttvalues 94 form_driver(form, REQ_NEXT_FIELD); 95 form_driver(form, REQ_PREV_FIELD); 96 /* add a struct for forms */ 97 /*for (i=0; i<nitems; i++) { 98 bufp = field_buffer(field[i], 0); 99 dprintf(fd, "\n+%s", bufp); 100 bufp = field_buffer(field[i], 1); 101 dprintf(fd, "-%s+", bufp); 102 }*/ 103 loop = false; 104 break; 105 case 27: /* Esc */ 106 output = BSDDIALOG_ESC; 107 loop = false; 108 break; 109 case '\t': // TAB 110 if (inentry) { 111 bs.curr = 0; 112 inentry = false; 113 curs_set(0); 114 } else { 115 bs.curr++; 116 inentry = bs.curr >= (int) bs.nbuttons ? true : false; 117 if (inentry) { 118 curs_set(2); 119 pos_form_cursor(form); 120 } 121 } 122 buttupdate = true; 123 break; 124 case KEY_LEFT: 125 if (inentry) { 126 form_driver(form, REQ_PREV_CHAR); 127 } else { 128 if (bs.curr > 0) { 129 bs.curr--; 130 buttupdate = true; 131 } 132 } 133 break; 134 case KEY_RIGHT: 135 if (inentry) { 136 form_driver(form, REQ_NEXT_CHAR); 137 } else { 138 if (bs.curr < (int) bs.nbuttons - 1) { 139 bs.curr++; 140 buttupdate = true; 141 } 142 } 143 break; 144 case KEY_UP: 145 if (nitems < 2) 146 break; 147 set_field_fore(current_field(form), t.fieldcolor); 148 set_field_back(current_field(form), t.fieldcolor); 149 form_driver(form, REQ_PREV_FIELD); 150 form_driver(form, REQ_END_LINE); 151 set_field_fore(current_field(form), t.currfieldcolor); 152 set_field_back(current_field(form), t.currfieldcolor); 153 break; 154 case KEY_DOWN: 155 if (nitems < 2) 156 break; 157 set_field_fore(current_field(form), t.fieldcolor); 158 set_field_back(current_field(form), t.fieldcolor); 159 form_driver(form, REQ_NEXT_FIELD); 160 form_driver(form, REQ_END_LINE); 161 set_field_fore(current_field(form), t.currfieldcolor); 162 set_field_back(current_field(form), t.currfieldcolor); 163 break; 164 case KEY_BACKSPACE: 165 form_driver(form, REQ_DEL_PREV); 166 break; 167 case KEY_DC: 168 form_driver(form, REQ_DEL_CHAR); 169 break; 170 default: 171 if (inentry) { 172 form_driver(form, input); 173 } 174 break; 175 } 176 } 177 178 curs_set(0); 179 180 return output; 181 } 182 183 static int 184 do_mixedform(struct bsddialog_conf conf, char* text, int rows, int cols, 185 int formheight, int nitems, struct formitem *items) 186 { 187 WINDOW *widget, *entry, *shadow; 188 int i, output, color, y, x; 189 FIELD **field; 190 FORM *form; 191 struct buttons bs; 192 193 if (new_widget(conf, &widget, &y, &x, text, &rows, &cols, &shadow, 194 true) <0) 195 return -1; 196 197 entry = new_boxed_window(conf, y + rows - 3 - formheight -2, x +1, 198 formheight+2, cols-2, LOWERED); 199 200 get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), 201 BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); 202 203 field = calloc(nitems + 1, sizeof(FIELD*)); 204 for (i=0; i < nitems; i++) { 205 field[i] = new_field(1, items[i].itemlen, items[i].yitem-1, items[i].xitem-1, 0, 1); 206 field_opts_off(field[i], O_STATIC); 207 set_max_field(field[i], items[i].inputlen); 208 set_field_buffer(field[i], 0, items[i].item); 209 set_field_buffer(field[i], 1, items[i].item); 210 field_opts_off(field[i], O_AUTOSKIP); 211 field_opts_off(field[i], O_BLANK); 212 //field_opts_off(field[i], O_BS_OVERLOAD); 213 214 if (ISITEMHIDDEN(items[i])) 215 field_opts_off(field[i], O_PUBLIC); 216 217 if (ISITEMREADONLY(items[i])) { 218 field_opts_off(field[i], O_EDIT); 219 field_opts_off(field[i], O_ACTIVE); 220 color = t.fieldreadonlycolor; 221 } else { 222 color = i == 0 ? t.currfieldcolor : t.fieldcolor; 223 } 224 set_field_fore(field[i], color); 225 set_field_back(field[i], color); 226 } 227 field[i] = NULL; 228 229 if (nitems == 1) {// inputbox or passwordbox 230 set_field_fore(field[0], t.widgetcolor); 231 set_field_back(field[0], t.widgetcolor); 232 } 233 234 form = new_form(field); 235 set_form_win(form, entry); 236 set_form_sub(form, derwin(entry, nitems, cols-4, 1, 1)); 237 post_form(form); 238 239 for (i=0; i < nitems; i++) 240 mvwaddstr(entry, items[i].ylabel, items[i].xlabel, items[i].label); 241 242 wrefresh(entry); 243 244 output = mixedform_handler(widget, rows-2, cols, bs, true, entry, form, 245 field, nitems /*,items*/); 246 247 unpost_form(form); 248 free_form(form); 249 for (i=0; i < nitems; i++) 250 free_field(field[i]); 251 free(field); 252 253 delwin(entry); 254 end_widget(conf, widget, rows, cols, shadow); 255 256 return output; 257 } 258 259 int bsddialog_inputbox(struct bsddialog_conf conf, char* text, int rows, int cols) 260 { 261 int output; 262 struct formitem item; 263 264 item.label = ""; 265 item.ylabel = 0; 266 item.xlabel = 0; 267 item.item = ""; // TODO add argv 268 item.yitem = 1; 269 item.xitem = 1; 270 item.itemlen = cols-4; 271 item.inputlen = 2048; // todo conf.sizeinput 272 item.itemflags = 0; 273 274 output = do_mixedform(conf, text, rows, cols, 1, 1, &item); 275 276 return output; 277 } 278 279 int bsddialog_passwordbox(struct bsddialog_conf conf, char* text, int rows, int cols) 280 { 281 int output; 282 struct formitem item; 283 284 item.label = ""; 285 item.ylabel = 0; 286 item.xlabel = 0; 287 item.item = ""; // TODO add argv 288 item.yitem = 1; 289 item.xitem = 1; 290 item.itemlen = cols-4; 291 item.inputlen = 2048; // todo conf.sizeinput 292 item.itemflags = ITEMHIDDEN; 293 294 output = do_mixedform(conf, text, rows, cols, 1, 1, &item); 295 296 return output; 297 } 298 299 int 300 bsddialog_mixedform(struct bsddialog_conf conf, char* text, int rows, int cols, 301 int formheight, int argc, char **argv) 302 { 303 int i, output, nitems; 304 struct formitem items[128]; 305 306 if ((argc % 9) != 0) 307 return (-1); 308 309 nitems = argc / 9; 310 for (i=0; i<nitems; i++) { 311 items[i].label = argv[9*i]; 312 items[i].ylabel = atoi(argv[9*i+1]); 313 items[i].xlabel = atoi(argv[9*i+2]); 314 items[i].item = argv[9*i+3]; 315 items[i].yitem = atoi(argv[9*i+4]); 316 items[i].xitem = atoi(argv[9*i+5]); 317 items[i].itemlen = atoi(argv[9*i+6]); 318 items[i].inputlen = atoi(argv[9*i+7]); 319 items[i].itemflags = atoi(argv[9*i+8]); 320 } 321 322 output = do_mixedform(conf, text, rows, cols, formheight, nitems, items); 323 324 return output; 325 } 326 327 int 328 bsddialog_form(struct bsddialog_conf conf, char* text, int rows, int cols, 329 int formheight, int argc, char **argv) 330 { 331 int i, output, nitems, itemlen, inputlen; 332 unsigned int flags = 0; 333 struct formitem items[128]; 334 335 if ((argc % 8) != 0) 336 return (-1); 337 338 nitems = argc / 8; 339 for (i=0; i<nitems; i++) { 340 items[i].label = argv[8*i]; 341 items[i].ylabel = atoi(argv[8*i+1]); 342 items[i].xlabel = atoi(argv[8*i+2]); 343 items[i].item = argv[8*i+3]; 344 items[i].yitem = atoi(argv[8*i+4]); 345 items[i].xitem = atoi(argv[8*i+5]); 346 347 itemlen = atoi(argv[8*i+6]); 348 items[i].itemlen = abs(itemlen); 349 350 inputlen = atoi(argv[8*i+7]); 351 items[i].inputlen = inputlen == 0 ? abs(itemlen) : inputlen; 352 353 flags = flags | (itemlen < 0 ? ITEMREADONLY : 0); 354 items[i].itemflags = flags; 355 } 356 357 output = do_mixedform(conf, text, rows, cols, formheight, nitems, items); 358 359 return output; 360 } 361 362 int 363 bsddialog_passwordform(struct bsddialog_conf conf, char* text, int rows, int cols, 364 int formheight, int argc, char **argv) 365 { 366 int i, output, nitems, itemlen, inputlen; 367 unsigned int flags = ITEMHIDDEN; 368 struct formitem items[128]; 369 370 if ((argc % 8) != 0) 371 return (-1); 372 373 nitems = argc / 8; 374 for (i=0; i<nitems; i++) { 375 items[i].label = argv[8*i]; 376 items[i].ylabel = atoi(argv[8*i+1]); 377 items[i].xlabel = atoi(argv[8*i+2]); 378 items[i].item = argv[8*i+3]; 379 items[i].yitem = atoi(argv[8*i+4]); 380 items[i].xitem = atoi(argv[8*i+5]); 381 382 itemlen = atoi(argv[8*i+6]); 383 items[i].itemlen = abs(itemlen); 384 385 inputlen = atoi(argv[8*i+7]); 386 items[i].inputlen = inputlen == 0 ? abs(itemlen) : inputlen; 387 388 flags = flags | (itemlen < 0 ? ITEMREADONLY : 0); 389 items[i].itemflags = flags; 390 } 391 392 output = do_mixedform(conf, text, rows, cols, formheight, nitems, items); 393 394 return output; 395 } 396 397