Remove misplaced squirrel_coverity patch
[supertux.git] / src / gui / menu_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmail.com>
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_manager.hpp"
18
19 #include <assert.h>
20
21 #include "control/input_manager.hpp"
22 #include "gui/dialog.hpp"
23 #include "gui/menu.hpp"
24 #include "gui/mousecursor.hpp"
25 #include "math/sizef.hpp"
26 #include "supertux/globals.hpp"
27 #include "supertux/menu/menu_storage.hpp"
28 #include "supertux/timer.hpp"
29 #include "util/gettext.hpp"
30 #include "util/log.hpp"
31 #include "video/drawing_context.hpp"
32
33 MenuManager* MenuManager::s_instance = 0;
34
35 MenuManager&
36 MenuManager::instance()
37 {
38   assert(s_instance);
39   return *s_instance;
40 }
41
42 namespace {
43
44 Rectf menu2rect(const Menu& menu)
45 {
46   return Rectf(menu.get_center_pos().x - menu.get_width() / 2,
47                menu.get_center_pos().y - menu.get_height() / 2,
48                menu.get_center_pos().x + menu.get_width() / 2,
49                menu.get_center_pos().y + menu.get_height() / 2);
50 }
51
52 } // namespace
53
54 class MenuTransition
55 {
56 private:
57   Rectf m_from_rect;
58   Rectf m_to_rect;
59
60   float m_effect_progress;
61   float m_effect_start_time;
62   bool m_is_active;
63
64 public:
65   MenuTransition() :
66     m_from_rect(),
67     m_to_rect(),
68     m_effect_progress(1.0f),
69     m_effect_start_time(),
70     m_is_active(false)
71   {
72   }
73
74   void start(const Rectf& from_rect,
75              const Rectf& to_rect)
76   {
77     m_from_rect = from_rect;
78     m_to_rect = to_rect;
79
80     m_effect_start_time = real_time;
81     m_effect_progress = 0.0f;
82
83     m_is_active = true;
84   }
85
86   void set(const Rectf& rect)
87   {
88     m_to_rect = m_from_rect = rect;
89   }
90
91   void update()
92   {
93     if (m_is_active)
94     {
95       m_effect_progress = (real_time - m_effect_start_time) * 6.0f;
96
97       if (m_effect_progress > 1.0f)
98       {
99         m_effect_progress = 1.0f;
100         m_is_active = false;
101       }
102     }
103   }
104
105   void draw(DrawingContext& context)
106   {
107     float p = m_effect_progress;
108
109     Rectf rect = m_to_rect;
110     if (m_is_active)
111     {
112       rect.p1.x = (m_to_rect.p1.x * p) + (m_from_rect.p1.x * (1.0f - p));
113       rect.p1.y = (m_to_rect.p1.y * p) + (m_from_rect.p1.y * (1.0f - p));
114       rect.p2.x = (m_to_rect.p2.x * p) + (m_from_rect.p2.x * (1.0f - p));
115       rect.p2.y = (m_to_rect.p2.y * p) + (m_from_rect.p2.y * (1.0f - p));
116     }
117
118     // draw menu background rectangles
119     context.draw_filled_rect(Rectf(rect.p1.x - 4, rect.p1.y - 10-4,
120                                    rect.p2.x + 4, rect.p2.y + 10 + 4),
121                              Color(0.2f, 0.3f, 0.4f, 0.8f),
122                              20.0f,
123                              LAYER_GUI-10);
124
125     context.draw_filled_rect(Rectf(rect.p1.x, rect.p1.y - 10,
126                                    rect.p2.x, rect.p2.y + 10),
127                              Color(0.6f, 0.7f, 0.8f, 0.5f),
128                              16.0f,
129                              LAYER_GUI-10);
130   }
131
132   bool is_active()
133   {
134     return m_is_active;
135   }
136 };
137
138 MenuManager::MenuManager() :
139   m_dialog(),
140   m_has_next_dialog(false),
141   m_next_dialog(),
142   m_menu_stack(),
143   m_transition(new MenuTransition)
144 {
145   s_instance = this;
146 }
147
148 MenuManager::~MenuManager()
149 {
150   s_instance = nullptr;
151 }
152
153 void
154 MenuManager::refresh()
155 {
156   for(auto i = m_menu_stack.begin(); i != m_menu_stack.end(); ++i)
157   {
158     (*i)->refresh();
159   }
160 }
161
162 void
163 MenuManager::process_input()
164 {
165   if (m_dialog)
166   {
167     m_dialog->process_input(*InputManager::current()->get_controller());
168   }
169   else if (current_menu())
170   {
171     current_menu()->process_input();
172   }
173 }
174
175 void
176 MenuManager::event(const SDL_Event& ev)
177 {
178   if (!m_transition->is_active())
179   {
180     if (m_dialog)
181     {
182       m_dialog->event(ev);
183     }
184     else if (current_menu())
185     {
186       // only pass events when the menu is fully visible and not in a
187       // transition animation
188       current_menu()->event(ev);
189     }
190   }
191 }
192
193 void
194 MenuManager::draw(DrawingContext& context)
195 {
196   if (m_has_next_dialog)
197   {
198     m_dialog = std::move(m_next_dialog);
199     m_has_next_dialog = false;
200   }
201
202   if (m_transition->is_active())
203   {
204     m_transition->update();
205     m_transition->draw(context);
206   }
207   else
208   {
209     if (m_dialog)
210     {
211       m_dialog->update();
212       m_dialog->draw(context);
213     }
214     else if (current_menu())
215     {
216       // brute force the transition into the right shape in case the
217       // menu has changed sizes
218       m_transition->set(menu2rect(*current_menu()));
219       m_transition->draw(context);
220
221       current_menu()->draw(context);
222     }
223   }
224
225   if (current_menu() && MouseCursor::current())
226   {
227     MouseCursor::current()->draw(context);
228   }
229 }
230
231 void
232 MenuManager::set_dialog(std::unique_ptr<Dialog> dialog)
233 {
234   // delay reseting m_dialog to a later point, as otherwise the Dialog
235   // can't unset itself without ending up with "delete this" problems
236   m_next_dialog = std::move(dialog);
237   m_has_next_dialog = true;
238 }
239
240 void
241 MenuManager::push_menu(int id)
242 {
243   push_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
244 }
245
246 void
247 MenuManager::set_menu(int id)
248 {
249   set_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
250 }
251
252 void
253 MenuManager::push_menu(std::unique_ptr<Menu> menu)
254 {
255   assert(menu);
256   transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
257              menu.get());
258   m_menu_stack.push_back(std::move(menu));
259 }
260
261 void
262 MenuManager::pop_menu()
263 {
264   if (m_menu_stack.empty())
265   {
266     log_warning << "trying to pop on an empty menu_stack" << std::endl;
267   }
268   else
269   {
270     transition(m_menu_stack.back().get(),
271                (m_menu_stack.size() >= 2)
272                ? m_menu_stack[m_menu_stack.size() - 2].get()
273                : nullptr);
274
275     m_menu_stack.pop_back();
276   }
277 }
278
279 void
280 MenuManager::set_menu(std::unique_ptr<Menu> menu)
281 {
282   if (menu)
283   {
284     transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
285                menu.get());
286     m_menu_stack.clear();
287     m_menu_stack.push_back(std::move(menu));
288   }
289   else
290   {
291     transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
292                nullptr);
293     m_menu_stack.clear();
294   }
295
296   // just to be sure...
297   InputManager::current()->reset();
298 }
299
300 void
301 MenuManager::clear_menu_stack()
302 {
303   transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
304              nullptr);
305   m_menu_stack.clear();
306 }
307
308 void
309 MenuManager::on_window_resize()
310 {
311   for(auto i = m_menu_stack.begin(); i != m_menu_stack.end(); ++i)
312   {
313     (*i)->on_window_resize();
314   }
315 }
316
317 Menu*
318 MenuManager::current_menu() const
319 {
320   if (m_menu_stack.empty())
321   {
322     return nullptr;
323   }
324   else
325   {
326     return m_menu_stack.back().get();
327   }
328 }
329
330 void
331 MenuManager::transition(Menu* from, Menu* to)
332 {
333   if (!from && !to)
334   {
335     return;
336   }
337   else
338   {
339     Rectf from_rect;
340     if (from)
341     {
342       from_rect = menu2rect(*from);
343     }
344     else
345     {
346       from_rect = Rectf(to->get_center_pos(), Sizef(0, 0));
347     }
348
349     Rectf to_rect;
350     if (to)
351     {
352       to_rect = menu2rect(*to);
353     }
354     else
355     {
356       to_rect = Rectf(from->get_center_pos(), Sizef(0, 0));
357     }
358
359     m_transition->start(from_rect, to_rect);
360   }
361 }
362
363 /* EOF */