49b292df400837690ef028abafba27b9cb43bf4f
[oweals/minetest.git] / src / guiKeyChangeMenu.cpp
1 /*
2  Minetest-c55
3  Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
4  Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
5  Copyright (C) 2011 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 "main.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 #define KMaxButtonPerColumns 12
36
37 enum
38 {
39         GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
40         //buttons
41         GUI_ID_KEY_FORWARD_BUTTON,
42         GUI_ID_KEY_BACKWARD_BUTTON,
43         GUI_ID_KEY_LEFT_BUTTON,
44         GUI_ID_KEY_RIGHT_BUTTON,
45         GUI_ID_KEY_USE_BUTTON,
46         GUI_ID_KEY_FLY_BUTTON,
47         GUI_ID_KEY_FAST_BUTTON,
48         GUI_ID_KEY_JUMP_BUTTON,
49         GUI_ID_KEY_CHAT_BUTTON,
50         GUI_ID_KEY_CMD_BUTTON,
51         GUI_ID_KEY_CONSOLE_BUTTON,
52         GUI_ID_KEY_SNEAK_BUTTON,
53         GUI_ID_KEY_DROP_BUTTON,
54         GUI_ID_KEY_INVENTORY_BUTTON,
55         GUI_ID_KEY_DUMP_BUTTON,
56         GUI_ID_KEY_RANGE_BUTTON
57 };
58
59 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
60                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
61 GUIModalMenu(env, parent, id, menumgr)
62 {
63         shift_down = false;
64         activeKey = -1;
65         this->key_used_text = NULL;
66         init_keys();
67         for(size_t i=0; i<key_settings.size(); i++)
68                 this->key_used.push_back(key_settings.at(i)->key);
69 }
70
71 GUIKeyChangeMenu::~GUIKeyChangeMenu()
72 {
73         removeChildren();
74 }
75
76 void GUIKeyChangeMenu::removeChildren()
77 {
78         const core::list<gui::IGUIElement*> &children = getChildren();
79         core::list<gui::IGUIElement*> children_copy;
80         for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
81                  != children.end(); i++)
82         {
83                 children_copy.push_back(*i);
84         }
85         for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
86                  != children_copy.end(); i++)
87         {
88                 (*i)->remove();
89         }
90 }
91
92 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
93 {
94         removeChildren();
95         v2s32 size(620, 430);
96         
97         core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
98                                                         screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
99                                                         screensize.Y / 2 + size.Y / 2);
100
101         DesiredRect = rect;
102         recalculateAbsolutePosition(false);
103
104         v2s32 topleft(0, 0);
105         changeCtype("");
106         {
107                 core::rect < s32 > rect(0, 0, 600, 40);
108                 rect += topleft + v2s32(25, 3);
109                 //gui::IGUIStaticText *t =
110                 Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"),
111                                                                    rect, false, true, this, -1);
112                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
113         }
114
115         // Build buttons
116
117         v2s32 offset(25, 60);
118
119         for(size_t i = 0; i < key_settings.size(); i++)
120         {
121                 key_setting *k = key_settings.at(i);
122                 {
123                         core::rect < s32 > rect(0, 0, 100, 20);
124                         rect += topleft + v2s32(offset.X, offset.Y);
125                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
126                 }
127
128                 {
129                         core::rect < s32 > rect(0, 0, 100, 30);
130                         rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
131                         k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name()));
132                 }
133                 if(i + 1 == KMaxButtonPerColumns)
134                         offset = v2s32(250, 60);
135                 else
136                         offset += v2s32(0, 25);
137         }
138
139         {
140                 core::rect < s32 > rect(0, 0, 100, 30);
141                 rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
142                 Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
143                                                            wgettext("Save"));
144         }
145         {
146                 core::rect < s32 > rect(0, 0, 100, 30);
147                 rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
148                 Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
149                                                            wgettext("Cancel"));
150         }
151         changeCtype("C");
152         
153 }
154
155 void GUIKeyChangeMenu::drawMenu()
156 {
157         gui::IGUISkin* skin = Environment->getSkin();
158         if (!skin)
159                 return;
160         video::IVideoDriver* driver = Environment->getVideoDriver();
161
162         video::SColor bgcolor(140, 0, 0, 0);
163
164         {
165                 core::rect < s32 > rect(0, 0, 620, 620);
166                 rect += AbsoluteRect.UpperLeftCorner;
167                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
168         }
169
170         gui::IGUIElement::draw();
171 }
172
173 bool GUIKeyChangeMenu::acceptInput()
174 {
175         for(size_t i = 0; i < key_settings.size(); i++)
176         {
177                 key_setting *k = key_settings.at(i);
178                 g_settings->set(k->setting_name, k->key.sym());
179         }
180         clearKeyCache();
181         return true;
182 }
183
184 bool GUIKeyChangeMenu::resetMenu()
185 {
186         if (activeKey >= 0)
187         {
188                 for(size_t i = 0; i < key_settings.size(); i++)
189                 {
190                         key_setting *k = key_settings.at(i);
191                         if(k->id == activeKey)
192                         {
193                                 k->button->setText(wgettext(k->key.name()));
194                                 break;
195                         }
196                 }
197                 activeKey = -1;
198                 return false;
199         }
200         return true;
201 }
202 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
203 {
204         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
205                 && event.KeyInput.PressedDown)
206         {
207                 changeCtype("");
208                 bool prefer_character = shift_down;
209                 KeyPress kp(event.KeyInput, prefer_character);
210                 
211                 bool shift_went_down = false;
212                 if(!shift_down &&
213                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
214                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
215                                 event.KeyInput.Key == irr::KEY_RSHIFT))
216                         shift_went_down = true;
217
218                 // Remove Key already in use message
219                 if(this->key_used_text)
220                 {
221                         this->key_used_text->remove();
222                         this->key_used_text = NULL;
223                 }
224                 // Display Key already in use message
225                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
226                 {
227                         core::rect < s32 > rect(0, 0, 600, 40);
228                         rect += v2s32(0, 0) + v2s32(25, 30);
229                         this->key_used_text = Environment->addStaticText(wgettext("Key already in use"),
230                                                                         rect, false, true, this, -1);
231                         //infostream << "Key already in use" << std::endl;
232                 }
233
234                 // But go on
235                 {
236                         key_setting *k=NULL;
237                         for(size_t i = 0; i < key_settings.size(); i++)
238                         {
239                                 if(key_settings.at(i)->id == activeKey)
240                                 {
241                                         k = key_settings.at(i);
242                                         break;
243                                 }
244                         }
245                         assert(k);
246                         k->key = kp;
247                         k->button->setText(wgettext(k->key.name()));
248
249                         this->key_used.push_back(kp);
250
251                         changeCtype("C");
252                         // Allow characters made with shift
253                         if(shift_went_down){
254                                 shift_down = true;
255                                 return false;
256                         }else{
257                                 activeKey = -1;
258                                 return true;
259                         }
260                 }
261         }
262         if (event.EventType == EET_GUI_EVENT)
263         {
264                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
265                         && isVisible())
266                 {
267                         if (!canTakeFocus(event.GUIEvent.Element))
268                         {
269                                 dstream << "GUIMainMenu: Not allowing focus change."
270                                 << std::endl;
271                                 // Returning true disables focus change
272                                 return true;
273                         }
274                 }
275                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
276                 {
277                         if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON &&
278                            event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
279                         {
280                                 changeCtype("");
281                         }
282
283                         switch (event.GUIEvent.Caller->getID())
284                         {
285                                 case GUI_ID_BACK_BUTTON: //back
286                                         acceptInput();
287                                         quitMenu();
288                                         return true;
289                                 case GUI_ID_ABORT_BUTTON: //abort
290                                         quitMenu();
291                                         return true;
292                                 default:
293                                         key_setting *k = NULL;
294                                         for(size_t i = 0; i < key_settings.size(); i++)
295                                         {
296                                                 if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
297                                                 {
298                                                         k = key_settings.at(i);
299                                                         break;
300                                                 }
301                                         }
302                                         assert(k);
303
304                                         resetMenu();
305                                         shift_down = false;
306                                         activeKey = event.GUIEvent.Caller->getID();
307                                         k->button->setText(wgettext("press key"));
308                                         this->key_used.erase(std::remove(this->key_used.begin(),
309                                                         this->key_used.end(), k->key), this->key_used.end());
310                                         break;
311                         }
312                         Environment->setFocus(this);
313                         //Buttons
314                         changeCtype("C");
315                 }
316         }
317         return Parent ? Parent->OnEvent(event) : false;
318 }
319
320 void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name)
321 {
322         key_setting *k = new key_setting;
323         k->id = id;
324         k->button_name = wgettext(button_name.c_str());
325         k->setting_name = setting_name;
326         k->key = getKeySetting(k->setting_name.c_str());
327         key_settings.push_back(k);
328 }
329
330 void GUIKeyChangeMenu::init_keys()
331 {
332         this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward");
333         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward");
334         this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left");
335         this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right");
336         this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1");
337         this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump");
338         this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak");
339         this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop");
340         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory");
341         this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat");
342         this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd");
343         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console");
344         this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove");
345         this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
346         this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect");
347         this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
348 }