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