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