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