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