Implemented mirror actions correctly. Bugfix: right direction of bad guys now working.
[supertux.git] / lib / gui / menu.cpp
index 941aa56..8842b77 100644 (file)
@@ -152,15 +152,23 @@ Menu::set_current(Menu* menu)
   current_ = menu;
 }
 
+MenuItem::MenuItem(MenuItemKind _kind, int _id) : kind(_kind) , id(_id)
+{
+  list.second = 0;
+}
+
+MenuItem::MenuItem(MenuItemKind _kind, int _id, const std::string& _text) : kind(_kind) , id(_id) , text(_text)
+{
+  list.second = 0;
+}
+
 /* Return a pointer to a new menu item */
 MenuItem*
-MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu* target_menu_, int id, int* int_p_)
+MenuItem::create(MenuItemKind kind_, const std::string& text_, int init_toggle_, Menu* target_menu_, int id_, int* int_p_)
 {
-  MenuItem *pnew_item = new MenuItem;
+  MenuItem *pnew_item = new MenuItem(kind_,id_);
 
-  pnew_item->kind = kind_;
-  pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text_) + 1));
-  strcpy(pnew_item->text, text_);
+  pnew_item->text = text_;
 
   if(kind_ == MN_TOGGLE)
     pnew_item->toggled = init_toggle_;
@@ -168,19 +176,10 @@ MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu*
     pnew_item->toggled = false;
 
   pnew_item->target_menu = target_menu_;
-  pnew_item->input = (char*) malloc(sizeof(char));
-  pnew_item->input[0] = '\0';
-
-  if(kind_ == MN_STRINGSELECT)
-    {
-      pnew_item->list = (string_list_type*) malloc(sizeof(string_list_type));
-      string_list_init(pnew_item->list);
-    }
-  else
-    pnew_item->list = NULL;
 
-  pnew_item->id = id;
   pnew_item->int_p = int_p_;
+  
+  pnew_item->list.second = 0;
 
   pnew_item->input_flickering = false;
   pnew_item->input_flickering_timer.init(true);
@@ -190,25 +189,15 @@ MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu*
 }
 
 void
-MenuItem::change_text(const  char *text_)
+MenuItem::change_text(const  std::string& text_)
 {
-  if (text_)
-    {
-      free(text);
-      text = (char*) malloc(sizeof(char )*(strlen(text_)+1));
-      strcpy(text, text_);
-    }
+  text = text_;
 }
 
 void
-MenuItem::change_input(const  char *text_)
+MenuItem::change_input(const  std::string& text_)
 {
-  if(text)
-    {
-      free(input);
-      input = (char*) malloc(sizeof(char )*(strlen(text_)+1));
-      strcpy(input, text_);
-    }
+  input = text_;
 }
 
 std::string MenuItem::get_input_with_symbol(bool active_item)
@@ -229,9 +218,9 @@ std::string MenuItem::get_input_with_symbol(bool active_item)
 
   char str[1024];
   if(input_flickering)
-    sprintf(str,"%s ",input);
+    sprintf(str,"%s ",input.c_str());
   else
-    sprintf(str,"%s_",input);
+    sprintf(str,"%s_",input.c_str());
 
   std::string string = str;
 
@@ -304,9 +293,7 @@ Menu::~Menu()
     {
       for(unsigned int i = 0; i < item.size(); ++i)
         {
-          free(item[i].text);
-          free(item[i].input);
-          string_list_free(item[i].list);
+          item[i].list.first.clear();
         }
     }
 }
@@ -338,6 +325,15 @@ void
 Menu::additem(MenuItemKind kind_, const std::string& text_, int toggle_, Menu* menu_, int id, int* int_p)
 {
   additem(MenuItem::create(kind_, text_.c_str(), toggle_, menu_, id, int_p));
+  
+    /* If a new menu is being built, the active item shouldn't be set to something
+       that isnt selectable.  Keep setting the active item to the most recently
+       added item until a selectable entry is found.
+    */
+    if (item[active_item].kind == MN_HL 
+          || item[active_item].kind == MN_LABEL
+          || item[active_item].kind == MN_DEACTIVE)
+          active_item = item.size() - 1;
 }
 
 /* Add an item to a menu */
@@ -348,6 +344,13 @@ Menu::additem(MenuItem* pmenu_item)
   delete pmenu_item;
 }
 
+/* Add an item to a menu */
+void
+Menu::additem(const MenuItem& pmenu_item)
+{
+  item.push_back(pmenu_item);
+}
+
 void
 Menu::clear()
 {
@@ -361,41 +364,54 @@ Menu::action()
   hit_item = -1;
   if(item.size() != 0)
     {
+      int last_active_item = active_item;
       switch(menuaction)
         {
         case MENU_ACTION_UP:
-          if (active_item > 0)
-            --active_item;
-          else
-            active_item = int(item.size())-1;
+          do {
+                 if (active_item > 0)
+                   --active_item;
+                 else
+                   active_item = int(item.size())-1;
+              } while ((item[active_item].kind == MN_HL 
+                       || item[active_item].kind == MN_LABEL
+                       || item[active_item].kind == MN_DEACTIVE)
+                       && (active_item != last_active_item));
+              
           break;
 
         case MENU_ACTION_DOWN:
-          if(active_item < int(item.size())-1)
-            ++active_item;
-          else
-            active_item = 0;
+          do {
+                 if(active_item < int(item.size())-1 )
+                   ++active_item;
+                 else
+                   active_item = 0;
+              } while ((item[active_item].kind == MN_HL
+                       || item[active_item].kind == MN_LABEL
+                       || item[active_item].kind == MN_DEACTIVE)
+                       && (active_item != last_active_item));
+
           break;
 
         case MENU_ACTION_LEFT:
           if(item[active_item].kind == MN_STRINGSELECT
-              && item[active_item].list->num_items != 0)
+              && item[active_item].list.first.size() != 0)
             {
-              if(item[active_item].list->active_item > 0)
-                --item[active_item].list->active_item;
+              if(item[active_item].list.second != item[active_item].list.first.begin())
+                --item[active_item].list.second;
               else
-                item[active_item].list->active_item = item[active_item].list->num_items-1;
+                item[active_item].list.second = item[active_item].list.first.end();
             }
           break;
 
         case MENU_ACTION_RIGHT:
           if(item[active_item].kind == MN_STRINGSELECT
-              && item[active_item].list->num_items != 0)
+              && item[active_item].list.first.size() != 0)
             {
-              if(item[active_item].list->active_item < item[active_item].list->num_items-1)
-                ++item[active_item].list->active_item;
+              if(item[active_item].list.second != item[active_item].list.first.end())
+                ++item[active_item].list.second;
               else
-                item[active_item].list->active_item = 0;
+                item[active_item].list.second = item[active_item].list.first.begin();
             }
           break;
 
@@ -438,13 +454,13 @@ Menu::action()
           if(item[active_item].kind == MN_TEXTFIELD
               || item[active_item].kind == MN_NUMFIELD)
             {
-              if(item[active_item].input != NULL)
+              if(!item[active_item].input.empty())
                 {
-                  int i = strlen(item[active_item].input);
+                  int i = item[active_item].input.size();
 
                   while(delete_character > 0)  /* remove charactes */
                     {
-                      item[active_item].input[i-1] = '\0';
+                      item[active_item].input.resize(i-1);
                       delete_character--;
                     }
                 }
@@ -455,19 +471,7 @@ Menu::action()
           if(item[active_item].kind == MN_TEXTFIELD
               || (item[active_item].kind == MN_NUMFIELD && mn_input_char >= '0' && mn_input_char <= '9'))
             {
-              if(item[active_item].input != NULL)
-                {
-                  int i = strlen(item[active_item].input);
-                  item[active_item].input = (char*) realloc(item[active_item].input,sizeof(char)*(i + 2));
-                  item[active_item].input[i] = mn_input_char;
-                  item[active_item].input[i+1] = '\0';
-                }
-              else
-                {
-                  item[active_item].input = (char*) malloc(2*sizeof(char));
-                  item[active_item].input[0] = mn_input_char;
-                  item[active_item].input[1] = '\0';
-                }
+              item[active_item].input.push_back(mn_input_char);
             }
 
         case MENU_ACTION_NONE:
@@ -475,18 +479,6 @@ Menu::action()
         }
     }
 
-  MenuItem& new_item = item[active_item];
-  if(new_item.kind == MN_DEACTIVE
-      || new_item.kind == MN_LABEL
-      || new_item.kind == MN_HL)
-    {
-      // Skip the horzontal line item
-      if (menuaction != MENU_ACTION_UP && menuaction != MENU_ACTION_DOWN)
-        menuaction = MENU_ACTION_DOWN;
-
-      if (item.size() > 1)
-        action();
-    }
 
   menuaction = MENU_ACTION_NONE;
 
@@ -526,9 +518,11 @@ Menu::draw_item(DrawingContext& context,
   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 list_width  =
-    int(text_font->get_text_width(string_list_active(pitem.list)));
-
+  int list_width = 0;
+  std::set<std::string>::iterator tmp = 0;
+  if(pitem.list.second != tmp)
+  list_width = int(text_font->get_text_width((*pitem.list.second)));
+  
   if (arrange_left)
     x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
 
@@ -542,9 +536,9 @@ Menu::draw_item(DrawingContext& context,
     {
     case MN_DEACTIVE:
       {
-        context.draw_text_center(deactive_font, pitem.text,
-                                 Vector(0, y_pos - int(deactive_font->get_height()/2)),
-                                 LAYER_GUI);
+        context.draw_text(deactive_font, pitem.text,
+                          Vector(screen->w/2, y_pos - int(deactive_font->get_height()/2)),
+                          CENTER_ALLIGN, LAYER_GUI);
         break;
       }
 
@@ -562,9 +556,9 @@ Menu::draw_item(DrawingContext& context,
       }
     case MN_LABEL:
       {
-        context.draw_text_center(label_font,
-                                 pitem.text, Vector(0, y_pos - int(label_font->get_height()/2)),
-                                 LAYER_GUI);
+        context.draw_text(label_font, pitem.text,
+                          Vector(screen->w/2, y_pos - int(label_font->get_height()/2)),
+                          CENTER_ALLIGN, LAYER_GUI);
         break;
       }
     case MN_TEXTFIELD:
@@ -596,21 +590,21 @@ Menu::draw_item(DrawingContext& context,
               context.draw_text(field_font,
                                 pitem.get_input_with_symbol(true),
                                 Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                                LAYER_GUI);
+                                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)),
-                                LAYER_GUI);
+                                LEFT_ALLIGN, LAYER_GUI);
           }
         else
           context.draw_text(field_font, pitem.input,
                             Vector(input_pos, y_pos - int(field_font->get_height()/2)),
-                            LAYER_GUI);
+                            LEFT_ALLIGN, LAYER_GUI);
 
         context.draw_text(text_font, pitem.text,
                           Vector(text_pos, y_pos - int(text_font->get_height()/2)),
-                          LAYER_GUI);
+                          LEFT_ALLIGN, LAYER_GUI);
         break;
       }
     case MN_STRINGSELECT:
@@ -637,19 +631,19 @@ Menu::draw_item(DrawingContext& context,
           Vector(list_pos_2, 18),
           Color(0,0,0,128), LAYER_GUI - 5);
 
-        context.draw_text_center(text_font, string_list_active(pitem.list),
-                                 Vector(text_pos, y_pos - int(text_font->get_height()/2)),
-                                 LAYER_GUI);
-        context.draw_text_center(text_font, pitem.text,
-                                 Vector(list_pos_2/2, y_pos - int(text_font->get_height()/2)),
-                                 LAYER_GUI);
+        context.draw_text(text_font, (*pitem.list.second),
+                                 Vector(screen->w/2 + text_pos, y_pos - int(text_font->get_height()/2)),
+                                 CENTER_ALLIGN, LAYER_GUI);
+        context.draw_text(text_font, pitem.text,
+                                 Vector(screen->w/2  + list_pos_2/2, y_pos - int(text_font->get_height()/2)),
+                                 CENTER_ALLIGN, LAYER_GUI);
         break;
       }
     case MN_BACK:
       {
-        context.draw_text_center(text_font, pitem.text,
-                                 Vector(0, y_pos - int(text_font->get_height()/2)),
-                                 LAYER_GUI);
+        context.draw_text(text_font, pitem.text,
+                          Vector(screen->w/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);
@@ -658,9 +652,9 @@ Menu::draw_item(DrawingContext& context,
 
     case MN_TOGGLE:
       {
-        context.draw_text_center(text_font, pitem.text,
-                                 Vector(0, y_pos - (text_font->get_height()/2)),
-                                 LAYER_GUI);
+        context.draw_text(text_font, pitem.text,
+                          Vector(screen->w/2, y_pos - (text_font->get_height()/2)),
+                          CENTER_ALLIGN, LAYER_GUI);
 
         if(pitem.toggled)
           context.draw_surface(checkbox_checked,
@@ -673,15 +667,15 @@ Menu::draw_item(DrawingContext& context,
         break;
       }
     case MN_ACTION:
-      context.draw_text_center(text_font, pitem.text,
-                               Vector(0, y_pos - int(text_font->get_height()/2)),
-                               LAYER_GUI);
+      context.draw_text(text_font, pitem.text,
+                        Vector(screen->w/2, y_pos - int(text_font->get_height()/2)),
+                        CENTER_ALLIGN, LAYER_GUI);
       break;
 
     case MN_GOTO:
-      context.draw_text_center(text_font, pitem.text,
-                               Vector(0, y_pos - int(text_font->get_height()/2)),
-                               LAYER_GUI);
+      context.draw_text(text_font, pitem.text,
+                        Vector(screen->w/2, y_pos - int(text_font->get_height()/2)),
+                        CENTER_ALLIGN, LAYER_GUI);
       break;
     }
 }
@@ -693,7 +687,7 @@ int Menu::get_width() const
     int menu_width = 0;
     for(unsigned int i = 0; i < item.size(); ++i)
       {
-        int w = strlen(item[i].text) + (item[i].input ? strlen(item[i].input) + 1 : 0) + strlen(string_list_active(item[i].list));
+        int w = item[i].text.size() + item[i].input.size() + 1; //+ ((item[i].list.second != item[i].list.first.end()) ? (strlen((*(item[i].list.second)).c_str())) : 0);
         if( w > menu_width )
           {
             menu_width = w;
@@ -714,8 +708,9 @@ int Menu::get_height() const
 void
 Menu::draw(DrawingContext& context)
 {
+
   int menu_height = get_height();
-  int menu_width  = get_width();
+  int menu_width = get_width();  
 
   /* Draw a transparent background */
   context.draw_filled_rect(
@@ -779,7 +774,7 @@ Menu::event(SDL_Event& event)
             /* An International Character. */
           }
 
-        if(item[active_item].kind == MN_CONTROLFIELD_KB)
+        if(item.size() > 0 && item[active_item].kind == MN_CONTROLFIELD_KB)
           {
             if(key == SDLK_ESCAPE)
               {
@@ -807,7 +802,7 @@ Menu::event(SDL_Event& event)
             menuaction = MENU_ACTION_RIGHT;
             break;
           case SDLK_SPACE:
-            if(item[active_item].kind == MN_TEXTFIELD)
+            if(item.size() > 0 && item[active_item].kind == MN_TEXTFIELD)
               {
                 menuaction = MENU_ACTION_INPUT;
                 mn_input_char = ' ';
@@ -856,8 +851,15 @@ Menu::event(SDL_Event& event)
             joystick_timer.stop();
         }
       break;
+    case SDL_JOYHATMOTION:
+      if(event.jhat.value & SDL_HAT_UP) {
+          menuaction = MENU_ACTION_UP;
+      } else if(event.jhat.value & SDL_HAT_DOWN) {
+          menuaction = MENU_ACTION_DOWN;
+      }
+      break;
     case  SDL_JOYBUTTONDOWN:
-      if (item[active_item].kind == MN_CONTROLFIELD_JS)
+      if (item.size() > 0 && item[active_item].kind == MN_CONTROLFIELD_JS)
         {
           // FIXME: This next line does nothing useable, right?
           // *item[active_item].int_p = key;
@@ -891,12 +893,21 @@ Menu::event(SDL_Event& event)
             y > pos_y - get_height()/2 &&
             y < pos_y + get_height()/2)
           {
-            active_item = (y - (pos_y - get_height()/2)) / 24;
-            mouse_cursor->set_state(MC_LINK);
+            int new_active_item = (y - (pos_y - get_height()/2)) / 24;
+          
+            /* only change the mouse focus to a selectable item */
+               if ((item[new_active_item].kind != MN_HL)
+                     && (item[new_active_item].kind != MN_LABEL)
+                     && (item[new_active_item].kind != MN_DEACTIVE))                
+              active_item = new_active_item;
+            
+                if(MouseCursor::current())
+              MouseCursor::current()->set_state(MC_LINK);
           }
         else
           {
-            mouse_cursor->set_state(MC_NORMAL);
+                if(MouseCursor::current())
+              MouseCursor::current()->set_state(MC_NORMAL);
           }
       }
       break;