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