]> andersk Git - moira.git/blame - clients/moira/menu.c
Here's another, which is probably better.
[moira.git] / clients / moira / menu.c
CommitLineData
07c56447 1/*
2aaf83c2 2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
4 * "mit-copyright.h".
5 *
6 * $Source$
7 * $Author$
8 * $Header$
9 * $Log$
10 * Revision 1.2 1987-08-03 04:16:51 wesommer
11 * Here's another, which is probably better.
12 *
13 * Revision 1.1 87/07/31 18:02:23 ambar
14 * Initial revision
15 *
16 *
07c56447 17 * Generic menu system module.
18 *
19 * Basically, we define an enormous tree structure which represents the
20 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
21 * also use the structure for a command-based system.
22 *
23 * By making the menu descriptions so general, we can ease porting to just
24 * about anything.
25 */
26
2aaf83c2 27#ifndef lint
28static char rcsid_menu_c[] = "$Header$";
29#endif lint
30
07c56447 31#include <stdio.h>
32#include <curses.h>
2aaf83c2 33#include <ctype.h>
07c56447 34#include "menu.h"
35
36#define MAX(A,B) ((A) > (B) ? (A) : (B))
37#define MIN(A,B) ((A) < (B) ? (A) : (B))
38
39#define MIN_INPUT 2 /* Minimum number of lines for input window */
40
2aaf83c2 41char *strcpy();
42char *strncpy();
43
07c56447 44/* Structure for holding current displayed menu */
45struct menu_screen {
46 WINDOW *ms_screen; /* Window for this menu */
47 WINDOW *ms_title; /* Title subwindow */
48 WINDOW *ms_menu; /* Menu subwindow */
49 WINDOW *ms_input; /* Input subwindow */
50 int ms_input_y; /* Input subwindow reference coordinate */
51} *cur_ms;
52
2aaf83c2 53#define NULLMS ((struct menu_screen *) 0)
54
55Menu *top_menu; /* Root for command search */
56
07c56447 57/*
58 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
59 * and a quit in any submenu should unwind back to here. (it might not,
60 * if user functions which run their own menus don't cooperate.)
61 * Start_menu should only be called once, at the start of the program.
62 */
63Start_menu(m)
64Menu *m;
65{
2aaf83c2 66 struct menu_screen *make_ms();
67
07c56447 68 if(initscr() == ERR) {
69 fputs("Can't initialize curses!\n", stderr);
2aaf83c2 70 Start_no_menu(m);
07c56447 71 }
72 raw(); /* We parse & print everything ourselves */
73 noecho();
2aaf83c2 74 cur_ms = make_ms(0); /* So we always have some current menu_screen */
75 top_menu = m;
76 (void) Do_menu(m); /* Run the menu */
07c56447 77 endwin();
78}
79
2aaf83c2 80/* Like Start_menu, except it doesn't print menus and doesn't use curses */
81Start_no_menu(m)
82Menu *m;
83{
84 cur_ms = NULLMS;
85 top_menu = m;
86 (void) Do_menu(m);
87}
88
07c56447 89/*
90 * Create a new menu screen template with the specified menu length
91 * and return it.
92 */
93struct menu_screen *make_ms(length)
94int length;
95{
96 struct menu_screen *ms;
97 char *malloc();
98
99 if(MAX_TITLE + length + MIN_INPUT > LINES) {
100 fputs("Menu too big!\n", stderr);
101 exit(2);
102 }
103
104 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
105
106 ms->ms_screen = newwin(0, 0, 0, 0);
107 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
108 ms->ms_menu = subwin(ms->ms_screen,
109 length, 0, MAX_TITLE, 0);
110 ms->ms_input = subwin(ms->ms_screen, 0, 0,
111 ms->ms_input_y = MAX_TITLE + length,
112 0);
113
114 scrollok(ms->ms_input, TRUE);
2aaf83c2 115 (void) wmove(ms->ms_input, 0, 0);
116 (void) wclear(ms->ms_screen);
07c56447 117
118 return(ms);
119}
120
121/*
122 * This routine destroys a menu_screen.
123 */
124destroy_ms(ms)
125struct menu_screen *ms;
126{
127 delwin(ms->ms_title);
128 delwin(ms->ms_menu);
129 delwin(ms->ms_input);
130 delwin(ms->ms_screen);
2aaf83c2 131 free((char *)ms);
07c56447 132}
133
134/*
135 * This guy actually puts up the menu
136 */
137int Do_menu(m)
138Menu *m;
139{
140 struct menu_screen *my_ms, *old_cur_ms;
141 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
2aaf83c2 142 char buf[MAX_ARGC * MAX_ARGLEN];
07c56447 143 char *argv[MAX_ARGC];
144 int line;
2aaf83c2 145 int i;
146 struct menu_line *command, *Find_command();
07c56447 147 int argc;
148 int quitflag;
149
07c56447 150 /* Entry function gets called with old menu_screen still current */
151 if(m->m_entry != NULLFUNC) m->m_entry(m);
152
2aaf83c2 153 /* The following get run only in curses mode */
154 if(cur_ms != NULLMS) {
155 /* Get a menu_screen */
156 old_cur_ms = cur_ms;
157 cur_ms = my_ms = make_ms(m->m_length + 2);
07c56447 158
2aaf83c2 159 /* Now print the title and the menu */
160 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS - strlen(m->m_title))>>1));
161 (void) wstandout(my_ms->ms_title);
162 (void) waddstr(my_ms->ms_title, m->m_title);
163 (void) wstandend(my_ms->ms_title);
164
165 for(line = 0; line < m->m_length; line++) {
166 (void) wmove(my_ms->ms_menu, line, 0);
167 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line+1,
168 m->m_lines[line].ml_command,
169 m->m_lines[line].ml_doc);
170 }
171 (void) wmove(my_ms->ms_menu, line++, 0);
172 (void) waddstr(my_ms->ms_menu, " r. (return ) Return to previous menu.");
173 (void) wmove(my_ms->ms_menu, line, 0);
174 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
07c56447 175
07c56447 176 }
07c56447 177
07c56447 178 for(;;) {
179 /* This will be set by a return val from func or submenu */
180 quitflag = DM_NORMAL;
2aaf83c2 181 /* This is here because we may be coming from another menu */
182 if(cur_ms != NULL) touchwin(my_ms->ms_screen);
07c56447 183 /* Get a command */
2aaf83c2 184 Prompt_input("Command: ", buf, sizeof(buf));
185 /* Parse it into the argument list */
186 /* If there's nothing there, try again */
187 /* Initialize argv */
188 for(argc = 0; argc < MAX_ARGC; argc++) argv[argc] = argvals[argc];
189
190 if((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0) continue;
191 if((line = atoi(argv[0])) > 0 && line <= m->m_length) {
192 command = &m->m_lines[line-1];
193 } else if(!strcmp(argv[0], "r")
194 || !strcmp(argv[0], "q")
195 || !strcmp(argv[0], "return")
196 || !strcmp(argv[0], "quit")) {
07c56447 197 /* here if it's either return or quit */
2aaf83c2 198 if(cur_ms != NULLMS) {
199 cur_ms = old_cur_ms;
200 destroy_ms(my_ms);
201 }
07c56447 202 if(m->m_exit != NULLFUNC) m->m_exit(m);
203 return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
2aaf83c2 204 /* finally, try to find it using Find_command */
205 } else if ((command = Find_command (argvals[0])) ==
206 (struct menu_line *) 0) {
207 Put_message("Command not recognized");
208 continue;
209 }
210 /* If we got to here, command is a valid menu_line */
211 /* Send the offical command name into the argv */
212 (void) strcpy(argvals[0], command->ml_command);
213 /* Show that we're working on it */
214 Put_message(command->ml_doc);
215 /* Print args that we've already got */
216 for(i = 1; i < argc; i++) {
217 if(command->ml_args[i].ma_prompt == NULL) break;
218 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
219 argv[i]);
220 Put_message(buf);
221 }
222 /* Get remaining arguments, if any */
223 for(; argc < command->ml_argc; argc++) {
224 Prompt_input(command->ml_args[argc].ma_prompt,
225 argvals[argc], sizeof(argvals[argc]));
226 }
227 if(command->ml_function != NULLFUNC) {
228 /* If it's got a function, call it */
229 quitflag = command->ml_function(argc, argv);
230 } else if(command->ml_submenu != NULLMENU) {
231 /* Else see if it is a submenu */
232 quitflag = Do_menu(command->ml_submenu);
07c56447 233 } else {
2aaf83c2 234 /* If it's got neither, something is wrong */
235 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
07c56447 236 }
237 if(quitflag == DM_QUIT) {
2aaf83c2 238 if(cur_ms != NULLMS) {
239 cur_ms = old_cur_ms;
240 destroy_ms(my_ms);
241 }
07c56447 242 if(m->m_exit != NULLFUNC) m->m_exit(m);
243 return(DM_QUIT);
244 }
245 }
246}
247
248/* Prompt the user for input in the input window of cur_ms */
249Prompt_input(prompt, buf, buflen)
250char *prompt;
251char *buf;
252int buflen;
253{
254 int c;
255 char *p;
256 int y, x, oldx;
257
2aaf83c2 258 if(cur_ms != NULLMS) {
259 (void) waddstr(cur_ms->ms_input, prompt);
260 getyx(cur_ms->ms_input, y, x);
261 oldx = x;
262 for (p = buf; p - buf < buflen; ) {
263 (void) wmove(cur_ms->ms_input, y, x);
264 (void) wclrtoeol(cur_ms->ms_input);
265 refresh_ms(cur_ms);
266 c = getchar();
267 switch (c) {
268 case 'L' & 037:
269 touchwin(cur_ms->ms_screen);
270 break;
271 case '\n': case '\r':
272 (void) waddch(cur_ms->ms_input, '\n');
273 *p = '\0';
274 Start_paging();
275 return;
276 case '\b':
277 case '\177':
278 if (p > buf) {
279 p--;
280 x--;
281 }
282 break;
283 case 'U' & 037: case '\007': case '\033':
284 x = oldx;
285 break;
286 default:
287 (void) waddch(cur_ms->ms_input, c);
288 *p++ = c;
289 x++;
290 break;
07c56447 291 }
07c56447 292 }
2aaf83c2 293 } else {
294 printf("%s", prompt);
295 (void) gets(buf);
296 Start_paging();
297 return;
07c56447 298 }
299}
300
2aaf83c2 301int lines_left;
302
303/* Start paging */
304/* This routine will cause the most recently put message to be the
305 one at the top of the screen when a ---More--- prompt is displayed */
306Start_paging()
307{
308 if(cur_ms != NULLMS) {
309 lines_left = LINES - cur_ms->ms_input_y - 1;
310 } else {
311 lines_left = 23;
312 }
313}
314
315/* Turn off paging */
316Stop_paging()
317{
318 lines_left = -1;
319}
320
07c56447 321/* Print a message in the input window of cur_ms */
322Put_message(msg)
323char *msg;
324{
2aaf83c2 325 int y, x;
326
327 if(lines_left >= 0) {
328 if(--lines_left == 0) {
329 /* Give the user a more prompt */
330 if(cur_ms != NULLMS) {
331 (void) wstandout(cur_ms->ms_input);
332 (void) wprintw(cur_ms->ms_input, "---More---");
333 (void) wstandend(cur_ms->ms_input);
334 refresh_ms(cur_ms);
335 (void) getchar(); /* We don't care what it is */
336 getyx(cur_ms->ms_input, y, x);
337 (void) wmove(cur_ms->ms_input, y, 0);
338 (void) wclrtoeol(cur_ms->ms_input);
339 } else {
340 printf("---More (hit return)---");
341 (void) getchar();
342 }
343 Start_paging(); /* Reset lines_left */
344 }
345 }
346
347 if(cur_ms != NULLMS) {
348 (void) wprintw(cur_ms->ms_input, "%s\n", msg);
349/* refresh_ms(cur_ms); */
350 } else {
351 puts(msg);
352 }
07c56447 353}
354
355/* Refresh a menu_screen onto the real screen */
356refresh_ms(ms)
357struct menu_screen *ms;
358{
359 int y, x;
360
361 getyx(ms->ms_input, y, x);
2aaf83c2 362 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
363 (void) wrefresh(ms->ms_screen);
364}
365
366/* Parse buf into a list of words, which will be placed in strings specified by
367 argv. Space for these strings must have already been allocated.
368 Only the first n characters of each word will be copied */
369Parse_words(buf, argv, n)
370char *buf;
371char *argv[];
372int n;
373{
374 char *start, *end; /* For sausage machine */
375 int argc;
376
377 start = buf;
378 for(argc = 0; argc < MAX_ARGC; argc++) {
379 while(isspace(*start)) start++; /* Kill whitespace */
380 if(*start == '\0') break; /* Nothing left */
381 /* Now find the end of the word */
382 for(end = start; *end != '\0' && !isspace(*end); end++);
383 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
384 argv[argc][MIN(end - start, n-1)] = '\0'; /* Terminate */
385 start = end;
386 }
387 return(argc);
388}
389
390/* This is the internal form of Find_command, which recursively searches
391 for a menu_line with command command in the specified menu */
392/* It will search to a maximum depth of d */
393struct menu_line *find_command_from(c, m, d)
394char *c;
395struct menu *m;
396int d;
397{
398 int line;
399 struct menu_line *maybe;
400
401 if(d < 0) return((struct menu_line *) 0); /* Too deep! */
402 for(line = 0; line < m->m_length; line++) {
403 if(!strcmp(c, m->m_lines[line].ml_command)) {
404 return(&m->m_lines[line]);
405 } else if(m->m_lines[line].ml_submenu != NULLMENU
406 && (maybe =
407 find_command_from(c, m->m_lines[line].ml_submenu, d-1))
408 != (struct menu_line *) 0) {
409 return(maybe);
410 }
411 }
412 /* If we got to here, nothing works */
413 return((struct menu_line *) 0);
07c56447 414}
2aaf83c2 415
416/* Find_command searches down the current menu tree */
417/* And returns a pointer to a menu_line with the specified command name */
418/* It returns (struct menu_line *) 0 if none is found */
419struct menu_line *Find_command(command)
420char *command;
421{
422 if(top_menu == NULLMENU) {
423 return((struct menu_line *) 0);
424 } else {
425 return(find_command_from(command, top_menu, MAX_MENU_DEPTH));
426 }
427}
428
429/*
430 * Local Variables:
431 * mode: c
432 * c-indent-level: 4
433 * c-continued-statement-offset: 4
434 * c-brace-offset: -4
435 * c-argdecl-indent: 4
436 * c-label-offset: -4
437 * End:
438 */
This page took 0.107498 seconds and 5 git commands to generate.