1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
5 * Derived from menuconfig.
12 int attr_main_heading
;
13 int attr_main_menu_box
;
14 int attr_main_menu_fore
;
15 int attr_main_menu_back
;
16 int attr_main_menu_grey
;
17 int attr_main_menu_heading
;
18 int attr_scrollwin_text
;
19 int attr_scrollwin_heading
;
20 int attr_scrollwin_box
;
22 int attr_dialog_menu_fore
;
23 int attr_dialog_menu_back
;
26 int attr_input_heading
;
29 int attr_function_text
;
30 int attr_function_highlight
;
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
38 struct nconf_attr_param
{
46 static 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
),
70 static 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
),
96 const struct nconf_attr_param
*p
;
101 use_default_colors();
102 p
= color_theme_params
;
104 p
= no_color_theme_params
;
107 for (; p
->attr
; p
++) {
108 int attr
= p
->highlight
;
112 init_pair(pair
, p
->color_fg
, p
->color_bg
);
113 attr
|= COLOR_PAIR(pair
);
120 /* this changes the windows attributes !!! */
121 void print_in_middle(WINDOW
*win
, int y
, int width
, const char *str
, int attrs
)
123 wattrset(win
, attrs
);
124 mvwprintw(win
, y
, (width
- strlen(str
)) / 2, "%s", str
);
127 int get_line_no(const char *text
)
135 for (i
= 0; text
[i
] != '\0'; i
++)
141 const char *get_line(const char *text
, int line_no
)
149 for (i
= 0; text
[i
] != '\0' && lines
< line_no
; i
++)
155 int get_line_length(const char *line
)
158 while (*line
!= '\0' && *line
!= '\n') {
165 /* print all lines to the window. */
166 void fill_window(WINDOW
*win
, const char *text
)
169 int total_lines
= get_line_no(text
);
173 /* do not go over end of line */
174 total_lines
= min(total_lines
, y
);
175 for (i
= 0; i
< total_lines
; i
++) {
177 const char *line
= get_line(text
, i
);
178 int len
= get_line_length(line
);
179 strncpy(tmp
, line
, min(len
, x
));
181 mvwprintw(win
, i
, 0, "%s", tmp
);
185 /* get the message, and buttons.
186 * each button must be a char*
187 * return the selected button
189 * this dialog is used for 2 different things:
190 * 1) show a text box, no buttons.
191 * 2) show a dialog, with horizontal buttons
193 int btn_dialog(WINDOW
*main_window
, const char *msg
, int btn_num
, ...)
206 ITEM
*btns
[btn_num
+1];
211 va_start(ap
, btn_num
);
212 for (i
= 0; i
< btn_num
; i
++) {
213 btn
= va_arg(ap
, char *);
214 btns
[i
] = new_item(btn
, "");
215 btns_width
+= strlen(btn
)+1;
218 btns
[btn_num
] = NULL
;
220 /* find the widest line of msg: */
221 msg_lines
= get_line_no(msg
);
222 for (i
= 0; i
< msg_lines
; i
++) {
223 const char *line
= get_line(msg
, i
);
224 int len
= get_line_length(line
);
229 total_width
= max(msg_width
, btns_width
);
230 /* place dialog in middle of screen */
231 y
= (getmaxy(stdscr
)-(msg_lines
+4))/2;
232 x
= (getmaxx(stdscr
)-(total_width
+4))/2;
235 /* create the windows */
237 win_rows
= msg_lines
+4;
239 win_rows
= msg_lines
+2;
241 win
= newwin(win_rows
, total_width
+4, y
, x
);
243 menu_win
= derwin(win
, 1, btns_width
, win_rows
-2,
244 1+(total_width
+2-btns_width
)/2);
245 menu
= new_menu(btns
);
246 msg_win
= derwin(win
, win_rows
-2, msg_width
, 1,
247 1+(total_width
+2-msg_width
)/2);
249 set_menu_fore(menu
, attr_dialog_menu_fore
);
250 set_menu_back(menu
, attr_dialog_menu_back
);
252 wattrset(win
, attr_dialog_box
);
256 wattrset(msg_win
, attr_dialog_text
);
257 fill_window(msg_win
, msg
);
259 set_menu_win(menu
, win
);
260 set_menu_sub(menu
, menu_win
);
261 set_menu_format(menu
, 1, btn_num
);
262 menu_opts_off(menu
, O_SHOWDESC
);
263 menu_opts_off(menu
, O_SHOWMATCH
);
264 menu_opts_on(menu
, O_ONEVALUE
);
265 menu_opts_on(menu
, O_NONCYCLIC
);
266 set_menu_mark(menu
, "");
271 refresh_all_windows(main_window
);
272 while ((res
= wgetch(win
))) {
275 menu_driver(menu
, REQ_LEFT_ITEM
);
278 menu_driver(menu
, REQ_RIGHT_ITEM
);
282 /* cycle through buttons */
283 if (item_index(current_item(menu
)) == btn_num
- 1)
284 menu_driver(menu
, REQ_FIRST_ITEM
);
286 menu_driver(menu
, REQ_NEXT_ITEM
);
290 case 27: /* ESCAPE */
297 refresh_all_windows(main_window
);
299 if (res
== 10 || res
== ' ') {
300 res
= item_index(current_item(menu
));
302 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
303 res
== KEY_F(F_EXIT
)) {
311 for (i
= 0; i
< btn_num
; i
++)
318 int dialog_inputbox(WINDOW
*main_window
,
319 const char *title
, const char *prompt
,
320 const char *init
, char **resultp
, int *result_len
)
322 int prompt_lines
= 0;
323 int prompt_width
= 0;
328 int i
, x
, y
, lines
, columns
, win_lines
, win_cols
;
330 int cursor_position
= strlen(init
);
332 char *result
= *resultp
;
334 getmaxyx(stdscr
, lines
, columns
);
336 if (strlen(init
)+1 > *result_len
) {
337 *result_len
= strlen(init
)+1;
338 *resultp
= result
= xrealloc(result
, *result_len
);
341 /* find the widest line of msg: */
342 prompt_lines
= get_line_no(prompt
);
343 for (i
= 0; i
< prompt_lines
; i
++) {
344 const char *line
= get_line(prompt
, i
);
345 int len
= get_line_length(line
);
346 prompt_width
= max(prompt_width
, len
);
350 prompt_width
= max(prompt_width
, strlen(title
));
352 win_lines
= min(prompt_lines
+6, lines
-2);
353 win_cols
= min(prompt_width
+7, columns
-2);
354 prompt_lines
= max(win_lines
-6, 0);
355 prompt_width
= max(win_cols
-7, 0);
357 /* place dialog in middle of screen */
358 y
= (lines
-win_lines
)/2;
359 x
= (columns
-win_cols
)/2;
361 strncpy(result
, init
, *result_len
);
363 /* create the windows */
364 win
= newwin(win_lines
, win_cols
, y
, x
);
365 prompt_win
= derwin(win
, prompt_lines
+1, prompt_width
, 2, 2);
366 form_win
= derwin(win
, 1, prompt_width
, prompt_lines
+3, 2);
367 keypad(form_win
, TRUE
);
369 wattrset(form_win
, attr_input_field
);
371 wattrset(win
, attr_input_box
);
373 wattrset(win
, attr_input_heading
);
375 mvwprintw(win
, 0, 3, "%s", title
);
378 wattrset(prompt_win
, attr_input_text
);
379 fill_window(prompt_win
, prompt
);
381 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
382 cursor_form_win
= min(cursor_position
, prompt_width
-1);
383 mvwprintw(form_win
, 0, 0, "%s",
384 result
+ cursor_position
-cursor_form_win
);
387 panel
= new_panel(win
);
389 /* show the cursor */
393 refresh_all_windows(main_window
);
394 while ((res
= wgetch(form_win
))) {
395 int len
= strlen(result
);
398 case 27: /* ESCAPE */
406 if (cursor_position
> 0) {
407 memmove(&result
[cursor_position
-1],
408 &result
[cursor_position
],
409 len
-cursor_position
+1);
416 if (cursor_position
>= 0 && cursor_position
< len
) {
417 memmove(&result
[cursor_position
],
418 &result
[cursor_position
+1],
419 len
-cursor_position
+1);
425 if (cursor_position
< len
) {
432 if (cursor_position
> 0) {
442 cursor_position
= len
;
443 cursor_form_win
= min(cursor_position
, prompt_width
-1);
446 if ((isgraph(res
) || isspace(res
))) {
447 /* one for new char, one for '\0' */
448 if (len
+2 > *result_len
) {
450 *resultp
= result
= realloc(result
,
453 /* insert the char at the proper position */
454 memmove(&result
[cursor_position
+1],
455 &result
[cursor_position
],
456 len
-cursor_position
+1);
457 result
[cursor_position
] = res
;
462 mvprintw(0, 0, "unknown key: %d\n", res
);
466 if (cursor_form_win
< 0)
468 else if (cursor_form_win
> prompt_width
-1)
469 cursor_form_win
= prompt_width
-1;
471 wmove(form_win
, 0, 0);
473 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
474 mvwprintw(form_win
, 0, 0, "%s",
475 result
+ cursor_position
-cursor_form_win
);
476 wmove(form_win
, 0, cursor_form_win
);
478 refresh_all_windows(main_window
);
483 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
484 res
== KEY_F(F_EXIT
)) {
487 } else if (res
== KEY_F(F_HELP
)) {
493 /* hide the cursor */
502 /* refresh all windows in the correct order */
503 void refresh_all_windows(WINDOW
*main_window
)
506 touchwin(main_window
);
510 void show_scroll_win(WINDOW
*main_window
,
514 (void)show_scroll_win_ext(main_window
, title
, (char *)text
, NULL
, NULL
, NULL
, NULL
);
517 /* layman's scrollable window... */
518 int show_scroll_win_ext(WINDOW
*main_window
, const char *title
, char *text
,
519 int *vscroll
, int *hscroll
,
520 extra_key_cb_fn extra_key_cb
, void *data
)
523 int total_lines
= get_line_no(text
);
524 int x
, y
, lines
, columns
;
525 int start_x
= 0, start_y
= 0;
526 int text_lines
= 0, text_cols
= 0;
541 getmaxyx(stdscr
, lines
, columns
);
543 /* find the widest line of msg: */
544 total_lines
= get_line_no(text
);
545 for (i
= 0; i
< total_lines
; i
++) {
546 const char *line
= get_line(text
, i
);
547 int len
= get_line_length(line
);
548 total_cols
= max(total_cols
, len
+2);
552 pad
= newpad(total_lines
+10, total_cols
+10);
553 wattrset(pad
, attr_scrollwin_text
);
554 fill_window(pad
, text
);
556 win_lines
= min(total_lines
+4, lines
-2);
557 win_cols
= min(total_cols
+2, columns
-2);
558 text_lines
= max(win_lines
-4, 0);
559 text_cols
= max(win_cols
-2, 0);
561 /* place window in middle of screen */
562 y
= (lines
-win_lines
)/2;
563 x
= (columns
-win_cols
)/2;
565 win
= newwin(win_lines
, win_cols
, y
, x
);
567 /* show the help in the help window, and show the help panel */
568 wattrset(win
, attr_scrollwin_box
);
570 wattrset(win
, attr_scrollwin_heading
);
571 mvwprintw(win
, 0, 3, " %s ", title
);
572 panel
= new_panel(win
);
574 /* handle scrolling */
576 copywin(pad
, win
, start_y
, start_x
, 2, 2, text_lines
,
582 attr_dialog_menu_fore
);
590 start_y
+= text_lines
-2;
594 start_y
-= text_lines
+2;
600 start_y
= total_lines
-text_lines
;
620 size_t start
= (get_line(text
, start_y
) - text
);
621 size_t end
= (get_line(text
, start_y
+ text_lines
) - text
);
623 if (extra_key_cb(res
, start
, end
, data
)) {
629 if (res
== 0 || res
== 10 || res
== 27 || res
== 'q' ||
630 res
== KEY_F(F_HELP
) || res
== KEY_F(F_BACK
) ||
631 res
== KEY_F(F_EXIT
))
635 if (start_y
>= total_lines
-text_lines
)
636 start_y
= total_lines
-text_lines
;
639 if (start_x
>= total_cols
-text_cols
)
640 start_x
= total_cols
-text_cols
;
649 refresh_all_windows(main_window
);