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_SWAP_OFFHAND_BUTTON,
62 GUI_ID_KEY_INVENTORY_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_DEC_RANGE_BUTTON,
78 GUI_ID_KEY_INC_RANGE_BUTTON,
79 GUI_ID_KEY_AUTOFWD_BUTTON,
81 GUI_ID_CB_AUX1_DESCENDS,
82 GUI_ID_CB_DOUBLETAP_JUMP,
86 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
87 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
88 ISimpleTextureSource *tsrc) :
89 GUIModalMenu(env, parent, id, menumgr),
95 GUIKeyChangeMenu::~GUIKeyChangeMenu()
98 key_used_text = nullptr;
100 for (key_setting *ks : key_settings) {
101 delete[] ks->button_name;
104 key_settings.clear();
107 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
110 key_used_text = nullptr;
112 const float s = m_gui_scale;
113 DesiredRect = core::rect<s32>(
114 screensize.X / 2 - 835 * s / 2,
115 screensize.Y / 2 - 430 * s / 2,
116 screensize.X / 2 + 835 * s / 2,
117 screensize.Y / 2 + 430 * s / 2
119 recalculateAbsolutePosition(false);
121 v2s32 size = DesiredRect.getSize();
125 core::rect<s32> rect(0, 0, 600 * s, 40 * s);
126 rect += topleft + v2s32(25 * s, 3 * s);
127 //gui::IGUIStaticText *t =
128 const wchar_t *text = wgettext("Keybindings.");
129 Environment->addStaticText(text,
130 rect, false, true, this, -1);
132 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
137 v2s32 offset(25 * s, 60 * s);
139 for(size_t i = 0; i < key_settings.size(); i++)
141 key_setting *k = key_settings.at(i);
143 core::rect<s32> rect(0, 0, 150 * s, 20 * s);
144 rect += topleft + v2s32(offset.X, offset.Y);
145 Environment->addStaticText(k->button_name, rect, false, true, this, -1);
149 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
150 rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
151 const wchar_t *text = wgettext(k->key.name());
152 k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text);
155 if ((i + 1) % KMaxButtonPerColumns == 0) {
159 offset += v2s32(0, 25 * s);
164 s32 option_x = offset.X;
165 s32 option_y = offset.Y + 5 * s;
166 u32 option_w = 180 * s;
168 core::rect<s32> rect(0, 0, option_w, 30 * s);
169 rect += topleft + v2s32(option_x, option_y);
170 const wchar_t *text = wgettext("\"Aux1\" = climb down");
171 Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
172 GUI_ID_CB_AUX1_DESCENDS, text);
175 offset += v2s32(0, 25 * s);
179 s32 option_x = offset.X;
180 s32 option_y = offset.Y + 5 * s;
181 u32 option_w = 280 * s;
183 core::rect<s32> rect(0, 0, option_w, 30 * s);
184 rect += topleft + v2s32(option_x, option_y);
185 const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
186 Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
187 GUI_ID_CB_DOUBLETAP_JUMP, text);
190 offset += v2s32(0, 25 * s);
194 s32 option_x = offset.X;
195 s32 option_y = offset.Y + 5 * s;
198 core::rect<s32> rect(0, 0, option_w, 30 * s);
199 rect += topleft + v2s32(option_x, option_y);
200 const wchar_t *text = wgettext("Automatic jumping");
201 Environment->addCheckBox(g_settings->getBool("autojump"), rect, this,
202 GUI_ID_CB_AUTOJUMP, text);
205 offset += v2s32(0, 25);
209 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
210 rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s);
211 const wchar_t *text = wgettext("Save");
212 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text);
216 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
217 rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s);
218 const wchar_t *text = wgettext("Cancel");
219 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text);
224 void GUIKeyChangeMenu::drawMenu()
226 gui::IGUISkin* skin = Environment->getSkin();
229 video::IVideoDriver* driver = Environment->getVideoDriver();
231 video::SColor bgcolor(140, 0, 0, 0);
232 driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
234 gui::IGUIElement::draw();
237 bool GUIKeyChangeMenu::acceptInput()
239 for (key_setting *k : key_settings) {
240 std::string default_key;
241 Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
243 if (k->key.sym() != default_key)
244 g_settings->set(k->setting_name, k->key.sym());
246 g_settings->remove(k->setting_name);
250 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
251 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
252 g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
255 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
256 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
257 g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
260 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUTOJUMP);
261 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
262 g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
267 g_gamecallback->signalKeyConfigChange();
272 bool GUIKeyChangeMenu::resetMenu()
275 const wchar_t *text = wgettext(active_key->key.name());
276 active_key->button->setText(text);
278 active_key = nullptr;
283 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
285 if (event.EventType == EET_KEY_INPUT_EVENT && active_key
286 && event.KeyInput.PressedDown) {
288 bool prefer_character = shift_down;
289 KeyPress kp(event.KeyInput, prefer_character);
291 if (event.KeyInput.Key == irr::KEY_DELETE)
292 kp = KeyPress(""); // To erase key settings
293 else if (event.KeyInput.Key == irr::KEY_ESCAPE)
294 kp = active_key->key; // Cancel
296 bool shift_went_down = false;
298 (event.KeyInput.Key == irr::KEY_SHIFT ||
299 event.KeyInput.Key == irr::KEY_LSHIFT ||
300 event.KeyInput.Key == irr::KEY_RSHIFT))
301 shift_went_down = true;
303 // Display Key already in use message
304 bool key_in_use = false;
305 if (strcmp(kp.sym(), "") != 0) {
306 for (key_setting *ks : key_settings) {
307 if (ks != active_key && ks->key == kp) {
314 if (key_in_use && !this->key_used_text) {
315 core::rect<s32> rect(0, 0, 600, 40);
316 rect += v2s32(0, 0) + v2s32(25, 30);
317 const wchar_t *text = wgettext("Key already in use");
318 this->key_used_text = Environment->addStaticText(text,
319 rect, false, true, this, -1);
321 } else if (!key_in_use && this->key_used_text) {
322 this->key_used_text->remove();
323 this->key_used_text = nullptr;
328 active_key->key = kp;
329 const wchar_t *text = wgettext(kp.name());
330 active_key->button->setText(text);
333 // Allow characters made with shift
334 if (shift_went_down){
339 active_key = nullptr;
342 } else if (event.EventType == EET_KEY_INPUT_EVENT && !active_key
343 && event.KeyInput.PressedDown
344 && event.KeyInput.Key == irr::KEY_ESCAPE) {
347 } else if (event.EventType == EET_GUI_EVENT) {
348 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
351 if (!canTakeFocus(event.GUIEvent.Element))
353 infostream << "GUIKeyChangeMenu: Not allowing focus change."
355 // Returning true disables focus change
359 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
361 switch (event.GUIEvent.Caller->getID())
363 case GUI_ID_BACK_BUTTON: //back
367 case GUI_ID_ABORT_BUTTON: //abort
372 for (key_setting *ks : key_settings) {
373 if (ks->id == event.GUIEvent.Caller->getID()) {
378 FATAL_ERROR_IF(!active_key, "Key setting not found");
381 const wchar_t *text = wgettext("press key");
382 active_key->button->setText(text);
386 Environment->setFocus(this);
389 return Parent ? Parent->OnEvent(event) : false;
392 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
394 key_setting *k = new key_setting;
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);
403 void GUIKeyChangeMenu::init_keys()
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_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1");
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_SWAP_OFFHAND_BUTTON, wgettext("Swap hand items"), "keymap_swap_offhand");
414 this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory");
415 this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON, wgettext("Prev. item"), "keymap_hotbar_previous");
416 this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON, wgettext("Next item"), "keymap_hotbar_next");
417 this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom");
418 this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode");
419 this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap");
420 this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove");
421 this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove");
422 this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove");
423 this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip");
424 this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute");
425 this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON, wgettext("Dec. volume"), "keymap_decrease_volume");
426 this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON, wgettext("Inc. volume"), "keymap_increase_volume");
427 this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward");
428 this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat");
429 this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON, wgettext("Screenshot"), "keymap_screenshot");
430 this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect");
431 this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min");
432 this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min");
433 this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console");
434 this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd");
435 this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local");
436 this->add_key(GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, wgettext("Block bounds"), "keymap_toggle_block_bounds");
437 this->add_key(GUI_ID_KEY_HUD_BUTTON, wgettext("Toggle HUD"), "keymap_toggle_hud");
438 this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat");
439 this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog");