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