File Index
Files and directories
Sign in
FILE

nconf.gui.c

File preview with syntax highlighting.

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