huge CVS merge, see ChangeLog for details.
[supertux.git] / src / menu.c
1 /*
2   menu.c
3   
4   Super Tux - Menu
5   
6   by Tobias Glaesser
7   tobi.web@gmx.de
8   http://www.newbreedsoftware.com/supertux/
9   
10   December 20, 2003 - December 30, 2003
11 */
12
13 #ifdef LINUX
14 #include <pwd.h>
15 #include <sys/types.h>
16 #include <ctype.h>
17 #endif
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22
23 #include "defines.h"
24 #include "globals.h"
25 #include "menu.h"
26 #include "screen.h"
27 #include "setup.h"
28 #include "sound.h"
29 #include "scene.h"
30 #include "leveleditor.h"
31 #include "timer.h"
32 #include "high_scores.h"
33
34 /* (global) menu variables */
35 int menuaction;
36 int show_menu;
37 int menu_change;
38 texture_type checkbox, checkbox_checked;
39
40 menu_type main_menu, game_menu, options_menu, leveleditor_menu, highscore_menu;
41 menu_type* current_menu, * last_menu;
42
43 /* input implementation variables */
44 int delete_character;
45 char mn_input_char;
46
47 /* Set the current menu */
48 void menu_set_current(menu_type* pmenu)
49 {
50   if(pmenu != current_menu)
51     {
52       menu_change = YES;
53       last_menu = current_menu;
54       current_menu = pmenu;
55     }
56 }
57
58 /* Return a pointer to a new menu item */
59 menu_item_type* menu_item_create(int kind, char *text, int init_toggle, void* target_menu)
60 {
61   menu_item_type *pnew_item = (menu_item_type*) malloc(sizeof(menu_item_type));
62   pnew_item->kind = kind;
63   pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text) + 1));
64   strcpy(pnew_item->text,text);
65   if(kind == MN_TOGGLE)
66     pnew_item->toggled = init_toggle;
67   else
68     pnew_item->toggled = NO;
69   pnew_item->target_menu = target_menu;
70   pnew_item->input = NULL;
71   return pnew_item;
72 }
73
74 /* Free a menu and all its items */
75 void menu_free(menu_type* pmenu)
76 {
77   int i;
78   if(pmenu->num_items != 0 && pmenu->item != NULL)
79     {
80       for(i = 0; i < pmenu->num_items; ++i)
81         {
82           free(pmenu->item[i].text);
83           free(pmenu->item[i].input);
84         }
85       free(pmenu->item);
86     }
87 }
88
89 /* Initialize a menu */
90 void menu_init(menu_type* pmenu)
91 {
92   pmenu->num_items = 0;
93   pmenu->active_item = 0;
94   pmenu->item = NULL;
95 }
96
97 /* Add an item to a menu */
98 void menu_additem(menu_type* pmenu, menu_item_type* pmenu_item)
99 {
100   ++pmenu->num_items;
101   pmenu->item = (menu_item_type*) realloc(pmenu->item, sizeof(menu_item_type) * pmenu->num_items);
102   memcpy(&pmenu->item[pmenu->num_items-1],pmenu_item,sizeof(menu_item_type));
103 }
104
105 /* Process actions done on the menu */
106 void menu_action(menu_type* pmenu)
107 {
108   int i;
109
110   if(pmenu->num_items != 0 && pmenu->item != NULL)
111     {
112       switch(menuaction)
113         {
114         case MN_UP:
115           if(pmenu->active_item > 0)
116             --pmenu->active_item;
117           else
118             pmenu->active_item = pmenu->num_items-1;
119           break;
120         case MN_DOWN:
121           if(pmenu->active_item < pmenu->num_items-1)
122             ++pmenu->active_item;
123           else
124             pmenu->active_item = 0;
125           break;
126         case MN_HIT:
127           if(pmenu->item[pmenu->active_item].kind == MN_GOTO && pmenu->item[pmenu->active_item].target_menu != NULL)
128             menu_set_current((menu_type*)pmenu->item[pmenu->active_item].target_menu);
129           else if(pmenu->item[pmenu->active_item].kind == MN_TOGGLE)
130             {
131               pmenu->item[pmenu->active_item].toggled = !pmenu->item[pmenu->active_item].toggled;
132               menu_change = YES;
133             }
134           else if(pmenu->item[pmenu->active_item].kind == MN_ACTION || pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
135             {
136               pmenu->item[pmenu->active_item].toggled = YES;
137             }
138           else if(pmenu->item[pmenu->active_item].kind == MN_BACK)
139             {
140               if(last_menu != NULL)
141                 menu_set_current(last_menu);
142             }
143           break;
144         case MN_REMOVE:
145           if(pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
146             {
147               if(pmenu->item[pmenu->active_item].input != NULL)
148                 {
149                   i = strlen(pmenu->item[pmenu->active_item].input);
150
151                   while(delete_character > 0)   /* remove charactes */
152                     {
153                       pmenu->item[pmenu->active_item].input[i-1] = '\0';
154                       delete_character--;
155                     }
156                 }
157             }
158           break;
159         case MN_INPUT:
160           if(pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
161             {
162               if(pmenu->item[pmenu->active_item].input != NULL)
163                 {       
164                   i = strlen(pmenu->item[pmenu->active_item].input);
165                   pmenu->item[pmenu->active_item].input = (char*) realloc(pmenu->item[pmenu->active_item].input,sizeof(char)*(i + 2));
166                   pmenu->item[pmenu->active_item].input[i] = mn_input_char;
167                   pmenu->item[pmenu->active_item].input[i+1] = '\0';
168                 }
169               else
170                 {
171                   pmenu->item[pmenu->active_item].input = (char*) malloc(2*sizeof(char));
172                   pmenu->item[pmenu->active_item].input[0] = mn_input_char;
173                   pmenu->item[pmenu->active_item].input[1] = '\0';
174                 }
175             }
176           break;
177         }
178     }
179     
180     menuaction = -1;
181
182   if(pmenu->item[pmenu->active_item].kind == MN_DEACTIVE)
183     menu_action(pmenu);
184 }
185
186 /* Check, if the value of the active menu item has changed. */
187 int menu_check(menu_type* pmenu)
188 {
189   if(pmenu->num_items != 0 && pmenu->item != NULL)
190     {
191       if((pmenu->item[pmenu->active_item].kind == MN_ACTION || pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD) && pmenu->item[pmenu->active_item].toggled == YES)
192         {
193           pmenu->item[pmenu->active_item].toggled = NO;
194           show_menu = 0;
195           return pmenu->active_item;
196         }
197       else if(pmenu->item[pmenu->active_item].kind == MN_TOGGLE)
198         {
199           return pmenu->active_item;
200         }
201       else
202         return -1;
203     }
204   else
205     return -1;
206 }
207
208 /* Draw the current menu. */
209 void menu_draw(menu_type* pmenu)
210 {
211   int i, y, menu_height, menu_width;
212
213   /* The width of the menu has to be more than the width of the text with the most characters */
214   menu_width = 0;
215   for(i = 0; i < pmenu->num_items; ++i)
216     {
217     y = strlen(pmenu->item[i].text) + (pmenu->item[i].input ? strlen(pmenu->item[i].input) : 0);
218       if( y > menu_width )
219         {
220           menu_width = y;
221           if( pmenu->item[i].kind == MN_TOGGLE)
222             menu_width += 2;
223         }
224     }
225   menu_width = menu_width * 16 + 48;
226   menu_height = (pmenu->num_items) * 24;
227   
228   /* Draw a transparent background */
229   fillrect(screen->w/2 - menu_width/2,screen->h/2-(((pmenu->num_items)*24)/2),menu_width,menu_height,150,150,150,100);
230
231   for(i = 0; i < pmenu->num_items; ++i)
232     {
233       if(pmenu->item[i].kind == MN_DEACTIVE)
234         {
235           text_drawf(&black_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
236         }
237       else if(pmenu->item[i].kind == MN_TEXTFIELD)
238         {
239           text_drawf(&gold_text,pmenu->item[i].input,(strlen(pmenu->item[i].text) * 16)/2,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
240           if(i == pmenu->active_item)
241             {
242               text_drawf(&blue_text,pmenu->item[i].text,-((strlen(pmenu->item[i].input) * 16)/2),(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,3,NO_UPDATE);
243             }
244           else
245             {
246               text_drawf(&white_text,pmenu->item[i].text,-((strlen(pmenu->item[i].input) * 16)/2),(i)*24 - menu_height/2 +10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
247             }
248         }
249       else if(i == pmenu->active_item)
250         {
251           text_drawf(&blue_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10 ,A_HMIDDLE, A_VMIDDLE,3,NO_UPDATE);
252         }
253       else
254         {
255           text_drawf(&white_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
256         }
257       if(pmenu->item[i].kind == MN_TOGGLE)
258         {
259
260           if(pmenu->item[i].toggled == YES)
261             texture_draw(&checkbox_checked,screen->w / 2 + (strlen(pmenu->item[i].text) * 16)/2  + 16,(i)*24 - menu_height/2 + 10 + screen->h / 2 -8,NO_UPDATE);
262           else
263             texture_draw(&checkbox,screen->w / 2 + (strlen(pmenu->item[i].text) * 16)/2 + 16,(i)*24 - menu_height/2 + 10 + screen->h / 2 - 8,NO_UPDATE);
264         }
265     }
266 }
267
268 /* Reset/Set global defaults */
269 void menu_reset(void)
270 {
271   menu_change = NO;
272   show_menu = NO;
273   menuaction = -1;
274   current_menu = NULL;
275   last_menu = NULL;
276
277   delete_character = 0;
278   mn_input_char = '\0';
279 }
280
281 /* --- MENU --- */
282 /* Draw the current menu and execute the (menu)events */
283 void menu_process_current(void)
284 {
285   menu_change = NO;
286
287   if(current_menu != NULL)
288     {
289       menu_action(current_menu);
290       menu_draw(current_menu);
291     }
292
293   menuaction = -1;
294 }
295
296 /* Check for menu event */
297 void menu_event(SDL_keysym* keysym)
298 {
299   SDLKey key = keysym->sym;
300   SDLMod keymod;
301   keymod = SDL_GetModState();
302   char ch[2];
303
304   /* If the current unicode character is an ASCII character,
305      assign it to ch. */
306   if ( (keysym->unicode & 0xFF80) == 0 )
307     {
308       ch[0] = keysym->unicode & 0x7F;
309       ch[1] = '\0';
310     }
311   else
312     {
313       /* An International Character. */
314     }
315
316   switch(key)
317     {
318     case SDLK_UP:               /* Menu Up */
319       menuaction = MN_UP;
320       menu_change = YES;
321       break;
322     case SDLK_DOWN:             /* Menu Down */
323       menuaction = MN_DOWN;
324       menu_change = YES;
325       break;
326     case SDLK_SPACE:            /* Menu Hit */
327     case SDLK_RETURN:
328       menuaction = MN_HIT;
329       menu_change = YES;
330       break;
331     case SDLK_DELETE:
332     case SDLK_BACKSPACE:
333       menuaction = MN_REMOVE;
334       menu_change = YES;
335       delete_character++;
336       break;
337     default:
338       if( key >= SDLK_0 && key <= SDLK_9)
339         {
340           menuaction = MN_INPUT;
341           menu_change = YES;
342           mn_input_char = *ch;
343         }
344       else if( key >= SDLK_a && key <= SDLK_z )
345         {
346           menuaction = MN_INPUT;
347           menu_change = YES;
348           mn_input_char = *ch;
349         }
350       else
351         {
352         mn_input_char = '\0';
353         }
354       break;
355     }
356
357
358   /* FIXME: NO JOYSTICK SUPPORT */
359   /*#ifdef JOY_YES
360   else if (event.type == SDL_JOYBUTTONDOWN)
361    {
362       Joystick button: Continue:
363
364      done = 1;
365    }
366   #endif*/
367 }
368