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