menu_sys.def revision 1.27 1 /* $NetBSD: menu_sys.def,v 1.27 2001/12/06 16:38:30 christos Exp $ */
2
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software develooped for the NetBSD Project by
20 * Piermont Information Systems Inc.
21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 */
38
39 /* menu_sys.defs -- Menu system standard routines. */
40
41 #include <string.h>
42 #include <ctype.h>
43
44 #define REQ_EXECUTE 1000
45 #define REQ_NEXT_ITEM 1001
46 #define REQ_PREV_ITEM 1002
47 #define REQ_REDISPLAY 1003
48 #define REQ_SCROLLDOWN 1004
49 #define REQ_SCROLLUP 1005
50 #define REQ_HELP 1006
51
52 /* Multiple key support */
53 #define KEYSEQ_FIRST 256
54 #define KEYSEQ_DOWN_ARROW 256
55 #define KEYSEQ_UP_ARROW 257
56 #define KEYSEQ_LEFT_ARROW 258
57 #define KEYSEQ_RIGHT_ARROW 259
58 #define KEYSEQ_PAGE_DOWN 260
59 #define KEYSEQ_PAGE_UP 261
60
61 struct keyseq {
62 char *termcap_name;
63 char *chars;
64 int numchars;
65 int keyseq_val;
66 struct keyseq *next;
67 };
68
69 /* keypad and other definitions */
70 struct keyseq _mc_key_seq[] = {
71 /* Cludge for xterm ... */
72 { NULL, "\033[B", 0, KEYSEQ_DOWN_ARROW, NULL },
73 { NULL, "\033[D", 0, KEYSEQ_LEFT_ARROW, NULL },
74 { NULL, "\033[C", 0, KEYSEQ_RIGHT_ARROW, NULL },
75 { NULL, "\033[A", 0, KEYSEQ_UP_ARROW, NULL },
76 /* Termcap defined */
77 { "kd", NULL, 0, KEYSEQ_DOWN_ARROW, NULL },
78 { "kl", NULL, 0, KEYSEQ_LEFT_ARROW, NULL },
79 { "kr", NULL, 0, KEYSEQ_RIGHT_ARROW, NULL },
80 { "ku", NULL, 0, KEYSEQ_UP_ARROW, NULL },
81 { "kf", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* scroll forward */
82 { "kN", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* next page */
83 { "kP", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* scroll backward */
84 { "kR", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* prev page */
85 /* other definitions */
86 { NULL, "\033v", 0, KEYSEQ_PAGE_UP, NULL }, /* ESC-v */
87 { NULL, "\026", 0, KEYSEQ_PAGE_DOWN, NULL }, /* CTL-v */
88 };
89
90 int _mc_num_key_seq = sizeof(_mc_key_seq) / sizeof(struct keyseq);
91 struct keyseq *pad_list = NULL;
92 static char str_area [512];
93 static int str_size = sizeof(str_area);
94 static char *str_ptr = str_area;
95
96 /* Macros */
97 #define MAX(x,y) ((x)>(y)?(x):(y))
98 #define MIN(x,y) ((x)<(y)?(x):(y))
99
100 /* Initialization state. */
101 static int __menu_init = 0;
102 int __m_endwin = 0;
103 static int max_lines = 0, max_cols = 0;
104 static char *scrolltext = " <: page up, >: page down";
105
106 static menudesc *menus = menu_def;
107
108 #ifdef DYNAMIC_MENUS
109 static int num_menus = 0;
110 static int num_avail = 0;
111 #define DYN_INIT_NUM 32
112 #endif
113
114 /* prototypes for in here! */
115 static void ins_keyseq (struct keyseq **seq, struct keyseq *ins);
116 static void init_keyseq (void);
117 static void init_menu (struct menudesc *m);
118 static char opt_ch (int op_no);
119 static void post_menu (struct menudesc *m);
120 static void process_help (struct menudesc *m, int num);
121 static void process_req (struct menudesc *m, int num, int req);
122 static int menucmd (WINDOW *w);
123
124 #ifndef NULL
125 #define NULL (void *)0
126 #endif
127
128 /* menu system processing routines */
129 #define mbeep() (void)fputc('\a', stderr)
130
131 static void
132 ins_keyseq (struct keyseq **seq, struct keyseq *ins)
133 {
134 if (*seq == NULL) {
135 ins->next = NULL;
136 *seq = ins;
137 } else if (ins->numchars <= (*seq)->numchars) {
138 ins->next = *seq;
139 *seq = ins;
140 } else
141 ins_keyseq (&(*seq)->next, ins);
142 }
143
144 static void
145 init_keyseq(void)
146 {
147 /*
148 * XXX XXX XXX THIS SHOULD BE NUKED FROM ORBIT! DO THIS
149 * XXX XXX XXX WITH NORMAL CURSES FACILITIES!
150 */
151 struct tinfo *ti;
152 char *term = getenv("TERM");
153 int i;
154
155 if (term == NULL || term[0] == '\0')
156 return;
157
158 if (t_getent(&ti, term) < 0)
159 return;
160
161 for (i = 0; i < _mc_num_key_seq; i++) {
162 if (_mc_key_seq[i].termcap_name == NULL)
163 continue;
164
165 _mc_key_seq[i].chars = t_getstr(ti, _mc_key_seq[i].termcap_name,
166 &str_ptr, &str_size);
167
168 if (_mc_key_seq[i].chars != NULL &&
169 (_mc_key_seq[i].numchars = strlen(_mc_key_seq[i].chars))
170 > 0)
171 ins_keyseq(&pad_list, &_mc_key_seq[i]);
172 }
173 t_freent(ti);
174 }
175
176 static int
177 mgetch(WINDOW *w)
178 {
179 static char buf[20];
180 static int num = 0;
181 struct keyseq *list = pad_list;
182 int i, ret;
183
184 /* key pad processing */
185 while (list) {
186 for (i=0; i< list->numchars; i++) {
187 if (i >= num)
188 buf[num++] = wgetch(w);
189 if (buf[i] != list->chars[i])
190 break;
191 }
192 if (i == list->numchars) {
193 num = 0;
194 return list->keyseq_val;
195 }
196 list = list->next;
197 }
198
199 ret = buf[0];
200 for (i = 0; i < strlen(buf); i++)
201 buf[i] = buf[i+1];
202 num--;
203 return ret;
204 }
205
206 static int
207 menucmd (WINDOW *w)
208 {
209 int ch;
210
211 while (TRUE) {
212 ch = mgetch(w);
213
214 switch (ch) {
215 case '\n':
216 return REQ_EXECUTE;
217 case '\016': /* Contnrol-P */
218 case KEYSEQ_DOWN_ARROW:
219 return REQ_NEXT_ITEM;
220 case '\020': /* Control-N */
221 case KEYSEQ_UP_ARROW:
222 return REQ_PREV_ITEM;
223 case '\014': /* Control-L */
224 return REQ_REDISPLAY;
225 case '<':
226 case '\010': /* Control-H (backspace) */
227 case KEYSEQ_PAGE_UP:
228 return REQ_SCROLLUP;
229 case '>':
230 case ' ':
231 case KEYSEQ_PAGE_DOWN:
232 return REQ_SCROLLDOWN;
233 case '?':
234 return REQ_HELP;
235 }
236
237 if (isalpha(ch))
238 return (ch);
239
240 mbeep();
241 wrefresh(w);
242 }
243 }
244
245 static void
246 init_menu (struct menudesc *m)
247 {
248 int wmax;
249 int hadd, wadd, exithadd;
250 int i;
251
252 hadd = ((m->mopt & MC_NOBOX) ? 0 : 2);
253 wadd = ((m->mopt & MC_NOBOX) ? 2 : 4);
254
255 hadd += strlen(m->title) != 0 ? 2 : 0;
256 exithadd = ((m->mopt & MC_NOEXITOPT) ? 0 : 1);
257
258 wmax = strlen(m->title);
259
260 /* Calculate h? h == number of visible options. */
261 if (m->h == 0) {
262 m->h = m->numopts + exithadd;
263 if (m->h + m->y + hadd >= max_lines && (m->mopt & MC_SCROLL))
264 m->h = max_lines - m->y - hadd ;
265 }
266
267 /* Check window heights and set scrolling */
268 if (m->h < m->numopts + exithadd) {
269 if (!(m->mopt & MC_SCROLL) || m->h < 3) {
270 endwin();
271 (void) fprintf (stderr,
272 "Window too short for menu \"%s\"\n",
273 m->title);
274 exit(1);
275 }
276 } else
277 m->mopt &= ~MC_SCROLL;
278
279 /* check for screen fit */
280 if (m->y + m->h + hadd > max_lines) {
281 endwin();
282 (void) fprintf (stderr,
283 "Screen too short for menu \"%s\"\n", m->title);
284 exit(1);
285
286 }
287
288 /* Calculate w? */
289 if (m->w == 0) {
290 if (m->mopt & MC_SCROLL)
291 wmax = MAX(wmax,strlen(scrolltext));
292 for (i=0; i < m->numopts; i++ )
293 wmax = MAX(wmax,strlen(m->opts[i].opt_name)+3);
294 m->w = wmax;
295 }
296
297 /* check and adjust for screen fit */
298 if (m->w + wadd > max_cols) {
299 endwin();
300 (void) fprintf (stderr,
301 "Screen too narrow for menu \"%s\"\n", m->title);
302 exit(1);
303
304 }
305 if (m->x == -1)
306 m->x = (max_cols - (m->w + wadd)) / 2; /* center */
307 else if (m->x + m->w + wadd > max_cols)
308 m->x = max_cols - (m->w + wadd);
309
310 /* Get the windows. */
311 m->mw = newwin(m->h+hadd, m->w+wadd, m->y, m->x);
312
313 if (m->mw == NULL) {
314 endwin();
315 (void) fprintf (stderr,
316 "Could not create window for menu \"%s\"\n", m->title);
317 exit(1);
318 }
319
320 /* XXX is it even worth doing this right? */
321 if (has_colors()) {
322 wbkgd(m->mw, COLOR_PAIR(1));
323 wattrset(m->mw, COLOR_PAIR(1));
324 }
325 }
326
327 static char
328 opt_ch (int op_no)
329 {
330 char c;
331 if (op_no < 25) {
332 c = 'a' + op_no;
333 if (c >= 'x') c++;
334 } else
335 c = 'A' + op_no - 25;
336 return (char) c;
337 }
338
339 static void
340 post_menu (struct menudesc *m)
341 {
342 int i;
343 int hasbox, cury, maxy, selrow, lastopt;
344 int tadd;
345 char optstr[5];
346
347 if (m->mopt & MC_NOBOX) {
348 cury = 0;
349 maxy = m->h;
350 hasbox = 0;
351 } else {
352 cury = 1;
353 maxy = m->h+1;
354 hasbox = 1;
355 }
356
357 /* Clear the window */
358 wclear (m->mw);
359
360 tadd = strlen(m->title) ? 2 : 0;
361
362 if (tadd) {
363 mvwaddstr(m->mw, cury, cury, " ");
364 mvwaddstr(m->mw, cury, cury + 1, m->title);
365 cury += 2;
366 maxy += 2;
367 }
368
369 /* Set defaults, calculate lastopt. */
370 selrow = -1;
371 if (m->mopt & MC_SCROLL) {
372 lastopt = MIN(m->numopts, m->topline+m->h-1);
373 maxy -= 1;
374 } else
375 lastopt = m->numopts;
376
377 for (i=m->topline; i<lastopt; i++, cury++) {
378 if (m->cursel == i) {
379 mvwaddstr (m->mw, cury, hasbox, ">");
380 wstandout(m->mw);
381 selrow = cury;
382 } else
383 mvwaddstr (m->mw, cury, hasbox, " ");
384 if (!(m->mopt & MC_NOSHORTCUT)) {
385 (void) sprintf (optstr, "%c: ", opt_ch(i));
386 waddstr (m->mw, optstr);
387 }
388 waddstr (m->mw, m->opts[i].opt_name);
389 if (m->cursel == i)
390 wstandend(m->mw);
391 }
392
393 /* Add the exit option. */
394 if (!(m->mopt & MC_NOEXITOPT) && cury < maxy) {
395 if (m->cursel >= m->numopts) {
396 mvwaddstr (m->mw, cury, hasbox, ">");
397 wstandout(m->mw);
398 selrow = cury;
399 } else
400 mvwaddstr (m->mw, cury, hasbox, " ");
401 if (!(m->mopt & MC_NOSHORTCUT))
402 waddstr (m->mw, "x: ");
403 waddstr (m->mw, m->exitstr);
404 if (m->cursel >= m->numopts)
405 wstandend(m->mw);
406 cury++;
407 }
408
409 /* Add the scroll line */
410 if (m->mopt & MC_SCROLL) {
411 mvwaddstr (m->mw, cury, hasbox, scrolltext);
412 if (selrow < 0)
413 selrow = cury;
414 }
415
416 /* Add the box. */
417 if (!(m->mopt & MC_NOBOX))
418 box(m->mw, 0, 0);
419
420 wmove(m->mw, selrow, hasbox);
421 }
422
423 static void
424 process_help (struct menudesc *m, int num)
425 {
426 char *help = m->helpstr;
427 int lineoff = 0;
428 int curoff = 0;
429 int again;
430 int winin;
431
432 /* Is there help? */
433 if (!help) {
434 mbeep();
435 return;
436 }
437
438 /* Display the help information. */
439 do {
440 if (lineoff < curoff) {
441 help = m->helpstr;
442 curoff = 0;
443 }
444 while (*help && curoff < lineoff) {
445 if (*help == '\n')
446 curoff++;
447 help++;
448 }
449
450 wclear(stdscr);
451 mvwaddstr (stdscr, 0, 0,
452 "Help: exit: x, page up: u <, page down: d >");
453 mvwaddstr (stdscr, 2, 0, help);
454 wmove (stdscr, 1, 0);
455 wrefresh(stdscr);
456
457 do {
458 winin = mgetch(stdscr);
459 if (winin < KEYSEQ_FIRST)
460 winin = tolower(winin);
461 again = 0;
462 switch (winin) {
463 case '<':
464 case 'u':
465 case KEYSEQ_UP_ARROW:
466 case KEYSEQ_LEFT_ARROW:
467 case KEYSEQ_PAGE_UP:
468 if (lineoff)
469 lineoff -= max_lines - 2;
470 else
471 again = 1;
472 break;
473 case '>':
474 case 'd':
475 case KEYSEQ_DOWN_ARROW:
476 case KEYSEQ_RIGHT_ARROW:
477 case KEYSEQ_PAGE_DOWN:
478 if (*help)
479 lineoff += max_lines - 2;
480 else
481 again = 1;
482 break;
483 case 'q':
484 break;
485 case 'x':
486 winin = 'q';
487 break;
488 default:
489 again = 1;
490 }
491 if (again)
492 mbeep();
493 } while (again);
494 } while (winin != 'q');
495
496 /* Restore current menu */
497 wclear(stdscr);
498 wrefresh(stdscr);
499 if (m->post_act)
500 (*m->post_act)();
501 }
502
503 static void
504 process_req (struct menudesc *m, int num, int req)
505 {
506 int ch;
507 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1 );
508 int refresh = 0;
509 int scroll_sel = 0;
510
511 if (req == REQ_EXECUTE)
512 return;
513
514 else if (req == REQ_NEXT_ITEM) {
515 if (m->cursel < m->numopts + hasexit - 1) {
516 m->cursel++;
517 scroll_sel = 1;
518 refresh = 1;
519 if (m->mopt & MC_SCROLL &&
520 m->cursel >= m->topline + m->h -1 )
521 m->topline += 1;
522 } else
523 mbeep();
524
525 } else if (req == REQ_PREV_ITEM) {
526 if (m->cursel > 0) {
527 m->cursel--;
528 scroll_sel = 1;
529 refresh = 1;
530 if (m->cursel < m->topline )
531 m->topline -= 1;
532 } else
533 mbeep();
534
535 } else if (req == REQ_REDISPLAY) {
536 wclear(stdscr);
537 wrefresh(stdscr);
538 if (m->post_act)
539 (*m->post_act)();
540 refresh = 1;
541
542 } else if (req == REQ_HELP) {
543 process_help (m, num);
544 refresh = 1;
545
546 } else if (req == REQ_SCROLLUP) {
547 if (!(m->mopt & MC_SCROLL))
548 mbeep();
549 else if (m->topline == 0)
550 mbeep();
551 else {
552 m->topline = MAX(0,m->topline-m->h+1);
553 m->cursel = MAX(0, m->cursel-m->h+1);
554 wclear (m->mw);
555 refresh = 1;
556 }
557
558 } else if (req == REQ_SCROLLDOWN) {
559 if (!(m->mopt & MC_SCROLL))
560 mbeep();
561 else if (m->topline + m->h - 1 >= m->numopts + hasexit)
562 mbeep();
563 else {
564 m->topline = MIN(m->topline+m->h-1,
565 m->numopts+hasexit-m->h+1);
566 m->cursel = MIN(m->numopts-1, m->cursel+m->h-1);
567 wclear (m->mw);
568 refresh = 1;
569 }
570
571 } else {
572 ch = req;
573 if (ch == 'x' && hasexit) {
574 m->cursel = m->numopts;
575 scroll_sel = 1;
576 refresh = 1;
577 } else
578 if (!(m->mopt & MC_NOSHORTCUT)) {
579 if (ch > 'z')
580 ch = 255;
581 if (ch >= 'a') {
582 if (ch > 'x') ch--;
583 ch = ch - 'a';
584 } else
585 ch = 25 + ch - 'A';
586 if (ch < 0 || ch >= m->numopts)
587 mbeep();
588 else {
589 m->cursel = ch;
590 scroll_sel = 1;
591 refresh = 1;
592 }
593 } else
594 mbeep();
595 }
596
597 if (m->mopt & MC_SCROLL && scroll_sel) {
598 while (m->cursel >= m->topline + m->h -1 )
599 m->topline = MIN(m->topline+m->h-1,
600 m->numopts+hasexit-m->h+1);
601 while (m->cursel < m->topline)
602 m->topline = MAX(0,m->topline-m->h+1);
603 }
604
605 if (refresh) {
606 post_menu (m);
607 wrefresh (m->mw);
608 }
609 }
610
611 int
612 menu_init (void)
613 {
614
615 if (__menu_init)
616 return 0;
617
618 if (initscr() == NULL)
619 return 1;
620
621 cbreak();
622 noecho();
623
624 /* XXX Should be configurable but it almost isn't worth it. */
625 if (has_colors()) {
626 start_color();
627 init_pair(1, COLOR_WHITE, COLOR_BLUE);
628 bkgd(COLOR_PAIR(1));
629 attrset(COLOR_PAIR(1));
630 }
631
632 max_lines = getmaxy(stdscr);
633 max_cols = getmaxx(stdscr);
634 init_keyseq();
635 #ifdef DYNAMIC_MENUS
636 num_menus = DYN_INIT_NUM;
637 while (num_menus < DYN_MENU_START)
638 num_menus *= 2;
639 menus = (menudesc *) malloc(sizeof(menudesc)*num_menus);
640 if (menus == NULL)
641 return 2;
642 (void) memset ((void *)menus, 0, sizeof(menudesc)*num_menus);
643 (void) memcpy ((void *)menus, (void *)menu_def,
644 sizeof(menudesc)*DYN_MENU_START);
645 num_avail = num_menus - DYN_MENU_START;
646 #endif
647
648 __menu_init = 1;
649 return (0);
650 }
651
652 void
653 process_menu (int num)
654 {
655 int sel = 0;
656 int req, done;
657 int last_num;
658
659 struct menudesc *m;
660
661 m = &menus[num];
662
663 done = FALSE;
664
665 /* Initialize? */
666 if (menu_init()) {
667 __menu_initerror();
668 return;
669 }
670
671 if (__m_endwin) {
672 wclear(stdscr);
673 wrefresh(stdscr);
674 __m_endwin = 0;
675 }
676 if (m->mw == NULL)
677 init_menu (m);
678
679 /* Always preselect option 0 and display from 0! */
680 m->cursel = 0;
681 m->topline = 0;
682
683 while (!done) {
684 last_num = num;
685 if (__m_endwin) {
686 wclear(stdscr);
687 wrefresh(stdscr);
688 __m_endwin = 0;
689 }
690 /* Process the display action */
691 if (m->post_act)
692 (*m->post_act)();
693 post_menu (m);
694 wrefresh (m->mw);
695
696 while ((req = menucmd (m->mw)) != REQ_EXECUTE)
697 process_req (m, num, req);
698
699 sel = m->cursel;
700 wclear (m->mw);
701 wrefresh (m->mw);
702
703 /* Process the items */
704 if (sel < m->numopts) {
705 if (m->opts[sel].opt_flags & OPT_ENDWIN) {
706 endwin();
707 __m_endwin = 1;
708 }
709 if (m->opts[sel].opt_action)
710 done = (*m->opts[sel].opt_action)(m);
711 if (m->opts[sel].opt_menu != -1) {
712 if (m->opts[sel].opt_flags & OPT_SUB)
713 process_menu (m->opts[sel].opt_menu);
714 else
715 num = m->opts[sel].opt_menu;
716 }
717
718 if (m->opts[sel].opt_flags & OPT_EXIT)
719 done = TRUE;
720
721 } else
722 done = TRUE;
723
724 /* Reselect m just in case */
725 if (num != last_num) {
726 m = &menus[num];
727
728 /* Initialize? */
729 if (m->mw == NULL)
730 init_menu (m);
731 if (m->post_act)
732 (*m->post_act)();
733 }
734 }
735
736 /* Process the exit action */
737 if (m->exit_act)
738 (*m->exit_act)();
739 }
740
741 /* Control L is end of standard routines, remaining only for dynamic. */
743
744 /* Beginning of routines for dynamic menus. */
745
746 /* local prototypes */
747 static int double_menus (void);
748
749 static int
750 double_menus (void)
751 {
752 menudesc *temp;
753
754 temp = (menudesc *) malloc(sizeof(menudesc)*num_menus*2);
755 if (temp == NULL)
756 return 0;
757 (void) memset ((void *)temp, 0,
758 sizeof(menudesc)*num_menus*2);
759 (void) memcpy ((void *)temp, (void *)menus,
760 sizeof(menudesc)*num_menus);
761 free (menus);
762 menus = temp;
763 num_avail = num_menus;
764 num_menus *= 2;
765
766 return 1;
767 }
768
769 int
770 new_menu (char * title, menu_ent * opts, int numopts,
771 int x, int y, int h, int w, int mopt,
772 void (*post_act)(void), void (*exit_act)(void), char * help)
773 {
774 int ix;
775
776 /* Check for free menu entry. */
777 if (num_avail == 0)
778 if (!double_menus ())
779 return -1;
780
781 /* Find free menu entry. */
782 for (ix = DYN_MENU_START; ix < num_menus && menus[ix].mopt & MC_VALID;
783 ix++) /* do nothing */;
784
785 /* if ix == num_menus ... panic */
786
787 /* Set Entries */
788 menus[ix].title = title ? title : "";
789 menus[ix].opts = opts;
790 menus[ix].numopts = numopts;
791 menus[ix].x = x;
792 menus[ix].y = y;
793 menus[ix].h = h;
794 menus[ix].w = w;
795 menus[ix].mopt = mopt | MC_VALID;
796 menus[ix].post_act = post_act;
797 menus[ix].exit_act = exit_act;
798 menus[ix].helpstr = help;
799 menus[ix].exitstr = "Exit";
800
801 init_menu (&menus[ix]);
802
803 return ix;
804 }
805
806 void
807 free_menu (int menu_no)
808 {
809 if (menu_no < num_menus) {
810 menus[menu_no].mopt &= ~MC_VALID;
811 if (menus[menu_no].mw != NULL)
812 delwin (menus[menu_no].mw);
813 }
814 }
815