4dc253d55bb1b0b74d05f6831db6019da42125a3
[supertux.git] / lib / gui / menu.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #ifndef WIN32
21 #include <sys/types.h>
22 #include <ctype.h>
23 #endif
24
25 #include <iostream>
26 #include <sstream>
27 #include <cstdlib>
28 #include <cstdio>
29 #include <string>
30 #include <cassert>
31
32 #include "app/globals.h"
33 #include "gui/menu.h"
34 #include "video/screen.h"
35 #include "video/drawing_context.h"
36 #include "app/setup.h"
37 #include "audio/sound.h"
38 #include "special/timer.h"
39 #include "app/gettext.h"
40
41 #define FLICK_CURSOR_TIME 500
42
43 Surface* checkbox;
44 Surface* checkbox_checked;
45 Surface* back;
46 Surface* arrow_left;
47 Surface* arrow_right;
48
49 Menu* main_menu      = 0;
50 Menu* game_menu      = 0;
51 Menu* options_menu   = 0;
52 Menu* options_keys_menu     = 0;
53 Menu* options_joystick_menu = 0;
54 Menu* highscore_menu = 0;
55 Menu* load_game_menu = 0;
56 Menu* save_game_menu = 0;
57 Menu* contrib_menu   = 0;
58 Menu* contrib_subset_menu   = 0;
59
60 std::vector<Menu*> Menu::last_menus;
61 Menu* Menu::current_ = 0;
62
63 /* just displays a Yes/No text that can be used to confirm stuff */
64 bool confirm_dialog(Surface *background, std::string text)
65 {
66   //Surface* cap_screen = Surface::CaptureScreen();
67   
68   Menu* dialog = new Menu;
69   dialog->additem(MN_DEACTIVE, text,0,0);
70   dialog->additem(MN_HL,"",0,0);
71   dialog->additem(MN_ACTION,_("Yes"),0,0,true);
72   dialog->additem(MN_ACTION,_("No"),0,0,false);
73   dialog->additem(MN_HL,"",0,0);
74
75   Menu::set_current(dialog);
76
77   DrawingContext context;
78
79   while(true)
80   {
81     SDL_Event event;
82
83     while (SDL_PollEvent(&event))
84     {
85       dialog->event(event);
86     }
87
88     if(background == NULL)
89       context.draw_gradient(Color(200,240,220), Color(200,200,220), LAYER_BACKGROUND0);
90     else
91       context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
92
93     dialog->draw(context);
94     dialog->action();
95
96     switch (dialog->check())
97     {
98     case true:
99       //delete cap_screen;
100       Menu::set_current(0);
101       delete dialog;
102       return true;
103       break;
104     case false:
105       //delete cap_screen;
106       Menu::set_current(0);
107       delete dialog;
108       return false;
109       break;
110     default:
111       break;
112     }
113
114     mouse_cursor->draw(context);
115     context.do_drawing();
116     SDL_Delay(25);
117   }
118
119   return false;
120 }
121
122 void
123 Menu::push_current(Menu* pmenu)
124 {
125   if (current_)
126     last_menus.push_back(current_);
127
128   current_ = pmenu;
129   current_->effect.start(500);
130 }
131
132 void
133 Menu::pop_current()
134 {
135   if (!last_menus.empty())
136   {
137     current_ = last_menus.back();
138     current_->effect.start(500);
139
140     last_menus.pop_back();
141   }
142   else
143   {
144     current_ = 0;
145   }
146 }
147
148 void
149 Menu::set_current(Menu* menu)
150 {
151   last_menus.clear();
152
153   if (menu)
154     menu->effect.start(500);
155
156   current_ = menu;
157 }
158
159 /* Return a pointer to a new menu item */
160 MenuItem*
161 MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu* target_menu_, int id, int* int_p_)
162 {
163   MenuItem *pnew_item = new MenuItem;
164
165   pnew_item->kind = kind_;
166   pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text_) + 1));
167   strcpy(pnew_item->text, text_);
168
169   if(kind_ == MN_TOGGLE)
170     pnew_item->toggled = init_toggle_;
171   else
172     pnew_item->toggled = false;
173
174   pnew_item->target_menu = target_menu_;
175   pnew_item->input = (char*) malloc(sizeof(char));
176   pnew_item->input[0] = '\0';
177
178   if(kind_ == MN_STRINGSELECT)
179   {
180     pnew_item->list = (string_list_type*) malloc(sizeof(string_list_type));
181     string_list_init(pnew_item->list);
182   }
183   else
184     pnew_item->list = NULL;
185
186   pnew_item->id = id;
187   pnew_item->int_p = int_p_;
188
189   pnew_item->input_flickering = false;
190   pnew_item->input_flickering_timer.init(true);
191   pnew_item->input_flickering_timer.start(FLICK_CURSOR_TIME);
192
193   return pnew_item;
194 }
195
196 void
197 MenuItem::change_text(const  char *text_)
198 {
199   if (text_)
200   {
201     free(text);
202     text = (char*) malloc(sizeof(char )*(strlen(text_)+1));
203     strcpy(text, text_);
204   }
205 }
206
207 void
208 MenuItem::change_input(const  char *text_)
209 {
210   if(text)
211   {
212     free(input);
213     input = (char*) malloc(sizeof(char )*(strlen(text_)+1));
214     strcpy(input, text_);
215   }
216 }
217
218 std::string MenuItem::get_input_with_symbol(bool active_item)
219 {
220   if(!active_item)
221     input_flickering = true;
222   else
223   {
224     if(input_flickering_timer.get_left() < 0)
225     {
226       if(input_flickering)
227         input_flickering = false;
228       else
229         input_flickering = true;
230       input_flickering_timer.start(FLICK_CURSOR_TIME);
231     }
232   }
233
234   char str[1024];
235   if(input_flickering)
236     sprintf(str,"%s ",input);
237   else
238     sprintf(str,"%s_",input);
239
240   std::string string = str;
241
242   return string;
243 }
244
245 /* Set ControlField for keyboard key */
246 void Menu::get_controlfield_key_into_input(MenuItem *item)
247 {
248   switch(*item->int_p)
249   {
250   case SDLK_UP:
251     item->change_input(_("Up cursor"));
252     break;
253   case SDLK_DOWN:
254     item->change_input(_("Down cursor"));
255     break;
256   case SDLK_LEFT:
257     item->change_input(_("Left cursor"));
258     break;
259   case SDLK_RIGHT:
260     item->change_input(_("Right cursor"));
261     break;
262   case SDLK_RETURN:
263     item->change_input(_("Return"));
264     break;
265   case SDLK_SPACE:
266     item->change_input(_("Space"));
267     break;
268   case SDLK_RSHIFT:
269     item->change_input(_("Right Shift"));
270     break;
271   case SDLK_LSHIFT:
272     item->change_input(_("Left Shift"));
273     break;
274   case SDLK_RCTRL:
275     item->change_input(_("Right Control"));
276     break;
277   case SDLK_LCTRL:
278     item->change_input(_("Left Control"));
279     break;
280   case SDLK_RALT:
281     item->change_input(_("Right Alt"));
282     break;
283   case SDLK_LALT:
284     item->change_input(_("Left Alt"));
285     break;
286   default:
287     {
288       char tmp[64];
289       snprintf(tmp, 64, "%d", *item->int_p);
290       item->change_input(tmp);
291     }
292     break;
293   }
294 }
295
296 /* Set ControlField for joystick button */
297 void Menu::get_controlfield_js_into_input(MenuItem *item)
298 {
299   std::ostringstream oss;
300   oss << "Button " << *item->int_p;
301   item->change_input(oss.str().c_str());
302 }
303
304 /* Free a menu and all its items */
305 Menu::~Menu()
306 {
307   if(item.size() != 0)
308   {
309     for(unsigned int i = 0; i < item.size(); ++i)
310     {
311       free(item[i].text);
312       free(item[i].input);
313       string_list_free(item[i].list);
314     }
315   }
316 }
317
318
319 Menu::Menu()
320 {
321   hit_item = -1;
322   menuaction = MENU_ACTION_NONE;
323   delete_character = 0;
324   mn_input_char = '\0';
325
326   pos_x        = screen->w/2;
327   pos_y        = screen->h/2;
328   arrange_left = 0;
329   active_item  = 0;
330   effect.init(false);
331
332   joystick_timer.init(true);
333 }
334
335 void Menu::set_pos(int x, int y, float rw, float rh)
336 {
337   pos_x = x + (int)((float)get_width() * rw);
338   pos_y = y + (int)((float)get_height() * rh);
339 }
340
341 void
342 Menu::additem(MenuItemKind kind_, const std::string& text_, int toggle_, Menu* menu_, int id, int* int_p)
343 {
344   additem(MenuItem::create(kind_, text_.c_str(), toggle_, menu_, id, int_p));
345 }
346
347 /* Add an item to a menu */
348 void
349 Menu::additem(MenuItem* pmenu_item)
350 {
351   item.push_back(*pmenu_item);
352   delete pmenu_item;
353 }
354
355 void
356 Menu::clear()
357 {
358   item.clear();
359 }
360
361 /* Process actions done on the menu */
362 void
363 Menu::action()
364 {
365   hit_item = -1;
366   if(item.size() != 0)
367   {
368     switch(menuaction)
369     {
370     case MENU_ACTION_UP:
371       if (active_item > 0)
372         --active_item;
373       else
374         active_item = int(item.size())-1;
375       break;
376
377     case MENU_ACTION_DOWN:
378       if(active_item < int(item.size())-1)
379         ++active_item;
380       else
381         active_item = 0;
382       break;
383
384     case MENU_ACTION_LEFT:
385       if(item[active_item].kind == MN_STRINGSELECT
386           && item[active_item].list->num_items != 0)
387       {
388         if(item[active_item].list->active_item > 0)
389           --item[active_item].list->active_item;
390         else
391           item[active_item].list->active_item = item[active_item].list->num_items-1;
392       }
393       break;
394
395     case MENU_ACTION_RIGHT:
396       if(item[active_item].kind == MN_STRINGSELECT
397           && item[active_item].list->num_items != 0)
398       {
399         if(item[active_item].list->active_item < item[active_item].list->num_items-1)
400           ++item[active_item].list->active_item;
401         else
402           item[active_item].list->active_item = 0;
403       }
404       break;
405
406     case MENU_ACTION_HIT:
407       {
408         hit_item = active_item;
409         switch (item[active_item].kind)
410         {
411         case MN_GOTO:
412           if (item[active_item].target_menu != NULL)
413             Menu::push_current(item[active_item].target_menu);
414           else
415             puts("NULLL");
416           break;
417
418         case MN_TOGGLE:
419           item[active_item].toggled = !item[active_item].toggled;
420           break;
421
422         case MN_ACTION:
423           Menu::set_current(0);
424           item[active_item].toggled = true;
425           break;
426         case MN_TEXTFIELD:
427         case MN_NUMFIELD:
428           menuaction = MENU_ACTION_DOWN;
429           action();
430           break;
431
432         case MN_BACK:
433           Menu::pop_current();
434           break;
435         default:
436           break;
437         }
438       }
439       break;
440
441     case MENU_ACTION_REMOVE:
442       if(item[active_item].kind == MN_TEXTFIELD
443           || item[active_item].kind == MN_NUMFIELD)
444       {
445         if(item[active_item].input != NULL)
446         {
447           int i = strlen(item[active_item].input);
448
449           while(delete_character > 0)   /* remove charactes */
450           {
451             item[active_item].input[i-1] = '\0';
452             delete_character--;
453           }
454         }
455       }
456       break;
457
458     case MENU_ACTION_INPUT:
459       if(item[active_item].kind == MN_TEXTFIELD
460           || (item[active_item].kind == MN_NUMFIELD && mn_input_char >= '0' && mn_input_char <= '9'))
461       {
462         if(item[active_item].input != NULL)
463         {
464           int i = strlen(item[active_item].input);
465           item[active_item].input = (char*) realloc(item[active_item].input,sizeof(char)*(i + 2));
466           item[active_item].input[i] = mn_input_char;
467           item[active_item].input[i+1] = '\0';
468         }
469         else
470         {
471           item[active_item].input = (char*) malloc(2*sizeof(char));
472           item[active_item].input[0] = mn_input_char;
473           item[active_item].input[1] = '\0';
474         }
475       }
476
477     case MENU_ACTION_NONE:
478       break;
479     }
480   }
481
482   MenuItem& new_item = item[active_item];
483   if(new_item.kind == MN_DEACTIVE
484       || new_item.kind == MN_LABEL
485       || new_item.kind == MN_HL)
486   {
487     // Skip the horzontal line item
488     if (menuaction != MENU_ACTION_UP && menuaction != MENU_ACTION_DOWN)
489       menuaction = MENU_ACTION_DOWN;
490
491     if (item.size() > 1)
492       action();
493   }
494
495   menuaction = MENU_ACTION_NONE;
496
497   if (active_item >= int(item.size()))
498     active_item = int(item.size()) - 1;
499 }
500
501 int
502 Menu::check()
503 {
504   if (hit_item != -1)
505     return item[hit_item].id;
506   else
507     return -1;
508 }
509
510 void
511 Menu::draw_item(DrawingContext& context,
512     int index, // Position of the current item in the menu
513     int menu_width, int menu_height)
514 {
515   MenuItem& pitem = item[index];
516
517   int effect_offset = 0;
518   {
519     int effect_time = 0;
520
521     if(effect.check())
522       effect_time = effect.get_left() / 4;
523
524     effect_offset = (index % 2) ? effect_time : -effect_time;
525   }
526
527   Font* text_font = white_text;
528   int x_pos       = pos_x;
529   int y_pos       = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
530   int shadow_size = 2;
531   int text_width  = int(text_font->get_text_width(pitem.text));
532   int input_width = int(text_font->get_text_width(pitem.input) + 10);
533   int list_width  =
534     int(text_font->get_text_width(string_list_active(pitem.list)));
535
536   if (arrange_left)
537     x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
538
539   if(index == active_item)
540   {
541     shadow_size = 3;
542     text_font = blue_text;
543   }
544
545   switch (pitem.kind)
546   {
547   case MN_DEACTIVE:
548     {
549       context.draw_text_center(gray_text, pitem.text,
550           Vector(0, y_pos - int(blue_text->get_height()/2)),
551           LAYER_GUI);
552       break;
553     }
554
555   case MN_HL:
556     {
557       // TODO
558       int x = pos_x - menu_width/2;
559       int y = y_pos - 12 - effect_offset;
560       /* Draw a horizontal line with a little 3d effect */
561       context.draw_filled_rect(Vector(x, y + 6),
562           Vector(menu_width, 4), Color(150,200,255,225), LAYER_GUI);
563       context.draw_filled_rect(Vector(x, y + 6),
564           Vector(menu_width, 2), Color(255,255,255,255), LAYER_GUI);
565       break;
566     }
567   case MN_LABEL:
568     {
569       context.draw_text_center(white_big_text,
570           pitem.text, Vector(0, y_pos - int(white_big_text->get_height()/2)),
571           LAYER_GUI);
572       break;
573     }
574   case MN_TEXTFIELD:
575   case MN_NUMFIELD:
576   case MN_CONTROLFIELD_KB:
577   case MN_CONTROLFIELD_JS:
578     {
579       int width = text_width + input_width + 5;
580       int text_pos = screen->w/2 - width/2;
581       int input_pos = text_pos + text_width + 10;
582
583       context.draw_filled_rect(
584           Vector(input_pos - 5, y_pos - 10),
585           Vector(input_width + 10, 20),
586           Color(255,255,255,255), LAYER_GUI-5);
587       context.draw_filled_rect(
588           Vector(input_pos - 4, y_pos - 9),
589           Vector(input_width + 8, 18),
590           Color(0,0,0,128), LAYER_GUI-4);
591
592       if(pitem.kind == MN_CONTROLFIELD_KB)
593         get_controlfield_key_into_input(&pitem);
594       else if (pitem.kind == MN_CONTROLFIELD_JS)
595         get_controlfield_js_into_input(&pitem);
596
597       if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
598       {
599         if(active_item == index)
600           context.draw_text(gold_text,
601               pitem.get_input_with_symbol(true),
602               Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
603               LAYER_GUI);
604         else
605           context.draw_text(gold_text,
606               pitem.get_input_with_symbol(false),
607               Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
608               LAYER_GUI);
609       }
610       else
611         context.draw_text(gold_text, pitem.input,
612             Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
613             LAYER_GUI);
614
615       context.draw_text(text_font, pitem.text,
616           Vector(text_pos, y_pos - int(text_font->get_height()/2)),
617           LAYER_GUI);
618       break;
619     }
620   case MN_STRINGSELECT:
621     {
622       int list_pos_2 = list_width + 16;
623       int list_pos   = list_width/2;
624       int text_pos   = (text_width + 16)/2;
625
626       /* Draw arrows */
627       context.draw_surface(arrow_left,
628           Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
629           LAYER_GUI);
630       context.draw_surface(arrow_right,
631           Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8),
632           LAYER_GUI);
633
634       /* Draw input background */
635       context.draw_filled_rect(
636           Vector(x_pos - list_pos + text_pos - 1, y_pos - 10),
637           Vector(list_pos_2 + 2, 20),
638           Color(255,255,255,255), LAYER_GUI - 4);
639       context.draw_filled_rect(
640           Vector(x_pos - list_pos + text_pos, y_pos - 9),
641           Vector(list_pos_2, 18),
642           Color(0,0,0,128), LAYER_GUI - 5);
643
644       context.draw_text_center(text_font, string_list_active(pitem.list),
645           Vector(text_pos, y_pos - int(text_font->get_height()/2)),
646           LAYER_GUI);
647       context.draw_text_center(text_font, pitem.text,
648           Vector(list_pos_2/2, y_pos - int(text_font->get_height()/2)),
649           LAYER_GUI);
650       break;
651     }
652   case MN_BACK:
653     {
654       context.draw_text_center(text_font, pitem.text,
655           Vector(0, y_pos - int(text_font->get_height()/2)),
656           LAYER_GUI);
657       context.draw_surface(back,
658           Vector(x_pos + text_width/2  + 16, y_pos - 8),
659           LAYER_GUI);
660       break;
661     }
662
663   case MN_TOGGLE:
664     {
665       context.draw_text_center(text_font, pitem.text,
666           Vector(0, y_pos - (text_font->get_height()/2)),
667           LAYER_GUI);
668
669       if(pitem.toggled)
670         context.draw_surface(checkbox_checked,
671             Vector(x_pos + (text_width+16)/2, y_pos - 8),
672             LAYER_GUI + 1);
673       else
674         context.draw_surface(checkbox,
675             Vector(x_pos + (text_width+16)/2, y_pos - 8),
676             LAYER_GUI + 1);                                      
677       break;
678     }
679   case MN_ACTION:
680     context.draw_text_center(text_font, pitem.text,
681         Vector(0, y_pos - int(text_font->get_height()/2)),
682         LAYER_GUI);
683     break;
684
685   case MN_GOTO:
686     context.draw_text_center(text_font, pitem.text,
687         Vector(0, y_pos - int(text_font->get_height()/2)),
688         LAYER_GUI);
689     break;
690   }
691 }
692
693 int Menu::get_width() const
694 {
695   /* The width of the menu has to be more than the width of the text
696      with the most characters */
697   int menu_width = 0;
698   for(unsigned int i = 0; i < item.size(); ++i)
699   {
700     int w = strlen(item[i].text) + (item[i].input ? strlen(item[i].input) + 1 : 0) + strlen(string_list_active(item[i].list));
701     if( w > menu_width )
702     {
703       menu_width = w;
704       if( item[i].kind == MN_TOGGLE)
705         menu_width += 2;
706     }
707   }
708
709   return (menu_width * 16 + 24);
710 }
711
712 int Menu::get_height() const
713 {
714   return item.size() * 24;
715 }
716
717 /* Draw the current menu. */
718 void
719 Menu::draw(DrawingContext& context)
720 {
721   int menu_height = get_height();
722   int menu_width  = get_width();
723
724   /* Draw a transparent background */
725   context.draw_filled_rect(
726       Vector(pos_x - menu_width/2, pos_y - 24*item.size()/2 - 10),
727       Vector(menu_width,menu_height + 20),
728       Color(150,180,200,125), LAYER_GUI-10);
729
730   for(unsigned int i = 0; i < item.size(); ++i)
731   {
732     draw_item(context, i, menu_width, menu_height);
733   }
734 }
735
736 MenuItem&
737 Menu::get_item_by_id(int id)
738 {
739   for(std::vector<MenuItem>::iterator i = item.begin(); i != item.end(); ++i)
740   {
741     if(i->id == id)
742       return *i;
743   }
744
745   assert(false);
746   static MenuItem dummyitem;
747   return dummyitem;
748 }
749
750 int Menu::get_active_item_id()
751 {
752   return item[active_item].id;
753 }
754
755 bool
756 Menu::isToggled(int id)
757 {
758   return get_item_by_id(id).toggled;
759 }
760
761 /* Check for menu event */
762 void
763 Menu::event(SDL_Event& event)
764 {
765   switch(event.type)
766     {
767     case SDL_KEYDOWN:
768       {
769         SDLKey key = key = event.key.keysym.sym;
770         SDLMod keymod;
771         char ch[2];
772         keymod = SDL_GetModState();
773
774         /* If the current unicode character is an ASCII character,
775            assign it to ch. */
776         if ( (event.key.keysym.unicode & 0xFF80) == 0 )
777           {
778             ch[0] = event.key.keysym.unicode & 0x7F;
779             ch[1] = '\0';
780           }
781         else
782           {
783             /* An International Character. */
784           }
785
786         if(item[active_item].kind == MN_CONTROLFIELD_KB)
787           {
788             if(key == SDLK_ESCAPE)
789               {
790                 Menu::pop_current();
791                 return;
792               }
793             *item[active_item].int_p = key;
794             menuaction = MENU_ACTION_DOWN;
795             return;
796           }
797
798
799         switch(key)
800           {
801           case SDLK_UP:         /* Menu Up */
802             menuaction = MENU_ACTION_UP;
803             break;
804           case SDLK_DOWN:               /* Menu Down */
805             menuaction = MENU_ACTION_DOWN;
806             break;
807           case SDLK_LEFT:               /* Menu Up */
808             menuaction = MENU_ACTION_LEFT;
809             break;
810           case SDLK_RIGHT:              /* Menu Down */
811             menuaction = MENU_ACTION_RIGHT;
812             break;
813           case SDLK_SPACE:
814             if(item[active_item].kind == MN_TEXTFIELD)
815               {
816                 menuaction = MENU_ACTION_INPUT;
817                 mn_input_char = ' ';
818                 break;
819               }
820           case SDLK_RETURN: /* Menu Hit */
821             menuaction = MENU_ACTION_HIT;
822             break;
823           case SDLK_DELETE:
824           case SDLK_BACKSPACE:
825             menuaction = MENU_ACTION_REMOVE;
826             delete_character++;
827             break;
828           case SDLK_ESCAPE:
829             Menu::pop_current();
830             break;
831           default:
832             if( (key >= SDLK_0 && key <= SDLK_9) || (key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_SPACE && key <= SDLK_SLASH))
833               {
834                 menuaction = MENU_ACTION_INPUT;
835                 mn_input_char = *ch;
836               }
837             else
838               {
839                 mn_input_char = '\0';
840               }
841             break;
842           }
843       }
844       break;
845       
846     case  SDL_JOYAXISMOTION:
847       if(event.jaxis.axis == joystick_keymap.y_axis)
848         {
849           if (event.jaxis.value > joystick_keymap.dead_zone && !joystick_timer.started())
850             {
851               menuaction = MENU_ACTION_DOWN;
852               joystick_timer.start(JOYSTICK_MENU_DELAY);
853             }
854           else if (event.jaxis.value < -joystick_keymap.dead_zone && !joystick_timer.started())
855             {
856               menuaction = MENU_ACTION_UP;
857               joystick_timer.start(JOYSTICK_MENU_DELAY);
858             }
859           else
860             joystick_timer.stop();
861         }
862       break;
863     case  SDL_JOYBUTTONDOWN:
864       if (item[active_item].kind == MN_CONTROLFIELD_JS)
865         {
866           // FIXME: This next line does nothing useable, right? 
867           // *item[active_item].int_p = key;
868           menuaction = MENU_ACTION_DOWN;
869         }
870       menuaction = MENU_ACTION_HIT;
871       break;
872
873     case SDL_MOUSEBUTTONDOWN:
874       {
875         int x = event.motion.x;
876         int y = event.motion.y;
877
878         if(x > pos_x - get_width()/2 &&
879            x < pos_x + get_width()/2 &&
880            y > pos_y - get_height()/2 &&
881            y < pos_y + get_height()/2)
882           {
883             menuaction = MENU_ACTION_HIT;
884           }
885       }
886       break;
887
888     case SDL_MOUSEMOTION:
889       {
890         int x = event.motion.x;
891         int y = event.motion.y;
892
893         if(x > pos_x - get_width()/2 &&
894            x < pos_x + get_width()/2 &&
895            y > pos_y - get_height()/2 &&
896            y < pos_y + get_height()/2)
897           {
898             active_item = (y - (pos_y - get_height()/2)) / 24;
899             mouse_cursor->set_state(MC_LINK);
900           }
901         else
902           {
903             mouse_cursor->set_state(MC_NORMAL);
904           }
905       }
906       break;
907
908     default:
909       break;
910     }
911 }
912
913
914 // EOF //