at v3.8 664 lines 17 kB view raw
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 */ 27int saved_x, saved_y; 28 29struct dialog_info dlg; 30 31static 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) \ 65do { \ 66 dlg.dialog.fg = (f); \ 67 dlg.dialog.bg = (b); \ 68 dlg.dialog.hl = (h); \ 69} while (0) 70 71static 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 104static 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 146static 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 */ 162static 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 179static 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 191static 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 */ 227static 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 */ 242void 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 255void 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 */ 274int 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 300void set_dialog_backtitle(const char *backtitle) 301{ 302 dlg.backtitle = backtitle; 303} 304 305/* 306 * End using dialog functions. 307 */ 308void 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 **/ 319void 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 */ 336void 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 */ 398void 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 */ 427void 428draw_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 */ 462void 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 */ 483int 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 */ 511int 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 */ 535int on_key_resize(void) 536{ 537 dialog_clear(); 538 return KEY_RESIZE; 539} 540 541struct dialog_list *item_cur; 542struct dialog_list item_nil; 543struct dialog_list *item_head; 544 545void 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 557void 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 574void 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 588void item_set_tag(char tag) 589{ 590 item_cur->node.tag = tag; 591} 592void item_set_data(void *ptr) 593{ 594 item_cur->node.data = ptr; 595} 596 597void item_set_selected(int val) 598{ 599 item_cur->node.selected = val; 600} 601 602int item_activate_selected(void) 603{ 604 item_foreach() 605 if (item_is_selected()) 606 return 1; 607 return 0; 608} 609 610void *item_data(void) 611{ 612 return item_cur->node.data; 613} 614 615char item_tag(void) 616{ 617 return item_cur->node.tag; 618} 619 620int 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 630void item_set(int n) 631{ 632 int i = 0; 633 item_foreach() 634 if (i++ == n) 635 return; 636} 637 638int 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 651const char *item_str(void) 652{ 653 return item_cur->node.str; 654} 655 656int item_is_selected(void) 657{ 658 return (item_cur->node.selected != 0); 659} 660 661int item_is_tag(char tag) 662{ 663 return (item_cur->node.tag == tag); 664}