1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2009-2015 Marianne Gagnon
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (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, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #include "guiengine/abstract_state_manager.hpp"
20
21 #include "config/user_config.hpp"
22 #include "guiengine/engine.hpp"
23 #include "guiengine/modaldialog.hpp"
24 #include "guiengine/screen_keyboard.hpp"
25 #include "guiengine/screen.hpp"
26 #include "input/device_manager.hpp"
27 #include "utils/debug.hpp"
28
29 #include <vector>
30 #include <iostream>
31 #include <IGUIEnvironment.h>
32
33 using namespace GUIEngine;
34
35
36 static const char RACE_STATE_NAME[] = "race";
37
AbstractStateManager()38 AbstractStateManager::AbstractStateManager()
39 {
40 m_game_mode = MENU;
41 } // AbstractStateManager
42
43 #if 0
44 #pragma mark -
45 #pragma mark Game State Management
46 #endif
47
48 // ----------------------------------------------------------------------------
49
enterGameState()50 void AbstractStateManager::enterGameState()
51 {
52 if (GUIEngine::isNoGraphics())
53 {
54 // No graphics STK won't push dialog
55 setGameState(GAME);
56 return;
57 }
58
59 // you need to close any dialog before calling this
60 assert(!ModalDialog::isADialogActive());
61 assert(!ScreenKeyboard::isActive());
62
63 if (getCurrentScreen() != NULL) getCurrentScreen()->tearDown();
64 m_menu_stack.clear();
65 m_menu_stack.emplace_back(RACE_STATE_NAME, (Screen*)NULL);
66
67 Debug::closeDebugMenu();
68 setGameState(GAME);
69 GUIEngine::cleanForGame();
70 } // enterGameState
71
72 // ----------------------------------------------------------------------------
73
getGameState()74 GameState AbstractStateManager::getGameState()
75 {
76 return m_game_mode;
77 } // getGameState
78
79 // ----------------------------------------------------------------------------
80
setGameState(GameState state)81 void AbstractStateManager::setGameState(GameState state)
82 {
83 if (m_game_mode == state) return; // no change
84
85 m_game_mode = state;
86
87 onGameStateChange(state);
88 } // setGameState
89
90
91 // ----------------------------------------------------------------------------
92
93 #if 0
94 #pragma mark -
95 #pragma mark Push/pop menus
96 #endif
97
pushMenu(Screen * screen)98 void AbstractStateManager::pushMenu(Screen* screen)
99 {
100 // currently, only a single in-game menu is supported
101 assert(m_game_mode != INGAME_MENU);
102
103 // you need to close any dialog before calling this
104 assert(!ModalDialog::isADialogActive());
105 assert(!ScreenKeyboard::isActive());
106
107 if (UserConfigParams::logGUI())
108 {
109 Log::info("AbstractStateManager::pushMenu", "Switching to screen %s",
110 screen->getName().c_str());
111 }
112
113 // Send tear-down event to previous menu
114 if (m_menu_stack.size() > 0 && m_game_mode != GAME)
115 getCurrentScreen()->tearDown();
116
117 m_menu_stack.emplace_back(screen->getName(), screen);
118 if (m_game_mode == GAME)
119 {
120 setGameState(INGAME_MENU);
121 }
122 else
123 {
124 setGameState(MENU);
125 }
126 switchToScreen(screen);
127
128 onTopMostScreenChanged();
129 } // pushMenu
130
131
132 // ----------------------------------------------------------------------------
133
pushScreen(Screen * screen)134 void AbstractStateManager::pushScreen(Screen* screen)
135 {
136 // you need to close any dialog before calling this
137 assert(!ModalDialog::isADialogActive());
138 assert(!ScreenKeyboard::isActive());
139
140 if (UserConfigParams::logGUI())
141 {
142 Log::info("AbstractStateManager::pushScreen", "Switching to screen %s",
143 screen->getName().c_str());
144 }
145
146 if (!screen->isLoaded()) screen->loadFromFile();
147 pushMenu(screen);
148 screen->init();
149
150 onTopMostScreenChanged();
151 } // pushScreen%
152
153 // ----------------------------------------------------------------------------
154
replaceTopMostScreen(Screen * screen,GUIEngine::GameState gameState)155 void AbstractStateManager::replaceTopMostScreen(Screen* screen, GUIEngine::GameState gameState)
156 {
157 if (gameState == GUIEngine::CURRENT)
158 gameState = getGameState();
159
160 //assert(m_game_mode != GAME);
161 // you need to close any dialog before calling this
162 assert(!ModalDialog::isADialogActive());
163 assert(!ScreenKeyboard::isActive());
164
165 if (!screen->isLoaded()) screen->loadFromFile();
166 std::string name = screen->getName();
167
168 if (UserConfigParams::logGUI())
169 {
170 Log::info("AbstractStateManager::replaceTopMostScreen", "Switching to screen %s",
171 name.c_str());
172 }
173
174 assert(m_menu_stack.size() > 0);
175
176 // Send tear-down event to previous menu
177 if (getCurrentScreen() != NULL)
178 getCurrentScreen()->tearDown();
179
180 m_menu_stack[m_menu_stack.size()-1] = std::make_pair(name, screen);
181 setGameState(gameState);
182 switchToScreen(screen);
183
184 // Send init event to new menu
185 getCurrentScreen()->init();
186
187 onTopMostScreenChanged();
188 } // replaceTopMostScreen
189
190 // ----------------------------------------------------------------------------
191
reshowTopMostMenu()192 void AbstractStateManager::reshowTopMostMenu()
193 {
194 assert(m_game_mode != GAME);
195 // you need to close any dialog before calling this
196 assert(!ModalDialog::isADialogActive());
197 assert(!ScreenKeyboard::isActive());
198
199 // Send tear-down event to previous menu
200 if (m_menu_stack.size() > 0)
201 {
202 Screen* currScreen = getCurrentScreen();
203 if (currScreen != NULL) getCurrentScreen()->tearDown();
204 }
205
206 switchToScreen(m_menu_stack[m_menu_stack.size()-1].second);
207
208 // Send init event to new menu
209 Screen* screen = getCurrentScreen();
210 if (!screen->isLoaded()) screen->loadFromFile();
211 screen->init();
212
213 onTopMostScreenChanged();
214 } // reshowTopMostMenu
215
216 // ----------------------------------------------------------------------------
217
popMenu()218 void AbstractStateManager::popMenu()
219 {
220 assert(m_game_mode != GAME);
221
222 if (m_menu_stack.empty())
223 return;
224
225 // Send tear-down event to menu
226 getCurrentScreen()->tearDown();
227 m_menu_stack.pop_back();
228
229 if (m_menu_stack.empty())
230 {
231 getGUIEnv()->clear();
232 getCurrentScreen()->elementsWereDeleted();
233 onStackEmptied();
234 return;
235 }
236
237 if (UserConfigParams::logGUI())
238 {
239 Log::info("AbstractStateManager::popMenu", "Switching to screen %s",
240 m_menu_stack[m_menu_stack.size()-1].first.c_str());
241 }
242
243 if (m_menu_stack[m_menu_stack.size()-1].first == RACE_STATE_NAME)
244 {
245 setGameState(GAME);
246 GUIEngine::cleanForGame();
247 }
248 else
249 {
250 setGameState(MENU);
251 switchToScreen(m_menu_stack[m_menu_stack.size()-1].second);
252
253 Screen* screen = getCurrentScreen();
254 if (!screen->isLoaded()) screen->loadFromFile();
255 screen->init();
256 }
257
258 onTopMostScreenChanged();
259 } // popMenu
260
261 // ----------------------------------------------------------------------------
262
resetAndGoToScreen(Screen * screen)263 void AbstractStateManager::resetAndGoToScreen(Screen* screen)
264 {
265 // you need to close any dialog before calling this
266 assert(!ModalDialog::isADialogActive());
267 assert(!ScreenKeyboard::isActive());
268
269 std::string name = screen->getName();
270
271 if (UserConfigParams::logGUI())
272 Log::info("AbstractStateManager::resetAndGoToScreen", "Switching to screen %s",
273 name.c_str());
274
275 if (m_game_mode != GAME) getCurrentScreen()->tearDown();
276 m_menu_stack.clear();
277
278 if (!screen->isLoaded()) screen->loadFromFile();
279 m_menu_stack.emplace_back(name, screen);
280 setGameState(MENU);
281
282 switchToScreen(screen);
283 getCurrentScreen()->init();
284
285 onTopMostScreenChanged();
286 } // resetAndGoToScreen
287
288 // ----------------------------------------------------------------------------
289
resetAndSetStack(Screen * screens[])290 void AbstractStateManager::resetAndSetStack(Screen* screens[])
291 {
292 assert(screens != NULL);
293 assert(screens[0] != NULL);
294 // you need to close any dialog before calling this
295 assert(!ModalDialog::isADialogActive());
296 assert(!ScreenKeyboard::isActive());
297
298 if (m_game_mode != GAME && getCurrentScreen())
299 getCurrentScreen()->tearDown();
300 m_menu_stack.clear();
301
302 for (int n=0; screens[n] != NULL; n++)
303 {
304 m_menu_stack.emplace_back(screens[n]->getName(), screens[n]);
305 }
306
307 setGameState(MENU);
308
309 switchToScreen(m_menu_stack[m_menu_stack.size()-1].second);
310 getCurrentScreen()->init();
311
312 onTopMostScreenChanged();
313 } // resetAndSetStack
314
315 // ----------------------------------------------------------------------------
316
onResize()317 void AbstractStateManager::onResize()
318 {
319 // Happens in the first resize in main.cpp
320 if (m_menu_stack.empty())
321 return;
322
323 // In game resizing
324 if (m_menu_stack[0].first == RACE_STATE_NAME)
325 {
326 if (m_menu_stack.size() == 1)
327 {
328 clearScreenCache();
329 m_menu_stack.emplace_back(RACE_STATE_NAME, (Screen*)NULL);
330 }
331 return;
332 }
333
334 // For some window manager it sends resize event when STK is not focus
335 // even if the screen is not resizable, prevent it from resizing if wrong
336 // screen
337 if (!m_menu_stack.back().second ||
338 !m_menu_stack.back().second->isResizable())
339 return;
340
341 std::vector<std::function<Screen*()> > screen_function;
342 for (auto& p : m_menu_stack)
343 screen_function.push_back(p.second->getNewScreenPointer());
344 clearScreenCache();
345 std::vector<Screen*> new_screen;
346 for (auto& screen : screen_function)
347 new_screen.push_back(screen());
348 new_screen.push_back(NULL);
349 resetAndSetStack(new_screen.data());
350 } // onResize
351