]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiKeyChangeMenu.cpp
Formspec: Unify argument checks (#11851)
[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_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         removeChildren();
97
98         for (key_setting *ks : key_settings) {
99                 delete[] ks->button_name;
100                 delete ks;
101         }
102         key_settings.clear();
103 }
104
105 void GUIKeyChangeMenu::removeChildren()
106 {
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);
111         }
112
113         for (gui::IGUIElement *i : children_copy) {
114                 i->remove();
115         }
116         key_used_text = nullptr;
117 }
118
119 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
120 {
121         removeChildren();
122
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
129         );
130         recalculateAbsolutePosition(false);
131
132         v2s32 size = DesiredRect.getSize();
133         v2s32 topleft(0, 0);
134
135         {
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);
142                 delete[] text;
143                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
144         }
145
146         // Build buttons
147
148         v2s32 offset(25 * s, 60 * s);
149
150         for(size_t i = 0; i < key_settings.size(); i++)
151         {
152                 key_setting *k = key_settings.at(i);
153                 {
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);
157                 }
158
159                 {
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);
164                         delete[] text;
165                 }
166                 if ((i + 1) % KMaxButtonPerColumns == 0) {
167                         offset.X += 260 * s;
168                         offset.Y = 60 * s;
169                 } else {
170                         offset += v2s32(0, 25 * s);
171                 }
172         }
173
174         {
175                 s32 option_x = offset.X;
176                 s32 option_y = offset.Y + 5 * s;
177                 u32 option_w = 180 * s;
178                 {
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);
184                         delete[] text;
185                 }
186                 offset += v2s32(0, 25 * s);
187         }
188
189         {
190                 s32 option_x = offset.X;
191                 s32 option_y = offset.Y + 5 * s;
192                 u32 option_w = 280 * s;
193                 {
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);
199                         delete[] text;
200                 }
201                 offset += v2s32(0, 25 * s);
202         }
203
204         {
205                 s32 option_x = offset.X;
206                 s32 option_y = offset.Y + 5 * s;
207                 u32 option_w = 280;
208                 {
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);
214                         delete[] text;
215                 }
216                 offset += v2s32(0, 25);
217         }
218
219         {
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);
224                 delete[] text;
225         }
226         {
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);
231                 delete[] text;
232         }
233 }
234
235 void GUIKeyChangeMenu::drawMenu()
236 {
237         gui::IGUISkin* skin = Environment->getSkin();
238         if (!skin)
239                 return;
240         video::IVideoDriver* driver = Environment->getVideoDriver();
241
242         video::SColor bgcolor(140, 0, 0, 0);
243         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
244
245         gui::IGUIElement::draw();
246 }
247
248 bool GUIKeyChangeMenu::acceptInput()
249 {
250         for (key_setting *k : key_settings) {
251                 std::string default_key;
252                 Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
253
254                 if (k->key.sym() != default_key)
255                         g_settings->set(k->setting_name, k->key.sym());
256                 else
257                         g_settings->remove(k->setting_name);
258         }
259
260         {
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());
264         }
265         {
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());
269         }
270         {
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());
274         }
275
276         clearKeyCache();
277
278         g_gamecallback->signalKeyConfigChange();
279
280         return true;
281 }
282
283 bool GUIKeyChangeMenu::resetMenu()
284 {
285         if (active_key) {
286                 const wchar_t *text = wgettext(active_key->key.name());
287                 active_key->button->setText(text);
288                 delete[] text;
289                 active_key = nullptr;
290                 return false;
291         }
292         return true;
293 }
294 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
295 {
296         if (event.EventType == EET_KEY_INPUT_EVENT && active_key
297                         && event.KeyInput.PressedDown) {
298
299                 bool prefer_character = shift_down;
300                 KeyPress kp(event.KeyInput, prefer_character);
301
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
306
307                 bool shift_went_down = false;
308                 if(!shift_down &&
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;
313
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) {
319                                         key_in_use = true;
320                                         break;
321                                 }
322                         }
323                 }
324
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);
331                         delete[] text;
332                 } else if (!key_in_use && this->key_used_text) {
333                         this->key_used_text->remove();
334                         this->key_used_text = nullptr;
335                 }
336
337                 // But go on
338                 {
339                         active_key->key = kp;
340                         const wchar_t *text = wgettext(kp.name());
341                         active_key->button->setText(text);
342                         delete[] text;
343
344                         // Allow characters made with shift
345                         if (shift_went_down){
346                                 shift_down = true;
347                                 return false;
348                         }
349
350                         active_key = nullptr;
351                         return true;
352                 }
353         } else if (event.EventType == EET_KEY_INPUT_EVENT && !active_key
354                         && event.KeyInput.PressedDown
355                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
356                 quitMenu();
357                 return true;
358         } else if (event.EventType == EET_GUI_EVENT) {
359                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
360                         && isVisible())
361                 {
362                         if (!canTakeFocus(event.GUIEvent.Element))
363                         {
364                                 infostream << "GUIKeyChangeMenu: Not allowing focus change."
365                                 << std::endl;
366                                 // Returning true disables focus change
367                                 return true;
368                         }
369                 }
370                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
371                 {
372                         switch (event.GUIEvent.Caller->getID())
373                         {
374                                 case GUI_ID_BACK_BUTTON: //back
375                                         acceptInput();
376                                         quitMenu();
377                                         return true;
378                                 case GUI_ID_ABORT_BUTTON: //abort
379                                         quitMenu();
380                                         return true;
381                                 default:
382                                         resetMenu();
383                                         for (key_setting *ks : key_settings) {
384                                                 if (ks->id == event.GUIEvent.Caller->getID()) {
385                                                         active_key = ks;
386                                                         break;
387                                                 }
388                                         }
389                                         FATAL_ERROR_IF(!active_key, "Key setting not found");
390
391                                         shift_down = false;
392                                         const wchar_t *text = wgettext("press key");
393                                         active_key->button->setText(text);
394                                         delete[] text;
395                                         break;
396                         }
397                         Environment->setFocus(this);
398                 }
399         }
400         return Parent ? Parent->OnEvent(event) : false;
401 }
402
403 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
404 {
405         key_setting *k = new key_setting;
406         k->id = id;
407
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);
412 }
413
414 void GUIKeyChangeMenu::init_keys()
415 {
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");
450 }