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