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