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 <ctype.h> 31 #include <curses.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #include "bsddialog.h" 36 #include "bsddialog_theme.h" 37 #include "lib_util.h" 38 39 #define DEPTH 2 40 #define MIN_HEIGHT VBORDERS + 6 /* 2 buttons 1 text 3 menu */ 41 42 enum menumode { 43 CHECKLISTMODE, 44 MENUMODE, 45 MIXEDLISTMODE, 46 RADIOLISTMODE, 47 SEPARATORMODE 48 }; 49 50 struct lineposition { 51 unsigned int maxsepstr; 52 unsigned int maxprefix; 53 unsigned int xselector; 54 unsigned int selectorlen; 55 unsigned int maxdepth; 56 unsigned int xname; 57 unsigned int maxname; 58 unsigned int xdesc; 59 unsigned int maxdesc; 60 unsigned int line; 61 }; 62 63 struct privateitem { 64 bool on; 65 int group; 66 int index; 67 enum menumode type; 68 struct bsddialog_menuitem *item; 69 }; 70 71 static void 72 set_on_output(struct bsddialog_conf *conf, int output, int ngroups, 73 struct bsddialog_menugroup *groups, struct privateitem *pritems) 74 { 75 int i, j, abs; 76 77 if (output != BSDDIALOG_OK && !conf->menu.on_without_ok) 78 return; 79 80 for(i = abs = 0; i < ngroups; i++) { 81 if (groups[i].type == BSDDIALOG_SEPARATOR) { 82 abs += groups[i].nitems; 83 continue; 84 } 85 86 for(j = 0; j < (int)groups[i].nitems; j++) { 87 groups[i].items[j].on = pritems[abs].on; 88 abs++; 89 } 90 } 91 } 92 93 static int getprev(struct privateitem *pritems, int abs) 94 { 95 int i; 96 97 for (i = abs - 1; i >= 0; i--) { 98 if (pritems[i].type == SEPARATORMODE) 99 continue; 100 return (i); 101 } 102 103 return (abs); 104 } 105 106 static int getnext(int npritems, struct privateitem *pritems, int abs) 107 { 108 int i; 109 110 for (i = abs + 1; i < npritems; i++) { 111 if (pritems[i].type == SEPARATORMODE) 112 continue; 113 return (i); 114 } 115 116 return (abs); 117 } 118 119 static int 120 getfirst_with_default(int npritems, struct privateitem *pritems, int ngroups, 121 struct bsddialog_menugroup *groups, int *focusgroup, int *focusitem) 122 { 123 int i, abs; 124 125 if ((abs = getnext(npritems, pritems, -1)) < 0) 126 return (abs); 127 128 if (focusgroup == NULL || focusitem == NULL) 129 return (abs); 130 if (*focusgroup < 0 || *focusgroup >= ngroups) 131 return (abs); 132 if (groups[*focusgroup].type == BSDDIALOG_SEPARATOR) 133 return (abs); 134 if (*focusitem < 0 || *focusitem >= (int)groups[*focusgroup].nitems) 135 return (abs); 136 137 for (i = abs; i < npritems; i++) { 138 if (pritems[i].group == *focusgroup && 139 pritems[i].index == *focusitem) 140 return (i); 141 } 142 143 return (abs); 144 } 145 146 static int 147 getfastnext(int menurows, int npritems, struct privateitem *pritems, int abs) 148 { 149 int a, start, i; 150 151 start = abs; 152 i = menurows; 153 do { 154 a = abs; 155 abs = getnext(npritems, pritems, abs); 156 i--; 157 } while (abs != a && abs < start + menurows && i > 0); 158 159 return (abs); 160 } 161 162 static int 163 getfastprev(int menurows, struct privateitem *pritems, int abs) 164 { 165 int a, start, i; 166 167 start = abs; 168 i = menurows; 169 do { 170 a = abs; 171 abs = getprev(pritems, abs); 172 i--; 173 } while (abs != a && abs > start - menurows && i > 0); 174 175 return (abs); 176 } 177 178 static int 179 getnextshortcut(struct bsddialog_conf *conf, int npritems, 180 struct privateitem *pritems, int abs, int key) 181 { 182 int i, ch, next; 183 184 next = -1; 185 for (i = 0; i < npritems; i++) { 186 if (pritems[i].type == SEPARATORMODE) 187 continue; 188 189 if (conf->menu.no_name) 190 ch = pritems[i].item->desc[0]; 191 else 192 ch = pritems[i].item->name[0]; 193 194 if (ch == key) { 195 if (i > abs) 196 return (i); 197 198 if (i < abs && next == -1) 199 next = i; 200 } 201 } 202 203 return (next != -1 ? next : abs); 204 } 205 206 static enum menumode 207 getmode(enum menumode mode, struct bsddialog_menugroup group) 208 { 209 if (mode == MIXEDLISTMODE) { 210 if (group.type == BSDDIALOG_SEPARATOR) 211 mode = SEPARATORMODE; 212 else if (group.type == BSDDIALOG_RADIOLIST) 213 mode = RADIOLISTMODE; 214 else if (group.type == BSDDIALOG_CHECKLIST) 215 mode = CHECKLISTMODE; 216 } 217 218 return (mode); 219 } 220 221 static void 222 drawseparators(struct bsddialog_conf *conf, WINDOW *pad, int linelen, 223 int nitems, struct privateitem *pritems) 224 { 225 int i, linech, labellen; 226 const char *desc, *name; 227 228 for (i = 0; i < nitems; i++) { 229 if (pritems[i].type != SEPARATORMODE) 230 continue; 231 if (conf->no_lines == false) { 232 wattron(pad, t.menu.desccolor); 233 linech = conf->ascii_lines ? '-' : ACS_HLINE; 234 mvwhline(pad, i, 0, linech, linelen); 235 wattroff(pad, t.menu.desccolor); 236 } 237 name = pritems[i].item->name; 238 desc = pritems[i].item->desc; 239 labellen = strlen(name) + strlen(desc) + 1; 240 wmove(pad, i, labellen < linelen ? linelen/2 - labellen/2 : 0); 241 wattron(pad, t.menu.namesepcolor); 242 waddstr(pad, name); 243 wattroff(pad, t.menu.namesepcolor); 244 if (strlen(name) > 0 && strlen(desc) > 0) 245 waddch(pad, ' '); 246 wattron(pad, t.menu.descsepcolor); 247 waddstr(pad, desc); 248 wattroff(pad, t.menu.descsepcolor); 249 } 250 } 251 252 static void 253 drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, 254 struct lineposition pos, struct privateitem *pritem, bool focus) 255 { 256 int colordesc, colorname, colorshortcut; 257 const char *shortcut; 258 struct bsddialog_menuitem *item; 259 260 item = pritem->item; 261 262 /* prefix */ 263 if (item->prefix != NULL && item->prefix[0] != '\0') 264 mvwaddstr(pad, y, 0, item->prefix); 265 266 /* selector */ 267 wmove(pad, y, pos.xselector); 268 wattron(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); 269 if (pritem->type == CHECKLISTMODE) 270 wprintw(pad, "[%c]", pritem->on ? 'X' : ' '); 271 if (pritem->type == RADIOLISTMODE) 272 wprintw(pad, "(%c)", pritem->on ? '*' : ' '); 273 wattroff(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); 274 275 /* name */ 276 colorname = focus ? t.menu.f_namecolor : t.menu.namecolor; 277 if (conf->menu.no_name == false) { 278 wattron(pad, colorname); 279 mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, item->name); 280 wattroff(pad, colorname); 281 } 282 283 /* description */ 284 if (conf->menu.no_name) 285 colordesc = focus ? t.menu.f_namecolor : t.menu.namecolor; 286 else 287 colordesc = focus ? t.menu.f_desccolor : t.menu.desccolor; 288 289 if (conf->menu.no_desc == false) { 290 wattron(pad, colordesc); 291 if (conf->menu.no_name) 292 mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, 293 item->desc); 294 else 295 mvwaddstr(pad, y, pos.xdesc, item->desc); 296 wattroff(pad, colordesc); 297 } 298 299 /* shortcut */ 300 if (conf->menu.shortcut_buttons == false) { 301 colorshortcut = focus ? 302 t.menu.f_shortcutcolor : t.menu.shortcutcolor; 303 wattron(pad, colorshortcut); 304 305 if (conf->menu.no_name) 306 shortcut = item->desc; 307 else 308 shortcut = item->name; 309 wmove(pad, y, pos.xname + item->depth * DEPTH); 310 if (shortcut != NULL && shortcut[0] != '\0') 311 waddch(pad, shortcut[0]); 312 wattroff(pad, colorshortcut); 313 } 314 315 /* bottom description */ 316 move(SCREENLINES - 1, 2); 317 clrtoeol(); 318 if (item->bottomdesc != NULL && focus) { 319 addstr(item->bottomdesc); 320 refresh(); 321 } 322 } 323 324 static int 325 menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, 326 const char *text, int linelen, unsigned int *menurows, int nitems, 327 struct buttons bs) 328 { 329 int htext, wtext, menusize, notext; 330 331 notext = 2; 332 if (*menurows == BSDDIALOG_AUTOSIZE) { 333 /* algo 1): grows vertically */ 334 /* notext = 1; */ 335 /* algo 2): grows horizontally, better with little screens */ 336 notext += nitems; 337 notext = MIN(notext, widget_max_height(conf) - HBORDERS - 3); 338 } else 339 notext += *menurows; 340 341 /* cols autosize, rows autosize, rows fullscreen, menu particularity */ 342 if (cols == BSDDIALOG_AUTOSIZE || rows <= BSDDIALOG_AUTOSIZE) { 343 if (text_size(conf, rows, cols, text, &bs, notext, linelen + 4, 344 &htext, &wtext) != 0) 345 return (BSDDIALOG_ERROR); 346 } 347 348 if (cols == BSDDIALOG_AUTOSIZE) 349 *w = widget_min_width(conf, wtext, linelen + 4, &bs); 350 351 if (rows == BSDDIALOG_AUTOSIZE) { 352 if (*menurows == 0) { 353 menusize = widget_max_height(conf) - HBORDERS - 354 2 /*buttons*/ - htext; 355 menusize = MIN(menusize, nitems + 2); 356 *menurows = menusize - 2 < 0 ? 0 : menusize - 2; 357 } 358 else /* h autosize with fixed menurows */ 359 menusize = *menurows + 2; 360 361 *h = widget_min_height(conf, htext, menusize, true); 362 /* 363 * avoid menurows overflow and 364 * with rows=AUTOSIZE menurows!=0 becomes max-menurows 365 */ 366 *menurows = MIN(*h - 6 - htext, (int)*menurows); 367 } else { 368 if (*menurows == 0) 369 *menurows = MIN(*h-6-htext, nitems); 370 } 371 372 return (0); 373 } 374 375 static int 376 menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, 377 struct buttons bs) 378 { 379 int mincols, textrow, menusize; 380 381 mincols = VBORDERS; 382 /* buttons */ 383 mincols += buttons_width(bs); 384 385 /* 386 * linelen check, comment to allow some hidden col otherwise portconfig 387 * could not show big menus like www/apache24 388 */ 389 /* mincols = MAX(mincols, linelen); */ 390 391 if (cols < mincols) 392 RETURN_ERROR("Few cols, width < size buttons or " 393 "name + descripion of the items"); 394 395 textrow = text != NULL && strlen(text) > 0 ? 1 : 0; 396 397 if (nitems > 0 && menurows == 0) 398 RETURN_ERROR("items > 0 but menurows == 0, probably terminal " 399 "too small"); 400 401 menusize = nitems > 0 ? 3 : 0; 402 if (rows < 2 + 2 + menusize + textrow) 403 RETURN_ERROR("Few lines for this menus"); 404 405 return (0); 406 } 407 408 /* the caller has to call prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); */ 409 static void 410 update_menuwin(struct bsddialog_conf *conf, WINDOW *menuwin, int h, int w, 411 int totnitems, unsigned int menurows, int ymenupad) 412 { 413 draw_borders(conf, menuwin, h, w, LOWERED); 414 415 if (totnitems > (int)menurows) { 416 wattron(menuwin, t.dialog.arrowcolor); 417 418 if (ymenupad > 0) 419 mvwprintw(menuwin, 0, 2, "^^^"); 420 421 if ((ymenupad + (int)menurows) < totnitems) 422 mvwprintw(menuwin, h-1, 2, "vvv"); 423 424 wattroff(menuwin, t.dialog.arrowcolor); 425 426 mvwprintw(menuwin, h-1, w-10, "%3d%%", 427 100 * (ymenupad + menurows) / totnitems); 428 } 429 } 430 431 static int 432 do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, 433 unsigned int menurows, enum menumode mode, unsigned int ngroups, 434 struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) 435 { 436 bool loop, onetrue, movefocus, automenurows, shortcut_butts; 437 int i, j, y, x, h, w, output, input; 438 int ymenupad, ys, ye, xs, xe, abs, next, totnitems; 439 WINDOW *shadow, *widget, *textpad, *menuwin, *menupad; 440 struct buttons bs; 441 struct lineposition pos = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 442 struct bsddialog_menuitem *item; 443 struct privateitem *pritems; 444 445 shortcut_butts = conf->menu.shortcut_buttons; 446 447 automenurows = (menurows == BSDDIALOG_AUTOSIZE) ? true : false; 448 449 totnitems = 0; 450 for (i = 0; i < (int)ngroups; i++) { 451 if (getmode(mode, groups[i]) == RADIOLISTMODE || 452 getmode(mode, groups[i]) == CHECKLISTMODE) 453 pos.selectorlen = 3; 454 455 for (j = 0; j < (int)groups[i].nitems; j++) { 456 totnitems++; 457 item = &groups[i].items[j]; 458 459 if (groups[i].type == BSDDIALOG_SEPARATOR) { 460 pos.maxsepstr = MAX(pos.maxsepstr, 461 strlen(item->name) + strlen(item->desc)); 462 continue; 463 } 464 465 pos.maxprefix = MAX(pos.maxprefix,strlen(item->prefix)); 466 pos.maxdepth = MAX(pos.maxdepth, item->depth); 467 pos.maxname = MAX(pos.maxname, strlen(item->name)); 468 pos.maxdesc = MAX(pos.maxdesc, strlen(item->desc)); 469 } 470 } 471 pos.maxname = conf->menu.no_name ? 0 : pos.maxname; 472 pos.maxdesc = conf->menu.no_desc ? 0 : pos.maxdesc; 473 pos.maxdepth = DEPTH * pos.maxdepth; 474 475 pos.xselector = pos.maxprefix + (pos.maxprefix != 0 ? 1 : 0); 476 pos.xname = pos.xselector + pos.selectorlen + 477 (pos.selectorlen > 0 ? 1 : 0); 478 pos.xdesc = pos.maxdepth + pos.xname + pos.maxname; 479 pos.xdesc += (pos.maxname != 0 ? 1 : 0); 480 pos.line = MAX(pos.maxsepstr + 3, pos.xdesc + pos.maxdesc); 481 482 483 get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); 484 485 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 486 return (BSDDIALOG_ERROR); 487 if (menu_autosize(conf, rows, cols, &h, &w, text, pos.line, &menurows, 488 totnitems, bs) != 0) 489 return (BSDDIALOG_ERROR); 490 if (menu_checksize(h, w, text, menurows, totnitems, bs) != 0) 491 return (BSDDIALOG_ERROR); 492 if (set_widget_position(conf, &y, &x, h, w) != 0) 493 return (BSDDIALOG_ERROR); 494 495 if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, 496 shortcut_butts) != 0) 497 return (BSDDIALOG_ERROR); 498 499 doupdate(); 500 501 prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, 502 y + h - menurows, x + 1 + w - TEXTHMARGIN); 503 504 menuwin = new_boxed_window(conf, y + h - 5 - menurows, x + 2, 505 menurows+2, w-4, LOWERED); 506 507 menupad = newpad(totnitems, pos.line); 508 wbkgd(menupad, t.dialog.color); 509 510 if ((pritems = calloc(totnitems, sizeof (struct privateitem))) == NULL) 511 RETURN_ERROR("Cannot allocate memory for internal menu items"); 512 513 abs = 0; 514 for (i = 0; i < (int)ngroups; i++) { 515 onetrue = false; 516 for (j = 0; j < (int)groups[i].nitems; j++) { 517 item = &groups[i].items[j]; 518 519 if (getmode(mode, groups[i]) == MENUMODE) { 520 pritems[abs].on = false; 521 } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { 522 pritems[abs].on = onetrue ? false : item->on; 523 if (pritems[abs].on) 524 onetrue = true; 525 } else { 526 pritems[abs].on = item->on; 527 } 528 pritems[abs].group = i; 529 pritems[abs].index = j; 530 pritems[abs].type = getmode(mode, groups[i]); 531 pritems[abs].item = item; 532 533 drawitem(conf, menupad, abs, pos, &pritems[abs], false); 534 abs++; 535 } 536 } 537 drawseparators(conf, menupad, MIN((int)pos.line, w-6), totnitems, 538 pritems); 539 abs = getfirst_with_default(totnitems, pritems, ngroups, groups, 540 focuslist, focusitem); 541 if (abs >= 0) 542 drawitem(conf, menupad, abs, pos, &pritems[abs], true); 543 544 ys = y + h - 5 - menurows + 1; 545 ye = y + h - 5 ; 546 if (conf->menu.align_left || (int)pos.line > w - 6) { 547 xs = x + 3; 548 xe = xs + w - 7; 549 } else { /* center */ 550 xs = x + 3 + (w-6)/2 - pos.line/2; 551 xe = xs + w - 5; 552 } 553 554 ymenupad = 0; 555 if ((int)(ymenupad + menurows) - 1 < abs) 556 ymenupad = abs - menurows + 1; 557 update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, menurows, 558 ymenupad); 559 wrefresh(menuwin); 560 prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); 561 562 movefocus = false; 563 loop = true; 564 while (loop) { 565 input = getch(); 566 switch(input) { 567 case KEY_ENTER: 568 case 10: /* Enter */ 569 output = bs.value[bs.curr]; 570 if (abs >= 0 && pritems[abs].type == MENUMODE) 571 pritems[abs].on = true; 572 set_on_output(conf, output, ngroups, groups, pritems); 573 loop = false; 574 break; 575 case 27: /* Esc */ 576 if (conf->key.enable_esc) { 577 output = BSDDIALOG_ESC; 578 if (abs >= 0 && pritems[abs].type == MENUMODE) 579 pritems[abs].on = true; 580 set_on_output(conf, output, ngroups, groups, 581 pritems); 582 loop = false; 583 } 584 break; 585 case '\t': /* TAB */ 586 bs.curr = (bs.curr + 1) % bs.nbuttons; 587 draw_buttons(widget, bs, shortcut_butts); 588 wrefresh(widget); 589 break; 590 case KEY_LEFT: 591 if (bs.curr > 0) { 592 bs.curr--; 593 draw_buttons(widget, bs, shortcut_butts); 594 wrefresh(widget); 595 } 596 break; 597 case KEY_RIGHT: 598 if (bs.curr < (int) bs.nbuttons - 1) { 599 bs.curr++; 600 draw_buttons(widget, bs, shortcut_butts); 601 wrefresh(widget); 602 } 603 break; 604 case KEY_F(1): 605 if (conf->key.f1_file == NULL && 606 conf->key.f1_message == NULL) 607 break; 608 if (f1help(conf) != 0) 609 return (BSDDIALOG_ERROR); 610 /* No break, screen size can change */ 611 case KEY_RESIZE: 612 /* Important for decreasing screen */ 613 hide_widget(y, x, h, w, conf->shadow); 614 refresh(); 615 616 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 617 return (BSDDIALOG_ERROR); 618 menurows = automenurows ? 0 : menurows; 619 if (menu_autosize(conf, rows, cols, &h, &w, text, 620 pos.line, &menurows, totnitems, bs) != 0) 621 return (BSDDIALOG_ERROR); 622 if (menu_checksize(h, w, text, menurows, totnitems, 623 bs) != 0) 624 return (BSDDIALOG_ERROR); 625 if (set_widget_position(conf, &y, &x, h, w) != 0) 626 return (BSDDIALOG_ERROR); 627 628 if (update_dialog(conf, shadow, widget, y, x, h, w, 629 textpad, text, &bs, shortcut_butts) != 0) 630 return (BSDDIALOG_ERROR); 631 632 doupdate(); 633 634 prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, 635 y + h - menurows, x + 1 + w - TEXTHMARGIN); 636 637 wclear(menuwin); 638 mvwin(menuwin, y + h - 5 - menurows, x + 2); 639 wresize(menuwin,menurows+2, w-4); 640 update_menuwin(conf, menuwin, menurows+2, w-4, 641 totnitems, menurows, ymenupad); 642 wrefresh(menuwin); 643 644 ys = y + h - 5 - menurows + 1; 645 ye = y + h - 5 ; 646 if (conf->menu.align_left || (int)pos.line > w - 6) { 647 xs = x + 3; 648 xe = xs + w - 7; 649 } else { /* center */ 650 xs = x + 3 + (w-6)/2 - pos.line/2; 651 xe = xs + w - 5; 652 } 653 654 drawseparators(conf, menupad, MIN((int)pos.line, w-6), 655 totnitems, pritems); 656 657 if ((int)(ymenupad + menurows) - 1 < abs) 658 ymenupad = abs - menurows + 1; 659 prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); 660 661 refresh(); 662 663 break; 664 } 665 666 if (abs < 0) 667 continue; 668 switch(input) { 669 case KEY_HOME: 670 next = getnext(totnitems, pritems, -1); 671 movefocus = next != abs; 672 break; 673 case KEY_UP: 674 next = getprev(pritems, abs); 675 movefocus = next != abs; 676 break; 677 case KEY_PPAGE: 678 next = getfastprev(menurows, pritems, abs); 679 movefocus = next != abs; 680 break; 681 case KEY_END: 682 next = getprev(pritems, totnitems); 683 movefocus = next != abs; 684 break; 685 case KEY_DOWN: 686 next = getnext(totnitems, pritems, abs); 687 movefocus = next != abs; 688 break; 689 case KEY_NPAGE: 690 next = getfastnext(menurows, totnitems, pritems, abs); 691 movefocus = next != abs; 692 break; 693 case ' ': /* Space */ 694 if (pritems[abs].type == MENUMODE) 695 break; 696 else if (pritems[abs].type == CHECKLISTMODE) 697 pritems[abs].on = !pritems[abs].on; 698 else { /* RADIOLISTMODE */ 699 for (i = abs - pritems[abs].index; 700 i < totnitems && 701 pritems[i].group == pritems[abs].group; 702 i++) { 703 if (i != abs && pritems[i].on) { 704 pritems[i].on = false; 705 drawitem(conf, menupad, i, pos, 706 &pritems[i], false); 707 } 708 } 709 pritems[abs].on = !pritems[abs].on; 710 } 711 drawitem(conf, menupad, abs, pos, &pritems[abs], true); 712 prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); 713 break; 714 default: 715 if (shortcut_butts) { 716 if (shortcut_buttons(input, &bs)) { 717 output = bs.value[bs.curr]; 718 if (pritems[abs].type == MENUMODE) 719 pritems[abs].on = true; 720 set_on_output(conf, output, ngroups, 721 groups, pritems); 722 loop = false; 723 } 724 break; 725 } 726 727 /* shourtcut items */ 728 next = getnextshortcut(conf, totnitems, pritems, abs, 729 input); 730 movefocus = next != abs; 731 } 732 733 if (movefocus) { 734 drawitem(conf, menupad, abs, pos, &pritems[abs], false); 735 abs = next; 736 drawitem(conf, menupad, abs, pos, &pritems[abs], true); 737 if (ymenupad > abs && ymenupad > 0) 738 ymenupad = abs; 739 if ((int)(ymenupad + menurows) <= abs) 740 ymenupad = abs - menurows + 1; 741 update_menuwin(conf, menuwin, menurows+2, w-4, 742 totnitems, menurows, ymenupad); 743 wrefresh(menuwin); 744 prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); 745 movefocus = false; 746 } 747 } 748 749 if (focuslist != NULL) 750 *focuslist = abs < 0 ? -1 : pritems[abs].group; 751 if (focusitem !=NULL) 752 *focusitem = abs < 0 ? -1 : pritems[abs].index; 753 754 delwin(menupad); 755 delwin(menuwin); 756 end_dialog(conf, shadow, widget, textpad); 757 free(pritems); 758 759 return (output); 760 } 761 762 /* API */ 763 int 764 bsddialog_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, 765 int cols, unsigned int menurows, unsigned int ngroups, 766 struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) 767 { 768 int output; 769 770 output = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, 771 ngroups, groups, focuslist, focusitem); 772 773 return (output); 774 } 775 776 int 777 bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, 778 int cols, unsigned int menurows, unsigned int nitems, 779 struct bsddialog_menuitem *items, int *focusitem) 780 { 781 int output, focuslist = 0; 782 struct bsddialog_menugroup group = { 783 BSDDIALOG_CHECKLIST /* unused */, nitems, items}; 784 785 output = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, 786 1, &group, &focuslist, focusitem); 787 788 return (output); 789 } 790 791 int 792 bsddialog_menu(struct bsddialog_conf *conf, const char *text, int rows, 793 int cols, unsigned int menurows, unsigned int nitems, 794 struct bsddialog_menuitem *items, int *focusitem) 795 { 796 int output, focuslist = 0; 797 struct bsddialog_menugroup group = { 798 BSDDIALOG_CHECKLIST /* unused */, nitems, items}; 799 800 output = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, 801 &group, &focuslist, focusitem); 802 803 return (output); 804 } 805 806 int 807 bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, 808 int cols, unsigned int menurows, unsigned int nitems, 809 struct bsddialog_menuitem *items, int *focusitem) 810 { 811 int output, focuslist = 0; 812 struct bsddialog_menugroup group = { 813 BSDDIALOG_RADIOLIST /* unused */, nitems, items}; 814 815 output = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, 816 1, &group, &focuslist, focusitem); 817 818 return (output); 819 } 820