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; 261 262 wattrset(stdscr, dlg.screen.atr); 263 mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle); 264 wmove(stdscr, 1, 1); 265 for (i = 1; i < COLS - 1; i++) 266 waddch(stdscr, ACS_HLINE); 267 } 268 wnoutrefresh(stdscr); 269 } 270 271 /* 272 * Do some initialization for dialog 273 */ 274 int init_dialog(const char *backtitle) 275 { 276 int height, width; 277 278 initscr(); /* Init curses */ 279 280 /* Get current cursor position for signal handler in mconf.c */ 281 getyx(stdscr, saved_y, saved_x); 282 283 getmaxyx(stdscr, height, width); 284 if (height < 19 || width < 80) { 285 endwin(); 286 return -ERRDISPLAYTOOSMALL; 287 } 288 289 dlg.backtitle = backtitle; 290 color_setup(getenv("MENUCONFIG_COLOR")); 291 292 keypad(stdscr, TRUE); 293 cbreak(); 294 noecho(); 295 dialog_clear(); 296 297 return 0; 298 } 299 300 void set_dialog_backtitle(const char *backtitle) 301 { 302 dlg.backtitle = backtitle; 303 } 304 305 /* 306 * End using dialog functions. 307 */ 308 void end_dialog(int x, int y) 309 { 310 /* move cursor back to original position */ 311 move(y, x); 312 refresh(); 313 endwin(); 314 } 315 316 /* Print the title of the dialog. Center the title and truncate 317 * tile if wider than dialog (- 2 chars). 318 **/ 319 void print_title(WINDOW *dialog, const char *title, int width) 320 { 321 if (title) { 322 int tlen = MIN(width - 2, strlen(title)); 323 wattrset(dialog, dlg.title.atr); 324 mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); 325 mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); 326 waddch(dialog, ' '); 327 } 328 } 329 330 /* 331 * Print a string of text in a window, automatically wrap around to the 332 * next line if the string is too long to fit on one line. Newline 333 * characters '\n' are replaced by spaces. We start on a new line 334 * if there is no room for at least 4 nonblanks following a double-space. 335 */ 336 void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) 337 { 338 int newl, cur_x, cur_y; 339 int i, prompt_len, room, wlen; 340 char tempstr[MAX_LEN + 1], *word, *sp, *sp2; 341 342 strcpy(tempstr, prompt); 343 344 prompt_len = strlen(tempstr); 345 346 /* 347 * Remove newlines 348 */ 349 for (i = 0; i < prompt_len; i++) { 350 if (tempstr[i] == '\n') 351 tempstr[i] = ' '; 352 } 353 354 if (prompt_len <= width - x * 2) { /* If prompt is short */ 355 wmove(win, y, (width - prompt_len) / 2); 356 waddstr(win, tempstr); 357 } else { 358 cur_x = x; 359 cur_y = y; 360 newl = 1; 361 word = tempstr; 362 while (word && *word) { 363 sp = strchr(word, ' '); 364 if (sp) 365 *sp++ = 0; 366 367 /* Wrap to next line if either the word does not fit, 368 or it is the first word of a new sentence, and it is 369 short, and the next word does not fit. */ 370 room = width - cur_x; 371 wlen = strlen(word); 372 if (wlen > room || 373 (newl && wlen < 4 && sp 374 && wlen + 1 + strlen(sp) > room 375 && (!(sp2 = strchr(sp, ' ')) 376 || wlen + 1 + (sp2 - sp) > room))) { 377 cur_y++; 378 cur_x = x; 379 } 380 wmove(win, cur_y, cur_x); 381 waddstr(win, word); 382 getyx(win, cur_y, cur_x); 383 cur_x++; 384 if (sp && *sp == ' ') { 385 cur_x++; /* double space */ 386 while (*++sp == ' ') ; 387 newl = 1; 388 } else 389 newl = 0; 390 word = sp; 391 } 392 } 393 } 394 395 /* 396 * Print a button 397 */ 398 void print_button(WINDOW * win, const char *label, int y, int x, int selected) 399 { 400 int i, temp; 401 402 wmove(win, y, x); 403 wattrset(win, selected ? dlg.button_active.atr 404 : dlg.button_inactive.atr); 405 waddstr(win, "<"); 406 temp = strspn(label, " "); 407 label += temp; 408 wattrset(win, selected ? dlg.button_label_active.atr 409 : dlg.button_label_inactive.atr); 410 for (i = 0; i < temp; i++) 411 waddch(win, ' '); 412 wattrset(win, selected ? dlg.button_key_active.atr 413 : dlg.button_key_inactive.atr); 414 waddch(win, label[0]); 415 wattrset(win, selected ? dlg.button_label_active.atr 416 : dlg.button_label_inactive.atr); 417 waddstr(win, (char *)label + 1); 418 wattrset(win, selected ? dlg.button_active.atr 419 : dlg.button_inactive.atr); 420 waddstr(win, ">"); 421 wmove(win, y, x + temp + 1); 422 } 423 424 /* 425 * Draw a rectangular box with line drawing characters 426 */ 427 void 428 draw_box(WINDOW * win, int y, int x, int height, int width, 429 chtype box, chtype border) 430 { 431 int i, j; 432 433 wattrset(win, 0); 434 for (i = 0; i < height; i++) { 435 wmove(win, y + i, x); 436 for (j = 0; j < width; j++) 437 if (!i && !j) 438 waddch(win, border | ACS_ULCORNER); 439 else if (i == height - 1 && !j) 440 waddch(win, border | ACS_LLCORNER); 441 else if (!i && j == width - 1) 442 waddch(win, box | ACS_URCORNER); 443 else if (i == height - 1 && j == width - 1) 444 waddch(win, box | ACS_LRCORNER); 445 else if (!i) 446 waddch(win, border | ACS_HLINE); 447 else if (i == height - 1) 448 waddch(win, box | ACS_HLINE); 449 else if (!j) 450 waddch(win, border | ACS_VLINE); 451 else if (j == width - 1) 452 waddch(win, box | ACS_VLINE); 453 else 454 waddch(win, box | ' '); 455 } 456 } 457 458 /* 459 * Draw shadows along the right and bottom edge to give a more 3D look 460 * to the boxes 461 */ 462 void draw_shadow(WINDOW * win, int y, int x, int height, int width) 463 { 464 int i; 465 466 if (has_colors()) { /* Whether terminal supports color? */ 467 wattrset(win, dlg.shadow.atr); 468 wmove(win, y + height, x + 2); 469 for (i = 0; i < width; i++) 470 waddch(win, winch(win) & A_CHARTEXT); 471 for (i = y + 1; i < y + height + 1; i++) { 472 wmove(win, i, x + width); 473 waddch(win, winch(win) & A_CHARTEXT); 474 waddch(win, winch(win) & A_CHARTEXT); 475 } 476 wnoutrefresh(win); 477 } 478 } 479 480 /* 481 * Return the position of the first alphabetic character in a string. 482 */ 483 int first_alpha(const char *string, const char *exempt) 484 { 485 int i, in_paren = 0, c; 486 487 for (i = 0; i < strlen(string); i++) { 488 c = tolower(string[i]); 489 490 if (strchr("<[(", c)) 491 ++in_paren; 492 if (strchr(">])", c) && in_paren > 0) 493 --in_paren; 494 495 if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) 496 return i; 497 } 498 499 return 0; 500 } 501 502 /* 503 * ncurses uses ESC to detect escaped char sequences. This resutl in 504 * a small timeout before ESC is actually delivered to the application. 505 * lxdialog suggest <ESC> <ESC> which is correctly translated to two 506 * times esc. But then we need to ignore the second esc to avoid stepping 507 * out one menu too much. Filter away all escaped key sequences since 508 * keypad(FALSE) turn off ncurses support for escape sequences - and thats 509 * needed to make notimeout() do as expected. 510 */ 511 int on_key_esc(WINDOW *win) 512 { 513 int key; 514 int key2; 515 int key3; 516 517 nodelay(win, TRUE); 518 keypad(win, FALSE); 519 key = wgetch(win); 520 key2 = wgetch(win); 521 do { 522 key3 = wgetch(win); 523 } while (key3 != ERR); 524 nodelay(win, FALSE); 525 keypad(win, TRUE); 526 if (key == KEY_ESC && key2 == ERR) 527 return KEY_ESC; 528 else if (key != ERR && key != KEY_ESC && key2 == ERR) 529 ungetch(key); 530 531 return -1; 532 } 533 534 /* redraw screen in new size */ 535 int on_key_resize(void) 536 { 537 dialog_clear(); 538 return KEY_RESIZE; 539 } 540 541 struct dialog_list *item_cur; 542 struct dialog_list item_nil; 543 struct dialog_list *item_head; 544 545 void item_reset(void) 546 { 547 struct dialog_list *p, *next; 548 549 for (p = item_head; p; p = next) { 550 next = p->next; 551 free(p); 552 } 553 item_head = NULL; 554 item_cur = &item_nil; 555 } 556 557 void item_make(const char *fmt, ...) 558 { 559 va_list ap; 560 struct dialog_list *p = malloc(sizeof(*p)); 561 562 if (item_head) 563 item_cur->next = p; 564 else 565 item_head = p; 566 item_cur = p; 567 memset(p, 0, sizeof(*p)); 568 569 va_start(ap, fmt); 570 vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap); 571 va_end(ap); 572 } 573 574 void item_add_str(const char *fmt, ...) 575 { 576 va_list ap; 577 size_t avail; 578 579 avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); 580 581 va_start(ap, fmt); 582 vsnprintf(item_cur->node.str + strlen(item_cur->node.str), 583 avail, fmt, ap); 584 item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0'; 585 va_end(ap); 586 } 587 588 void item_set_tag(char tag) 589 { 590 item_cur->node.tag = tag; 591 } 592 void item_set_data(void *ptr) 593 { 594 item_cur->node.data = ptr; 595 } 596 597 void item_set_selected(int val) 598 { 599 item_cur->node.selected = val; 600 } 601 602 int item_activate_selected(void) 603 { 604 item_foreach() 605 if (item_is_selected()) 606 return 1; 607 return 0; 608 } 609 610 void *item_data(void) 611 { 612 return item_cur->node.data; 613 } 614 615 char item_tag(void) 616 { 617 return item_cur->node.tag; 618 } 619 620 int item_count(void) 621 { 622 int n = 0; 623 struct dialog_list *p; 624 625 for (p = item_head; p; p = p->next) 626 n++; 627 return n; 628 } 629 630 void item_set(int n) 631 { 632 int i = 0; 633 item_foreach() 634 if (i++ == n) 635 return; 636 } 637 638 int item_n(void) 639 { 640 int n = 0; 641 struct dialog_list *p; 642 643 for (p = item_head; p; p = p->next) { 644 if (p == item_cur) 645 return n; 646 n++; 647 } 648 return 0; 649 } 650 651 const char *item_str(void) 652 { 653 return item_cur->node.str; 654 } 655 656 int item_is_selected(void) 657 { 658 return (item_cur->node.selected != 0); 659 } 660 661 int item_is_tag(char tag) 662 { 663 return (item_cur->node.tag == tag); 664 } 665