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