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