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