1 /*  Sarien - A Sierra AGI resource interpreter engine
2  *  Copyright (C) 1999,2001 Stuart George and Claudio Matsuoka
3  *
4  *  $Id: menu.c,v 1.50 2001/11/04 19:22:35 cmatsuoka Exp $
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; see docs/COPYING for further details.
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 #include <assert.h>
14 #include "sarien.h"
15 #include "agi.h"
16 #include "sprite.h"
17 #include "graphics.h"
18 #include "keyboard.h"
19 #include "menu.h"
20 #include "text.h"
21 #include "list.h"
22 
23 
24 struct agi_menu {
25 	struct list_head list;		/**< list head for menubar list */
26 	struct list_head down;		/**< list head for menu options */
27 	int index;			/**< number of menu in menubar */
28 	int width;			/**< width of menu in characters */
29 	int height;			/**< height of menu in characters */
30 	int col;			/**< column of menubar entry */
31 	int wincol;			/**< column of menu window */
32 	char *text;			/**< menu name */
33 };
34 
35 struct agi_menu_option {
36 	struct list_head list;		/**< list head for menu options */
37 	int enabled;			/**< option is enabled or disabled */
38 	int event;			/**< menu event */
39 	int index;			/**< number of option in this menu */
40 	char *text;			/**< text of menu option */
41 };
42 
43 static LIST_HEAD(menubar);
44 
45 
get_menu(int i)46 static struct agi_menu *get_menu (int i)
47 {
48 	struct list_head *h;
49 	struct agi_menu *m;
50 
51 	list_for_each (h, &menubar, next) {
52 		m = list_entry (h, struct agi_menu, list);
53 		if (m->index == i)
54 			return m;
55 	}
56 
57 	return NULL;
58 }
59 
get_menu_option(int i,int j)60 static struct agi_menu_option *get_menu_option (int i, int j)
61 {
62 	struct list_head *h;
63 	struct agi_menu *m;
64 	struct agi_menu_option *d;
65 
66 	m = get_menu (i);
67 
68 	list_for_each (h, &m->down, next) {
69 		d = list_entry (h, struct agi_menu_option, list);
70 		if (d->index == j)
71 			return d;
72 	}
73 
74 	return NULL;
75 }
76 
draw_menu_bar()77 static void draw_menu_bar ()
78 {
79 	struct list_head *h;
80 	struct agi_menu *m;
81 
82 #ifdef FANCY_BOX
83 	draw_box (0, 0, GFX_WIDTH - 1, 12, MENU_BG, MENU_LINE, 0);
84 #else
85 	clear_lines (0, 0, MENU_BG);
86 #endif
87 
88 	list_for_each (h, &menubar, next) {
89 		m = list_entry (h, struct agi_menu, list);
90 #ifdef FANCY_BOX
91 		draw_text (m->text, 0, m->col * CHAR_COLS,
92 			3, 40, MENU_FG, MENU_BG);
93 #else
94 		print_text (m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG);
95 #endif
96 	}
97 
98 	flush_lines (0, 0);
99 }
100 
101 
draw_menu_hilite(int cur_menu)102 static void draw_menu_hilite (int cur_menu)
103 {
104 	struct agi_menu *m;
105 
106 	m = get_menu (cur_menu);
107 	_D ("[%s]", m->text);
108 #ifdef FANCY_BOX
109 	draw_box (m->col * CHAR_COLS - 4, 1, (m->col + strlen (m->text)) *
110 		CHAR_COLS + 2, 11, MENU_BG, MENU_LINE, 0);
111 	draw_text (m->text, 0, m->col * CHAR_COLS, 3, 40, MENU_FG, MENU_BG);
112 #else
113 	print_text (m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG);
114 #endif
115 	flush_lines (0, 0);
116 }
117 
118 /* draw box and pulldowns. */
draw_menu_option(int h_menu)119 static void draw_menu_option (int h_menu)
120 {
121 	struct list_head *h;
122 	struct agi_menu *m = NULL;
123 	struct agi_menu_option *d = NULL;
124 
125 	/* find which vertical menu it is */
126 	m = get_menu (h_menu);
127 
128 #ifdef FANCY_BOX
129 	draw_box (m->wincol * CHAR_COLS + 2,
130 		1 * CHAR_LINES + 4,
131 		(m->wincol + m->width + 2) * CHAR_COLS - 3,
132 		(1 + m->height + 1) * CHAR_LINES + 1 + m->height * 2,
133 		MENU_BG, MENU_LINE, 0);
134 
135 	list_for_each (h, &m->down, next) {
136 		d = list_entry (h, struct agi_menu_option, list);
137 		draw_text (d->text, 0, (m->wincol + 1) * CHAR_COLS,
138 			(d->index + 2) * CHAR_LINES + d->index * 2,
139 			m->width + 2, MENU_FG, MENU_BG);
140 #else
141 	draw_box (m->wincol * CHAR_COLS, 1 * CHAR_LINES,
142 		(m->wincol + m->width + 2) * CHAR_COLS,
143 		(1 + m->height + 2) * CHAR_LINES, MENU_BG, MENU_LINE, 0);
144 
145 	list_for_each (h, &m->down, next) {
146 		d = list_entry (h, struct agi_menu_option, list);
147 		print_text (d->text, 0, m->wincol + 1, d->index + 2,
148 			m->width + 2, MENU_FG, MENU_BG);
149 #endif
150 	}
151 }
152 
153 static void draw_menu_option_hilite (int h_menu, int v_menu)
154 {
155 	struct agi_menu *m;
156 	struct agi_menu_option *d;
157 
158 	m = get_menu (h_menu);
159 	d = get_menu_option (h_menu, v_menu);
160 
161 #ifdef FANCY_BOX
162 	draw_box (m->wincol * CHAR_COLS + 4,
163 		(v_menu + 2) * CHAR_LINES - 2 + v_menu * 2,
164 		(m->wincol + m->width + 1) * CHAR_COLS + 3,
165 		(v_menu + 2) * CHAR_LINES + 9 + v_menu * 2,
166 		MENU_BG, MENU_LINE, 0);
167 
168 	draw_text (d->text, 0, (m->wincol + 1) * CHAR_COLS,
169 		(v_menu + 2) * CHAR_LINES + v_menu * 2, m->width + 2,
170 		MENU_FG, MENU_BG);
171 #else
172 	print_text (d->text, 0, m->wincol + 1, v_menu + 2, m->width + 2,
173 		MENU_BG, MENU_FG);
174 #endif
175 }
176 
177 
178 static void new_menu_selected (i)
179 {
180 	show_pic ();
181 	draw_menu_bar ();
182    	draw_menu_hilite (i);
183     	draw_menu_option (i);
184 }
185 
186 #ifdef USE_MOUSE
187 static int mouse_over_text (unsigned int line, unsigned int col, char *s)
188 {
189 	if (mouse.x < col * CHAR_COLS)
190 		return FALSE;
191 
192 	if (mouse.x > (col + strlen (s)) * CHAR_COLS)
193 		return FALSE;
194 
195 #ifdef FANCY_BOX
196 	if ((mouse.y + 4) < line * (CHAR_LINES + 2))
197 		return FALSE;
198 
199 	if ((mouse.y + 4) >= (line + 1) * (CHAR_LINES + 2))
200 		return FALSE;
201 #else
202 	if (mouse.y < line * CHAR_LINES)
203 		return FALSE;
204 
205 	if (mouse.y >= (line + 1) * CHAR_LINES)
206 		return FALSE;
207 #endif
208 
209 	return TRUE;
210 }
211 #endif
212 
213 static int h_index;
214 static int v_index;
215 static int h_col;
216 static int h_max_menu;
217 static int v_max_menu[10];
218 
219 
220 #if 0
221 static void add_about_option ()
222 {
223 	struct agi_menu *m;
224 	struct agi_menu_option *d;
225 	char text[] = "About Sarien";
226 
227 	d = malloc (sizeof (struct agi_menu_option));
228 	d->text = strdup (text);
229 	d->enabled = TRUE;
230 	d->event = 255;
231 	d->index = (v_max_menu[0] += 1);
232 
233 	m = list_entry (menubar.next, struct agi_menu, list);
234 	list_add_tail (&d->list, &m->down);
235 	m->height++;
236 	if (m->width < strlen (text))
237 		m->width = strlen (text);
238 }
239 #endif
240 
241 /*
242  * Public functions
243  */
244 
245 void init_menus ()
246 {
247 	h_index = 0;
248 	h_col = 1;
249 }
250 
251 
252 void deinit_menus ()
253 {
254 	struct list_head *h, *h2, *v, *v2;
255 	struct agi_menu *m = NULL;
256 	struct agi_menu_option *d = NULL;
257 
258 	for (h = (&menubar)->prev; h != (&menubar); h = h2) {
259 		m = list_entry (h, struct agi_menu, list);
260 		h2 = h->prev;
261 		_D ("deiniting hmenu %s", m->text);
262 		for (v = (&m->down)->prev; v != (&m->down); v = v2) {
263 			d = list_entry (v, struct agi_menu_option, list);
264 			v2 = v->prev;
265 			_D ("  deiniting vmenu %s", d->text);
266 			list_del (v);
267 			free (d->text);
268 			free (d);
269 		}
270 		list_del (h);
271 		free (m->text);
272 		free (m);
273 	}
274 }
275 
276 
277 void add_menu (char *s)
278 {
279 	struct agi_menu *m;
280 
281 	m = malloc (sizeof (struct agi_menu));
282 	m->text = strdup (s);
283 	while (m->text[strlen(m->text) - 1] == ' ')
284 		m->text[strlen(m->text) - 1] = 0;
285 	m->down.next = &m->down;
286 	m->down.prev = &m->down;
287 	m->width = 0;
288 	m->height = 0;
289 	m->index = h_index++;
290 	m->col = h_col;
291 	m->wincol = h_col - 1;
292 	v_index = 0;
293 	v_max_menu[m->index] = 0;
294 	h_col += strlen (m->text) + 1;
295 	h_max_menu = m->index;
296 
297 	_D (_D_WARN "add menu: '%s' %02x", s, m->text[strlen(m->text)]);
298 	list_add_tail (&m->list, &menubar);
299 }
300 
301 
302 void add_menu_item (char *s, int code)
303 {
304 	struct agi_menu *m;
305 	struct agi_menu_option *d;
306 	int l;
307 
308 	d = malloc (sizeof (struct agi_menu_option));
309 	d->text = strdup (s);
310 	d->enabled = TRUE;
311 	d->event = code;
312 	d->index = v_index++;
313 
314 	m = list_entry (menubar.prev, struct agi_menu, list);
315 	m->height++;
316 
317 	v_max_menu[m->index] = d->index;
318 
319 	l = strlen (d->text);
320 	if (l > 40)
321 		l = 38;
322 	if (m->wincol + l > 38)
323 		m->wincol = 38 - l;
324 	if (l > m->width)
325 		m->width = l;
326 
327 	_D (_D_WARN "Adding menu item: %s (size = %d)", s, m->height);
328 	list_add_tail (&d->list, &m->down);
329 }
330 
331 void submit_menu ()
332 {
333 	struct list_head *h, *h2;
334 	struct agi_menu *m = NULL;
335 
336 	_D (_D_WARN "Submitting menu");
337 
338 	/* If a menu has no options, delete it */
339 	for (h = (&menubar)->prev; h != (&menubar); h = h2) {
340 		m = list_entry (h, struct agi_menu, list);
341 		h2 = h->prev;
342 		if ((&m->down)->prev == (&m->down)) {
343 			list_del (h);
344 			free (m->text);
345 			free (m);
346 			h_max_menu--;
347 		}
348 	}
349 }
350 
351 int menu_keyhandler (int key)
352 {
353 	static int clock_val;
354 	static int h_cur_menu = 0;
355 	static int v_cur_menu = 0;
356 	static int menu_active = FALSE;
357 	struct agi_menu_option *d;
358 	struct list_head *h;
359 	struct agi_menu *m;
360 	static int button_used = 0;
361 
362 	if (!getflag (F_menus_work))
363 		return FALSE;
364 
365 	if (!menu_active) {
366 		clock_val = game.clock_enabled;
367 		game.clock_enabled = FALSE;
368    		draw_menu_bar ();
369 	}
370 
371 #ifdef USE_MOUSE
372 	/*
373 	 * Mouse handling
374 	 */
375 	if (mouse.button) {
376 		int hmenu, vmenu;
377 
378 		button_used = 1;	/* Button has been used at least once */
379 		if (mouse.y <= CHAR_LINES) {
380 			/* on the menubar */
381 			hmenu = 0;
382 
383 			list_for_each (h, &menubar, next) {
384 				m = list_entry (h, struct agi_menu, list);
385 				if (mouse_over_text (0, m->col, m->text)) {
386 					break;
387 				} else {
388 					hmenu++;
389 				}
390 			}
391 
392 			if (hmenu <= h_max_menu) {
393 				if (h_cur_menu != hmenu) {
394 					v_cur_menu = -1;
395 					new_menu_selected (hmenu);
396 				}
397 				h_cur_menu = hmenu;
398 			}
399 		} else {
400 			/* not in menubar */
401 			struct agi_menu_option *d;
402 
403 			vmenu = 0;
404 
405 			m = get_menu (h_cur_menu);
406 			list_for_each (h, &m->down, next) {
407 				d = list_entry (h, struct agi_menu_option, list);
408 				if (mouse_over_text (2 + d->index,
409 					m->wincol + 1, d->text))
410 				{
411 					break;
412 				} else {
413 					vmenu++;
414 				}
415 			}
416 
417 			if (vmenu <= v_max_menu[h_cur_menu]) {
418 				if (v_cur_menu != vmenu) {
419 					draw_menu_option (h_cur_menu);
420 					draw_menu_option_hilite (h_cur_menu,
421 						vmenu);
422 				}
423 				v_cur_menu = vmenu;
424 			}
425 		}
426 	} else if (button_used) {
427 		/* Button released */
428 		button_used = 0;
429 
430 		_D (_D_WARN "button released!");
431 
432 		if (v_cur_menu < 0)
433 			v_cur_menu = 0;
434 
435 		draw_menu_option_hilite (h_cur_menu, v_cur_menu);
436 
437 		if (mouse.y <= CHAR_LINES) {
438 			/* on the menubar */
439 		} else {
440 			/* see which option we selected */
441 			m = get_menu (h_cur_menu);
442 			list_for_each (h, &m->down, next) {
443 				d = list_entry (h, struct agi_menu_option, list);
444 				if (mouse_over_text (2 + d->index, m->wincol + 1, d->text)) {
445 					/* activate that option */
446 					if (d->enabled) {
447 						_D ("event %d registered", d->event);
448 	    					game.ev_scan[d->event].occured = TRUE;
449 	    					game.ev_scan[d->event].data = d->event;
450 						goto exit_menu;
451 					}
452 				}
453 			}
454 			goto exit_menu;
455 		}
456 	}
457 #endif /* USE_MOUSE */
458 
459 	if (!menu_active) {
460    		if (h_cur_menu >= 0) {
461 			draw_menu_hilite (h_cur_menu);
462 			draw_menu_option (h_cur_menu);
463    			if (!button_used && v_cur_menu >= 0)
464    				draw_menu_option_hilite(h_cur_menu, v_cur_menu);
465 		}
466 		menu_active = TRUE;
467 	}
468 
469     	switch (key) {
470 	case KEY_ESCAPE:
471 		_D (_D_WARN "KEY_ESCAPE");
472 		goto exit_menu;
473     	case KEY_ENTER:
474 		_D (_D_WARN "KEY_ENTER");
475 		d = get_menu_option (h_cur_menu, v_cur_menu);
476 		if (d->enabled) {
477 			_D ("event %d registered", d->event);
478     			game.ev_scan[d->event].occured = TRUE;
479     			game.ev_scan[d->event].data = d->event;
480 			goto exit_menu;
481     		}
482     		break;
483     	case KEY_DOWN:
484     	case KEY_UP:
485     	    	v_cur_menu += key == KEY_DOWN ? 1 : -1;
486 
487 		if (v_cur_menu < 0)
488 			v_cur_menu = 0;
489 		if (v_cur_menu > v_max_menu[h_cur_menu])
490 			v_cur_menu = v_max_menu[h_cur_menu];
491 
492 		draw_menu_option (h_cur_menu);
493 		draw_menu_option_hilite (h_cur_menu, v_cur_menu);
494     		break;
495     	case KEY_RIGHT:
496     	case KEY_LEFT:
497 		h_cur_menu += key == KEY_RIGHT ? 1 : -1;
498 
499 		if (h_cur_menu < 0)
500 			h_cur_menu = h_max_menu;
501 		if (h_cur_menu > h_max_menu)
502 			h_cur_menu = 0;
503 
504 		v_cur_menu = 0;
505 		new_menu_selected (h_cur_menu);
506     		draw_menu_option_hilite (h_cur_menu, v_cur_menu);
507     		break;
508     	}
509 
510 	return TRUE;
511 
512 exit_menu:
513 	button_used = 0;
514 	show_pic ();
515 	write_status ();
516 
517 	setvar (V_key, 0);
518 	game.keypress = 0;
519 	game.clock_enabled = clock_val;
520 	old_input_mode ();
521 	_D (_D_WARN "exit_menu: input mode reset to %d", game.input_mode);
522 	menu_active = FALSE;
523 
524 	return TRUE;
525 }
526 
527 
528 void menu_set_item (int event, int state)
529 {
530 	struct list_head *h, *v;
531 	struct agi_menu *m = NULL;
532 	struct agi_menu_option *d = NULL;
533 
534 	/* scan all menus for event number # */
535 
536 	list_for_each (h, &menubar, next) {
537 		m = list_entry (h, struct agi_menu, list);
538 		list_for_each (v, &m->down, next) {
539 			d = list_entry (v, struct agi_menu_option, list);
540 			if (d->event == event) {
541 				d->enabled = state;
542 				return;
543 			}
544 		}
545 	}
546 }
547 
548 /* end of file: menu.c */
549 
550