1 /*
2 * Alizarin Tetris
3 * A generic way to display menus.
4 *
5 * Copyright 2000, Kiri Wagstaff & Westley Weimer
6 */
7
8 #include <config.h> /* go autoconf! */
9 #include <assert.h>
10
11 #include "atris.h"
12 #include "display.h"
13 #include "menu.h"
14
15 /***************************************************************************
16 * menu()
17 * Create a new menu and return it.
18 *********************************************************************PROTO*/
menu(int nButtons,char ** strings,int defaultchoice,Uint32 face_color0,Uint32 text_color0,Uint32 face_color1,Uint32 text_color1,int x,int y)19 ATMenu* menu(int nButtons, char** strings, int defaultchoice,
20 Uint32 face_color0, Uint32 text_color0,
21 Uint32 face_color1, Uint32 text_color1, int x, int y)
22 {
23 int i, yp;
24 ATMenu* am = (ATMenu*)malloc(sizeof(ATMenu)); assert(am);
25
26 /* Create the buttons */
27 am->buttons = (ATButton**)malloc(nButtons*sizeof(ATButton*));
28 assert(am->buttons);
29 am->clicked = (char*)malloc(nButtons*sizeof(char)); assert(am->clicked);
30 am->nButtons = nButtons;
31 am->x = x; am->y = y;
32 am->defaultchoice = defaultchoice;
33
34 yp = y;
35
36 Assert(nButtons >= 0);
37
38 for (i=0; i<nButtons; i++)
39 {
40 /* Line them up x-wise */
41 am->buttons[i] = button(strings[i], face_color0, text_color0,
42 face_color1, text_color1, x, yp);
43 yp += am->buttons[i]->area.h; /* add a separating space? */
44 am->clicked[i] = FALSE;
45 }
46
47 am->w = am->buttons[0]->area.w;
48 am->h = (am->buttons[nButtons-1]->area.y - am->buttons[0]->area.y)
49 + am->buttons[nButtons-1]->area.h;
50 /* Set the default one */
51 if (defaultchoice >= 0) am->clicked[defaultchoice] = TRUE;
52
53 return am;
54 }
55
56 /***************************************************************************
57 * show_menu()
58 * Displays the menu.
59 *********************************************************************PROTO*/
show_menu(ATMenu * am)60 void show_menu(ATMenu* am)
61 {
62 int i;
63 for (i=0; i<am->nButtons; i++)
64 show_button(am->buttons[i], am->clicked[i]);
65 }
66
67 /***************************************************************************
68 * check_menu()
69 * Returns the index of the button that was clicked (-1 if none).
70 *********************************************************************PROTO*/
check_menu(ATMenu * am,int x,int y)71 int check_menu(ATMenu* am, int x, int y)
72 {
73 int i, j;
74 for (i=0; i<am->nButtons; i++)
75 if (check_button(am->buttons[i], x, y))
76 {
77 /* invert its clicked state */
78 if (am->clicked[i])
79 {
80 am->clicked[i] = FALSE;
81 /* reset the default */
82 am->clicked[am->defaultchoice] = TRUE;
83 show_button(am->buttons[am->defaultchoice],
84 am->clicked[am->defaultchoice]);
85 }
86 else
87 {
88 /* blank all others */
89 for (j=0; j<am->nButtons; j++)
90 if (i!=j && am->clicked[j])
91 {
92 am->clicked[j] = FALSE;
93 show_button(am->buttons[j], am->clicked[j]);
94 }
95 am->clicked[i] = TRUE;
96 }
97 /* redraw it */
98 show_button(am->buttons[i], am->clicked[i]);
99 Debug("Button %d was clicked.\n", i);
100 return i;
101 }
102 return -1;
103 }
104
105 /***************************************************************************
106 * delete_menu()
107 * Deletes the menu and all of its buttons.
108 *********************************************************************PROTO*/
delete_menu(ATMenu * am)109 void delete_menu(ATMenu* am)
110 {
111 int i;
112 for (i=0; i<am->nButtons; i++)
113 Free(am->buttons[i]);
114 Free(am);
115 }
116
117 #define BORDER_X 8
118 #define BORDER_Y 8
119 #define BUTTON_X_SPACE 4
120 #define RADIO_HEIGHT 12
121
122 /***************************************************************************
123 * setup_radio()
124 * Prepared a WalkRadio button for display.
125 *********************************************************************PROTO*/
126 void
setup_radio(WalkRadio * wr)127 setup_radio(WalkRadio *wr)
128 {
129 int i;
130 int our_height;
131
132 wr->face_color[0] = int_button_face0;
133 wr->face_color[1] = int_button_face1;
134 wr->text_color[0] = int_button_text0;
135 wr->text_color[1] = int_button_text1;
136 wr->border_color[0] = int_button_border0;
137 wr->border_color[1] = int_button_border1;
138
139 wr->bitmap0 = malloc(sizeof(SDL_Surface *) * wr->n); Assert(wr->bitmap0);
140 wr->bitmap1 = malloc(sizeof(SDL_Surface *) * wr->n); Assert(wr->bitmap1);
141 wr->w = 0;
142 wr->h = 0;
143
144 for (i=0; i<wr->n; i++) {
145 SDL_Color sc;
146
147 SDL_GetRGB(wr->text_color[0], screen->format, &sc.r, &sc.g, &sc.b);
148 wr->bitmap0[i] = TTF_RenderText_Blended(sfont, wr->label[i], sc);
149 Assert(wr->bitmap0[i]);
150
151 SDL_GetRGB(wr->text_color[1], screen->format, &sc.r, &sc.g, &sc.b);
152 wr->bitmap1[i] = TTF_RenderText_Blended(sfont, wr->label[i], sc);
153 Assert(wr->bitmap1[i]);
154
155 if (wr->bitmap0[i]->w + BUTTON_X_SPACE > wr->w)
156 wr->w = wr->bitmap0[i]->w + BUTTON_X_SPACE;
157 if (wr->bitmap0[i]->h > wr->h) wr->h = wr->bitmap0[i]->h;
158 }
159 wr->w += 5 ;
160 wr->h += 10 ;
161 wr->area.w = wr->w + BORDER_X;
162
163 our_height = RADIO_HEIGHT;
164 if (wr->n < our_height) our_height = wr->n;
165
166 wr->area.h = wr->h * our_height + BORDER_Y;
167 return;
168 }
169
170 /***************************************************************************
171 * check_radio()
172 * Determines if we have clicked in a radio button.
173 *********************************************************************PROTO*/
174 int
check_radio(WalkRadio * wr,int x,int y)175 check_radio(WalkRadio *wr, int x, int y)
176 {
177 if (wr->inactive) return 0;
178
179 if ( x >= wr->area.x &&
180 x <= wr->area.x + wr->area.w &&
181 y >= wr->area.y &&
182 y <= wr->area.y + wr->area.h ) {
183 int start, stop;
184 int c;
185
186 if (wr->n < RADIO_HEIGHT) { /* draw them all */
187 start = 0;
188 stop = wr->n;
189 } else { /* at least four */
190 start = wr->defaultchoice - (RADIO_HEIGHT/2);
191 if (start < 0) start = 0;
192 stop = start + RADIO_HEIGHT;
193 if (stop > wr->n) { /* we're at the bottom */
194 start -= (stop - wr->n);
195 stop = wr->n;
196 }
197 }
198 c = (y - wr->area.y) / wr->h;
199 c += start;
200 if (c < 0) c = 0;
201 if (c >= wr->n) c = wr->n - 1;
202 wr->defaultchoice = c;
203 return 1;
204 }
205 return 0;
206 }
207
208 /***************************************************************************
209 * clear_radio()
210 * Clears away a WalkRadio button. Works even if I am inactive.
211 *********************************************************************PROTO*/
212 void
clear_radio(WalkRadio * wr)213 clear_radio(WalkRadio *wr)
214 {
215 SDL_FillRect(widget_layer, &wr->area, int_black);
216 SDL_FillRect(screen, &wr->area, int_black);
217 SDL_BlitSurface(flame_layer, &wr->area, screen, &wr->area);
218 SDL_BlitSurface(widget_layer, &wr->area, screen, &wr->area);
219 SDL_UpdateSafe(screen, 1, &wr->area);
220 return;
221 }
222
223 /***************************************************************************
224 * draw_radio()
225 * Draws a WalkRadio button.
226 *
227 * Only draws me if I am not inactive.
228 *********************************************************************PROTO*/
229 void
draw_radio(WalkRadio * wr,int state)230 draw_radio(WalkRadio *wr, int state)
231 {
232 int start, stop, i;
233 SDL_Rect draw;
234
235 if (wr->inactive) return;
236
237 wr->area.x = wr->x;
238 wr->area.y = wr->y;
239
240 SDL_FillRect(widget_layer, &wr->area, wr->border_color[state]);
241
242 if (wr->n < RADIO_HEIGHT) { /* draw them all */
243 start = 0;
244 stop = wr->n;
245 } else { /* at least four */
246 start = wr->defaultchoice - (RADIO_HEIGHT/2);
247 if (start < 0) start = 0;
248 stop = start + RADIO_HEIGHT;
249 if (stop > wr->n) { /* we're at the bottom */
250 start -= (stop - wr->n);
251 stop = wr->n;
252 }
253 }
254
255 draw.x = wr->x + BORDER_X/2;
256 draw.y = wr->y + BORDER_Y/2;
257 draw.w = wr->w;
258 draw.h = wr->h;
259
260 for (i=start; i<stop; i++) {
261 int on = (wr->defaultchoice == i);
262 SDL_Rect text;
263
264 SDL_FillRect(widget_layer, &draw, wr->text_color[on]);
265 draw.x += 2; draw.y += 2; draw.w -= 4; draw.h -= 4;
266 if (state || on)
267 SDL_FillRect(widget_layer, &draw, wr->face_color[on]);
268 else
269 SDL_FillRect(widget_layer, &draw, wr->border_color[on]);
270 draw.x += 3; draw.y += 3; draw.w -= 6; draw.h -= 6;
271
272 text.x = draw.x; text.y = draw.y;
273 text.w = wr->bitmap0[i]->w;
274 text.h = wr->bitmap0[i]->h;
275 text.x += (draw.w - text.w) / 2;
276
277 if (on) {
278 SDL_BlitSafe(wr->bitmap1[i], NULL, widget_layer, &text);
279 } else {
280 SDL_BlitSafe(wr->bitmap0[i], NULL, widget_layer, &text);
281 }
282 draw.x -= 5; draw.y -= 5; draw.w += 10; draw.h += 10;
283
284 draw.y += draw.h; /* move down! */
285 }
286
287 SDL_BlitSurface(flame_layer, &wr->area, screen, &wr->area);
288 SDL_BlitSurface(widget_layer, &wr->area, screen, &wr->area);
289 SDL_UpdateSafe(screen, 1, &wr->area);
290 }
291
292 /***************************************************************************
293 * create_single_wrg()
294 * Creates a walk-radio-group with a single walk-radio button with N
295 * choices.
296 *********************************************************************PROTO*/
297 WalkRadioGroup *
create_single_wrg(int n)298 create_single_wrg(int n)
299 {
300 WalkRadioGroup *wrg;
301
302 Calloc(wrg, WalkRadioGroup *, 1 * sizeof(*wrg));
303 wrg->n = 1;
304 Calloc(wrg->wr, WalkRadio *, wrg->n * sizeof(*wrg->wr));
305 wrg->wr[0].n = n;
306 Calloc(wrg->wr[0].label, char **, wrg->wr[0].n * sizeof(*wrg->wr[0].label));
307 return wrg;
308 }
309
310 /***************************************************************************
311 * handle_radio_event()
312 * Tries to handle the given event with respect to the given set of
313 * WalkRadio's. Returns non-zero if an action said that we are done.
314 *********************************************************************PROTO*/
315 int
handle_radio_event(WalkRadioGroup * wrg,const SDL_Event * ev)316 handle_radio_event(WalkRadioGroup *wrg, const SDL_Event *ev)
317 {
318 int i, ret;
319 if (ev->type == SDL_MOUSEBUTTONDOWN) {
320 for (i=0; i<wrg->n; i++) {
321 if (check_radio(&wrg->wr[i], ev->button.x, ev->button.y)) {
322 if (i != wrg->cur) {
323 draw_radio(&wrg->wr[wrg->cur], 0);
324 }
325 draw_radio(&wrg->wr[i], 1);
326 if (wrg->wr[i].action) {
327 ret = wrg->wr[i].action(&wrg->wr[i]);
328 return ret;
329 } else
330 return wrg->wr[i].defaultchoice;
331 }
332 }
333 } else if (ev->type == SDL_KEYDOWN) {
334 switch (ev->key.keysym.sym) {
335 case SDLK_RETURN:
336 if (wrg->wr[wrg->cur].action) {
337 ret = wrg->wr[wrg->cur].action(&wrg->wr[wrg->cur]);
338 return ret;
339 } else
340 return wrg->wr[wrg->cur].defaultchoice;
341 return 1;
342
343 case SDLK_UP:
344 wrg->wr[wrg->cur].defaultchoice --;
345 if (wrg->wr[wrg->cur].defaultchoice < 0)
346 wrg->wr[wrg->cur].defaultchoice = wrg->wr[wrg->cur].n - 1;
347 draw_radio(&wrg->wr[wrg->cur], 1);
348 return -1;
349 break;
350
351 case SDLK_DOWN:
352 wrg->wr[wrg->cur].defaultchoice ++;
353 if (wrg->wr[wrg->cur].defaultchoice >= wrg->wr[wrg->cur].n)
354 wrg->wr[wrg->cur].defaultchoice = 0;
355 draw_radio(&wrg->wr[wrg->cur], 1);
356 return -1;
357
358 case SDLK_LEFT:
359 draw_radio(&wrg->wr[wrg->cur], 0);
360 do {
361 wrg->cur--;
362 if (wrg->cur < 0)
363 wrg->cur = wrg->n - 1;
364 } while (wrg->wr[wrg->cur].inactive);
365 draw_radio(&wrg->wr[wrg->cur], 1);
366 return -1;
367
368 case SDLK_RIGHT:
369 draw_radio(&wrg->wr[wrg->cur], 0);
370 do {
371 wrg->cur++;
372 if (wrg->cur >= wrg->n)
373 wrg->cur = 0;
374 } while (wrg->wr[wrg->cur].inactive);
375 draw_radio(&wrg->wr[wrg->cur], 1);
376 return -1;
377
378 default:
379 return -1;
380 }
381 }
382 return -1;
383 }
384
385 /*
386 * $Log: menu.c,v $
387 * Revision 1.17 2000/11/06 01:22:40 wkiri
388 * Updated menu system.
389 *
390 * Revision 1.16 2000/11/06 00:24:01 weimer
391 * add WalkRadioGroup modifications (inactive field for Kiri) and some support
392 * for special pieces
393 *
394 * Revision 1.15 2000/11/02 03:06:20 weimer
395 * better interface for walk-radio menus: we are now ready for Kiri to change
396 * them to add run-time options ...
397 *
398 * Revision 1.14 2000/10/29 21:23:28 weimer
399 * One last round of header-file changes to reflect my newest and greatest
400 * knowledge of autoconf/automake. Now if you fail to have some bizarro
401 * function, we try to go on anyway unless it is vastly needed.
402 *
403 * Revision 1.13 2000/10/29 17:23:13 weimer
404 * incorporate "xflame" flaming background for added spiffiness ...
405 *
406 * Revision 1.12 2000/10/21 01:22:44 weimer
407 * prevent segfault from out-of-range values
408 *
409 * Revision 1.11 2000/10/21 01:14:43 weimer
410 * massic autoconf/automake restructure ...
411 *
412 * Revision 1.10 2000/10/19 22:06:51 weimer
413 * minor changes ...
414 *
415 * Revision 1.9 2000/10/18 23:57:49 weimer
416 * general fixup, color changes, display changes.
417 * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors,
418 * we hope!
419 *
420 * Revision 1.8 2000/10/13 02:26:54 weimer
421 * rudimentary identity functions, including adding new players
422 *
423 * Revision 1.7 2000/10/12 22:21:25 weimer
424 * display changes, more multi-local-play threading (e.g., myScore ->
425 * Score[0]), that sort of thing ...
426 *
427 * Revision 1.6 2000/10/12 19:17:08 weimer
428 * Further support for AI players and multiple game types.
429 *
430 * Revision 1.5 2000/10/12 00:49:08 weimer
431 * added "AI" support and walking radio menus for initial game configuration
432 *
433 * Revision 1.4 2000/09/09 17:05:35 wkiri
434 * Hideous log changes (Wes: how dare you include a comment character!)
435 *
436 * Revision 1.3 2000/09/09 16:58:27 weimer
437 * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up
438 * main(), isolate the behavior of the three game types. Move graphic files
439 * into graphics/-, style files into styles/-, remove some unused files,
440 * add game flow support (breaks between games, remembering your level within
441 * this game), multiplayer support for the event loop, some global variable
442 * cleanup. All that and a bag of chips!
443 *
444 * Revision 1.2 2000/09/04 22:49:51 wkiri
445 * gamemenu now uses the new nifty menus. Also, added delete_menu.
446 *
447 * Revision 1.1 2000/09/04 19:54:26 wkiri
448 * Added menus (menu.[ch]).
449 */
450