Re-add jungles, apple trees
[oweals/minetest.git] / src / guiKeyChangeMenu.cpp
1 /*
2  Minetest
3  Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4  Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
5  Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation; either version 2.1 of the License, or
10  (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU Lesser General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License along
18  with this program; if not, write to the Free Software Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "guiKeyChangeMenu.h"
23 #include "debug.h"
24 #include "serialization.h"
25 #include "main.h"
26 #include <string>
27 #include <IGUICheckBox.h>
28 #include <IGUIEditBox.h>
29 #include <IGUIButton.h>
30 #include <IGUIStaticText.h>
31 #include <IGUIFont.h>
32 #include "settings.h"
33 #include <algorithm>
34
35 #define KMaxButtonPerColumns 12
36
37 enum
38 {
39         GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
40         // buttons
41         GUI_ID_KEY_FORWARD_BUTTON,
42         GUI_ID_KEY_BACKWARD_BUTTON,
43         GUI_ID_KEY_LEFT_BUTTON,
44         GUI_ID_KEY_RIGHT_BUTTON,
45         GUI_ID_KEY_USE_BUTTON,
46         GUI_ID_KEY_FLY_BUTTON,
47         GUI_ID_KEY_FAST_BUTTON,
48         GUI_ID_KEY_JUMP_BUTTON,
49         GUI_ID_KEY_NOCLIP_BUTTON,
50         GUI_ID_KEY_CHAT_BUTTON,
51         GUI_ID_KEY_CMD_BUTTON,
52         GUI_ID_KEY_CONSOLE_BUTTON,
53         GUI_ID_KEY_SNEAK_BUTTON,
54         GUI_ID_KEY_DROP_BUTTON,
55         GUI_ID_KEY_INVENTORY_BUTTON,
56         GUI_ID_KEY_DUMP_BUTTON,
57         GUI_ID_KEY_RANGE_BUTTON,
58         // other
59         GUI_ID_CB_AUX1_DESCENDS,
60         GUI_ID_CB_DOUBLETAP_JUMP,
61 };
62
63 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
64                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
65 GUIModalMenu(env, parent, id, menumgr)
66 {
67         shift_down = false;
68         activeKey = -1;
69         this->key_used_text = NULL;
70         init_keys();
71         for(size_t i=0; i<key_settings.size(); i++)
72                 this->key_used.push_back(key_settings.at(i)->key);
73 }
74
75 GUIKeyChangeMenu::~GUIKeyChangeMenu()
76 {
77         removeChildren();
78 }
79
80 void GUIKeyChangeMenu::removeChildren()
81 {
82         const core::list<gui::IGUIElement*> &children = getChildren();
83         core::list<gui::IGUIElement*> children_copy;
84         for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
85                  != children.end(); i++)
86         {
87                 children_copy.push_back(*i);
88         }
89         for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
90                  != children_copy.end(); i++)
91         {
92                 (*i)->remove();
93         }
94 }
95
96 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
97 {
98         removeChildren();
99         v2s32 size(620, 430);
100         
101         core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
102                                                         screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
103                                                         screensize.Y / 2 + size.Y / 2);
104
105         DesiredRect = rect;
106         recalculateAbsolutePosition(false);
107
108         v2s32 topleft(0, 0);
109         changeCtype("");
110         {
111                 core::rect < s32 > rect(0, 0, 600, 40);
112                 rect += topleft + v2s32(25, 3);
113                 //gui::IGUIStaticText *t =
114                 Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"),
115                                                                    rect, false, true, this, -1);
116                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
117         }
118
119         // Build buttons
120
121         v2s32 offset(25, 60);
122
123         for(size_t i = 0; i < key_settings.size(); i++)
124         {
125                 key_setting *k = key_settings.at(i);
126                 {
127                         core::rect < s32 > rect(0, 0, 100, 20);
128                         rect += topleft + v2s32(offset.X, offset.Y);
129                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
130                 }
131
132                 {
133                         core::rect < s32 > rect(0, 0, 100, 30);
134                         rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
135                         k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name()));
136                 }
137                 if(i + 1 == KMaxButtonPerColumns)
138                         offset = v2s32(250, 60);
139                 else
140                         offset += v2s32(0, 25);
141         }
142         
143         {
144                 s32 option_x = offset.X + 10;
145                 s32 option_y = offset.Y;
146                 u32 option_w = 180;
147                 {
148                         core::rect<s32> rect(0, 0, option_w, 30);
149                         rect += topleft + v2s32(option_x, option_y);
150                         Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
151                                         GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down"));
152                 }
153                 offset += v2s32(0, 25);
154         }
155
156         {
157                 s32 option_x = offset.X + 10;
158                 s32 option_y = offset.Y;
159                 u32 option_w = 220;
160                 {
161                         core::rect<s32> rect(0, 0, option_w, 30);
162                         rect += topleft + v2s32(option_x, option_y);
163                         Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
164                                         GUI_ID_CB_DOUBLETAP_JUMP, wgettext("Double tap \"jump\" to toggle fly"));
165                 }
166                 offset += v2s32(0, 25);
167         }
168
169         {
170                 core::rect < s32 > rect(0, 0, 100, 30);
171                 rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
172                 Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
173                                                            wgettext("Save"));
174         }
175         {
176                 core::rect < s32 > rect(0, 0, 100, 30);
177                 rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
178                 Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
179                                                            wgettext("Cancel"));
180         }
181         changeCtype("C");
182         
183 }
184
185 void GUIKeyChangeMenu::drawMenu()
186 {
187         gui::IGUISkin* skin = Environment->getSkin();
188         if (!skin)
189                 return;
190         video::IVideoDriver* driver = Environment->getVideoDriver();
191
192         video::SColor bgcolor(140, 0, 0, 0);
193
194         {
195                 core::rect < s32 > rect(0, 0, 620, 620);
196                 rect += AbsoluteRect.UpperLeftCorner;
197                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
198         }
199
200         gui::IGUIElement::draw();
201 }
202
203 bool GUIKeyChangeMenu::acceptInput()
204 {
205         for(size_t i = 0; i < key_settings.size(); i++)
206         {
207                 key_setting *k = key_settings.at(i);
208                 g_settings->set(k->setting_name, k->key.sym());
209         }
210         {
211                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
212                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
213                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
214         }
215         {
216                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
217                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
218                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
219         }
220         clearKeyCache();
221         return true;
222 }
223
224 bool GUIKeyChangeMenu::resetMenu()
225 {
226         if (activeKey >= 0)
227         {
228                 for(size_t i = 0; i < key_settings.size(); i++)
229                 {
230                         key_setting *k = key_settings.at(i);
231                         if(k->id == activeKey)
232                         {
233                                 k->button->setText(wgettext(k->key.name()));
234                                 break;
235                         }
236                 }
237                 activeKey = -1;
238                 return false;
239         }
240         return true;
241 }
242 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
243 {
244         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
245                 && event.KeyInput.PressedDown)
246         {
247                 changeCtype("");
248                 bool prefer_character = shift_down;
249                 KeyPress kp(event.KeyInput, prefer_character);
250                 
251                 bool shift_went_down = false;
252                 if(!shift_down &&
253                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
254                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
255                                 event.KeyInput.Key == irr::KEY_RSHIFT))
256                         shift_went_down = true;
257
258                 // Remove Key already in use message
259                 if(this->key_used_text)
260                 {
261                         this->key_used_text->remove();
262                         this->key_used_text = NULL;
263                 }
264                 // Display Key already in use message
265                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
266                 {
267                         core::rect < s32 > rect(0, 0, 600, 40);
268                         rect += v2s32(0, 0) + v2s32(25, 30);
269                         this->key_used_text = Environment->addStaticText(wgettext("Key already in use"),
270                                                                         rect, false, true, this, -1);
271                         //infostream << "Key already in use" << std::endl;
272                 }
273
274                 // But go on
275                 {
276                         key_setting *k=NULL;
277                         for(size_t i = 0; i < key_settings.size(); i++)
278                         {
279                                 if(key_settings.at(i)->id == activeKey)
280                                 {
281                                         k = key_settings.at(i);
282                                         break;
283                                 }
284                         }
285                         assert(k);
286                         k->key = kp;
287                         k->button->setText(wgettext(k->key.name()));
288
289                         this->key_used.push_back(kp);
290
291                         changeCtype("C");
292                         // Allow characters made with shift
293                         if(shift_went_down){
294                                 shift_down = true;
295                                 return false;
296                         }else{
297                                 activeKey = -1;
298                                 return true;
299                         }
300                 }
301         }
302         if (event.EventType == EET_GUI_EVENT)
303         {
304                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
305                         && isVisible())
306                 {
307                         if (!canTakeFocus(event.GUIEvent.Element))
308                         {
309                                 dstream << "GUIMainMenu: Not allowing focus change."
310                                 << std::endl;
311                                 // Returning true disables focus change
312                                 return true;
313                         }
314                 }
315                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
316                 {
317                         if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON &&
318                            event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
319                         {
320                                 changeCtype("");
321                         }
322
323                         switch (event.GUIEvent.Caller->getID())
324                         {
325                                 case GUI_ID_BACK_BUTTON: //back
326                                         acceptInput();
327                                         quitMenu();
328                                         return true;
329                                 case GUI_ID_ABORT_BUTTON: //abort
330                                         quitMenu();
331                                         return true;
332                                 default:
333                                         key_setting *k = NULL;
334                                         for(size_t i = 0; i < key_settings.size(); i++)
335                                         {
336                                                 if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
337                                                 {
338                                                         k = key_settings.at(i);
339                                                         break;
340                                                 }
341                                         }
342                                         assert(k);
343
344                                         resetMenu();
345                                         shift_down = false;
346                                         activeKey = event.GUIEvent.Caller->getID();
347                                         k->button->setText(wgettext("press key"));
348                                         this->key_used.erase(std::remove(this->key_used.begin(),
349                                                         this->key_used.end(), k->key), this->key_used.end());
350                                         break;
351                         }
352                         Environment->setFocus(this);
353                         //Buttons
354                         changeCtype("C");
355                 }
356         }
357         return Parent ? Parent->OnEvent(event) : false;
358 }
359
360 void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name)
361 {
362         key_setting *k = new key_setting;
363         k->id = id;
364         k->button_name = wgettext(button_name.c_str());
365         k->setting_name = setting_name;
366         k->key = getKeySetting(k->setting_name.c_str());
367         key_settings.push_back(k);
368 }
369
370 void GUIKeyChangeMenu::init_keys()
371 {
372         this->add_key(GUI_ID_KEY_FORWARD_BUTTON, gettext("Forward"), "keymap_forward");
373         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, gettext("Backward"), "keymap_backward");
374         this->add_key(GUI_ID_KEY_LEFT_BUTTON, gettext("Left"), "keymap_left");
375         this->add_key(GUI_ID_KEY_RIGHT_BUTTON, gettext("Right"), "keymap_right");
376         this->add_key(GUI_ID_KEY_USE_BUTTON, gettext("Use"), "keymap_special1");
377         this->add_key(GUI_ID_KEY_JUMP_BUTTON, gettext("Jump"), "keymap_jump");
378         this->add_key(GUI_ID_KEY_SNEAK_BUTTON, gettext("Sneak"), "keymap_sneak");
379         this->add_key(GUI_ID_KEY_DROP_BUTTON, gettext("Drop"), "keymap_drop");
380         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, gettext("Inventory"), "keymap_inventory");
381         this->add_key(GUI_ID_KEY_CHAT_BUTTON, gettext("Chat"), "keymap_chat");
382         this->add_key(GUI_ID_KEY_CMD_BUTTON, gettext("Command"), "keymap_cmd");
383         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, gettext("Console"), "keymap_console");
384         this->add_key(GUI_ID_KEY_FLY_BUTTON, gettext("Toggle fly"), "keymap_freemove");
385         this->add_key(GUI_ID_KEY_FAST_BUTTON, gettext("Toggle fast"), "keymap_fastmove");
386         this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, gettext("Toggle noclip"), "keymap_noclip");
387         this->add_key(GUI_ID_KEY_RANGE_BUTTON, gettext("Range select"), "keymap_rangeselect");
388         this->add_key(GUI_ID_KEY_DUMP_BUTTON, gettext("Print stacks"), "keymap_print_debug_stacks");
389 }