World creation button and dialog and functionality
[oweals/minetest.git] / src / guiMainMenu.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-12 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "guiMainMenu.h"
21 #include "guiKeyChangeMenu.h"
22 #include "guiCreateWorld.h"
23 #include "guiMessageMenu.h"
24 #include "debug.h"
25 #include "serialization.h"
26 #include <string>
27 #include <IGUICheckBox.h>
28 #include <IGUIEditBox.h>
29 #include <IGUIButton.h>
30 #include <IGUIStaticText.h>
31 #include <IGUIFont.h>
32 #include <IGUIListBox.h>
33 // For IGameCallback
34 #include "guiPauseMenu.h"
35 #include "gettext.h"
36 #include "utility.h"
37
38 struct CreateWorldDestMainMenu : public CreateWorldDest
39 {
40         CreateWorldDestMainMenu(GUIMainMenu *menu):
41                 m_menu(menu)
42         {}
43         void accepted(std::wstring name, std::string gameid)
44         {
45                 m_menu->createNewWorld(name, gameid);
46         }
47         GUIMainMenu *m_menu;
48 };
49
50 enum
51 {
52         GUI_ID_QUIT_BUTTON = 101,
53         GUI_ID_NAME_INPUT,
54         GUI_ID_ADDRESS_INPUT,
55         GUI_ID_PORT_INPUT,
56         GUI_ID_FANCYTREE_CB,
57         GUI_ID_SMOOTH_LIGHTING_CB,
58         GUI_ID_3D_CLOUDS_CB,
59         GUI_ID_OPAQUE_WATER_CB,
60         GUI_ID_DAMAGE_CB,
61         GUI_ID_CREATIVE_CB,
62         GUI_ID_JOIN_GAME_BUTTON,
63         GUI_ID_CHANGE_KEYS_BUTTON,
64         GUI_ID_DELETE_WORLD_BUTTON,
65         GUI_ID_CREATE_WORLD_BUTTON,
66         GUI_ID_WORLD_LISTBOX,
67 };
68
69 GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
70                 gui::IGUIElement* parent, s32 id,
71                 IMenuManager *menumgr,
72                 MainMenuData *data,
73                 IGameCallback *gamecallback
74 ):
75         GUIModalMenu(env, parent, id, menumgr),
76         m_data(data),
77         m_accepted(false),
78         m_gamecallback(gamecallback)
79 {
80         assert(m_data);
81         this->env = env;
82         this->parent = parent;
83         this->id = id;
84         this->menumgr = menumgr;
85 }
86
87 GUIMainMenu::~GUIMainMenu()
88 {
89         removeChildren();
90 }
91
92 void GUIMainMenu::removeChildren()
93 {
94         const core::list<gui::IGUIElement*> &children = getChildren();
95         core::list<gui::IGUIElement*> children_copy;
96         for(core::list<gui::IGUIElement*>::ConstIterator
97                         i = children.begin(); i != children.end(); i++)
98         {
99                 children_copy.push_back(*i);
100         }
101         for(core::list<gui::IGUIElement*>::Iterator
102                         i = children_copy.begin();
103                         i != children_copy.end(); i++)
104         {
105                 (*i)->remove();
106         }
107 }
108
109 void GUIMainMenu::regenerateGui(v2u32 screensize)
110 {
111         /*
112                 Read stuff from elements into m_data
113         */
114         readInput(m_data);
115         
116         /*
117                 Remove stuff
118         */
119         removeChildren();
120         
121         /*
122                 Calculate new sizes and positions
123         */
124         
125         v2s32 size(620, 430);
126
127         core::rect<s32> rect(
128                         screensize.X/2 - size.X/2,
129                         screensize.Y/2 - size.Y/2,
130                         screensize.X/2 + size.X/2,
131                         screensize.Y/2 + size.Y/2
132         );
133
134         DesiredRect = rect;
135         recalculateAbsolutePosition(false);
136
137         //v2s32 size = rect.getSize();
138
139         /*
140                 Add stuff
141         */
142
143         /*
144                 Client section
145         */
146
147         v2s32 topleft_client(40, 0);
148         v2s32 size_client = size - v2s32(40, 0);
149         
150         changeCtype("");
151         
152         // Version
153         {
154                 core::rect<s32> rect(0, 0, 300, 30);
155                 rect += topleft_client + v2s32(-36, 0);
156                 Environment->addStaticText(narrow_to_wide(VERSION_STRING).c_str(), 
157                         rect, false, true, this, -1);
158         }
159         // CLIENT
160         {
161                 core::rect<s32> rect(0, 0, 20, 125);
162                 rect += topleft_client + v2s32(-15, 80);
163                 const wchar_t *text = L"C\nL\nI\nE\nN\nT";
164                 //gui::IGUIStaticText *t =
165                 Environment->addStaticText(text, rect, false, true, this, -1);
166                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
167         }
168         // Nickname + password
169         {
170                 core::rect<s32> rect(0, 0, 110, 20);
171                 rect += topleft_client + v2s32(35, 50+6);
172                 Environment->addStaticText(wgettext("Name/Password"), 
173                         rect, false, true, this, -1);
174         }
175         changeCtype("C");
176         {
177                 core::rect<s32> rect(0, 0, 230, 30);
178                 rect += topleft_client + v2s32(160, 50);
179                 gui::IGUIElement *e = 
180                 Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
181                 if(m_data->name == L"")
182                         Environment->setFocus(e);
183         }
184         {
185                 core::rect<s32> rect(0, 0, 120, 30);
186                 rect += topleft_client + v2s32(size_client.X-60-100, 50);
187                 gui::IGUIEditBox *e =
188                 Environment->addEditBox(L"", rect, true, this, 264);
189                 e->setPasswordBox(true);
190                 if(m_data->name != L"" && m_data->address != L"")
191                         Environment->setFocus(e);
192
193         }
194         changeCtype("");
195         // Address + port
196         {
197                 core::rect<s32> rect(0, 0, 110, 20);
198                 rect += topleft_client + v2s32(35, 100+6);
199                 Environment->addStaticText(wgettext("Address/Port"),
200                         rect, false, true, this, -1);
201         }
202         changeCtype("C");
203         {
204                 core::rect<s32> rect(0, 0, 230, 30);
205                 rect += topleft_client + v2s32(160, 100);
206                 gui::IGUIElement *e = 
207                 Environment->addEditBox(m_data->address.c_str(), rect, true, this, GUI_ID_ADDRESS_INPUT);
208                 if(m_data->name != L"" && m_data->address == L"")
209                         Environment->setFocus(e);
210         }
211         {
212                 core::rect<s32> rect(0, 0, 120, 30);
213                 //rect += topleft_client + v2s32(160+250+20, 125);
214                 rect += topleft_client + v2s32(size_client.X-60-100, 100);
215                 Environment->addEditBox(m_data->port.c_str(), rect, true, this, GUI_ID_PORT_INPUT);
216         }
217         changeCtype("");
218         {
219                 core::rect<s32> rect(0, 0, 400, 20);
220                 rect += topleft_client + v2s32(160, 100+35);
221                 Environment->addStaticText(wgettext("Leave address blank to start a local server."),
222                         rect, false, true, this, -1);
223         }
224         {
225                 core::rect<s32> rect(0, 0, 250, 30);
226                 rect += topleft_client + v2s32(35, 150);
227                 Environment->addCheckBox(m_data->fancy_trees, rect, this, GUI_ID_FANCYTREE_CB,
228                         wgettext("Fancy trees")); 
229         }
230         {
231                 core::rect<s32> rect(0, 0, 250, 30);
232                 rect += topleft_client + v2s32(35, 150+20);
233                 Environment->addCheckBox(m_data->smooth_lighting, rect, this, GUI_ID_SMOOTH_LIGHTING_CB,
234                                 wgettext("Smooth Lighting"));
235         }
236         {
237                 core::rect<s32> rect(0, 0, 250, 30);
238                 rect += topleft_client + v2s32(35, 150+40);
239                 Environment->addCheckBox(m_data->clouds_3d, rect, this, GUI_ID_3D_CLOUDS_CB,
240                                 wgettext("3D Clouds"));
241         }
242         {
243                 core::rect<s32> rect(0, 0, 250, 30);
244                 rect += topleft_client + v2s32(35, 150+60);
245                 Environment->addCheckBox(m_data->opaque_water, rect, this, GUI_ID_OPAQUE_WATER_CB,
246                                 wgettext("Opaque water"));
247         }
248         // Start game button
249         {
250                 core::rect<s32> rect(0, 0, 180, 30);
251                 //rect += topleft_client + v2s32(size_client.X/2-180/2, 225-30/2);
252                 rect += topleft_client + v2s32(size_client.X-180-40, 150+25);
253                 Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
254                         wgettext("Start Game / Connect"));
255         }
256
257         // Key change button
258         {
259                 core::rect<s32> rect(0, 0, 100, 30);
260                 //rect += topleft_client + v2s32(size_client.X/2-180/2, 225-30/2);
261                 rect += topleft_client + v2s32(size_client.X-180-40-100-20, 150+25);
262                 Environment->addButton(rect, this, GUI_ID_CHANGE_KEYS_BUTTON,
263                         wgettext("Change keys"));
264         }
265         /*
266                 Server section
267         */
268
269         v2s32 topleft_server(40, 290);
270         v2s32 size_server = size - v2s32(40, 0);
271         
272         // SERVER
273         {
274                 core::rect<s32> rect(0, 0, 20, 125);
275                 rect += topleft_server + v2s32(-15, 15);
276                 const wchar_t *text = L"S\nE\nR\nV\nE\nR";
277                 //gui::IGUIStaticText *t =
278                 Environment->addStaticText(text, rect, false, true, this, -1);
279                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
280         }
281         // Server parameters
282         {
283                 core::rect<s32> rect(0, 0, 250, 30);
284                 rect += topleft_server + v2s32(20+250+20, 20);
285                 Environment->addCheckBox(m_data->creative_mode, rect, this, GUI_ID_CREATIVE_CB,
286                         wgettext("Creative Mode"));
287         }
288         {
289                 core::rect<s32> rect(0, 0, 250, 30);
290                 rect += topleft_server + v2s32(20+250+20, 40);
291                 Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
292                         wgettext("Enable Damage"));
293         }
294         // Delete world button
295         {
296                 core::rect<s32> rect(0, 0, 130, 30);
297                 rect += topleft_server + v2s32(20+250+20, 90);
298                 Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
299                           wgettext("Delete world"));
300         }
301         // Create world button
302         {
303                 core::rect<s32> rect(0, 0, 130, 30);
304                 rect += topleft_server + v2s32(20+250+20+140, 90);
305                 Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
306                           wgettext("Create world"));
307         }
308         // World selection listbox
309         {
310                 core::rect<s32> rect(0, 0, 250, 120);
311                 rect += topleft_server + v2s32(20, 10);
312                 gui::IGUIListBox *e = Environment->addListBox(rect, this,
313                                 GUI_ID_WORLD_LISTBOX);
314                 e->setDrawBackground(true);
315                 for(std::list<std::wstring>::const_iterator i = m_data->worlds.begin();
316                                 i != m_data->worlds.end(); i++){
317                         e->addItem(i->c_str());
318                 }
319                 e->setSelected(m_data->selected_world);
320         }
321         changeCtype("C");
322 }
323
324 void GUIMainMenu::drawMenu()
325 {
326         gui::IGUISkin* skin = Environment->getSkin();
327         if (!skin)
328                 return;
329         video::IVideoDriver* driver = Environment->getVideoDriver();
330         
331         /*video::SColor bgcolor(140,0,0,0);
332         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
333
334         video::SColor bgcolor(140,0,0,0);
335
336         {
337                 core::rect<s32> rect(0, 0, 620, 270);
338                 rect += AbsoluteRect.UpperLeftCorner;
339                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
340         }
341
342         {
343                 core::rect<s32> rect(0, 290, 620, 430);
344                 rect += AbsoluteRect.UpperLeftCorner;
345                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
346         }
347
348         gui::IGUIElement::draw();
349 }
350
351 void GUIMainMenu::readInput(MainMenuData *dst)
352 {
353         {
354                 gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
355                 if(e != NULL)
356                         dst->name = e->getText();
357         }
358         {
359                 gui::IGUIElement *e = getElementFromId(264);
360                 if(e != NULL)
361                         dst->password = e->getText();
362         }
363         {
364                 gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
365                 if(e != NULL)
366                         dst->address = e->getText();
367         }
368         {
369                 gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
370                 if(e != NULL)
371                         dst->port = e->getText();
372         }
373         {
374                 gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
375                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
376                         dst->creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
377         }
378         {
379                 gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
380                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
381                         dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
382         }
383         {
384                 gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
385                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
386                         dst->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
387         }
388         {
389                 gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
390                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
391                         dst->smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
392         }
393         {
394                 gui::IGUIElement *e = getElementFromId(GUI_ID_3D_CLOUDS_CB);
395                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
396                         dst->clouds_3d = ((gui::IGUICheckBox*)e)->isChecked();
397         }
398         {
399                 gui::IGUIElement *e = getElementFromId(GUI_ID_OPAQUE_WATER_CB);
400                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
401                         dst->opaque_water = ((gui::IGUICheckBox*)e)->isChecked();
402         }
403
404         {
405                 gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
406                 if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
407                         dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
408         }
409 }
410
411 void GUIMainMenu::acceptInput()
412 {
413         readInput(m_data);
414         m_accepted = true;
415 }
416
417 bool GUIMainMenu::OnEvent(const SEvent& event)
418 {
419         if(event.EventType==EET_KEY_INPUT_EVENT)
420         {
421                 if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
422                 {
423                         m_gamecallback->exitToOS();
424                         quitMenu();
425                         return true;
426                 }
427                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
428                 {
429                         acceptInput();
430                         quitMenu();
431                         return true;
432                 }
433         }
434         if(event.EventType==EET_GUI_EVENT)
435         {
436                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
437                                 && isVisible())
438                 {
439                         if(!canTakeFocus(event.GUIEvent.Element))
440                         {
441                                 dstream<<"GUIMainMenu: Not allowing focus change."
442                                                 <<std::endl;
443                                 // Returning true disables focus change
444                                 return true;
445                         }
446                 }
447                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
448                 {
449                         switch(event.GUIEvent.Caller->getID())
450                         {
451                         case GUI_ID_JOIN_GAME_BUTTON:
452                                 acceptInput();
453                                 quitMenu();
454                                 return true;
455                         case GUI_ID_CHANGE_KEYS_BUTTON: {
456                                 GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(env, parent, -1,menumgr);
457                                 kmenu->drop();
458                                 return true;
459                         }
460                         case GUI_ID_DELETE_WORLD_BUTTON: {
461                                 acceptInput();
462                                 m_data->delete_world = true;
463                                 quitMenu();
464                                 return true;
465                         }
466                         case GUI_ID_CREATE_WORLD_BUTTON: {
467                                 std::vector<SubgameSpec> games = getAvailableGames();
468                                 if(games.size() == 0){
469                                         GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
470                                                         -1, menumgr,
471                                                         wgettext("Cannot create world: No games found"));
472                                         menu->drop();
473                                 } else {
474                                         CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
475                                         GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
476                                                         menumgr, dest, games);
477                                         menu->drop();
478                                 }
479                                 return true;
480                         }
481                         }
482                 }
483                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
484                 {
485                         switch(event.GUIEvent.Caller->getID())
486                         {
487                                 case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264:
488                                 acceptInput();
489                                 quitMenu();
490                                 return true;
491                         }
492                 }
493                 if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
494                 {
495                         switch(event.GUIEvent.Caller->getID())
496                         {
497                                 case GUI_ID_WORLD_LISTBOX:
498                                 acceptInput();
499                                 m_data->address = L""; // Force local game
500                                 quitMenu();
501                                 return true;
502                         }
503                 }
504         }
505
506         return Parent ? Parent->OnEvent(event) : false;
507 }
508
509 void GUIMainMenu::createNewWorld(std::wstring name, std::string gameid)
510 {
511         if(name == L"")
512                 return;
513         acceptInput();
514         m_data->create_world_name = name;
515         m_data->create_world_gameid = gameid;
516         quitMenu();
517 }
518