menu_sys.def revision 1.13 1 /* $NetBSD: menu_sys.def,v 1.13 1998/07/03 15:20:30 phil 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 char *str_ptr = str_area;
94
95 /* Macros */
96 #define MAX(x,y) ((x)>(y)?(x):(y))
97 #define MIN(x,y) ((x)<(y)?(x):(y))
98
99 /* Initialization state. */
100 static int __menu_init = 0;
101 int __m_endwin = 0;
102 static int max_lines = 0;
103 static char *scrolltext = " <: page up, >: page down";
104
105 /* prototypes for in here! */
106 static void ins_keyseq (struct keyseq **seq, struct keyseq *ins);
107 static void init_keyseq (void);
108 static void init_menu (struct menudesc *m);
109 static void post_menu (struct menudesc *m);
110 static void process_help (struct menudesc *m, int num);
111 static void process_req (struct menudesc *m, int num, int req);
112 static void mbeep (void);
113 static int menucmd (WINDOW *w);
114
115 #ifndef NULL
116 #define NULL (void *)0
117 #endif
118
119 /* menu system processing routines */
120
121 static void mbeep (void)
122 {
123 fprintf (stderr,"\a");
124 }
125
126 static void ins_keyseq (struct keyseq **seq, struct keyseq *ins)
127 {
128 if (*seq == NULL) {
129 ins->next = NULL;
130 *seq = ins;
131 } else if (ins->numchars <= (*seq)->numchars) {
132 ins->next = *seq;
133 *seq = ins;
134 } else
135 ins_keyseq (&(*seq)->next, ins);
136 }
137
138 static void init_keyseq (void)
139 {
140 int i;
141 for (i=0; i<_mc_num_key_seq; i++) {
142 if (_mc_key_seq[i].termcap_name)
143 _mc_key_seq[i].chars =
144 tgetstr (_mc_key_seq[i].termcap_name,
145 &str_ptr);
146 if (_mc_key_seq[i].chars != NULL &&
147 (_mc_key_seq[i].numchars = strlen(_mc_key_seq[i].chars))
148 > 0)
149 ins_keyseq (&pad_list,&_mc_key_seq[i]);
150 }
151 }
152
153 static int mgetch(WINDOW *w)
154 {
155 static char buf[20];
156 static int num = 0;
157 struct keyseq *list = pad_list;
158 int i, ret;
159
160 /* key pad processing */
161 while (list) {
162 for (i=0; i< list->numchars; i++) {
163 if (i >= num)
164 buf[num++] = wgetch(w);
165 if (buf[i] != list->chars[i])
166 break;
167 }
168 if (i == list->numchars) {
169 num = 0;
170 return list->keyseq_val;
171 }
172 list = list->next;
173 }
174
175 ret = buf[0];
176 for (i = 0; i < strlen(buf); i++)
177 buf[i] = buf[i+1];
178 num--;
179 return ret;
180 }
181
182 static int menucmd (WINDOW *w)
183 {
184 int ch;
185
186 while (TRUE) {
187 ch = mgetch(w);
188
189 switch (ch) {
190 case '\n':
191 return REQ_EXECUTE;
192 case '\016': /* Contnrol-P */
193 case KEYSEQ_DOWN_ARROW:
194 return REQ_NEXT_ITEM;
195 case '\020': /* Control-N */
196 case KEYSEQ_UP_ARROW:
197 return REQ_PREV_ITEM;
198 case '\014': /* Control-L */
199 return REQ_REDISPLAY;
200 case '<':
201 case '\010': /* Control-H (backspace) */
202 case KEYSEQ_PAGE_UP:
203 return REQ_SCROLLUP;
204 case '>':
205 case ' ':
206 case KEYSEQ_PAGE_DOWN:
207 return REQ_SCROLLDOWN;
208 case '?':
209 return REQ_HELP;
210 }
211
212 if (isalpha(ch))
213 return (ch);
214
215 mbeep();
216 wrefresh(w);
217 }
218 }
219
220 static void init_menu (struct menudesc *m)
221 {
222 int max;
223 char **p;
224 int add, exitadd;
225
226 add = ((m->mopt & NOBOX) ? 2 : 4);
227 exitadd = ((m->mopt & NOEXITOPT) ? 0 : 1);
228 max = strlen(m->title);
229
230 /* Calculate h? h == number of visible options. */
231 if (m->h == 0) {
232 m->h = m->numopts + exitadd;
233 if (m->h + m->y + add >= max_lines && (m->mopt & SCROLL))
234 m->h = max_lines - m->y - add ;
235 }
236
237 /* Check window heights and set scrolling */
238 if (m->h < m->numopts + exitadd) {
239 if (!(m->mopt & SCROLL) || m->h < 3) {
240 endwin();
241 (void) fprintf (stderr,
242 "Window too small for menu \"%s\"\n",
243 m->title);
244 exit(1);
245 }
246 } else
247 m->mopt &= ~SCROLL;
248
249 /* check for screen fit */
250 if (m->y + m->h + add > max_lines) {
251 endwin();
252 (void) fprintf (stderr,
253 "Screen too small for menu \"%s\"\n", m->title);
254 exit(1);
255
256 }
257
258 /* Calculate w? */
259 if (m->w == 0) {
260 p = m->opts;
261 if (m->mopt & SCROLL)
262 max = strlen(scrolltext);
263 while (*p) {
264 max = MAX(max,strlen(*p));
265 p++;
266 }
267 m->w = max;
268 }
269
270 /* Get the windows. */
271 m->mw = newwin(m->h+add, m->w+add, m->y, m->x);
272
273 if (m->mw == NULL) {
274 endwin();
275 (void) fprintf (stderr,
276 "Could not create window for window with title "
277 " \"%s\"\n", m->title);
278 exit(1);
279 }
280 }
281
282 static void post_menu (struct menudesc *m)
283 {
284 int i;
285 int hasbox, cury, maxy, selrow, lastopt;
286 int tadd;
287
288 if (m->mopt & NOBOX) {
289 cury = 0;
290 maxy = m->h;
291 hasbox = 0;
292 } else {
293 cury = 1;
294 maxy = m->h+1;
295 hasbox = 1;
296 }
297
298 /* Clear the window */
299 wclear (m->mw);
300
301 tadd = strlen(m->title) ? 2 : 0;
302
303 if (tadd) {
304 mvwaddstr(m->mw, cury, cury, m->title);
305 cury += 2;
306 maxy += 2;
307 }
308
309 /* Set defaults, calculate lastopt. */
310 selrow = -1;
311 if (m->mopt & SCROLL) {
312 lastopt = MIN(m->numopts, m->topline+m->h-1);
313 maxy -= 1;
314 } else
315 lastopt = m->numopts;
316
317 for (i=m->topline; i<lastopt; i++, cury++) {
318 if (m->cursel == i) {
319 mvwaddstr (m->mw, cury, hasbox, ">");
320 wstandout(m->mw);
321 selrow = cury;
322 } else
323 mvwaddstr (m->mw, cury, hasbox, " ");
324 waddstr (m->mw, m->opts[i]);
325 if (m->cursel == i)
326 wstandend(m->mw);
327 }
328
329 /* Add the exit option. */
330 if (!(m->mopt & NOEXITOPT) && cury < maxy) {
331 if (m->cursel >= m->numopts) {
332 mvwaddstr (m->mw, cury, hasbox, ">");
333 wstandout(m->mw);
334 selrow = cury;
335 } else
336 mvwaddstr (m->mw, cury, hasbox, " ");
337 waddstr (m->mw, "x: Exit");
338 if (m->cursel >= m->numopts)
339 wstandend(m->mw);
340 cury++;
341 }
342
343 /* Add the scroll line */
344 if (m->mopt & SCROLL) {
345 mvwaddstr (m->mw, cury, hasbox, scrolltext);
346 if (selrow < 0)
347 selrow = cury;
348 }
349
350 /* Add the box. */
351 if (!(m->mopt & NOBOX))
352 box(m->mw, '*', '*');
353
354 wmove(m->mw, selrow, hasbox);
355 }
356
357 static void process_help (struct menudesc *m, int num)
358 {
359 char *help = m->helpstr;
360 int lineoff = 0;
361 int curoff = 0;
362 int again;
363 int winin;
364
365 /* Is there help? */
366 if (!help) {
367 mbeep();
368 return;
369 }
370
371 /* Display the help information. */
372 do {
373 if (lineoff < curoff) {
374 help = m->helpstr;
375 curoff = 0;
376 }
377 while (*help && curoff < lineoff) {
378 if (*help == '\n')
379 curoff++;
380 help++;
381 }
382
383 wclear(stdscr);
384 mvwaddstr (stdscr, 0, 0,
385 "Help: exit: x, page up: u <, page down: d >");
386 mvwaddstr (stdscr, 2, 0, help);
387 wmove (stdscr, 1, 0);
388 wrefresh(stdscr);
389
390 do {
391 winin = mgetch(stdscr);
392 if (winin < KEYSEQ_FIRST)
393 winin = tolower(winin);
394 again = 0;
395 switch (winin) {
396 case '<':
397 case 'u':
398 case KEYSEQ_UP_ARROW:
399 case KEYSEQ_LEFT_ARROW:
400 case KEYSEQ_PAGE_UP:
401 if (lineoff)
402 lineoff -= max_lines - 2;
403 else
404 again = 1;
405 break;
406 case '>':
407 case 'd':
408 case KEYSEQ_DOWN_ARROW:
409 case KEYSEQ_RIGHT_ARROW:
410 case KEYSEQ_PAGE_DOWN:
411 if (*help)
412 lineoff += max_lines - 2;
413 else
414 again = 1;
415 break;
416 case 'q':
417 break;
418 case 'x':
419 winin = 'q';
420 break;
421 default:
422 again = 1;
423 }
424 if (again)
425 mbeep();
426 } while (again);
427 } while (winin != 'q');
428
429 /* Restore current menu */
430 wclear(stdscr);
431 wrefresh(stdscr);
432 process_item (&num, -2);
433 }
434
435 static void process_req (struct menudesc *m, int num, int req)
436 {
437 int ch;
438 int hasexit = (m->mopt & NOEXITOPT ? 0 : 1 );
439 int refresh = 0;
440 int scroll_sel = 0;
441
442 if (req == REQ_EXECUTE)
443 return;
444
445 else if (req == REQ_NEXT_ITEM) {
446 if (m->cursel < m->numopts + hasexit - 1) {
447 m->cursel++;
448 scroll_sel = 1;
449 refresh = 1;
450 if (m->mopt & SCROLL &&
451 m->cursel >= m->topline + m->h -1 )
452 m->topline += 1;
453 } else
454 mbeep();
455
456 } else if (req == REQ_PREV_ITEM) {
457 if (m->cursel > 0) {
458 m->cursel--;
459 scroll_sel = 1;
460 refresh = 1;
461 if (m->cursel < m->topline )
462 m->topline -= 1;
463 } else
464 mbeep();
465
466 } else if (req == REQ_REDISPLAY) {
467 wclear(stdscr);
468 wrefresh(stdscr);
469 process_item (&num, -2);
470 refresh = 1;
471
472 } else if (req == REQ_HELP) {
473 process_help (m, num);
474 refresh = 1;
475
476 } else if (req == REQ_SCROLLUP) {
477 if (!(m->mopt & SCROLL))
478 mbeep();
479 else if (m->topline == 0)
480 mbeep();
481 else {
482 m->topline = MAX(0,m->topline-m->h-1);
483 wclear (m->mw);
484 refresh = 1;
485 }
486
487 } else if (req == REQ_SCROLLDOWN) {
488 if (!(m->mopt & SCROLL))
489 mbeep();
490 else if (m->topline + m->h - 1 >= m->numopts + hasexit)
491 mbeep();
492 else {
493 m->topline = MIN(m->topline+m->h-1,
494 m->numopts+hasexit-m->h+1);
495 wclear (m->mw);
496 refresh = 1;
497 }
498
499 } else {
500 ch = req;
501 if (ch == 'x' && hasexit) {
502 m->cursel = m->numopts;
503 scroll_sel = 1;
504 refresh = 1;
505 } else {
506 if (ch > 'z')
507 ch = 255;
508 if (ch >= 'a') {
509 if (ch > 'x') ch--;
510 ch = ch - 'a';
511 } else
512 ch = 25 + ch - 'A';
513 if (ch < 0 || ch >= m->numopts)
514 mbeep();
515 else {
516 m->cursel = ch;
517 scroll_sel = 1;
518 refresh = 1;
519 }
520 }
521 }
522
523 if (m->mopt & SCROLL && scroll_sel) {
524 while (m->cursel >= m->topline + m->h -1 )
525 m->topline = MIN(m->topline+m->h-1,
526 m->numopts+hasexit-m->h+1);
527 while (m->cursel < m->topline)
528 m->topline = MAX(0,m->topline-m->h+1);
529 }
530
531 if (refresh) {
532 post_menu (m);
533 wrefresh (m->mw);
534 }
535 }
536
537 void process_menu (int num)
538 {
539 int sel = 0;
540 int req, done;
541 int last_num;
542
543 struct menudesc *m = &menus[num];
544
545 done = FALSE;
546
547 /* Initialize? */
548 if (!__menu_init) {
549 if (initscr() == NULL) {
550 __menu_initerror();
551 return;
552 }
553 cbreak();
554 noecho();
555 max_lines = stdscr->maxy;
556 init_keyseq();
557 __menu_init = 1;
558 }
559 if (__m_endwin) {
560 wclear(stdscr);
561 wrefresh(stdscr);
562 __m_endwin = 0;
563 }
564 if (m->mw == NULL)
565 init_menu (m);
566
567 /* Always preselect option 0 and display from 0! */
568 m->cursel = 0;
569 m->topline = 0;
570
571 while (!done) {
572 last_num = num;
573 if (__m_endwin) {
574 wclear(stdscr);
575 wrefresh(stdscr);
576 __m_endwin = 0;
577 }
578 /* Process the display action */
579 process_item (&num, -2);
580 post_menu (m);
581 wrefresh (m->mw);
582
583 while ((req = menucmd (m->mw)) != REQ_EXECUTE)
584 process_req (m, num, req);
585
586 sel = m->cursel;
587 wclear (m->mw);
588 wrefresh (m->mw);
589
590 /* Process the items */
591 if (sel < m->numopts)
592 done = process_item (&num, sel);
593 else
594 done = TRUE;
595
596 /* Reselect m just in case */
597 if (num != last_num) {
598 m = &menus[num];
599 /* Initialize? */
600 if (m->mw == NULL)
601 init_menu (m);
602 process_item (&num, -2);
603 }
604 }
605
606 /* Process the exit action */
607 process_item (&num, -1);
608 }
609
610 /* Control L is end of standard routines, remaining only for dynamic. */
612
613 /* Beginning of routines for dynamic menus. */
614
615
616