Cleaned up some function names in MenuManager
[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 #include <stdexcept>
21
22 #include "control/input_manager.hpp"
23 #include "gui/menu_item.hpp"
24 #include "gui/menu_manager.hpp"
25 #include "gui/mousecursor.hpp"
26 #include "supertux/globals.hpp"
27 #include "supertux/resources.hpp"
28 #include "supertux/screen_manager.hpp"
29 #include "supertux/timer.hpp"
30 #include "util/gettext.hpp"
31 #include "video/drawing_context.hpp"
32 #include "video/font.hpp"
33 #include "video/renderer.hpp"
34
35 static const float MENU_REPEAT_INITIAL = 0.4f;
36 static const float MENU_REPEAT_RATE    = 0.1f;
37
38 Menu::Menu() :
39   hit_item(),
40   pos(),
41   menuaction(),
42   delete_character(),
43   mn_input_char(),
44   menu_repeat_time(),
45   close(false),
46   items(),
47   effect_progress(),
48   effect_start_time(),
49   arrange_left(),
50   active_item()
51 {
52   MenuManager::instance().m_all_menus.push_back(this);
53
54   hit_item = -1;
55   menuaction = MENU_ACTION_NONE;
56   delete_character = 0;
57   mn_input_char = '\0';
58
59   pos.x        = SCREEN_WIDTH/2;
60   pos.y        = SCREEN_HEIGHT/2;
61   arrange_left = 0;
62   active_item  = -1;
63
64   effect_progress   = 0.0f;
65   effect_start_time = 0.0f;
66 }
67
68 Menu::~Menu()
69 {
70   MenuManager::instance().m_all_menus.remove(this);
71
72   if (MenuManager::instance().m_current == this)
73     MenuManager::instance().m_current = nullptr;
74
75   if (MenuManager::instance().m_previous == this)
76     MenuManager::instance().m_previous = nullptr;
77 }
78
79 void
80 Menu::set_pos(float x, float y, float rw, float rh)
81 {
82   pos.x = x + get_width()  * rw;
83   pos.y = y + get_height() * rh;
84 }
85
86 /* Add an item to a menu */
87 MenuItem*
88 Menu::add_item(std::unique_ptr<MenuItem> new_item)
89 {
90   items.push_back(std::move(new_item));
91   MenuItem* item = items.back().get();
92
93   /* If a new menu is being built, the active item shouldn't be set to
94    * something that isn't selectable. Set the active_item to the first
95    * selectable item added.
96    */
97   if (active_item == -1
98       && item->kind != MN_HL
99       && item->kind != MN_LABEL
100       && item->kind != MN_INACTIVE)
101   {
102     active_item = items.size() - 1;
103   }
104
105   return item;
106 }
107
108 MenuItem*
109 Menu::add_hl()
110 {
111   std::unique_ptr<MenuItem> item(new MenuItem(MN_HL));
112   return add_item(std::move(item));
113 }
114
115 MenuItem*
116 Menu::add_label(const std::string& text)
117 {
118   std::unique_ptr<MenuItem> item(new MenuItem(MN_LABEL));
119   item->text = text;
120   return add_item(std::move(item));
121 }
122
123 MenuItem*
124 Menu::add_controlfield(int id, const std::string& text,
125                        const std::string& mapping)
126 {
127   std::unique_ptr<MenuItem> item(new MenuItem(MN_CONTROLFIELD, id));
128   item->change_text(text);
129   item->change_input(mapping);
130   return add_item(std::move(item));
131 }
132
133 MenuItem*
134 Menu::add_entry(int id, const std::string& text)
135 {
136   std::unique_ptr<MenuItem> item(new MenuItem(MN_ACTION, id));
137   item->text = text;
138   return add_item(std::move(item));
139 }
140
141 MenuItem*
142 Menu::add_inactive(int id, const std::string& text)
143 {
144   std::unique_ptr<MenuItem> item(new MenuItem(MN_INACTIVE, id));
145   item->text = text;
146   return add_item(std::move(item));
147 }
148
149 MenuItem*
150 Menu::add_toggle(int id, const std::string& text, bool toogled)
151 {
152   std::unique_ptr<MenuItem> item(new MenuItem(MN_TOGGLE, id));
153   item->text = text;
154   item->toggled = toogled;
155   return add_item(std::move(item));
156 }
157
158 MenuItem*
159 Menu::add_string_select(int id, const std::string& text)
160 {
161   std::unique_ptr<MenuItem> item(new MenuItem(MN_STRINGSELECT, id));
162   item->text = text;
163   return add_item(std::move(item));
164 }
165
166 MenuItem*
167 Menu::add_back(const std::string& text)
168 {
169   std::unique_ptr<MenuItem> item(new MenuItem(MN_BACK));
170   item->text = text;
171   return add_item(std::move(item));
172 }
173
174 MenuItem*
175 Menu::add_submenu(const std::string& text, Menu* submenu, int id)
176 {
177   std::unique_ptr<MenuItem> item(new MenuItem(MN_GOTO, id));
178   item->text = text;
179   item->target_menu = submenu;
180   return add_item(std::move(item));
181 }
182
183 void
184 Menu::clear()
185 {
186   items.clear();
187   active_item = -1;
188 }
189
190 /* Process actions done on the menu */
191 void
192 Menu::update()
193 {
194   int menu_height = (int) get_height();
195   if (menu_height > SCREEN_HEIGHT)
196   { // Scrolling
197     int scroll_offset = (menu_height - SCREEN_HEIGHT) / 2 + 32;
198     pos.y = SCREEN_HEIGHT/2 - scroll_offset * ((float(active_item) / (items.size()-1)) - 0.5f) * 2.0f;
199   }
200
201   effect_progress = (real_time - effect_start_time) * 6.0f;
202
203   if(effect_progress >= 1.0f) {
204     effect_progress = 1.0f;
205
206     if (close) {
207       MenuManager::instance().m_current = 0;
208       close = false;
209     }
210   }
211   else if (effect_progress <= 0.0f) {
212     effect_progress = 0.0f;
213   }
214
215   Controller* controller = g_input_manager->get_controller();
216   /** check main input controller... */
217   if(controller->pressed(Controller::UP)) {
218     menuaction = MENU_ACTION_UP;
219     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
220   }
221   if(controller->hold(Controller::UP) &&
222      menu_repeat_time != 0 && real_time > menu_repeat_time) {
223     menuaction = MENU_ACTION_UP;
224     menu_repeat_time = real_time + MENU_REPEAT_RATE;
225   }
226
227   if(controller->pressed(Controller::DOWN)) {
228     menuaction = MENU_ACTION_DOWN;
229     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
230   }
231   if(controller->hold(Controller::DOWN) &&
232      menu_repeat_time != 0 && real_time > menu_repeat_time) {
233     menuaction = MENU_ACTION_DOWN;
234     menu_repeat_time = real_time + MENU_REPEAT_RATE;
235   }
236
237   if(controller->pressed(Controller::LEFT)) {
238     menuaction = MENU_ACTION_LEFT;
239     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
240   }
241   if(controller->hold(Controller::LEFT) &&
242      menu_repeat_time != 0 && real_time > menu_repeat_time) {
243     menuaction = MENU_ACTION_LEFT;
244     menu_repeat_time = real_time + MENU_REPEAT_RATE;
245   }
246
247   if(controller->pressed(Controller::RIGHT)) {
248     menuaction = MENU_ACTION_RIGHT;
249     menu_repeat_time = real_time + MENU_REPEAT_INITIAL;
250   }
251   if(controller->hold(Controller::RIGHT) &&
252      menu_repeat_time != 0 && real_time > menu_repeat_time) {
253     menuaction = MENU_ACTION_RIGHT;
254     menu_repeat_time = real_time + MENU_REPEAT_RATE;
255   }
256
257   if(controller->pressed(Controller::ACTION)
258      || controller->pressed(Controller::MENU_SELECT)) {
259     menuaction = MENU_ACTION_HIT;
260   }
261   if(controller->pressed(Controller::PAUSE_MENU)
262     || controller->pressed(Controller::MENU_BACK)) {
263     menuaction = MENU_ACTION_BACK;
264   }
265
266   hit_item = -1;
267   if(items.size() == 0)
268     return;
269
270   int last_active_item = active_item;
271   switch(menuaction) {
272     case MENU_ACTION_UP:
273       do {
274         if (active_item > 0)
275           --active_item;
276         else
277           active_item = int(items.size())-1;
278       } while ((items[active_item]->kind == MN_HL
279                 || items[active_item]->kind == MN_LABEL
280                 || items[active_item]->kind == MN_INACTIVE)
281                && (active_item != last_active_item));
282
283       break;
284
285     case MENU_ACTION_DOWN:
286       do {
287         if(active_item < int(items.size())-1 )
288           ++active_item;
289         else
290           active_item = 0;
291       } while ((items[active_item]->kind == MN_HL
292                 || items[active_item]->kind == MN_LABEL
293                 || items[active_item]->kind == MN_INACTIVE)
294                && (active_item != last_active_item));
295
296       break;
297
298     case MENU_ACTION_LEFT:
299       if(items[active_item]->kind == MN_STRINGSELECT) {
300         if(items[active_item]->selected > 0)
301           items[active_item]->selected--;
302         else
303           items[active_item]->selected = items[active_item]->list.size()-1;
304
305         menu_action(items[active_item].get());
306       }
307       break;
308
309     case MENU_ACTION_RIGHT:
310       if(items[active_item]->kind == MN_STRINGSELECT) {
311         if(items[active_item]->selected+1 < items[active_item]->list.size())
312           items[active_item]->selected++;
313         else
314           items[active_item]->selected = 0;
315
316         menu_action(items[active_item].get());
317       }
318       break;
319
320     case MENU_ACTION_HIT: {
321       hit_item = active_item;
322       switch (items[active_item]->kind) {
323         case MN_GOTO:
324           assert(items[active_item]->target_menu != 0);
325           MenuManager::instance().push_current(items[active_item]->target_menu);
326           break;
327
328         case MN_TOGGLE:
329           items[active_item]->toggled = !items[active_item]->toggled;
330           menu_action(items[active_item].get());
331           break;
332
333         case MN_CONTROLFIELD:
334           menu_action(items[active_item].get());
335           break;
336
337         case MN_ACTION:
338           menu_action(items[active_item].get());
339           break;
340
341         case MN_STRINGSELECT:
342           if(items[active_item]->selected+1 < items[active_item]->list.size())
343             items[active_item]->selected++;
344           else
345             items[active_item]->selected = 0;
346
347           menu_action(items[active_item].get());
348           break;
349
350         case MN_TEXTFIELD:
351         case MN_NUMFIELD:
352           menuaction = MENU_ACTION_DOWN;
353           update();
354           break;
355
356         case MN_BACK:
357           MenuManager::instance().pop_current();
358           break;
359         default:
360           break;
361       }
362       break;
363     }
364
365     case MENU_ACTION_REMOVE:
366       if(items[active_item]->kind == MN_TEXTFIELD
367          || items[active_item]->kind == MN_NUMFIELD)
368       {
369         if(!items[active_item]->input.empty())
370         {
371           int i = items[active_item]->input.size();
372
373           while(delete_character > 0)        /* remove characters */
374           {
375             items[active_item]->input.resize(i-1);
376             delete_character--;
377           }
378         }
379       }
380       break;
381
382     case MENU_ACTION_INPUT:
383       if(items[active_item]->kind == MN_TEXTFIELD
384          || (items[active_item]->kind == MN_NUMFIELD
385              && mn_input_char >= '0' && mn_input_char <= '9'))
386       {
387         items[active_item]->input.push_back(mn_input_char);
388       }
389       break;
390
391     case MENU_ACTION_BACK:
392       MenuManager::instance().pop_current();
393       break;
394
395     case MENU_ACTION_NONE:
396       break;
397   }
398   menuaction = MENU_ACTION_NONE;
399
400   assert(active_item < int(items.size()));
401 }
402
403 int
404 Menu::check()
405 {
406   if (hit_item != -1)
407   {
408     int id = items[hit_item]->id;
409     // Clear event when checked out.. (we would end up in a loop when we try to leave "fake" submenu like Addons or Contrib)
410     hit_item = -1;
411     return id;
412   }
413   else
414     return -1;
415 }
416
417 void
418 Menu::menu_action(MenuItem* )
419 {}
420
421 void
422 Menu::draw_item(DrawingContext& context, int index)
423 {
424   float menu_height = get_height();
425   float menu_width  = get_width();
426
427   MenuItem& pitem = *(items[index]);
428
429   Color text_color = default_color;
430   float x_pos       = pos.x;
431   float y_pos       = pos.y + 24*index - menu_height/2 + 12;
432   int text_width  = int(Resources::normal_font->get_text_width(pitem.text));
433   int input_width = int(Resources::normal_font->get_text_width(pitem.input) + 10);
434   int list_width = 0;
435
436   float left  = pos.x - menu_width/2 + 16;
437   float right = pos.x + menu_width/2 - 16;
438
439   if(pitem.list.size() > 0) {
440     list_width = (int) Resources::normal_font->get_text_width(pitem.list[pitem.selected]);
441   }
442
443   if (arrange_left)
444     x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
445
446   if(index == active_item)
447   {
448     text_color = active_color;
449   }
450
451   if(active_item == index)
452   {
453     float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
454     context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2 + 10 - 2, y_pos - 12 - 2),
455                                    Vector(pos.x + menu_width/2 - 10 + 2, y_pos + 12 + 2)),
456                              Color(1.0f, 1.0f, 1.0f, blink),
457                              14.0f,
458                              LAYER_GUI-10);
459     context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2 + 10, y_pos - 12),
460                                    Vector(pos.x + menu_width/2 - 10, y_pos + 12)),
461                              Color(1.0f, 1.0f, 1.0f, 0.5f),
462                              12.0f,
463                              LAYER_GUI-10);
464   }
465
466   switch (pitem.kind)
467   {
468     case MN_INACTIVE:
469     {
470       context.draw_text(Resources::normal_font, pitem.text,
471                         Vector(pos.x, y_pos - int(Resources::normal_font->get_height()/2)),
472                         ALIGN_CENTER, LAYER_GUI, inactive_color);
473       break;
474     }
475
476     case MN_HL:
477     {
478       // TODO
479       float x = pos.x - menu_width/2;
480       float y = y_pos - 12;
481       /* Draw a horizontal line with a little 3d effect */
482       context.draw_filled_rect(Vector(x, y + 6),
483                                Vector(menu_width, 4),
484                                Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
485       context.draw_filled_rect(Vector(x, y + 6),
486                                Vector(menu_width, 2),
487                                Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
488       break;
489     }
490     case MN_LABEL:
491     {
492       context.draw_text(Resources::big_font, pitem.text,
493                         Vector(pos.x, y_pos - int(Resources::big_font->get_height()/2)),
494                         ALIGN_CENTER, LAYER_GUI, label_color);
495       break;
496     }
497     case MN_TEXTFIELD:
498     case MN_NUMFIELD:
499     case MN_CONTROLFIELD:
500     {
501       if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
502       {
503         if(active_item == index)
504           context.draw_text(Resources::normal_font,
505                             pitem.get_input_with_symbol(true),
506                             Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
507                             ALIGN_RIGHT, LAYER_GUI, field_color);
508         else
509           context.draw_text(Resources::normal_font,
510                             pitem.get_input_with_symbol(false),
511                             Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
512                             ALIGN_RIGHT, LAYER_GUI, field_color);
513       }
514       else
515         context.draw_text(Resources::normal_font, pitem.input,
516                           Vector(right, y_pos - int(Resources::normal_font->get_height()/2)),
517                           ALIGN_RIGHT, LAYER_GUI, field_color);
518
519       context.draw_text(Resources::normal_font, pitem.text,
520                         Vector(left, y_pos - int(Resources::normal_font->get_height()/2)),
521                         ALIGN_LEFT, LAYER_GUI, text_color);
522       break;
523     }
524     case MN_STRINGSELECT:
525     {
526       float roff = Resources::arrow_left->get_width();
527       // Draw left side
528       context.draw_text(Resources::normal_font, pitem.text,
529                         Vector(left, y_pos - int(Resources::normal_font->get_height()/2)),
530                         ALIGN_LEFT, LAYER_GUI, text_color);
531
532       // Draw right side
533       context.draw_surface(Resources::arrow_left,
534                            Vector(right - list_width - roff - roff, y_pos - 8),
535                            LAYER_GUI);
536       context.draw_surface(Resources::arrow_right,
537                            Vector(right - roff, y_pos - 8),
538                            LAYER_GUI);
539       context.draw_text(Resources::normal_font, pitem.list[pitem.selected],
540                         Vector(right - roff, y_pos - int(Resources::normal_font->get_height()/2)),
541                         ALIGN_RIGHT, LAYER_GUI, text_color);
542       break;
543     }
544     case MN_BACK:
545     {
546       context.draw_text(Resources::Resources::normal_font, pitem.text,
547                         Vector(pos.x, y_pos - int(Resources::normal_font->get_height()/2)),
548                         ALIGN_CENTER, LAYER_GUI, text_color);
549       context.draw_surface(Resources::back,
550                            Vector(x_pos + text_width/2  + 16, y_pos - 8),
551                            LAYER_GUI);
552       break;
553     }
554
555     case MN_TOGGLE:
556     {
557       context.draw_text(Resources::normal_font, pitem.text,
558                         Vector(pos.x - menu_width/2 + 16, y_pos - (Resources::normal_font->get_height()/2)),
559                         ALIGN_LEFT, LAYER_GUI, text_color);
560
561       if(pitem.toggled)
562         context.draw_surface(Resources::checkbox_checked,
563                              Vector(x_pos + (menu_width/2-16) - Resources::checkbox->get_width(), y_pos - 8),
564                              LAYER_GUI + 1);
565       else
566         context.draw_surface(Resources::checkbox,
567                              Vector(x_pos + (menu_width/2-16) - Resources::checkbox->get_width(), y_pos - 8),
568                              LAYER_GUI + 1);
569       break;
570     }
571     case MN_ACTION:
572       context.draw_text(Resources::normal_font, pitem.text,
573                         Vector(pos.x, y_pos - int(Resources::normal_font->get_height()/2)),
574                         ALIGN_CENTER, LAYER_GUI, text_color);
575       break;
576
577     case MN_GOTO:
578       context.draw_text(Resources::normal_font, pitem.text,
579                         Vector(pos.x, y_pos - int(Resources::normal_font->get_height()/2)),
580                         ALIGN_CENTER, LAYER_GUI, text_color);
581       break;
582   }
583 }
584
585 float
586 Menu::get_width() const
587 {
588   /* The width of the menu has to be more than the width of the text
589      with the most characters */
590   float menu_width = 0;
591   for(unsigned int i = 0; i < items.size(); ++i)
592   {
593     FontPtr font = Resources::Resources::normal_font;
594     if(items[i]->kind == MN_LABEL)
595       font = Resources::big_font;
596
597     float w = font->get_text_width(items[i]->text) +
598       Resources::big_font->get_text_width(items[i]->input) + 16;
599     if(items[i]->kind == MN_TOGGLE)
600       w += 32;
601     if (items[i]->kind == MN_STRINGSELECT)
602       w += font->get_text_width(items[i]->list[items[i]->selected]) + 32;
603
604
605     if(w > menu_width)
606       menu_width = w;
607   }
608
609   return menu_width + 24;
610 }
611
612 float
613 Menu::get_height() const
614 {
615   return items.size() * 24;
616 }
617
618 /* Draw the current menu. */
619 void
620 Menu::draw(DrawingContext& context)
621 {
622   if(MouseCursor::current()) {
623     MouseCursor::current()->draw(context);
624   }
625
626   float menu_width  = get_width();
627   float menu_height = get_height();
628
629   if (effect_progress != 1.0f)
630   {
631     if (close)
632     {
633       menu_width *= 1.0f - effect_progress;
634       menu_height *= 1.0f - effect_progress;
635     }
636     else if (MenuManager::instance().m_previous)
637     {
638       menu_width  = (menu_width  * effect_progress) + (MenuManager::instance().m_previous->get_width()  * (1.0f - effect_progress));
639       menu_height = (menu_height * effect_progress) + (MenuManager::instance().m_previous->get_height() * (1.0f - effect_progress));
640       //std::cout << effect_progress << " " << this << " " << last_menus.back() << std::endl;
641     }
642     else
643     {
644       menu_width  *= effect_progress;
645       menu_height *= effect_progress;
646     }
647   }
648
649   /* Draw a transparent background */
650   context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2-4, pos.y - menu_height/2 - 10-4),
651                                  Vector(pos.x + menu_width/2+4, pos.y - menu_height/2 + 10 + menu_height+4)),
652                            Color(0.2f, 0.3f, 0.4f, 0.8f),
653                            20.0f,
654                            LAYER_GUI-10);
655
656   context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2, pos.y - menu_height/2 - 10),
657                                  Vector(pos.x + menu_width/2, pos.y - menu_height/2 + 10 + menu_height)),
658                            Color(0.6f, 0.7f, 0.8f, 0.5f),
659                            16.0f,
660                            LAYER_GUI-10);
661
662   if (!items[active_item]->help.empty())
663   {
664     int text_width  = (int) Resources::normal_font->get_text_width(items[active_item]->help);
665     int text_height = (int) Resources::normal_font->get_text_height(items[active_item]->help);
666
667     Rectf text_rect(pos.x - text_width/2 - 8,
668                    SCREEN_HEIGHT - 48 - text_height/2 - 4,
669                    pos.x + text_width/2 + 8,
670                    SCREEN_HEIGHT - 48 + text_height/2 + 4);
671
672     context.draw_filled_rect(Rectf(text_rect.p1 - Vector(4,4),
673                                    text_rect.p2 + Vector(4,4)),
674                              Color(0.2f, 0.3f, 0.4f, 0.8f),
675                              16.0f,
676                              LAYER_GUI-10);
677
678     context.draw_filled_rect(text_rect,
679                              Color(0.6f, 0.7f, 0.8f, 0.5f),
680                              16.0f,
681                              LAYER_GUI-10);
682
683     context.draw_text(Resources::normal_font, items[active_item]->help,
684                       Vector(pos.x, SCREEN_HEIGHT - 48 - text_height/2),
685                       ALIGN_CENTER, LAYER_GUI);
686   }
687
688   if (effect_progress == 1.0f)
689     for(unsigned int i = 0; i < items.size(); ++i)
690     {
691       draw_item(context, i);
692     }
693 }
694
695 MenuItem&
696 Menu::get_item_by_id(int id)
697 {
698   for (const auto& item : items)
699   {
700     if (item->id == id)
701     {
702       return *item;
703     }
704   }
705
706   throw std::runtime_error("MenuItem not found");
707 }
708
709 const MenuItem&
710 Menu::get_item_by_id(int id) const
711 {
712   for (const auto& item : items)
713   {
714     if (item->id == id)
715     {
716       return *item;
717     }
718   }
719
720   throw std::runtime_error("MenuItem not found");
721 }
722
723 int Menu::get_active_item_id()
724 {
725   return items[active_item]->id;
726 }
727
728 bool
729 Menu::is_toggled(int id) const
730 {
731   return get_item_by_id(id).toggled;
732 }
733
734 void
735 Menu::set_toggled(int id, bool toggled)
736 {
737   get_item_by_id(id).toggled = toggled;
738 }
739
740 /* Check for menu event */
741 void
742 Menu::event(const SDL_Event& event)
743 {
744   if(effect_progress != 1.0f)
745     return;
746
747   switch(event.type) {
748     case SDL_MOUSEBUTTONDOWN:
749     if(event.button.button == SDL_BUTTON_LEFT)
750     {
751       Vector mouse_pos = Renderer::instance()->to_logical(event.motion.x, event.motion.y);
752       int x = int(mouse_pos.x);
753       int y = int(mouse_pos.y);
754
755       if(x > pos.x - get_width()/2 &&
756          x < pos.x + get_width()/2 &&
757          y > pos.y - get_height()/2 &&
758          y < pos.y + get_height()/2)
759       {
760         menuaction = MENU_ACTION_HIT;
761       }
762     }
763     break;
764
765     case SDL_MOUSEMOTION:
766     {
767       Vector mouse_pos = Renderer::instance()->to_logical(event.motion.x, event.motion.y);
768       float x = mouse_pos.x;
769       float y = mouse_pos.y;
770
771       if(x > pos.x - get_width()/2 &&
772          x < pos.x + get_width()/2 &&
773          y > pos.y - get_height()/2 &&
774          y < pos.y + get_height()/2)
775       {
776         int new_active_item
777           = static_cast<int> ((y - (pos.y - get_height()/2)) / 24);
778
779         /* only change the mouse focus to a selectable item */
780         if ((items[new_active_item]->kind != MN_HL)
781             && (items[new_active_item]->kind != MN_LABEL)
782             && (items[new_active_item]->kind != MN_INACTIVE))
783           active_item = new_active_item;
784
785         if(MouseCursor::current())
786           MouseCursor::current()->set_state(MC_LINK);
787       }
788       else
789       {
790         if(MouseCursor::current())
791           MouseCursor::current()->set_state(MC_NORMAL);
792       }
793     }
794     break;
795
796     default:
797       break;
798   }
799 }
800
801 void
802 Menu::set_active_item(int id)
803 {
804   for(size_t i = 0; i < items.size(); ++i) {
805     if(items[i]->id == id) {
806       active_item = i;
807       break;
808     }
809   }
810 }
811
812 /* EOF */