Added supertux/globals.?pp to collect all the random global variables
[supertux.git] / src / gui / menu.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "gui/menu.hpp"
18
19 #include <math.h>
20
21 #include "control/joystickkeyboardcontroller.hpp"
22 #include "gui/menu_item.hpp"
23 #include "gui/mousecursor.hpp"
24 #include "supertux/globals.hpp"
25 #include "supertux/mainloop.hpp"
26 #include "supertux/resources.hpp"
27 #include "supertux/timer.hpp"
28 #include "util/gettext.hpp"
29 #include "video/drawing_context.hpp"
30 #include "video/font.hpp"
31
32 static const float MENU_REPEAT_INITIAL = 0.4f;
33 static const float MENU_REPEAT_RATE    = 0.1f;
34
35 extern SDL_Surface* g_screen;
36
37 std::vector<Menu*> Menu::last_menus;
38 std::list<Menu*> Menu::all_menus;
39 Menu* Menu::current_ = 0;
40 Menu* Menu::previous = 0;
41
42 /* just displays a Yes/No text that can be used to confirm stuff */
43 bool confirm_dialog(Surface *background, std::string text)
44 {
45   //Surface* cap_screen = Surface::CaptureScreen();
46   Menu* dialog = new Menu;
47   dialog->add_inactive(-1, text);
48   dialog->add_hl();
49   dialog->add_entry(true, _("Yes"));
50   dialog->add_entry(false, _("No"));
51   dialog->add_hl();
52
53   Menu::set_current(dialog);
54
55   DrawingContext context;
56
57   // TODO make this a screen and not another mainloop...
58   while(true)
59   {
60     SDL_Event event;
61     while (SDL_PollEvent(&event)) {
62       if(event.type == SDL_QUIT)
63         g_main_loop->quit();
64       g_main_controller->process_event(event);
65       dialog->event(event);
66     }
67
68     if(background == NULL)
69       context.draw_gradient(Color(0.8f, 0.95f, 0.85f), Color(0.8f, 0.8f, 0.8f),
70                             LAYER_BACKGROUND0);
71     else
72       context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
73
74     dialog->draw(context);
75     dialog->update();
76
77     switch (dialog->check())
78     {
79       case true:
80         //delete cap_screen;
81         Menu::set_current(0);
82         delete dialog;
83         return true;
84         break;
85       case false:
86         //delete cap_screen;
87         Menu::set_current(0);
88         delete dialog;
89         return false;
90         break;
91       default:
92         break;
93     }
94
95     mouse_cursor->draw(context);
96     context.do_drawing();
97     SDL_Delay(25);
98   }
99
100   return false;
101 }
102
103 void
104 Menu::push_current(Menu* pmenu)
105 {
106   previous = current_;
107
108   if (current_)
109     last_menus.push_back(current_);
110
111   current_ = pmenu;
112   current_->effect_start_time = real_time;
113   current_->effect_progress   = 0.0f;
114 }
115
116 void
117 Menu::pop_current()
118 {
119   previous = current_;
120
121   if (last_menus.size() >= 1) {
122     current_ = last_menus.back();
123     current_->effect_start_time = real_time;
124     current_->effect_progress   = 0.0f;
125     last_menus.pop_back();
126   } else {
127     set_current(NULL);
128   }
129 }
130
131 void
132 Menu::set_current(Menu* menu)
133 {
134   if (current_ && current_->close == true)
135     return;
136
137   previous = current_;
138
139   if (menu) {
140     menu->effect_start_time = real_time;
141     menu->effect_progress = 0.0f;
142     current_ = menu;
143   }
144   else if (current_) {
145     last_menus.clear();                         //NULL new menu pointer => close all menus
146     current_->effect_start_time = real_time;
147     current_->effect_progress = 0.0f;
148     current_->close = true;
149   }
150
151   // just to be sure...
152   g_main_controller->reset();
153 }
154
155 void
156 Menu::recalc_pos()
157 {
158   if (current_)
159     current_->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
160
161   for(std::list<Menu*>::iterator i = all_menus.begin(); i != all_menus.end(); ++i)
162   {
163     // FIXME: This is of course not quite right, since it ignores any previous set_pos() calls
164     (*i)->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
165   }
166 }
167
168 Menu::~Menu()
169 {
170   all_menus.remove(this);
171
172   for(std::vector<MenuItem*>::iterator i = items.begin();
173       i != items.end(); ++i)
174     delete *i;
175
176   if(current_ == this)
177     current_ = NULL;
178
179   if (previous == this)
180     previous = NULL;
181 }
182
183 Menu::Menu() :
184   hit_item(),
185   pos_x(),
186   pos_y(),
187   menuaction(),
188   delete_character(),
189   mn_input_char(),
190   menu_repeat_time(),
191   close(false),
192   items(),
193   effect_progress(),
194   effect_start_time(),
195   arrange_left(),
196   active_item(),
197   checkbox(),
198   checkbox_checked(),
199   back(),
200   arrow_left(),
201   arrow_right()
202 {
203   all_menus.push_back(this);
204
205   hit_item = -1;
206   menuaction = MENU_ACTION_NONE;
207   delete_character = 0;
208   mn_input_char = '\0';
209
210   pos_x        = SCREEN_WIDTH/2;
211   pos_y        = SCREEN_HEIGHT/2;
212   arrange_left = 0;
213   active_item  = -1;
214
215   effect_progress   = 0.0f;
216   effect_start_time = 0.0f;
217
218   checkbox.reset(new Surface("images/engine/menu/checkbox-unchecked.png"));
219   checkbox_checked.reset(new Surface("images/engine/menu/checkbox-checked.png"));
220   back.reset(new Surface("images/engine/menu/arrow-back.png"));
221   arrow_left.reset(new Surface("images/engine/menu/arrow-left.png"));
222   arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
223 }
224
225 void
226 Menu::set_pos(float x, float y, float rw, float rh)
227 {
228   pos_x = x + get_width()  * rw;
229   pos_y = y + get_height() * rh;
230 }
231
232 /* Add an item to a menu */
233 void
234 Menu::additem(MenuItem* item)
235 {
236   items.push_back(item);
237
238   /* If a new menu is being built, the active item shouldn't be set to
239    * something that isn't selectable. Set the active_item to the first
240    * selectable item added.
241    */
242   if (active_item == -1
243       && item->kind != MN_HL
244       && item->kind != MN_LABEL
245       && item->kind != MN_INACTIVE) {
246     active_item = items.size() - 1;
247   }
248 }
249
250 MenuItem*
251 Menu::add_hl()
252 {
253   MenuItem* item = new MenuItem(MN_HL);
254   additem(item);
255   return item;
256 }
257
258 MenuItem*
259 Menu::add_label(const std::string& text)
260 {
261   MenuItem* item = new MenuItem(MN_LABEL);
262   item->text = text;
263   additem(item);
264   return item;
265 }
266
267 MenuItem*
268 Menu::add_controlfield(int id, const std::string& text,
269                        const std::string& mapping)
270 {
271   MenuItem* item = new MenuItem(MN_CONTROLFIELD, id);
272   item->change_text(text);
273   item->change_input(mapping);
274   additem(item);
275   return item;
276 }
277
278 MenuItem*
279 Menu::add_entry(int id, const std::string& text)
280 {
281   MenuItem* item = new MenuItem(MN_ACTION, id);
282   item->text = text;
283   additem(item);
284   return item;
285 }
286
287 MenuItem*
288 Menu::add_inactive(int id, const std::string& text)
289 {
290   MenuItem* item = new MenuItem(MN_INACTIVE, id);
291   item->text = text;
292   additem(item);
293   return item;
294 }
295
296 MenuItem*
297 Menu::add_toggle(int id, const std::string& text, bool toogled)
298 {
299   MenuItem* item = new MenuItem(MN_TOGGLE, id);
300   item->text = text;
301   item->toggled = toogled;
302   additem(item);
303   return item;
304 }
305
306 MenuItem*
307 Menu::add_string_select(int id, const std::string& text)
308 {
309   MenuItem* item = new MenuItem(MN_STRINGSELECT, id);
310   item->text = text;
311   additem(item);
312   return item;
313 }
314
315 MenuItem*
316 Menu::add_back(const std::string& text)
317 {
318   MenuItem* item = new MenuItem(MN_BACK);
319   item->text = text;
320   additem(item);
321   return item;
322 }
323
324 MenuItem*
325 Menu::add_submenu(const std::string& text, Menu* submenu, int id)
326 {
327   MenuItem* item = new MenuItem(MN_GOTO, id);
328   item->text = text;
329   item->target_menu = submenu;
330   additem(item);
331   return item;
332 }
333
334 void
335 Menu::clear()
336 {
337   for(std::vector<MenuItem*>::iterator i = items.begin();
338       i != items.end(); ++i) {
339     delete *i;
340   }
341   items.clear();
342   active_item = -1;
343 }
344
345 /* Process actions done on the menu */
346 void
347 Menu::update()
348 {
349   int menu_height = (int) get_height();
350   if (menu_height > SCREEN_HEIGHT)
351   { // Scrolling
352     int scroll_offset = (menu_height - SCREEN_HEIGHT) / 2 + 32;
353     pos_y = SCREEN_HEIGHT/2 - scroll_offset * ((float(active_item) / (items.size()-1)) - 0.5f) * 2.0f;
354   }
355
356   effect_progress = (real_time - effect_start_time) * 6.0f;
357
358   if(effect_progress >= 1.0f) {
359     effect_progress = 1.0f;
360
361     if (close) {
362       current_ = 0;
363       close = false;
364     }
365   }
366   else if (effect_progress <= 0.0f) {
367     effect_progress = 0.0f;
368   }
369
370   /** check main input controller... */
371   if(g_main_controller->pressed(Controller::UP)) {
372     menuaction = MENU_ACTION_UP;
373     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
374   }
375   if(g_main_controller->hold(Controller::UP) &&
376      menu_repeat_time != 0 && real_time > menu_repeat_time) {
377     menuaction = MENU_ACTION_UP;
378     menu_repeat_time = real_time + MENU_REPEAT_RATE;
379   }
380
381   if(g_main_controller->pressed(Controller::DOWN)) {
382     menuaction = MENU_ACTION_DOWN;
383     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
384   }
385   if(g_main_controller->hold(Controller::DOWN) &&
386      menu_repeat_time != 0 && real_time > menu_repeat_time) {
387     menuaction = MENU_ACTION_DOWN;
388     menu_repeat_time = real_time + MENU_REPEAT_RATE;
389   }
390
391   if(g_main_controller->pressed(Controller::LEFT)) {
392     menuaction = MENU_ACTION_LEFT;
393     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
394   }
395   if(g_main_controller->hold(Controller::LEFT) &&
396      menu_repeat_time != 0 && real_time > menu_repeat_time) {
397     menuaction = MENU_ACTION_LEFT;
398     menu_repeat_time = real_time + MENU_REPEAT_RATE;
399   }
400
401   if(g_main_controller->pressed(Controller::RIGHT)) {
402     menuaction = MENU_ACTION_RIGHT;
403     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
404   }
405   if(g_main_controller->hold(Controller::RIGHT) &&
406      menu_repeat_time != 0 && real_time > menu_repeat_time) {
407     menuaction = MENU_ACTION_RIGHT;
408     menu_repeat_time = real_time + MENU_REPEAT_RATE;
409   }
410
411   if(g_main_controller->pressed(Controller::ACTION)
412      || g_main_controller->pressed(Controller::MENU_SELECT)) {
413     menuaction = MENU_ACTION_HIT;
414   }
415   if(g_main_controller->pressed(Controller::PAUSE_MENU)) {
416     menuaction = MENU_ACTION_BACK;
417   }
418
419   hit_item = -1;
420   if(items.size() == 0)
421     return;
422
423   int last_active_item = active_item;
424   switch(menuaction) {
425     case MENU_ACTION_UP:
426       do {
427         if (active_item > 0)
428           --active_item;
429         else
430           active_item = int(items.size())-1;
431       } while ((items[active_item]->kind == MN_HL
432                 || items[active_item]->kind == MN_LABEL
433                 || items[active_item]->kind == MN_INACTIVE)
434                && (active_item != last_active_item));
435
436       break;
437
438     case MENU_ACTION_DOWN:
439       do {
440         if(active_item < int(items.size())-1 )
441           ++active_item;
442         else
443           active_item = 0;
444       } while ((items[active_item]->kind == MN_HL
445                 || items[active_item]->kind == MN_LABEL
446                 || items[active_item]->kind == MN_INACTIVE)
447                && (active_item != last_active_item));
448
449       break;
450
451     case MENU_ACTION_LEFT:
452       if(items[active_item]->kind == MN_STRINGSELECT) {
453         if(items[active_item]->selected > 0)
454           items[active_item]->selected--;
455         else
456           items[active_item]->selected = items[active_item]->list.size()-1;
457         
458         menu_action(items[active_item]);
459       }
460       break;
461
462     case MENU_ACTION_RIGHT:
463       if(items[active_item]->kind == MN_STRINGSELECT) {
464         if(items[active_item]->selected+1 < items[active_item]->list.size())
465           items[active_item]->selected++;
466         else
467           items[active_item]->selected = 0;
468         
469         menu_action(items[active_item]);
470       }
471       break;
472
473     case MENU_ACTION_HIT: {
474       hit_item = active_item;
475       switch (items[active_item]->kind) {
476         case MN_GOTO:
477           assert(items[active_item]->target_menu != 0);
478           Menu::push_current(items[active_item]->target_menu);
479           break;
480
481         case MN_TOGGLE:
482           items[active_item]->toggled = !items[active_item]->toggled;
483           menu_action(items[active_item]);
484           break;
485
486         case MN_CONTROLFIELD:
487           menu_action(items[active_item]);
488           break;
489
490         case MN_ACTION:
491           menu_action(items[active_item]);
492           break;
493
494         case MN_STRINGSELECT:
495           if(items[active_item]->selected+1 < items[active_item]->list.size())
496             items[active_item]->selected++;
497           else
498             items[active_item]->selected = 0;
499
500           menu_action(items[active_item]);
501           break;
502
503         case MN_TEXTFIELD:
504         case MN_NUMFIELD:
505           menuaction = MENU_ACTION_DOWN;
506           update();
507           break;
508
509         case MN_BACK:
510           Menu::pop_current();
511           break;
512         default:
513           break;
514       }
515       break;
516     }
517
518     case MENU_ACTION_REMOVE:
519       if(items[active_item]->kind == MN_TEXTFIELD
520          || items[active_item]->kind == MN_NUMFIELD)
521       {
522         if(!items[active_item]->input.empty())
523         {
524           int i = items[active_item]->input.size();
525
526           while(delete_character > 0)        /* remove characters */
527           {
528             items[active_item]->input.resize(i-1);
529             delete_character--;
530           }
531         }
532       }
533       break;
534
535     case MENU_ACTION_INPUT:
536       if(items[active_item]->kind == MN_TEXTFIELD
537          || (items[active_item]->kind == MN_NUMFIELD
538              && mn_input_char >= '0' && mn_input_char <= '9'))
539       {
540         items[active_item]->input.push_back(mn_input_char);
541       }
542       break;
543
544     case MENU_ACTION_BACK:
545       Menu::pop_current();
546       break;
547
548     case MENU_ACTION_NONE:
549       break;
550   }
551   menuaction = MENU_ACTION_NONE;
552
553   assert(active_item < int(items.size()));
554 }
555
556 int
557 Menu::check()
558 {
559   if (hit_item != -1) {
560     int id = items[hit_item]->id;
561     hit_item = -1;                      //Clear event when checked out.. (we would end up in a loop when we try to leave "fake" submenu like Addons or Contrib)
562     return id;
563   }
564   else
565     return -1;
566 }
567
568 void
569 Menu::menu_action(MenuItem* )
570 {}
571
572 void
573 Menu::draw_item(DrawingContext& context, int index)
574 {
575   float menu_height = get_height();
576   float menu_width  = get_width();
577
578   MenuItem& pitem = *(items[index]);
579
580   Color text_color = default_color;
581   float x_pos       = pos_x;
582   float y_pos       = pos_y + 24*index - menu_height/2 + 12;
583   int shadow_size = 2;
584   int text_width  = int(normal_font->get_text_width(pitem.text));
585   int input_width = int(normal_font->get_text_width(pitem.input) + 10);
586   int list_width = 0;
587
588   float left  = pos_x - menu_width/2 + 16;
589   float right = pos_x + menu_width/2 - 16;
590
591   if(pitem.list.size() > 0) {
592     list_width = (int) normal_font->get_text_width(pitem.list[pitem.selected]);
593   }
594
595   if (arrange_left)
596     x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
597
598   if(index == active_item)
599   {
600     shadow_size = 3;
601     text_color = active_color;
602   }
603
604   if(active_item == index)
605   {
606     float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
607     context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10 - 2, y_pos - 12 - 2),
608                                   Vector(pos_x + menu_width/2 - 10 + 2, y_pos + 12 + 2)),
609                              Color(1.0f, 1.0f, 1.0f, blink),
610                              14.0f,
611                              LAYER_GUI-10);
612     context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10, y_pos - 12),
613                                   Vector(pos_x + menu_width/2 - 10, y_pos + 12)),
614                              Color(1.0f, 1.0f, 1.0f, 0.5f),
615                              12.0f,
616                              LAYER_GUI-10);
617   }
618
619   switch (pitem.kind)
620   {
621     case MN_INACTIVE:
622     {
623       context.draw_text(normal_font, pitem.text,
624                         Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
625                         ALIGN_CENTER, LAYER_GUI, inactive_color);
626       break;
627     }
628
629     case MN_HL:
630     {
631       // TODO
632       float x = pos_x - menu_width/2;
633       float y = y_pos - 12;
634       /* Draw a horizontal line with a little 3d effect */
635       context.draw_filled_rect(Vector(x, y + 6),
636                                Vector(menu_width, 4),
637                                Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
638       context.draw_filled_rect(Vector(x, y + 6),
639                                Vector(menu_width, 2),
640                                Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
641       break;
642     }
643     case MN_LABEL:
644     {
645       context.draw_text(big_font, pitem.text,
646                         Vector(pos_x, y_pos - int(big_font->get_height()/2)),
647                         ALIGN_CENTER, LAYER_GUI, label_color);
648       break;
649     }
650     case MN_TEXTFIELD:
651     case MN_NUMFIELD:
652     case MN_CONTROLFIELD:
653     {
654       if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
655       {
656         if(active_item == index)
657           context.draw_text(normal_font,
658                             pitem.get_input_with_symbol(true),
659                             Vector(right, y_pos - int(normal_font->get_height()/2)),
660                             ALIGN_RIGHT, LAYER_GUI, field_color);
661         else
662           context.draw_text(normal_font,
663                             pitem.get_input_with_symbol(false),
664                             Vector(right, y_pos - int(normal_font->get_height()/2)),
665                             ALIGN_RIGHT, LAYER_GUI, field_color);
666       }
667       else
668         context.draw_text(normal_font, pitem.input,
669                           Vector(right, y_pos - int(normal_font->get_height()/2)),
670                           ALIGN_RIGHT, LAYER_GUI, field_color);
671
672       context.draw_text(normal_font, pitem.text,
673                         Vector(left, y_pos - int(normal_font->get_height()/2)),
674                         ALIGN_LEFT, LAYER_GUI, text_color);
675       break;
676     }
677     case MN_STRINGSELECT:
678     {
679       float roff = arrow_left->get_width();
680       // Draw left side
681       context.draw_text(normal_font, pitem.text,
682                         Vector(left, y_pos - int(normal_font->get_height()/2)),
683                         ALIGN_LEFT, LAYER_GUI, text_color);
684
685       // Draw right side
686       context.draw_surface(arrow_left.get(),
687                            Vector(right - list_width - roff - roff, y_pos - 8),
688                            LAYER_GUI);
689       context.draw_surface(arrow_right.get(),
690                            Vector(right - roff, y_pos - 8),
691                            LAYER_GUI);
692       context.draw_text(normal_font, pitem.list[pitem.selected],
693                         Vector(right - roff, y_pos - int(normal_font->get_height()/2)),
694                         ALIGN_RIGHT, LAYER_GUI, text_color);
695       break;
696     }
697     case MN_BACK:
698     {
699       context.draw_text(normal_font, pitem.text,
700                         Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
701                         ALIGN_CENTER, LAYER_GUI, text_color);
702       context.draw_surface(back.get(),
703                            Vector(x_pos + text_width/2  + 16, y_pos - 8),
704                            LAYER_GUI);
705       break;
706     }
707
708     case MN_TOGGLE:
709     {
710       context.draw_text(normal_font, pitem.text,
711                         Vector(pos_x - menu_width/2 + 16, y_pos - (normal_font->get_height()/2)),
712                         ALIGN_LEFT, LAYER_GUI, text_color);
713
714       if(pitem.toggled)
715         context.draw_surface(checkbox_checked.get(),
716                              Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
717                              LAYER_GUI + 1);
718       else
719         context.draw_surface(checkbox.get(),
720                              Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
721                              LAYER_GUI + 1);
722       break;
723     }
724     case MN_ACTION:
725       context.draw_text(normal_font, pitem.text,
726                         Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
727                         ALIGN_CENTER, LAYER_GUI, text_color);
728       break;
729
730     case MN_GOTO:
731       context.draw_text(normal_font, pitem.text,
732                         Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
733                         ALIGN_CENTER, LAYER_GUI, text_color);
734       break;
735   }
736 }
737
738 float
739 Menu::get_width() const
740 {
741   /* The width of the menu has to be more than the width of the text
742      with the most characters */
743   float menu_width = 0;
744   for(unsigned int i = 0; i < items.size(); ++i)
745   {
746     Font* font = normal_font;
747     if(items[i]->kind == MN_LABEL)
748       font = big_font;
749
750     float w = font->get_text_width(items[i]->text) +
751       big_font->get_text_width(items[i]->input) + 16;
752     if(items[i]->kind == MN_TOGGLE)
753       w += 32;
754
755     if(w > menu_width)
756       menu_width = w;
757   }
758
759   return menu_width + 24;
760 }
761
762 float
763 Menu::get_height() const
764 {
765   return items.size() * 24;
766 }
767
768 /* Draw the current menu. */
769 void
770 Menu::draw(DrawingContext& context)
771 {
772   if(MouseCursor::current()) {
773     MouseCursor::current()->draw(context);
774   }
775
776   float menu_width  = get_width();
777   float menu_height = get_height();
778
779   if (effect_progress != 1.0f)
780   {
781     if (close)
782     {
783       menu_width  = (current_->get_width()  * (1.0f - effect_progress));
784       menu_height = (current_->get_height() * (1.0f - effect_progress));
785     }
786     else if (Menu::previous)
787     {
788       menu_width  = (menu_width  * effect_progress) + (Menu::previous->get_width()  * (1.0f - effect_progress));
789       menu_height = (menu_height * effect_progress) + (Menu::previous->get_height() * (1.0f - effect_progress));
790       //std::cout << effect_progress << " " << this << " " << last_menus.back() << std::endl;
791     }
792     else
793     {
794       menu_width  *= effect_progress;
795       menu_height *= effect_progress;
796     }
797   }
798
799   /* Draw a transparent background */
800   context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2-4, pos_y - menu_height/2 - 10-4),
801                                 Vector(pos_x + menu_width/2+4, pos_y - menu_height/2 + 10 + menu_height+4)),
802                            Color(0.2f, 0.3f, 0.4f, 0.8f), 
803                            20.0f,
804                            LAYER_GUI-10);
805
806   context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2, pos_y - menu_height/2 - 10),
807                                 Vector(pos_x + menu_width/2, pos_y - menu_height/2 + 10 + menu_height)),
808                            Color(0.6f, 0.7f, 0.8f, 0.5f), 
809                            16.0f,
810                            LAYER_GUI-10);
811
812   if (!items[active_item]->help.empty())
813   {
814     int text_width  = (int) normal_font->get_text_width(items[active_item]->help);
815     int text_height = (int) normal_font->get_text_height(items[active_item]->help);
816       
817     Rect text_rect(pos_x - text_width/2 - 8, 
818                    SCREEN_HEIGHT - 48 - text_height/2 - 4,
819                    pos_x + text_width/2 + 8, 
820                    SCREEN_HEIGHT - 48 + text_height/2 + 4);
821         
822     context.draw_filled_rect(Rect(text_rect.p1 - Vector(4,4),
823                                   text_rect.p2 + Vector(4,4)),
824                              Color(0.2f, 0.3f, 0.4f, 0.8f), 
825                              16.0f,
826                              LAYER_GUI-10);
827       
828     context.draw_filled_rect(text_rect,
829                              Color(0.6f, 0.7f, 0.8f, 0.5f), 
830                              16.0f,
831                              LAYER_GUI-10);
832
833     context.draw_text(normal_font, items[active_item]->help,
834                       Vector(pos_x, SCREEN_HEIGHT - 48 - text_height/2),
835                       ALIGN_CENTER, LAYER_GUI);
836   }
837
838   if (effect_progress == 1.0f)
839     for(unsigned int i = 0; i < items.size(); ++i)
840     {
841       draw_item(context, i);
842     }
843 }
844
845 MenuItem&
846 Menu::get_item_by_id(int id)
847 {
848   for(std::vector<MenuItem*>::iterator i = items.begin();
849       i != items.end(); ++i) {
850     MenuItem& item = **i;
851
852     if(item.id == id)
853       return item;
854   }
855
856   throw std::runtime_error("MenuItem not found");
857 }
858
859 const MenuItem&
860 Menu::get_item_by_id(int id) const
861 {
862   for(std::vector<MenuItem*>::const_iterator i = items.begin();
863       i != items.end(); ++i) {
864     const MenuItem& item = **i;
865
866     if(item.id == id)
867       return item;
868   }
869
870   throw std::runtime_error("MenuItem not found");
871 }
872
873 int Menu::get_active_item_id()
874 {
875   return items[active_item]->id;
876 }
877
878 bool
879 Menu::is_toggled(int id) const
880 {
881   return get_item_by_id(id).toggled;
882 }
883
884 void
885 Menu::set_toggled(int id, bool toggled)
886 {
887   get_item_by_id(id).toggled = toggled;
888 }
889
890 Menu*
891 Menu::get_parent() const
892 {
893   if (last_menus.empty())
894     return 0;
895   else
896     return last_menus.back();
897 }
898
899 /* Check for menu event */
900 void
901 Menu::event(const SDL_Event& event)
902 {
903   if(effect_progress != 1.0f)
904     return;
905
906   switch(event.type) {
907     case SDL_MOUSEBUTTONDOWN:
908     {
909       int x = int(event.motion.x * float(SCREEN_WIDTH)/g_screen->w);
910       int y = int(event.motion.y * float(SCREEN_HEIGHT)/g_screen->h);
911
912       if(x > pos_x - get_width()/2 &&
913          x < pos_x + get_width()/2 &&
914          y > pos_y - get_height()/2 &&
915          y < pos_y + get_height()/2)
916       {
917         menuaction = MENU_ACTION_HIT;
918       }
919     }
920     break;
921
922     case SDL_MOUSEMOTION:
923     {
924       float x = event.motion.x * SCREEN_WIDTH/g_screen->w;
925       float y = event.motion.y * SCREEN_HEIGHT/g_screen->h;
926
927       if(x > pos_x - get_width()/2 &&
928          x < pos_x + get_width()/2 &&
929          y > pos_y - get_height()/2 &&
930          y < pos_y + get_height()/2)
931       {
932         int new_active_item
933           = static_cast<int> ((y - (pos_y - get_height()/2)) / 24);
934
935         /* only change the mouse focus to a selectable item */
936         if ((items[new_active_item]->kind != MN_HL)
937             && (items[new_active_item]->kind != MN_LABEL)
938             && (items[new_active_item]->kind != MN_INACTIVE))
939           active_item = new_active_item;
940
941         if(MouseCursor::current())
942           MouseCursor::current()->set_state(MC_LINK);
943       }
944       else
945       {
946         if(MouseCursor::current())
947           MouseCursor::current()->set_state(MC_NORMAL);
948       }
949     }
950     break;
951
952     default:
953       break;
954   }
955 }
956
957 void
958 Menu::set_active_item(int id)
959 {
960   for(size_t i = 0; i < items.size(); ++i) {
961     MenuItem* item = items[i];
962     if(item->id == id) {
963       active_item = i;
964       break;
965     }
966   }
967 }
968
969 /* EOF */