1 /* 2 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com? 3 * Released under the terms of the GNU GPL v2.0. 4 * 5 * Derived from menuconfig. 6 * 7 */ 8 #include "nconf.h" 9 #include "lkc.h" 10 11 /* a list of all the different widgets we use */ 12 attributes_t attributes[ATTR_MAX+1] = {0}; 13 14 /* available colors: 15 COLOR_BLACK 0 16 COLOR_RED 1 17 COLOR_GREEN 2 18 COLOR_YELLOW 3 19 COLOR_BLUE 4 20 COLOR_MAGENTA 5 21 COLOR_CYAN 6 22 COLOR_WHITE 7 23 */ 24 static void set_normal_colors(void) 25 { 26 init_pair(NORMAL, -1, -1); 27 init_pair(MAIN_HEADING, COLOR_MAGENTA, -1); 28 29 /* FORE is for the selected item */ 30 init_pair(MAIN_MENU_FORE, -1, -1); 31 /* BACK for all the rest */ 32 init_pair(MAIN_MENU_BACK, -1, -1); 33 init_pair(MAIN_MENU_GREY, -1, -1); 34 init_pair(MAIN_MENU_HEADING, COLOR_GREEN, -1); 35 init_pair(MAIN_MENU_BOX, COLOR_YELLOW, -1); 36 37 init_pair(SCROLLWIN_TEXT, -1, -1); 38 init_pair(SCROLLWIN_HEADING, COLOR_GREEN, -1); 39 init_pair(SCROLLWIN_BOX, COLOR_YELLOW, -1); 40 41 init_pair(DIALOG_TEXT, -1, -1); 42 init_pair(DIALOG_BOX, COLOR_YELLOW, -1); 43 init_pair(DIALOG_MENU_BACK, COLOR_YELLOW, -1); 44 init_pair(DIALOG_MENU_FORE, COLOR_RED, -1); 45 46 init_pair(INPUT_BOX, COLOR_YELLOW, -1); 47 init_pair(INPUT_HEADING, COLOR_GREEN, -1); 48 init_pair(INPUT_TEXT, -1, -1); 49 init_pair(INPUT_FIELD, -1, -1); 50 51 init_pair(FUNCTION_HIGHLIGHT, -1, -1); 52 init_pair(FUNCTION_TEXT, COLOR_YELLOW, -1); 53 } 54 55 /* available attributes: 56 A_NORMAL Normal display (no highlight) 57 A_STANDOUT Best highlighting mode of the terminal. 58 A_UNDERLINE Underlining 59 A_REVERSE Reverse video 60 A_BLINK Blinking 61 A_DIM Half bright 62 A_BOLD Extra bright or bold 63 A_PROTECT Protected mode 64 A_INVIS Invisible or blank mode 65 A_ALTCHARSET Alternate character set 66 A_CHARTEXT Bit-mask to extract a character 67 COLOR_PAIR(n) Color-pair number n 68 */ 69 static void normal_color_theme(void) 70 { 71 /* automatically add color... */ 72 #define mkattr(name, attr) do { \ 73 attributes[name] = attr | COLOR_PAIR(name); } while (0) 74 mkattr(NORMAL, NORMAL); 75 mkattr(MAIN_HEADING, A_BOLD | A_UNDERLINE); 76 77 mkattr(MAIN_MENU_FORE, A_REVERSE); 78 mkattr(MAIN_MENU_BACK, A_NORMAL); 79 mkattr(MAIN_MENU_GREY, A_NORMAL); 80 mkattr(MAIN_MENU_HEADING, A_BOLD); 81 mkattr(MAIN_MENU_BOX, A_NORMAL); 82 83 mkattr(SCROLLWIN_TEXT, A_NORMAL); 84 mkattr(SCROLLWIN_HEADING, A_BOLD); 85 mkattr(SCROLLWIN_BOX, A_BOLD); 86 87 mkattr(DIALOG_TEXT, A_BOLD); 88 mkattr(DIALOG_BOX, A_BOLD); 89 mkattr(DIALOG_MENU_FORE, A_STANDOUT); 90 mkattr(DIALOG_MENU_BACK, A_NORMAL); 91 92 mkattr(INPUT_BOX, A_NORMAL); 93 mkattr(INPUT_HEADING, A_BOLD); 94 mkattr(INPUT_TEXT, A_NORMAL); 95 mkattr(INPUT_FIELD, A_UNDERLINE); 96 97 mkattr(FUNCTION_HIGHLIGHT, A_BOLD); 98 mkattr(FUNCTION_TEXT, A_REVERSE); 99 } 100 101 static void no_colors_theme(void) 102 { 103 /* automatically add highlight, no color */ 104 #define mkattrn(name, attr) { attributes[name] = attr; } 105 106 mkattrn(NORMAL, NORMAL); 107 mkattrn(MAIN_HEADING, A_BOLD | A_UNDERLINE); 108 109 mkattrn(MAIN_MENU_FORE, A_STANDOUT); 110 mkattrn(MAIN_MENU_BACK, A_NORMAL); 111 mkattrn(MAIN_MENU_GREY, A_NORMAL); 112 mkattrn(MAIN_MENU_HEADING, A_BOLD); 113 mkattrn(MAIN_MENU_BOX, A_NORMAL); 114 115 mkattrn(SCROLLWIN_TEXT, A_NORMAL); 116 mkattrn(SCROLLWIN_HEADING, A_BOLD); 117 mkattrn(SCROLLWIN_BOX, A_BOLD); 118 119 mkattrn(DIALOG_TEXT, A_NORMAL); 120 mkattrn(DIALOG_BOX, A_BOLD); 121 mkattrn(DIALOG_MENU_FORE, A_STANDOUT); 122 mkattrn(DIALOG_MENU_BACK, A_NORMAL); 123 124 mkattrn(INPUT_BOX, A_BOLD); 125 mkattrn(INPUT_HEADING, A_BOLD); 126 mkattrn(INPUT_TEXT, A_NORMAL); 127 mkattrn(INPUT_FIELD, A_UNDERLINE); 128 129 mkattrn(FUNCTION_HIGHLIGHT, A_BOLD); 130 mkattrn(FUNCTION_TEXT, A_REVERSE); 131 } 132 133 void set_colors(void) 134 { 135 start_color(); 136 use_default_colors(); 137 set_normal_colors(); 138 if (has_colors()) { 139 normal_color_theme(); 140 } else { 141 /* give defaults */ 142 no_colors_theme(); 143 } 144 } 145 146 147 /* this changes the windows attributes !!! */ 148 void print_in_middle(WINDOW *win, 149 int starty, 150 int startx, 151 int width, 152 const char *string, 153 chtype color) 154 { int length, x, y; 155 float temp; 156 157 158 if (win == NULL) 159 win = stdscr; 160 getyx(win, y, x); 161 if (startx != 0) 162 x = startx; 163 if (starty != 0) 164 y = starty; 165 if (width == 0) 166 width = 80; 167 168 length = strlen(string); 169 temp = (width - length) / 2; 170 x = startx + (int)temp; 171 (void) wattrset(win, color); 172 mvwprintw(win, y, x, "%s", string); 173 refresh(); 174 } 175 176 int get_line_no(const char *text) 177 { 178 int i; 179 int total = 1; 180 181 if (!text) 182 return 0; 183 184 for (i = 0; text[i] != '\0'; i++) 185 if (text[i] == '\n') 186 total++; 187 return total; 188 } 189 190 const char *get_line(const char *text, int line_no) 191 { 192 int i; 193 int lines = 0; 194 195 if (!text) 196 return NULL; 197 198 for (i = 0; text[i] != '\0' && lines < line_no; i++) 199 if (text[i] == '\n') 200 lines++; 201 return text+i; 202 } 203 204 int get_line_length(const char *line) 205 { 206 int res = 0; 207 while (*line != '\0' && *line != '\n') { 208 line++; 209 res++; 210 } 211 return res; 212 } 213 214 /* print all lines to the window. */ 215 void fill_window(WINDOW *win, const char *text) 216 { 217 int x, y; 218 int total_lines = get_line_no(text); 219 int i; 220 221 getmaxyx(win, y, x); 222 /* do not go over end of line */ 223 total_lines = min(total_lines, y); 224 for (i = 0; i < total_lines; i++) { 225 char tmp[x+10]; 226 const char *line = get_line(text, i); 227 int len = get_line_length(line); 228 strncpy(tmp, line, min(len, x)); 229 tmp[len] = '\0'; 230 mvwprintw(win, i, 0, "%s", tmp); 231 } 232 } 233 234 /* get the message, and buttons. 235 * each button must be a char* 236 * return the selected button 237 * 238 * this dialog is used for 2 different things: 239 * 1) show a text box, no buttons. 240 * 2) show a dialog, with horizontal buttons 241 */ 242 int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...) 243 { 244 va_list ap; 245 char *btn; 246 int btns_width = 0; 247 int msg_lines = 0; 248 int msg_width = 0; 249 int total_width; 250 int win_rows = 0; 251 WINDOW *win; 252 WINDOW *msg_win; 253 WINDOW *menu_win; 254 MENU *menu; 255 ITEM *btns[btn_num+1]; 256 int i, x, y; 257 int res = -1; 258 259 260 va_start(ap, btn_num); 261 for (i = 0; i < btn_num; i++) { 262 btn = va_arg(ap, char *); 263 btns[i] = new_item(btn, ""); 264 btns_width += strlen(btn)+1; 265 } 266 va_end(ap); 267 btns[btn_num] = NULL; 268 269 /* find the widest line of msg: */ 270 msg_lines = get_line_no(msg); 271 for (i = 0; i < msg_lines; i++) { 272 const char *line = get_line(msg, i); 273 int len = get_line_length(line); 274 if (msg_width < len) 275 msg_width = len; 276 } 277 278 total_width = max(msg_width, btns_width); 279 /* place dialog in middle of screen */ 280 y = (getmaxy(stdscr)-(msg_lines+4))/2; 281 x = (getmaxx(stdscr)-(total_width+4))/2; 282 283 284 /* create the windows */ 285 if (btn_num > 0) 286 win_rows = msg_lines+4; 287 else 288 win_rows = msg_lines+2; 289 290 win = newwin(win_rows, total_width+4, y, x); 291 keypad(win, TRUE); 292 menu_win = derwin(win, 1, btns_width, win_rows-2, 293 1+(total_width+2-btns_width)/2); 294 menu = new_menu(btns); 295 msg_win = derwin(win, win_rows-2, msg_width, 1, 296 1+(total_width+2-msg_width)/2); 297 298 set_menu_fore(menu, attributes[DIALOG_MENU_FORE]); 299 set_menu_back(menu, attributes[DIALOG_MENU_BACK]); 300 301 (void) wattrset(win, attributes[DIALOG_BOX]); 302 box(win, 0, 0); 303 304 /* print message */ 305 (void) wattrset(msg_win, attributes[DIALOG_TEXT]); 306 fill_window(msg_win, msg); 307 308 set_menu_win(menu, win); 309 set_menu_sub(menu, menu_win); 310 set_menu_format(menu, 1, btn_num); 311 menu_opts_off(menu, O_SHOWDESC); 312 menu_opts_off(menu, O_SHOWMATCH); 313 menu_opts_on(menu, O_ONEVALUE); 314 menu_opts_on(menu, O_NONCYCLIC); 315 set_menu_mark(menu, ""); 316 post_menu(menu); 317 318 319 touchwin(win); 320 refresh_all_windows(main_window); 321 while ((res = wgetch(win))) { 322 switch (res) { 323 case KEY_LEFT: 324 menu_driver(menu, REQ_LEFT_ITEM); 325 break; 326 case KEY_RIGHT: 327 menu_driver(menu, REQ_RIGHT_ITEM); 328 break; 329 case 10: /* ENTER */ 330 case 27: /* ESCAPE */ 331 case ' ': 332 case KEY_F(F_BACK): 333 case KEY_F(F_EXIT): 334 break; 335 } 336 touchwin(win); 337 refresh_all_windows(main_window); 338 339 if (res == 10 || res == ' ') { 340 res = item_index(current_item(menu)); 341 break; 342 } else if (res == 27 || res == KEY_F(F_BACK) || 343 res == KEY_F(F_EXIT)) { 344 res = KEY_EXIT; 345 break; 346 } 347 } 348 349 unpost_menu(menu); 350 free_menu(menu); 351 for (i = 0; i < btn_num; i++) 352 free_item(btns[i]); 353 354 delwin(win); 355 return res; 356 } 357 358 int dialog_inputbox(WINDOW *main_window, 359 const char *title, const char *prompt, 360 const char *init, char **resultp, int *result_len) 361 { 362 int prompt_lines = 0; 363 int prompt_width = 0; 364 WINDOW *win; 365 WINDOW *prompt_win; 366 WINDOW *form_win; 367 PANEL *panel; 368 int i, x, y, lines, columns, win_lines, win_cols; 369 int res = -1; 370 int cursor_position = strlen(init); 371 int cursor_form_win; 372 char *result = *resultp; 373 374 getmaxyx(stdscr, lines, columns); 375 376 if (strlen(init)+1 > *result_len) { 377 *result_len = strlen(init)+1; 378 *resultp = result = xrealloc(result, *result_len); 379 } 380 381 /* find the widest line of msg: */ 382 prompt_lines = get_line_no(prompt); 383 for (i = 0; i < prompt_lines; i++) { 384 const char *line = get_line(prompt, i); 385 int len = get_line_length(line); 386 prompt_width = max(prompt_width, len); 387 } 388 389 if (title) 390 prompt_width = max(prompt_width, strlen(title)); 391 392 win_lines = min(prompt_lines+6, lines-2); 393 win_cols = min(prompt_width+7, columns-2); 394 prompt_lines = max(win_lines-6, 0); 395 prompt_width = max(win_cols-7, 0); 396 397 /* place dialog in middle of screen */ 398 y = (lines-win_lines)/2; 399 x = (columns-win_cols)/2; 400 401 strncpy(result, init, *result_len); 402 403 /* create the windows */ 404 win = newwin(win_lines, win_cols, y, x); 405 prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2); 406 form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2); 407 keypad(form_win, TRUE); 408 409 (void) wattrset(form_win, attributes[INPUT_FIELD]); 410 411 (void) wattrset(win, attributes[INPUT_BOX]); 412 box(win, 0, 0); 413 (void) wattrset(win, attributes[INPUT_HEADING]); 414 if (title) 415 mvwprintw(win, 0, 3, "%s", title); 416 417 /* print message */ 418 (void) wattrset(prompt_win, attributes[INPUT_TEXT]); 419 fill_window(prompt_win, prompt); 420 421 mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); 422 cursor_form_win = min(cursor_position, prompt_width-1); 423 mvwprintw(form_win, 0, 0, "%s", 424 result + cursor_position-cursor_form_win); 425 426 /* create panels */ 427 panel = new_panel(win); 428 429 /* show the cursor */ 430 curs_set(1); 431 432 touchwin(win); 433 refresh_all_windows(main_window); 434 while ((res = wgetch(form_win))) { 435 int len = strlen(result); 436 switch (res) { 437 case 10: /* ENTER */ 438 case 27: /* ESCAPE */ 439 case KEY_F(F_HELP): 440 case KEY_F(F_EXIT): 441 case KEY_F(F_BACK): 442 break; 443 case 127: 444 case KEY_BACKSPACE: 445 if (cursor_position > 0) { 446 memmove(&result[cursor_position-1], 447 &result[cursor_position], 448 len-cursor_position+1); 449 cursor_position--; 450 cursor_form_win--; 451 len--; 452 } 453 break; 454 case KEY_DC: 455 if (cursor_position >= 0 && cursor_position < len) { 456 memmove(&result[cursor_position], 457 &result[cursor_position+1], 458 len-cursor_position+1); 459 len--; 460 } 461 break; 462 case KEY_UP: 463 case KEY_RIGHT: 464 if (cursor_position < len) { 465 cursor_position++; 466 cursor_form_win++; 467 } 468 break; 469 case KEY_DOWN: 470 case KEY_LEFT: 471 if (cursor_position > 0) { 472 cursor_position--; 473 cursor_form_win--; 474 } 475 break; 476 case KEY_HOME: 477 cursor_position = 0; 478 cursor_form_win = 0; 479 break; 480 case KEY_END: 481 cursor_position = len; 482 cursor_form_win = min(cursor_position, prompt_width-1); 483 break; 484 default: 485 if ((isgraph(res) || isspace(res))) { 486 /* one for new char, one for '\0' */ 487 if (len+2 > *result_len) { 488 *result_len = len+2; 489 *resultp = result = realloc(result, 490 *result_len); 491 } 492 /* insert the char at the proper position */ 493 memmove(&result[cursor_position+1], 494 &result[cursor_position], 495 len-cursor_position+1); 496 result[cursor_position] = res; 497 cursor_position++; 498 cursor_form_win++; 499 len++; 500 } else { 501 mvprintw(0, 0, "unknown key: %d\n", res); 502 } 503 break; 504 } 505 if (cursor_form_win < 0) 506 cursor_form_win = 0; 507 else if (cursor_form_win > prompt_width-1) 508 cursor_form_win = prompt_width-1; 509 510 wmove(form_win, 0, 0); 511 wclrtoeol(form_win); 512 mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); 513 mvwprintw(form_win, 0, 0, "%s", 514 result + cursor_position-cursor_form_win); 515 wmove(form_win, 0, cursor_form_win); 516 touchwin(win); 517 refresh_all_windows(main_window); 518 519 if (res == 10) { 520 res = 0; 521 break; 522 } else if (res == 27 || res == KEY_F(F_BACK) || 523 res == KEY_F(F_EXIT)) { 524 res = KEY_EXIT; 525 break; 526 } else if (res == KEY_F(F_HELP)) { 527 res = 1; 528 break; 529 } 530 } 531 532 /* hide the cursor */ 533 curs_set(0); 534 del_panel(panel); 535 delwin(prompt_win); 536 delwin(form_win); 537 delwin(win); 538 return res; 539 } 540 541 /* refresh all windows in the correct order */ 542 void refresh_all_windows(WINDOW *main_window) 543 { 544 update_panels(); 545 touchwin(main_window); 546 refresh(); 547 } 548 549 /* layman's scrollable window... */ 550 void show_scroll_win(WINDOW *main_window, 551 const char *title, 552 const char *text) 553 { 554 int res; 555 int total_lines = get_line_no(text); 556 int x, y, lines, columns; 557 int start_x = 0, start_y = 0; 558 int text_lines = 0, text_cols = 0; 559 int total_cols = 0; 560 int win_cols = 0; 561 int win_lines = 0; 562 int i = 0; 563 WINDOW *win; 564 WINDOW *pad; 565 PANEL *panel; 566 567 getmaxyx(stdscr, lines, columns); 568 569 /* find the widest line of msg: */ 570 total_lines = get_line_no(text); 571 for (i = 0; i < total_lines; i++) { 572 const char *line = get_line(text, i); 573 int len = get_line_length(line); 574 total_cols = max(total_cols, len+2); 575 } 576 577 /* create the pad */ 578 pad = newpad(total_lines+10, total_cols+10); 579 (void) wattrset(pad, attributes[SCROLLWIN_TEXT]); 580 fill_window(pad, text); 581 582 win_lines = min(total_lines+4, lines-2); 583 win_cols = min(total_cols+2, columns-2); 584 text_lines = max(win_lines-4, 0); 585 text_cols = max(win_cols-2, 0); 586 587 /* place window in middle of screen */ 588 y = (lines-win_lines)/2; 589 x = (columns-win_cols)/2; 590 591 win = newwin(win_lines, win_cols, y, x); 592 keypad(win, TRUE); 593 /* show the help in the help window, and show the help panel */ 594 (void) wattrset(win, attributes[SCROLLWIN_BOX]); 595 box(win, 0, 0); 596 (void) wattrset(win, attributes[SCROLLWIN_HEADING]); 597 mvwprintw(win, 0, 3, " %s ", title); 598 panel = new_panel(win); 599 600 /* handle scrolling */ 601 do { 602 603 copywin(pad, win, start_y, start_x, 2, 2, text_lines, 604 text_cols, 0); 605 print_in_middle(win, 606 text_lines+2, 607 0, 608 text_cols, 609 "<OK>", 610 attributes[DIALOG_MENU_FORE]); 611 wrefresh(win); 612 613 res = wgetch(win); 614 switch (res) { 615 case KEY_NPAGE: 616 case ' ': 617 case 'd': 618 start_y += text_lines-2; 619 break; 620 case KEY_PPAGE: 621 case 'u': 622 start_y -= text_lines+2; 623 break; 624 case KEY_HOME: 625 start_y = 0; 626 break; 627 case KEY_END: 628 start_y = total_lines-text_lines; 629 break; 630 case KEY_DOWN: 631 case 'j': 632 start_y++; 633 break; 634 case KEY_UP: 635 case 'k': 636 start_y--; 637 break; 638 case KEY_LEFT: 639 case 'h': 640 start_x--; 641 break; 642 case KEY_RIGHT: 643 case 'l': 644 start_x++; 645 break; 646 } 647 if (res == 10 || res == 27 || res == 'q' || 648 res == KEY_F(F_HELP) || res == KEY_F(F_BACK) || 649 res == KEY_F(F_EXIT)) 650 break; 651 if (start_y < 0) 652 start_y = 0; 653 if (start_y >= total_lines-text_lines) 654 start_y = total_lines-text_lines; 655 if (start_x < 0) 656 start_x = 0; 657 if (start_x >= total_cols-text_cols) 658 start_x = total_cols-text_cols; 659 } while (res); 660 661 del_panel(panel); 662 delwin(win); 663 refresh_all_windows(main_window); 664 } 665