1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
5 * Derived from menuconfig.
11 int attr_main_heading
;
12 int attr_main_menu_box
;
13 int attr_main_menu_fore
;
14 int attr_main_menu_back
;
15 int attr_main_menu_grey
;
16 int attr_main_menu_heading
;
17 int attr_scrollwin_text
;
18 int attr_scrollwin_heading
;
19 int attr_scrollwin_box
;
21 int attr_dialog_menu_fore
;
22 int attr_dialog_menu_back
;
25 int attr_input_heading
;
28 int attr_function_text
;
29 int attr_function_highlight
;
31 #define COLOR_ATTR(_at, _fg, _bg, _hl) \
32 { .attr = &(_at), .has_color = true, .color_fg = _fg, .color_bg = _bg, .highlight = _hl }
33 #define NO_COLOR_ATTR(_at, _hl) \
34 { .attr = &(_at), .has_color = false, .highlight = _hl }
35 #define COLOR_DEFAULT -1
37 struct nconf_attr_param
{
45 static const struct nconf_attr_param color_theme_params
[] = {
46 COLOR_ATTR(attr_normal
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_NORMAL
),
47 COLOR_ATTR(attr_main_heading
, COLOR_MAGENTA
, COLOR_DEFAULT
, A_BOLD
| A_UNDERLINE
),
48 COLOR_ATTR(attr_main_menu_box
, COLOR_YELLOW
, COLOR_DEFAULT
, A_NORMAL
),
49 COLOR_ATTR(attr_main_menu_fore
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_REVERSE
),
50 COLOR_ATTR(attr_main_menu_back
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_NORMAL
),
51 COLOR_ATTR(attr_main_menu_grey
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_NORMAL
),
52 COLOR_ATTR(attr_main_menu_heading
, COLOR_GREEN
, COLOR_DEFAULT
, A_BOLD
),
53 COLOR_ATTR(attr_scrollwin_text
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_NORMAL
),
54 COLOR_ATTR(attr_scrollwin_heading
, COLOR_GREEN
, COLOR_DEFAULT
, A_BOLD
),
55 COLOR_ATTR(attr_scrollwin_box
, COLOR_YELLOW
, COLOR_DEFAULT
, A_BOLD
),
56 COLOR_ATTR(attr_dialog_text
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_BOLD
),
57 COLOR_ATTR(attr_dialog_menu_fore
, COLOR_RED
, COLOR_DEFAULT
, A_STANDOUT
),
58 COLOR_ATTR(attr_dialog_menu_back
, COLOR_YELLOW
, COLOR_DEFAULT
, A_NORMAL
),
59 COLOR_ATTR(attr_dialog_box
, COLOR_YELLOW
, COLOR_DEFAULT
, A_BOLD
),
60 COLOR_ATTR(attr_input_box
, COLOR_YELLOW
, COLOR_DEFAULT
, A_NORMAL
),
61 COLOR_ATTR(attr_input_heading
, COLOR_GREEN
, COLOR_DEFAULT
, A_BOLD
),
62 COLOR_ATTR(attr_input_text
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_NORMAL
),
63 COLOR_ATTR(attr_input_field
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_UNDERLINE
),
64 COLOR_ATTR(attr_function_text
, COLOR_YELLOW
, COLOR_DEFAULT
, A_REVERSE
),
65 COLOR_ATTR(attr_function_highlight
, COLOR_DEFAULT
, COLOR_DEFAULT
, A_BOLD
),
69 static const struct nconf_attr_param no_color_theme_params
[] = {
70 NO_COLOR_ATTR(attr_normal
, A_NORMAL
),
71 NO_COLOR_ATTR(attr_main_heading
, A_BOLD
| A_UNDERLINE
),
72 NO_COLOR_ATTR(attr_main_menu_box
, A_NORMAL
),
73 NO_COLOR_ATTR(attr_main_menu_fore
, A_STANDOUT
),
74 NO_COLOR_ATTR(attr_main_menu_back
, A_NORMAL
),
75 NO_COLOR_ATTR(attr_main_menu_grey
, A_NORMAL
),
76 NO_COLOR_ATTR(attr_main_menu_heading
, A_BOLD
),
77 NO_COLOR_ATTR(attr_scrollwin_text
, A_NORMAL
),
78 NO_COLOR_ATTR(attr_scrollwin_heading
, A_BOLD
),
79 NO_COLOR_ATTR(attr_scrollwin_box
, A_BOLD
),
80 NO_COLOR_ATTR(attr_dialog_text
, A_NORMAL
),
81 NO_COLOR_ATTR(attr_dialog_menu_fore
, A_STANDOUT
),
82 NO_COLOR_ATTR(attr_dialog_menu_back
, A_NORMAL
),
83 NO_COLOR_ATTR(attr_dialog_box
, A_BOLD
),
84 NO_COLOR_ATTR(attr_input_box
, A_BOLD
),
85 NO_COLOR_ATTR(attr_input_heading
, A_BOLD
),
86 NO_COLOR_ATTR(attr_input_text
, A_NORMAL
),
87 NO_COLOR_ATTR(attr_input_field
, A_UNDERLINE
),
88 NO_COLOR_ATTR(attr_function_text
, A_REVERSE
),
89 NO_COLOR_ATTR(attr_function_highlight
, A_BOLD
),
95 const struct nconf_attr_param
*p
;
100 use_default_colors();
101 p
= color_theme_params
;
103 p
= no_color_theme_params
;
106 for (; p
->attr
; p
++) {
107 int attr
= p
->highlight
;
111 init_pair(pair
, p
->color_fg
, p
->color_bg
);
112 attr
|= COLOR_PAIR(pair
);
119 /* this changes the windows attributes !!! */
120 void print_in_middle(WINDOW
*win
, int y
, int width
, const char *str
, int attrs
)
122 wattrset(win
, attrs
);
123 mvwprintw(win
, y
, (width
- strlen(str
)) / 2, "%s", str
);
126 int get_line_no(const char *text
)
134 for (i
= 0; text
[i
] != '\0'; i
++)
140 const char *get_line(const char *text
, int line_no
)
148 for (i
= 0; text
[i
] != '\0' && lines
< line_no
; i
++)
154 int get_line_length(const char *line
)
157 while (*line
!= '\0' && *line
!= '\n') {
164 /* print all lines to the window. */
165 void fill_window(WINDOW
*win
, const char *text
)
168 int total_lines
= get_line_no(text
);
172 /* do not go over end of line */
173 total_lines
= min(total_lines
, y
);
174 for (i
= 0; i
< total_lines
; i
++) {
176 const char *line
= get_line(text
, i
);
177 int len
= get_line_length(line
);
178 strncpy(tmp
, line
, min(len
, x
));
180 mvwprintw(win
, i
, 0, "%s", tmp
);
184 /* get the message, and buttons.
185 * each button must be a char*
186 * return the selected button
188 * this dialog is used for 2 different things:
189 * 1) show a text box, no buttons.
190 * 2) show a dialog, with horizontal buttons
192 int btn_dialog(WINDOW
*main_window
, const char *msg
, int btn_num
, ...)
205 ITEM
*btns
[btn_num
+1];
210 va_start(ap
, btn_num
);
211 for (i
= 0; i
< btn_num
; i
++) {
212 btn
= va_arg(ap
, char *);
213 btns
[i
] = new_item(btn
, "");
214 btns_width
+= strlen(btn
)+1;
217 btns
[btn_num
] = NULL
;
219 /* find the widest line of msg: */
220 msg_lines
= get_line_no(msg
);
221 for (i
= 0; i
< msg_lines
; i
++) {
222 const char *line
= get_line(msg
, i
);
223 int len
= get_line_length(line
);
228 total_width
= max(msg_width
, btns_width
);
229 /* place dialog in middle of screen */
230 y
= (getmaxy(stdscr
)-(msg_lines
+4))/2;
231 x
= (getmaxx(stdscr
)-(total_width
+4))/2;
234 /* create the windows */
236 win_rows
= msg_lines
+4;
238 win_rows
= msg_lines
+2;
240 win
= newwin(win_rows
, total_width
+4, y
, x
);
242 menu_win
= derwin(win
, 1, btns_width
, win_rows
-2,
243 1+(total_width
+2-btns_width
)/2);
244 menu
= new_menu(btns
);
245 msg_win
= derwin(win
, win_rows
-2, msg_width
, 1,
246 1+(total_width
+2-msg_width
)/2);
248 set_menu_fore(menu
, attr_dialog_menu_fore
);
249 set_menu_back(menu
, attr_dialog_menu_back
);
251 wattrset(win
, attr_dialog_box
);
255 wattrset(msg_win
, attr_dialog_text
);
256 fill_window(msg_win
, msg
);
258 set_menu_win(menu
, win
);
259 set_menu_sub(menu
, menu_win
);
260 set_menu_format(menu
, 1, btn_num
);
261 menu_opts_off(menu
, O_SHOWDESC
);
262 menu_opts_off(menu
, O_SHOWMATCH
);
263 menu_opts_on(menu
, O_ONEVALUE
);
264 menu_opts_on(menu
, O_NONCYCLIC
);
265 set_menu_mark(menu
, "");
270 refresh_all_windows(main_window
);
271 while ((res
= wgetch(win
))) {
274 menu_driver(menu
, REQ_LEFT_ITEM
);
277 menu_driver(menu
, REQ_RIGHT_ITEM
);
280 case 27: /* ESCAPE */
287 refresh_all_windows(main_window
);
289 if (res
== 10 || res
== ' ') {
290 res
= item_index(current_item(menu
));
292 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
293 res
== KEY_F(F_EXIT
)) {
301 for (i
= 0; i
< btn_num
; i
++)
308 int dialog_inputbox(WINDOW
*main_window
,
309 const char *title
, const char *prompt
,
310 const char *init
, char **resultp
, int *result_len
)
312 int prompt_lines
= 0;
313 int prompt_width
= 0;
318 int i
, x
, y
, lines
, columns
, win_lines
, win_cols
;
320 int cursor_position
= strlen(init
);
322 char *result
= *resultp
;
324 getmaxyx(stdscr
, lines
, columns
);
326 if (strlen(init
)+1 > *result_len
) {
327 *result_len
= strlen(init
)+1;
328 *resultp
= result
= xrealloc(result
, *result_len
);
331 /* find the widest line of msg: */
332 prompt_lines
= get_line_no(prompt
);
333 for (i
= 0; i
< prompt_lines
; i
++) {
334 const char *line
= get_line(prompt
, i
);
335 int len
= get_line_length(line
);
336 prompt_width
= max(prompt_width
, len
);
340 prompt_width
= max(prompt_width
, strlen(title
));
342 win_lines
= min(prompt_lines
+6, lines
-2);
343 win_cols
= min(prompt_width
+7, columns
-2);
344 prompt_lines
= max(win_lines
-6, 0);
345 prompt_width
= max(win_cols
-7, 0);
347 /* place dialog in middle of screen */
348 y
= (lines
-win_lines
)/2;
349 x
= (columns
-win_cols
)/2;
351 strncpy(result
, init
, *result_len
);
353 /* create the windows */
354 win
= newwin(win_lines
, win_cols
, y
, x
);
355 prompt_win
= derwin(win
, prompt_lines
+1, prompt_width
, 2, 2);
356 form_win
= derwin(win
, 1, prompt_width
, prompt_lines
+3, 2);
357 keypad(form_win
, TRUE
);
359 wattrset(form_win
, attr_input_field
);
361 wattrset(win
, attr_input_box
);
363 wattrset(win
, attr_input_heading
);
365 mvwprintw(win
, 0, 3, "%s", title
);
368 wattrset(prompt_win
, attr_input_text
);
369 fill_window(prompt_win
, prompt
);
371 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
372 cursor_form_win
= min(cursor_position
, prompt_width
-1);
373 mvwprintw(form_win
, 0, 0, "%s",
374 result
+ cursor_position
-cursor_form_win
);
377 panel
= new_panel(win
);
379 /* show the cursor */
383 refresh_all_windows(main_window
);
384 while ((res
= wgetch(form_win
))) {
385 int len
= strlen(result
);
388 case 27: /* ESCAPE */
396 if (cursor_position
> 0) {
397 memmove(&result
[cursor_position
-1],
398 &result
[cursor_position
],
399 len
-cursor_position
+1);
406 if (cursor_position
>= 0 && cursor_position
< len
) {
407 memmove(&result
[cursor_position
],
408 &result
[cursor_position
+1],
409 len
-cursor_position
+1);
415 if (cursor_position
< len
) {
422 if (cursor_position
> 0) {
432 cursor_position
= len
;
433 cursor_form_win
= min(cursor_position
, prompt_width
-1);
436 if ((isgraph(res
) || isspace(res
))) {
437 /* one for new char, one for '\0' */
438 if (len
+2 > *result_len
) {
440 *resultp
= result
= realloc(result
,
443 /* insert the char at the proper position */
444 memmove(&result
[cursor_position
+1],
445 &result
[cursor_position
],
446 len
-cursor_position
+1);
447 result
[cursor_position
] = res
;
452 mvprintw(0, 0, "unknown key: %d\n", res
);
456 if (cursor_form_win
< 0)
458 else if (cursor_form_win
> prompt_width
-1)
459 cursor_form_win
= prompt_width
-1;
461 wmove(form_win
, 0, 0);
463 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
464 mvwprintw(form_win
, 0, 0, "%s",
465 result
+ cursor_position
-cursor_form_win
);
466 wmove(form_win
, 0, cursor_form_win
);
468 refresh_all_windows(main_window
);
473 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
474 res
== KEY_F(F_EXIT
)) {
477 } else if (res
== KEY_F(F_HELP
)) {
483 /* hide the cursor */
492 /* refresh all windows in the correct order */
493 void refresh_all_windows(WINDOW
*main_window
)
496 touchwin(main_window
);
500 void show_scroll_win(WINDOW
*main_window
,
504 (void)show_scroll_win_ext(main_window
, title
, (char *)text
, NULL
, NULL
, NULL
, NULL
);
507 /* layman's scrollable window... */
508 int show_scroll_win_ext(WINDOW
*main_window
, const char *title
, char *text
,
509 int *vscroll
, int *hscroll
,
510 extra_key_cb_fn extra_key_cb
, void *data
)
513 int total_lines
= get_line_no(text
);
514 int x
, y
, lines
, columns
;
515 int start_x
= 0, start_y
= 0;
516 int text_lines
= 0, text_cols
= 0;
531 getmaxyx(stdscr
, lines
, columns
);
533 /* find the widest line of msg: */
534 total_lines
= get_line_no(text
);
535 for (i
= 0; i
< total_lines
; i
++) {
536 const char *line
= get_line(text
, i
);
537 int len
= get_line_length(line
);
538 total_cols
= max(total_cols
, len
+2);
542 pad
= newpad(total_lines
+10, total_cols
+10);
543 wattrset(pad
, attr_scrollwin_text
);
544 fill_window(pad
, text
);
546 win_lines
= min(total_lines
+4, lines
-2);
547 win_cols
= min(total_cols
+2, columns
-2);
548 text_lines
= max(win_lines
-4, 0);
549 text_cols
= max(win_cols
-2, 0);
551 /* place window in middle of screen */
552 y
= (lines
-win_lines
)/2;
553 x
= (columns
-win_cols
)/2;
555 win
= newwin(win_lines
, win_cols
, y
, x
);
557 /* show the help in the help window, and show the help panel */
558 wattrset(win
, attr_scrollwin_box
);
560 wattrset(win
, attr_scrollwin_heading
);
561 mvwprintw(win
, 0, 3, " %s ", title
);
562 panel
= new_panel(win
);
564 /* handle scrolling */
566 copywin(pad
, win
, start_y
, start_x
, 2, 2, text_lines
,
572 attr_dialog_menu_fore
);
580 start_y
+= text_lines
-2;
584 start_y
-= text_lines
+2;
590 start_y
= total_lines
-text_lines
;
610 size_t start
= (get_line(text
, start_y
) - text
);
611 size_t end
= (get_line(text
, start_y
+ text_lines
) - text
);
613 if (extra_key_cb(res
, start
, end
, data
)) {
619 if (res
== 0 || res
== 10 || res
== 27 || res
== 'q' ||
620 res
== KEY_F(F_HELP
) || res
== KEY_F(F_BACK
) ||
621 res
== KEY_F(F_EXIT
))
625 if (start_y
>= total_lines
-text_lines
)
626 start_y
= total_lines
-text_lines
;
629 if (start_x
>= total_cols
-text_cols
)
630 start_x
= total_cols
-text_cols
;
639 refresh_all_windows(main_window
);