]> git.lizzy.rs Git - minetest.git/blob - src/guiMainMenu.cpp
Add shutdown hook interface to Lua API
[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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 #include <IGUIImage.h>
36 // For IGameCallback
37 #include "guiPauseMenu.h"
38 #include "gettext.h"
39 #include "tile.h" // getTexturePath
40 #include "filesys.h"
41 #include "util/string.h"
42 #include "subgame.h"
43
44 struct CreateWorldDestMainMenu : public CreateWorldDest
45 {
46         CreateWorldDestMainMenu(GUIMainMenu *menu):
47                 m_menu(menu)
48         {}
49         void accepted(std::wstring name, std::string gameid)
50         {
51                 std::string name_narrow = wide_to_narrow(name);
52                 if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
53                 {
54                         m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters"));
55                         return;
56                 }
57                 std::vector<WorldSpec> worlds = getAvailableWorlds();
58                 for(std::vector<WorldSpec>::iterator i = worlds.begin();
59                     i != worlds.end(); i++)
60                 {
61                         if((*i).name == name_narrow)
62                         {
63                                 m_menu->displayMessageMenu(wgettext("Cannot create world: A world by this name already exists"));
64                                 return;
65                         }
66                 }
67                 m_menu->createNewWorld(name, gameid);
68         }
69         GUIMainMenu *m_menu;
70 };
71
72 struct ConfirmDestDeleteWorld : public ConfirmDest
73 {
74         ConfirmDestDeleteWorld(WorldSpec spec, GUIMainMenu *menu,
75                         const std::vector<std::string> &paths):
76                 m_spec(spec),
77                 m_menu(menu),
78                 m_paths(paths)
79         {}
80         void answer(bool answer)
81         {
82                 if(answer == false)
83                         return;
84                 m_menu->deleteWorld(m_paths);
85         }
86         WorldSpec m_spec;
87         GUIMainMenu *m_menu;
88         std::vector<std::string> m_paths;
89 };
90
91 enum
92 {
93         GUI_ID_QUIT_BUTTON = 101,
94         GUI_ID_NAME_INPUT,
95         GUI_ID_ADDRESS_INPUT,
96         GUI_ID_PORT_INPUT,
97         GUI_ID_FANCYTREE_CB,
98         GUI_ID_SMOOTH_LIGHTING_CB,
99         GUI_ID_3D_CLOUDS_CB,
100         GUI_ID_OPAQUE_WATER_CB,
101         GUI_ID_MIPMAP_CB,
102         GUI_ID_ANISOTROPIC_CB,
103         GUI_ID_BILINEAR_CB,
104         GUI_ID_TRILINEAR_CB,
105         GUI_ID_DAMAGE_CB,
106         GUI_ID_CREATIVE_CB,
107         GUI_ID_JOIN_GAME_BUTTON,
108         GUI_ID_CHANGE_KEYS_BUTTON,
109         GUI_ID_DELETE_WORLD_BUTTON,
110         GUI_ID_CREATE_WORLD_BUTTON,
111         GUI_ID_CONFIGURE_WORLD_BUTTON,
112         GUI_ID_WORLD_LISTBOX,
113         GUI_ID_TAB_CONTROL,
114 };
115
116 enum
117 {
118         TAB_SINGLEPLAYER=0,
119         TAB_MULTIPLAYER,
120         TAB_ADVANCED,
121         TAB_SETTINGS,
122         TAB_CREDITS
123 };
124
125 GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
126                 gui::IGUIElement* parent, s32 id,
127                 IMenuManager *menumgr,
128                 MainMenuData *data,
129                 IGameCallback *gamecallback
130 ):
131         GUIModalMenu(env, parent, id, menumgr),
132         m_data(data),
133         m_accepted(false),
134         m_gamecallback(gamecallback),
135         m_is_regenerating(false)
136 {
137         assert(m_data);
138         this->env = env;
139         this->parent = parent;
140         this->id = id;
141         this->menumgr = menumgr;
142 }
143
144 GUIMainMenu::~GUIMainMenu()
145 {
146         removeChildren();
147 }
148
149 void GUIMainMenu::removeChildren()
150 {
151         const core::list<gui::IGUIElement*> &children = getChildren();
152         core::list<gui::IGUIElement*> children_copy;
153         for(core::list<gui::IGUIElement*>::ConstIterator
154                         i = children.begin(); i != children.end(); i++)
155         {
156                 children_copy.push_back(*i);
157         }
158         for(core::list<gui::IGUIElement*>::Iterator
159                         i = children_copy.begin();
160                         i != children_copy.end(); i++)
161         {
162                 (*i)->remove();
163         }
164 }
165
166 void GUIMainMenu::regenerateGui(v2u32 screensize)
167 {
168         m_is_regenerating = true;
169         /*
170                 Read stuff from elements into m_data
171         */
172         readInput(m_data);
173
174         /*
175                 Remove stuff
176         */
177         removeChildren();
178         
179         /*
180                 Calculate new sizes and positions
181         */
182         
183         v2s32 size(screensize.X, screensize.Y);
184
185         core::rect<s32> rect(
186                         screensize.X/2 - size.X/2,
187                         screensize.Y/2 - size.Y/2,
188                         screensize.X/2 + size.X/2,
189                         screensize.Y/2 + size.Y/2
190         );
191
192         DesiredRect = rect;
193         recalculateAbsolutePosition(false);
194
195         //v2s32 size = rect.getSize();
196
197         /*
198                 Add stuff
199         */
200
201         changeCtype("");
202
203         // Version
204         //if(m_data->selected_tab != TAB_CREDITS)
205         {
206                 core::rect<s32> rect(0, 0, size.X, 40);
207                 rect += v2s32(4, 0);
208                 Environment->addStaticText(narrow_to_wide(
209                                 "Minetest-c55 " VERSION_STRING).c_str(),
210                                 rect, false, true, this, -1);
211         }
212
213         //v2s32 center(size.X/2, size.Y/2);
214         v2s32 c800(size.X/2-400, size.Y/2-300);
215         
216         m_topleft_client = c800 + v2s32(90, 70+50+30);
217         m_size_client = v2s32(620, 270);
218
219         m_size_server = v2s32(620, 140);
220
221         if(m_data->selected_tab == TAB_ADVANCED)
222         {
223                 m_topleft_client = c800 + v2s32(90, 70+50+30);
224                 m_size_client = v2s32(620, 200);
225
226                 m_size_server = v2s32(620, 140);
227         }
228
229         m_topleft_server = m_topleft_client + v2s32(0, m_size_client.Y+20);
230         
231         // Tabs
232 #if 1
233         {
234                 core::rect<s32> rect(0, 0, m_size_client.X, 30);
235                 rect += m_topleft_client + v2s32(0, -30);
236                 gui::IGUITabControl *e = Environment->addTabControl(
237                                 rect, this, true, true, GUI_ID_TAB_CONTROL);
238                 e->addTab(wgettext("Singleplayer"));
239                 e->addTab(wgettext("Multiplayer"));
240                 e->addTab(wgettext("Advanced"));
241                 e->addTab(wgettext("Settings"));
242                 e->addTab(wgettext("Credits"));
243                 e->setActiveTab(m_data->selected_tab);
244         }
245 #endif
246         
247         if(m_data->selected_tab == TAB_SINGLEPLAYER)
248         {
249                 // HYBRID
250                 {
251                         core::rect<s32> rect(0, 0, 10, m_size_client.Y);
252                         rect += m_topleft_client + v2s32(15, 0);
253                         //const wchar_t *text = L"H\nY\nB\nR\nI\nD";
254                         const wchar_t *text = L"T\nA\nP\nE\n\nA\nN\nD\n\nG\nL\nU\nE";
255                         gui::IGUIStaticText *t =
256                         Environment->addStaticText(text, rect, false, false, this, -1);
257                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
258                 }
259                 u32 bs = 5;
260                 // World selection listbox
261                 u32 world_sel_h = 160;
262                 u32 world_sel_w = 365;
263                 //s32 world_sel_x = 50;
264                 s32 world_sel_x = m_size_client.X-world_sel_w-30;
265                 s32 world_sel_y = 30;
266                 u32 world_button_count = 3;
267                 u32 world_button_w = (world_sel_w)/world_button_count - bs
268                                 + bs/(world_button_count-1);
269                 {
270                         core::rect<s32> rect(0, 0, world_sel_w-4, 20);
271                         rect += m_topleft_client + v2s32(world_sel_x+4, world_sel_y-20);
272                         /*gui::IGUIStaticText *e =*/ Environment->addStaticText(
273                                         wgettext("Select World:"), 
274                                         rect, false, true, this, -1);
275                         /*e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);*/
276                 }
277                 {
278                         core::rect<s32> rect(0, 0, world_sel_w, world_sel_h);
279                         rect += m_topleft_client + v2s32(world_sel_x, world_sel_y);
280                         gui::IGUIListBox *e = Environment->addListBox(rect, this,
281                                         GUI_ID_WORLD_LISTBOX);
282                         e->setDrawBackground(true);
283                         for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
284                                         i != m_data->worlds.end(); i++){
285                                 e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
286                         }
287                         e->setSelected(m_data->selected_world);
288                         Environment->setFocus(e);
289                 }
290                 // Delete world button
291                 {
292                         core::rect<s32> rect(0, 0, world_button_w, 30);
293                         rect += m_topleft_client + v2s32(world_sel_x, world_sel_y+world_sel_h+0);
294                         Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
295                                   wgettext("Delete"));
296                 }
297                 // Create world button
298                 {
299                         core::rect<s32> rect(0, 0, world_button_w, 30);
300                         rect += m_topleft_client + v2s32(world_sel_x+world_button_w+bs, world_sel_y+world_sel_h+0);
301                         Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
302                                   wgettext("New"));
303                 }
304                 // Configure world button
305                 {
306                         core::rect<s32> rect(0, 0, world_button_w, 30);
307                         rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*2,
308                                         world_sel_y+world_sel_h+0);
309                         Environment->addButton(rect, this, GUI_ID_CONFIGURE_WORLD_BUTTON,
310                                   wgettext("Configure"));
311                 }
312                 // Start game button
313                 {
314                         /*core::rect<s32> rect(0, 0, world_button_w, 30);
315                         rect += m_topleft_client + v2s32(world_sel_x+(world_button_w+bs)*3,
316                                         world_sel_y+world_sel_h+0);*/
317                         u32 bw = 160;
318                         /*core::rect<s32> rect(0, 0, bw, 30);
319                         rect += m_topleft_client + v2s32(m_size_client.X-bw-30,
320                                         m_size_client.Y-30-15);*/
321                         core::rect<s32> rect(0, 0, bw, 30);
322                         rect += m_topleft_client + v2s32(world_sel_x+world_sel_w-bw,
323                                         world_sel_y+world_sel_h+30+bs);
324                         Environment->addButton(rect, this,
325                                         GUI_ID_JOIN_GAME_BUTTON, wgettext("Play"));
326                 }
327                 // Options
328                 s32 option_x = 50;
329                 //s32 option_x = 50+world_sel_w+20;
330                 s32 option_y = 30;
331                 u32 option_w = 150;
332                 {
333                         core::rect<s32> rect(0, 0, option_w, 30);
334                         rect += m_topleft_client + v2s32(option_x, option_y+20*0);
335                         Environment->addCheckBox(m_data->creative_mode, rect, this,
336                                         GUI_ID_CREATIVE_CB, wgettext("Creative Mode"));
337                 }
338                 {
339                         core::rect<s32> rect(0, 0, option_w, 30);
340                         rect += m_topleft_client + v2s32(option_x, option_y+20*1);
341                         Environment->addCheckBox(m_data->enable_damage, rect, this,
342                                         GUI_ID_DAMAGE_CB, wgettext("Enable Damage"));
343                 }
344                 changeCtype("C");
345         }
346         else if(m_data->selected_tab == TAB_MULTIPLAYER)
347         {
348                 changeCtype("");
349                 // CLIENT
350                 {
351                         core::rect<s32> rect(0, 0, 10, m_size_client.Y);
352                         rect += m_topleft_client + v2s32(15, 0);
353                         const wchar_t *text = L"C\nL\nI\nE\nN\nT";
354                         gui::IGUIStaticText *t =
355                         Environment->addStaticText(text, rect, false, false, this, -1);
356                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
357                 }
358                 // Nickname + password
359                 {
360                         core::rect<s32> rect(0, 0, 110, 20);
361                         rect += m_topleft_client + v2s32(35+30, 50+6);
362                         Environment->addStaticText(wgettext("Name/Password"), 
363                                 rect, false, true, this, -1);
364                 }
365                 changeCtype("C");
366                 {
367                         core::rect<s32> rect(0, 0, 230, 30);
368                         rect += m_topleft_client + v2s32(160+30, 50);
369                         gui::IGUIElement *e = 
370                         Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
371                         if(m_data->name == L"")
372                                 Environment->setFocus(e);
373                 }
374                 {
375                         core::rect<s32> rect(0, 0, 120, 30);
376                         rect += m_topleft_client + v2s32(m_size_client.X-60-100, 50);
377                         gui::IGUIEditBox *e =
378                         Environment->addEditBox(L"", rect, true, this, 264);
379                         e->setPasswordBox(true);
380                         if(m_data->name != L"" && m_data->address != L"")
381                                 Environment->setFocus(e);
382
383                 }
384                 changeCtype("");
385                 // Address + port
386                 {
387                         core::rect<s32> rect(0, 0, 110, 20);
388                         rect += m_topleft_client + v2s32(35+30, 100+6);
389                         Environment->addStaticText(wgettext("Address/Port"),
390                                 rect, false, true, this, -1);
391                 }
392                 changeCtype("C");
393                 {
394                         core::rect<s32> rect(0, 0, 230, 30);
395                         rect += m_topleft_client + v2s32(160+30, 100);
396                         gui::IGUIElement *e = 
397                         Environment->addEditBox(m_data->address.c_str(), rect, true,
398                                         this, GUI_ID_ADDRESS_INPUT);
399                         if(m_data->name != L"" && m_data->address == L"")
400                                 Environment->setFocus(e);
401                 }
402                 {
403                         core::rect<s32> rect(0, 0, 120, 30);
404                         rect += m_topleft_client + v2s32(m_size_client.X-60-100, 100);
405                         Environment->addEditBox(m_data->port.c_str(), rect, true,
406                                         this, GUI_ID_PORT_INPUT);
407                 }
408                 changeCtype("");
409                 // Start game button
410                 {
411                         core::rect<s32> rect(0, 0, 180, 30);
412                         rect += m_topleft_client + v2s32(m_size_client.X-180-30,
413                                         m_size_client.Y-30-15);
414                         Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
415                                 wgettext("Start Game / Connect"));
416                 }
417                 changeCtype("C");
418         }
419         else if(m_data->selected_tab == TAB_ADVANCED)
420         {
421                 changeCtype("");
422                 // CLIENT
423                 {
424                         core::rect<s32> rect(0, 0, 10, m_size_client.Y);
425                         rect += m_topleft_client + v2s32(15, 0);
426                         const wchar_t *text = L"C\nL\nI\nE\nN\nT";
427                         gui::IGUIStaticText *t =
428                         Environment->addStaticText(text, rect, false, false, this, -1);
429                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
430                 }
431                 // Nickname + password
432                 {
433                         core::rect<s32> rect(0, 0, 110, 20);
434                         rect += m_topleft_client + v2s32(35+30, 35+6);
435                         Environment->addStaticText(wgettext("Name/Password"), 
436                                 rect, false, true, this, -1);
437                 }
438                 changeCtype("C");
439                 {
440                         core::rect<s32> rect(0, 0, 230, 30);
441                         rect += m_topleft_client + v2s32(160+30, 35);
442                         gui::IGUIElement *e = 
443                         Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
444                         if(m_data->name == L"")
445                                 Environment->setFocus(e);
446                 }
447                 {
448                         core::rect<s32> rect(0, 0, 120, 30);
449                         rect += m_topleft_client + v2s32(m_size_client.X-60-100, 35);
450                         gui::IGUIEditBox *e =
451                         Environment->addEditBox(L"", rect, true, this, 264);
452                         e->setPasswordBox(true);
453                         if(m_data->name != L"" && m_data->address != L"")
454                                 Environment->setFocus(e);
455
456                 }
457                 changeCtype("");
458                 // Address + port
459                 {
460                         core::rect<s32> rect(0, 0, 110, 20);
461                         rect += m_topleft_client + v2s32(35+30, 75+6);
462                         Environment->addStaticText(wgettext("Address/Port"),
463                                 rect, false, true, this, -1);
464                 }
465                 changeCtype("C");
466                 {
467                         core::rect<s32> rect(0, 0, 230, 30);
468                         rect += m_topleft_client + v2s32(160+30, 75);
469                         gui::IGUIElement *e = 
470                         Environment->addEditBox(m_data->address.c_str(), rect, true,
471                                         this, GUI_ID_ADDRESS_INPUT);
472                         if(m_data->name != L"" && m_data->address == L"")
473                                 Environment->setFocus(e);
474                 }
475                 {
476                         core::rect<s32> rect(0, 0, 120, 30);
477                         rect += m_topleft_client + v2s32(m_size_client.X-60-100, 75);
478                         Environment->addEditBox(m_data->port.c_str(), rect, true,
479                                         this, GUI_ID_PORT_INPUT);
480                 }
481                 changeCtype("");
482                 {
483                         core::rect<s32> rect(0, 0, 400, 20);
484                         rect += m_topleft_client + v2s32(160+30, 75+35);
485                         Environment->addStaticText(wgettext("Leave address blank to start a local server."),
486                                 rect, false, true, this, -1);
487                 }
488                 // Start game button
489                 {
490                         core::rect<s32> rect(0, 0, 180, 30);
491                         rect += m_topleft_client + v2s32(m_size_client.X-180-30,
492                                         m_size_client.Y-30-20);
493                         Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
494                                 wgettext("Start Game / Connect"));
495                 }
496                 /*
497                         Server section
498                 */
499                 // SERVER
500                 {
501                         core::rect<s32> rect(0, 0, 10, m_size_server.Y);
502                         rect += m_topleft_server + v2s32(15, 0);
503                         const wchar_t *text = L"S\nE\nR\nV\nE\nR";
504                         gui::IGUIStaticText *t =
505                         Environment->addStaticText(text, rect, false, false, this, -1);
506                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
507                 }
508                 // Server parameters
509                 {
510                         core::rect<s32> rect(0, 0, 250, 30);
511                         rect += m_topleft_server + v2s32(30+20+250+20, 20);
512                         Environment->addCheckBox(m_data->creative_mode, rect, this, GUI_ID_CREATIVE_CB,
513                                 wgettext("Creative Mode"));
514                 }
515                 {
516                         core::rect<s32> rect(0, 0, 250, 30);
517                         rect += m_topleft_server + v2s32(30+20+250+20, 40);
518                         Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
519                                 wgettext("Enable Damage"));
520                 }
521                 // Delete world button
522                 {
523                         core::rect<s32> rect(0, 0, 130, 30);
524                         rect += m_topleft_server + v2s32(30+20+250+20, 90);
525                         Environment->addButton(rect, this, GUI_ID_DELETE_WORLD_BUTTON,
526                                   wgettext("Delete world"));
527                 }
528                 // Create world button
529                 {
530                         core::rect<s32> rect(0, 0, 130, 30);
531                         rect += m_topleft_server + v2s32(30+20+250+20+140, 90);
532                         Environment->addButton(rect, this, GUI_ID_CREATE_WORLD_BUTTON,
533                                   wgettext("Create world"));
534                 }
535                 // World selection listbox
536                 {
537                         core::rect<s32> rect(0, 0, 250, 120);
538                         rect += m_topleft_server + v2s32(30+20, 10);
539                         gui::IGUIListBox *e = Environment->addListBox(rect, this,
540                                         GUI_ID_WORLD_LISTBOX);
541                         e->setDrawBackground(true);
542                         for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
543                                         i != m_data->worlds.end(); i++){
544                                 e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
545                         }
546                         e->setSelected(m_data->selected_world);
547                 }
548                 changeCtype("C");
549         }
550         else if(m_data->selected_tab == TAB_SETTINGS)
551         {
552                 {
553                         core::rect<s32> rect(0, 0, 10, m_size_client.Y);
554                         rect += m_topleft_client + v2s32(15, 0);
555                         const wchar_t *text = L"S\nE\nT\nT\nI\nN\nG\nS";
556                         gui::IGUIStaticText *t =
557                         Environment->addStaticText(text, rect, false, false, this, -1);
558                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
559                 }
560                 s32 option_x = 70;
561                 s32 option_y = 50;
562                 u32 option_w = 150;
563                 {
564                         core::rect<s32> rect(0, 0, option_w, 30);
565                         rect += m_topleft_client + v2s32(option_x, option_y);
566                         Environment->addCheckBox(m_data->fancy_trees, rect, this,
567                                         GUI_ID_FANCYTREE_CB, wgettext("Fancy trees")); 
568                 }
569                 {
570                         core::rect<s32> rect(0, 0, option_w, 30);
571                         rect += m_topleft_client + v2s32(option_x, option_y+20);
572                         Environment->addCheckBox(m_data->smooth_lighting, rect, this,
573                                         GUI_ID_SMOOTH_LIGHTING_CB, wgettext("Smooth Lighting"));
574                 }
575                 {
576                         core::rect<s32> rect(0, 0, option_w, 30);
577                         rect += m_topleft_client + v2s32(option_x, option_y+20*2);
578                         Environment->addCheckBox(m_data->clouds_3d, rect, this,
579                                         GUI_ID_3D_CLOUDS_CB, wgettext("3D Clouds"));
580                 }
581                 {
582                         core::rect<s32> rect(0, 0, option_w, 30);
583                         rect += m_topleft_client + v2s32(option_x, option_y+20*3);
584                         Environment->addCheckBox(m_data->opaque_water, rect, this,
585                                         GUI_ID_OPAQUE_WATER_CB, wgettext("Opaque water"));
586                 }
587
588
589                 // Anisotropic/mipmap/bi-/trilinear settings
590
591                 {
592                         core::rect<s32> rect(0, 0, option_w+20, 30);
593                         rect += m_topleft_client + v2s32(option_x+175, option_y);
594                         Environment->addCheckBox(m_data->mip_map, rect, this,
595                                        GUI_ID_MIPMAP_CB, wgettext("Mip-Mapping"));
596                 }
597
598                 {
599                         core::rect<s32> rect(0, 0, option_w+20, 30);
600                         rect += m_topleft_client + v2s32(option_x+175, option_y+20);
601                         Environment->addCheckBox(m_data->anisotropic_filter, rect, this,
602                                        GUI_ID_ANISOTROPIC_CB, wgettext("Anisotropic Filtering"));
603                 }
604
605                 {
606                         core::rect<s32> rect(0, 0, option_w+20, 30);
607                         rect += m_topleft_client + v2s32(option_x+175, option_y+20*2);
608                         Environment->addCheckBox(m_data->bilinear_filter, rect, this,
609                                        GUI_ID_BILINEAR_CB, wgettext("Bi-Linear Filtering"));
610                 }
611
612                 {
613                         core::rect<s32> rect(0, 0, option_w+20, 30);
614                         rect += m_topleft_client + v2s32(option_x+175, option_y+20*3);
615                         Environment->addCheckBox(m_data->trilinear_filter, rect, this,
616                                        GUI_ID_TRILINEAR_CB, wgettext("Tri-Linear Filtering"));
617                 }
618
619                 // Key change button
620                 {
621                         core::rect<s32> rect(0, 0, 120, 30);
622                         /*rect += m_topleft_client + v2s32(m_size_client.X-120-30,
623                                         m_size_client.Y-30-20);*/
624                         rect += m_topleft_client + v2s32(option_x, option_y+120);
625                         Environment->addButton(rect, this,
626                                         GUI_ID_CHANGE_KEYS_BUTTON, wgettext("Change keys"));
627                 }
628                 changeCtype("C");
629         }
630         else if(m_data->selected_tab == TAB_CREDITS)
631         {
632                 // CREDITS
633                 {
634                         core::rect<s32> rect(0, 0, 10, m_size_client.Y);
635                         rect += m_topleft_client + v2s32(15, 0);
636                         const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS";
637                         gui::IGUIStaticText *t =
638                         Environment->addStaticText(text, rect, false, false, this, -1);
639                         t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
640                 }
641                 {
642                         core::rect<s32> rect(0, 0, 454, 250);
643                         rect += m_topleft_client + v2s32(110, 50+35);
644                         Environment->addStaticText(narrow_to_wide(
645                         "Minetest-c55 " VERSION_STRING "\n"
646                         "http://minetest.net/\n"
647                         "\n"
648                         "by Perttu Ahola <celeron55@gmail.com>\n"
649                         "and contributors: tango_, kahrl (kaaaaaahrl?), erlehmann (the hippie), SpeedProg, JacobF (sqlite worlds), teddydestodes, marktraceur, darkrose, Jonathan Neuschäfer (who the hell?), Felix Krausse (broke liquids, IIRC), sfan5... and >10 more random people."
650                         ).c_str(), rect, false, true, this, -1);
651                 }
652         }
653
654         m_is_regenerating = false;
655 }
656
657 void GUIMainMenu::drawMenu()
658 {
659         gui::IGUISkin* skin = Environment->getSkin();
660         if (!skin)
661                 return;
662         video::IVideoDriver* driver = Environment->getVideoDriver();
663         
664         /*video::SColor bgcolor(140,0,0,0);
665         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
666
667         video::SColor bgcolor(140,0,0,0);
668
669         if(getTab() == TAB_SINGLEPLAYER)
670         {
671                 {
672                         core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
673                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
674                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
675                 }
676         }
677         else if(getTab() == TAB_MULTIPLAYER)
678         {
679                 {
680                         core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
681                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
682                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
683                 }
684         }
685         else if(getTab() == TAB_ADVANCED)
686         {
687                 {
688                         core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
689                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
690                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
691                 }
692                 {
693                         core::rect<s32> rect(0, 0, m_size_server.X, m_size_server.Y);
694                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_server;
695                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
696                 }
697         }
698         else if(getTab() == TAB_SETTINGS)
699         {
700                 {
701                         core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
702                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
703                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
704                 }
705         }
706         else if(getTab() == TAB_CREDITS)
707         {
708                 {
709                         core::rect<s32> rect(0, 0, m_size_client.X, m_size_client.Y);
710                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
711                         driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
712                 }
713                 video::ITexture *logotexture =
714                                 driver->getTexture(getTexturePath("menulogo.png").c_str());
715                 if(logotexture)
716                 {
717                         v2s32 logosize(logotexture->getOriginalSize().Width,
718                                         logotexture->getOriginalSize().Height);
719                         logosize *= 2;
720                         core::rect<s32> rect(0,0,logosize.X,logosize.Y);
721                         rect += AbsoluteRect.UpperLeftCorner + m_topleft_client;
722                         rect += v2s32(130, 50);
723                         driver->draw2DImage(logotexture, rect,
724                                 core::rect<s32>(core::position2d<s32>(0,0),
725                                 core::dimension2di(logotexture->getSize())),
726                                 NULL, NULL, true);
727                 }
728         }
729
730         gui::IGUIElement::draw();
731 }
732
733 void GUIMainMenu::readInput(MainMenuData *dst)
734 {
735         {
736                 gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
737                 if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
738                         dst->selected_tab = ((gui::IGUITabControl*)e)->getActiveTab();
739         }
740         if(dst->selected_tab == TAB_SINGLEPLAYER)
741         {
742                 dst->simple_singleplayer_mode = true;
743         }
744         else
745         {
746                 dst->simple_singleplayer_mode = false;
747                 {
748                         gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
749                         if(e != NULL)
750                                 dst->name = e->getText();
751                 }
752                 {
753                         gui::IGUIElement *e = getElementFromId(264);
754                         if(e != NULL)
755                                 dst->password = e->getText();
756                 }
757                 {
758                         gui::IGUIElement *e = getElementFromId(GUI_ID_ADDRESS_INPUT);
759                         if(e != NULL)
760                                 dst->address = e->getText();
761                 }
762                 {
763                         gui::IGUIElement *e = getElementFromId(GUI_ID_PORT_INPUT);
764                         if(e != NULL)
765                                 dst->port = e->getText();
766                 }
767         }
768         {
769                 gui::IGUIElement *e = getElementFromId(GUI_ID_CREATIVE_CB);
770                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
771                         dst->creative_mode = ((gui::IGUICheckBox*)e)->isChecked();
772         }
773         {
774                 gui::IGUIElement *e = getElementFromId(GUI_ID_DAMAGE_CB);
775                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
776                         dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
777         }
778         {
779                 gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
780                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
781                         dst->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
782         }
783         {
784                 gui::IGUIElement *e = getElementFromId(GUI_ID_SMOOTH_LIGHTING_CB);
785                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
786                         dst->smooth_lighting = ((gui::IGUICheckBox*)e)->isChecked();
787         }
788         {
789                 gui::IGUIElement *e = getElementFromId(GUI_ID_3D_CLOUDS_CB);
790                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
791                         dst->clouds_3d = ((gui::IGUICheckBox*)e)->isChecked();
792         }
793         {
794                 gui::IGUIElement *e = getElementFromId(GUI_ID_OPAQUE_WATER_CB);
795                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
796                         dst->opaque_water = ((gui::IGUICheckBox*)e)->isChecked();
797         }
798
799         {
800                 gui::IGUIElement *e = getElementFromId(GUI_ID_MIPMAP_CB);
801                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
802                         dst->mip_map = ((gui::IGUICheckBox*)e)->isChecked();
803         }
804
805         {
806                 gui::IGUIElement *e = getElementFromId(GUI_ID_ANISOTROPIC_CB);
807                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
808                         dst->anisotropic_filter = ((gui::IGUICheckBox*)e)->isChecked();
809         }
810
811         {
812                 gui::IGUIElement *e = getElementFromId(GUI_ID_BILINEAR_CB);
813                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
814                         dst->bilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
815         }
816
817         {
818                 gui::IGUIElement *e = getElementFromId(GUI_ID_TRILINEAR_CB);
819                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
820                         dst->trilinear_filter = ((gui::IGUICheckBox*)e)->isChecked();
821         }
822
823         {
824                 gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
825                 if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
826                         dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
827         }
828 }
829
830 void GUIMainMenu::acceptInput()
831 {
832         readInput(m_data);
833         m_accepted = true;
834 }
835
836 bool GUIMainMenu::OnEvent(const SEvent& event)
837 {
838         if(event.EventType==EET_KEY_INPUT_EVENT)
839         {
840                 if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
841                 {
842                         m_gamecallback->exitToOS();
843                         quitMenu();
844                         return true;
845                 }
846                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
847                 {
848                         acceptInput();
849                         quitMenu();
850                         return true;
851                 }
852         }
853         if(event.EventType==EET_GUI_EVENT)
854         {
855                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
856                                 && isVisible())
857                 {
858                         if(!canTakeFocus(event.GUIEvent.Element))
859                         {
860                                 dstream<<"GUIMainMenu: Not allowing focus change."
861                                                 <<std::endl;
862                                 // Returning true disables focus change
863                                 return true;
864                         }
865                 }
866                 if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED)
867                 {
868                         if(!m_is_regenerating)
869                                 regenerateGui(m_screensize_old);
870                         return true;
871                 }
872                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
873                 {
874                         switch(event.GUIEvent.Caller->getID())
875                         {
876                         case GUI_ID_JOIN_GAME_BUTTON: {
877                                 MainMenuData cur;
878                                 readInput(&cur);
879                                 if(cur.address == L"" && getTab() == TAB_MULTIPLAYER){
880                                         (new GUIMessageMenu(env, parent, -1, menumgr,
881                                                         wgettext("Address required."))
882                                                         )->drop();
883                                         return true;
884                                 }
885                                 acceptInput();
886                                 quitMenu();
887                                 return true;
888                         }
889                         case GUI_ID_CHANGE_KEYS_BUTTON: {
890                                 GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(env, parent, -1,menumgr);
891                                 kmenu->drop();
892                                 return true;
893                         }
894                         case GUI_ID_DELETE_WORLD_BUTTON: {
895                                 MainMenuData cur;
896                                 readInput(&cur);
897                                 if(cur.selected_world == -1){
898                                         (new GUIMessageMenu(env, parent, -1, menumgr,
899                                                         wgettext("Cannot delete world: Nothing selected"))
900                                                         )->drop();
901                                 } else {
902                                         WorldSpec spec = m_data->worlds[cur.selected_world];
903                                         // Get files and directories involved
904                                         std::vector<std::string> paths;
905                                         paths.push_back(spec.path);
906                                         fs::GetRecursiveSubPaths(spec.path, paths);
907                                         // Launch confirmation dialog
908                                         ConfirmDestDeleteWorld *dest = new
909                                                         ConfirmDestDeleteWorld(spec, this, paths);
910                                         std::wstring text = wgettext("Delete world");
911                                         text += L" \"";
912                                         text += narrow_to_wide(spec.name);
913                                         text += L"\"?\n\n";
914                                         text += wgettext("Files to be deleted");
915                                         text += L":\n";
916                                         for(u32 i=0; i<paths.size(); i++){
917                                                 if(i == 3){ text += L"..."; break; }
918                                                 text += narrow_to_wide(paths[i]) + L"\n";
919                                         }
920                                         (new GUIConfirmMenu(env, parent, -1, menumgr, dest,
921                                                         text.c_str()))->drop();
922                                 }
923                                 return true;
924                         }
925                         case GUI_ID_CREATE_WORLD_BUTTON: {
926                                 std::vector<SubgameSpec> games = getAvailableGames();
927                                 if(games.size() == 0){
928                                         GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
929                                                         -1, menumgr,
930                                                         wgettext("Cannot create world: No games found"));
931                                         menu->drop();
932                                 } else {
933                                         CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
934                                         GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
935                                                         menumgr, dest, games);
936                                         menu->drop();
937                                 }
938                                 return true;
939                         }
940                         case GUI_ID_CONFIGURE_WORLD_BUTTON: {
941                                 GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
942                                                 -1, menumgr,
943                                                 wgettext("Nothing here"));
944                                 menu->drop();
945                                 return true;
946                         }
947                         }
948                 }
949                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
950                 {
951                         switch(event.GUIEvent.Caller->getID())
952                         {
953                                 case GUI_ID_ADDRESS_INPUT: case GUI_ID_PORT_INPUT: case GUI_ID_NAME_INPUT: case 264:
954                                 acceptInput();
955                                 quitMenu();
956                                 return true;
957                         }
958                 }
959                 if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
960                 {
961                         switch(event.GUIEvent.Caller->getID())
962                         {
963                         case GUI_ID_WORLD_LISTBOX:
964                                 acceptInput();
965                                 if(getTab() != TAB_SINGLEPLAYER)
966                                         m_data->address = L""; // Force local game
967                                 quitMenu();
968                                 return true;
969                         }
970                 }
971         }
972
973         return Parent ? Parent->OnEvent(event) : false;
974 }
975
976 void GUIMainMenu::createNewWorld(std::wstring name, std::string gameid)
977 {
978         if(name == L"")
979                 return;
980         acceptInput();
981         m_data->create_world_name = name;
982         m_data->create_world_gameid = gameid;
983         quitMenu();
984 }
985
986 void GUIMainMenu::deleteWorld(const std::vector<std::string> &paths)
987 {
988         // Delete files
989         bool did = fs::DeletePaths(paths);
990         if(!did){
991                 GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
992                                 -1, menumgr, wgettext("Failed to delete all world files"));
993                 menu->drop();
994         }
995         // Quit menu to refresh it
996         acceptInput();
997         m_data->only_refresh = true;
998         quitMenu();
999 }
1000         
1001 int GUIMainMenu::getTab()
1002 {
1003         gui::IGUIElement *e = getElementFromId(GUI_ID_TAB_CONTROL);
1004         if(e != NULL && e->getType() == gui::EGUIET_TAB_CONTROL)
1005                 return ((gui::IGUITabControl*)e)->getActiveTab();
1006         return TAB_SINGLEPLAYER; // Default
1007 }
1008
1009 void GUIMainMenu::displayMessageMenu(std::wstring msg)
1010 {
1011         (new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
1012 }