kconfig/nconf.gui.c
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu May 12 19:45:30 2011 +0200 (2011-05-12)
changeset 2454 c70da6c50bdb
permissions -rw-r--r--
kconfig: add the nconf frontend

The nconf frontend is the new hot topic in the kconfig land! :-)

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
     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_BLUE, -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 = (LINES-(msg_lines+4))/2;
   280 	x = (COLS-(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 *result, 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;
   368 	int res = -1;
   369 	int cursor_position = strlen(init);
   370 
   371 
   372 	/* find the widest line of msg: */
   373 	prompt_lines = get_line_no(prompt);
   374 	for (i = 0; i < prompt_lines; i++) {
   375 		const char *line = get_line(prompt, i);
   376 		int len = get_line_length(line);
   377 		prompt_width = max(prompt_width, len);
   378 	}
   379 
   380 	if (title)
   381 		prompt_width = max(prompt_width, strlen(title));
   382 
   383 	/* place dialog in middle of screen */
   384 	y = (LINES-(prompt_lines+4))/2;
   385 	x = (COLS-(prompt_width+4))/2;
   386 
   387 	strncpy(result, init, result_len);
   388 
   389 	/* create the windows */
   390 	win = newwin(prompt_lines+6, prompt_width+7, y, x);
   391 	prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
   392 	form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
   393 	keypad(form_win, TRUE);
   394 
   395 	(void) wattrset(form_win, attributes[INPUT_FIELD]);
   396 
   397 	(void) wattrset(win, attributes[INPUT_BOX]);
   398 	box(win, 0, 0);
   399 	(void) wattrset(win, attributes[INPUT_HEADING]);
   400 	if (title)
   401 		mvwprintw(win, 0, 3, "%s", title);
   402 
   403 	/* print message */
   404 	(void) wattrset(prompt_win, attributes[INPUT_TEXT]);
   405 	fill_window(prompt_win, prompt);
   406 
   407 	mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
   408 	mvwprintw(form_win, 0, 0, "%s", result);
   409 
   410 	/* create panels */
   411 	panel = new_panel(win);
   412 
   413 	/* show the cursor */
   414 	curs_set(1);
   415 
   416 	touchwin(win);
   417 	refresh_all_windows(main_window);
   418 	while ((res = wgetch(form_win))) {
   419 		int len = strlen(result);
   420 		switch (res) {
   421 		case 10: /* ENTER */
   422 		case 27: /* ESCAPE */
   423 		case KEY_F(F_HELP):
   424 		case KEY_F(F_EXIT):
   425 		case KEY_F(F_BACK):
   426 			break;
   427 		case 127:
   428 		case KEY_BACKSPACE:
   429 			if (cursor_position > 0) {
   430 				memmove(&result[cursor_position-1],
   431 						&result[cursor_position],
   432 						len-cursor_position+1);
   433 				cursor_position--;
   434 			}
   435 			break;
   436 		case KEY_DC:
   437 			if (cursor_position >= 0 && cursor_position < len) {
   438 				memmove(&result[cursor_position],
   439 						&result[cursor_position+1],
   440 						len-cursor_position+1);
   441 			}
   442 			break;
   443 		case KEY_UP:
   444 		case KEY_RIGHT:
   445 			if (cursor_position < len &&
   446 			    cursor_position < min(result_len, prompt_width))
   447 				cursor_position++;
   448 			break;
   449 		case KEY_DOWN:
   450 		case KEY_LEFT:
   451 			if (cursor_position > 0)
   452 				cursor_position--;
   453 			break;
   454 		default:
   455 			if ((isgraph(res) || isspace(res)) &&
   456 					len-2 < result_len) {
   457 				/* insert the char at the proper position */
   458 				memmove(&result[cursor_position+1],
   459 						&result[cursor_position],
   460 						len+1);
   461 				result[cursor_position] = res;
   462 				cursor_position++;
   463 			} else {
   464 				mvprintw(0, 0, "unknow key: %d\n", res);
   465 			}
   466 			break;
   467 		}
   468 		wmove(form_win, 0, 0);
   469 		wclrtoeol(form_win);
   470 		mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
   471 		mvwprintw(form_win, 0, 0, "%s", result);
   472 		wmove(form_win, 0, cursor_position);
   473 		touchwin(win);
   474 		refresh_all_windows(main_window);
   475 
   476 		if (res == 10) {
   477 			res = 0;
   478 			break;
   479 		} else if (res == 27 || res == KEY_F(F_BACK) ||
   480 				res == KEY_F(F_EXIT)) {
   481 			res = KEY_EXIT;
   482 			break;
   483 		} else if (res == KEY_F(F_HELP)) {
   484 			res = 1;
   485 			break;
   486 		}
   487 	}
   488 
   489 	/* hide the cursor */
   490 	curs_set(0);
   491 	del_panel(panel);
   492 	delwin(prompt_win);
   493 	delwin(form_win);
   494 	delwin(win);
   495 	return res;
   496 }
   497 
   498 /* refresh all windows in the correct order */
   499 void refresh_all_windows(WINDOW *main_window)
   500 {
   501 	update_panels();
   502 	touchwin(main_window);
   503 	refresh();
   504 }
   505 
   506 /* layman's scrollable window... */
   507 void show_scroll_win(WINDOW *main_window,
   508 		const char *title,
   509 		const char *text)
   510 {
   511 	int res;
   512 	int total_lines = get_line_no(text);
   513 	int x, y;
   514 	int start_x = 0, start_y = 0;
   515 	int text_lines = 0, text_cols = 0;
   516 	int total_cols = 0;
   517 	int win_cols = 0;
   518 	int win_lines = 0;
   519 	int i = 0;
   520 	WINDOW *win;
   521 	WINDOW *pad;
   522 	PANEL *panel;
   523 
   524 	/* find the widest line of msg: */
   525 	total_lines = get_line_no(text);
   526 	for (i = 0; i < total_lines; i++) {
   527 		const char *line = get_line(text, i);
   528 		int len = get_line_length(line);
   529 		total_cols = max(total_cols, len+2);
   530 	}
   531 
   532 	/* create the pad */
   533 	pad = newpad(total_lines+10, total_cols+10);
   534 	(void) wattrset(pad, attributes[SCROLLWIN_TEXT]);
   535 	fill_window(pad, text);
   536 
   537 	win_lines = min(total_lines+4, LINES-2);
   538 	win_cols = min(total_cols+2, COLS-2);
   539 	text_lines = max(win_lines-4, 0);
   540 	text_cols = max(win_cols-2, 0);
   541 
   542 	/* place window in middle of screen */
   543 	y = (LINES-win_lines)/2;
   544 	x = (COLS-win_cols)/2;
   545 
   546 	win = newwin(win_lines, win_cols, y, x);
   547 	keypad(win, TRUE);
   548 	/* show the help in the help window, and show the help panel */
   549 	(void) wattrset(win, attributes[SCROLLWIN_BOX]);
   550 	box(win, 0, 0);
   551 	(void) wattrset(win, attributes[SCROLLWIN_HEADING]);
   552 	mvwprintw(win, 0, 3, " %s ", title);
   553 	panel = new_panel(win);
   554 
   555 	/* handle scrolling */
   556 	do {
   557 
   558 		copywin(pad, win, start_y, start_x, 2, 2, text_lines,
   559 				text_cols, 0);
   560 		print_in_middle(win,
   561 				text_lines+2,
   562 				0,
   563 				text_cols,
   564 				"<OK>",
   565 				attributes[DIALOG_MENU_FORE]);
   566 		wrefresh(win);
   567 
   568 		res = wgetch(win);
   569 		switch (res) {
   570 		case KEY_NPAGE:
   571 		case ' ':
   572 			start_y += text_lines-2;
   573 			break;
   574 		case KEY_PPAGE:
   575 			start_y -= text_lines+2;
   576 			break;
   577 		case KEY_HOME:
   578 			start_y = 0;
   579 			break;
   580 		case KEY_END:
   581 			start_y = total_lines-text_lines;
   582 			break;
   583 		case KEY_DOWN:
   584 		case 'j':
   585 			start_y++;
   586 			break;
   587 		case KEY_UP:
   588 		case 'k':
   589 			start_y--;
   590 			break;
   591 		case KEY_LEFT:
   592 		case 'h':
   593 			start_x--;
   594 			break;
   595 		case KEY_RIGHT:
   596 		case 'l':
   597 			start_x++;
   598 			break;
   599 		}
   600 		if (res == 10 || res == 27 || res == 'q'
   601 		    || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) {
   602 			break;
   603 		}
   604 		if (start_y < 0)
   605 			start_y = 0;
   606 		if (start_y >= total_lines-text_lines)
   607 			start_y = total_lines-text_lines;
   608 		if (start_x < 0)
   609 			start_x = 0;
   610 		if (start_x >= total_cols-text_cols)
   611 			start_x = total_cols-text_cols;
   612 	} while (res);
   613 
   614 	del_panel(panel);
   615 	delwin(win);
   616 	refresh_all_windows(main_window);
   617 }