Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / src / gui / menu.cpp
index d70b16a..6c2970a 100644 (file)
@@ -1,12 +1,10 @@
-//  $Id$
-//
 //  SuperTux
-//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
 //
-//  This program is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU General Public License
-//  as published by the Free Software Foundation; either version 2
-//  of the License, or (at your option) any later version.
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
 //
 //  This program is distributed in the hope that it will be useful,
 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
 //  GNU General Public License for more details.
 //
 //  You should have received a copy of the GNU General Public License
-//  along with this program; if not, write to the Free Software
-//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#include <config.h>
-
-#include <sys/types.h>
-#include <ctype.h>
-
-#include <iostream>
-#include <sstream>
-#include <cstdlib>
-#include <cstdio>
-#include <string>
-#include <cassert>
-#include <stdexcept>
-
-#include "menu.h"
-#include "video/screen.h"
-#include "video/drawing_context.h"
-#include "gettext.h"
-#include "math/vector.h"
-#include "main.h"
-#include "resources.h"
-#include "control/joystickkeyboardcontroller.h"
-
-static const int MENU_REPEAT_INITIAL = 400;
-static const int MENU_REPEAT_RATE = 200;
-static const int FLICK_CURSOR_TIME = 500;
-
-extern SDL_Surface* screen;
-
-Surface* checkbox;
-Surface* checkbox_checked;
-Surface* back;
-Surface* arrow_left;
-Surface* arrow_right;
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#include <math.h>
+
+#include "control/joystickkeyboardcontroller.hpp"
+#include "gui/menu.hpp"
+#include "supertux/main.hpp"
+#include "supertux/mainloop.hpp"
+#include "supertux/resources.hpp"
+#include "supertux/timer.hpp"
+#include "util/gettext.hpp"
+#include "video/drawing_context.hpp"
+
+static const float MENU_REPEAT_INITIAL = 0.4f;
+static const float MENU_REPEAT_RATE    = 0.1f;
+static const float FLICK_CURSOR_TIME   = 0.5f;
+
+extern SDL_Surface* g_screen;
 
 std::vector<Menu*> Menu::last_menus;
+std::list<Menu*> Menu::all_menus;
 Menu* Menu::current_ = 0;
-Font* Menu::default_font;
-Font* Menu::active_font;
-Font* Menu::deactive_font;
-Font* Menu::label_font;
-Font* Menu::field_font;
+Menu* Menu::previous = 0;
 
 /* just displays a Yes/No text that can be used to confirm stuff */
 bool confirm_dialog(Surface *background, std::string text)
 {
   //Surface* cap_screen = Surface::CaptureScreen();
   Menu* dialog = new Menu;
-  dialog->add_deactive(-1, text);
+  dialog->add_inactive(-1, text);
   dialog->add_hl();
   dialog->add_entry(true, _("Yes"));
   dialog->add_entry(false, _("No"));
   dialog->add_hl();
-  
+
   Menu::set_current(dialog);
 
   DrawingContext context;
 
+  // TODO make this a screen and not another mainloop...
   while(true)
-    {
-      SDL_Event event;
-
+  {
+    SDL_Event event;
+    while (SDL_PollEvent(&event)) {
       if(event.type == SDL_QUIT)
-        throw std::runtime_error("received window close event");
-      
-      while (SDL_PollEvent(&event)) {
-        dialog->event(event);
-      }
-
-      if(background == NULL)
-        context.draw_gradient(Color(200,240,220), Color(200,200,220), LAYER_BACKGROUND0);
-      else
-        context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
+        g_main_loop->quit();
+      g_main_controller->process_event(event);
+      dialog->event(event);
+    }
 
-      dialog->draw(context);
-      dialog->action();
+    if(background == NULL)
+      context.draw_gradient(Color(0.8f, 0.95f, 0.85f), Color(0.8f, 0.8f, 0.8f),
+                            LAYER_BACKGROUND0);
+    else
+      context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
 
-      switch (dialog->check())
-        {
-        case true:
-          //delete cap_screen;
-          Menu::set_current(0);
-          delete dialog;
-          return true;
-          break;
-        case false:
-          //delete cap_screen;
-          Menu::set_current(0);
-          delete dialog;
-          return false;
-          break;
-        default:
-          break;
-        }
+    dialog->draw(context);
+    dialog->update();
 
-      mouse_cursor->draw(context);
-      context.do_drawing();
-      SDL_Delay(25);
+    switch (dialog->check())
+    {
+      case true:
+        //delete cap_screen;
+        Menu::set_current(0);
+        delete dialog;
+        return true;
+        break;
+      case false:
+        //delete cap_screen;
+        Menu::set_current(0);
+        delete dialog;
+        return false;
+        break;
+      default:
+        break;
     }
 
+    mouse_cursor->draw(context);
+    context.do_drawing();
+    SDL_Delay(25);
+  }
+
   return false;
 }
 
 void
 Menu::push_current(Menu* pmenu)
 {
+  previous = current_;
+
   if (current_)
     last_menus.push_back(current_);
 
   current_ = pmenu;
-  current_->effect_ticks = SDL_GetTicks();
+  current_->effect_start_time = real_time;
+  current_->effect_progress   = 0.0f;
 }
 
 void
 Menu::pop_current()
 {
+  previous = current_;
+
   if (last_menus.size() >= 1) {
     current_ = last_menus.back();
-    current_->effect_ticks = SDL_GetTicks();
+    current_->effect_start_time = real_time;
+    current_->effect_progress   = 0.0f;
     last_menus.pop_back();
   } else {
-    current_ = 0;
+    set_current(NULL);
   }
 }
 
 void
 Menu::set_current(Menu* menu)
 {
-  last_menus.clear();
+  if (current_ && current_->close == true)
+    return;
+
+  previous = current_;
 
-  if (menu)
-    menu->effect_ticks = SDL_GetTicks();
+  if (menu) {
+    menu->effect_start_time = real_time;
+    menu->effect_progress = 0.0f;
+    current_ = menu;
+  }
+  else if (current_) {
+    last_menus.clear();                         //NULL new menu pointer => close all menus
+    current_->effect_start_time = real_time;
+    current_->effect_progress = 0.0f;
+    current_->close = true;
+  }
 
-  current_ = menu;
   // just to be sure...
-  main_controller->reset();
+  g_main_controller->reset();
+}
+
+void
+Menu::recalc_pos()
+{
+  if (current_)
+    current_->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
+
+  for(std::list<Menu*>::iterator i = all_menus.begin(); i != all_menus.end(); ++i)
+  {
+    // FIXME: This is of course not quite right, since it ignores any previous set_pos() calls
+    (*i)->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
+  }
 }
 
 MenuItem::MenuItem(MenuItemKind _kind, int _id)
@@ -173,19 +182,31 @@ MenuItem::change_input(const  std::string& text_)
   input = text_;
 }
 
+void
+MenuItem::set_help(const std::string& help_text)
+{
+  std::string overflow;
+  help = normal_font->wrap_to_width(help_text, 600, &overflow);
+  while (!overflow.empty())
+  {
+    help += "\n";
+    help += normal_font->wrap_to_width(overflow, 600, &overflow);
+  }
+}
+
 std::string MenuItem::get_input_with_symbol(bool active_item)
 {
   if(!active_item) {
     input_flickering = true;
   } else {
-    input_flickering = (SDL_GetTicks() / FLICK_CURSOR_TIME) % 2;
+    input_flickering = ((int) (real_time / FLICK_CURSOR_TIME)) % 2;
   }
 
   char str[1024];
   if(input_flickering)
-    sprintf(str,"%s ",input.c_str());
+    snprintf(str, sizeof(str), "%s ",input.c_str());
   else
-    sprintf(str,"%s_",input.c_str());
+    snprintf(str, sizeof(str), "%s_",input.c_str());
 
   std::string string = str;
 
@@ -194,13 +215,24 @@ std::string MenuItem::get_input_with_symbol(bool active_item)
 
 Menu::~Menu()
 {
+  all_menus.remove(this);
+
   for(std::vector<MenuItem*>::iterator i = items.begin();
       i != items.end(); ++i)
     delete *i;
+
+  if(current_ == this)
+    current_ = NULL;
+
+  if (previous == this)
+    previous = NULL;
 }
 
 Menu::Menu()
+  : close(false)
 {
+  all_menus.push_back(this);
+
   hit_item = -1;
   menuaction = MENU_ACTION_NONE;
   delete_character = 0;
@@ -210,12 +242,22 @@ Menu::Menu()
   pos_y        = SCREEN_HEIGHT/2;
   arrange_left = 0;
   active_item  = -1;
+
+  effect_progress   = 0.0f;
+  effect_start_time = 0.0f;
+
+  checkbox.reset(new Surface("images/engine/menu/checkbox-unchecked.png"));
+  checkbox_checked.reset(new Surface("images/engine/menu/checkbox-checked.png"));
+  back.reset(new Surface("images/engine/menu/arrow-back.png"));
+  arrow_left.reset(new Surface("images/engine/menu/arrow-left.png"));
+  arrow_right.reset(new Surface("images/engine/menu/arrow-right.png"));
 }
 
-void Menu::set_pos(int x, int y, float rw, float rh)
+void
+Menu::set_pos(float x, float y, float rw, float rh)
 {
-  pos_x = x + (int)((float)get_width() * rw);
-  pos_y = y + (int)((float)get_height() * rh);
+  pos_x = x + get_width()  * rw;
+  pos_y = y + get_height() * rh;
 }
 
 /* Add an item to a menu */
@@ -225,81 +267,99 @@ Menu::additem(MenuItem* item)
   items.push_back(item);
 
   /* If a new menu is being built, the active item shouldn't be set to
-   * something that isnt selectable. Set the active_item to the first
-   * selectable item added
+   * something that isn't selectable. Set the active_item to the first
+   * selectable item added.
    */
   if (active_item == -1
-      && item->kind != MN_HL 
+      && item->kind != MN_HL
       && item->kind != MN_LABEL
-      && item->kind != MN_DEACTIVE) {
+      && item->kind != MN_INACTIVE) {
     active_item = items.size() - 1;
   }
 }
 
-void
+MenuItem*
 Menu::add_hl()
 {
-  additem(new MenuItem(MN_HL));
+  MenuItem* item = new MenuItem(MN_HL);
+  additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_label(const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_LABEL);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_controlfield(int id, const std::string& text,
-               const std::string& mapping)
+                       const std::string& mapping)
 {
   MenuItem* item = new MenuItem(MN_CONTROLFIELD, id);
   item->change_text(text);
-       item->change_input(mapping);
+  item->change_input(mapping);
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_entry(int id, const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_ACTION, id);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
-Menu::add_deactive(int id, const std::string& text)
+MenuItem*
+Menu::add_inactive(int id, const std::string& text)
 {
-  MenuItem* item = new MenuItem(MN_DEACTIVE, id);
+  MenuItem* item = new MenuItem(MN_INACTIVE, id);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_toggle(int id, const std::string& text, bool toogled)
 {
   MenuItem* item = new MenuItem(MN_TOGGLE, id);
   item->text = text;
   item->toggled = toogled;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
+Menu::add_string_select(int id, const std::string& text)
+{
+  MenuItem* item = new MenuItem(MN_STRINGSELECT, id);
+  item->text = text;
+  additem(item);
+  return item;
+}
+
+MenuItem*
 Menu::add_back(const std::string& text)
 {
   MenuItem* item = new MenuItem(MN_BACK);
   item->text = text;
   additem(item);
+  return item;
 }
 
-void
+MenuItem*
 Menu::add_submenu(const std::string& text, Menu* submenu, int id)
 {
   MenuItem* item = new MenuItem(MN_GOTO, id);
   item->text = text;
   item->target_menu = submenu;
   additem(item);
+  return item;
 }
 
 void
@@ -315,41 +375,82 @@ Menu::clear()
 
 /* Process actions done on the menu */
 void
-Menu::action()
+Menu::update()
 {
+  int menu_height = (int) get_height();
+  if (menu_height > SCREEN_HEIGHT)
+  { // Scrolling
+    int scroll_offset = (menu_height - SCREEN_HEIGHT) / 2 + 32;
+    pos_y = SCREEN_HEIGHT/2 - scroll_offset * ((float(active_item) / (items.size()-1)) - 0.5f) * 2.0f;
+  }
+
+  effect_progress = (real_time - effect_start_time) * 6.0f;
+
+  if(effect_progress >= 1.0f) {
+    effect_progress = 1.0f;
+
+    if (close) {
+      current_ = 0;
+      close = false;
+    }
+  }
+  else if (effect_progress <= 0.0f) {
+    effect_progress = 0.0f;
+  }
+
   /** check main input controller... */
-  Uint32 ticks = SDL_GetTicks();
-  if(main_controller->pressed(Controller::UP)) {
+  if(g_main_controller->pressed(Controller::UP)) {
     menuaction = MENU_ACTION_UP;
-    menu_repeat_ticks = ticks + MENU_REPEAT_INITIAL;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
   }
-  if(main_controller->hold(Controller::UP) && 
-      menu_repeat_ticks != 0 && ticks > menu_repeat_ticks) {
+  if(g_main_controller->hold(Controller::UP) &&
+     menu_repeat_time != 0 && real_time > menu_repeat_time) {
     menuaction = MENU_ACTION_UP;
-    menu_repeat_ticks = ticks + MENU_REPEAT_RATE;
-  } 
-  if(main_controller->pressed(Controller::DOWN)) {
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
+  }
+
+  if(g_main_controller->pressed(Controller::DOWN)) {
     menuaction = MENU_ACTION_DOWN;
-    menu_repeat_ticks = ticks + MENU_REPEAT_INITIAL;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
   }
-  if(main_controller->hold(Controller::DOWN) && 
-      menu_repeat_ticks != 0 && ticks > menu_repeat_ticks) {
+  if(g_main_controller->hold(Controller::DOWN) &&
+     menu_repeat_time != 0 && real_time > menu_repeat_time) {
     menuaction = MENU_ACTION_DOWN;
-    menu_repeat_ticks = ticks + MENU_REPEAT_RATE;
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
+  }
+
+  if(g_main_controller->pressed(Controller::LEFT)) {
+    menuaction = MENU_ACTION_LEFT;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+  }
+  if(g_main_controller->hold(Controller::LEFT) &&
+     menu_repeat_time != 0 && real_time > menu_repeat_time) {
+    menuaction = MENU_ACTION_LEFT;
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
+  }
+
+  if(g_main_controller->pressed(Controller::RIGHT)) {
+    menuaction = MENU_ACTION_RIGHT;
+    menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
+  }
+  if(g_main_controller->hold(Controller::RIGHT) &&
+     menu_repeat_time != 0 && real_time > menu_repeat_time) {
+    menuaction = MENU_ACTION_RIGHT;
+    menu_repeat_time = real_time + MENU_REPEAT_RATE;
   }
-  if(main_controller->pressed(Controller::JUMP)
-     || main_controller->pressed(Controller::ACTION)
-     || main_controller->pressed(Controller::MENU_SELECT)) {
+
+  if(g_main_controller->pressed(Controller::ACTION)
+     || g_main_controller->pressed(Controller::MENU_SELECT)) {
     menuaction = MENU_ACTION_HIT;
   }
-  if(main_controller->pressed(Controller::PAUSE_MENU)) {
+  if(g_main_controller->pressed(Controller::PAUSE_MENU)) {
     menuaction = MENU_ACTION_BACK;
   }
 
   hit_item = -1;
   if(items.size() == 0)
     return;
-  
+
   int last_active_item = active_item;
   switch(menuaction) {
     case MENU_ACTION_UP:
@@ -358,13 +459,13 @@ Menu::action()
           --active_item;
         else
           active_item = int(items.size())-1;
-      } while ((items[active_item]->kind == MN_HL 
+      } while ((items[active_item]->kind == MN_HL
                 || items[active_item]->kind == MN_LABEL
-                || items[active_item]->kind == MN_DEACTIVE)
+                || items[active_item]->kind == MN_INACTIVE)
                && (active_item != last_active_item));
-      
+
       break;
-      
+
     case MENU_ACTION_DOWN:
       do {
         if(active_item < int(items.size())-1 )
@@ -373,29 +474,33 @@ Menu::action()
           active_item = 0;
       } while ((items[active_item]->kind == MN_HL
                 || items[active_item]->kind == MN_LABEL
-                || items[active_item]->kind == MN_DEACTIVE)
+                || items[active_item]->kind == MN_INACTIVE)
                && (active_item != last_active_item));
-      
+
       break;
-      
+
     case MENU_ACTION_LEFT:
       if(items[active_item]->kind == MN_STRINGSELECT) {
         if(items[active_item]->selected > 0)
           items[active_item]->selected--;
         else
           items[active_item]->selected = items[active_item]->list.size()-1;
+        
+        menu_action(items[active_item]);
       }
       break;
-      
+
     case MENU_ACTION_RIGHT:
       if(items[active_item]->kind == MN_STRINGSELECT) {
         if(items[active_item]->selected+1 < items[active_item]->list.size())
           items[active_item]->selected++;
         else
           items[active_item]->selected = 0;
+        
+        menu_action(items[active_item]);
       }
       break;
-      
+
     case MENU_ACTION_HIT: {
       hit_item = active_item;
       switch (items[active_item]->kind) {
@@ -403,26 +508,35 @@ Menu::action()
           assert(items[active_item]->target_menu != 0);
           Menu::push_current(items[active_item]->target_menu);
           break;
-          
+
         case MN_TOGGLE:
           items[active_item]->toggled = !items[active_item]->toggled;
           menu_action(items[active_item]);
           break;
-          
+
         case MN_CONTROLFIELD:
           menu_action(items[active_item]);
           break;
-          
+
         case MN_ACTION:
           menu_action(items[active_item]);
           break;
-          
+
+        case MN_STRINGSELECT:
+          if(items[active_item]->selected+1 < items[active_item]->list.size())
+            items[active_item]->selected++;
+          else
+            items[active_item]->selected = 0;
+
+          menu_action(items[active_item]);
+          break;
+
         case MN_TEXTFIELD:
         case MN_NUMFIELD:
           menuaction = MENU_ACTION_DOWN;
-          action();
+          update();
           break;
-          
+
         case MN_BACK:
           Menu::pop_current();
           break;
@@ -431,7 +545,7 @@ Menu::action()
       }
       break;
     }
-    
+
     case MENU_ACTION_REMOVE:
       if(items[active_item]->kind == MN_TEXTFIELD
          || items[active_item]->kind == MN_NUMFIELD)
@@ -439,8 +553,8 @@ Menu::action()
         if(!items[active_item]->input.empty())
         {
           int i = items[active_item]->input.size();
-          
-          while(delete_character > 0)  /* remove charactes */
+
+          while(delete_character > 0)        /* remove characters */
           {
             items[active_item]->input.resize(i-1);
             delete_character--;
@@ -448,16 +562,16 @@ Menu::action()
         }
       }
       break;
-      
+
     case MENU_ACTION_INPUT:
       if(items[active_item]->kind == MN_TEXTFIELD
-         || (items[active_item]->kind == MN_NUMFIELD 
+         || (items[active_item]->kind == MN_NUMFIELD
              && mn_input_char >= '0' && mn_input_char <= '9'))
       {
         items[active_item]->input.push_back(mn_input_char);
       }
       break;
-      
+
     case MENU_ACTION_BACK:
       Menu::pop_current();
       break;
@@ -473,8 +587,11 @@ Menu::action()
 int
 Menu::check()
 {
-  if (hit_item != -1)
-    return items[hit_item]->id;
+  if (hit_item != -1) {
+    int id = items[hit_item]->id;
+    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)
+    return id;
+  }
   else
     return -1;
 }
@@ -486,221 +603,271 @@ Menu::menu_action(MenuItem* )
 void
 Menu::draw_item(DrawingContext& context, int index)
 {
-  int menu_height = get_height();
-  int menu_width = get_width();  
+  float menu_height = get_height();
+  float menu_width  = get_width();
 
   MenuItem& pitem = *(items[index]);
 
-  int effect_offset = 0;
-  if(effect_ticks != 0) {
-    if(SDL_GetTicks() - effect_ticks > 500) {
-      effect_ticks = 0;
-    } else {
-      Uint32 effect_time = (500 - (SDL_GetTicks() - effect_ticks)) / 4;
-      effect_offset = (index % 2) ? effect_time : -effect_time;
-    }
-  }
-
-  Font* text_font = default_font;
-  int x_pos       = pos_x;
-  int y_pos       = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
+  Color text_color = default_color;
+  float x_pos       = pos_x;
+  float y_pos       = pos_y + 24*index - menu_height/2 + 12;
   int shadow_size = 2;
-  int text_width  = int(text_font->get_text_width(pitem.text));
-  int input_width = int(text_font->get_text_width(pitem.input) + 10);
+  int text_width  = int(normal_font->get_text_width(pitem.text));
+  int input_width = int(normal_font->get_text_width(pitem.input) + 10);
   int list_width = 0;
+
+  float left  = pos_x - menu_width/2 + 16;
+  float right = pos_x + menu_width/2 - 16;
+
   if(pitem.list.size() > 0) {
-    list_width = (int) text_font->get_text_width(pitem.list[pitem.selected]);
+    list_width = (int) normal_font->get_text_width(pitem.list[pitem.selected]);
   }
-  
+
   if (arrange_left)
     x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
 
   if(index == active_item)
-    {
-      shadow_size = 3;
-      text_font = active_font;
-    }
+  {
+    shadow_size = 3;
+    text_color = active_color;
+  }
+
+  if(active_item == index)
+  {
+    float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
+    context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10 - 2, y_pos - 12 - 2),
+                                  Vector(pos_x + menu_width/2 - 10 + 2, y_pos + 12 + 2)),
+                             Color(1.0f, 1.0f, 1.0f, blink),
+                             14.0f,
+                             LAYER_GUI-10);
+    context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2 + 10, y_pos - 12),
+                                  Vector(pos_x + menu_width/2 - 10, y_pos + 12)),
+                             Color(1.0f, 1.0f, 1.0f, 0.5f),
+                             12.0f,
+                             LAYER_GUI-10);
+  }
 
   switch (pitem.kind)
+  {
+    case MN_INACTIVE:
     {
-    case MN_DEACTIVE:
-      {
-        context.draw_text(deactive_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(deactive_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
-        break;
-      }
+      context.draw_text(normal_font, pitem.text,
+                        Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI, inactive_color);
+      break;
+    }
 
     case MN_HL:
-      {
-        // TODO
-        int x = pos_x - menu_width/2;
-        int y = y_pos - 12 - effect_offset;
-        /* Draw a horizontal line with a little 3d effect */
-        context.draw_filled_rect(Vector(x, y + 6),
-                                 Vector(menu_width, 4), Color(150,200,255,225), LAYER_GUI);
-        context.draw_filled_rect(Vector(x, y + 6),
-                                 Vector(menu_width, 2), Color(255,255,255,255), LAYER_GUI);
-        break;
-      }
+    {
+      // TODO
+      float x = pos_x - menu_width/2;
+      float y = y_pos - 12;
+      /* Draw a horizontal line with a little 3d effect */
+      context.draw_filled_rect(Vector(x, y + 6),
+                               Vector(menu_width, 4),
+                               Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
+      context.draw_filled_rect(Vector(x, y + 6),
+                               Vector(menu_width, 2),
+                               Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
+      break;
+    }
     case MN_LABEL:
-      {
-        context.draw_text(label_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(label_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
-        break;
-      }
+    {
+      context.draw_text(big_font, pitem.text,
+                        Vector(pos_x, y_pos - int(big_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI, label_color);
+      break;
+    }
     case MN_TEXTFIELD:
     case MN_NUMFIELD:
     case MN_CONTROLFIELD:
+    {
+      if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
       {
-        int width = text_width + input_width + 5;
-        int text_pos = SCREEN_WIDTH/2 - width/2;
-        int input_pos = text_pos + text_width + 10;
-
-        context.draw_filled_rect(
-          Vector(input_pos - 5, y_pos - 10),
-          Vector(input_width + 10, 20),
-          Color(255,255,255,255), LAYER_GUI-5);
-        context.draw_filled_rect(
-          Vector(input_pos - 4, y_pos - 9),
-          Vector(input_width + 8, 18),
-          Color(0,0,0,128), LAYER_GUI-4);
-
-        if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
-          {
-            if(active_item == index)
-              context.draw_text(field_font,
-                                pitem.get_input_with_symbol(true),
-                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                                LEFT_ALLIGN, LAYER_GUI);
-            else
-              context.draw_text(field_font,
-                                pitem.get_input_with_symbol(false),
-                                Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                                LEFT_ALLIGN, LAYER_GUI);
-          }
+        if(active_item == index)
+          context.draw_text(normal_font,
+                            pitem.get_input_with_symbol(true),
+                            Vector(right, y_pos - int(normal_font->get_height()/2)),
+                            ALIGN_RIGHT, LAYER_GUI, field_color);
         else
-          context.draw_text(field_font, pitem.input,
-                            Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                            LEFT_ALLIGN, LAYER_GUI);
-
-        context.draw_text(text_font, pitem.text,
-                          Vector(text_pos, y_pos - int(text_font->get_height()/2)),
-                          LEFT_ALLIGN, LAYER_GUI);
-        break;
+          context.draw_text(normal_font,
+                            pitem.get_input_with_symbol(false),
+                            Vector(right, y_pos - int(normal_font->get_height()/2)),
+                            ALIGN_RIGHT, LAYER_GUI, field_color);
       }
+      else
+        context.draw_text(normal_font, pitem.input,
+                          Vector(right, y_pos - int(normal_font->get_height()/2)),
+                          ALIGN_RIGHT, LAYER_GUI, field_color);
+
+      context.draw_text(normal_font, pitem.text,
+                        Vector(left, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_LEFT, LAYER_GUI, text_color);
+      break;
+    }
     case MN_STRINGSELECT:
-      {
-        int list_pos_2 = list_width + 16;
-        int list_pos   = list_width/2;
-        int text_pos   = (text_width + 16)/2;
-
-        /* Draw arrows */
-        context.draw_surface(arrow_left,
-                             Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
-                             LAYER_GUI);
-        context.draw_surface(arrow_right,
-                             Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8),
-                             LAYER_GUI);
-
-        /* Draw input background */
-        context.draw_filled_rect(
-          Vector(x_pos - list_pos + text_pos - 1, y_pos - 10),
-          Vector(list_pos_2 + 2, 20),
-          Color(255,255,255,255), LAYER_GUI - 4);
-        context.draw_filled_rect(
-          Vector(x_pos - list_pos + text_pos, y_pos - 9),
-          Vector(list_pos_2, 18),
-          Color(0,0,0,128), LAYER_GUI - 5);
-
-        context.draw_text(text_font, pitem.list[pitem.selected],
-                                 Vector(SCREEN_WIDTH/2 + text_pos, y_pos - int(text_font->get_height()/2)),
-                                 CENTER_ALLIGN, LAYER_GUI);
-        context.draw_text(text_font, pitem.text,
-                                 Vector(SCREEN_WIDTH/2  + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
-                                 CENTER_ALLIGN, LAYER_GUI);
-        break;
-      }
+    {
+      float roff = arrow_left->get_width();
+      // Draw left side
+      context.draw_text(normal_font, pitem.text,
+                        Vector(left, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_LEFT, LAYER_GUI, text_color);
+
+      // Draw right side
+      context.draw_surface(arrow_left.get(),
+                           Vector(right - list_width - roff - roff, y_pos - 8),
+                           LAYER_GUI);
+      context.draw_surface(arrow_right.get(),
+                           Vector(right - roff, y_pos - 8),
+                           LAYER_GUI);
+      context.draw_text(normal_font, pitem.list[pitem.selected],
+                        Vector(right - roff, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_RIGHT, LAYER_GUI, text_color);
+      break;
+    }
     case MN_BACK:
-      {
-        context.draw_text(text_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
-        context.draw_surface(back,
-                             Vector(x_pos + text_width/2  + 16, y_pos - 8),
-                             LAYER_GUI);
-        break;
-      }
+    {
+      context.draw_text(normal_font, pitem.text,
+                        Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI, text_color);
+      context.draw_surface(back.get(),
+                           Vector(x_pos + text_width/2  + 16, y_pos - 8),
+                           LAYER_GUI);
+      break;
+    }
 
     case MN_TOGGLE:
-      {
-        context.draw_text(text_font, pitem.text,
-                          Vector(SCREEN_WIDTH/2, y_pos - (text_font->get_height()/2)),
-                          CENTER_ALLIGN, LAYER_GUI);
-
-        if(pitem.toggled)
-          context.draw_surface(checkbox_checked,
-                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
-                               LAYER_GUI + 1);
-        else
-          context.draw_surface(checkbox,
-                               Vector(x_pos + (text_width+16)/2, y_pos - 8),
-                               LAYER_GUI + 1);
-        break;
-      }
+    {
+      context.draw_text(normal_font, pitem.text,
+                        Vector(pos_x - menu_width/2 + 16, y_pos - (normal_font->get_height()/2)),
+                        ALIGN_LEFT, LAYER_GUI, text_color);
+
+      if(pitem.toggled)
+        context.draw_surface(checkbox_checked.get(),
+                             Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
+                             LAYER_GUI + 1);
+      else
+        context.draw_surface(checkbox.get(),
+                             Vector(x_pos + (menu_width/2-16) - checkbox->get_width(), y_pos - 8),
+                             LAYER_GUI + 1);
+      break;
+    }
     case MN_ACTION:
-      context.draw_text(text_font, pitem.text,
-                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                        CENTER_ALLIGN, LAYER_GUI);
+      context.draw_text(normal_font, pitem.text,
+                        Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI, text_color);
       break;
 
     case MN_GOTO:
-      context.draw_text(text_font, pitem.text,
-                        Vector(SCREEN_WIDTH/2, y_pos - int(text_font->get_height()/2)),
-                        CENTER_ALLIGN, LAYER_GUI);
+      context.draw_text(normal_font, pitem.text,
+                        Vector(pos_x, y_pos - int(normal_font->get_height()/2)),
+                        ALIGN_CENTER, LAYER_GUI, text_color);
       break;
-    }
+  }
 }
 
-int Menu::get_width() const
+float
+Menu::get_width() const
+{
+  /* The width of the menu has to be more than the width of the text
+     with the most characters */
+  float menu_width = 0;
+  for(unsigned int i = 0; i < items.size(); ++i)
   {
-    /* The width of the menu has to be more than the width of the text
-       with the most characters */
-    int menu_width = 0;
-    for(unsigned int i = 0; i < items.size(); ++i)
-      {
-        int w = items[i]->text.size() + items[i]->input.size() + 1;
-        if(w > menu_width)
-          {
-            menu_width = w;
-            if( items[i]->kind == MN_TOGGLE)
-              menu_width += 2;
-          }
-      }
+    Font* font = normal_font;
+    if(items[i]->kind == MN_LABEL)
+      font = big_font;
 
-    return (menu_width * 16 + 24);
-  }
+    float w = font->get_text_width(items[i]->text) +
+      big_font->get_text_width(items[i]->input) + 16;
+    if(items[i]->kind == MN_TOGGLE)
+      w += 32;
 
-int Menu::get_height() const
-  {
-    return items.size() * 24;
+    if(w > menu_width)
+      menu_width = w;
   }
 
+  return menu_width + 24;
+}
+
+float
+Menu::get_height() const
+{
+  return items.size() * 24;
+}
+
 /* Draw the current menu. */
 void
 Menu::draw(DrawingContext& context)
 {
-  int menu_height = get_height();
-  int menu_width = get_width();  
+  if(MouseCursor::current()) {
+    MouseCursor::current()->draw(context);
+  }
+
+  float menu_width  = get_width();
+  float menu_height = get_height();
+
+  if (effect_progress != 1.0f)
+  {
+    if (close)
+    {
+      menu_width  = (current_->get_width()  * (1.0f - effect_progress));
+      menu_height = (current_->get_height() * (1.0f - effect_progress));
+    }
+    else if (Menu::previous)
+    {
+      menu_width  = (menu_width  * effect_progress) + (Menu::previous->get_width()  * (1.0f - effect_progress));
+      menu_height = (menu_height * effect_progress) + (Menu::previous->get_height() * (1.0f - effect_progress));
+      //std::cout << effect_progress << " " << this << " " << last_menus.back() << std::endl;
+    }
+    else
+    {
+      menu_width  *= effect_progress;
+      menu_height *= effect_progress;
+    }
+  }
 
   /* Draw a transparent background */
-  context.draw_filled_rect(
-    Vector(pos_x - menu_width/2, pos_y - 24*items.size()/2 - 10),
-    Vector(menu_width,menu_height + 20),
-    Color(150,180,200,125), LAYER_GUI-10);
+  context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2-4, pos_y - menu_height/2 - 10-4),
+                                Vector(pos_x + menu_width/2+4, pos_y - menu_height/2 + 10 + menu_height+4)),
+                           Color(0.2f, 0.3f, 0.4f, 0.8f), 
+                           20.0f,
+                           LAYER_GUI-10);
+
+  context.draw_filled_rect(Rect(Vector(pos_x - menu_width/2, pos_y - menu_height/2 - 10),
+                                Vector(pos_x + menu_width/2, pos_y - menu_height/2 + 10 + menu_height)),
+                           Color(0.6f, 0.7f, 0.8f, 0.5f), 
+                           16.0f,
+                           LAYER_GUI-10);
+
+  if (!items[active_item]->help.empty())
+  {
+    int text_width  = (int) normal_font->get_text_width(items[active_item]->help);
+    int text_height = (int) normal_font->get_text_height(items[active_item]->help);
+      
+    Rect text_rect(pos_x - text_width/2 - 8, 
+                   SCREEN_HEIGHT - 48 - text_height/2 - 4,
+                   pos_x + text_width/2 + 8, 
+                   SCREEN_HEIGHT - 48 + text_height/2 + 4);
+        
+    context.draw_filled_rect(Rect(text_rect.p1 - Vector(4,4),
+                                  text_rect.p2 + Vector(4,4)),
+                             Color(0.2f, 0.3f, 0.4f, 0.8f), 
+                             16.0f,
+                             LAYER_GUI-10);
+      
+    context.draw_filled_rect(text_rect,
+                             Color(0.6f, 0.7f, 0.8f, 0.5f), 
+                             16.0f,
+                             LAYER_GUI-10);
+
+    context.draw_text(normal_font, items[active_item]->help,
+                      Vector(pos_x, SCREEN_HEIGHT - 48 - text_height/2),
+                      ALIGN_CENTER, LAYER_GUI);
+  }
 
-  for(unsigned int i = 0; i < items.size(); ++i)
+  if (effect_progress == 1.0f)
+    for(unsigned int i = 0; i < items.size(); ++i)
     {
       draw_item(context, i);
     }
@@ -712,7 +879,7 @@ Menu::get_item_by_id(int id)
   for(std::vector<MenuItem*>::iterator i = items.begin();
       i != items.end(); ++i) {
     MenuItem& item = **i;
-    
+
     if(item.id == id)
       return item;
   }
@@ -726,7 +893,7 @@ Menu::get_item_by_id(int id) const
   for(std::vector<MenuItem*>::const_iterator i = items.begin();
       i != items.end(); ++i) {
     const MenuItem& item = **i;
-    
+
     if(item.id == id)
       return item;
   }
@@ -745,61 +912,77 @@ Menu::is_toggled(int id) const
   return get_item_by_id(id).toggled;
 }
 
+void
+Menu::set_toggled(int id, bool toggled)
+{
+  get_item_by_id(id).toggled = toggled;
+}
+
+Menu*
+Menu::get_parent() const
+{
+  if (last_menus.empty())
+    return 0;
+  else
+    return last_menus.back();
+}
+
 /* Check for menu event */
 void
 Menu::event(const SDL_Event& event)
 {
-  if(effect_ticks != 0)
+  if(effect_progress != 1.0f)
     return;
 
   switch(event.type) {
     case SDL_MOUSEBUTTONDOWN:
-      {
-        int x = int(event.motion.x * float(SCREEN_WIDTH)/screen->w);
-        int y = int(event.motion.y * float(SCREEN_HEIGHT)/screen->h);
+    {
+      int x = int(event.motion.x * float(SCREEN_WIDTH)/g_screen->w);
+      int y = int(event.motion.y * float(SCREEN_HEIGHT)/g_screen->h);
 
-        if(x > pos_x - get_width()/2 &&
-            x < pos_x + get_width()/2 &&
-            y > pos_y - get_height()/2 &&
-            y < pos_y + get_height()/2)
-          {
-            menuaction = MENU_ACTION_HIT;
-          }
+      if(x > pos_x - get_width()/2 &&
+         x < pos_x + get_width()/2 &&
+         y > pos_y - get_height()/2 &&
+         y < pos_y + get_height()/2)
+      {
+        menuaction = MENU_ACTION_HIT;
       }
-      break;
+    }
+    break;
 
     case SDL_MOUSEMOTION:
+    {
+      float x = event.motion.x * SCREEN_WIDTH/g_screen->w;
+      float y = event.motion.y * SCREEN_HEIGHT/g_screen->h;
+
+      if(x > pos_x - get_width()/2 &&
+         x < pos_x + get_width()/2 &&
+         y > pos_y - get_height()/2 &&
+         y < pos_y + get_height()/2)
       {
-        int x = int(event.motion.x * float(SCREEN_WIDTH)/screen->w);
-        int y = int(event.motion.y * float(SCREEN_HEIGHT)/screen->h);
+        int new_active_item
+          = static_cast<int> ((y - (pos_y - get_height()/2)) / 24);
 
-        if(x > pos_x - get_width()/2 &&
-            x < pos_x + get_width()/2 &&
-            y > pos_y - get_height()/2 &&
-            y < pos_y + get_height()/2)
-          {
-            int new_active_item = (y - (pos_y - get_height()/2)) / 24;
-          
-            /* only change the mouse focus to a selectable item */
-            if ((items[new_active_item]->kind != MN_HL)
-                && (items[new_active_item]->kind != MN_LABEL)
-                && (items[new_active_item]->kind != MN_DEACTIVE))
-              active_item = new_active_item;
-            
-            if(MouseCursor::current())
-              MouseCursor::current()->set_state(MC_LINK);
-          }
-        else
-        {
-          if(MouseCursor::current())
-            MouseCursor::current()->set_state(MC_NORMAL);
-        }
+        /* only change the mouse focus to a selectable item */
+        if ((items[new_active_item]->kind != MN_HL)
+            && (items[new_active_item]->kind != MN_LABEL)
+            && (items[new_active_item]->kind != MN_INACTIVE))
+          active_item = new_active_item;
+
+        if(MouseCursor::current())
+          MouseCursor::current()->set_state(MC_LINK);
       }
-      break;
+      else
+      {
+        if(MouseCursor::current())
+          MouseCursor::current()->set_state(MC_NORMAL);
+      }
+    }
+    break;
 
     default:
       break;
-    }
+  }
 }
 
 void
@@ -813,3 +996,5 @@ Menu::set_active_item(int id)
     }
   }
 }
+
+/* EOF */