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