]> git.lizzy.rs Git - dragonfireclient.git/blob - src/guiKeyChangeMenu.cpp
Key change menu: Expose more keys
[dragonfireclient.git] / src / 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_DUMP_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 };
82
83 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
84                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
85 GUIModalMenu(env, parent, id, menumgr)
86 {
87         init_keys();
88         for (size_t i = 0; i < key_settings.size(); i++)
89                 key_used.push_back(key_settings.at(i)->key);
90 }
91
92 GUIKeyChangeMenu::~GUIKeyChangeMenu()
93 {
94         removeChildren();
95
96         for (std::vector<key_setting*>::iterator iter = key_settings.begin();
97                         iter != key_settings.end(); ++iter) {
98                 delete[] (*iter)->button_name;
99                 delete (*iter);
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 (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
109                  != children.end(); i++)
110         {
111                 children_copy.push_back(*i);
112         }
113         for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
114                  != children_copy.end(); i++)
115         {
116                 (*i)->remove();
117         }
118 }
119
120 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
121 {
122         removeChildren();
123         v2s32 size(745, 430);
124
125         core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
126                                                         screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
127                                                         screensize.Y / 2 + size.Y / 2);
128
129         DesiredRect = rect;
130         recalculateAbsolutePosition(false);
131
132         v2s32 topleft(0, 0);
133
134         {
135                 core::rect < s32 > rect(0, 0, 600, 40);
136                 rect += topleft + v2s32(25, 3);
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, 60);
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, 20);
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, 30);
160                         rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
161                         const wchar_t *text = wgettext(k->key.name());
162                         k->button = Environment->addButton(rect, this, k->id, text);
163                         delete[] text;
164                 }
165                 if ((i + 1) % KMaxButtonPerColumns == 0) {
166                         offset.X += 230;
167                         offset.Y = 60;
168                 } else {
169                         offset += v2s32(0, 25);
170                 }
171         }
172
173         {
174                 s32 option_x = offset.X;
175                 s32 option_y = offset.Y + 5;
176                 u32 option_w = 180;
177                 {
178                         core::rect<s32> rect(0, 0, option_w, 30);
179                         rect += topleft + v2s32(option_x, option_y);
180                         const wchar_t *text = wgettext("\"Use\" = 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);
186         }
187
188         {
189                 s32 option_x = offset.X;
190                 s32 option_y = offset.Y + 5;
191                 u32 option_w = 280;
192                 {
193                         core::rect<s32> rect(0, 0, option_w, 30);
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);
201         }
202
203         {
204                 core::rect < s32 > rect(0, 0, 100, 30);
205                 rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
206                 const wchar_t *text =  wgettext("Save");
207                 Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
208                                  text);
209                 delete[] text;
210         }
211         {
212                 core::rect < s32 > rect(0, 0, 100, 30);
213                 rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
214                 const wchar_t *text = wgettext("Cancel");
215                 Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
216                                 text);
217                 delete[] text;
218         }
219 }
220
221 void GUIKeyChangeMenu::drawMenu()
222 {
223         gui::IGUISkin* skin = Environment->getSkin();
224         if (!skin)
225                 return;
226         video::IVideoDriver* driver = Environment->getVideoDriver();
227
228         video::SColor bgcolor(140, 0, 0, 0);
229
230         {
231                 core::rect < s32 > rect(0, 0, 745, 620);
232                 rect += AbsoluteRect.UpperLeftCorner;
233                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
234         }
235
236         gui::IGUIElement::draw();
237 }
238
239 bool GUIKeyChangeMenu::acceptInput()
240 {
241         for(size_t i = 0; i < key_settings.size(); i++)
242         {
243                 key_setting *k = key_settings.at(i);
244                 g_settings->set(k->setting_name, k->key.sym());
245         }
246         {
247                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
248                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
249                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
250         }
251         {
252                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
253                 if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
254                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
255         }
256
257         clearKeyCache();
258
259         g_gamecallback->signalKeyConfigChange();
260
261         return true;
262 }
263
264 bool GUIKeyChangeMenu::resetMenu()
265 {
266         if (activeKey >= 0)
267         {
268                 for(size_t i = 0; i < key_settings.size(); i++)
269                 {
270                         key_setting *k = key_settings.at(i);
271                         if(k->id == activeKey)
272                         {
273                                 const wchar_t *text = wgettext(k->key.name());
274                                 k->button->setText(text);
275                                 delete[] text;
276                                 break;
277                         }
278                 }
279                 activeKey = -1;
280                 return false;
281         }
282         return true;
283 }
284 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
285 {
286         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
287                         && event.KeyInput.PressedDown) {
288
289                 bool prefer_character = shift_down;
290                 KeyPress kp(event.KeyInput, prefer_character);
291
292                 bool shift_went_down = false;
293                 if(!shift_down &&
294                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
295                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
296                                 event.KeyInput.Key == irr::KEY_RSHIFT))
297                         shift_went_down = true;
298
299                 // Remove Key already in use message
300                 if(this->key_used_text)
301                 {
302                         this->key_used_text->remove();
303                         this->key_used_text = NULL;
304                 }
305                 // Display Key already in use message
306                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
307                 {
308                         core::rect < s32 > rect(0, 0, 600, 40);
309                         rect += v2s32(0, 0) + v2s32(25, 30);
310                         const wchar_t *text = wgettext("Key already in use");
311                         this->key_used_text = Environment->addStaticText(text,
312                                         rect, false, true, this, -1);
313                         delete[] text;
314                         //infostream << "Key already in use" << std::endl;
315                 }
316
317                 // But go on
318                 {
319                         key_setting *k = NULL;
320                         for(size_t i = 0; i < key_settings.size(); i++)
321                         {
322                                 if(key_settings.at(i)->id == activeKey)
323                                 {
324                                         k = key_settings.at(i);
325                                         break;
326                                 }
327                         }
328                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
329                         k->key = kp;
330                         const wchar_t *text = wgettext(k->key.name());
331                         k->button->setText(text);
332                         delete[] text;
333
334                         this->key_used.push_back(kp);
335
336                         // Allow characters made with shift
337                         if(shift_went_down){
338                                 shift_down = true;
339                                 return false;
340                         }else{
341                                 activeKey = -1;
342                                 return true;
343                         }
344                 }
345         } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
346                         && event.KeyInput.PressedDown
347                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
348                 quitMenu();
349                 return true;
350         } else if (event.EventType == EET_GUI_EVENT) {
351                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
352                         && isVisible())
353                 {
354                         if (!canTakeFocus(event.GUIEvent.Element))
355                         {
356                                 dstream << "GUIMainMenu: Not allowing focus change."
357                                 << std::endl;
358                                 // Returning true disables focus change
359                                 return true;
360                         }
361                 }
362                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
363                 {
364                         switch (event.GUIEvent.Caller->getID())
365                         {
366                                 case GUI_ID_BACK_BUTTON: //back
367                                         acceptInput();
368                                         quitMenu();
369                                         return true;
370                                 case GUI_ID_ABORT_BUTTON: //abort
371                                         quitMenu();
372                                         return true;
373                                 default:
374                                         key_setting *k = NULL;
375                                         for(size_t i = 0; i < key_settings.size(); i++)
376                                         {
377                                                 if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
378                                                 {
379                                                         k = key_settings.at(i);
380                                                         break;
381                                                 }
382                                         }
383                                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
384
385                                         resetMenu();
386                                         shift_down = false;
387                                         activeKey = event.GUIEvent.Caller->getID();
388                                         const wchar_t *text = wgettext("press key");
389                                         k->button->setText(text);
390                                         delete[] text;
391                                         this->key_used.erase(std::remove(this->key_used.begin(),
392                                                         this->key_used.end(), k->key), this->key_used.end());
393                                         break;
394                         }
395                         Environment->setFocus(this);
396                 }
397         }
398         return Parent ? Parent->OnEvent(event) : false;
399 }
400
401 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
402 {
403         key_setting *k = new key_setting;
404         k->id = id;
405
406         k->button_name = button_name;
407         k->setting_name = setting_name;
408         k->key = getKeySetting(k->setting_name.c_str());
409         key_settings.push_back(k);
410 }
411
412 void GUIKeyChangeMenu::init_keys()
413 {
414         this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
415         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
416         this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
417         this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
418         this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Use"),              "keymap_special1");
419         this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
420         this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
421         this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
422         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
423         this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
424         this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
425         this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
426         this->add_key(GUI_ID_KEY_CAMERA_BUTTON,    wgettext("Change camera"),    "keymap_camera_mode");
427         this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
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_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
431         this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
432         this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
433         this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
434         this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
435         this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON,   wgettext("Autoforward"),      "keymap_autoforward");
436         this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
437         this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"),       "keymap_screenshot");
438         this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
439         this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"),       "keymap_decrease_viewing_range_min");
440         this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"),       "keymap_increase_viewing_range_min");
441         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
442         this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
443         this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
444         this->add_key(GUI_ID_KEY_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
445         this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
446         this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_force_fog_off");
447         this->add_key(GUI_ID_KEY_DUMP_BUTTON,      wgettext("Print stacks"),     "keymap_print_debug_stacks");
448 }
449