]> andersk Git - moira.git/blob - clients/moira/menu.c
ee75bdeda726ebf908aec5bda723d1e4ebd820b7
[moira.git] / clients / moira / menu.c
1 /*
2  * Generic menu system module.
3  *
4  * Basically, we define an enormous tree structure which represents the
5  * menu.  Some extra pieces (ml_command, ma_doc) get thrown in so we can
6  * also use the structure for a command-based system.
7  *
8  * By making the menu descriptions so general, we can ease porting to just
9  * about anything.
10  */
11
12 #include <stdio.h>
13 #include <curses.h>
14 #include "menu.h"
15
16 #define MAX(A,B) ((A) > (B) ? (A) : (B))
17 #define MIN(A,B) ((A) < (B) ? (A) : (B))
18
19 #define MIN_INPUT 2             /* Minimum number of lines for input window */
20
21 /* Structure for holding current displayed menu */
22 struct menu_screen {
23     WINDOW *ms_screen;          /* Window for this menu */
24     WINDOW *ms_title;           /* Title subwindow */
25     WINDOW *ms_menu;            /* Menu subwindow */
26     WINDOW *ms_input;           /* Input subwindow */
27     int ms_input_y;             /* Input subwindow reference coordinate */
28 } *cur_ms;
29
30 /*
31  * Start_menu takes a menu as an argument.  It initializes curses u.s.w.,
32  * and a quit in any submenu should unwind back to here.  (it might not,
33  * if user functions which run their own menus don't cooperate.)
34  * Start_menu should only be called once, at the start of the program.
35  */
36 Start_menu(m)
37 Menu *m;
38 {
39     if(initscr() == ERR) {
40         fputs("Can't initialize curses!\n", stderr);
41         exit(1);
42     }
43     raw();                      /* We parse & print everything ourselves */
44     noecho();
45     Do_menu(m);                 /* Run the menu */
46     endwin();
47 }
48
49 /*
50  * Create a new menu screen template with the specified menu length
51  * and return it.
52  */
53 struct menu_screen *make_ms(length)
54 int length;
55 {
56     struct menu_screen *ms;
57     char *malloc();
58
59     if(MAX_TITLE + length + MIN_INPUT > LINES) {
60         fputs("Menu too big!\n", stderr);
61         exit(2);
62     }
63
64     ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
65
66     ms->ms_screen = newwin(0, 0, 0, 0);
67     ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
68     ms->ms_menu = subwin(ms->ms_screen,
69                          length, 0, MAX_TITLE, 0);
70     ms->ms_input = subwin(ms->ms_screen, 0, 0,
71                           ms->ms_input_y = MAX_TITLE + length,
72                           0);
73
74     scrollok(ms->ms_input, TRUE);
75     wmove(ms->ms_input, 0, 0);
76     wclear(ms->ms_screen);
77
78     return(ms);
79 }
80
81 /*
82  * This routine destroys a menu_screen.
83  */
84 destroy_ms(ms)
85 struct menu_screen *ms;
86 {
87     delwin(ms->ms_title);
88     delwin(ms->ms_menu);
89     delwin(ms->ms_input);
90     delwin(ms->ms_screen);
91     free(ms);
92 }
93
94 /*
95  * This guy actually puts up the menu
96  */
97 int Do_menu(m)
98 Menu *m;
99 {
100     struct menu_screen *my_ms, *old_cur_ms;
101     char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
102     char *argv[MAX_ARGC];
103     int line;
104     int argc;
105     int quitflag;
106
107     /* Initialize argv */
108     for(argc = 0; argc < MAX_ARGC; argc++) argv[argc] = argvals[argc];
109
110     /* Entry function gets called with old menu_screen still current */
111     if(m->m_entry != NULLFUNC) m->m_entry(m);
112
113     /* Get a menu_screen */
114     old_cur_ms = cur_ms;
115     cur_ms = my_ms = make_ms(m->m_length + 2);
116
117     /* Now print the title and the menu */
118     wmove(my_ms->ms_title, 0, MAX(0, (COLS - strlen(m->m_title))>>1));
119     wstandout(my_ms->ms_title);
120     waddstr(my_ms->ms_title, m->m_title);
121     wstandend(my_ms->ms_title);
122
123     for(line = 0; line < m->m_length; line++) {
124         wmove(my_ms->ms_menu, line, 0);
125         wprintw(my_ms->ms_menu, "%2d. (%-8s) %s.", line+1,
126                 m->m_lines[line].ml_command,
127                 m->m_lines[line].ml_doc);
128     }
129     wmove(my_ms->ms_menu, line++, 0);
130     waddstr(my_ms->ms_menu, " r. (return  ) Return to previous menu.");
131     wmove(my_ms->ms_menu, line, 0);
132     waddstr(my_ms->ms_menu, " q. (quit    ) Quit.");
133
134     refresh_ms(my_ms);
135     for(;;) {
136         /* This will be set by a return val from func or submenu */
137         quitflag = DM_NORMAL;
138         /* Get a command */
139         Prompt_input("Command: ", argvals[0], sizeof(argvals[0]));
140         /* See if it matches anything (this is kind of dumb, for now) */
141         if((line = atoi(argvals[0])) > 0 && line <= m->m_length) {
142             line--;
143             /* Send the command into the argv */
144             strcpy(argvals[0], m->m_lines[line].ml_command);
145             /* Show that we're working on it */
146             Put_message(m->m_lines[line].ml_doc);
147             /* Get arguments, if any */
148             for(argc = 1; argc < m->m_lines[line].ml_argc; argc++) {
149                 Prompt_input(m->m_lines[line].ml_args[argc].ma_prompt,
150                              argvals[argc], sizeof(argvals[argc]));
151             }
152             if(m->m_lines[line].ml_function != NULLFUNC) {
153                 /* If it's got a function, call it */
154                 quitflag = m->m_lines[line].ml_function(argv, argc);
155             } else if(m->m_lines[line].ml_submenu != NULLMENU) {
156                 /* Else see if it is a submenu */
157                 quitflag = Do_menu(m->m_lines[line].ml_submenu);
158             } else {
159                 /* If it's got neither, something is wrong */
160                 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
161             }
162         } else if(!strcmp(argv[0], "r") || !strcmp(argv[0], "q")) {
163             /* here if it's either return or quit */
164             cur_ms = old_cur_ms;
165             destroy_ms(my_ms);
166             if(m->m_exit != NULLFUNC) m->m_exit(m);
167             return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
168         } else {
169             /* Couldn't find it, give up */
170             Put_message("Command not recognized");
171             continue;           /* No good */
172         }
173         if(quitflag == DM_QUIT) {
174             cur_ms = old_cur_ms;
175             destroy_ms(my_ms);
176             if(m->m_exit != NULLFUNC) m->m_exit(m);
177             return(DM_QUIT);
178         }
179     }
180 }
181
182 /* Prompt the user for input in the input window of cur_ms */
183 Prompt_input(prompt, buf, buflen)
184 char *prompt;
185 char *buf;
186 int buflen;
187 {
188     int c;
189     char *p;
190     int y, x, oldx;
191
192     waddstr(cur_ms->ms_input, prompt);
193     getyx(cur_ms->ms_input, y, x);
194     oldx = x;
195     for (p = buf; p - buf < buflen; ) {
196         wmove(cur_ms->ms_input, y, x);
197         wclrtoeol(cur_ms->ms_input);
198         refresh_ms(cur_ms);
199         c = getchar();
200         switch (c) {
201         case 'L' & 037:
202             touchwin(cur_ms->ms_screen);
203             break;
204         case '\n': case '\r':
205             waddch(cur_ms->ms_input, '\n');
206             *p = '\0';
207             return;
208         case '\b':
209         case '\177':
210             if (p > buf) {
211                 p--;
212                 x--;
213             }
214             break;
215         case 'U' & 037: case '\007': case '\033':
216             x = oldx;
217             break;
218         default:
219             waddch(cur_ms->ms_input, c);
220             *p++ = c;
221             x++;
222             break;
223         }
224     }
225 }
226
227 /* Print a message in the input window of cur_ms */
228 Put_message(msg)
229 char *msg;
230 {
231     wprintw(cur_ms->ms_input, "%s\n", msg);
232     refresh_ms(cur_ms);
233 }
234
235 /* Refresh a menu_screen onto the real screen */
236 refresh_ms(ms)
237 struct menu_screen *ms;
238 {
239     int y, x;
240
241     getyx(ms->ms_input, y, x);
242     wmove(ms->ms_screen, y + ms->ms_input_y, x);
243     touchwin(ms->ms_screen);
244     wrefresh(ms->ms_screen);
245 }
This page took 0.043247 seconds and 3 git commands to generate.