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
);
281 case 27: /* ESCAPE */
288 refresh_all_windows(main_window
);
290 if (res
== 10 || res
== ' ') {
291 res
= item_index(current_item(menu
));
293 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
294 res
== KEY_F(F_EXIT
)) {
302 for (i
= 0; i
< btn_num
; i
++)
309 int dialog_inputbox(WINDOW
*main_window
,
310 const char *title
, const char *prompt
,
311 const char *init
, char **resultp
, int *result_len
)
313 int prompt_lines
= 0;
314 int prompt_width
= 0;
319 int i
, x
, y
, lines
, columns
, win_lines
, win_cols
;
321 int cursor_position
= strlen(init
);
323 char *result
= *resultp
;
325 getmaxyx(stdscr
, lines
, columns
);
327 if (strlen(init
)+1 > *result_len
) {
328 *result_len
= strlen(init
)+1;
329 *resultp
= result
= xrealloc(result
, *result_len
);
332 /* find the widest line of msg: */
333 prompt_lines
= get_line_no(prompt
);
334 for (i
= 0; i
< prompt_lines
; i
++) {
335 const char *line
= get_line(prompt
, i
);
336 int len
= get_line_length(line
);
337 prompt_width
= max(prompt_width
, len
);
341 prompt_width
= max(prompt_width
, strlen(title
));
343 win_lines
= min(prompt_lines
+6, lines
-2);
344 win_cols
= min(prompt_width
+7, columns
-2);
345 prompt_lines
= max(win_lines
-6, 0);
346 prompt_width
= max(win_cols
-7, 0);
348 /* place dialog in middle of screen */
349 y
= (lines
-win_lines
)/2;
350 x
= (columns
-win_cols
)/2;
352 strncpy(result
, init
, *result_len
);
354 /* create the windows */
355 win
= newwin(win_lines
, win_cols
, y
, x
);
356 prompt_win
= derwin(win
, prompt_lines
+1, prompt_width
, 2, 2);
357 form_win
= derwin(win
, 1, prompt_width
, prompt_lines
+3, 2);
358 keypad(form_win
, TRUE
);
360 wattrset(form_win
, attr_input_field
);
362 wattrset(win
, attr_input_box
);
364 wattrset(win
, attr_input_heading
);
366 mvwprintw(win
, 0, 3, "%s", title
);
369 wattrset(prompt_win
, attr_input_text
);
370 fill_window(prompt_win
, prompt
);
372 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
373 cursor_form_win
= min(cursor_position
, prompt_width
-1);
374 mvwprintw(form_win
, 0, 0, "%s",
375 result
+ cursor_position
-cursor_form_win
);
378 panel
= new_panel(win
);
380 /* show the cursor */
384 refresh_all_windows(main_window
);
385 while ((res
= wgetch(form_win
))) {
386 int len
= strlen(result
);
389 case 27: /* ESCAPE */
397 if (cursor_position
> 0) {
398 memmove(&result
[cursor_position
-1],
399 &result
[cursor_position
],
400 len
-cursor_position
+1);
407 if (cursor_position
>= 0 && cursor_position
< len
) {
408 memmove(&result
[cursor_position
],
409 &result
[cursor_position
+1],
410 len
-cursor_position
+1);
416 if (cursor_position
< len
) {
423 if (cursor_position
> 0) {
433 cursor_position
= len
;
434 cursor_form_win
= min(cursor_position
, prompt_width
-1);
437 if ((isgraph(res
) || isspace(res
))) {
438 /* one for new char, one for '\0' */
439 if (len
+2 > *result_len
) {
441 *resultp
= result
= realloc(result
,
444 /* insert the char at the proper position */
445 memmove(&result
[cursor_position
+1],
446 &result
[cursor_position
],
447 len
-cursor_position
+1);
448 result
[cursor_position
] = res
;
453 mvprintw(0, 0, "unknown key: %d\n", res
);
457 if (cursor_form_win
< 0)
459 else if (cursor_form_win
> prompt_width
-1)
460 cursor_form_win
= prompt_width
-1;
462 wmove(form_win
, 0, 0);
464 mvwprintw(form_win
, 0, 0, "%*s", prompt_width
, " ");
465 mvwprintw(form_win
, 0, 0, "%s",
466 result
+ cursor_position
-cursor_form_win
);
467 wmove(form_win
, 0, cursor_form_win
);
469 refresh_all_windows(main_window
);
474 } else if (res
== 27 || res
== KEY_F(F_BACK
) ||
475 res
== KEY_F(F_EXIT
)) {
478 } else if (res
== KEY_F(F_HELP
)) {
484 /* hide the cursor */
493 /* refresh all windows in the correct order */
494 void refresh_all_windows(WINDOW
*main_window
)
497 touchwin(main_window
);
501 void show_scroll_win(WINDOW
*main_window
,
505 (void)show_scroll_win_ext(main_window
, title
, (char *)text
, NULL
, NULL
, NULL
, NULL
);
508 /* layman's scrollable window... */
509 int show_scroll_win_ext(WINDOW
*main_window
, const char *title
, char *text
,
510 int *vscroll
, int *hscroll
,
511 extra_key_cb_fn extra_key_cb
, void *data
)
514 int total_lines
= get_line_no(text
);
515 int x
, y
, lines
, columns
;
516 int start_x
= 0, start_y
= 0;
517 int text_lines
= 0, text_cols
= 0;
532 getmaxyx(stdscr
, lines
, columns
);
534 /* find the widest line of msg: */
535 total_lines
= get_line_no(text
);
536 for (i
= 0; i
< total_lines
; i
++) {
537 const char *line
= get_line(text
, i
);
538 int len
= get_line_length(line
);
539 total_cols
= max(total_cols
, len
+2);
543 pad
= newpad(total_lines
+10, total_cols
+10);
544 wattrset(pad
, attr_scrollwin_text
);
545 fill_window(pad
, text
);
547 win_lines
= min(total_lines
+4, lines
-2);
548 win_cols
= min(total_cols
+2, columns
-2);
549 text_lines
= max(win_lines
-4, 0);
550 text_cols
= max(win_cols
-2, 0);
552 /* place window in middle of screen */
553 y
= (lines
-win_lines
)/2;
554 x
= (columns
-win_cols
)/2;
556 win
= newwin(win_lines
, win_cols
, y
, x
);
558 /* show the help in the help window, and show the help panel */
559 wattrset(win
, attr_scrollwin_box
);
561 wattrset(win
, attr_scrollwin_heading
);
562 mvwprintw(win
, 0, 3, " %s ", title
);
563 panel
= new_panel(win
);
565 /* handle scrolling */
567 copywin(pad
, win
, start_y
, start_x
, 2, 2, text_lines
,
573 attr_dialog_menu_fore
);
581 start_y
+= text_lines
-2;
585 start_y
-= text_lines
+2;
591 start_y
= total_lines
-text_lines
;
611 size_t start
= (get_line(text
, start_y
) - text
);
612 size_t end
= (get_line(text
, start_y
+ text_lines
) - text
);
614 if (extra_key_cb(res
, start
, end
, data
)) {
620 if (res
== 0 || res
== 10 || res
== 27 || res
== 'q' ||
621 res
== KEY_F(F_HELP
) || res
== KEY_F(F_BACK
) ||
622 res
== KEY_F(F_EXIT
))
626 if (start_y
>= total_lines
-text_lines
)
627 start_y
= total_lines
-text_lines
;
630 if (start_x
>= total_cols
-text_cols
)
631 start_x
= total_cols
-text_cols
;
640 refresh_all_windows(main_window
);