1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include "SelectMenu.h"
4
5 #include <SDL_keycode.h>
6 #include <boost/bind.hpp>
7 #include <sstream>
8 #include <stack>
9 #include <boost/cstdint.hpp>
10
11 #include "SelectionWidget.h"
12 #include "System/AIScriptHandler.h"
13 #include "Game/ClientSetup.h"
14 #include "Game/GlobalUnsynced.h"
15 #include "Game/PreGame.h"
16 #include "Rendering/Fonts/glFont.h"
17 #include "Rendering/GL/myGL.h"
18 #include "System/Config/ConfigHandler.h"
19 #include "System/Exceptions.h"
20 #include "System/Log/ILog.h"
21 #include "System/Util.h"
22 #include "System/Input/InputHandler.h"
23 #include "System/FileSystem/ArchiveScanner.h"
24 #include "System/FileSystem/FileHandler.h"
25 #include "System/FileSystem/VFSHandler.h"
26 #include "System/FileSystem/FileSystem.h"
27 #include "System/MsgStrings.h"
28 #include "System/StartScriptGen.h"
29 #include "aGui/Gui.h"
30 #include "aGui/VerticalLayout.h"
31 #include "aGui/HorizontalLayout.h"
32 #include "aGui/Button.h"
33 #include "aGui/LineEdit.h"
34 #include "aGui/TextElement.h"
35 #include "aGui/Window.h"
36 #include "aGui/Picture.h"
37 #include "aGui/List.h"
38 #include "alphanum.hpp"
39
40 using std::string;
41 using agui::Button;
42 using agui::HorizontalLayout;
43
44 CONFIG(std::string, address).defaultValue("").description("Last Ip/hostname used as direct connect in the menu.");
45 CONFIG(std::string, LastSelectedSetting).defaultValue("").description("Stores the previously selected setting, when editing settings within the Spring main menu.");
46 CONFIG(std::string, MenuArchive).defaultValue("Spring Bitmaps").description("Archive name for the default Menu.");
47
48 class ConnectWindow : public agui::Window
49 {
50 public:
ConnectWindow()51 ConnectWindow() : agui::Window("Connect to server")
52 {
53 agui::gui->AddElement(this);
54 SetPos(0.5, 0.5);
55 SetSize(0.4, 0.2);
56
57 agui::VerticalLayout* wndLayout = new agui::VerticalLayout(this);
58 HorizontalLayout* input = new HorizontalLayout(wndLayout);
59 /*agui::TextElement* label = */new agui::TextElement("Address:", input); // will be deleted in input
60 address = new agui::LineEdit(input);
61 address->DefaultAction.connect(boost::bind(&ConnectWindow::Finish, this, true));
62 address->SetFocus(true);
63 address->SetContent(configHandler->GetString("address"));
64 HorizontalLayout* buttons = new HorizontalLayout(wndLayout);
65 Button* connect = new Button("Connect", buttons);
66 connect->Clicked.connect(boost::bind(&ConnectWindow::Finish, this, true));
67 Button* close = new Button("Close", buttons);
68 close->Clicked.connect(boost::bind(&ConnectWindow::Finish, this, false));
69 GeometryChange();
70 }
71
72 boost::signals2::signal<void (std::string)> Connect;
73 agui::LineEdit* address;
74
75 private:
Finish(bool connect)76 void Finish(bool connect)
77 {
78 if (connect)
79 Connect(address->GetContent());
80 else
81 WantClose();
82 };
83 };
84
85 class SettingsWindow : public agui::Window
86 {
87 public:
SettingsWindow(std::string & name)88 SettingsWindow(std::string &name) : agui::Window(name)
89 {
90 agui::gui->AddElement(this);
91 SetPos(0.5, 0.5);
92 SetSize(0.4, 0.2);
93
94 agui::VerticalLayout* wndLayout = new agui::VerticalLayout(this);
95 HorizontalLayout* input = new HorizontalLayout(wndLayout);
96 /*agui::TextElement* value_label = */new agui::TextElement("Value:", input); // will be deleted in input
97 value = new agui::LineEdit(input);
98 value->DefaultAction.connect(boost::bind(&SettingsWindow::Finish, this, true));
99 value->SetFocus(true);
100 if (configHandler->IsSet(name))
101 value->SetContent(configHandler->GetString(name));
102 HorizontalLayout* buttons = new HorizontalLayout(wndLayout);
103 Button* ok = new Button("OK", buttons);
104 ok->Clicked.connect(boost::bind(&SettingsWindow::Finish, this, true));
105 Button* close = new Button("Cancel", buttons);
106 close->Clicked.connect(boost::bind(&SettingsWindow::Finish, this, false));
107 GeometryChange();
108 }
109
110 boost::signals2::signal<void (std::string)> OK;
111 agui::LineEdit* value;
112
113 private:
Finish(bool set)114 void Finish(bool set)
115 {
116 if (set)
117 OK(title + " = " + value->GetContent());
118 else
119 WantClose();
120 };
121 };
122
SelectMenu(boost::shared_ptr<ClientSetup> setup)123 SelectMenu::SelectMenu(boost::shared_ptr<ClientSetup> setup)
124 : GuiElement(NULL)
125 , clientSetup(setup)
126 , conWindow(NULL)
127 , settingsWindow(NULL)
128 , curSelect(NULL)
129 {
130 SetPos(0,0);
131 SetSize(1,1);
132 agui::gui->AddElement(this, true);
133
134 { // GUI stuff
135 agui::Picture* background = new agui::Picture(this);;
136 {
137 const std::string archive = archiveScanner->ArchiveFromName(configHandler->GetString("MenuArchive"));
138 const std::string archivePath = archiveScanner->GetArchivePath(archive)+archive;
139 vfsHandler->AddArchive(archivePath, false);
140 const std::vector<std::string> files = CFileHandler::FindFiles("bitmaps/ui/background/", "*");
141 if (!files.empty()) {
142 //TODO: select by resolution / aspect ratio with fallback image
143 background->Load(files[gu->RandInt() % files.size()]);
144 }
145 vfsHandler->RemoveArchive(archivePath);
146 }
147 selw = new SelectionWidget(this);
148 agui::VerticalLayout* menu = new agui::VerticalLayout(this);
149 menu->SetPos(0.1, 0.5);
150 menu->SetSize(0.4, 0.4);
151 menu->SetBorder(1.2f);
152 /*agui::TextElement* title = */new agui::TextElement("Spring", menu); // will be deleted in menu
153 Button* single = new Button("Test the Game", menu);
154 single->Clicked.connect(boost::bind(&SelectMenu::Single, this));
155
156 userSetting = configHandler->GetString("LastSelectedSetting");
157 Button* editsettings = new Button("Edit settings", menu);
158 editsettings->Clicked.connect(boost::bind(&SelectMenu::ShowSettingsList, this));
159
160 Button* direct = new Button("Direct connect", menu);
161 direct->Clicked.connect(boost::bind(&SelectMenu::ShowConnectWindow, this, true));
162
163 Button* quit = new Button("Quit", menu);
164 quit->Clicked.connect(boost::bind(&SelectMenu::Quit, this));
165 background->GeometryChange();
166 }
167
168 if (!clientSetup->isHost) {
169 ShowConnectWindow(true);
170 }
171 }
172
~SelectMenu()173 SelectMenu::~SelectMenu()
174 {
175 ShowConnectWindow(false);
176 ShowSettingsWindow(false, "");
177 CleanWindow();
178 }
179
Draw()180 bool SelectMenu::Draw()
181 {
182 spring_msecs(10).sleep();
183 ClearScreen();
184 agui::gui->Draw();
185
186 return true;
187 }
188
Single()189 void SelectMenu::Single()
190 {
191 static bool once = false;
192 if (selw->userMod == SelectionWidget::NoModSelect)
193 {
194 selw->ShowModList();
195 }
196 else if (selw->userMap == SelectionWidget::NoMapSelect)
197 {
198 selw->ShowMapList();
199 }
200 else if (selw->userScript == SelectionWidget::NoScriptSelect)
201 {
202 selw->ShowScriptList();
203 }
204 else if (!once) // in case of double-click
205 {
206 if (selw->userScript == SelectionWidget::SandboxAI) {
207 selw->userScript.clear();
208 }
209 once = true;
210
211 pregame = new CPreGame(clientSetup);
212 pregame->LoadSetupscript(StartScriptGen::CreateDefaultSetup(selw->userMap, selw->userMod, selw->userScript, clientSetup->myPlayerName));
213 return agui::gui->RmElement(this);
214 //delete this;
215 }
216 }
217
Quit()218 void SelectMenu::Quit()
219 {
220 gu->globalQuit = true;
221 return agui::gui->RmElement(this);
222 //delete this;
223 }
224
ShowConnectWindow(bool show)225 void SelectMenu::ShowConnectWindow(bool show)
226 {
227 if (show && !conWindow)
228 {
229 conWindow = new ConnectWindow();
230 conWindow->Connect.connect(boost::bind(&SelectMenu::DirectConnect, this, _1));
231 conWindow->WantClose.connect(boost::bind(&SelectMenu::ShowConnectWindow, this, false));
232 }
233 else if (!show && conWindow)
234 {
235 agui::gui->RmElement(conWindow);
236 conWindow = NULL;
237 }
238 }
239
ShowSettingsWindow(bool show,std::string name)240 void SelectMenu::ShowSettingsWindow(bool show, std::string name)
241 {
242 if (show)
243 {
244 if(settingsWindow) {
245 agui::gui->RmElement(settingsWindow);
246 settingsWindow = NULL;
247 }
248 settingsWindow = new SettingsWindow(name);
249 settingsWindow->OK.connect(boost::bind(&SelectMenu::ShowSettingsWindow, this, false, _1));
250 settingsWindow->WantClose.connect(boost::bind(&SelectMenu::ShowSettingsWindow, this, false, ""));
251 }
252 else if (!show && settingsWindow)
253 {
254 agui::gui->RmElement(settingsWindow);
255 settingsWindow = NULL;
256 size_t p = name.find(" = ");
257 if(p != std::string::npos) {
258 configHandler->SetString(name.substr(0,p), name.substr(p + 3));
259 ShowSettingsList();
260 }
261 if(curSelect)
262 curSelect->list->SetFocus(true);
263 }
264 }
265
ShowSettingsList()266 void SelectMenu::ShowSettingsList()
267 {
268 if (!curSelect) {
269 curSelect = new ListSelectWnd("Select setting");
270 curSelect->Selected.connect(boost::bind(&SelectMenu::SelectSetting, this, _1));
271 curSelect->WantClose.connect(boost::bind(&SelectMenu::CleanWindow, this));
272 }
273 curSelect->list->RemoveAllItems();
274 const std::map<std::string, std::string> &data = configHandler->GetData();
275 typedef std::map<std::string, std::string, doj::alphanum_less<std::string> > DataSorted;
276 const DataSorted dataSorted(data.begin(), data.end());
277 for(DataSorted::const_iterator iter = dataSorted.begin(); iter != dataSorted.end(); ++iter)
278 curSelect->list->AddItem(iter->first + " = " + iter->second, "");
279 if(data.find(userSetting) != data.end())
280 curSelect->list->SetCurrentItem(userSetting + " = " + configHandler->GetString(userSetting));
281 curSelect->list->RefreshQuery();
282 }
283
SelectSetting(std::string setting)284 void SelectMenu::SelectSetting(std::string setting) {
285 size_t p = setting.find(" = ");
286 if(p != std::string::npos)
287 setting = setting.substr(0, p);
288 userSetting = setting;
289 configHandler->SetString("LastSelectedSetting", userSetting);
290 ShowSettingsWindow(true, userSetting);
291 }
292
CleanWindow()293 void SelectMenu::CleanWindow() {
294 if (curSelect) {
295 ShowSettingsWindow(false, "");
296 agui::gui->RmElement(curSelect);
297 curSelect = NULL;
298 }
299 }
300
DirectConnect(const std::string & addr)301 void SelectMenu::DirectConnect(const std::string& addr)
302 {
303 configHandler->SetString("address", addr);
304 clientSetup->hostIP = addr;
305 clientSetup->isHost = false;
306 pregame = new CPreGame(clientSetup);
307 return agui::gui->RmElement(this);
308 //delete this;
309 }
310
HandleEventSelf(const SDL_Event & ev)311 bool SelectMenu::HandleEventSelf(const SDL_Event& ev)
312 {
313 switch (ev.type) {
314 case SDL_KEYDOWN: {
315 if (ev.key.keysym.sym == SDLK_ESCAPE) {
316 LOG("User exited");
317 Quit();
318 } else if (ev.key.keysym.sym == SDLK_RETURN) {
319 Single();
320 return true;
321 }
322 break;
323 }
324 }
325 return false;
326 }
327