]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiKeyChangeMenu.cpp
Formspecs: volume and key settings windows can now be closed by doubleclicking/tappin...
[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_USE_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_HUD_BUTTON,
74         GUI_ID_KEY_FOG_BUTTON,
75         GUI_ID_KEY_DEC_RANGE_BUTTON,
76         GUI_ID_KEY_INC_RANGE_BUTTON,
77         GUI_ID_KEY_AUTOFWD_BUTTON,
78         // other
79         GUI_ID_CB_AUX1_DESCENDS,
80         GUI_ID_CB_DOUBLETAP_JUMP,
81         GUI_ID_CB_AUTOJUMP,
82 };
83
84 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
85                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
86                 ISimpleTextureSource *tsrc) :
87                 GUIModalMenu(env, parent, id, menumgr),
88                 m_tsrc(tsrc)
89 {
90         init_keys();
91 }
92
93 GUIKeyChangeMenu::~GUIKeyChangeMenu()
94 {
95         removeChildren();
96
97         for (key_setting *ks : key_settings) {
98                 delete[] ks->button_name;
99                 delete ks;
100         }
101         key_settings.clear();
102 }
103
104 void GUIKeyChangeMenu::removeChildren()
105 {
106         const core::list<gui::IGUIElement*> &children = getChildren();
107         core::list<gui::IGUIElement*> children_copy;
108         for (gui::IGUIElement*i : children) {
109                 children_copy.push_back(i);
110         }
111
112         for (gui::IGUIElement *i : children_copy) {
113                 i->remove();
114         }
115         key_used_text = nullptr;
116 }
117
118 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
119 {
120         removeChildren();
121
122         const float s = m_gui_scale;
123         DesiredRect = core::rect<s32>(
124                 screensize.X / 2 - 835 * s / 2,
125                 screensize.Y / 2 - 430 * s / 2,
126                 screensize.X / 2 + 835 * s / 2,
127                 screensize.Y / 2 + 430 * s / 2
128         );
129         recalculateAbsolutePosition(false);
130
131         v2s32 size = DesiredRect.getSize();
132         v2s32 topleft(0, 0);
133
134         {
135                 core::rect<s32> rect(0, 0, 600 * s, 40 * s);
136                 rect += topleft + v2s32(25 * s, 3 * s);
137                 //gui::IGUIStaticText *t =
138                 const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
139                 Environment->addStaticText(text,
140                                                                    rect, false, true, this, -1);
141                 delete[] text;
142                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
143         }
144
145         // Build buttons
146
147         v2s32 offset(25 * s, 60 * s);
148
149         for(size_t i = 0; i < key_settings.size(); i++)
150         {
151                 key_setting *k = key_settings.at(i);
152                 {
153                         core::rect<s32> rect(0, 0, 150 * s, 20 * s);
154                         rect += topleft + v2s32(offset.X, offset.Y);
155                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
156                 }
157
158                 {
159                         core::rect<s32> rect(0, 0, 100 * s, 30 * s);
160                         rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
161                         const wchar_t *text = wgettext(k->key.name());
162                         k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text);
163                         delete[] text;
164                 }
165                 if ((i + 1) % KMaxButtonPerColumns == 0) {
166                         offset.X += 260 * s;
167                         offset.Y = 60 * s;
168                 } else {
169                         offset += v2s32(0, 25 * s);
170                 }
171         }
172
173         {
174                 s32 option_x = offset.X;
175                 s32 option_y = offset.Y + 5 * s;
176                 u32 option_w = 180 * s;
177                 {
178                         core::rect<s32> rect(0, 0, option_w, 30 * s);
179                         rect += topleft + v2s32(option_x, option_y);
180                         const wchar_t *text = wgettext("\"Special\" = climb down");
181                         Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
182                                         GUI_ID_CB_AUX1_DESCENDS, text);
183                         delete[] text;
184                 }
185                 offset += v2s32(0, 25 * s);
186         }
187
188         {
189                 s32 option_x = offset.X;
190                 s32 option_y = offset.Y + 5 * s;
191                 u32 option_w = 280 * s;
192                 {
193                         core::rect<s32> rect(0, 0, option_w, 30 * s);
194                         rect += topleft + v2s32(option_x, option_y);
195                         const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
196                         Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
197                                         GUI_ID_CB_DOUBLETAP_JUMP, text);
198                         delete[] text;
199                 }
200                 offset += v2s32(0, 25 * s);
201         }
202
203         {
204                 s32 option_x = offset.X;
205                 s32 option_y = offset.Y + 5 * s;
206                 u32 option_w = 280;
207                 {
208                         core::rect<s32> rect(0, 0, option_w, 30 * s);
209                         rect += topleft + v2s32(option_x, option_y);
210                         const wchar_t *text = wgettext("Automatic jumping");
211                         Environment->addCheckBox(g_settings->getBool("autojump"), rect, this,
212                                         GUI_ID_CB_AUTOJUMP, text);
213                         delete[] text;
214                 }
215                 offset += v2s32(0, 25);
216         }
217
218         {
219                 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
220                 rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s);
221                 const wchar_t *text =  wgettext("Save");
222                 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text);
223                 delete[] text;
224         }
225         {
226                 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
227                 rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s);
228                 const wchar_t *text = wgettext("Cancel");
229                 GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text);
230                 delete[] text;
231         }
232 }
233
234 void GUIKeyChangeMenu::drawMenu()
235 {
236         gui::IGUISkin* skin = Environment->getSkin();
237         if (!skin)
238                 return;
239         video::IVideoDriver* driver = Environment->getVideoDriver();
240
241         video::SColor bgcolor(140, 0, 0, 0);
242         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
243
244         gui::IGUIElement::draw();
245 }
246
247 bool GUIKeyChangeMenu::acceptInput()
248 {
249         for (key_setting *k : key_settings) {
250                 std::string default_key;
251                 g_settings->getDefaultNoEx(k->setting_name, default_key);
252
253                 if (k->key.sym() != default_key)
254                         g_settings->set(k->setting_name, k->key.sym());
255                 else
256                         g_settings->remove(k->setting_name);
257         }
258
259         {
260                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
261                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
262                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
263         }
264         {
265                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
266                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
267                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
268         }
269         {
270                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUTOJUMP);
271                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
272                         g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
273         }
274
275         clearKeyCache();
276
277         g_gamecallback->signalKeyConfigChange();
278
279         return true;
280 }
281
282 bool GUIKeyChangeMenu::resetMenu()
283 {
284         if (active_key) {
285                 const wchar_t *text = wgettext(active_key->key.name());
286                 active_key->button->setText(text);
287                 delete[] text;
288                 active_key = nullptr;
289                 return false;
290         }
291         return true;
292 }
293 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
294 {
295         if (event.EventType == EET_KEY_INPUT_EVENT && active_key
296                         && event.KeyInput.PressedDown) {
297
298                 bool prefer_character = shift_down;
299                 KeyPress kp(event.KeyInput, prefer_character);
300
301                 if (event.KeyInput.Key == irr::KEY_DELETE)
302                         kp = KeyPress(""); // To erase key settings
303                 else if (event.KeyInput.Key == irr::KEY_ESCAPE)
304                         kp = active_key->key; // Cancel
305
306                 bool shift_went_down = false;
307                 if(!shift_down &&
308                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
309                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
310                                 event.KeyInput.Key == irr::KEY_RSHIFT))
311                         shift_went_down = true;
312
313                 // Display Key already in use message
314                 bool key_in_use = false;
315                 if (strcmp(kp.sym(), "") != 0) {
316                         for (key_setting *ks : key_settings) {
317                                 if (ks != active_key && ks->key == kp) {
318                                         key_in_use = true;
319                                         break;
320                                 }
321                         }
322                 }
323
324                 if (key_in_use && !this->key_used_text) {
325                         core::rect<s32> rect(0, 0, 600, 40);
326                         rect += v2s32(0, 0) + v2s32(25, 30);
327                         const wchar_t *text = wgettext("Key already in use");
328                         this->key_used_text = Environment->addStaticText(text,
329                                         rect, false, true, this, -1);
330                         delete[] text;
331                 } else if (!key_in_use && this->key_used_text) {
332                         this->key_used_text->remove();
333                         this->key_used_text = nullptr;
334                 }
335
336                 // But go on
337                 {
338                         active_key->key = kp;
339                         const wchar_t *text = wgettext(kp.name());
340                         active_key->button->setText(text);
341                         delete[] text;
342
343                         // Allow characters made with shift
344                         if (shift_went_down){
345                                 shift_down = true;
346                                 return false;
347                         }
348
349                         active_key = nullptr;
350                         return true;
351                 }
352         } else if (event.EventType == EET_KEY_INPUT_EVENT && !active_key
353                         && event.KeyInput.PressedDown
354                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
355                 quitMenu();
356                 return true;
357         } else if (event.EventType == EET_GUI_EVENT) {
358                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
359                         && isVisible())
360                 {
361                         if (!canTakeFocus(event.GUIEvent.Element))
362                         {
363                                 infostream << "GUIKeyChangeMenu: Not allowing focus change."
364                                 << std::endl;
365                                 // Returning true disables focus change
366                                 return true;
367                         }
368                 }
369                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
370                 {
371                         switch (event.GUIEvent.Caller->getID())
372                         {
373                                 case GUI_ID_BACK_BUTTON: //back
374                                         acceptInput();
375                                         quitMenu();
376                                         return true;
377                                 case GUI_ID_ABORT_BUTTON: //abort
378                                         quitMenu();
379                                         return true;
380                                 default:
381                                         resetMenu();
382                                         for (key_setting *ks : key_settings) {
383                                                 if (ks->id == event.GUIEvent.Caller->getID()) {
384                                                         active_key = ks;
385                                                         break;
386                                                 }
387                                         }
388                                         FATAL_ERROR_IF(!active_key, "Key setting not found");
389
390                                         shift_down = false;
391                                         const wchar_t *text = wgettext("press key");
392                                         active_key->button->setText(text);
393                                         delete[] text;
394                                         break;
395                         }
396                         Environment->setFocus(this);
397                 }
398         }
399         return Parent ? Parent->OnEvent(event) : false;
400 }
401
402 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
403 {
404         key_setting *k = new key_setting;
405         k->id = id;
406
407         k->button_name = button_name;
408         k->setting_name = setting_name;
409         k->key = getKeySetting(k->setting_name.c_str());
410         key_settings.push_back(k);
411 }
412
413 void GUIKeyChangeMenu::init_keys()
414 {
415         this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
416         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
417         this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
418         this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
419         this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Special"),          "keymap_special1");
420         this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
421         this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
422         this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
423         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
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_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
446         this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
447         this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_fog");
448 }