1 /* 2 * util.c 3 * 4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) 5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22 #include <stdarg.h> 23 24 #include "dialog.h" 25 26 /* Needed in signal handler in mconf.c */ 27 int saved_x, saved_y; 28 29 struct dialog_info dlg; 30 31 static void set_mono_theme(void) 32 { 33 dlg.screen.atr = A_NORMAL; 34 dlg.shadow.atr = A_NORMAL; 35 dlg.dialog.atr = A_NORMAL; 36 dlg.title.atr = A_BOLD; 37 dlg.border.atr = A_NORMAL; 38 dlg.button_active.atr = A_REVERSE; 39 dlg.button_inactive.atr = A_DIM; 40 dlg.button_key_active.atr = A_REVERSE; 41 dlg.button_key_inactive.atr = A_BOLD; 42 dlg.button_label_active.atr = A_REVERSE; 43 dlg.button_label_inactive.atr = A_NORMAL; 44 dlg.inputbox.atr = A_NORMAL; 45 dlg.inputbox_border.atr = A_NORMAL; 46 dlg.searchbox.atr = A_NORMAL; 47 dlg.searchbox_title.atr = A_BOLD; 48 dlg.searchbox_border.atr = A_NORMAL; 49 dlg.position_indicator.atr = A_BOLD; 50 dlg.menubox.atr = A_NORMAL; 51 dlg.menubox_border.atr = A_NORMAL; 52 dlg.item.atr = A_NORMAL; 53 dlg.item_selected.atr = A_REVERSE; 54 dlg.tag.atr = A_BOLD; 55 dlg.tag_selected.atr = A_REVERSE; 56 dlg.tag_key.atr = A_BOLD; 57 dlg.tag_key_selected.atr = A_REVERSE; 58 dlg.check.atr = A_BOLD; 59 dlg.check_selected.atr = A_REVERSE; 60 dlg.uarrow.atr = A_BOLD; 61 dlg.darrow.atr = A_BOLD; 62 } 63 64 #define DLG_COLOR(dialog, f, b, h) \ 65 do { \ 66 dlg.dialog.fg = (f); \ 67 dlg.dialog.bg = (b); \ 68 dlg.dialog.hl = (h); \ 69 } while (0) 70 71 static void set_classic_theme(void) 72 { 73 DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true); 74 DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true); 75 DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false); 76 DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true); 77 DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true); 78 DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true); 79 DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false); 80 DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true); 81 DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false); 82 DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true); 83 DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true); 84 DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false); 85 DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false); 86 DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false); 87 DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true); 88 DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true); 89 DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true); 90 DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false); 91 DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true); 92 DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false); 93 DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true); 94 DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true); 95 DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true); 96 DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true); 97 DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true); 98 DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false); 99 DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true); 100 DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true); 101 DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true); 102 } 103 104 static void set_blackbg_theme(void) 105 { 106 DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true); 107 DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false); 108 DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false); 109 DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false); 110 DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true); 111 112 DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false); 113 DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false); 114 DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true); 115 DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false); 116 DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false); 117 DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true); 118 119 DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false); 120 DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false); 121 122 DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false); 123 DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true); 124 DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true); 125 126 DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false); 127 128 DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false); 129 DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true); 130 131 DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false); 132 DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false); 133 134 DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false); 135 DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true); 136 DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false); 137 DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true); 138 139 DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false); 140 DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true); 141 142 DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false); 143 DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false); 144 } 145 146 static void set_bluetitle_theme(void) 147 { 148 set_classic_theme(); 149 DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true); 150 DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true); 151 DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true); 152 DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true); 153 DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true); 154 DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true); 155 DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true); 156 157 } 158 159 /* 160 * Select color theme 161 */ 162 static int set_theme(const char *theme) 163 { 164 int use_color = 1; 165 if (!theme) 166 set_bluetitle_theme(); 167 else if (strcmp(theme, "classic") == 0) 168 set_classic_theme(); 169 else if (strcmp(theme, "bluetitle") == 0) 170 set_bluetitle_theme(); 171 else if (strcmp(theme, "blackbg") == 0) 172 set_blackbg_theme(); 173 else if (strcmp(theme, "mono") == 0) 174 use_color = 0; 175 176 return use_color; 177 } 178 179 static void init_one_color(struct dialog_color *color) 180 { 181 static int pair = 0; 182 183 pair++; 184 init_pair(pair, color->fg, color->bg); 185 if (color->hl) 186 color->atr = A_BOLD | COLOR_PAIR(pair); 187 else 188 color->atr = COLOR_PAIR(pair); 189 } 190 191 static void init_dialog_colors(void) 192 { 193 init_one_color(&dlg.screen); 194 init_one_color(&dlg.shadow); 195 init_one_color(&dlg.dialog); 196 init_one_color(&dlg.title); 197 init_one_color(&dlg.border); 198 init_one_color(&dlg.button_active); 199 init_one_color(&dlg.button_inactive); 200 init_one_color(&dlg.button_key_active); 201 init_one_color(&dlg.button_key_inactive); 202 init_one_color(&dlg.button_label_active); 203 init_one_color(&dlg.button_label_inactive); 204 init_one_color(&dlg.inputbox); 205 init_one_color(&dlg.inputbox_border); 206 init_one_color(&dlg.searchbox); 207 init_one_color(&dlg.searchbox_title); 208 init_one_color(&dlg.searchbox_border); 209 init_one_color(&dlg.position_indicator); 210 init_one_color(&dlg.menubox); 211 init_one_color(&dlg.menubox_border); 212 init_one_color(&dlg.item); 213 init_one_color(&dlg.item_selected); 214 init_one_color(&dlg.tag); 215 init_one_color(&dlg.tag_selected); 216 init_one_color(&dlg.tag_key); 217 init_one_color(&dlg.tag_key_selected); 218 init_one_color(&dlg.check); 219 init_one_color(&dlg.check_selected); 220 init_one_color(&dlg.uarrow); 221 init_one_color(&dlg.darrow); 222 } 223 224 /* 225 * Setup for color display 226 */ 227 static void color_setup(const char *theme) 228 { 229 int use_color; 230 231 use_color = set_theme(theme); 232 if (use_color && has_colors()) { 233 start_color(); 234 init_dialog_colors(); 235 } else 236 set_mono_theme(); 237 } 238 239 /* 240 * Set window to attribute 'attr' 241 */ 242 void attr_clear(WINDOW * win, int height, int width, chtype attr) 243 { 244 int i, j; 245 246 wattrset(win, attr); 247 for (i = 0; i < height; i++) { 248 wmove(win, i, 0); 249 for (j = 0; j < width; j++) 250 waddch(win, ' '); 251 } 252 touchwin(win); 253 } 254 255 void dialog_clear(void) 256 { 257 attr_clear(stdscr, LINES, COLS, dlg.screen.atr); 258 /* Display background title if it exists ... - SLH */ 259 if (dlg.backtitle != NULL) { 260 int i, len = 0, skip = 0; 261 struct subtitle_list *pos; 262 263 wattrset(stdscr, dlg.screen.atr); 264 mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle); 265 266 for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { 267 /* 3 is for the arrow and spaces */ 268 len += strlen(pos->text) + 3; 269 } 270 271 wmove(stdscr, 1, 1); 272 if (len > COLS - 2) { 273 const char *ellipsis = "[...] "; 274 waddstr(stdscr, ellipsis); 275 skip = len - (COLS - 2 - strlen(ellipsis)); 276 } 277 278 for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { 279 if (skip == 0) 280 waddch(stdscr, ACS_RARROW); 281 else 282 skip--; 283 284 if (skip == 0) 285 waddch(stdscr, ' '); 286 else 287 skip--; 288 289 if (skip < strlen(pos->text)) { 290 waddstr(stdscr, pos->text + skip); 291 skip = 0; 292 } else 293 skip -= strlen(pos->text); 294 295 if (skip == 0) 296 waddch(stdscr, ' '); 297 else 298 skip--; 299 } 300 301 for (i = len + 1; i < COLS - 1; i++) 302 waddch(stdscr, ACS_HLINE); 303 } 304 wnoutrefresh(stdscr); 305 } 306 307 /* 308 * Do some initialization for dialog 309 */ 310 int init_dialog(const char *backtitle) 311 { 312 int height, width; 313 314 initscr(); /* Init curses */ 315 316 /* Get current cursor position for signal handler in mconf.c */ 317 getyx(stdscr, saved_y, saved_x); 318 319 getmaxyx(stdscr, height, width); 320 if (height < 19 || width < 80) { 321 endwin(); 322 return -ERRDISPLAYTOOSMALL; 323 } 324 325 dlg.backtitle = backtitle; 326 color_setup(getenv("MENUCONFIG_COLOR")); 327 328 keypad(stdscr, TRUE); 329 cbreak(); 330 noecho(); 331 dialog_clear(); 332 333 return 0; 334 } 335 336 void set_dialog_backtitle(const char *backtitle) 337 { 338 dlg.backtitle = backtitle; 339 } 340 341 void set_dialog_subtitles(struct subtitle_list *subtitles) 342 { 343 dlg.subtitles = subtitles; 344 } 345 346 /* 347 * End using dialog functions. 348 */ 349 void end_dialog(int x, int y) 350 { 351 /* move cursor back to original position */ 352 move(y, x); 353 refresh(); 354 endwin(); 355 } 356 357 /* Print the title of the dialog. Center the title and truncate 358 * tile if wider than dialog (- 2 chars). 359 **/ 360 void print_title(WINDOW *dialog, const char *title, int width) 361 { 362 if (title) { 363 int tlen = MIN(width - 2, strlen(title)); 364 wattrset(dialog, dlg.title.atr); 365 mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); 366 mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); 367 waddch(dialog, ' '); 368 } 369 } 370 371 /* 372 * Print a string of text in a window, automatically wrap around to the 373 * next line if the string is too long to fit on one line. Newline 374 * characters '\n' are replaced by spaces. We start on a new line 375 * if there is no room for at least 4 nonblanks following a double-space. 376 */ 377 void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) 378 { 379 int newl, cur_x, cur_y; 380 int i, prompt_len, room, wlen; 381 char tempstr[MAX_LEN + 1], *word, *sp, *sp2; 382 383 strcpy(tempstr, prompt); 384 385 prompt_len = strlen(tempstr); 386 387 /* 388 * Remove newlines 389 */ 390 for (i = 0; i < prompt_len; i++) { 391 if (tempstr[i] == '\n') 392 tempstr[i] = ' '; 393 } 394 395 if (prompt_len <= width - x * 2) { /* If prompt is short */ 396 wmove(win, y, (width - prompt_len) / 2); 397 waddstr(win, tempstr); 398 } else { 399 cur_x = x; 400 cur_y = y; 401 newl = 1; 402 word = tempstr; 403 while (word && *word) { 404 sp = strchr(word, ' '); 405 if (sp) 406 *sp++ = 0; 407 408 /* Wrap to next line if either the word does not fit, 409 or it is the first word of a new sentence, and it is 410 short, and the next word does not fit. */ 411 room = width - cur_x; 412 wlen = strlen(word); 413 if (wlen > room || 414 (newl && wlen < 4 && sp 415 && wlen + 1 + strlen(sp) > room 416 && (!(sp2 = strchr(sp, ' ')) 417 || wlen + 1 + (sp2 - sp) > room))) { 418 cur_y++; 419 cur_x = x; 420 } 421 wmove(win, cur_y, cur_x); 422 waddstr(win, word); 423 getyx(win, cur_y, cur_x); 424 cur_x++; 425 if (sp && *sp == ' ') { 426 cur_x++; /* double space */ 427 while (*++sp == ' ') ; 428 newl = 1; 429 } else 430 newl = 0; 431 word = sp; 432 } 433 } 434 } 435 436 /* 437 * Print a button 438 */ 439 void print_button(WINDOW * win, const char *label, int y, int x, int selected) 440 { 441 int i, temp; 442 443 wmove(win, y, x); 444 wattrset(win, selected ? dlg.button_active.atr 445 : dlg.button_inactive.atr); 446 waddstr(win, "<"); 447 temp = strspn(label, " "); 448 label += temp; 449 wattrset(win, selected ? dlg.button_label_active.atr 450 : dlg.button_label_inactive.atr); 451 for (i = 0; i < temp; i++) 452 waddch(win, ' '); 453 wattrset(win, selected ? dlg.button_key_active.atr 454 : dlg.button_key_inactive.atr); 455 waddch(win, label[0]); 456 wattrset(win, selected ? dlg.button_label_active.atr 457 : dlg.button_label_inactive.atr); 458 waddstr(win, (char *)label + 1); 459 wattrset(win, selected ? dlg.button_active.atr 460 : dlg.button_inactive.atr); 461 waddstr(win, ">"); 462 wmove(win, y, x + temp + 1); 463 } 464 465 /* 466 * Draw a rectangular box with line drawing characters 467 */ 468 void 469 draw_box(WINDOW * win, int y, int x, int height, int width, 470 chtype box, chtype border) 471 { 472 int i, j; 473 474 wattrset(win, 0); 475 for (i = 0; i < height; i++) { 476 wmove(win, y + i, x); 477 for (j = 0; j < width; j++) 478 if (!i && !j) 479 waddch(win, border | ACS_ULCORNER); 480 else if (i == height - 1 && !j) 481 waddch(win, border | ACS_LLCORNER); 482 else if (!i && j == width - 1) 483 waddch(win, box | ACS_URCORNER); 484 else if (i == height - 1 && j == width - 1) 485 waddch(win, box | ACS_LRCORNER); 486 else if (!i) 487 waddch(win, border | ACS_HLINE); 488 else if (i == height - 1) 489 waddch(win, box | ACS_HLINE); 490 else if (!j) 491 waddch(win, border | ACS_VLINE); 492 else if (j == width - 1) 493 waddch(win, box | ACS_VLINE); 494 else 495 waddch(win, box | ' '); 496 } 497 } 498 499 /* 500 * Draw shadows along the right and bottom edge to give a more 3D look 501 * to the boxes 502 */ 503 void draw_shadow(WINDOW * win, int y, int x, int height, int width) 504 { 505 int i; 506 507 if (has_colors()) { /* Whether terminal supports color? */ 508 wattrset(win, dlg.shadow.atr); 509 wmove(win, y + height, x + 2); 510 for (i = 0; i < width; i++) 511 waddch(win, winch(win) & A_CHARTEXT); 512 for (i = y + 1; i < y + height + 1; i++) { 513 wmove(win, i, x + width); 514 waddch(win, winch(win) & A_CHARTEXT); 515 waddch(win, winch(win) & A_CHARTEXT); 516 } 517 wnoutrefresh(win); 518 } 519 } 520 521 /* 522 * Return the position of the first alphabetic character in a string. 523 */ 524 int first_alpha(const char *string, const char *exempt) 525 { 526 int i, in_paren = 0, c; 527 528 for (i = 0; i < strlen(string); i++) { 529 c = tolower(string[i]); 530 531 if (strchr("<[(", c)) 532 ++in_paren; 533 if (strchr(">])", c) && in_paren > 0) 534 --in_paren; 535 536 if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) 537 return i; 538 } 539 540 return 0; 541 } 542 543 /* 544 * ncurses uses ESC to detect escaped char sequences. This resutl in 545 * a small timeout before ESC is actually delivered to the application. 546 * lxdialog suggest <ESC> <ESC> which is correctly translated to two 547 * times esc. But then we need to ignore the second esc to avoid stepping 548 * out one menu too much. Filter away all escaped key sequences since 549 * keypad(FALSE) turn off ncurses support for escape sequences - and thats 550 * needed to make notimeout() do as expected. 551 */ 552 int on_key_esc(WINDOW *win) 553 { 554 int key; 555 int key2; 556 int key3; 557 558 nodelay(win, TRUE); 559 keypad(win, FALSE); 560 key = wgetch(win); 561 key2 = wgetch(win); 562 do { 563 key3 = wgetch(win); 564 } while (key3 != ERR); 565 nodelay(win, FALSE); 566 keypad(win, TRUE); 567 if (key == KEY_ESC && key2 == ERR) 568 return KEY_ESC; 569 else if (key != ERR && key != KEY_ESC && key2 == ERR) 570 ungetch(key); 571 572 return -1; 573 } 574 575 /* redraw screen in new size */ 576 int on_key_resize(void) 577 { 578 dialog_clear(); 579 return KEY_RESIZE; 580 } 581 582 struct dialog_list *item_cur; 583 struct dialog_list item_nil; 584 struct dialog_list *item_head; 585 586 void item_reset(void) 587 { 588 struct dialog_list *p, *next; 589 590 for (p = item_head; p; p = next) { 591 next = p->next; 592 free(p); 593 } 594 item_head = NULL; 595 item_cur = &item_nil; 596 } 597 598 void item_make(const char *fmt, ...) 599 { 600 va_list ap; 601 struct dialog_list *p = malloc(sizeof(*p)); 602 603 if (item_head) 604 item_cur->next = p; 605 else 606 item_head = p; 607 item_cur = p; 608 memset(p, 0, sizeof(*p)); 609 610 va_start(ap, fmt); 611 vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap); 612 va_end(ap); 613 } 614 615 void item_add_str(const char *fmt, ...) 616 { 617 va_list ap; 618 size_t avail; 619 620 avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); 621 622 va_start(ap, fmt); 623 vsnprintf(item_cur->node.str + strlen(item_cur->node.str), 624 avail, fmt, ap); 625 item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0'; 626 va_end(ap); 627 } 628 629 void item_set_tag(char tag) 630 { 631 item_cur->node.tag = tag; 632 } 633 void item_set_data(void *ptr) 634 { 635 item_cur->node.data = ptr; 636 } 637 638 void item_set_selected(int val) 639 { 640 item_cur->node.selected = val; 641 } 642 643 int item_activate_selected(void) 644 { 645 item_foreach() 646 if (item_is_selected()) 647 return 1; 648 return 0; 649 } 650 651 void *item_data(void) 652 { 653 return item_cur->node.data; 654 } 655 656 char item_tag(void) 657 { 658 return item_cur->node.tag; 659 } 660 661 int item_count(void) 662 { 663 int n = 0; 664 struct dialog_list *p; 665 666 for (p = item_head; p; p = p->next) 667 n++; 668 return n; 669 } 670 671 void item_set(int n) 672 { 673 int i = 0; 674 item_foreach() 675 if (i++ == n) 676 return; 677 } 678 679 int item_n(void) 680 { 681 int n = 0; 682 struct dialog_list *p; 683 684 for (p = item_head; p; p = p->next) { 685 if (p == item_cur) 686 return n; 687 n++; 688 } 689 return 0; 690 } 691 692 const char *item_str(void) 693 { 694 return item_cur->node.str; 695 } 696 697 int item_is_selected(void) 698 { 699 return (item_cur->node.selected != 0); 700 } 701 702 int item_is_tag(char tag) 703 { 704 return (item_cur->node.tag == tag); 705 } 706