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