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>
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.
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.
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.
22 #include "guiKeyChangeMenu.h"
24 #include "guiButton.h"
25 #include "serialization.h"
27 #include <IGUICheckBox.h>
28 #include <IGUIEditBox.h>
29 #include <IGUIButton.h>
30 #include <IGUIStaticText.h>
35 #include "mainmenumanager.h" // for g_gamecallback
37 #define KMaxButtonPerColumns 12
39 extern MainGameCallback *g_gamecallback;
43 GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
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,
80 GUI_ID_CB_AUX1_DESCENDS,
81 GUI_ID_CB_DOUBLETAP_JUMP,
85 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
86 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
87 ISimpleTextureSource *tsrc) :
88 GUIModalMenu(env, parent, id, menumgr),
94 GUIKeyChangeMenu::~GUIKeyChangeMenu()
98 for (key_setting *ks : key_settings) {
99 delete[] ks->button_name;
102 key_settings.clear();
105 void GUIKeyChangeMenu::removeChildren()
107 const core::list<gui::IGUIElement*> &children = getChildren();
108 core::list<gui::IGUIElement*> children_copy;
109 for (gui::IGUIElement*i : children) {
110 children_copy.push_back(i);
113 for (gui::IGUIElement *i : children_copy) {
116 key_used_text = nullptr;
119 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
123 const float s = m_gui_scale;
124 DesiredRect = core::rect<s32>(
125 screensize.X / 2 - 835 * s / 2,
126 screensize.Y / 2 - 430 * s / 2,
127 screensize.X / 2 + 835 * s / 2,
128 screensize.Y / 2 + 430 * s / 2
130 recalculateAbsolutePosition(false);
132 v2s32 size = DesiredRect.getSize();
136 core::rect<s32> rect(0, 0, 600 * s, 40 * s);
137 rect += topleft + v2s32(25 * s, 3 * s);
138 //gui::IGUIStaticText *t =
139 const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
140 Environment->addStaticText(text,
141 rect, false, true, this, -1);
143 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
148 v2s32 offset(25 * s, 60 * s);
150 for(size_t i = 0; i < key_settings.size(); i++)
152 key_setting *k = key_settings.at(i);
154 core::rect<s32> rect(0, 0, 150 * s, 20 * s);
155 rect += topleft + v2s32(offset.X, offset.Y);
156 Environment->addStaticText(k->button_name, rect, false, true, this, -1);
160 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
161 rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
162 const wchar_t *text = wgettext(k->key.name());
163 k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text);
166 if ((i + 1) % KMaxButtonPerColumns == 0) {
170 offset += v2s32(0, 25 * s);
175 s32 option_x = offset.X;
176 s32 option_y = offset.Y + 5 * s;
177 u32 option_w = 180 * s;
179 core::rect<s32> rect(0, 0, option_w, 30 * s);
180 rect += topleft + v2s32(option_x, option_y);
181 const wchar_t *text = wgettext("\"Aux1\" = climb down");
182 Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
183 GUI_ID_CB_AUX1_DESCENDS, text);
186 offset += v2s32(0, 25 * s);
190 s32 option_x = offset.X;
191 s32 option_y = offset.Y + 5 * s;
192 u32 option_w = 280 * s;
194 core::rect<s32> rect(0, 0, option_w, 30 * s);
195 rect += topleft + v2s32(option_x, option_y);
196 const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
197 Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
198 GUI_ID_CB_DOUBLETAP_JUMP, text);
201 offset += v2s32(0, 25 * s);
205 s32 option_x = offset.X;
206 s32 option_y = offset.Y + 5 * s;
209 core::rect<s32> rect(0, 0, option_w, 30 * s);
210 rect += topleft + v2s32(option_x, option_y);
211 const wchar_t *text = wgettext("Automatic jumping");
212 Environment->addCheckBox(g_settings->getBool("autojump"), rect, this,
213 GUI_ID_CB_AUTOJUMP, text);
216 offset += v2s32(0, 25);
220 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
221 rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s);
222 const wchar_t *text = wgettext("Save");
223 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text);
227 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
228 rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s);
229 const wchar_t *text = wgettext("Cancel");
230 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text);
235 void GUIKeyChangeMenu::drawMenu()
237 gui::IGUISkin* skin = Environment->getSkin();
240 video::IVideoDriver* driver = Environment->getVideoDriver();
242 video::SColor bgcolor(140, 0, 0, 0);
243 driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
245 gui::IGUIElement::draw();
248 bool GUIKeyChangeMenu::acceptInput()
250 for (key_setting *k : key_settings) {
251 std::string default_key;
252 Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
254 if (k->key.sym() != default_key)
255 g_settings->set(k->setting_name, k->key.sym());
257 g_settings->remove(k->setting_name);
261 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
262 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
263 g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
266 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
267 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
268 g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
271 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUTOJUMP);
272 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
273 g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
278 g_gamecallback->signalKeyConfigChange();
283 bool GUIKeyChangeMenu::resetMenu()
286 const wchar_t *text = wgettext(active_key->key.name());
287 active_key->button->setText(text);
289 active_key = nullptr;
294 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
296 if (event.EventType == EET_KEY_INPUT_EVENT && active_key
297 && event.KeyInput.PressedDown) {
299 bool prefer_character = shift_down;
300 KeyPress kp(event.KeyInput, prefer_character);
302 if (event.KeyInput.Key == irr::KEY_DELETE)
303 kp = KeyPress(""); // To erase key settings
304 else if (event.KeyInput.Key == irr::KEY_ESCAPE)
305 kp = active_key->key; // Cancel
307 bool shift_went_down = false;
309 (event.KeyInput.Key == irr::KEY_SHIFT ||
310 event.KeyInput.Key == irr::KEY_LSHIFT ||
311 event.KeyInput.Key == irr::KEY_RSHIFT))
312 shift_went_down = true;
314 // Display Key already in use message
315 bool key_in_use = false;
316 if (strcmp(kp.sym(), "") != 0) {
317 for (key_setting *ks : key_settings) {
318 if (ks != active_key && ks->key == kp) {
325 if (key_in_use && !this->key_used_text) {
326 core::rect<s32> rect(0, 0, 600, 40);
327 rect += v2s32(0, 0) + v2s32(25, 30);
328 const wchar_t *text = wgettext("Key already in use");
329 this->key_used_text = Environment->addStaticText(text,
330 rect, false, true, this, -1);
332 } else if (!key_in_use && this->key_used_text) {
333 this->key_used_text->remove();
334 this->key_used_text = nullptr;
339 active_key->key = kp;
340 const wchar_t *text = wgettext(kp.name());
341 active_key->button->setText(text);
344 // Allow characters made with shift
345 if (shift_went_down){
350 active_key = nullptr;
353 } else if (event.EventType == EET_KEY_INPUT_EVENT && !active_key
354 && event.KeyInput.PressedDown
355 && event.KeyInput.Key == irr::KEY_ESCAPE) {
358 } else if (event.EventType == EET_GUI_EVENT) {
359 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
362 if (!canTakeFocus(event.GUIEvent.Element))
364 infostream << "GUIKeyChangeMenu: Not allowing focus change."
366 // Returning true disables focus change
370 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
372 switch (event.GUIEvent.Caller->getID())
374 case GUI_ID_BACK_BUTTON: //back
378 case GUI_ID_ABORT_BUTTON: //abort
383 for (key_setting *ks : key_settings) {
384 if (ks->id == event.GUIEvent.Caller->getID()) {
389 FATAL_ERROR_IF(!active_key, "Key setting not found");
392 const wchar_t *text = wgettext("press key");
393 active_key->button->setText(text);
397 Environment->setFocus(this);
400 return Parent ? Parent->OnEvent(event) : false;
403 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
405 key_setting *k = new key_setting;
408 k->button_name = button_name;
409 k->setting_name = setting_name;
410 k->key = getKeySetting(k->setting_name.c_str());
411 key_settings.push_back(k);
414 void GUIKeyChangeMenu::init_keys()
416 this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward");
417 this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward");
418 this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left");
419 this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right");
420 this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1");
421 this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump");
422 this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak");
423 this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop");
424 this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory");
425 this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON, wgettext("Prev. item"), "keymap_hotbar_previous");
426 this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON, wgettext("Next item"), "keymap_hotbar_next");
427 this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom");
428 this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode");
429 this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap");
430 this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove");
431 this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove");
432 this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove");
433 this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip");
434 this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute");
435 this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON, wgettext("Dec. volume"), "keymap_decrease_volume");
436 this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON, wgettext("Inc. volume"), "keymap_increase_volume");
437 this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward");
438 this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat");
439 this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON, wgettext("Screenshot"), "keymap_screenshot");
440 this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect");
441 this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min");
442 this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min");
443 this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console");
444 this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd");
445 this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local");
446 this->add_key(GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, wgettext("Block bounds"), "keymap_toggle_block_bounds");
447 this->add_key(GUI_ID_KEY_HUD_BUTTON, wgettext("Toggle HUD"), "keymap_toggle_hud");
448 this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat");
449 this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog");