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