menu_sys.def revision 1.6
1/*	$NetBSD: menu_sys.def,v 1.6 1998/06/24 14:44:52 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
49#define KEYPAD_DOWN_ARROW 256
50#define KEYPAD_UP_ARROW   257
51
52#define MAX(x,y) ((x)>(y)?(x):(y))
53
54/* Initialization state. */
55static int __menu_init = 0;
56int __m_endwin = 0;
57static int max_lines = 0;
58
59/* prototypes for in here! */
60
61static void init_menu (struct menudesc *m);
62static void post_menu (struct menudesc *m);
63static void process_help (struct menudesc *m, int num);
64static void process_req (struct menudesc *m, int num, int req);
65static void mbeep (void);
66static int menucmd (WINDOW *w);
67
68#ifndef NULL
69#define NULL (void *)0
70#endif
71
72/* menu system processing routines */
73
74static void mbeep (void)
75{
76	fprintf (stderr,"\a");
77}
78
79static int mgetch(WINDOW *w)
80{
81	static char buf[20];
82	static int  num = 0;
83
84	int i, ret;
85
86	for (i=0; i< strlen(KD); i++) {
87		if (i >= num)
88			buf[num++] = wgetch(w);
89		if (buf[i] != KD[i])
90			break;
91	}
92	if (i == strlen(KD)) {
93		num = 0;
94		return KEYPAD_DOWN_ARROW;
95	}		
96
97	for (i=0; i< strlen(KU); i++) {
98		if (i >= num)
99			buf[num++] = wgetch(w);
100		if (buf[i] != KU[i])
101			break;
102	}
103	if (i == strlen(KU)) {
104		num = 0;
105		return KEYPAD_UP_ARROW;
106	}		
107
108	ret = buf[0];
109	for (i = 0; i < strlen(buf); i++)
110		buf[i] = buf[i+1];
111	num--;
112	return ret;
113}
114
115static int menucmd (WINDOW *w)
116{
117	int ch;
118
119	while (TRUE) {
120		ch = mgetch(w);
121		
122		switch (ch) {
123		case '\n':
124			return REQ_EXECUTE;
125		case '\016':
126		case KEYPAD_DOWN_ARROW:
127			return REQ_NEXT_ITEM;
128		case '\020':
129		case KEYPAD_UP_ARROW:
130			return REQ_PREV_ITEM;
131		case '\014':
132		        return REQ_REDISPLAY;
133		}
134		
135		if (isalpha(ch) || ch == '?')
136			return (ch);
137
138		mbeep();
139		wrefresh(w);
140	}
141}
142
143static void init_menu (struct menudesc *m)
144{
145	int max;
146	char **p;
147	int add=4;
148
149	if (m->mopt & NOBOX)
150		add = 2;
151
152	max = strlen(m->title);
153
154	/* Calculate h? */
155	if (m->h == 0) {
156		m->h = m->numopts + ((m->mopt & NOEXITOPT) ? 0 : 1)
157			- (max ? 0 : 2);
158	}
159	
160
161	/* Calculate w? */
162	if (m->w == 0) {
163		p = m->opts;
164		while (*p) {
165			max = MAX(max,strlen(*p));
166			p++;
167		}
168		m->w = max;
169	}
170
171	/* Get the windows. */
172	m->mw = newwin(m->h+add, m->w+add, m->y, m->x);
173
174	if (m->mw == NULL) {
175		endwin();
176		(void) fprintf (stderr,
177			"Could not create window for window with title "
178			" \"%s\"\n", m->title);
179		exit(1);
180	} 
181}
182
183static void post_menu (struct menudesc *m)
184{
185	int i;
186	int hasbox, cury;
187	int tadd;
188	
189	if (m->mopt & NOBOX) {
190		cury = 0;
191		hasbox = 0;
192	} else {
193		cury = 1;
194		hasbox = 1;
195	}
196
197	tadd = strlen(m->title) ? 2 : 0;
198
199	if (tadd) {
200		mvwaddstr(m->mw, cury, cury, m->title);
201		cury += 2;
202	}
203
204	for (i=0; i<m->numopts; i++, cury++) {
205		if (m->cursel == i) {
206			mvwaddstr (m->mw, cury, hasbox, ">");
207			wstandout(m->mw);
208		} else
209			mvwaddstr (m->mw, cury, hasbox, " ");
210		waddstr (m->mw, m->opts[i]);
211		if (m->cursel == i)
212			wstandend(m->mw);
213	}
214	if (!(m->mopt & NOEXITOPT))
215		mvwaddstr (m->mw, cury, hasbox, " x: Exit");
216	
217	if (!(m->mopt & NOBOX))
218		box(m->mw, '*', '*');
219
220	wmove(m->mw,tadd+hasbox+m->cursel, hasbox);
221}
222
223static void process_help (struct menudesc *m, int num)
224{
225	char *help = m->helpstr;
226        int lineoff = 0;
227	int curoff = 0;
228	int again;
229        int winin;
230
231	/* Is there help? */
232	if (!help) {
233		mbeep();
234		return;
235	}
236
237	/* Display the help information. */
238        do {
239		if (lineoff < curoff) {
240			help = m->helpstr;
241			curoff = 0;
242		}
243		while (*help && curoff < lineoff) {
244			if (*help == '\n')
245				curoff++;
246			help++;
247		}
248	
249		wclear(stdscr);
250		mvwaddstr (stdscr, 0, 0, 
251			"Help: exit: q,  page up: u <, page down: d >");
252		mvwaddstr (stdscr, 2, 0, help);
253		wmove (stdscr, 1, 0);
254	  	wrefresh(stdscr);
255
256		do {
257			winin = mgetch(stdscr);
258			winin = tolower(winin);
259			again = 0;
260			switch (winin) {
261				case '<':
262				case 'u':
263		                case KEYPAD_UP_ARROW:
264					if (lineoff)
265						lineoff -= max_lines - 2;
266					break;
267				case '>':
268				case 'd':
269		                case KEYPAD_DOWN_ARROW:
270					if (*help)
271						lineoff += max_lines - 2;
272					break;
273				case 'q':
274					break;
275				default:
276					again = 1;
277			}
278		} while (again);
279	} while (winin != 'q');
280
281	/* Restore current menu */    
282	wclear(stdscr);
283	wrefresh(stdscr);
284	process_item (&num, -2);
285	post_menu (m);
286	wrefresh (m->mw);
287}
288
289static void process_req (struct menudesc *m, int num, int req)
290{
291	int ch;
292	int lastsel = m->cursel;
293	int hasexit = (m->mopt & NOEXITOPT ? 0 : 1 );
294	int hasbox = (m->mopt & NOBOX ? 0 : 1);
295	int tadd = strlen(m->title) ? 2 : 0;
296
297	if (req == REQ_EXECUTE)
298		return;
299	else if (req == REQ_NEXT_ITEM) {
300		if (m->cursel < m->numopts + hasexit - 1)
301			m->cursel++;
302		else
303			mbeep();
304	} else if (req == REQ_PREV_ITEM) {
305		if (m->cursel > 0)
306			m->cursel--;
307		else
308			mbeep();
309
310	} else if (req == REQ_REDISPLAY) {
311		wclear(stdscr);
312		wrefresh(stdscr);
313		process_item (&num, -2);
314		post_menu (m);
315		wrefresh (m->mw);
316
317	} else if (req == '?') {
318		process_help (m, num);
319	} else {
320		ch = tolower (req);
321		if (ch == 'x' && hasexit)
322			m->cursel = m->numopts;
323		else {
324			ch = ch - 'a';
325			if (ch < 0 || ch >= m->numopts)
326				mbeep();
327			else
328				m->cursel = ch;
329		}
330	}
331	if (m->cursel != lastsel) {
332		mvwaddstr (m->mw, lastsel+tadd+hasbox, hasbox, " ");
333		if (lastsel < m->numopts)
334			waddstr (m->mw, m->opts[lastsel]);
335		else
336			waddstr (m->mw, "x: Exit");
337		mvwaddstr (m->mw, m->cursel+tadd+hasbox, hasbox, ">");
338		wstandout(m->mw);
339		if (m->cursel < m->numopts)
340			waddstr (m->mw, m->opts[m->cursel]);
341		else
342			waddstr (m->mw, "x: Exit");
343		wstandend(m->mw);
344		wmove(m->mw,tadd+hasbox+m->cursel, hasbox);
345		wrefresh(m->mw);
346	}
347}
348
349void process_menu (int num)
350{
351	int sel = 0;
352	int req, done;
353	int last_num;
354
355	struct menudesc *m = &menus[num];
356
357	done = FALSE;
358
359	/* Initialize? */
360	if (!__menu_init) {
361		if (initscr() == NULL) {
362			__menu_initerror();
363			return;
364		}
365		cbreak();
366		noecho();
367		max_lines = stdscr->maxy;
368		__menu_init = 1;
369	}
370	if (__m_endwin) {
371     	        wclear(stdscr);
372		wrefresh(stdscr);
373		__m_endwin = 0;
374	}
375	if (m->mw == NULL)
376		init_menu (m);
377
378	/* Always preselect 0! */
379	m->cursel = 0;
380
381	while (!done) {
382		last_num = num;
383		if (__m_endwin) {
384			wclear(stdscr);
385			wrefresh(stdscr);
386			__m_endwin = 0;
387		}
388		/* Process the display action */
389		process_item (&num, -2);
390		post_menu (m);
391		wrefresh (m->mw);
392
393		while ((req = menucmd (m->mw)) != REQ_EXECUTE)
394			process_req (m, num, req);
395
396		sel = m->cursel;
397		wclear (m->mw);
398		wrefresh (m->mw);
399
400		/* Process the items */
401		if (sel < m->numopts)
402			done = process_item (&num, sel);
403		else
404			done = TRUE;
405
406		/* Reselect m just in case */
407		if (num != last_num) {
408			m = &menus[num];
409			/* Initialize? */
410			if (m->mw == NULL)
411				init_menu (m);
412			process_item (&num, -2);
413		}
414	}
415
416	/* Process the exit action */
417	process_item (&num, -1);
418}
419