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