]> git.lizzy.rs Git - minetest.git/blob - src/guiKeyChangeMenu.cpp
Isolate irrlicht references and use a singleton (#6041)
[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 <string>
26 #include <IGUICheckBox.h>
27 #include <IGUIEditBox.h>
28 #include <IGUIButton.h>
29 #include <IGUIStaticText.h>
30 #include <IGUIFont.h>
31 #include "settings.h"
32 #include <algorithm>
33
34 #include "mainmenumanager.h"  // for g_gamecallback
35
36 #define KMaxButtonPerColumns 12
37
38 extern MainGameCallback *g_gamecallback;
39
40 enum
41 {
42         GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
43         // buttons
44         GUI_ID_KEY_FORWARD_BUTTON,
45         GUI_ID_KEY_BACKWARD_BUTTON,
46         GUI_ID_KEY_LEFT_BUTTON,
47         GUI_ID_KEY_RIGHT_BUTTON,
48         GUI_ID_KEY_USE_BUTTON,
49         GUI_ID_KEY_FLY_BUTTON,
50         GUI_ID_KEY_FAST_BUTTON,
51         GUI_ID_KEY_JUMP_BUTTON,
52         GUI_ID_KEY_NOCLIP_BUTTON,
53         GUI_ID_KEY_CINEMATIC_BUTTON,
54         GUI_ID_KEY_CHAT_BUTTON,
55         GUI_ID_KEY_CMD_BUTTON,
56         GUI_ID_KEY_CMD_LOCAL_BUTTON,
57         GUI_ID_KEY_CONSOLE_BUTTON,
58         GUI_ID_KEY_SNEAK_BUTTON,
59         GUI_ID_KEY_DROP_BUTTON,
60         GUI_ID_KEY_INVENTORY_BUTTON,
61         GUI_ID_KEY_HOTBAR_PREV_BUTTON,
62         GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
63         GUI_ID_KEY_MUTE_BUTTON,
64         GUI_ID_KEY_DEC_VOLUME_BUTTON,
65         GUI_ID_KEY_INC_VOLUME_BUTTON,
66         GUI_ID_KEY_DUMP_BUTTON,
67         GUI_ID_KEY_RANGE_BUTTON,
68         GUI_ID_KEY_ZOOM_BUTTON,
69         // other
70         GUI_ID_CB_AUX1_DESCENDS,
71         GUI_ID_CB_DOUBLETAP_JUMP,
72 };
73
74 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
75                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
76 GUIModalMenu(env, parent, id, menumgr)
77 {
78         init_keys();
79         for (size_t i = 0; i < key_settings.size(); i++)
80                 key_used.push_back(key_settings.at(i)->key);
81 }
82
83 GUIKeyChangeMenu::~GUIKeyChangeMenu()
84 {
85         removeChildren();
86
87         for (std::vector<key_setting*>::iterator iter = key_settings.begin();
88                         iter != key_settings.end(); ++iter) {
89                 delete[] (*iter)->button_name;
90                 delete (*iter);
91         }
92         key_settings.clear();
93 }
94
95 void GUIKeyChangeMenu::removeChildren()
96 {
97         const core::list<gui::IGUIElement*> &children = getChildren();
98         core::list<gui::IGUIElement*> children_copy;
99         for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
100                  != children.end(); i++)
101         {
102                 children_copy.push_back(*i);
103         }
104         for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
105                  != children_copy.end(); i++)
106         {
107                 (*i)->remove();
108         }
109 }
110
111 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
112 {
113         removeChildren();
114         v2s32 size(745, 430);
115
116         core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
117                                                         screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
118                                                         screensize.Y / 2 + size.Y / 2);
119
120         DesiredRect = rect;
121         recalculateAbsolutePosition(false);
122
123         v2s32 topleft(0, 0);
124
125         {
126                 core::rect < s32 > rect(0, 0, 600, 40);
127                 rect += topleft + v2s32(25, 3);
128                 //gui::IGUIStaticText *t =
129                 const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
130                 Environment->addStaticText(text,
131                                                                    rect, false, true, this, -1);
132                 delete[] text;
133                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
134         }
135
136         // Build buttons
137
138         v2s32 offset(25, 60);
139
140         for(size_t i = 0; i < key_settings.size(); i++)
141         {
142                 key_setting *k = key_settings.at(i);
143                 {
144                         core::rect < s32 > rect(0, 0, 150, 20);
145                         rect += topleft + v2s32(offset.X, offset.Y);
146                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
147                 }
148
149                 {
150                         core::rect < s32 > rect(0, 0, 100, 30);
151                         rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
152                         const wchar_t *text = wgettext(k->key.name());
153                         k->button = Environment->addButton(rect, this, k->id, text);
154                         delete[] text;
155                 }
156                 if ((i + 1) % KMaxButtonPerColumns == 0) {
157                         offset.X += 230;
158                         offset.Y = 60;
159                 } else {
160                         offset += v2s32(0, 25);
161                 }
162         }
163
164         {
165                 s32 option_x = offset.X;
166                 s32 option_y = offset.Y + 5;
167                 u32 option_w = 180;
168                 {
169                         core::rect<s32> rect(0, 0, option_w, 30);
170                         rect += topleft + v2s32(option_x, option_y);
171                         const wchar_t *text = wgettext("\"Use\" = climb down");
172                         Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
173                                         GUI_ID_CB_AUX1_DESCENDS, text);
174                         delete[] text;
175                 }
176                 offset += v2s32(0, 25);
177         }
178
179         {
180                 s32 option_x = offset.X;
181                 s32 option_y = offset.Y + 5;
182                 u32 option_w = 280;
183                 {
184                         core::rect<s32> rect(0, 0, option_w, 30);
185                         rect += topleft + v2s32(option_x, option_y);
186                         const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
187                         Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
188                                         GUI_ID_CB_DOUBLETAP_JUMP, text);
189                         delete[] text;
190                 }
191                 offset += v2s32(0, 25);
192         }
193
194         {
195                 core::rect < s32 > rect(0, 0, 100, 30);
196                 rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
197                 const wchar_t *text =  wgettext("Save");
198                 Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
199                                  text);
200                 delete[] text;
201         }
202         {
203                 core::rect < s32 > rect(0, 0, 100, 30);
204                 rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
205                 const wchar_t *text = wgettext("Cancel");
206                 Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
207                                 text);
208                 delete[] text;
209         }
210 }
211
212 void GUIKeyChangeMenu::drawMenu()
213 {
214         gui::IGUISkin* skin = Environment->getSkin();
215         if (!skin)
216                 return;
217         video::IVideoDriver* driver = Environment->getVideoDriver();
218
219         video::SColor bgcolor(140, 0, 0, 0);
220
221         {
222                 core::rect < s32 > rect(0, 0, 745, 620);
223                 rect += AbsoluteRect.UpperLeftCorner;
224                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
225         }
226
227         gui::IGUIElement::draw();
228 }
229
230 bool GUIKeyChangeMenu::acceptInput()
231 {
232         for(size_t i = 0; i < key_settings.size(); i++)
233         {
234                 key_setting *k = key_settings.at(i);
235                 g_settings->set(k->setting_name, k->key.sym());
236         }
237         {
238                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
239                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
240                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
241         }
242         {
243                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
244                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
245                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
246         }
247
248         clearKeyCache();
249
250         g_gamecallback->signalKeyConfigChange();
251
252         return true;
253 }
254
255 bool GUIKeyChangeMenu::resetMenu()
256 {
257         if (activeKey >= 0)
258         {
259                 for(size_t i = 0; i < key_settings.size(); i++)
260                 {
261                         key_setting *k = key_settings.at(i);
262                         if(k->id == activeKey)
263                         {
264                                 const wchar_t *text = wgettext(k->key.name());
265                                 k->button->setText(text);
266                                 delete[] text;
267                                 break;
268                         }
269                 }
270                 activeKey = -1;
271                 return false;
272         }
273         return true;
274 }
275 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
276 {
277         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
278                         && event.KeyInput.PressedDown) {
279
280                 bool prefer_character = shift_down;
281                 KeyPress kp(event.KeyInput, prefer_character);
282
283                 bool shift_went_down = false;
284                 if(!shift_down &&
285                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
286                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
287                                 event.KeyInput.Key == irr::KEY_RSHIFT))
288                         shift_went_down = true;
289
290                 // Remove Key already in use message
291                 if(this->key_used_text)
292                 {
293                         this->key_used_text->remove();
294                         this->key_used_text = NULL;
295                 }
296                 // Display Key already in use message
297                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
298                 {
299                         core::rect < s32 > rect(0, 0, 600, 40);
300                         rect += v2s32(0, 0) + v2s32(25, 30);
301                         const wchar_t *text = wgettext("Key already in use");
302                         this->key_used_text = Environment->addStaticText(text,
303                                         rect, false, true, this, -1);
304                         delete[] text;
305                         //infostream << "Key already in use" << std::endl;
306                 }
307
308                 // But go on
309                 {
310                         key_setting *k = NULL;
311                         for(size_t i = 0; i < key_settings.size(); i++)
312                         {
313                                 if(key_settings.at(i)->id == activeKey)
314                                 {
315                                         k = key_settings.at(i);
316                                         break;
317                                 }
318                         }
319                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
320                         k->key = kp;
321                         const wchar_t *text = wgettext(k->key.name());
322                         k->button->setText(text);
323                         delete[] text;
324
325                         this->key_used.push_back(kp);
326
327                         // Allow characters made with shift
328                         if(shift_went_down){
329                                 shift_down = true;
330                                 return false;
331                         }else{
332                                 activeKey = -1;
333                                 return true;
334                         }
335                 }
336         } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
337                         && event.KeyInput.PressedDown
338                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
339                 quitMenu();
340                 return true;
341         } else if (event.EventType == EET_GUI_EVENT) {
342                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
343                         && isVisible())
344                 {
345                         if (!canTakeFocus(event.GUIEvent.Element))
346                         {
347                                 dstream << "GUIMainMenu: Not allowing focus change."
348                                 << std::endl;
349                                 // Returning true disables focus change
350                                 return true;
351                         }
352                 }
353                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
354                 {
355                         switch (event.GUIEvent.Caller->getID())
356                         {
357                                 case GUI_ID_BACK_BUTTON: //back
358                                         acceptInput();
359                                         quitMenu();
360                                         return true;
361                                 case GUI_ID_ABORT_BUTTON: //abort
362                                         quitMenu();
363                                         return true;
364                                 default:
365                                         key_setting *k = NULL;
366                                         for(size_t i = 0; i < key_settings.size(); i++)
367                                         {
368                                                 if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
369                                                 {
370                                                         k = key_settings.at(i);
371                                                         break;
372                                                 }
373                                         }
374                                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
375
376                                         resetMenu();
377                                         shift_down = false;
378                                         activeKey = event.GUIEvent.Caller->getID();
379                                         const wchar_t *text = wgettext("press key");
380                                         k->button->setText(text);
381                                         delete[] text;
382                                         this->key_used.erase(std::remove(this->key_used.begin(),
383                                                         this->key_used.end(), k->key), this->key_used.end());
384                                         break;
385                         }
386                         Environment->setFocus(this);
387                 }
388         }
389         return Parent ? Parent->OnEvent(event) : false;
390 }
391
392 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
393 {
394         key_setting *k = new key_setting;
395         k->id = id;
396
397         k->button_name = button_name;
398         k->setting_name = setting_name;
399         k->key = getKeySetting(k->setting_name.c_str());
400         key_settings.push_back(k);
401 }
402
403 void GUIKeyChangeMenu::init_keys()
404 {
405         this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
406         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
407         this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
408         this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
409         this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Use"),              "keymap_special1");
410         this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
411         this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
412         this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
413         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
414         this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
415         this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
416         this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
417         this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
418         this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
419         this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
420         this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
421         this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
422         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
423         this->add_key(GUI_ID_KEY_FLY_BUTTON,       wgettext("Toggle fly"),       "keymap_freemove");
424         this->add_key(GUI_ID_KEY_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
425         this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
426         this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
427         this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
428         this->add_key(GUI_ID_KEY_DUMP_BUTTON,      wgettext("Print stacks"),     "keymap_print_debug_stacks");
429         this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
430 }
431