]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiKeyChangeMenu.cpp
ca97e0b9eb590f66456d9b30450a20129e19e426
[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 "serialization.h"
25 #include <string>
26 #include <IGUICheckBox.h>
27 #include <IGUIEditBox.h>
28 #include <IGUIButton.h>
29 #include <IGUIStaticText.h>
30 #include <IGUIFont.h>
31 #include "settings.h"
32 #include <algorithm>
33
34 #include "mainmenumanager.h"  // for g_gamecallback
35
36 #define KMaxButtonPerColumns 12
37
38 extern MainGameCallback *g_gamecallback;
39
40 enum
41 {
42         GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
43         // buttons
44         GUI_ID_KEY_FORWARD_BUTTON,
45         GUI_ID_KEY_BACKWARD_BUTTON,
46         GUI_ID_KEY_LEFT_BUTTON,
47         GUI_ID_KEY_RIGHT_BUTTON,
48         GUI_ID_KEY_USE_BUTTON,
49         GUI_ID_KEY_FLY_BUTTON,
50         GUI_ID_KEY_FAST_BUTTON,
51         GUI_ID_KEY_JUMP_BUTTON,
52         GUI_ID_KEY_NOCLIP_BUTTON,
53         GUI_ID_KEY_CINEMATIC_BUTTON,
54         GUI_ID_KEY_CHAT_BUTTON,
55         GUI_ID_KEY_CMD_BUTTON,
56         GUI_ID_KEY_CMD_LOCAL_BUTTON,
57         GUI_ID_KEY_CONSOLE_BUTTON,
58         GUI_ID_KEY_SNEAK_BUTTON,
59         GUI_ID_KEY_DROP_BUTTON,
60         GUI_ID_KEY_INVENTORY_BUTTON,
61         GUI_ID_KEY_HOTBAR_PREV_BUTTON,
62         GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
63         GUI_ID_KEY_MUTE_BUTTON,
64         GUI_ID_KEY_DEC_VOLUME_BUTTON,
65         GUI_ID_KEY_INC_VOLUME_BUTTON,
66         GUI_ID_KEY_RANGE_BUTTON,
67         GUI_ID_KEY_ZOOM_BUTTON,
68         GUI_ID_KEY_CAMERA_BUTTON,
69         GUI_ID_KEY_MINIMAP_BUTTON,
70         GUI_ID_KEY_SCREENSHOT_BUTTON,
71         GUI_ID_KEY_CHATLOG_BUTTON,
72         GUI_ID_KEY_HUD_BUTTON,
73         GUI_ID_KEY_FOG_BUTTON,
74         GUI_ID_KEY_DEC_RANGE_BUTTON,
75         GUI_ID_KEY_INC_RANGE_BUTTON,
76         GUI_ID_KEY_AUTOFWD_BUTTON,
77         // other
78         GUI_ID_CB_AUX1_DESCENDS,
79         GUI_ID_CB_DOUBLETAP_JUMP,
80 };
81
82 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
83                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
84 GUIModalMenu(env, parent, id, menumgr)
85 {
86         init_keys();
87         for (key_setting *ks : key_settings)
88                 key_used.push_back(ks->key);
89 }
90
91 GUIKeyChangeMenu::~GUIKeyChangeMenu()
92 {
93         removeChildren();
94
95         for (key_setting *ks : key_settings) {
96                 delete[] ks->button_name;
97                 delete ks;
98         }
99         key_settings.clear();
100 }
101
102 void GUIKeyChangeMenu::removeChildren()
103 {
104         const core::list<gui::IGUIElement*> &children = getChildren();
105         core::list<gui::IGUIElement*> children_copy;
106         for (gui::IGUIElement*i : children) {
107                 children_copy.push_back(i);
108         }
109
110         for (gui::IGUIElement *i : children_copy) {
111                 i->remove();
112         }
113 }
114
115 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
116 {
117         removeChildren();
118         v2s32 size(745, 430);
119
120         core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
121                                                         screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
122                                                         screensize.Y / 2 + size.Y / 2);
123
124         DesiredRect = rect;
125         recalculateAbsolutePosition(false);
126
127         v2s32 topleft(0, 0);
128
129         {
130                 core::rect < s32 > rect(0, 0, 600, 40);
131                 rect += topleft + v2s32(25, 3);
132                 //gui::IGUIStaticText *t =
133                 const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
134                 Environment->addStaticText(text,
135                                                                    rect, false, true, this, -1);
136                 delete[] text;
137                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
138         }
139
140         // Build buttons
141
142         v2s32 offset(25, 60);
143
144         for(size_t i = 0; i < key_settings.size(); i++)
145         {
146                 key_setting *k = key_settings.at(i);
147                 {
148                         core::rect < s32 > rect(0, 0, 150, 20);
149                         rect += topleft + v2s32(offset.X, offset.Y);
150                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
151                 }
152
153                 {
154                         core::rect < s32 > rect(0, 0, 100, 30);
155                         rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
156                         const wchar_t *text = wgettext(k->key.name());
157                         k->button = Environment->addButton(rect, this, k->id, text);
158                         delete[] text;
159                 }
160                 if ((i + 1) % KMaxButtonPerColumns == 0) {
161                         offset.X += 230;
162                         offset.Y = 60;
163                 } else {
164                         offset += v2s32(0, 25);
165                 }
166         }
167
168         {
169                 s32 option_x = offset.X;
170                 s32 option_y = offset.Y + 5;
171                 u32 option_w = 180;
172                 {
173                         core::rect<s32> rect(0, 0, option_w, 30);
174                         rect += topleft + v2s32(option_x, option_y);
175                         const wchar_t *text = wgettext("\"Special\" = climb down");
176                         Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
177                                         GUI_ID_CB_AUX1_DESCENDS, text);
178                         delete[] text;
179                 }
180                 offset += v2s32(0, 25);
181         }
182
183         {
184                 s32 option_x = offset.X;
185                 s32 option_y = offset.Y + 5;
186                 u32 option_w = 280;
187                 {
188                         core::rect<s32> rect(0, 0, option_w, 30);
189                         rect += topleft + v2s32(option_x, option_y);
190                         const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
191                         Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
192                                         GUI_ID_CB_DOUBLETAP_JUMP, text);
193                         delete[] text;
194                 }
195                 offset += v2s32(0, 25);
196         }
197
198         {
199                 core::rect < s32 > rect(0, 0, 100, 30);
200                 rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
201                 const wchar_t *text =  wgettext("Save");
202                 Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
203                                  text);
204                 delete[] text;
205         }
206         {
207                 core::rect < s32 > rect(0, 0, 100, 30);
208                 rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
209                 const wchar_t *text = wgettext("Cancel");
210                 Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
211                                 text);
212                 delete[] text;
213         }
214 }
215
216 void GUIKeyChangeMenu::drawMenu()
217 {
218         gui::IGUISkin* skin = Environment->getSkin();
219         if (!skin)
220                 return;
221         video::IVideoDriver* driver = Environment->getVideoDriver();
222
223         video::SColor bgcolor(140, 0, 0, 0);
224
225         {
226                 core::rect < s32 > rect(0, 0, 745, 620);
227                 rect += AbsoluteRect.UpperLeftCorner;
228                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
229         }
230
231         gui::IGUIElement::draw();
232 }
233
234 bool GUIKeyChangeMenu::acceptInput()
235 {
236         for (key_setting *k : key_settings) {
237                 g_settings->set(k->setting_name, k->key.sym());
238         }
239
240         {
241                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
242                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
243                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
244         }
245         {
246                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
247                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
248                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
249         }
250
251         clearKeyCache();
252
253         g_gamecallback->signalKeyConfigChange();
254
255         return true;
256 }
257
258 bool GUIKeyChangeMenu::resetMenu()
259 {
260         if (activeKey >= 0)
261         {
262                 for (key_setting *k : key_settings) {
263                         if (k->id == activeKey) {
264                                 const wchar_t *text = wgettext(k->key.name());
265                                 k->button->setText(text);
266                                 delete[] text;
267                                 break;
268                         }
269                 }
270                 activeKey = -1;
271                 return false;
272         }
273         return true;
274 }
275 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
276 {
277         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
278                         && event.KeyInput.PressedDown) {
279
280                 bool prefer_character = shift_down;
281                 KeyPress kp(event.KeyInput, prefer_character);
282
283                 bool shift_went_down = false;
284                 if(!shift_down &&
285                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
286                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
287                                 event.KeyInput.Key == irr::KEY_RSHIFT))
288                         shift_went_down = true;
289
290                 // Remove Key already in use message
291                 if(this->key_used_text)
292                 {
293                         this->key_used_text->remove();
294                         this->key_used_text = NULL;
295                 }
296                 // Display Key already in use message
297                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
298                 {
299                         core::rect < s32 > rect(0, 0, 600, 40);
300                         rect += v2s32(0, 0) + v2s32(25, 30);
301                         const wchar_t *text = wgettext("Key already in use");
302                         this->key_used_text = Environment->addStaticText(text,
303                                         rect, false, true, this, -1);
304                         delete[] text;
305                         //infostream << "Key already in use" << std::endl;
306                 }
307
308                 // But go on
309                 {
310                         key_setting *k = NULL;
311                         for (key_setting *ks : key_settings) {
312                                 if (ks->id == activeKey) {
313                                         k = ks;
314                                         break;
315                                 }
316                         }
317                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
318                         k->key = kp;
319                         const wchar_t *text = wgettext(k->key.name());
320                         k->button->setText(text);
321                         delete[] text;
322
323                         this->key_used.push_back(kp);
324
325                         // Allow characters made with shift
326                         if(shift_went_down){
327                                 shift_down = true;
328                                 return false;
329                         }
330
331                         activeKey = -1;
332                         return true;
333                 }
334         } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
335                         && event.KeyInput.PressedDown
336                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
337                 quitMenu();
338                 return true;
339         } else if (event.EventType == EET_GUI_EVENT) {
340                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
341                         && isVisible())
342                 {
343                         if (!canTakeFocus(event.GUIEvent.Element))
344                         {
345                                 dstream << "GUIMainMenu: Not allowing focus change."
346                                 << std::endl;
347                                 // Returning true disables focus change
348                                 return true;
349                         }
350                 }
351                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
352                 {
353                         switch (event.GUIEvent.Caller->getID())
354                         {
355                                 case GUI_ID_BACK_BUTTON: //back
356                                         acceptInput();
357                                         quitMenu();
358                                         return true;
359                                 case GUI_ID_ABORT_BUTTON: //abort
360                                         quitMenu();
361                                         return true;
362                                 default:
363                                         key_setting *k = NULL;
364
365                                         for (key_setting *ks : key_settings) {
366                                                 if (ks->id == event.GUIEvent.Caller->getID()) {
367                                                         k = ks;
368                                                         break;
369                                                 }
370                                         }
371                                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
372
373                                         resetMenu();
374                                         shift_down = false;
375                                         activeKey = event.GUIEvent.Caller->getID();
376                                         const wchar_t *text = wgettext("press key");
377                                         k->button->setText(text);
378                                         delete[] text;
379                                         this->key_used.erase(std::remove(this->key_used.begin(),
380                                                         this->key_used.end(), k->key), this->key_used.end());
381                                         break;
382                         }
383                         Environment->setFocus(this);
384                 }
385         }
386         return Parent ? Parent->OnEvent(event) : false;
387 }
388
389 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
390 {
391         key_setting *k = new key_setting;
392         k->id = id;
393
394         k->button_name = button_name;
395         k->setting_name = setting_name;
396         k->key = getKeySetting(k->setting_name.c_str());
397         key_settings.push_back(k);
398 }
399
400 void GUIKeyChangeMenu::init_keys()
401 {
402         this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
403         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
404         this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
405         this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
406         this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Special"),          "keymap_special1");
407         this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
408         this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
409         this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
410         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
411         this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
412         this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
413         this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
414         this->add_key(GUI_ID_KEY_CAMERA_BUTTON,    wgettext("Change camera"),    "keymap_camera_mode");
415         this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
416         this->add_key(GUI_ID_KEY_MINIMAP_BUTTON,   wgettext("Toggle minimap"),   "keymap_minimap");
417         this->add_key(GUI_ID_KEY_FLY_BUTTON,       wgettext("Toggle fly"),       "keymap_freemove");
418         this->add_key(GUI_ID_KEY_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
419         this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
420         this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
421         this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
422         this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
423         this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON,   wgettext("Autoforward"),      "keymap_autoforward");
424         this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
425         this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"),       "keymap_screenshot");
426         this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
427         this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"),       "keymap_decrease_viewing_range_min");
428         this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"),       "keymap_increase_viewing_range_min");
429         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
430         this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
431         this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
432         this->add_key(GUI_ID_KEY_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
433         this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
434         this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_fog");
435 }
436