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