Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 :
21 : #include <cstdlib>
22 : #include <algorithm>
23 : #include <iterator>
24 : #include <sstream>
25 : #include <limits>
26 : #include "guiFormSpecMenu.h"
27 : #include "guiTable.h"
28 : #include "constants.h"
29 : #include "gamedef.h"
30 : #include "keycode.h"
31 : #include "strfnd.h"
32 : #include <IGUICheckBox.h>
33 : #include <IGUIEditBox.h>
34 : #include <IGUIButton.h>
35 : #include <IGUIStaticText.h>
36 : #include <IGUIFont.h>
37 : #include <IGUITabControl.h>
38 : #include <IGUIComboBox.h>
39 : #include "log.h"
40 : #include "client/tile.h" // ITextureSource
41 : #include "hud.h" // drawItemStack
42 : #include "filesys.h"
43 : #include "gettime.h"
44 : #include "gettext.h"
45 : #include "scripting_game.h"
46 : #include "porting.h"
47 : #include "settings.h"
48 : #include "client.h"
49 : #include "fontengine.h"
50 : #include "util/hex.h"
51 : #include "util/numeric.h"
52 : #include "util/string.h" // for parseColorString()
53 : #include "guiscalingfilter.h"
54 :
55 : #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
56 : #include "intlGUIEditBox.h"
57 : #endif
58 :
59 : #define MY_CHECKPOS(a,b) \
60 : if (v_pos.size() != 2) { \
61 : errorstream<< "Invalid pos for element " << a << "specified: \"" \
62 : << parts[b] << "\"" << std::endl; \
63 : return; \
64 : }
65 :
66 : #define MY_CHECKGEOM(a,b) \
67 : if (v_geom.size() != 2) { \
68 : errorstream<< "Invalid pos for element " << a << "specified: \"" \
69 : << parts[b] << "\"" << std::endl; \
70 : return; \
71 : }
72 : /*
73 : GUIFormSpecMenu
74 : */
75 2 : static unsigned int font_line_height(gui::IGUIFont *font)
76 : {
77 2 : return font->getDimension(L"Ay").Height + font->getKerningHeight();
78 : }
79 :
80 2 : GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
81 : gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
82 : InventoryManager *invmgr, IGameDef *gamedef,
83 : ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
84 : Client* client, bool remap_dbl_click) :
85 2 : GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
86 : m_device(dev),
87 : m_invmgr(invmgr),
88 : m_gamedef(gamedef),
89 : m_tsrc(tsrc),
90 : m_client(client),
91 : m_selected_item(NULL),
92 : m_selected_amount(0),
93 : m_selected_dragging(false),
94 : m_tooltip_element(NULL),
95 : m_hovered_time(0),
96 : m_old_tooltip_id(-1),
97 : m_rmouse_auto_place(false),
98 : m_allowclose(true),
99 : m_lock(false),
100 : m_form_src(fsrc),
101 : m_text_dst(tdst),
102 : m_formspec_version(0),
103 : m_focused_element(""),
104 : m_font(NULL),
105 4 : m_remap_dbl_click(remap_dbl_click)
106 : #ifdef __ANDROID__
107 : , m_JavaDialogFieldName("")
108 : #endif
109 : {
110 2 : current_keys_pending.key_down = false;
111 2 : current_keys_pending.key_up = false;
112 2 : current_keys_pending.key_enter = false;
113 2 : current_keys_pending.key_escape = false;
114 :
115 2 : m_doubleclickdetect[0].time = 0;
116 2 : m_doubleclickdetect[1].time = 0;
117 :
118 2 : m_doubleclickdetect[0].pos = v2s32(0, 0);
119 2 : m_doubleclickdetect[1].pos = v2s32(0, 0);
120 :
121 2 : m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
122 2 : }
123 :
124 6 : GUIFormSpecMenu::~GUIFormSpecMenu()
125 : {
126 2 : removeChildren();
127 :
128 3 : for (u32 i = 0; i < m_tables.size(); ++i) {
129 1 : GUITable *table = m_tables[i].second;
130 1 : table->drop();
131 : }
132 :
133 2 : delete m_selected_item;
134 :
135 2 : if (m_form_src != NULL) {
136 2 : delete m_form_src;
137 : }
138 2 : if (m_text_dst != NULL) {
139 2 : delete m_text_dst;
140 : }
141 4 : }
142 :
143 7 : void GUIFormSpecMenu::removeChildren()
144 : {
145 7 : const core::list<gui::IGUIElement*> &children = getChildren();
146 :
147 71 : while(!children.empty()) {
148 32 : (*children.getLast())->remove();
149 : }
150 :
151 7 : if(m_tooltip_element) {
152 5 : m_tooltip_element->remove();
153 5 : m_tooltip_element->drop();
154 5 : m_tooltip_element = NULL;
155 : }
156 :
157 7 : }
158 :
159 3 : void GUIFormSpecMenu::setInitialFocus()
160 : {
161 : // Set initial focus according to following order of precedence:
162 : // 1. first empty editbox
163 : // 2. first editbox
164 : // 3. first table
165 : // 4. last button
166 : // 5. first focusable (not statictext, not tabheader)
167 : // 6. first child element
168 :
169 5 : core::list<gui::IGUIElement*> children = getChildren();
170 :
171 : // in case "children" contains any NULL elements, remove them
172 45 : for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
173 30 : it != children.end();) {
174 12 : if (*it)
175 12 : ++it;
176 : else
177 0 : it = children.erase(it);
178 : }
179 :
180 : // 1. first empty editbox
181 36 : for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
182 24 : it != children.end(); ++it) {
183 20 : if ((*it)->getType() == gui::EGUIET_EDIT_BOX
184 10 : && (*it)->getText()[0] == 0) {
185 1 : Environment->setFocus(*it);
186 1 : return;
187 : }
188 : }
189 :
190 : // 2. first editbox
191 6 : for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
192 4 : it != children.end(); ++it) {
193 0 : if ((*it)->getType() == gui::EGUIET_EDIT_BOX) {
194 0 : Environment->setFocus(*it);
195 0 : return;
196 : }
197 : }
198 :
199 : // 3. first table
200 6 : for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
201 4 : it != children.end(); ++it) {
202 0 : if ((*it)->getTypeName() == std::string("GUITable")) {
203 0 : Environment->setFocus(*it);
204 0 : return;
205 : }
206 : }
207 :
208 : // 4. last button
209 6 : for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
210 4 : it != children.end(); --it) {
211 0 : if ((*it)->getType() == gui::EGUIET_BUTTON) {
212 0 : Environment->setFocus(*it);
213 0 : return;
214 : }
215 : }
216 :
217 : // 5. first focusable (not statictext, not tabheader)
218 6 : for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
219 4 : it != children.end(); ++it) {
220 0 : if ((*it)->getType() != gui::EGUIET_STATIC_TEXT &&
221 0 : (*it)->getType() != gui::EGUIET_TAB_CONTROL) {
222 0 : Environment->setFocus(*it);
223 0 : return;
224 : }
225 : }
226 :
227 : // 6. first child element
228 2 : if (children.empty())
229 2 : Environment->setFocus(this);
230 : else
231 0 : Environment->setFocus(*(children.begin()));
232 : }
233 :
234 3 : GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
235 : {
236 3 : for (u32 i = 0; i < m_tables.size(); ++i) {
237 3 : if (tablename == m_tables[i].first.fname)
238 3 : return m_tables[i].second;
239 : }
240 0 : return 0;
241 : }
242 :
243 159 : static std::vector<std::string> split(const std::string &s, char delim)
244 : {
245 159 : std::vector<std::string> tokens;
246 :
247 318 : std::string current = "";
248 159 : bool last_was_escape = false;
249 7308 : for (unsigned int i = 0; i < s.size(); i++) {
250 7149 : char si = s.c_str()[i];
251 7149 : if (last_was_escape) {
252 0 : current += '\\';
253 0 : current += si;
254 0 : last_was_escape = false;
255 : } else {
256 7149 : if (si == delim) {
257 275 : tokens.push_back(current);
258 275 : current = "";
259 275 : last_was_escape = false;
260 6874 : } else if (si == '\\') {
261 0 : last_was_escape = true;
262 : } else {
263 6874 : current += si;
264 6874 : last_was_escape = false;
265 : }
266 : }
267 : }
268 : //push last element
269 159 : tokens.push_back(current);
270 :
271 318 : return tokens;
272 : }
273 :
274 3 : void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
275 : {
276 3 : std::vector<std::string> parts = split(element,',');
277 :
278 6 : if (((parts.size() == 2) || parts.size() == 3) ||
279 0 : ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
280 : {
281 3 : if (parts[1].find(';') != std::string::npos)
282 0 : parts[1] = parts[1].substr(0,parts[1].find(';'));
283 :
284 3 : data->invsize.X = MYMAX(0, stof(parts[0]));
285 3 : data->invsize.Y = MYMAX(0, stof(parts[1]));
286 :
287 3 : lockSize(false);
288 3 : if (parts.size() == 3) {
289 3 : if (parts[2] == "true") {
290 1 : lockSize(true,v2u32(800,600));
291 : }
292 : }
293 :
294 3 : data->explicit_size = true;
295 3 : return;
296 : }
297 0 : errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
298 : }
299 :
300 0 : void GUIFormSpecMenu::parseList(parserData* data,std::string element)
301 : {
302 0 : if (m_gamedef == 0) {
303 0 : errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
304 0 : return;
305 : }
306 :
307 0 : std::vector<std::string> parts = split(element,';');
308 :
309 0 : if (((parts.size() == 4) || (parts.size() == 5)) ||
310 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
311 : {
312 0 : std::string location = parts[0];
313 0 : std::string listname = parts[1];
314 0 : std::vector<std::string> v_pos = split(parts[2],',');
315 0 : std::vector<std::string> v_geom = split(parts[3],',');
316 0 : std::string startindex = "";
317 0 : if (parts.size() == 5)
318 0 : startindex = parts[4];
319 :
320 0 : MY_CHECKPOS("list",2);
321 0 : MY_CHECKGEOM("list",3);
322 :
323 0 : InventoryLocation loc;
324 :
325 0 : if(location == "context" || location == "current_name")
326 0 : loc = m_current_inventory_location;
327 : else
328 0 : loc.deSerialize(location);
329 :
330 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
331 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
332 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
333 :
334 0 : v2s32 geom;
335 0 : geom.X = stoi(v_geom[0]);
336 0 : geom.Y = stoi(v_geom[1]);
337 :
338 0 : s32 start_i = 0;
339 0 : if(startindex != "")
340 0 : start_i = stoi(startindex);
341 :
342 0 : if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
343 0 : errorstream<< "Invalid list element: '" << element << "'" << std::endl;
344 0 : return;
345 : }
346 :
347 0 : if(!data->explicit_size)
348 0 : errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
349 0 : m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
350 0 : return;
351 : }
352 0 : errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
353 : }
354 :
355 0 : void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
356 : {
357 0 : if (m_gamedef == 0) {
358 0 : errorstream << "WARNING: invalid use of 'listring' with m_gamedef==0" << std::endl;
359 0 : return;
360 : }
361 :
362 0 : std::vector<std::string> parts = split(element, ';');
363 :
364 0 : if (parts.size() == 2) {
365 0 : std::string location = parts[0];
366 0 : std::string listname = parts[1];
367 :
368 0 : InventoryLocation loc;
369 :
370 0 : if (location == "context" || location == "current_name")
371 0 : loc = m_current_inventory_location;
372 : else
373 0 : loc.deSerialize(location);
374 :
375 0 : m_inventory_rings.push_back(ListRingSpec(loc, listname));
376 0 : return;
377 0 : } else if ((element == "") && (m_inventorylists.size() > 1)) {
378 0 : size_t siz = m_inventorylists.size();
379 : // insert the last two inv list elements into the list ring
380 0 : const ListDrawSpec &spa = m_inventorylists[siz - 2];
381 0 : const ListDrawSpec &spb = m_inventorylists[siz - 1];
382 0 : m_inventory_rings.push_back(ListRingSpec(spa.inventoryloc, spa.listname));
383 0 : m_inventory_rings.push_back(ListRingSpec(spb.inventoryloc, spb.listname));
384 0 : return;
385 : }
386 0 : errorstream<< "Invalid list ring element(" << parts.size() << ", "
387 0 : << m_inventorylists.size() << "): '" << element << "'" << std::endl;
388 : }
389 :
390 2 : void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
391 : {
392 2 : std::vector<std::string> parts = split(element,';');
393 :
394 4 : if (((parts.size() >= 3) && (parts.size() <= 4)) ||
395 0 : ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
396 : {
397 4 : std::vector<std::string> v_pos = split(parts[0],',');
398 4 : std::string name = parts[1];
399 4 : std::string label = parts[2];
400 4 : std::string selected = "";
401 :
402 2 : if (parts.size() >= 4)
403 2 : selected = parts[3];
404 :
405 2 : MY_CHECKPOS("checkbox",0);
406 :
407 2 : v2s32 pos = padding;
408 2 : pos.X += stof(v_pos[0]) * (float) spacing.X;
409 2 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
410 :
411 2 : bool fselected = false;
412 :
413 2 : if (selected == "true")
414 0 : fselected = true;
415 :
416 4 : std::wstring wlabel = utf8_to_wide(label);
417 :
418 : core::rect<s32> rect = core::rect<s32>(
419 2 : pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
420 2 : pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
421 6 : pos.Y + ((imgsize.Y/2) + m_btn_height));
422 :
423 : FieldSpec spec(
424 : name,
425 : wlabel, //Needed for displaying text on MSVC
426 : wlabel,
427 2 : 258+m_fields.size()
428 4 : );
429 :
430 2 : spec.ftype = f_CheckBox;
431 :
432 4 : gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
433 4 : spec.fid, spec.flabel.c_str());
434 :
435 2 : if (spec.fname == data->focused_fieldname) {
436 0 : Environment->setFocus(e);
437 : }
438 :
439 2 : m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
440 2 : m_fields.push_back(spec);
441 2 : return;
442 : }
443 0 : errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
444 : }
445 :
446 0 : void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
447 : {
448 0 : std::vector<std::string> parts = split(element,';');
449 :
450 0 : if (parts.size() >= 5) {
451 0 : std::vector<std::string> v_pos = split(parts[0],',');
452 0 : std::vector<std::string> v_dim = split(parts[1],',');
453 0 : std::string name = parts[2];
454 0 : std::string value = parts[4];
455 :
456 0 : MY_CHECKPOS("scrollbar",0);
457 :
458 0 : v2s32 pos = padding;
459 0 : pos.X += stof(v_pos[0]) * (float) spacing.X;
460 0 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
461 :
462 0 : if (v_dim.size() != 2) {
463 0 : errorstream<< "Invalid size for element " << "scrollbar"
464 0 : << "specified: \"" << parts[1] << "\"" << std::endl;
465 0 : return;
466 : }
467 :
468 0 : v2s32 dim;
469 0 : dim.X = stof(v_dim[0]) * (float) spacing.X;
470 0 : dim.Y = stof(v_dim[1]) * (float) spacing.Y;
471 :
472 : core::rect<s32> rect =
473 0 : core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
474 :
475 : FieldSpec spec(
476 : name,
477 : L"",
478 : L"",
479 0 : 258+m_fields.size()
480 0 : );
481 :
482 0 : bool is_horizontal = true;
483 :
484 0 : if (parts[2] == "vertical")
485 0 : is_horizontal = false;
486 :
487 0 : spec.ftype = f_ScrollBar;
488 0 : spec.send = true;
489 : gui::IGUIScrollBar* e =
490 0 : Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
491 :
492 0 : e->setMax(1000);
493 0 : e->setMin(0);
494 0 : e->setPos(stoi(parts[4]));
495 0 : e->setSmallStep(10);
496 0 : e->setLargeStep(100);
497 :
498 0 : m_scrollbars.push_back(std::pair<FieldSpec,gui::IGUIScrollBar*>(spec,e));
499 0 : m_fields.push_back(spec);
500 0 : return;
501 : }
502 0 : errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
503 : }
504 :
505 0 : void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
506 : {
507 0 : std::vector<std::string> parts = split(element,';');
508 :
509 0 : if ((parts.size() == 3) ||
510 0 : ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
511 : {
512 0 : std::vector<std::string> v_pos = split(parts[0],',');
513 0 : std::vector<std::string> v_geom = split(parts[1],',');
514 0 : std::string name = unescape_string(parts[2]);
515 :
516 0 : MY_CHECKPOS("image",0);
517 0 : MY_CHECKGEOM("image",1);
518 :
519 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
520 0 : pos.X += stof(v_pos[0]) * (float) spacing.X;
521 0 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
522 :
523 0 : v2s32 geom;
524 0 : geom.X = stof(v_geom[0]) * (float)imgsize.X;
525 0 : geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
526 :
527 0 : if(!data->explicit_size)
528 0 : errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
529 0 : m_images.push_back(ImageDrawSpec(name, pos, geom));
530 0 : return;
531 : }
532 :
533 0 : if (parts.size() == 2) {
534 0 : std::vector<std::string> v_pos = split(parts[0],',');
535 0 : std::string name = unescape_string(parts[1]);
536 :
537 0 : MY_CHECKPOS("image",0);
538 :
539 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
540 0 : pos.X += stof(v_pos[0]) * (float) spacing.X;
541 0 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
542 :
543 0 : if(!data->explicit_size)
544 0 : errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
545 0 : m_images.push_back(ImageDrawSpec(name, pos));
546 0 : return;
547 : }
548 0 : errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
549 : }
550 :
551 0 : void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
552 : {
553 0 : std::vector<std::string> parts = split(element,';');
554 :
555 0 : if ((parts.size() == 3) ||
556 0 : ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
557 : {
558 0 : std::vector<std::string> v_pos = split(parts[0],',');
559 0 : std::vector<std::string> v_geom = split(parts[1],',');
560 0 : std::string name = parts[2];
561 :
562 0 : MY_CHECKPOS("itemimage",0);
563 0 : MY_CHECKGEOM("itemimage",1);
564 :
565 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
566 0 : pos.X += stof(v_pos[0]) * (float) spacing.X;
567 0 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
568 :
569 0 : v2s32 geom;
570 0 : geom.X = stof(v_geom[0]) * (float)imgsize.X;
571 0 : geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
572 :
573 0 : if(!data->explicit_size)
574 0 : errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
575 0 : m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
576 0 : return;
577 : }
578 0 : errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
579 : }
580 :
581 10 : void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
582 : std::string type)
583 : {
584 10 : std::vector<std::string> parts = split(element,';');
585 :
586 20 : if ((parts.size() == 4) ||
587 0 : ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
588 : {
589 20 : std::vector<std::string> v_pos = split(parts[0],',');
590 20 : std::vector<std::string> v_geom = split(parts[1],',');
591 20 : std::string name = parts[2];
592 20 : std::string label = parts[3];
593 :
594 10 : MY_CHECKPOS("button",0);
595 10 : MY_CHECKGEOM("button",1);
596 :
597 10 : v2s32 pos = padding;
598 10 : pos.X += stof(v_pos[0]) * (float)spacing.X;
599 10 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
600 :
601 10 : v2s32 geom;
602 10 : geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
603 10 : pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
604 :
605 : core::rect<s32> rect =
606 10 : core::rect<s32>(pos.X, pos.Y - m_btn_height,
607 20 : pos.X + geom.X, pos.Y + m_btn_height);
608 :
609 10 : if(!data->explicit_size)
610 0 : errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
611 :
612 10 : label = unescape_string(label);
613 :
614 20 : std::wstring wlabel = utf8_to_wide(label);
615 :
616 : FieldSpec spec(
617 : name,
618 : wlabel,
619 : L"",
620 10 : 258+m_fields.size()
621 20 : );
622 10 : spec.ftype = f_Button;
623 10 : if(type == "button_exit")
624 6 : spec.is_exit = true;
625 20 : gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
626 20 : spec.flabel.c_str());
627 :
628 10 : if (spec.fname == data->focused_fieldname) {
629 1 : Environment->setFocus(e);
630 : }
631 :
632 10 : m_fields.push_back(spec);
633 10 : return;
634 : }
635 0 : errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
636 : }
637 :
638 0 : void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
639 : {
640 0 : std::vector<std::string> parts = split(element,';');
641 :
642 0 : if (((parts.size() == 3) || (parts.size() == 4)) ||
643 0 : ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
644 : {
645 0 : std::vector<std::string> v_pos = split(parts[0],',');
646 0 : std::vector<std::string> v_geom = split(parts[1],',');
647 0 : std::string name = unescape_string(parts[2]);
648 :
649 0 : MY_CHECKPOS("background",0);
650 0 : MY_CHECKGEOM("background",1);
651 :
652 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
653 0 : pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
654 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
655 :
656 0 : v2s32 geom;
657 0 : geom.X = stof(v_geom[0]) * (float)spacing.X;
658 0 : geom.Y = stof(v_geom[1]) * (float)spacing.Y;
659 :
660 0 : if (parts.size() == 4) {
661 0 : m_clipbackground = is_yes(parts[3]);
662 0 : if (m_clipbackground) {
663 0 : pos.X = stoi(v_pos[0]); //acts as offset
664 0 : pos.Y = stoi(v_pos[1]); //acts as offset
665 : }
666 : }
667 :
668 0 : if(!data->explicit_size)
669 0 : errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
670 0 : m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
671 0 : return;
672 : }
673 0 : errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
674 : }
675 :
676 0 : void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
677 : {
678 0 : std::vector<std::string> parts = split(element,';');
679 :
680 0 : data->table_options.clear();
681 0 : for (size_t i = 0; i < parts.size(); ++i) {
682 : // Parse table option
683 0 : std::string opt = unescape_string(parts[i]);
684 0 : data->table_options.push_back(GUITable::splitOption(opt));
685 : }
686 0 : }
687 :
688 2 : void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
689 : {
690 4 : std::vector<std::string> parts = split(element,';');
691 :
692 2 : data->table_columns.clear();
693 4 : for (size_t i = 0; i < parts.size(); ++i) {
694 4 : std::vector<std::string> col_parts = split(parts[i],',');
695 4 : GUITable::TableColumn column;
696 : // Parse column type
697 2 : if (!col_parts.empty())
698 2 : column.type = col_parts[0];
699 : // Parse column options
700 2 : for (size_t j = 1; j < col_parts.size(); ++j) {
701 0 : std::string opt = unescape_string(col_parts[j]);
702 0 : column.options.push_back(GUITable::splitOption(opt));
703 : }
704 2 : data->table_columns.push_back(column);
705 : }
706 2 : }
707 :
708 2 : void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
709 : {
710 2 : std::vector<std::string> parts = split(element,';');
711 :
712 4 : if (((parts.size() == 4) || (parts.size() == 5)) ||
713 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
714 : {
715 4 : std::vector<std::string> v_pos = split(parts[0],',');
716 4 : std::vector<std::string> v_geom = split(parts[1],',');
717 4 : std::string name = parts[2];
718 4 : std::vector<std::string> items = split(parts[3],',');
719 4 : std::string str_initial_selection = "";
720 4 : std::string str_transparent = "false";
721 :
722 2 : if (parts.size() >= 5)
723 2 : str_initial_selection = parts[4];
724 :
725 2 : MY_CHECKPOS("table",0);
726 2 : MY_CHECKGEOM("table",1);
727 :
728 2 : v2s32 pos = padding;
729 2 : pos.X += stof(v_pos[0]) * (float)spacing.X;
730 2 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
731 :
732 2 : v2s32 geom;
733 2 : geom.X = stof(v_geom[0]) * (float)spacing.X;
734 2 : geom.Y = stof(v_geom[1]) * (float)spacing.Y;
735 :
736 :
737 2 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
738 :
739 : FieldSpec spec(
740 : name,
741 : L"",
742 : L"",
743 2 : 258+m_fields.size()
744 4 : );
745 :
746 2 : spec.ftype = f_Table;
747 :
748 6 : for (unsigned int i = 0; i < items.size(); ++i) {
749 4 : items[i] = unescape_string(items[i]);
750 : }
751 :
752 : //now really show table
753 : GUITable *e = new GUITable(Environment, this, spec.fid, rect,
754 2 : m_tsrc);
755 :
756 2 : if (spec.fname == data->focused_fieldname) {
757 1 : Environment->setFocus(e);
758 : }
759 :
760 2 : e->setTable(data->table_options, data->table_columns, items);
761 :
762 2 : if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
763 1 : e->setDynamicData(data->table_dyndata[name]);
764 : }
765 :
766 4 : if ((str_initial_selection != "") &&
767 2 : (str_initial_selection != "0"))
768 1 : e->setSelected(stoi(str_initial_selection.c_str()));
769 :
770 2 : m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
771 2 : m_fields.push_back(spec);
772 2 : return;
773 : }
774 0 : errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
775 : }
776 :
777 0 : void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
778 : {
779 0 : std::vector<std::string> parts = split(element,';');
780 :
781 0 : if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
782 0 : ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
783 : {
784 0 : std::vector<std::string> v_pos = split(parts[0],',');
785 0 : std::vector<std::string> v_geom = split(parts[1],',');
786 0 : std::string name = parts[2];
787 0 : std::vector<std::string> items = split(parts[3],',');
788 0 : std::string str_initial_selection = "";
789 0 : std::string str_transparent = "false";
790 :
791 0 : if (parts.size() >= 5)
792 0 : str_initial_selection = parts[4];
793 :
794 0 : if (parts.size() >= 6)
795 0 : str_transparent = parts[5];
796 :
797 0 : MY_CHECKPOS("textlist",0);
798 0 : MY_CHECKGEOM("textlist",1);
799 :
800 0 : v2s32 pos = padding;
801 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
802 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
803 :
804 0 : v2s32 geom;
805 0 : geom.X = stof(v_geom[0]) * (float)spacing.X;
806 0 : geom.Y = stof(v_geom[1]) * (float)spacing.Y;
807 :
808 :
809 0 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
810 :
811 : FieldSpec spec(
812 : name,
813 : L"",
814 : L"",
815 0 : 258+m_fields.size()
816 0 : );
817 :
818 0 : spec.ftype = f_Table;
819 :
820 0 : for (unsigned int i = 0; i < items.size(); ++i) {
821 0 : items[i] = unescape_string(items[i]);
822 : }
823 :
824 : //now really show list
825 : GUITable *e = new GUITable(Environment, this, spec.fid, rect,
826 0 : m_tsrc);
827 :
828 0 : if (spec.fname == data->focused_fieldname) {
829 0 : Environment->setFocus(e);
830 : }
831 :
832 0 : e->setTextList(items, is_yes(str_transparent));
833 :
834 0 : if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
835 0 : e->setDynamicData(data->table_dyndata[name]);
836 : }
837 :
838 0 : if ((str_initial_selection != "") &&
839 0 : (str_initial_selection != "0"))
840 0 : e->setSelected(stoi(str_initial_selection.c_str()));
841 :
842 0 : m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
843 0 : m_fields.push_back(spec);
844 0 : return;
845 : }
846 0 : errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl;
847 : }
848 :
849 :
850 0 : void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
851 : {
852 0 : std::vector<std::string> parts = split(element,';');
853 :
854 0 : if ((parts.size() == 5) ||
855 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
856 : {
857 0 : std::vector<std::string> v_pos = split(parts[0],',');
858 0 : std::string name = parts[2];
859 0 : std::vector<std::string> items = split(parts[3],',');
860 0 : std::string str_initial_selection = "";
861 0 : str_initial_selection = parts[4];
862 :
863 0 : MY_CHECKPOS("dropdown",0);
864 :
865 0 : v2s32 pos = padding;
866 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
867 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
868 :
869 0 : s32 width = stof(parts[1]) * (float)spacing.Y;
870 :
871 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
872 0 : pos.X + width, pos.Y + (m_btn_height * 2));
873 :
874 : FieldSpec spec(
875 : name,
876 : L"",
877 : L"",
878 0 : 258+m_fields.size()
879 0 : );
880 :
881 0 : spec.ftype = f_DropDown;
882 0 : spec.send = true;
883 :
884 : //now really show list
885 0 : gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
886 :
887 0 : if (spec.fname == data->focused_fieldname) {
888 0 : Environment->setFocus(e);
889 : }
890 :
891 0 : for (unsigned int i=0; i < items.size(); i++) {
892 0 : e->addItem(utf8_to_wide(items[i]).c_str());
893 : }
894 :
895 0 : if (str_initial_selection != "")
896 0 : e->setSelected(stoi(str_initial_selection.c_str())-1);
897 :
898 0 : m_fields.push_back(spec);
899 0 : return;
900 : }
901 0 : errorstream << "Invalid dropdown element(" << parts.size() << "): '"
902 0 : << element << "'" << std::endl;
903 : }
904 :
905 2 : void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
906 : {
907 2 : std::vector<std::string> parts = split(element,';');
908 :
909 4 : if ((parts.size() == 4) ||
910 0 : ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
911 : {
912 4 : std::vector<std::string> v_pos = split(parts[0],',');
913 4 : std::vector<std::string> v_geom = split(parts[1],',');
914 4 : std::string name = parts[2];
915 4 : std::string label = parts[3];
916 :
917 2 : MY_CHECKPOS("pwdfield",0);
918 2 : MY_CHECKGEOM("pwdfield",1);
919 :
920 2 : v2s32 pos;
921 2 : pos.X += stof(v_pos[0]) * (float)spacing.X;
922 2 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
923 :
924 2 : v2s32 geom;
925 2 : geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
926 :
927 2 : pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
928 2 : pos.Y -= m_btn_height;
929 2 : geom.Y = m_btn_height*2;
930 :
931 2 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
932 :
933 2 : label = unescape_string(label);
934 :
935 4 : std::wstring wlabel = utf8_to_wide(label);
936 :
937 : FieldSpec spec(
938 : name,
939 : wlabel,
940 : L"",
941 2 : 258+m_fields.size()
942 4 : );
943 :
944 2 : spec.send = true;
945 2 : gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
946 :
947 2 : if (spec.fname == data->focused_fieldname) {
948 0 : Environment->setFocus(e);
949 : }
950 :
951 2 : if (label.length() >= 1)
952 : {
953 0 : int font_height = g_fontengine->getTextHeight();
954 0 : rect.UpperLeftCorner.Y -= font_height;
955 0 : rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
956 0 : Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
957 : }
958 :
959 2 : e->setPasswordBox(true,L'*');
960 :
961 : irr::SEvent evt;
962 2 : evt.EventType = EET_KEY_INPUT_EVENT;
963 2 : evt.KeyInput.Key = KEY_END;
964 2 : evt.KeyInput.Char = 0;
965 2 : evt.KeyInput.Control = 0;
966 2 : evt.KeyInput.Shift = 0;
967 2 : evt.KeyInput.PressedDown = true;
968 2 : e->OnEvent(evt);
969 2 : m_fields.push_back(spec);
970 2 : return;
971 : }
972 0 : errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl;
973 : }
974 :
975 0 : void GUIFormSpecMenu::parseSimpleField(parserData* data,
976 : std::vector<std::string> &parts)
977 : {
978 0 : std::string name = parts[0];
979 0 : std::string label = parts[1];
980 0 : std::string default_val = parts[2];
981 :
982 0 : core::rect<s32> rect;
983 :
984 0 : if(data->explicit_size)
985 0 : errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
986 :
987 0 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
988 0 : pos.Y = ((m_fields.size()+2)*60);
989 0 : v2s32 size = DesiredRect.getSize();
990 :
991 0 : rect = core::rect<s32>(size.X / 2 - 150, pos.Y,
992 0 : (size.X / 2 - 150) + 300, pos.Y + (m_btn_height*2));
993 :
994 :
995 0 : if(m_form_src)
996 0 : default_val = m_form_src->resolveText(default_val);
997 :
998 0 : default_val = unescape_string(default_val);
999 0 : label = unescape_string(label);
1000 :
1001 0 : std::wstring wlabel = utf8_to_wide(label);
1002 :
1003 : FieldSpec spec(
1004 : name,
1005 : wlabel,
1006 0 : utf8_to_wide(default_val),
1007 0 : 258+m_fields.size()
1008 0 : );
1009 :
1010 0 : if (name == "")
1011 : {
1012 : // spec field id to 0, this stops submit searching for a value that isn't there
1013 0 : Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
1014 : }
1015 : else
1016 : {
1017 0 : spec.send = true;
1018 : gui::IGUIElement *e;
1019 : #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
1020 0 : if (g_settings->getBool("freetype")) {
1021 : e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
1022 0 : true, Environment, this, spec.fid, rect);
1023 : } else {
1024 : #else
1025 : {
1026 : #endif
1027 0 : e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
1028 : }
1029 0 : if (spec.fname == data->focused_fieldname) {
1030 0 : Environment->setFocus(e);
1031 : }
1032 :
1033 : irr::SEvent evt;
1034 0 : evt.EventType = EET_KEY_INPUT_EVENT;
1035 0 : evt.KeyInput.Key = KEY_END;
1036 0 : evt.KeyInput.Char = 0;
1037 0 : evt.KeyInput.Control = 0;
1038 0 : evt.KeyInput.Shift = 0;
1039 0 : evt.KeyInput.PressedDown = true;
1040 0 : e->OnEvent(evt);
1041 :
1042 0 : if (label.length() >= 1)
1043 : {
1044 0 : int font_height = g_fontengine->getTextHeight();
1045 0 : rect.UpperLeftCorner.Y -= font_height;
1046 0 : rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
1047 0 : Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
1048 : }
1049 : }
1050 :
1051 0 : m_fields.push_back(spec);
1052 0 : }
1053 :
1054 10 : void GUIFormSpecMenu::parseTextArea(parserData* data,
1055 : std::vector<std::string>& parts,std::string type)
1056 : {
1057 :
1058 20 : std::vector<std::string> v_pos = split(parts[0],',');
1059 20 : std::vector<std::string> v_geom = split(parts[1],',');
1060 20 : std::string name = parts[2];
1061 20 : std::string label = parts[3];
1062 20 : std::string default_val = parts[4];
1063 :
1064 10 : MY_CHECKPOS(type,0);
1065 10 : MY_CHECKGEOM(type,1);
1066 :
1067 10 : v2s32 pos;
1068 10 : pos.X = stof(v_pos[0]) * (float) spacing.X;
1069 10 : pos.Y = stof(v_pos[1]) * (float) spacing.Y;
1070 :
1071 10 : v2s32 geom;
1072 :
1073 10 : geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
1074 :
1075 10 : if (type == "textarea")
1076 : {
1077 4 : geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
1078 4 : pos.Y += m_btn_height;
1079 : }
1080 : else
1081 : {
1082 6 : pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
1083 6 : pos.Y -= m_btn_height;
1084 6 : geom.Y = m_btn_height*2;
1085 : }
1086 :
1087 10 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1088 :
1089 10 : if(!data->explicit_size)
1090 0 : errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
1091 :
1092 10 : if(m_form_src)
1093 10 : default_val = m_form_src->resolveText(default_val);
1094 :
1095 :
1096 10 : default_val = unescape_string(default_val);
1097 10 : label = unescape_string(label);
1098 :
1099 20 : std::wstring wlabel = utf8_to_wide(label);
1100 :
1101 : FieldSpec spec(
1102 : name,
1103 : wlabel,
1104 20 : utf8_to_wide(default_val),
1105 10 : 258+m_fields.size()
1106 30 : );
1107 :
1108 10 : if (name == "")
1109 : {
1110 : // spec field id to 0, this stops submit searching for a value that isn't there
1111 4 : Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
1112 : }
1113 : else
1114 : {
1115 6 : spec.send = true;
1116 :
1117 : gui::IGUIEditBox *e;
1118 : #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
1119 6 : if (g_settings->getBool("freetype")) {
1120 : e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
1121 6 : true, Environment, this, spec.fid, rect);
1122 : } else {
1123 : #else
1124 : {
1125 : #endif
1126 0 : e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
1127 : }
1128 :
1129 6 : if (spec.fname == data->focused_fieldname) {
1130 0 : Environment->setFocus(e);
1131 : }
1132 :
1133 6 : if (type == "textarea")
1134 : {
1135 0 : e->setMultiLine(true);
1136 0 : e->setWordWrap(true);
1137 0 : e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
1138 : } else {
1139 : irr::SEvent evt;
1140 6 : evt.EventType = EET_KEY_INPUT_EVENT;
1141 6 : evt.KeyInput.Key = KEY_END;
1142 6 : evt.KeyInput.Char = 0;
1143 6 : evt.KeyInput.Control = 0;
1144 6 : evt.KeyInput.Shift = 0;
1145 6 : evt.KeyInput.PressedDown = true;
1146 6 : e->OnEvent(evt);
1147 : }
1148 :
1149 6 : if (label.length() >= 1)
1150 : {
1151 0 : int font_height = g_fontengine->getTextHeight();
1152 0 : rect.UpperLeftCorner.Y -= font_height;
1153 0 : rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
1154 0 : Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
1155 : }
1156 : }
1157 10 : m_fields.push_back(spec);
1158 : }
1159 :
1160 10 : void GUIFormSpecMenu::parseField(parserData* data,std::string element,
1161 : std::string type)
1162 : {
1163 10 : std::vector<std::string> parts = split(element,';');
1164 :
1165 10 : if (parts.size() == 3 || parts.size() == 4) {
1166 0 : parseSimpleField(data,parts);
1167 0 : return;
1168 : }
1169 :
1170 20 : if ((parts.size() == 5) ||
1171 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
1172 : {
1173 10 : parseTextArea(data,parts,type);
1174 10 : return;
1175 : }
1176 0 : errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
1177 : }
1178 :
1179 4 : void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
1180 : {
1181 4 : std::vector<std::string> parts = split(element,';');
1182 :
1183 8 : if ((parts.size() == 2) ||
1184 0 : ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
1185 : {
1186 8 : std::vector<std::string> v_pos = split(parts[0],',');
1187 8 : std::string text = parts[1];
1188 :
1189 4 : MY_CHECKPOS("label",0);
1190 :
1191 4 : v2s32 pos = padding;
1192 4 : pos.X += stof(v_pos[0]) * (float)spacing.X;
1193 4 : pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
1194 :
1195 4 : if(!data->explicit_size)
1196 0 : errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
1197 :
1198 4 : text = unescape_string(text);
1199 8 : std::vector<std::string> lines = split(text, '\n');
1200 :
1201 8 : for (unsigned int i = 0; i != lines.size(); i++) {
1202 : // Lines are spaced at the nominal distance of
1203 : // 2/5 inventory slot, even if the font doesn't
1204 : // quite match that. This provides consistent
1205 : // form layout, at the expense of sometimes
1206 : // having sub-optimal spacing for the font.
1207 : // We multiply by 2 and then divide by 5, rather
1208 : // than multiply by 0.4, to get exact results
1209 : // in the integer cases: 0.4 is not exactly
1210 : // representable in binary floating point.
1211 4 : s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
1212 8 : std::wstring wlabel = utf8_to_wide(lines[i]);
1213 : core::rect<s32> rect = core::rect<s32>(
1214 4 : pos.X, posy - m_btn_height,
1215 4 : pos.X + m_font->getDimension(wlabel.c_str()).Width,
1216 12 : posy + m_btn_height);
1217 : FieldSpec spec(
1218 : "",
1219 : wlabel,
1220 : L"",
1221 4 : 258+m_fields.size()
1222 8 : );
1223 : gui::IGUIStaticText *e =
1224 8 : Environment->addStaticText(spec.flabel.c_str(),
1225 8 : rect, false, false, this, spec.fid);
1226 : e->setTextAlignment(gui::EGUIA_UPPERLEFT,
1227 4 : gui::EGUIA_CENTER);
1228 4 : m_fields.push_back(spec);
1229 : }
1230 :
1231 4 : return;
1232 : }
1233 0 : errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
1234 : }
1235 :
1236 0 : void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
1237 : {
1238 0 : std::vector<std::string> parts = split(element,';');
1239 :
1240 0 : if ((parts.size() == 2) ||
1241 0 : ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
1242 : {
1243 0 : std::vector<std::string> v_pos = split(parts[0],',');
1244 0 : std::wstring text = utf8_to_wide(unescape_string(parts[1]));
1245 :
1246 0 : MY_CHECKPOS("vertlabel",1);
1247 :
1248 0 : v2s32 pos = padding;
1249 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
1250 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1251 :
1252 : core::rect<s32> rect = core::rect<s32>(
1253 0 : pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
1254 0 : pos.X+15, pos.Y +
1255 0 : font_line_height(m_font)
1256 0 : * (text.length()+1)
1257 0 : +((imgsize.Y/2)- m_btn_height));
1258 : //actually text.length() would be correct but adding +1 avoids to break all mods
1259 :
1260 0 : if(!data->explicit_size)
1261 0 : errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
1262 :
1263 0 : std::wstring label = L"";
1264 :
1265 0 : for (unsigned int i=0; i < text.length(); i++) {
1266 0 : label += text[i];
1267 0 : label += L"\n";
1268 : }
1269 :
1270 : FieldSpec spec(
1271 : "",
1272 : label,
1273 : L"",
1274 0 : 258+m_fields.size()
1275 0 : );
1276 : gui::IGUIStaticText *t =
1277 0 : Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
1278 0 : t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
1279 0 : m_fields.push_back(spec);
1280 0 : return;
1281 : }
1282 0 : errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
1283 : }
1284 :
1285 0 : void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
1286 : std::string type)
1287 : {
1288 0 : std::vector<std::string> parts = split(element,';');
1289 :
1290 0 : if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
1291 0 : ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
1292 : {
1293 0 : std::vector<std::string> v_pos = split(parts[0],',');
1294 0 : std::vector<std::string> v_geom = split(parts[1],',');
1295 0 : std::string image_name = parts[2];
1296 0 : std::string name = parts[3];
1297 0 : std::string label = parts[4];
1298 :
1299 0 : MY_CHECKPOS("imagebutton",0);
1300 0 : MY_CHECKGEOM("imagebutton",1);
1301 :
1302 0 : v2s32 pos = padding;
1303 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
1304 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1305 0 : v2s32 geom;
1306 0 : geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
1307 0 : geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
1308 :
1309 0 : bool noclip = false;
1310 0 : bool drawborder = true;
1311 0 : std::string pressed_image_name = "";
1312 :
1313 0 : if (parts.size() >= 7) {
1314 0 : if (parts[5] == "true")
1315 0 : noclip = true;
1316 0 : if (parts[6] == "false")
1317 0 : drawborder = false;
1318 : }
1319 :
1320 0 : if (parts.size() >= 8) {
1321 0 : pressed_image_name = parts[7];
1322 : }
1323 :
1324 0 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1325 :
1326 0 : if(!data->explicit_size)
1327 0 : errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
1328 :
1329 0 : image_name = unescape_string(image_name);
1330 0 : pressed_image_name = unescape_string(pressed_image_name);
1331 0 : label = unescape_string(label);
1332 :
1333 0 : std::wstring wlabel = utf8_to_wide(label);
1334 :
1335 : FieldSpec spec(
1336 : name,
1337 : wlabel,
1338 0 : utf8_to_wide(image_name),
1339 0 : 258+m_fields.size()
1340 0 : );
1341 0 : spec.ftype = f_Button;
1342 0 : if(type == "image_button_exit")
1343 0 : spec.is_exit = true;
1344 :
1345 0 : video::ITexture *texture = 0;
1346 0 : video::ITexture *pressed_texture = 0;
1347 0 : texture = m_tsrc->getTexture(image_name);
1348 0 : if (pressed_image_name != "")
1349 0 : pressed_texture = m_tsrc->getTexture(pressed_image_name);
1350 : else
1351 0 : pressed_texture = texture;
1352 :
1353 0 : gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
1354 :
1355 0 : if (spec.fname == data->focused_fieldname) {
1356 0 : Environment->setFocus(e);
1357 : }
1358 :
1359 0 : e->setUseAlphaChannel(true);
1360 0 : e->setImage(guiScalingImageButton(
1361 0 : Environment->getVideoDriver(), texture, geom.X, geom.Y));
1362 0 : e->setPressedImage(guiScalingImageButton(
1363 0 : Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
1364 0 : e->setScaleImage(true);
1365 0 : e->setNotClipped(noclip);
1366 0 : e->setDrawBorder(drawborder);
1367 :
1368 0 : m_fields.push_back(spec);
1369 0 : return;
1370 : }
1371 :
1372 0 : errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
1373 : }
1374 :
1375 2 : void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
1376 : {
1377 2 : std::vector<std::string> parts = split(element,';');
1378 :
1379 4 : if (((parts.size() == 4) || (parts.size() == 6)) ||
1380 0 : ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
1381 : {
1382 4 : std::vector<std::string> v_pos = split(parts[0],',');
1383 4 : std::string name = parts[1];
1384 4 : std::vector<std::string> buttons = split(parts[2],',');
1385 4 : std::string str_index = parts[3];
1386 2 : bool show_background = true;
1387 2 : bool show_border = true;
1388 2 : int tab_index = stoi(str_index) -1;
1389 :
1390 2 : MY_CHECKPOS("tabheader",0);
1391 :
1392 2 : if (parts.size() == 6) {
1393 2 : if (parts[4] == "true")
1394 2 : show_background = false;
1395 2 : if (parts[5] == "false")
1396 2 : show_border = false;
1397 : }
1398 :
1399 : FieldSpec spec(
1400 : name,
1401 : L"",
1402 : L"",
1403 2 : 258+m_fields.size()
1404 4 : );
1405 :
1406 2 : spec.ftype = f_TabHeader;
1407 :
1408 2 : v2s32 pos(0,0);
1409 2 : pos.X += stof(v_pos[0]) * (float)spacing.X;
1410 2 : pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
1411 2 : v2s32 geom;
1412 2 : geom.X = DesiredRect.getWidth();
1413 2 : geom.Y = m_btn_height*2;
1414 :
1415 2 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
1416 4 : pos.Y+geom.Y);
1417 :
1418 4 : gui::IGUITabControl *e = Environment->addTabControl(rect, this,
1419 4 : show_background, show_border, spec.fid);
1420 2 : e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
1421 2 : irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
1422 2 : e->setTabHeight(m_btn_height*2);
1423 :
1424 2 : if (spec.fname == data->focused_fieldname) {
1425 0 : Environment->setFocus(e);
1426 : }
1427 :
1428 2 : e->setNotClipped(true);
1429 :
1430 16 : for (unsigned int i = 0; i < buttons.size(); i++) {
1431 14 : e->addTab(utf8_to_wide(buttons[i]).c_str(), -1);
1432 : }
1433 :
1434 4 : if ((tab_index >= 0) &&
1435 4 : (buttons.size() < INT_MAX) &&
1436 2 : (tab_index < (int) buttons.size()))
1437 2 : e->setActiveTab(tab_index);
1438 :
1439 2 : m_fields.push_back(spec);
1440 2 : return;
1441 : }
1442 0 : errorstream << "Invalid TabHeader element(" << parts.size() << "): '"
1443 0 : << element << "'" << std::endl;
1444 : }
1445 :
1446 0 : void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
1447 : {
1448 :
1449 0 : if (m_gamedef == 0) {
1450 : errorstream <<
1451 0 : "WARNING: invalid use of item_image_button with m_gamedef==0"
1452 0 : << std::endl;
1453 0 : return;
1454 : }
1455 :
1456 0 : std::vector<std::string> parts = split(element,';');
1457 :
1458 0 : if ((parts.size() == 5) ||
1459 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
1460 : {
1461 0 : std::vector<std::string> v_pos = split(parts[0],',');
1462 0 : std::vector<std::string> v_geom = split(parts[1],',');
1463 0 : std::string item_name = parts[2];
1464 0 : std::string name = parts[3];
1465 0 : std::string label = parts[4];
1466 :
1467 0 : MY_CHECKPOS("itemimagebutton",0);
1468 0 : MY_CHECKGEOM("itemimagebutton",1);
1469 :
1470 0 : v2s32 pos = padding;
1471 0 : pos.X += stof(v_pos[0]) * (float)spacing.X;
1472 0 : pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1473 0 : v2s32 geom;
1474 0 : geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
1475 0 : geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
1476 :
1477 0 : core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1478 :
1479 0 : if(!data->explicit_size)
1480 0 : errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
1481 :
1482 0 : IItemDefManager *idef = m_gamedef->idef();
1483 0 : ItemStack item;
1484 0 : item.deSerialize(item_name, idef);
1485 0 : video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
1486 :
1487 0 : m_tooltips[name] =
1488 0 : TooltipSpec(item.getDefinition(idef).description,
1489 : m_default_tooltip_bgcolor,
1490 0 : m_default_tooltip_color);
1491 :
1492 0 : label = unescape_string(label);
1493 : FieldSpec spec(
1494 : name,
1495 0 : utf8_to_wide(label),
1496 0 : utf8_to_wide(item_name),
1497 0 : 258 + m_fields.size()
1498 0 : );
1499 :
1500 0 : gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
1501 :
1502 0 : if (spec.fname == data->focused_fieldname) {
1503 0 : Environment->setFocus(e);
1504 : }
1505 :
1506 0 : e->setUseAlphaChannel(true);
1507 0 : e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
1508 0 : e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
1509 0 : e->setScaleImage(true);
1510 0 : spec.ftype = f_Button;
1511 0 : rect+=data->basepos-padding;
1512 0 : spec.rect=rect;
1513 0 : m_fields.push_back(spec);
1514 0 : return;
1515 : }
1516 0 : errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
1517 : }
1518 :
1519 2 : void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
1520 : {
1521 2 : std::vector<std::string> parts = split(element,';');
1522 :
1523 4 : if ((parts.size() == 3) ||
1524 0 : ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
1525 : {
1526 4 : std::vector<std::string> v_pos = split(parts[0],',');
1527 4 : std::vector<std::string> v_geom = split(parts[1],',');
1528 :
1529 2 : MY_CHECKPOS("box",0);
1530 2 : MY_CHECKGEOM("box",1);
1531 :
1532 2 : v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
1533 2 : pos.X += stof(v_pos[0]) * (float) spacing.X;
1534 2 : pos.Y += stof(v_pos[1]) * (float) spacing.Y;
1535 :
1536 2 : v2s32 geom;
1537 2 : geom.X = stof(v_geom[0]) * (float)spacing.X;
1538 2 : geom.Y = stof(v_geom[1]) * (float)spacing.Y;
1539 :
1540 2 : video::SColor tmp_color;
1541 :
1542 2 : if (parseColorString(parts[2], tmp_color, false)) {
1543 2 : BoxDrawSpec spec(pos, geom, tmp_color);
1544 :
1545 2 : m_boxes.push_back(spec);
1546 : }
1547 : else {
1548 0 : errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
1549 : }
1550 2 : return;
1551 : }
1552 0 : errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
1553 : }
1554 :
1555 0 : void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
1556 : {
1557 0 : std::vector<std::string> parts = split(element,';');
1558 :
1559 0 : if (((parts.size() == 1) || (parts.size() == 2)) ||
1560 0 : ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
1561 : {
1562 0 : parseColorString(parts[0],m_bgcolor,false);
1563 :
1564 0 : if (parts.size() == 2) {
1565 0 : std::string fullscreen = parts[1];
1566 0 : m_bgfullscreen = is_yes(fullscreen);
1567 : }
1568 0 : return;
1569 : }
1570 0 : errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
1571 : }
1572 :
1573 0 : void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
1574 : {
1575 0 : std::vector<std::string> parts = split(element,';');
1576 :
1577 0 : if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
1578 0 : ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
1579 : {
1580 0 : parseColorString(parts[0], m_slotbg_n, false);
1581 0 : parseColorString(parts[1], m_slotbg_h, false);
1582 :
1583 0 : if (parts.size() >= 3) {
1584 0 : if (parseColorString(parts[2], m_slotbordercolor, false)) {
1585 0 : m_slotborder = true;
1586 : }
1587 : }
1588 0 : if (parts.size() == 5) {
1589 0 : video::SColor tmp_color;
1590 :
1591 0 : if (parseColorString(parts[3], tmp_color, false))
1592 0 : m_default_tooltip_bgcolor = tmp_color;
1593 0 : if (parseColorString(parts[4], tmp_color, false))
1594 0 : m_default_tooltip_color = tmp_color;
1595 : }
1596 0 : return;
1597 : }
1598 0 : errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
1599 : }
1600 :
1601 0 : void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
1602 : {
1603 0 : std::vector<std::string> parts = split(element,';');
1604 0 : if (parts.size() == 2) {
1605 0 : std::string name = parts[0];
1606 0 : m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
1607 0 : m_default_tooltip_bgcolor, m_default_tooltip_color);
1608 0 : return;
1609 0 : } else if (parts.size() == 4) {
1610 0 : std::string name = parts[0];
1611 0 : video::SColor tmp_color1, tmp_color2;
1612 0 : if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
1613 0 : m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
1614 0 : tmp_color1, tmp_color2);
1615 0 : return;
1616 : }
1617 : }
1618 0 : errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
1619 : }
1620 :
1621 5 : bool GUIFormSpecMenu::parseVersionDirect(std::string data)
1622 : {
1623 : //some prechecks
1624 5 : if (data == "")
1625 2 : return false;
1626 :
1627 6 : std::vector<std::string> parts = split(data,'[');
1628 :
1629 3 : if (parts.size() < 2) {
1630 0 : return false;
1631 : }
1632 :
1633 3 : if (parts[0] != "formspec_version") {
1634 0 : return false;
1635 : }
1636 :
1637 3 : if (is_number(parts[1])) {
1638 3 : m_formspec_version = mystoi(parts[1]);
1639 3 : return true;
1640 : }
1641 :
1642 0 : return false;
1643 : }
1644 :
1645 8 : bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element)
1646 : {
1647 8 : if (element == "")
1648 2 : return false;
1649 :
1650 12 : std::vector<std::string> parts = split(element,'[');
1651 :
1652 6 : if (parts.size() < 2)
1653 0 : return false;
1654 :
1655 12 : std::string type = trim(parts[0]);
1656 12 : std::string description = trim(parts[1]);
1657 :
1658 6 : if (type != "size" && type != "invsize")
1659 3 : return false;
1660 :
1661 3 : if (type == "invsize")
1662 0 : log_deprecated("Deprecated formspec element \"invsize\" is used");
1663 :
1664 3 : parseSize(data, description);
1665 :
1666 3 : return true;
1667 : }
1668 :
1669 41 : void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
1670 : {
1671 : //some prechecks
1672 41 : if (element == "")
1673 46 : return;
1674 :
1675 36 : std::vector<std::string> parts = split(element,'[');
1676 :
1677 : // ugly workaround to keep compatibility
1678 36 : if (parts.size() > 2) {
1679 0 : if (trim(parts[0]) == "image") {
1680 0 : for (unsigned int i=2;i< parts.size(); i++) {
1681 0 : parts[1] += "[" + parts[i];
1682 : }
1683 : }
1684 0 : else { return; }
1685 : }
1686 :
1687 36 : if (parts.size() < 2) {
1688 0 : return;
1689 : }
1690 :
1691 36 : std::string type = trim(parts[0]);
1692 36 : std::string description = trim(parts[1]);
1693 :
1694 36 : if (type == "list") {
1695 0 : parseList(data,description);
1696 0 : return;
1697 : }
1698 :
1699 36 : if (type == "listring") {
1700 0 : parseListRing(data, description);
1701 0 : return;
1702 : }
1703 :
1704 36 : if (type == "checkbox") {
1705 2 : parseCheckbox(data,description);
1706 2 : return;
1707 : }
1708 :
1709 34 : if (type == "image") {
1710 0 : parseImage(data,description);
1711 0 : return;
1712 : }
1713 :
1714 34 : if (type == "item_image") {
1715 0 : parseItemImage(data,description);
1716 0 : return;
1717 : }
1718 :
1719 34 : if ((type == "button") || (type == "button_exit")) {
1720 10 : parseButton(data,description,type);
1721 10 : return;
1722 : }
1723 :
1724 24 : if (type == "background") {
1725 0 : parseBackground(data,description);
1726 0 : return;
1727 : }
1728 :
1729 24 : if (type == "tableoptions"){
1730 0 : parseTableOptions(data,description);
1731 0 : return;
1732 : }
1733 :
1734 24 : if (type == "tablecolumns"){
1735 2 : parseTableColumns(data,description);
1736 2 : return;
1737 : }
1738 :
1739 22 : if (type == "table"){
1740 2 : parseTable(data,description);
1741 2 : return;
1742 : }
1743 :
1744 20 : if (type == "textlist"){
1745 0 : parseTextList(data,description);
1746 0 : return;
1747 : }
1748 :
1749 20 : if (type == "dropdown"){
1750 0 : parseDropDown(data,description);
1751 0 : return;
1752 : }
1753 :
1754 20 : if (type == "pwdfield") {
1755 2 : parsePwdField(data,description);
1756 2 : return;
1757 : }
1758 :
1759 18 : if ((type == "field") || (type == "textarea")){
1760 10 : parseField(data,description,type);
1761 10 : return;
1762 : }
1763 :
1764 8 : if (type == "label") {
1765 4 : parseLabel(data,description);
1766 4 : return;
1767 : }
1768 :
1769 4 : if (type == "vertlabel") {
1770 0 : parseVertLabel(data,description);
1771 0 : return;
1772 : }
1773 :
1774 4 : if (type == "item_image_button") {
1775 0 : parseItemImageButton(data,description);
1776 0 : return;
1777 : }
1778 :
1779 4 : if ((type == "image_button") || (type == "image_button_exit")) {
1780 0 : parseImageButton(data,description,type);
1781 0 : return;
1782 : }
1783 :
1784 4 : if (type == "tabheader") {
1785 2 : parseTabHeader(data,description);
1786 2 : return;
1787 : }
1788 :
1789 2 : if (type == "box") {
1790 2 : parseBox(data,description);
1791 2 : return;
1792 : }
1793 :
1794 0 : if (type == "bgcolor") {
1795 0 : parseBackgroundColor(data,description);
1796 0 : return;
1797 : }
1798 :
1799 0 : if (type == "listcolors") {
1800 0 : parseListColors(data,description);
1801 0 : return;
1802 : }
1803 :
1804 0 : if (type == "tooltip") {
1805 0 : parseTooltip(data,description);
1806 0 : return;
1807 : }
1808 :
1809 0 : if (type == "scrollbar") {
1810 0 : parseScrollBar(data, description);
1811 0 : return;
1812 : }
1813 :
1814 : // Ignore others
1815 : infostream
1816 0 : << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
1817 0 : <<std::endl;
1818 : }
1819 :
1820 5 : void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
1821 : {
1822 : /* useless to regenerate without a screensize */
1823 5 : if ((screensize.X <= 0) || (screensize.Y <= 0)) {
1824 0 : return;
1825 : }
1826 :
1827 10 : parserData mydata;
1828 :
1829 : //preserve tables
1830 6 : for (u32 i = 0; i < m_tables.size(); ++i) {
1831 2 : std::string tablename = m_tables[i].first.fname;
1832 1 : GUITable *table = m_tables[i].second;
1833 1 : mydata.table_dyndata[tablename] = table->getDynamicData();
1834 : }
1835 :
1836 : //set focus
1837 5 : if (!m_focused_element.empty())
1838 2 : mydata.focused_fieldname = m_focused_element;
1839 :
1840 : //preserve focus
1841 5 : gui::IGUIElement *focused_element = Environment->getFocus();
1842 5 : if (focused_element && focused_element->getParent() == this) {
1843 1 : s32 focused_id = focused_element->getID();
1844 1 : if (focused_id > 257) {
1845 12 : for (u32 i=0; i<m_fields.size(); i++) {
1846 12 : if (m_fields[i].fid == focused_id) {
1847 1 : mydata.focused_fieldname =
1848 2 : m_fields[i].fname;
1849 1 : break;
1850 : }
1851 : }
1852 : }
1853 : }
1854 :
1855 : // Remove children
1856 5 : removeChildren();
1857 :
1858 6 : for (u32 i = 0; i < m_tables.size(); ++i) {
1859 1 : GUITable *table = m_tables[i].second;
1860 1 : table->drop();
1861 : }
1862 :
1863 5 : mydata.size= v2s32(100,100);
1864 5 : mydata.screensize = screensize;
1865 :
1866 : // Base position of contents of form
1867 5 : mydata.basepos = getBasePos();
1868 :
1869 : /* Convert m_init_draw_spec to m_inventorylists */
1870 :
1871 5 : m_inventorylists.clear();
1872 5 : m_images.clear();
1873 5 : m_backgrounds.clear();
1874 5 : m_itemimages.clear();
1875 5 : m_tables.clear();
1876 5 : m_checkboxes.clear();
1877 5 : m_scrollbars.clear();
1878 5 : m_fields.clear();
1879 5 : m_boxes.clear();
1880 5 : m_tooltips.clear();
1881 :
1882 : // Set default values (fits old formspec values)
1883 5 : m_bgcolor = video::SColor(140,0,0,0);
1884 5 : m_bgfullscreen = false;
1885 :
1886 5 : m_slotbg_n = video::SColor(255,128,128,128);
1887 5 : m_slotbg_h = video::SColor(255,192,192,192);
1888 :
1889 5 : m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
1890 5 : m_default_tooltip_color = video::SColor(255,255,255,255);
1891 :
1892 5 : m_slotbordercolor = video::SColor(200,0,0,0);
1893 5 : m_slotborder = false;
1894 :
1895 5 : m_clipbackground = false;
1896 : // Add tooltip
1897 : {
1898 : assert(m_tooltip_element == NULL);
1899 : // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
1900 5 : m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
1901 5 : m_tooltip_element->enableOverrideColor(true);
1902 5 : m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
1903 5 : m_tooltip_element->setDrawBackground(true);
1904 5 : m_tooltip_element->setDrawBorder(true);
1905 5 : m_tooltip_element->setOverrideColor(m_default_tooltip_color);
1906 5 : m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
1907 5 : m_tooltip_element->setWordWrap(false);
1908 : //we're not parent so no autograb for this one!
1909 5 : m_tooltip_element->grab();
1910 : }
1911 :
1912 10 : std::vector<std::string> elements = split(m_formspec_string,']');
1913 5 : unsigned int i = 0;
1914 :
1915 : /* try to read version from first element only */
1916 5 : if (elements.size() >= 1) {
1917 5 : if ( parseVersionDirect(elements[0]) ) {
1918 3 : i++;
1919 : }
1920 : }
1921 :
1922 : /* we need size first in order to calculate image scale */
1923 5 : mydata.explicit_size = false;
1924 11 : for (; i< elements.size(); i++) {
1925 8 : if (!parseSizeDirect(&mydata, elements[i])) {
1926 5 : break;
1927 : }
1928 : }
1929 :
1930 5 : if (mydata.explicit_size) {
1931 : // compute scaling for specified form size
1932 3 : if (m_lock) {
1933 1 : v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
1934 1 : v2u32 delta = current_screensize - m_lockscreensize;
1935 :
1936 1 : if (current_screensize.Y > m_lockscreensize.Y)
1937 0 : delta.Y /= 2;
1938 : else
1939 1 : delta.Y = 0;
1940 :
1941 1 : if (current_screensize.X > m_lockscreensize.X)
1942 0 : delta.X /= 2;
1943 : else
1944 1 : delta.X = 0;
1945 :
1946 1 : offset = v2s32(delta.X,delta.Y);
1947 :
1948 1 : mydata.screensize = m_lockscreensize;
1949 : } else {
1950 2 : offset = v2s32(0,0);
1951 : }
1952 :
1953 3 : double gui_scaling = g_settings->getFloat("gui_scaling");
1954 3 : double screen_dpi = porting::getDisplayDensity() * 96;
1955 :
1956 : double use_imgsize;
1957 3 : if (m_lock) {
1958 : // In fixed-size mode, inventory image size
1959 : // is 0.53 inch multiplied by the gui_scaling
1960 : // config parameter. This magic size is chosen
1961 : // to make the main menu (15.5 inventory images
1962 : // wide, including border) just fit into the
1963 : // default window (800 pixels wide) at 96 DPI
1964 : // and default scaling (1.00).
1965 1 : use_imgsize = 0.5555 * screen_dpi * gui_scaling;
1966 : } else {
1967 : // In variable-size mode, we prefer to make the
1968 : // inventory image size 1/15 of screen height,
1969 : // multiplied by the gui_scaling config parameter.
1970 : // If the preferred size won't fit the whole
1971 : // form on the screen, either horizontally or
1972 : // vertically, then we scale it down to fit.
1973 : // (The magic numbers in the computation of what
1974 : // fits arise from the scaling factors in the
1975 : // following stanza, including the form border,
1976 : // help text space, and 0.1 inventory slot spare.)
1977 : // However, a minimum size is also set, that
1978 : // the image size can't be less than 0.3 inch
1979 : // multiplied by gui_scaling, even if this means
1980 : // the form doesn't fit the screen.
1981 2 : double prefer_imgsize = mydata.screensize.Y / 15 *
1982 2 : gui_scaling;
1983 2 : double fitx_imgsize = mydata.screensize.X /
1984 2 : ((5.0/4.0) * (0.5 + mydata.invsize.X));
1985 2 : double fity_imgsize = mydata.screensize.Y /
1986 2 : ((15.0/13.0) * (0.85 * mydata.invsize.Y));
1987 2 : double screen_dpi = porting::getDisplayDensity() * 96;
1988 2 : double min_imgsize = 0.3 * screen_dpi * gui_scaling;
1989 4 : use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
1990 4 : MYMIN(fitx_imgsize, fity_imgsize)));
1991 : }
1992 :
1993 : // Everything else is scaled in proportion to the
1994 : // inventory image size. The inventory slot spacing
1995 : // is 5/4 image size horizontally and 15/13 image size
1996 : // vertically. The padding around the form (incorporating
1997 : // the border of the outer inventory slots) is 3/8
1998 : // image size. Font height (baseline to baseline)
1999 : // is 2/5 vertical inventory slot spacing, and button
2000 : // half-height is 7/8 of font height.
2001 3 : imgsize = v2s32(use_imgsize, use_imgsize);
2002 3 : spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13);
2003 3 : padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8);
2004 3 : m_btn_height = use_imgsize*15.0/13 * 0.35;
2005 :
2006 3 : m_font = g_fontengine->getFont();
2007 :
2008 9 : mydata.size = v2s32(
2009 3 : padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
2010 3 : padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
2011 3 : );
2012 15 : DesiredRect = mydata.rect = core::rect<s32>(
2013 3 : mydata.screensize.X/2 - mydata.size.X/2 + offset.X,
2014 3 : mydata.screensize.Y/2 - mydata.size.Y/2 + offset.Y,
2015 3 : mydata.screensize.X/2 + mydata.size.X/2 + offset.X,
2016 3 : mydata.screensize.Y/2 + mydata.size.Y/2 + offset.Y
2017 3 : );
2018 : } else {
2019 : // Non-size[] form must consist only of text fields and
2020 : // implicit "Proceed" button. Use default font, and
2021 : // temporary form size which will be recalculated below.
2022 2 : m_font = g_fontengine->getFont();
2023 2 : m_btn_height = font_line_height(m_font) * 0.875;
2024 10 : DesiredRect = core::rect<s32>(
2025 2 : mydata.screensize.X/2 - 580/2,
2026 2 : mydata.screensize.Y/2 - 300/2,
2027 2 : mydata.screensize.X/2 + 580/2,
2028 2 : mydata.screensize.Y/2 + 300/2
2029 2 : );
2030 : }
2031 5 : recalculateAbsolutePosition(false);
2032 5 : mydata.basepos = getBasePos();
2033 5 : m_tooltip_element->setOverrideFont(m_font);
2034 :
2035 5 : gui::IGUISkin* skin = Environment->getSkin();
2036 5 : sanity_check(skin != NULL);
2037 5 : gui::IGUIFont *old_font = skin->getFont();
2038 5 : skin->setFont(m_font);
2039 :
2040 87 : for (; i< elements.size(); i++) {
2041 41 : parseElement(&mydata, elements[i]);
2042 : }
2043 :
2044 : // If there are fields without explicit size[], add a "Proceed"
2045 : // button and adjust size to fit all the fields.
2046 5 : if (m_fields.size() && !mydata.explicit_size) {
2047 0 : mydata.rect = core::rect<s32>(
2048 0 : mydata.screensize.X/2 - 580/2,
2049 0 : mydata.screensize.Y/2 - 300/2,
2050 0 : mydata.screensize.X/2 + 580/2,
2051 0 : mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
2052 0 : );
2053 0 : DesiredRect = mydata.rect;
2054 0 : recalculateAbsolutePosition(false);
2055 0 : mydata.basepos = getBasePos();
2056 :
2057 : {
2058 0 : v2s32 pos = mydata.basepos;
2059 0 : pos.Y = ((m_fields.size()+2)*60);
2060 :
2061 0 : v2s32 size = DesiredRect.getSize();
2062 0 : mydata.rect =
2063 0 : core::rect<s32>(size.X/2-70, pos.Y,
2064 0 : (size.X/2-70)+140, pos.Y + (m_btn_height*2));
2065 0 : const wchar_t *text = wgettext("Proceed");
2066 0 : Environment->addButton(mydata.rect, this, 257, text);
2067 0 : delete[] text;
2068 : }
2069 :
2070 : }
2071 :
2072 : //set initial focus if parser didn't set it
2073 5 : focused_element = Environment->getFocus();
2074 5 : if (!focused_element
2075 5 : || !isMyChild(focused_element)
2076 7 : || focused_element->getType() == gui::EGUIET_TAB_CONTROL)
2077 3 : setInitialFocus();
2078 :
2079 5 : skin->setFont(old_font);
2080 : }
2081 :
2082 : #ifdef __ANDROID__
2083 : bool GUIFormSpecMenu::getAndroidUIInput()
2084 : {
2085 : /* no dialog shown */
2086 : if (m_JavaDialogFieldName == "") {
2087 : return false;
2088 : }
2089 :
2090 : /* still waiting */
2091 : if (porting::getInputDialogState() == -1) {
2092 : return true;
2093 : }
2094 :
2095 : std::string fieldname = m_JavaDialogFieldName;
2096 : m_JavaDialogFieldName = "";
2097 :
2098 : /* no value abort dialog processing */
2099 : if (porting::getInputDialogState() != 0) {
2100 : return false;
2101 : }
2102 :
2103 : for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
2104 : iter != m_fields.end(); iter++) {
2105 :
2106 : if (iter->fname != fieldname) {
2107 : continue;
2108 : }
2109 : IGUIElement* tochange = getElementFromId(iter->fid);
2110 :
2111 : if (tochange == 0) {
2112 : return false;
2113 : }
2114 :
2115 : if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
2116 : return false;
2117 : }
2118 :
2119 : std::string text = porting::getInputDialogValue();
2120 :
2121 : ((gui::IGUIEditBox*) tochange)->
2122 : setText(utf8_to_wide(text).c_str());
2123 : }
2124 : return false;
2125 : }
2126 : #endif
2127 :
2128 0 : GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
2129 : {
2130 0 : core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
2131 :
2132 0 : for(u32 i=0; i<m_inventorylists.size(); i++)
2133 : {
2134 0 : const ListDrawSpec &s = m_inventorylists[i];
2135 :
2136 0 : for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
2137 0 : s32 item_i = i + s.start_item_i;
2138 0 : s32 x = (i%s.geom.X) * spacing.X;
2139 0 : s32 y = (i/s.geom.X) * spacing.Y;
2140 0 : v2s32 p0(x,y);
2141 0 : core::rect<s32> rect = imgrect + s.pos + p0;
2142 0 : if(rect.isPointInside(p))
2143 : {
2144 0 : return ItemSpec(s.inventoryloc, s.listname, item_i);
2145 : }
2146 : }
2147 : }
2148 :
2149 0 : return ItemSpec(InventoryLocation(), "", -1);
2150 : }
2151 :
2152 0 : void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
2153 : {
2154 0 : video::IVideoDriver* driver = Environment->getVideoDriver();
2155 :
2156 0 : Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
2157 0 : if(!inv){
2158 0 : infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
2159 0 : <<"The inventory location "
2160 0 : <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
2161 0 : <<std::endl;
2162 0 : return;
2163 : }
2164 0 : InventoryList *ilist = inv->getList(s.listname);
2165 0 : if(!ilist){
2166 0 : infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
2167 0 : <<"The inventory list \""<<s.listname<<"\" @ \""
2168 0 : <<s.inventoryloc.dump()<<"\" doesn't exist"
2169 0 : <<std::endl;
2170 0 : return;
2171 : }
2172 :
2173 0 : core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
2174 :
2175 0 : for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
2176 : {
2177 0 : s32 item_i = i + s.start_item_i;
2178 0 : if(item_i >= (s32) ilist->getSize())
2179 0 : break;
2180 0 : s32 x = (i%s.geom.X) * spacing.X;
2181 0 : s32 y = (i/s.geom.X) * spacing.Y;
2182 0 : v2s32 p(x,y);
2183 0 : core::rect<s32> rect = imgrect + s.pos + p;
2184 0 : ItemStack item;
2185 0 : if(ilist)
2186 0 : item = ilist->getItem(item_i);
2187 :
2188 0 : bool selected = m_selected_item
2189 0 : && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
2190 0 : && m_selected_item->listname == s.listname
2191 0 : && m_selected_item->i == item_i;
2192 0 : bool hovering = rect.isPointInside(m_pointer);
2193 :
2194 0 : if(phase == 0)
2195 : {
2196 0 : if(hovering)
2197 0 : driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
2198 : else
2199 0 : driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
2200 : }
2201 :
2202 : //Draw inv slot borders
2203 0 : if (m_slotborder) {
2204 0 : s32 x1 = rect.UpperLeftCorner.X;
2205 0 : s32 y1 = rect.UpperLeftCorner.Y;
2206 0 : s32 x2 = rect.LowerRightCorner.X;
2207 0 : s32 y2 = rect.LowerRightCorner.Y;
2208 0 : s32 border = 1;
2209 0 : driver->draw2DRectangle(m_slotbordercolor,
2210 : core::rect<s32>(v2s32(x1 - border, y1 - border),
2211 0 : v2s32(x2 + border, y1)), NULL);
2212 0 : driver->draw2DRectangle(m_slotbordercolor,
2213 : core::rect<s32>(v2s32(x1 - border, y2),
2214 0 : v2s32(x2 + border, y2 + border)), NULL);
2215 0 : driver->draw2DRectangle(m_slotbordercolor,
2216 : core::rect<s32>(v2s32(x1 - border, y1),
2217 0 : v2s32(x1, y2)), NULL);
2218 0 : driver->draw2DRectangle(m_slotbordercolor,
2219 : core::rect<s32>(v2s32(x2, y1),
2220 0 : v2s32(x2 + border, y2)), NULL);
2221 : }
2222 :
2223 0 : if(phase == 1)
2224 : {
2225 : // Draw item stack
2226 0 : if(selected)
2227 : {
2228 0 : item.takeItem(m_selected_amount);
2229 : }
2230 0 : if(!item.empty())
2231 : {
2232 0 : drawItemStack(driver, m_font, item,
2233 0 : rect, &AbsoluteClippingRect, m_gamedef);
2234 : }
2235 :
2236 : // Draw tooltip
2237 0 : std::string tooltip_text = "";
2238 0 : if (hovering && !m_selected_item)
2239 0 : tooltip_text = item.getDefinition(m_gamedef->idef()).description;
2240 0 : if (tooltip_text != "") {
2241 0 : std::vector<std::string> tt_rows = str_split(tooltip_text, '\n');
2242 0 : m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
2243 0 : m_tooltip_element->setOverrideColor(m_default_tooltip_color);
2244 0 : m_tooltip_element->setVisible(true);
2245 0 : this->bringToFront(m_tooltip_element);
2246 0 : m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str());
2247 0 : s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
2248 0 : s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
2249 0 : v2u32 screenSize = driver->getScreenSize();
2250 0 : int tooltip_offset_x = m_btn_height;
2251 0 : int tooltip_offset_y = m_btn_height;
2252 : #ifdef __ANDROID__
2253 : tooltip_offset_x *= 3;
2254 : tooltip_offset_y = 0;
2255 : if (m_pointer.X > (s32)screenSize.X / 2)
2256 : tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1;
2257 : #endif
2258 0 : s32 tooltip_x = m_pointer.X + tooltip_offset_x;
2259 0 : s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
2260 0 : if (tooltip_x + tooltip_width > (s32)screenSize.X)
2261 0 : tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
2262 0 : if (tooltip_y + tooltip_height > (s32)screenSize.Y)
2263 0 : tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
2264 0 : m_tooltip_element->setRelativePosition(core::rect<s32>(
2265 : core::position2d<s32>(tooltip_x, tooltip_y),
2266 0 : core::dimension2d<s32>(tooltip_width, tooltip_height)));
2267 : }
2268 : }
2269 : }
2270 : }
2271 :
2272 111 : void GUIFormSpecMenu::drawSelectedItem()
2273 : {
2274 111 : if(!m_selected_item)
2275 111 : return;
2276 :
2277 0 : video::IVideoDriver* driver = Environment->getVideoDriver();
2278 :
2279 0 : Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
2280 0 : sanity_check(inv);
2281 0 : InventoryList *list = inv->getList(m_selected_item->listname);
2282 0 : sanity_check(list);
2283 0 : ItemStack stack = list->getItem(m_selected_item->i);
2284 0 : stack.count = m_selected_amount;
2285 :
2286 0 : core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
2287 0 : core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
2288 0 : drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef);
2289 : }
2290 :
2291 111 : void GUIFormSpecMenu::drawMenu()
2292 : {
2293 111 : if(m_form_src){
2294 222 : std::string newform = m_form_src->getForm();
2295 111 : if(newform != m_formspec_string){
2296 3 : m_formspec_string = newform;
2297 3 : regenerateGui(m_screensize_old);
2298 : }
2299 : }
2300 :
2301 111 : gui::IGUISkin* skin = Environment->getSkin();
2302 111 : sanity_check(skin != NULL);
2303 111 : gui::IGUIFont *old_font = skin->getFont();
2304 111 : skin->setFont(m_font);
2305 :
2306 111 : updateSelectedItem();
2307 :
2308 111 : video::IVideoDriver* driver = Environment->getVideoDriver();
2309 :
2310 111 : v2u32 screenSize = driver->getScreenSize();
2311 111 : core::rect<s32> allbg(0, 0, screenSize.X , screenSize.Y);
2312 111 : if (m_bgfullscreen)
2313 0 : driver->draw2DRectangle(m_bgcolor, allbg, &allbg);
2314 : else
2315 111 : driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
2316 :
2317 111 : m_tooltip_element->setVisible(false);
2318 :
2319 : /*
2320 : Draw backgrounds
2321 : */
2322 111 : for(u32 i=0; i<m_backgrounds.size(); i++)
2323 : {
2324 0 : const ImageDrawSpec &spec = m_backgrounds[i];
2325 0 : video::ITexture *texture = m_tsrc->getTexture(spec.name);
2326 :
2327 0 : if (texture != 0) {
2328 : // Image size on screen
2329 0 : core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
2330 : // Image rectangle on screen
2331 0 : core::rect<s32> rect = imgrect + spec.pos;
2332 :
2333 0 : if (m_clipbackground) {
2334 0 : core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
2335 0 : rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
2336 0 : AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
2337 0 : AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X,
2338 0 : AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
2339 : }
2340 :
2341 0 : const video::SColor color(255,255,255,255);
2342 0 : const video::SColor colors[] = {color,color,color,color};
2343 0 : draw2DImageFilterScaled(driver, texture, rect,
2344 : core::rect<s32>(core::position2d<s32>(0,0),
2345 0 : core::dimension2di(texture->getOriginalSize())),
2346 0 : NULL/*&AbsoluteClippingRect*/, colors, true);
2347 : }
2348 : else {
2349 0 : errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
2350 0 : errorstream << "\t" << spec.name << std::endl;
2351 : }
2352 : }
2353 :
2354 : /*
2355 : Draw Boxes
2356 : */
2357 202 : for(u32 i=0; i<m_boxes.size(); i++)
2358 : {
2359 91 : const BoxDrawSpec &spec = m_boxes[i];
2360 :
2361 91 : irr::video::SColor todraw = spec.color;
2362 :
2363 91 : todraw.setAlpha(140);
2364 :
2365 91 : core::rect<s32> rect(spec.pos.X,spec.pos.Y,
2366 182 : spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
2367 :
2368 91 : driver->draw2DRectangle(todraw, rect, 0);
2369 : }
2370 : /*
2371 : Draw images
2372 : */
2373 111 : for(u32 i=0; i<m_images.size(); i++)
2374 : {
2375 0 : const ImageDrawSpec &spec = m_images[i];
2376 0 : video::ITexture *texture = m_tsrc->getTexture(spec.name);
2377 :
2378 0 : if (texture != 0) {
2379 0 : const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
2380 : // Image size on screen
2381 0 : core::rect<s32> imgrect;
2382 :
2383 0 : if (spec.scale)
2384 0 : imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
2385 : else {
2386 :
2387 0 : imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
2388 : }
2389 : // Image rectangle on screen
2390 0 : core::rect<s32> rect = imgrect + spec.pos;
2391 0 : const video::SColor color(255,255,255,255);
2392 0 : const video::SColor colors[] = {color,color,color,color};
2393 0 : draw2DImageFilterScaled(driver, texture, rect,
2394 : core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
2395 0 : NULL/*&AbsoluteClippingRect*/, colors, true);
2396 : }
2397 : else {
2398 0 : errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
2399 0 : errorstream << "\t" << spec.name << std::endl;
2400 : }
2401 : }
2402 :
2403 : /*
2404 : Draw item images
2405 : */
2406 111 : for(u32 i=0; i<m_itemimages.size(); i++)
2407 : {
2408 0 : if (m_gamedef == 0)
2409 0 : break;
2410 :
2411 0 : const ImageDrawSpec &spec = m_itemimages[i];
2412 0 : IItemDefManager *idef = m_gamedef->idef();
2413 0 : ItemStack item;
2414 0 : item.deSerialize(spec.name, idef);
2415 0 : video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
2416 : // Image size on screen
2417 0 : core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
2418 : // Image rectangle on screen
2419 0 : core::rect<s32> rect = imgrect + spec.pos;
2420 0 : const video::SColor color(255,255,255,255);
2421 0 : const video::SColor colors[] = {color,color,color,color};
2422 0 : draw2DImageFilterScaled(driver, texture, rect,
2423 : core::rect<s32>(core::position2d<s32>(0,0),
2424 0 : core::dimension2di(texture->getOriginalSize())),
2425 0 : NULL/*&AbsoluteClippingRect*/, colors, true);
2426 : }
2427 :
2428 : /*
2429 : Draw items
2430 : Phase 0: Item slot rectangles
2431 : Phase 1: Item images; prepare tooltip
2432 : */
2433 111 : int start_phase=0;
2434 333 : for(int phase=start_phase; phase<=1; phase++)
2435 222 : for(u32 i=0; i<m_inventorylists.size(); i++)
2436 : {
2437 0 : drawList(m_inventorylists[i], phase);
2438 : }
2439 :
2440 : /*
2441 : Call base class
2442 : */
2443 111 : gui::IGUIElement::draw();
2444 :
2445 : /* TODO find way to show tooltips on touchscreen */
2446 : #ifndef HAVE_TOUCHSCREENGUI
2447 111 : m_pointer = m_device->getCursorControl()->getPosition();
2448 : #endif
2449 :
2450 : /*
2451 : Draw fields/buttons tooltips
2452 : */
2453 : gui::IGUIElement *hovered =
2454 111 : Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
2455 :
2456 111 : if (hovered != NULL) {
2457 111 : s32 id = hovered->getID();
2458 :
2459 111 : u32 delta = 0;
2460 111 : if (id == -1) {
2461 53 : m_old_tooltip_id = id;
2462 53 : m_old_tooltip = "";
2463 : } else {
2464 58 : if (id == m_old_tooltip_id) {
2465 51 : delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
2466 : } else {
2467 7 : m_hovered_time = getTimeMs();
2468 7 : m_old_tooltip_id = id;
2469 : }
2470 : }
2471 :
2472 111 : if (id != -1 && delta >= m_tooltip_show_delay) {
2473 573 : for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
2474 382 : iter != m_fields.end(); iter++) {
2475 176 : if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
2476 0 : if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
2477 0 : m_old_tooltip = m_tooltips[iter->fname].tooltip;
2478 0 : m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str());
2479 0 : std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
2480 0 : s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
2481 0 : s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
2482 0 : int tooltip_offset_x = m_btn_height;
2483 0 : int tooltip_offset_y = m_btn_height;
2484 : #ifdef __ANDROID__
2485 : tooltip_offset_x *= 3;
2486 : tooltip_offset_y = 0;
2487 : if (m_pointer.X > (s32)screenSize.X / 2)
2488 : tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1;
2489 : #endif
2490 0 : s32 tooltip_x = m_pointer.X + tooltip_offset_x;
2491 0 : s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
2492 0 : if (tooltip_x + tooltip_width > (s32)screenSize.X)
2493 0 : tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
2494 0 : if (tooltip_y + tooltip_height > (s32)screenSize.Y)
2495 0 : tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
2496 0 : m_tooltip_element->setRelativePosition(core::rect<s32>(
2497 : core::position2d<s32>(tooltip_x, tooltip_y),
2498 0 : core::dimension2d<s32>(tooltip_width, tooltip_height)));
2499 : }
2500 0 : m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
2501 0 : m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
2502 0 : m_tooltip_element->setVisible(true);
2503 0 : this->bringToFront(m_tooltip_element);
2504 0 : break;
2505 : }
2506 : }
2507 : }
2508 : }
2509 :
2510 : /*
2511 : Draw dragged item stack
2512 : */
2513 111 : drawSelectedItem();
2514 :
2515 111 : skin->setFont(old_font);
2516 111 : }
2517 :
2518 111 : void GUIFormSpecMenu::updateSelectedItem()
2519 : {
2520 : // If the selected stack has become empty for some reason, deselect it.
2521 : // If the selected stack has become inaccessible, deselect it.
2522 : // If the selected stack has become smaller, adjust m_selected_amount.
2523 222 : ItemStack selected = verifySelectedItem();
2524 :
2525 : // WARNING: BLACK MAGIC
2526 : // See if there is a stack suited for our current guess.
2527 : // If such stack does not exist, clear the guess.
2528 222 : if(m_selected_content_guess.name != "" &&
2529 111 : selected.name == m_selected_content_guess.name &&
2530 0 : selected.count == m_selected_content_guess.count){
2531 : // Selected item fits the guess. Skip the black magic.
2532 : }
2533 111 : else if(m_selected_content_guess.name != ""){
2534 0 : bool found = false;
2535 0 : for(u32 i=0; i<m_inventorylists.size() && !found; i++){
2536 0 : const ListDrawSpec &s = m_inventorylists[i];
2537 0 : Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
2538 0 : if(!inv)
2539 0 : continue;
2540 0 : InventoryList *list = inv->getList(s.listname);
2541 0 : if(!list)
2542 0 : continue;
2543 0 : for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
2544 0 : u32 item_i = i + s.start_item_i;
2545 0 : if(item_i >= list->getSize())
2546 0 : continue;
2547 0 : ItemStack stack = list->getItem(item_i);
2548 0 : if(stack.name == m_selected_content_guess.name &&
2549 0 : stack.count == m_selected_content_guess.count){
2550 0 : found = true;
2551 0 : infostream<<"Client: Changing selected content guess to "
2552 0 : <<s.inventoryloc.dump()<<" "<<s.listname
2553 0 : <<" "<<item_i<<std::endl;
2554 0 : delete m_selected_item;
2555 0 : m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
2556 0 : m_selected_amount = stack.count;
2557 : }
2558 : }
2559 : }
2560 0 : if(!found){
2561 0 : infostream<<"Client: Discarding selected content guess: "
2562 0 : <<m_selected_content_guess.getItemString()<<std::endl;
2563 0 : m_selected_content_guess.name = "";
2564 : }
2565 : }
2566 :
2567 : // If craftresult is nonempty and nothing else is selected, select it now.
2568 111 : if(!m_selected_item)
2569 : {
2570 111 : for(u32 i=0; i<m_inventorylists.size(); i++)
2571 : {
2572 0 : const ListDrawSpec &s = m_inventorylists[i];
2573 0 : if(s.listname == "craftpreview")
2574 : {
2575 0 : Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
2576 0 : InventoryList *list = inv->getList("craftresult");
2577 0 : if(list && list->getSize() >= 1 && !list->getItem(0).empty())
2578 : {
2579 0 : m_selected_item = new ItemSpec;
2580 0 : m_selected_item->inventoryloc = s.inventoryloc;
2581 0 : m_selected_item->listname = "craftresult";
2582 0 : m_selected_item->i = 0;
2583 0 : m_selected_amount = 0;
2584 0 : m_selected_dragging = false;
2585 0 : break;
2586 : }
2587 : }
2588 : }
2589 : }
2590 :
2591 : // If craftresult is selected, keep the whole stack selected
2592 111 : if(m_selected_item && m_selected_item->listname == "craftresult")
2593 : {
2594 0 : m_selected_amount = verifySelectedItem().count;
2595 : }
2596 111 : }
2597 :
2598 111 : ItemStack GUIFormSpecMenu::verifySelectedItem()
2599 : {
2600 : // If the selected stack has become empty for some reason, deselect it.
2601 : // If the selected stack has become inaccessible, deselect it.
2602 : // If the selected stack has become smaller, adjust m_selected_amount.
2603 : // Return the selected stack.
2604 :
2605 111 : if(m_selected_item)
2606 : {
2607 0 : if(m_selected_item->isValid())
2608 : {
2609 0 : Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
2610 0 : if(inv)
2611 : {
2612 0 : InventoryList *list = inv->getList(m_selected_item->listname);
2613 0 : if(list && (u32) m_selected_item->i < list->getSize())
2614 : {
2615 0 : ItemStack stack = list->getItem(m_selected_item->i);
2616 0 : if(m_selected_amount > stack.count)
2617 0 : m_selected_amount = stack.count;
2618 0 : if(!stack.empty())
2619 0 : return stack;
2620 : }
2621 : }
2622 : }
2623 :
2624 : // selection was not valid
2625 0 : delete m_selected_item;
2626 0 : m_selected_item = NULL;
2627 0 : m_selected_amount = 0;
2628 0 : m_selected_dragging = false;
2629 : }
2630 111 : return ItemStack();
2631 : }
2632 :
2633 4 : void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
2634 : {
2635 4 : if(m_text_dst)
2636 : {
2637 8 : StringMap fields;
2638 :
2639 4 : if (quitmode == quit_mode_accept) {
2640 1 : fields["quit"] = "true";
2641 : }
2642 :
2643 4 : if (quitmode == quit_mode_cancel) {
2644 0 : fields["quit"] = "true";
2645 0 : m_text_dst->gotText(fields);
2646 0 : return;
2647 : }
2648 :
2649 4 : if (current_keys_pending.key_down) {
2650 0 : fields["key_down"] = "true";
2651 0 : current_keys_pending.key_down = false;
2652 : }
2653 :
2654 4 : if (current_keys_pending.key_up) {
2655 0 : fields["key_up"] = "true";
2656 0 : current_keys_pending.key_up = false;
2657 : }
2658 :
2659 4 : if (current_keys_pending.key_enter) {
2660 0 : fields["key_enter"] = "true";
2661 0 : current_keys_pending.key_enter = false;
2662 : }
2663 :
2664 4 : if (current_keys_pending.key_escape) {
2665 0 : fields["key_escape"] = "true";
2666 0 : current_keys_pending.key_escape = false;
2667 : }
2668 :
2669 48 : for(unsigned int i=0; i<m_fields.size(); i++) {
2670 44 : const FieldSpec &s = m_fields[i];
2671 44 : if(s.send) {
2672 32 : std::string name = s.fname;
2673 16 : if (s.ftype == f_Button) {
2674 1 : fields[name] = wide_to_utf8(s.flabel);
2675 15 : } else if (s.ftype == f_Table) {
2676 3 : GUITable *table = getTable(s.fname);
2677 3 : if (table) {
2678 3 : fields[name] = table->checkEvent();
2679 : }
2680 : }
2681 12 : else if(s.ftype == f_DropDown) {
2682 : // no dynamic cast possible due to some distributions shipped
2683 : // without rtti support in irrlicht
2684 0 : IGUIElement * element = getElementFromId(s.fid);
2685 0 : gui::IGUIComboBox *e = NULL;
2686 0 : if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
2687 0 : e = static_cast<gui::IGUIComboBox*>(element);
2688 : }
2689 0 : s32 selected = e->getSelected();
2690 0 : if (selected >= 0) {
2691 0 : fields[name] =
2692 0 : wide_to_utf8(e->getItem(selected));
2693 : }
2694 : }
2695 12 : else if (s.ftype == f_TabHeader) {
2696 : // no dynamic cast possible due to some distributions shipped
2697 : // without rtti support in irrlicht
2698 0 : IGUIElement * element = getElementFromId(s.fid);
2699 0 : gui::IGUITabControl *e = NULL;
2700 0 : if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
2701 0 : e = static_cast<gui::IGUITabControl*>(element);
2702 : }
2703 :
2704 0 : if (e != 0) {
2705 0 : std::stringstream ss;
2706 0 : ss << (e->getActiveTab() +1);
2707 0 : fields[name] = ss.str();
2708 : }
2709 : }
2710 12 : else if (s.ftype == f_CheckBox) {
2711 : // no dynamic cast possible due to some distributions shipped
2712 : // without rtti support in irrlicht
2713 0 : IGUIElement * element = getElementFromId(s.fid);
2714 0 : gui::IGUICheckBox *e = NULL;
2715 0 : if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
2716 0 : e = static_cast<gui::IGUICheckBox*>(element);
2717 : }
2718 :
2719 0 : if (e != 0) {
2720 0 : if (e->isChecked())
2721 0 : fields[name] = "true";
2722 : else
2723 0 : fields[name] = "false";
2724 : }
2725 : }
2726 12 : else if (s.ftype == f_ScrollBar) {
2727 : // no dynamic cast possible due to some distributions shipped
2728 : // without rtti support in irrlicht
2729 0 : IGUIElement * element = getElementFromId(s.fid);
2730 0 : gui::IGUIScrollBar *e = NULL;
2731 0 : if ((element) && (element->getType() == gui::EGUIET_SCROLL_BAR)) {
2732 0 : e = static_cast<gui::IGUIScrollBar*>(element);
2733 : }
2734 :
2735 0 : if (e != 0) {
2736 0 : std::stringstream os;
2737 0 : os << e->getPos();
2738 0 : if (s.fdefault == L"Changed")
2739 0 : fields[name] = "CHG:" + os.str();
2740 : else
2741 0 : fields[name] = "VAL:" + os.str();
2742 : }
2743 : }
2744 : else
2745 : {
2746 12 : IGUIElement* e = getElementFromId(s.fid);
2747 12 : if(e != NULL) {
2748 12 : fields[name] = wide_to_utf8(e->getText());
2749 : }
2750 : }
2751 : }
2752 : }
2753 :
2754 4 : m_text_dst->gotText(fields);
2755 : }
2756 : }
2757 :
2758 225 : static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
2759 : {
2760 340 : while(tocheck != NULL) {
2761 214 : if (tocheck == parent) {
2762 99 : return true;
2763 : }
2764 115 : tocheck = tocheck->getParent();
2765 : }
2766 11 : return false;
2767 : }
2768 :
2769 140 : bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
2770 : {
2771 : // The IGUITabControl renders visually using the skin's selected
2772 : // font, which we override for the duration of form drawing,
2773 : // but computes tab hotspots based on how it would have rendered
2774 : // using the font that is selected at the time of button release.
2775 : // To make these two consistent, temporarily override the skin's
2776 : // font while the IGUITabControl is processing the event.
2777 250 : if (event.EventType == EET_MOUSE_INPUT_EVENT &&
2778 110 : event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
2779 2 : s32 x = event.MouseInput.X;
2780 2 : s32 y = event.MouseInput.Y;
2781 : gui::IGUIElement *hovered =
2782 4 : Environment->getRootGUIElement()->getElementFromPoint(
2783 2 : core::position2d<s32>(x, y));
2784 4 : if (hovered && isMyChild(hovered) &&
2785 2 : hovered->getType() == gui::EGUIET_TAB_CONTROL) {
2786 0 : gui::IGUISkin* skin = Environment->getSkin();
2787 0 : sanity_check(skin != NULL);
2788 0 : gui::IGUIFont *old_font = skin->getFont();
2789 0 : skin->setFont(m_font);
2790 0 : bool retval = hovered->OnEvent(event);
2791 0 : skin->setFont(old_font);
2792 0 : return retval;
2793 : }
2794 : }
2795 :
2796 : // Fix Esc/Return key being eaten by checkboxen and tables
2797 140 : if(event.EventType==EET_KEY_INPUT_EVENT) {
2798 2 : KeyPress kp(event.KeyInput);
2799 4 : if (kp == EscapeKey || kp == CancelKey
2800 1 : || kp == getKeySetting("keymap_inventory")
2801 2 : || event.KeyInput.Key==KEY_RETURN) {
2802 1 : gui::IGUIElement *focused = Environment->getFocus();
2803 2 : if (focused && isMyChild(focused) &&
2804 2 : (focused->getType() == gui::EGUIET_LIST_BOX ||
2805 1 : focused->getType() == gui::EGUIET_CHECK_BOX)) {
2806 0 : OnEvent(event);
2807 0 : return true;
2808 : }
2809 : }
2810 : }
2811 : // Mouse wheel events: send to hovered element instead of focused
2812 140 : if(event.EventType==EET_MOUSE_INPUT_EVENT
2813 110 : && event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
2814 0 : s32 x = event.MouseInput.X;
2815 0 : s32 y = event.MouseInput.Y;
2816 : gui::IGUIElement *hovered =
2817 0 : Environment->getRootGUIElement()->getElementFromPoint(
2818 0 : core::position2d<s32>(x, y));
2819 0 : if (hovered && isMyChild(hovered)) {
2820 0 : hovered->OnEvent(event);
2821 0 : return true;
2822 : }
2823 : }
2824 :
2825 140 : if (event.EventType == EET_MOUSE_INPUT_EVENT) {
2826 110 : s32 x = event.MouseInput.X;
2827 110 : s32 y = event.MouseInput.Y;
2828 : gui::IGUIElement *hovered =
2829 220 : Environment->getRootGUIElement()->getElementFromPoint(
2830 110 : core::position2d<s32>(x, y));
2831 110 : if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
2832 3 : m_old_tooltip_id = -1;
2833 3 : m_old_tooltip = "";
2834 : }
2835 110 : if (!isChild(hovered,this)) {
2836 11 : if (DoubleClickDetection(event)) {
2837 0 : return true;
2838 : }
2839 : }
2840 : }
2841 :
2842 : #ifdef __ANDROID__
2843 : // display software keyboard when clicking edit boxes
2844 : if (event.EventType == EET_MOUSE_INPUT_EVENT
2845 : && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
2846 : gui::IGUIElement *hovered =
2847 : Environment->getRootGUIElement()->getElementFromPoint(
2848 : core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
2849 : if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
2850 : bool retval = hovered->OnEvent(event);
2851 : if (retval) {
2852 : Environment->setFocus(hovered);
2853 : }
2854 : m_JavaDialogFieldName = getNameByID(hovered->getID());
2855 : std::string message = gettext("Enter ");
2856 : std::string label = wide_to_utf8(getLabelByID(hovered->getID()));
2857 : if (label == "") {
2858 : label = "text";
2859 : }
2860 : message += gettext(label) + ":";
2861 :
2862 : /* single line text input */
2863 : int type = 2;
2864 :
2865 : /* multi line text input */
2866 : if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
2867 : type = 1;
2868 : }
2869 :
2870 : /* passwords are always single line */
2871 : if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
2872 : type = 3;
2873 : }
2874 :
2875 : porting::showInputDialog(gettext("ok"), "",
2876 : wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
2877 : type);
2878 : return retval;
2879 : }
2880 : }
2881 :
2882 : if (event.EventType == EET_TOUCH_INPUT_EVENT)
2883 : {
2884 : SEvent translated;
2885 : memset(&translated, 0, sizeof(SEvent));
2886 : translated.EventType = EET_MOUSE_INPUT_EVENT;
2887 : gui::IGUIElement* root = Environment->getRootGUIElement();
2888 :
2889 : if (!root) {
2890 : errorstream
2891 : << "GUIFormSpecMenu::preprocessEvent unable to get root element"
2892 : << std::endl;
2893 : return false;
2894 : }
2895 : gui::IGUIElement* hovered = root->getElementFromPoint(
2896 : core::position2d<s32>(
2897 : event.TouchInput.X,
2898 : event.TouchInput.Y));
2899 :
2900 : translated.MouseInput.X = event.TouchInput.X;
2901 : translated.MouseInput.Y = event.TouchInput.Y;
2902 : translated.MouseInput.Control = false;
2903 :
2904 : bool dont_send_event = false;
2905 :
2906 : if (event.TouchInput.touchedCount == 1) {
2907 : switch (event.TouchInput.Event) {
2908 : case ETIE_PRESSED_DOWN:
2909 : m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
2910 : translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
2911 : translated.MouseInput.ButtonStates = EMBSM_LEFT;
2912 : m_down_pos = m_pointer;
2913 : break;
2914 : case ETIE_MOVED:
2915 : m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
2916 : translated.MouseInput.Event = EMIE_MOUSE_MOVED;
2917 : translated.MouseInput.ButtonStates = EMBSM_LEFT;
2918 : break;
2919 : case ETIE_LEFT_UP:
2920 : translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
2921 : translated.MouseInput.ButtonStates = 0;
2922 : hovered = root->getElementFromPoint(m_down_pos);
2923 : /* we don't have a valid pointer element use last
2924 : * known pointer pos */
2925 : translated.MouseInput.X = m_pointer.X;
2926 : translated.MouseInput.Y = m_pointer.Y;
2927 :
2928 : /* reset down pos */
2929 : m_down_pos = v2s32(0,0);
2930 : break;
2931 : default:
2932 : dont_send_event = true;
2933 : //this is not supposed to happen
2934 : errorstream
2935 : << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
2936 : << event.TouchInput.Event << std::endl;
2937 : }
2938 : } else if ( (event.TouchInput.touchedCount == 2) &&
2939 : (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
2940 : hovered = root->getElementFromPoint(m_down_pos);
2941 :
2942 : translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
2943 : translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
2944 : translated.MouseInput.X = m_pointer.X;
2945 : translated.MouseInput.Y = m_pointer.Y;
2946 :
2947 : if (hovered) {
2948 : hovered->OnEvent(translated);
2949 : }
2950 :
2951 : translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
2952 : translated.MouseInput.ButtonStates = EMBSM_LEFT;
2953 :
2954 :
2955 : if (hovered) {
2956 : hovered->OnEvent(translated);
2957 : }
2958 : dont_send_event = true;
2959 : }
2960 : /* ignore unhandled 2 touch events ... accidental moving for example */
2961 : else if (event.TouchInput.touchedCount == 2) {
2962 : dont_send_event = true;
2963 : }
2964 : else if (event.TouchInput.touchedCount > 2) {
2965 : errorstream
2966 : << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
2967 : << event.TouchInput.touchedCount << " ignoring them" << std::endl;
2968 : }
2969 :
2970 : if (dont_send_event) {
2971 : return true;
2972 : }
2973 :
2974 : /* check if translated event needs to be preprocessed again */
2975 : if (preprocessEvent(translated)) {
2976 : return true;
2977 : }
2978 : if (hovered) {
2979 : grab();
2980 : bool retval = hovered->OnEvent(translated);
2981 :
2982 : if (event.TouchInput.Event == ETIE_LEFT_UP) {
2983 : /* reset pointer */
2984 : m_pointer = v2s32(0,0);
2985 : }
2986 : drop();
2987 : return retval;
2988 : }
2989 : }
2990 : #endif
2991 :
2992 140 : return false;
2993 : }
2994 :
2995 : /******************************************************************************/
2996 11 : bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
2997 : {
2998 : /* The following code is for capturing double-clicks of the mouse button
2999 : * and translating the double-click into an EET_KEY_INPUT_EVENT event
3000 : * -- which closes the form -- under some circumstances.
3001 : *
3002 : * There have been many github issues reporting this as a bug even though it
3003 : * was an intended feature. For this reason, remapping the double-click as
3004 : * an ESC must be explicitly set when creating this class via the
3005 : * /p remap_dbl_click parameter of the constructor.
3006 : */
3007 :
3008 11 : if (!m_remap_dbl_click)
3009 11 : return false;
3010 :
3011 0 : if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
3012 0 : m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
3013 0 : m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
3014 :
3015 0 : m_doubleclickdetect[1].pos = m_pointer;
3016 0 : m_doubleclickdetect[1].time = getTimeMs();
3017 : }
3018 0 : else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
3019 0 : u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs());
3020 0 : if (delta > 400) {
3021 0 : return false;
3022 : }
3023 :
3024 : double squaredistance =
3025 : m_doubleclickdetect[0].pos
3026 0 : .getDistanceFromSQ(m_doubleclickdetect[1].pos);
3027 :
3028 0 : if (squaredistance > (30*30)) {
3029 0 : return false;
3030 : }
3031 :
3032 0 : SEvent* translated = new SEvent();
3033 : assert(translated != 0);
3034 : //translate doubleclick to escape
3035 0 : memset(translated, 0, sizeof(SEvent));
3036 0 : translated->EventType = irr::EET_KEY_INPUT_EVENT;
3037 0 : translated->KeyInput.Key = KEY_ESCAPE;
3038 0 : translated->KeyInput.Control = false;
3039 0 : translated->KeyInput.Shift = false;
3040 0 : translated->KeyInput.PressedDown = true;
3041 0 : translated->KeyInput.Char = 0;
3042 0 : OnEvent(*translated);
3043 :
3044 : // no need to send the key up event as we're already deleted
3045 : // and no one else did notice this event
3046 0 : delete translated;
3047 0 : return true;
3048 : }
3049 :
3050 0 : return false;
3051 : }
3052 :
3053 135 : bool GUIFormSpecMenu::OnEvent(const SEvent& event)
3054 : {
3055 135 : if (event.EventType==EET_KEY_INPUT_EVENT) {
3056 2 : KeyPress kp(event.KeyInput);
3057 3 : if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
3058 1 : (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
3059 0 : if (m_allowclose) {
3060 0 : doPause = false;
3061 0 : acceptInput(quit_mode_cancel);
3062 0 : quitMenu();
3063 : } else {
3064 0 : m_text_dst->gotText(L"MenuQuit");
3065 : }
3066 0 : return true;
3067 3 : } else if (m_client != NULL && event.KeyInput.PressedDown &&
3068 1 : (kp == getKeySetting("keymap_screenshot"))) {
3069 0 : m_client->makeScreenshot(m_device);
3070 : }
3071 1 : if (event.KeyInput.PressedDown &&
3072 0 : (event.KeyInput.Key==KEY_RETURN ||
3073 0 : event.KeyInput.Key==KEY_UP ||
3074 0 : event.KeyInput.Key==KEY_DOWN)
3075 : ) {
3076 0 : switch (event.KeyInput.Key) {
3077 : case KEY_RETURN:
3078 0 : current_keys_pending.key_enter = true;
3079 0 : break;
3080 : case KEY_UP:
3081 0 : current_keys_pending.key_up = true;
3082 0 : break;
3083 : case KEY_DOWN:
3084 0 : current_keys_pending.key_down = true;
3085 0 : break;
3086 : break;
3087 : default:
3088 : //can't happen at all!
3089 0 : FATAL_ERROR("Reached a source line that can't ever been reached");
3090 : break;
3091 : }
3092 0 : if (current_keys_pending.key_enter && m_allowclose) {
3093 0 : acceptInput(quit_mode_accept);
3094 0 : quitMenu();
3095 : } else {
3096 0 : acceptInput();
3097 : }
3098 0 : return true;
3099 : }
3100 :
3101 : }
3102 :
3103 : /* Mouse event other than movement, or crossing the border of inventory
3104 : field while holding right mouse button
3105 : */
3106 509 : if (event.EventType == EET_MOUSE_INPUT_EVENT &&
3107 208 : (event.MouseInput.Event != EMIE_MOUSE_MOVED ||
3108 208 : (event.MouseInput.Event == EMIE_MOUSE_MOVED &&
3109 104 : event.MouseInput.isRightPressed() &&
3110 135 : getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
3111 :
3112 : // Get selected item and hovered/clicked item (s)
3113 :
3114 0 : m_old_tooltip_id = -1;
3115 0 : updateSelectedItem();
3116 0 : ItemSpec s = getItemAtPos(m_pointer);
3117 :
3118 0 : Inventory *inv_selected = NULL;
3119 0 : Inventory *inv_s = NULL;
3120 0 : InventoryList *list_s = NULL;
3121 :
3122 0 : if (m_selected_item) {
3123 0 : inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
3124 0 : sanity_check(inv_selected);
3125 0 : sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
3126 : }
3127 :
3128 0 : u32 s_count = 0;
3129 :
3130 0 : if (s.isValid())
3131 : do { // breakable
3132 0 : inv_s = m_invmgr->getInventory(s.inventoryloc);
3133 :
3134 0 : if (!inv_s) {
3135 0 : errorstream << "InventoryMenu: The selected inventory location "
3136 0 : << "\"" << s.inventoryloc.dump() << "\" doesn't exist"
3137 0 : << std::endl;
3138 0 : s.i = -1; // make it invalid again
3139 0 : break;
3140 : }
3141 :
3142 0 : list_s = inv_s->getList(s.listname);
3143 0 : if (list_s == NULL) {
3144 0 : verbosestream << "InventoryMenu: The selected inventory list \""
3145 0 : << s.listname << "\" does not exist" << std::endl;
3146 0 : s.i = -1; // make it invalid again
3147 0 : break;
3148 : }
3149 :
3150 0 : if ((u32)s.i >= list_s->getSize()) {
3151 0 : infostream << "InventoryMenu: The selected inventory list \""
3152 0 : << s.listname << "\" is too small (i=" << s.i << ", size="
3153 0 : << list_s->getSize() << ")" << std::endl;
3154 0 : s.i = -1; // make it invalid again
3155 0 : break;
3156 : }
3157 :
3158 0 : s_count = list_s->getItem(s.i).count;
3159 : } while(0);
3160 :
3161 0 : bool identical = (m_selected_item != NULL) && s.isValid() &&
3162 0 : (inv_selected == inv_s) &&
3163 0 : (m_selected_item->listname == s.listname) &&
3164 0 : (m_selected_item->i == s.i);
3165 :
3166 : // buttons: 0 = left, 1 = right, 2 = middle
3167 : // up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
3168 0 : int button = 0;
3169 0 : int updown = 2;
3170 0 : if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
3171 0 : { button = 0; updown = 0; }
3172 0 : else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
3173 0 : { button = 1; updown = 0; }
3174 0 : else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
3175 0 : { button = 2; updown = 0; }
3176 0 : else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
3177 0 : { button = 0; updown = 1; }
3178 0 : else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
3179 0 : { button = 1; updown = 1; }
3180 0 : else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
3181 0 : { button = 2; updown = 1; }
3182 0 : else if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
3183 0 : { updown = -1;}
3184 :
3185 : // Set this number to a positive value to generate a move action
3186 : // from m_selected_item to s.
3187 0 : u32 move_amount = 0;
3188 :
3189 : // Set this number to a positive value to generate a move action
3190 : // from s to the next inventory ring.
3191 0 : u32 shift_move_amount = 0;
3192 :
3193 : // Set this number to a positive value to generate a drop action
3194 : // from m_selected_item.
3195 0 : u32 drop_amount = 0;
3196 :
3197 : // Set this number to a positive value to generate a craft action at s.
3198 0 : u32 craft_amount = 0;
3199 :
3200 0 : if (updown == 0) {
3201 : // Some mouse button has been pressed
3202 :
3203 : //infostream<<"Mouse button "<<button<<" pressed at p=("
3204 : // <<p.X<<","<<p.Y<<")"<<std::endl;
3205 :
3206 0 : m_selected_dragging = false;
3207 :
3208 0 : if (s.isValid() && s.listname == "craftpreview") {
3209 : // Craft preview has been clicked: craft
3210 0 : craft_amount = (button == 2 ? 10 : 1);
3211 0 : } else if (m_selected_item == NULL) {
3212 0 : if (s_count != 0) {
3213 : // Non-empty stack has been clicked: select or shift-move it
3214 0 : m_selected_item = new ItemSpec(s);
3215 :
3216 : u32 count;
3217 0 : if (button == 1) // right
3218 0 : count = (s_count + 1) / 2;
3219 0 : else if (button == 2) // middle
3220 0 : count = MYMIN(s_count, 10);
3221 : else // left
3222 0 : count = s_count;
3223 :
3224 0 : if (!event.MouseInput.Shift) {
3225 : // no shift: select item
3226 0 : m_selected_amount = count;
3227 0 : m_selected_dragging = true;
3228 0 : m_rmouse_auto_place = false;
3229 : } else {
3230 : // shift pressed: move item
3231 0 : if (button != 1)
3232 0 : shift_move_amount = count;
3233 : else // count of 1 at left click like after drag & drop
3234 0 : shift_move_amount = 1;
3235 : }
3236 : }
3237 : } else { // m_selected_item != NULL
3238 : assert(m_selected_amount >= 1);
3239 :
3240 0 : if (s.isValid()) {
3241 : // Clicked a slot: move
3242 0 : if (button == 1) // right
3243 0 : move_amount = 1;
3244 0 : else if (button == 2) // middle
3245 0 : move_amount = MYMIN(m_selected_amount, 10);
3246 : else // left
3247 0 : move_amount = m_selected_amount;
3248 :
3249 0 : if (identical) {
3250 0 : if (move_amount >= m_selected_amount)
3251 0 : m_selected_amount = 0;
3252 : else
3253 0 : m_selected_amount -= move_amount;
3254 0 : move_amount = 0;
3255 : }
3256 : }
3257 0 : else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
3258 : // Clicked outside of the window: drop
3259 0 : if (button == 1) // right
3260 0 : drop_amount = 1;
3261 0 : else if (button == 2) // middle
3262 0 : drop_amount = MYMIN(m_selected_amount, 10);
3263 : else // left
3264 0 : drop_amount = m_selected_amount;
3265 : }
3266 : }
3267 : }
3268 0 : else if (updown == 1) {
3269 : // Some mouse button has been released
3270 :
3271 : //infostream<<"Mouse button "<<button<<" released at p=("
3272 : // <<p.X<<","<<p.Y<<")"<<std::endl;
3273 :
3274 0 : if (m_selected_item != NULL && m_selected_dragging && s.isValid()) {
3275 0 : if (!identical) {
3276 : // Dragged to different slot: move all selected
3277 0 : move_amount = m_selected_amount;
3278 : }
3279 0 : } else if (m_selected_item != NULL && m_selected_dragging &&
3280 0 : !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
3281 : // Dragged outside of window: drop all selected
3282 0 : drop_amount = m_selected_amount;
3283 : }
3284 :
3285 0 : m_selected_dragging = false;
3286 : // Keep count of how many times right mouse button has been
3287 : // clicked. One click is drag without dropping. Click + release
3288 : // + click changes to drop one item when moved mode
3289 0 : if (button == 1 && m_selected_item != NULL)
3290 0 : m_rmouse_auto_place = !m_rmouse_auto_place;
3291 0 : } else if (updown == -1) {
3292 : // Mouse has been moved and rmb is down and mouse pointer just
3293 : // entered a new inventory field (checked in the entry-if, this
3294 : // is the only action here that is generated by mouse movement)
3295 0 : if (m_selected_item != NULL && s.isValid()) {
3296 : // Move 1 item
3297 : // TODO: middle mouse to move 10 items might be handy
3298 0 : if (m_rmouse_auto_place) {
3299 : // Only move an item if the destination slot is empty
3300 : // or contains the same item type as what is going to be
3301 : // moved
3302 0 : InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
3303 0 : InventoryList *list_to = list_s;
3304 : assert(list_from && list_to);
3305 0 : ItemStack stack_from = list_from->getItem(m_selected_item->i);
3306 0 : ItemStack stack_to = list_to->getItem(s.i);
3307 0 : if (stack_to.empty() || stack_to.name == stack_from.name)
3308 0 : move_amount = 1;
3309 : }
3310 : }
3311 : }
3312 :
3313 : // Possibly send inventory action to server
3314 0 : if (move_amount > 0) {
3315 : // Send IACTION_MOVE
3316 :
3317 : assert(m_selected_item && m_selected_item->isValid());
3318 : assert(s.isValid());
3319 :
3320 : assert(inv_selected && inv_s);
3321 0 : InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
3322 0 : InventoryList *list_to = list_s;
3323 : assert(list_from && list_to);
3324 0 : ItemStack stack_from = list_from->getItem(m_selected_item->i);
3325 0 : ItemStack stack_to = list_to->getItem(s.i);
3326 :
3327 : // Check how many items can be moved
3328 0 : move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
3329 0 : ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
3330 : // If source stack cannot be added to destination stack at all,
3331 : // they are swapped
3332 0 : if ((leftover.count == stack_from.count) &&
3333 0 : (leftover.name == stack_from.name)) {
3334 0 : m_selected_amount = stack_to.count;
3335 : // In case the server doesn't directly swap them but instead
3336 : // moves stack_to somewhere else, set this
3337 0 : m_selected_content_guess = stack_to;
3338 0 : m_selected_content_guess_inventory = s.inventoryloc;
3339 : }
3340 : // Source stack goes fully into destination stack
3341 0 : else if (leftover.empty()) {
3342 0 : m_selected_amount -= move_amount;
3343 0 : m_selected_content_guess = ItemStack(); // Clear
3344 : }
3345 : // Source stack goes partly into destination stack
3346 : else {
3347 0 : move_amount -= leftover.count;
3348 0 : m_selected_amount -= move_amount;
3349 0 : m_selected_content_guess = ItemStack(); // Clear
3350 : }
3351 :
3352 0 : infostream << "Handing IACTION_MOVE to manager" << std::endl;
3353 0 : IMoveAction *a = new IMoveAction();
3354 0 : a->count = move_amount;
3355 0 : a->from_inv = m_selected_item->inventoryloc;
3356 0 : a->from_list = m_selected_item->listname;
3357 0 : a->from_i = m_selected_item->i;
3358 0 : a->to_inv = s.inventoryloc;
3359 0 : a->to_list = s.listname;
3360 0 : a->to_i = s.i;
3361 0 : m_invmgr->inventoryAction(a);
3362 0 : } else if (shift_move_amount > 0) {
3363 0 : u32 mis = m_inventory_rings.size();
3364 0 : u32 i = 0;
3365 0 : for (; i < mis; i++) {
3366 0 : const ListRingSpec &sp = m_inventory_rings[i];
3367 0 : if (sp.inventoryloc == s.inventoryloc
3368 0 : && sp.listname == s.listname)
3369 0 : break;
3370 : }
3371 : do {
3372 0 : if (i >= mis) // if not found
3373 0 : break;
3374 0 : u32 to_inv_ind = (i + 1) % mis;
3375 0 : const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
3376 0 : InventoryList *list_from = list_s;
3377 0 : if (!s.isValid())
3378 0 : break;
3379 0 : Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
3380 0 : if (!inv_to)
3381 0 : break;
3382 0 : InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
3383 0 : if (!list_to)
3384 0 : break;
3385 0 : ItemStack stack_from = list_from->getItem(s.i);
3386 : assert(shift_move_amount <= stack_from.count);
3387 0 : if (m_client->getProtoVersion() >= 25) {
3388 0 : infostream << "Handing IACTION_MOVE to manager" << std::endl;
3389 0 : IMoveAction *a = new IMoveAction();
3390 0 : a->count = shift_move_amount;
3391 0 : a->from_inv = s.inventoryloc;
3392 0 : a->from_list = s.listname;
3393 0 : a->from_i = s.i;
3394 0 : a->to_inv = to_inv_sp.inventoryloc;
3395 0 : a->to_list = to_inv_sp.listname;
3396 0 : a->move_somewhere = true;
3397 0 : m_invmgr->inventoryAction(a);
3398 : } else {
3399 : // find a place (or more than one) to add the new item
3400 0 : u32 ilt_size = list_to->getSize();
3401 0 : ItemStack leftover;
3402 0 : for (u32 slot_to = 0; slot_to < ilt_size
3403 0 : && shift_move_amount > 0; slot_to++) {
3404 0 : list_to->itemFits(slot_to, stack_from, &leftover);
3405 0 : if (leftover.count < stack_from.count) {
3406 0 : infostream << "Handing IACTION_MOVE to manager" << std::endl;
3407 0 : IMoveAction *a = new IMoveAction();
3408 0 : a->count = MYMIN(shift_move_amount,
3409 0 : (u32) (stack_from.count - leftover.count));
3410 0 : shift_move_amount -= a->count;
3411 0 : a->from_inv = s.inventoryloc;
3412 0 : a->from_list = s.listname;
3413 0 : a->from_i = s.i;
3414 0 : a->to_inv = to_inv_sp.inventoryloc;
3415 0 : a->to_list = to_inv_sp.listname;
3416 0 : a->to_i = slot_to;
3417 0 : m_invmgr->inventoryAction(a);
3418 0 : stack_from = leftover;
3419 : }
3420 : }
3421 : }
3422 : } while (0);
3423 0 : } else if (drop_amount > 0) {
3424 0 : m_selected_content_guess = ItemStack(); // Clear
3425 :
3426 : // Send IACTION_DROP
3427 :
3428 : assert(m_selected_item && m_selected_item->isValid());
3429 : assert(inv_selected);
3430 0 : InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
3431 : assert(list_from);
3432 0 : ItemStack stack_from = list_from->getItem(m_selected_item->i);
3433 :
3434 : // Check how many items can be dropped
3435 0 : drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
3436 : assert(drop_amount > 0 && drop_amount <= m_selected_amount);
3437 0 : m_selected_amount -= drop_amount;
3438 :
3439 0 : infostream << "Handing IACTION_DROP to manager" << std::endl;
3440 0 : IDropAction *a = new IDropAction();
3441 0 : a->count = drop_amount;
3442 0 : a->from_inv = m_selected_item->inventoryloc;
3443 0 : a->from_list = m_selected_item->listname;
3444 0 : a->from_i = m_selected_item->i;
3445 0 : m_invmgr->inventoryAction(a);
3446 0 : } else if (craft_amount > 0) {
3447 0 : m_selected_content_guess = ItemStack(); // Clear
3448 :
3449 : // Send IACTION_CRAFT
3450 :
3451 : assert(s.isValid());
3452 : assert(inv_s);
3453 :
3454 0 : infostream << "Handing IACTION_CRAFT to manager" << std::endl;
3455 0 : ICraftAction *a = new ICraftAction();
3456 0 : a->count = craft_amount;
3457 0 : a->craft_inv = s.inventoryloc;
3458 0 : m_invmgr->inventoryAction(a);
3459 : }
3460 :
3461 : // If m_selected_amount has been decreased to zero, deselect
3462 0 : if (m_selected_amount == 0) {
3463 0 : delete m_selected_item;
3464 0 : m_selected_item = NULL;
3465 0 : m_selected_amount = 0;
3466 0 : m_selected_dragging = false;
3467 0 : m_selected_content_guess = ItemStack();
3468 : }
3469 0 : m_old_pointer = m_pointer;
3470 : }
3471 135 : if (event.EventType == EET_GUI_EVENT) {
3472 :
3473 60 : if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
3474 30 : && isVisible()) {
3475 : // find the element that was clicked
3476 2 : for (unsigned int i=0; i<m_fields.size(); i++) {
3477 0 : FieldSpec &s = m_fields[i];
3478 0 : if ((s.ftype == f_TabHeader) &&
3479 0 : (s.fid == event.GUIEvent.Caller->getID())) {
3480 0 : s.send = true;
3481 0 : acceptInput();
3482 0 : s.send = false;
3483 0 : return true;
3484 : }
3485 : }
3486 : }
3487 60 : if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
3488 30 : && isVisible()) {
3489 4 : if (!canTakeFocus(event.GUIEvent.Element)) {
3490 0 : infostream<<"GUIFormSpecMenu: Not allowing focus change."
3491 0 : <<std::endl;
3492 : // Returning true disables focus change
3493 0 : return true;
3494 : }
3495 : }
3496 59 : if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
3497 58 : (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
3498 58 : (event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
3499 29 : (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
3500 1 : unsigned int btn_id = event.GUIEvent.Caller->getID();
3501 :
3502 1 : if (btn_id == 257) {
3503 0 : if (m_allowclose) {
3504 0 : acceptInput(quit_mode_accept);
3505 0 : quitMenu();
3506 : } else {
3507 0 : acceptInput();
3508 0 : m_text_dst->gotText(L"ExitButton");
3509 : }
3510 : // quitMenu deallocates menu
3511 0 : return true;
3512 : }
3513 :
3514 : // find the element that was clicked
3515 6 : for (u32 i = 0; i < m_fields.size(); i++) {
3516 6 : FieldSpec &s = m_fields[i];
3517 : // if its a button, set the send field so
3518 : // lua knows which button was pressed
3519 12 : if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
3520 6 : (s.fid == event.GUIEvent.Caller->getID())) {
3521 1 : s.send = true;
3522 1 : if (s.is_exit) {
3523 1 : if (m_allowclose) {
3524 1 : acceptInput(quit_mode_accept);
3525 1 : quitMenu();
3526 : } else {
3527 0 : m_text_dst->gotText(L"ExitButton");
3528 : }
3529 1 : return true;
3530 : } else {
3531 0 : acceptInput(quit_mode_no);
3532 0 : s.send = false;
3533 0 : return true;
3534 : }
3535 5 : } else if ((s.ftype == f_DropDown) &&
3536 0 : (s.fid == event.GUIEvent.Caller->getID())) {
3537 : // only send the changed dropdown
3538 0 : for (u32 i = 0; i < m_fields.size(); i++) {
3539 0 : FieldSpec &s2 = m_fields[i];
3540 0 : if (s2.ftype == f_DropDown) {
3541 0 : s2.send = false;
3542 : }
3543 : }
3544 0 : s.send = true;
3545 0 : acceptInput(quit_mode_no);
3546 :
3547 : // revert configuration to make sure dropdowns are sent on
3548 : // regular button click
3549 0 : for (u32 i = 0; i < m_fields.size(); i++) {
3550 0 : FieldSpec &s2 = m_fields[i];
3551 0 : if (s2.ftype == f_DropDown) {
3552 0 : s2.send = true;
3553 : }
3554 : }
3555 0 : return true;
3556 5 : } else if ((s.ftype == f_ScrollBar) &&
3557 0 : (s.fid == event.GUIEvent.Caller->getID())) {
3558 0 : s.fdefault = L"Changed";
3559 0 : acceptInput(quit_mode_no);
3560 0 : s.fdefault = L"";
3561 : }
3562 : }
3563 : }
3564 :
3565 29 : if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
3566 0 : if (event.GUIEvent.Caller->getID() > 257) {
3567 :
3568 0 : if (m_allowclose) {
3569 0 : acceptInput(quit_mode_accept);
3570 0 : quitMenu();
3571 : } else {
3572 0 : current_keys_pending.key_enter = true;
3573 0 : acceptInput();
3574 : }
3575 : // quitMenu deallocates menu
3576 0 : return true;
3577 : }
3578 : }
3579 :
3580 29 : if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
3581 3 : int current_id = event.GUIEvent.Caller->getID();
3582 3 : if (current_id > 257) {
3583 : // find the element that was clicked
3584 39 : for (u32 i = 0; i < m_fields.size(); i++) {
3585 36 : FieldSpec &s = m_fields[i];
3586 : // if it's a table, set the send field
3587 : // so lua knows which table was changed
3588 36 : if ((s.ftype == f_Table) && (s.fid == current_id)) {
3589 3 : s.send = true;
3590 3 : acceptInput();
3591 3 : s.send=false;
3592 : }
3593 : }
3594 3 : return true;
3595 : }
3596 : }
3597 : }
3598 :
3599 131 : return Parent ? Parent->OnEvent(event) : false;
3600 : }
3601 :
3602 : /**
3603 : * get name of element by element id
3604 : * @param id of element
3605 : * @return name string or empty string
3606 : */
3607 0 : std::string GUIFormSpecMenu::getNameByID(s32 id)
3608 : {
3609 0 : for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
3610 0 : iter != m_fields.end(); iter++) {
3611 0 : if (iter->fid == id) {
3612 0 : return iter->fname;
3613 : }
3614 : }
3615 0 : return "";
3616 : }
3617 :
3618 : /**
3619 : * get label of element by id
3620 : * @param id of element
3621 : * @return label string or empty string
3622 : */
3623 0 : std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
3624 : {
3625 0 : for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
3626 0 : iter != m_fields.end(); iter++) {
3627 0 : if (iter->fid == id) {
3628 0 : return iter->flabel;
3629 : }
3630 : }
3631 0 : return L"";
3632 3 : }
|