]> andersk Git - moira.git/blob - clients/moira/menu.c
Here's another, which is probably better.
[moira.git] / clients / moira / menu.c
1 /*
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  *
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
27 #ifndef lint
28 static char rcsid_menu_c[] = "$Header$";
29 #endif lint
30
31 #include <stdio.h>
32 #include <curses.h>
33 #include <ctype.h>
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
41 char *strcpy();
42 char *strncpy();
43
44 /* Structure for holding current displayed menu */
45 struct 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
53 #define NULLMS ((struct menu_screen *) 0)
54
55 Menu *top_menu;                 /* Root for command search */
56
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  */
63 Start_menu(m)
64 Menu *m;
65 {
66     struct menu_screen *make_ms();
67
68     if(initscr() == ERR) {
69         fputs("Can't initialize curses!\n", stderr);
70         Start_no_menu(m);
71     }
72     raw();                      /* We parse & print everything ourselves */
73     noecho();
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 */
77     endwin();
78 }
79
80 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
81 Start_no_menu(m)
82 Menu *m;
83 {
84     cur_ms = NULLMS;
85     top_menu = m;
86     (void) Do_menu(m);
87 }
88
89 /*
90  * Create a new menu screen template with the specified menu length
91  * and return it.
92  */
93 struct menu_screen *make_ms(length)
94 int 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);
115     (void) wmove(ms->ms_input, 0, 0);
116     (void) wclear(ms->ms_screen);
117
118     return(ms);
119 }
120
121 /*
122  * This routine destroys a menu_screen.
123  */
124 destroy_ms(ms)
125 struct menu_screen *ms;
126 {
127     delwin(ms->ms_title);
128     delwin(ms->ms_menu);
129     delwin(ms->ms_input);
130     delwin(ms->ms_screen);
131     free((char *)ms);
132 }
133
134 /*
135  * This guy actually puts up the menu
136  */
137 int Do_menu(m)
138 Menu *m;
139 {
140     struct menu_screen *my_ms, *old_cur_ms;
141     char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
142     char buf[MAX_ARGC * MAX_ARGLEN];
143     char *argv[MAX_ARGC];
144     int line;
145     int i;
146     struct menu_line *command, *Find_command();
147     int argc;
148     int quitflag;
149
150     /* Entry function gets called with old menu_screen still current */
151     if(m->m_entry != NULLFUNC) m->m_entry(m);
152
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);
158
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.");
175
176     }
177
178     for(;;) {
179         /* This will be set by a return val from func or submenu */
180         quitflag = DM_NORMAL;
181         /* This is here because we may be coming from another menu */
182         if(cur_ms != NULL) touchwin(my_ms->ms_screen);
183         /* Get a command */
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")) {
197             /* here if it's either return or quit */
198             if(cur_ms != NULLMS) {
199                 cur_ms = old_cur_ms;
200                 destroy_ms(my_ms);
201             }
202             if(m->m_exit != NULLFUNC) m->m_exit(m);
203             return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
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);
233         } else {
234             /* If it's got neither, something is wrong */
235             Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
236         }
237         if(quitflag == DM_QUIT) {
238             if(cur_ms != NULLMS) {
239                 cur_ms = old_cur_ms;
240                 destroy_ms(my_ms);
241             }
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 */
249 Prompt_input(prompt, buf, buflen)
250 char *prompt;
251 char *buf;
252 int buflen;
253 {
254     int c;
255     char *p;
256     int y, x, oldx;
257
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;
291             }
292         }
293     } else {
294         printf("%s", prompt);
295         (void) gets(buf);
296         Start_paging();
297         return;
298     }
299 }
300
301 int 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 */
306 Start_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 */
316 Stop_paging()
317 {
318     lines_left = -1;
319 }
320
321 /* Print a message in the input window of cur_ms */
322 Put_message(msg)
323 char *msg;
324 {
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     }
353 }
354
355 /* Refresh a menu_screen onto the real screen */
356 refresh_ms(ms)
357 struct menu_screen *ms;
358 {
359     int y, x;
360
361     getyx(ms->ms_input, y, x);
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 */
369 Parse_words(buf, argv, n)
370 char *buf;
371 char *argv[];
372 int 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 */
393 struct menu_line *find_command_from(c, m, d)
394 char *c;
395 struct menu *m;
396 int 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);
414 }
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 */
419 struct menu_line *Find_command(command)
420 char *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.067849 seconds and 5 git commands to generate.