]> andersk Git - moira.git/blame - clients/moira/menu.c
This one appears to work.
[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$
33a6b7f2 10 * Revision 1.3 1987-08-03 05:10:34 wesommer
11 * This one appears to work.
2aaf83c2 12 *
33a6b7f2 13 * Revision 1.2 87/08/03 04:16:51 wesommer
14 * Here's another, which is probably better.
15 *
2aaf83c2 16 * Revision 1.1 87/07/31 18:02:23 ambar
17 * Initial revision
18 *
19 *
07c56447 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
2aaf83c2 30#ifndef lint
31static char rcsid_menu_c[] = "$Header$";
32#endif lint
33
07c56447 34#include <stdio.h>
35#include <curses.h>
2aaf83c2 36#include <ctype.h>
07c56447 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
2aaf83c2 44char *strcpy();
45char *strncpy();
46
07c56447 47/* Structure for holding current displayed menu */
48struct 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
2aaf83c2 56#define NULLMS ((struct menu_screen *) 0)
57
58Menu *top_menu; /* Root for command search */
59
07c56447 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 */
66Start_menu(m)
67Menu *m;
68{
2aaf83c2 69 struct menu_screen *make_ms();
70
07c56447 71 if(initscr() == ERR) {
72 fputs("Can't initialize curses!\n", stderr);
2aaf83c2 73 Start_no_menu(m);
07c56447 74 }
75 raw(); /* We parse & print everything ourselves */
76 noecho();
2aaf83c2 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 */
07c56447 80 endwin();
81}
82
2aaf83c2 83/* Like Start_menu, except it doesn't print menus and doesn't use curses */
84Start_no_menu(m)
85Menu *m;
86{
87 cur_ms = NULLMS;
88 top_menu = m;
89 (void) Do_menu(m);
90}
91
07c56447 92/*
93 * Create a new menu screen template with the specified menu length
94 * and return it.
95 */
96struct menu_screen *make_ms(length)
97int 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);
2aaf83c2 118 (void) wmove(ms->ms_input, 0, 0);
119 (void) wclear(ms->ms_screen);
07c56447 120
121 return(ms);
122}
123
124/*
125 * This routine destroys a menu_screen.
126 */
127destroy_ms(ms)
128struct menu_screen *ms;
129{
130 delwin(ms->ms_title);
131 delwin(ms->ms_menu);
132 delwin(ms->ms_input);
133 delwin(ms->ms_screen);
2aaf83c2 134 free((char *)ms);
07c56447 135}
136
137/*
138 * This guy actually puts up the menu
139 */
140int Do_menu(m)
141Menu *m;
142{
143 struct menu_screen *my_ms, *old_cur_ms;
144 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
2aaf83c2 145 char buf[MAX_ARGC * MAX_ARGLEN];
07c56447 146 char *argv[MAX_ARGC];
147 int line;
2aaf83c2 148 int i;
149 struct menu_line *command, *Find_command();
07c56447 150 int argc;
151 int quitflag;
152
07c56447 153 /* Entry function gets called with old menu_screen still current */
154 if(m->m_entry != NULLFUNC) m->m_entry(m);
155
2aaf83c2 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);
07c56447 161
2aaf83c2 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.");
07c56447 178
07c56447 179 }
07c56447 180
07c56447 181 for(;;) {
182 /* This will be set by a return val from func or submenu */
183 quitflag = DM_NORMAL;
2aaf83c2 184 /* This is here because we may be coming from another menu */
185 if(cur_ms != NULL) touchwin(my_ms->ms_screen);
07c56447 186 /* Get a command */
2aaf83c2 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")) {
07c56447 200 /* here if it's either return or quit */
2aaf83c2 201 if(cur_ms != NULLMS) {
202 cur_ms = old_cur_ms;
203 destroy_ms(my_ms);
204 }
07c56447 205 if(m->m_exit != NULLFUNC) m->m_exit(m);
206 return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
2aaf83c2 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);
07c56447 236 } else {
2aaf83c2 237 /* If it's got neither, something is wrong */
238 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
07c56447 239 }
240 if(quitflag == DM_QUIT) {
2aaf83c2 241 if(cur_ms != NULLMS) {
242 cur_ms = old_cur_ms;
243 destroy_ms(my_ms);
244 }
07c56447 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 */
252Prompt_input(prompt, buf, buflen)
253char *prompt;
254char *buf;
255int buflen;
256{
257 int c;
258 char *p;
259 int y, x, oldx;
260
2aaf83c2 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;
07c56447 294 }
07c56447 295 }
2aaf83c2 296 } else {
297 printf("%s", prompt);
298 (void) gets(buf);
299 Start_paging();
300 return;
07c56447 301 }
302}
303
2aaf83c2 304int 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 */
309Start_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 */
319Stop_paging()
320{
321 lines_left = -1;
322}
323
07c56447 324/* Print a message in the input window of cur_ms */
325Put_message(msg)
326char *msg;
327{
2aaf83c2 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 }
07c56447 356}
357
358/* Refresh a menu_screen onto the real screen */
359refresh_ms(ms)
360struct menu_screen *ms;
361{
362 int y, x;
363
364 getyx(ms->ms_input, y, x);
2aaf83c2 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 */
372Parse_words(buf, argv, n)
373char *buf;
374char *argv[];
375int 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 */
396struct menu_line *find_command_from(c, m, d)
397char *c;
398struct menu *m;
399int 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);
07c56447 417}
2aaf83c2 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 */
422struct menu_line *Find_command(command)
423char *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.121848 seconds and 5 git commands to generate.