|
|
1.1 root 1: /*
2: * Copyright (C) 2008 Nir Tzachar <[email protected]?
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.