]> git.lizzy.rs Git - minetest.git/blob - src/guiFormSpecMenu.cpp
e826c22c7bf96b64ecac95c1ded12abaeb9d5fca
[minetest.git] / src / guiFormSpecMenu.cpp
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 "constants.h"
28 #include "gamedef.h"
29 #include "keycode.h"
30 #include "strfnd.h"
31 #include <IGUICheckBox.h>
32 #include <IGUIEditBox.h>
33 #include <IGUIButton.h>
34 #include <IGUIStaticText.h>
35 #include <IGUIFont.h>
36 #include <IGUIListBox.h>
37 #include <IGUITabControl.h>
38 #include <IGUIScrollBar.h>
39 #include <IGUIComboBox.h>
40 #include "log.h"
41 #include "tile.h" // ITextureSource
42 #include "util/string.h"
43 #include "util/numeric.h"
44 #include "filesys.h"
45
46 #include "gettext.h"
47
48
49 #define MY_CHECKPOS(a,b)                                                                                                        \
50         if (v_pos.size() != 2) {                                                                                                \
51                 errorstream<< "Invalid pos for element " << a << "specified: \""        \
52                         << parts[b] << "\"" << std::endl;                                                               \
53                         return;                                                                                                                 \
54         }
55
56 #define MY_CHECKGEOM(a,b)                                                                                                       \
57         if (v_geom.size() != 2) {                                                                                               \
58                 errorstream<< "Invalid pos for element " << a << "specified: \""        \
59                         << parts[b] << "\"" << std::endl;                                                               \
60                         return;                                                                                                                 \
61         }
62
63
64 void drawItemStack(video::IVideoDriver *driver,
65                 gui::IGUIFont *font,
66                 const ItemStack &item,
67                 const core::rect<s32> &rect,
68                 const core::rect<s32> *clip,
69                 IGameDef *gamedef)
70 {
71         if(item.empty())
72                 return;
73         
74         const ItemDefinition &def = item.getDefinition(gamedef->idef());
75         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
76
77         // Draw the inventory texture
78         if(texture != NULL)
79         {
80                 const video::SColor color(255,255,255,255);
81                 const video::SColor colors[] = {color,color,color,color};
82                 driver->draw2DImage(texture, rect,
83                         core::rect<s32>(core::position2d<s32>(0,0),
84                         core::dimension2di(texture->getOriginalSize())),
85                         clip, colors, true);
86         }
87
88         if(def.type == ITEM_TOOL && item.wear != 0)
89         {
90                 // Draw a progressbar
91                 float barheight = rect.getHeight()/16;
92                 float barpad_x = rect.getWidth()/16;
93                 float barpad_y = rect.getHeight()/16;
94                 core::rect<s32> progressrect(
95                         rect.UpperLeftCorner.X + barpad_x,
96                         rect.LowerRightCorner.Y - barpad_y - barheight,
97                         rect.LowerRightCorner.X - barpad_x,
98                         rect.LowerRightCorner.Y - barpad_y);
99
100                 // Shrink progressrect by amount of tool damage
101                 float wear = item.wear / 65535.0;
102                 int progressmid =
103                         wear * progressrect.UpperLeftCorner.X +
104                         (1-wear) * progressrect.LowerRightCorner.X;
105
106                 // Compute progressbar color
107                 //   wear = 0.0: green
108                 //   wear = 0.5: yellow
109                 //   wear = 1.0: red
110                 video::SColor color(255,255,255,255);
111                 int wear_i = MYMIN(floor(wear * 600), 511);
112                 wear_i = MYMIN(wear_i + 10, 511);
113                 if(wear_i <= 255)
114                         color.set(255, wear_i, 255, 0);
115                 else
116                         color.set(255, 255, 511-wear_i, 0);
117
118                 core::rect<s32> progressrect2 = progressrect;
119                 progressrect2.LowerRightCorner.X = progressmid;
120                 driver->draw2DRectangle(color, progressrect2, clip);
121
122                 color = video::SColor(255,0,0,0);
123                 progressrect2 = progressrect;
124                 progressrect2.UpperLeftCorner.X = progressmid;
125                 driver->draw2DRectangle(color, progressrect2, clip);
126         }
127
128         if(font != NULL && item.count >= 2)
129         {
130                 // Get the item count as a string
131                 std::string text = itos(item.count);
132                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
133                 v2s32 sdim(dim.X,dim.Y);
134
135                 core::rect<s32> rect2(
136                         /*rect.UpperLeftCorner,
137                         core::dimension2d<u32>(rect.getWidth(), 15)*/
138                         rect.LowerRightCorner - sdim,
139                         sdim
140                 );
141
142                 video::SColor bgcolor(128,0,0,0);
143                 driver->draw2DRectangle(bgcolor, rect2, clip);
144
145                 video::SColor color(255,255,255,255);
146                 font->draw(text.c_str(), rect2, color, false, false, clip);
147         }
148 }
149
150 /*
151         GUIFormSpecMenu
152 */
153
154 GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
155                 gui::IGUIElement* parent, s32 id,
156                 IMenuManager *menumgr,
157                 InventoryManager *invmgr,
158                 IGameDef *gamedef
159 ):
160         GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
161         m_device(dev),
162         m_invmgr(invmgr),
163         m_gamedef(gamedef),
164         m_form_src(NULL),
165         m_text_dst(NULL),
166         m_selected_item(NULL),
167         m_selected_amount(0),
168         m_selected_dragging(false),
169         m_tooltip_element(NULL),
170         m_allowclose(true),
171         m_use_gettext(false),
172         m_lock(false)
173 {
174         current_keys_pending.key_down = false;
175         current_keys_pending.key_up = false;
176         current_keys_pending.key_enter = false;
177
178 }
179
180 GUIFormSpecMenu::~GUIFormSpecMenu()
181 {
182         removeChildren();
183
184         delete m_selected_item;
185         delete m_form_src;
186         delete m_text_dst;
187 }
188
189 void GUIFormSpecMenu::removeChildren()
190 {
191         const core::list<gui::IGUIElement*> &children = getChildren();
192         core::list<gui::IGUIElement*> children_copy;
193         for(core::list<gui::IGUIElement*>::ConstIterator
194                         i = children.begin(); i != children.end(); i++)
195         {
196                 children_copy.push_back(*i);
197         }
198         for(core::list<gui::IGUIElement*>::Iterator
199                         i = children_copy.begin();
200                         i != children_copy.end(); i++)
201         {
202                 (*i)->remove();
203         }
204         /*{
205                 gui::IGUIElement *e = getElementFromId(256);
206                 if(e != NULL)
207                         e->remove();
208         }*/
209         if(m_tooltip_element)
210         {
211                 m_tooltip_element->remove();
212                 m_tooltip_element = NULL;
213         }
214 }
215
216 int GUIFormSpecMenu::getListboxIndex(std::string listboxname) {
217
218         std::wstring wlistboxname = narrow_to_wide(listboxname.c_str());
219
220         for(unsigned int i=0; i < m_listboxes.size(); i++) {
221
222                 std::wstring name(m_listboxes[i].first.fname.c_str());
223                 if ( name == wlistboxname) {
224                         return m_listboxes[i].second->getSelected();
225                 }
226         }
227         return -1;
228 }
229
230 std::vector<std::string> split(const std::string &s, char delim, bool escape=false) {
231         std::vector<std::string> tokens;
232
233         if (!escape) {
234                 int startpos = 0;
235                 size_t nextpos = s.find(delim);
236
237                 while(nextpos != std::string::npos) {
238                         std::string toadd = s.substr(startpos,nextpos-startpos);
239                         tokens.push_back(toadd);
240                         startpos = nextpos+1;
241                         nextpos = s.find(delim,nextpos+1);
242                 }
243
244                 //push last element
245                 tokens.push_back(s.substr(startpos));
246         }
247         else {
248                 std::string current = "";
249                 current += s.c_str()[0];
250                 bool last_was_escape = false;
251                 for(unsigned int i=1; i < s.size(); i++) {
252                         if (last_was_escape) {
253                                 current += '\\';
254                                 current += s.c_str()[i];
255                                 last_was_escape = false;
256                         }
257                         else {
258                                 if (s.c_str()[i] == delim) {
259                                         tokens.push_back(current);
260                                         current = "";
261                                         last_was_escape = false;
262                                 }
263                                 else if (s.c_str()[i] == '\\'){
264                                         last_was_escape = true;
265                                 }
266                                 else {
267                                         current += s.c_str()[i];
268                                         last_was_escape = false;
269                                 }
270                         }
271                 }
272                 //push last element
273                 tokens.push_back(current);
274         }
275
276         return tokens;
277 }
278
279 void GUIFormSpecMenu::parseSize(parserData* data,std::string element) {
280         std::vector<std::string> parts = split(element,',');
281
282         if (parts.size() == 2) {
283                 v2f invsize;
284
285                 if (parts[1].find(';') != std::string::npos)
286                         parts[1] = parts[1].substr(0,parts[1].find(';'));
287
288                 invsize.X = stof(parts[0]);
289                 invsize.Y = stof(parts[1]);
290
291                 infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
292
293                 if (m_lock) {
294                         v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
295                         v2u32 delta = current_screensize - m_lockscreensize;
296
297                         if (current_screensize.Y > m_lockscreensize.Y)
298                                 delta.Y /= 2;
299                         else
300                                 delta.Y = 0;
301
302                         if (current_screensize.X > m_lockscreensize.X)
303                                 delta.X /= 2;
304                         else
305                                 delta.X = 0;
306
307                         offset = v2s32(delta.X,delta.Y);
308
309                         data->screensize = m_lockscreensize;
310                 }
311                 else {
312                         offset = v2s32(0,0);
313                 }
314
315                 padding = v2s32(data->screensize.Y/40, data->screensize.Y/40);
316                 spacing = v2s32(data->screensize.Y/12, data->screensize.Y/13);
317                 imgsize = v2s32(data->screensize.Y/15, data->screensize.Y/15);
318                 data->size = v2s32(
319                         padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
320                         padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (data->helptext_h-5)
321                 );
322                 data->rect = core::rect<s32>(
323                                 data->screensize.X/2 - data->size.X/2 + offset.X,
324                                 data->screensize.Y/2 - data->size.Y/2 + offset.Y,
325                                 data->screensize.X/2 + data->size.X/2 + offset.X,
326                                 data->screensize.Y/2 + data->size.Y/2 + offset.Y
327                 );
328
329                 DesiredRect = data->rect;
330                 recalculateAbsolutePosition(false);
331                 data->basepos = getBasePos();
332                 data->bp_set = 2;
333                 return;
334         }
335         errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'"  << std::endl;
336 }
337
338 void GUIFormSpecMenu::parseList(parserData* data,std::string element) {
339
340         if (m_gamedef == 0) {
341                 errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
342                 return;
343         }
344
345         std::vector<std::string> parts = split(element,';');
346
347         if ((parts.size() == 4) || (parts.size() == 5)) {
348                 std::string location = parts[0];
349                 std::string listname = parts[1];
350                 std::vector<std::string> v_pos  = split(parts[2],',');
351                 std::vector<std::string> v_geom = split(parts[3],',');
352                 std::string startindex = "";
353                 if (parts.size() == 5)
354                         startindex = parts[4];
355
356                 MY_CHECKPOS("list",2);
357                 MY_CHECKGEOM("list",3);
358
359                 InventoryLocation loc;
360
361                 if(location == "context" || location == "current_name")
362                         loc = m_current_inventory_location;
363                 else
364                         loc.deSerialize(location);
365
366                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
367                 pos.X += stof(v_pos[0]) * (float)spacing.X;
368                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
369
370                 v2s32 geom;
371                 geom.X = stoi(v_geom[0]);
372                 geom.Y = stoi(v_geom[1]);
373                 infostream<<"list inv="<<location<<", listname="<<listname
374                                 <<", pos=("<<pos.X<<","<<pos.Y<<")"
375                                 <<", geom=("<<geom.X<<","<<geom.Y<<")"
376                                 <<std::endl;
377
378                 s32 start_i = 0;
379                 if(startindex != "")
380                         start_i = stoi(startindex);
381                 if(data->bp_set != 2)
382                         errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
383                 m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
384                 return;
385         }
386         errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
387 }
388
389 void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) {
390         std::vector<std::string> parts = split(element,';');
391
392         if ((parts.size() == 3) || (parts.size() == 4)) {
393                 std::vector<std::string> v_pos = split(parts[0],',');
394                 std::string name = parts[1];
395                 std::string label = parts[2];
396                 std::string selected = "";
397
398                 if (parts.size() == 4)
399                         selected = parts[3];
400
401                 MY_CHECKPOS("checkbox",0);
402
403                 v2s32 pos = padding;
404                 pos.X += stof(v_pos[0]) * (float) spacing.X;
405                 pos.Y += stof(v_pos[1]) * (float) spacing.Y;
406
407                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
408
409                 bool fselected = false;
410
411                 if (selected == "true")
412                         fselected = true;
413
414                 std::wstring wlabel = narrow_to_wide(label.c_str());
415
416                 if (m_use_gettext)
417                         wlabel = wstrgettext(label);
418
419                 FieldSpec spec = FieldSpec(
420                                 narrow_to_wide(name.c_str()),
421                                 L"",
422                                 wlabel,
423                                 258+m_fields.size()
424                         );
425
426                 spec.ftype = f_CheckBox;
427                 spec.flabel = wlabel; //Needed for displaying text on MSVC
428                 gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
429                                         spec.fid, spec.flabel.c_str());
430                 m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
431                 m_fields.push_back(spec);
432                 return;
433         }
434         errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'"  << std::endl;
435 }
436
437 void GUIFormSpecMenu::parseImage(parserData* data,std::string element) {
438         std::vector<std::string> parts = split(element,';');
439
440         if (parts.size() == 3) {
441                 std::vector<std::string> v_pos = split(parts[0],',');
442                 std::vector<std::string> v_geom = split(parts[1],',');
443                 std::string name = parts[2];
444
445                 MY_CHECKPOS("image",0);
446                 MY_CHECKGEOM("image",1);
447
448                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
449                 pos.X += stof(v_pos[0]) * (float) spacing.X;
450                 pos.Y += stof(v_pos[1]) * (float) spacing.Y;
451
452                 v2s32 geom;
453                 geom.X = stoi(v_geom[0]) * (float)imgsize.X;
454                 geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
455
456                 infostream<<"image name="<<name
457                                 <<", pos=("<<pos.X<<","<<pos.Y<<")"
458                                 <<", geom=("<<geom.X<<","<<geom.Y<<")"
459                                 <<std::endl;
460                 if(data->bp_set != 2)
461                         errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
462                 m_images.push_back(ImageDrawSpec(name, pos, geom));
463                 return;
464         }
465
466         if (parts.size() == 2) {
467                 std::vector<std::string> v_pos = split(parts[0],',');
468                 std::string name = parts[1];
469
470                 MY_CHECKPOS("image",0);
471
472                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
473                 pos.X += stof(v_pos[0]) * (float) spacing.X;
474                 pos.Y += stof(v_pos[1]) * (float) spacing.Y;
475
476                 std::cout<<"image name="<<name
477                                 <<", pos=("<<pos.X<<","<<pos.Y<<")"
478                                 <<std::endl;
479                 if(data->bp_set != 2)
480                         errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
481                 m_images.push_back(ImageDrawSpec(name, pos));
482                 return;
483         }
484         errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'"  << std::endl;
485 }
486
487 void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) {
488         std::vector<std::string> parts = split(element,';');
489
490         if (parts.size() == 3) {
491                 std::vector<std::string> v_pos = split(parts[0],',');
492                 std::vector<std::string> v_geom = split(parts[1],',');
493                 std::string name = parts[2];
494
495                 MY_CHECKPOS("itemimage",0);
496                 MY_CHECKGEOM("itemimage",1);
497
498                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
499                 pos.X += stof(v_pos[0]) * (float) spacing.X;
500                 pos.Y += stof(v_pos[1]) * (float) spacing.Y;
501
502                 v2s32 geom;
503                 geom.X = stoi(v_geom[0]) * (float)imgsize.X;
504                 geom.Y = stoi(v_geom[1]) * (float)imgsize.Y;
505
506                 infostream<<"item name="<<name
507                                 <<", pos=("<<pos.X<<","<<pos.Y<<")"
508                                 <<", geom=("<<geom.X<<","<<geom.Y<<")"
509                                 <<std::endl;
510                 if(data->bp_set != 2)
511                         errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
512                 m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
513                 return;
514         }
515         errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'"  << std::endl;
516 }
517
518 void GUIFormSpecMenu::parseButton(parserData* data,std::string element,std::string type) {
519         std::vector<std::string> parts = split(element,';');
520
521         if (parts.size() == 4) {
522                 std::vector<std::string> v_pos = split(parts[0],',');
523                 std::vector<std::string> v_geom = split(parts[1],',');
524                 std::string name = parts[2];
525                 std::string label = parts[3];
526
527                 MY_CHECKPOS("button",0);
528                 MY_CHECKGEOM("button",1);
529
530                 v2s32 pos = padding;
531                 pos.X += stof(v_pos[0]) * (float)spacing.X;
532                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
533
534                 v2s32 geom;
535                 geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
536                 pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
537
538                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
539
540                 if(data->bp_set != 2)
541                         errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
542
543                 label = unescape_string(label);
544
545                 std::wstring wlabel = narrow_to_wide(label.c_str());
546
547                 if (m_use_gettext)
548                         wlabel = wstrgettext(label);
549
550                 FieldSpec spec = FieldSpec(
551                         narrow_to_wide(name.c_str()),
552                         wlabel,
553                         L"",
554                         258+m_fields.size()
555                 );
556                 spec.ftype = f_Button;
557                 if(type == "button_exit")
558                         spec.is_exit = true;
559
560                 Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
561                 m_fields.push_back(spec);
562                 return;
563         }
564         errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'"  << std::endl;
565 }
566
567 void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
568         std::vector<std::string> parts = split(element,';');
569
570         if (parts.size() == 3) {
571                 std::vector<std::string> v_pos = split(parts[0],',');
572                 std::vector<std::string> v_geom = split(parts[1],',');
573                 std::string name = parts[2];
574
575                 MY_CHECKPOS("background",0);
576                 MY_CHECKGEOM("background",1);
577
578                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
579                 pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
580                 pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
581
582                 v2s32 geom;
583                 geom.X = stof(v_geom[0]) * (float)spacing.X;
584                 geom.Y = stof(v_geom[1]) * (float)spacing.Y;
585
586                 infostream<<"image name="<<name
587                                 <<", pos=("<<pos.X<<","<<pos.Y<<")"
588                                 <<", geom=("<<geom.X<<","<<geom.Y<<")"
589                                 <<std::endl;
590                 if(data->bp_set != 2)
591                         errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
592                 m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
593                 return;
594         }
595         errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'"  << std::endl;
596 }
597
598 void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
599         std::vector<std::string> parts = split(element,';');
600
601         if ((parts.size() == 5) || (parts.size() == 6)) {
602                 std::vector<std::string> v_pos = split(parts[0],',');
603                 std::vector<std::string> v_geom = split(parts[1],',');
604                 std::string name = parts[2];
605                 std::vector<std::string> items = split(parts[3],',',true);
606                 std::string str_initial_selection = "";
607                 std::string str_transparent = "false";
608
609                 if (parts.size() >= 5)
610                         str_initial_selection = parts[4];
611
612                 if (parts.size() >= 6)
613                         str_transparent = parts[5];
614
615                 MY_CHECKPOS("textlist",0);
616                 MY_CHECKGEOM("textlist",1);
617
618                 v2s32 pos = padding;
619                 pos.X += stof(v_pos[0]) * (float)spacing.X;
620                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
621
622                 v2s32 geom;
623                 geom.X = stof(v_geom[0]) * (float)spacing.X;
624                 geom.Y = stof(v_geom[1]) * (float)spacing.Y;
625
626
627                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
628
629                 std::wstring fname_w = narrow_to_wide(name.c_str());
630
631                 FieldSpec spec = FieldSpec(
632                         fname_w,
633                         L"",
634                         L"",
635                         258+m_fields.size()
636                 );
637
638                 spec.ftype = f_ListBox;
639
640                 //now really show list
641                 gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid);
642
643                 //don't reset if we already have a user specified selection
644                 if (data->listbox_selections.find(fname_w) == data->listbox_selections.end()) {
645                         e->setAutoScrollEnabled(false);
646                 }
647
648                 if (str_transparent == "false")
649                         e->setDrawBackground(true);
650
651                 for (unsigned int i=0; i < items.size(); i++) {
652                         if (items[i].c_str()[0] == '#') {
653                                 if (items[i].c_str()[1] == '#') {
654                                         e->addItem(narrow_to_wide(unescape_string(items[i])).c_str() +1);
655                                 }
656                                 else {
657                                         std::string color = items[i].substr(1,6);
658                                         std::wstring toadd =
659                                                 narrow_to_wide(unescape_string(items[i]).c_str() + 7);
660
661
662                                         e->addItem(toadd.c_str());
663
664                                         irr::video::SColor toset;
665
666                                         if (parseColor(color, toset))
667                                                 e->setItemOverrideColor(i,toset);
668                                 }
669                         }
670                         else {
671                                 e->addItem(narrow_to_wide(unescape_string(items[i])).c_str());
672                         }
673                 }
674
675                 if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
676                         e->setSelected(data->listbox_selections[fname_w]);
677                 }
678
679                 if (str_initial_selection != "")
680                         e->setSelected(stoi(str_initial_selection.c_str())-1);
681
682                 m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
683                 m_fields.push_back(spec);
684                 return;
685         }
686         errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'"  << std::endl;
687 }
688
689
690 void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) {
691         std::vector<std::string> parts = split(element,';');
692
693         if (parts.size() == 5) {
694                 std::vector<std::string> v_pos = split(parts[0],',');
695                 std::string name = parts[2];
696                 std::vector<std::string> items = split(parts[3],',');
697                 std::string str_initial_selection = "";
698                 str_initial_selection = parts[4];
699
700                 MY_CHECKPOS("dropdown",0);
701
702                 v2s32 pos = padding;
703                 pos.X += stof(v_pos[0]) * (float)spacing.X;
704                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
705
706                 s32 width = stof(parts[1]) * (float)spacing.Y;
707
708                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+width, pos.Y+30);
709
710                 std::wstring fname_w = narrow_to_wide(name.c_str());
711
712                 FieldSpec spec = FieldSpec(
713                         fname_w,
714                         L"",
715                         L"",
716                         258+m_fields.size()
717                 );
718
719                 spec.ftype = f_DropDown;
720                 spec.send = true;
721
722                 //now really show list
723                 gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
724
725                 //don't reset if we already have a user specified selection
726                 //if (data->combobox_selections.find(fname_w) == data->listbox_selections.end()) {
727                 //      e->setAutoScrollEnabled(false);
728                 //}
729
730                 for (unsigned int i=0; i < items.size(); i++) {
731                         e->addItem(narrow_to_wide(items[i]).c_str());
732                 }
733
734                 if (str_initial_selection != "")
735                         e->setSelected(stoi(str_initial_selection.c_str())-1);
736
737                 //if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
738                 //      e->setSelected(data->listbox_selections[fname_w]);
739                 //}
740
741                 //m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
742                 m_fields.push_back(spec);
743                 return;
744         }
745         errorstream << "Invalid dropdown element(" << parts.size() << "): '"
746                                 << element << "'"  << std::endl;
747 }
748
749 void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) {
750         std::vector<std::string> parts = split(element,';');
751
752         if (parts.size() == 4) {
753                 std::vector<std::string> v_pos = split(parts[0],',');
754                 std::vector<std::string> v_geom = split(parts[1],',');
755                 std::string name = parts[2];
756                 std::string label = parts[3];
757
758                 MY_CHECKPOS("pwdfield",0);
759                 MY_CHECKGEOM("pwdfield",1);
760
761                 v2s32 pos;
762                 pos.X += stof(v_pos[0]) * (float)spacing.X;
763                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
764
765                 v2s32 geom;
766                 geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
767
768                 pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
769                 pos.Y -= 15;
770                 geom.Y = 30;
771
772                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
773
774                 label = unescape_string(label);
775
776                 std::wstring wlabel = narrow_to_wide(label.c_str());
777
778                 if (m_use_gettext) {
779                         if (label.length() > 1)
780                                 wlabel = wstrgettext(label);
781                         else
782                                 wlabel = L"";
783                 }
784
785                 FieldSpec spec = FieldSpec(
786                         narrow_to_wide(name.c_str()),
787                         wlabel,
788                         L"",
789                         258+m_fields.size()
790                         );
791
792                 spec.send = true;
793                 gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
794                 Environment->setFocus(e);
795
796                 if (label.length() > 1)
797                 {
798                         rect.UpperLeftCorner.Y -= 15;
799                         rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
800                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
801                 }
802
803                 e->setPasswordBox(true,L'*');
804
805                 irr::SEvent evt;
806                 evt.KeyInput.Key = KEY_END;
807                 evt.EventType = EET_KEY_INPUT_EVENT;
808                 evt.KeyInput.PressedDown = true;
809                 e->OnEvent(evt);
810                 m_fields.push_back(spec);
811                 return;
812         }
813         errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'"  << std::endl;
814 }
815
816 void GUIFormSpecMenu::parseSimpleField(parserData* data,std::vector<std::string> &parts) {
817         std::string name = parts[0];
818         std::string label = parts[1];
819         std::string default_val = parts[2];
820
821         core::rect<s32> rect;
822
823         if(!data->bp_set)
824         {
825                 rect = core::rect<s32>(
826                         data->screensize.X/2 - 580/2,
827                         data->screensize.Y/2 - 300/2,
828                         data->screensize.X/2 + 580/2,
829                         data->screensize.Y/2 + 300/2
830                 );
831                 DesiredRect = rect;
832                 recalculateAbsolutePosition(false);
833                 data->basepos = getBasePos();
834                 data->bp_set = 1;
835         }
836         else if(data->bp_set == 2)
837                 errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
838
839         v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
840         pos.Y = ((m_fields.size()+2)*60);
841         v2s32 size = DesiredRect.getSize();
842
843         rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
844
845
846         if(m_form_src)
847                 default_val = m_form_src->resolveText(default_val);
848
849         default_val = unescape_string(default_val);
850         label = unescape_string(label);
851
852         std::wstring wlabel = narrow_to_wide(label.c_str());
853
854         if (m_use_gettext) {
855                 if (label.length() > 1)
856                         wlabel = wstrgettext(label);
857                 else
858                         wlabel = L"";
859         }
860
861         FieldSpec spec = FieldSpec(
862                 narrow_to_wide(name.c_str()),
863                 wlabel,
864                 narrow_to_wide(default_val.c_str()),
865                 258+m_fields.size()
866         );
867
868         if (name == "")
869         {
870                 // spec field id to 0, this stops submit searching for a value that isn't there
871                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
872         }
873         else
874         {
875                 spec.send = true;
876                 gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
877                 Environment->setFocus(e);
878
879                 irr::SEvent evt;
880                 evt.KeyInput.Key = KEY_END;
881                 evt.EventType = EET_KEY_INPUT_EVENT;
882                 evt.KeyInput.PressedDown = true;
883                 e->OnEvent(evt);
884
885                 if (label.length() > 1)
886                 {
887                         rect.UpperLeftCorner.Y -= 15;
888                         rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
889                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
890                 }
891         }
892
893         m_fields.push_back(spec);
894 }
895
896 void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type) {
897
898         std::vector<std::string> v_pos = split(parts[0],',');
899         std::vector<std::string> v_geom = split(parts[1],',');
900         std::string name = parts[2];
901         std::string label = parts[3];
902         std::string default_val = parts[4];
903
904         MY_CHECKPOS(type,0);
905         MY_CHECKGEOM(type,1);
906
907         v2s32 pos;
908         pos.X = stof(v_pos[0]) * (float) spacing.X;
909         pos.Y = stof(v_pos[1]) * (float) spacing.Y;
910
911         v2s32 geom;
912
913         geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
914
915         if (type == "textarea")
916         {
917                 geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
918                 pos.Y += 15;
919         }
920         else
921         {
922                 pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
923                 pos.Y -= 15;
924                 geom.Y = 30;
925         }
926
927         core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
928
929         if(data->bp_set != 2)
930                 errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
931
932         if(m_form_src)
933                 default_val = m_form_src->resolveText(default_val);
934
935
936         default_val = unescape_string(default_val);
937         label = unescape_string(label);
938
939         std::wstring wlabel = narrow_to_wide(label.c_str());
940
941         if (m_use_gettext) {
942                 if (label.length() > 1)
943                         wlabel = wstrgettext(label);
944                 else
945                         wlabel = L"";
946         }
947
948         FieldSpec spec = FieldSpec(
949                 narrow_to_wide(name.c_str()),
950                 wlabel,
951                 narrow_to_wide(default_val.c_str()),
952                 258+m_fields.size()
953         );
954
955         if (name == "")
956         {
957                 // spec field id to 0, this stops submit searching for a value that isn't there
958                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
959         }
960         else
961         {
962                 spec.send = true;
963                 gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
964                 Environment->setFocus(e);
965
966                 if (type == "textarea")
967                 {
968                         e->setMultiLine(true);
969                         e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
970                 } else {
971                         irr::SEvent evt;
972                         evt.EventType            = EET_KEY_INPUT_EVENT;
973                         evt.KeyInput.Key         = KEY_END;
974                         evt.KeyInput.Char        = 0;
975                         evt.KeyInput.Control     = 0;
976                         evt.KeyInput.Shift       = 0;
977                         evt.KeyInput.PressedDown = true;
978                         e->OnEvent(evt);
979                 }
980
981                 if (label.length() > 1)
982                 {
983                         rect.UpperLeftCorner.Y -= 15;
984                         rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
985                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
986                 }
987         }
988         m_fields.push_back(spec);
989 }
990
991 void GUIFormSpecMenu::parseField(parserData* data,std::string element,std::string type) {
992         std::vector<std::string> parts = split(element,';',true);
993
994         if (parts.size() == 3) {
995                 parseSimpleField(data,parts);
996                 return;
997         }
998
999         if (parts.size() == 5) {
1000                 parseTextArea(data,parts,type);
1001                 return;
1002         }
1003         errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'"  << std::endl;
1004 }
1005
1006 void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) {
1007         std::vector<std::string> parts = split(element,';');
1008
1009         if (parts.size() == 2) {
1010                 std::vector<std::string> v_pos = split(parts[0],',');
1011                 std::string text = parts[1];
1012
1013                 MY_CHECKPOS("label",0);
1014
1015                 v2s32 pos = padding;
1016                 pos.X += stof(v_pos[0]) * (float)spacing.X;
1017                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1018
1019                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
1020
1021                 if(data->bp_set != 2)
1022                         errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
1023
1024                 text = unescape_string(text);
1025
1026                 std::wstring wlabel = narrow_to_wide(text.c_str());
1027
1028                 if (m_use_gettext)
1029                         wlabel = wstrgettext(text);
1030
1031                 FieldSpec spec = FieldSpec(
1032                         L"",
1033                         wlabel,
1034                         L"",
1035                         258+m_fields.size()
1036                 );
1037                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
1038                 m_fields.push_back(spec);
1039                 return;
1040         }
1041         errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'"  << std::endl;
1042 }
1043
1044 void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) {
1045         std::vector<std::string> parts = split(element,';');
1046
1047         if (parts.size() == 2) {
1048                 std::vector<std::string> v_pos = split(parts[0],',');
1049                 std::string text = parts[1];
1050
1051                 MY_CHECKPOS("vertlabel",1);
1052
1053                 v2s32 pos = padding;
1054                 pos.X += stof(v_pos[0]) * (float)spacing.X;
1055                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1056
1057                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+15, pos.Y+300);
1058
1059                 if(data->bp_set != 2)
1060                         errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
1061
1062                 text = unescape_string(text);
1063                 std::string label = "";
1064
1065                 if (m_use_gettext) {
1066                         const char* toset = gettext(text.c_str());
1067
1068                         text = std::string(toset);
1069                 }
1070
1071                 for (unsigned int i=0; i < text.length(); i++) {
1072                         label += text.c_str()[i];
1073                         label += "\n";
1074                 }
1075
1076                 FieldSpec spec = FieldSpec(
1077                         L"",
1078                         narrow_to_wide(label.c_str()),
1079                         L"",
1080                         258+m_fields.size()
1081                 );
1082                 gui::IGUIStaticText *t =
1083                                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
1084                 t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
1085                 m_fields.push_back(spec);
1086                 return;
1087         }
1088         errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'"  << std::endl;
1089 }
1090
1091 void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std::string type) {
1092         std::vector<std::string> parts = split(element,';');
1093
1094         if ((parts.size() == 5) || (parts.size() == 7) || (parts.size() == 8)) {
1095                 std::vector<std::string> v_pos = split(parts[0],',');
1096                 std::vector<std::string> v_geom = split(parts[1],',');
1097                 std::string image_name = parts[2];
1098                 std::string name = parts[3];
1099                 std::string label = parts[4];
1100
1101                 MY_CHECKPOS("imagebutton",0);
1102                 MY_CHECKGEOM("imagebutton",1);
1103
1104                 v2s32 pos = padding;
1105                 pos.X += stof(v_pos[0]) * (float)spacing.X;
1106                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1107                 v2s32 geom;
1108                 geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
1109                 geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
1110
1111                 bool noclip = false;
1112                 bool drawborder = true;
1113
1114                 if ((parts.size() >= 7)) {
1115                         if (parts[5] == "true")
1116                                 noclip = true;
1117
1118                         if (parts[6] == "false")
1119                                 drawborder = false;
1120                 }
1121                 
1122                 std::string pressed_image_name = "";
1123                 
1124                 if ((parts.size() == 8)) {
1125                         pressed_image_name = parts[7];
1126                 }
1127
1128                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1129
1130                 if(data->bp_set != 2)
1131                         errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
1132
1133                 label = unescape_string(label);
1134
1135                 std::wstring wlabel = narrow_to_wide(label.c_str());
1136
1137                 if (m_use_gettext)
1138                         wlabel = wstrgettext(label);
1139
1140                 FieldSpec spec = FieldSpec(
1141                         narrow_to_wide(name.c_str()),
1142                         wlabel,
1143                         narrow_to_wide(image_name.c_str()),
1144                         258+m_fields.size()
1145                 );
1146                 spec.ftype = f_Button;
1147                 if(type == "image_button_exit")
1148                         spec.is_exit = true;
1149
1150                 video::ITexture *texture = 0;
1151                 video::ITexture *pressed_texture = 0;
1152                 //if there's no gamedef specified try to get direct
1153                 //TODO check for possible texture leak
1154                 if (m_gamedef != 0) {
1155                         texture = m_gamedef->tsrc()->getTexture(image_name);
1156                         if ((parts.size() == 8)) {
1157                                 pressed_texture = m_gamedef->tsrc()->getTexture(pressed_image_name);
1158                         }
1159                 } else {
1160                         if (fs::PathExists(image_name)) {
1161                                 texture = Environment->getVideoDriver()->getTexture(image_name.c_str());
1162                                 m_Textures.push_back(texture);
1163                         }
1164                         if (fs::PathExists(pressed_image_name)) {
1165                                 pressed_texture = Environment->getVideoDriver()->getTexture(pressed_image_name.c_str());
1166                                 m_Textures.push_back(pressed_texture);
1167                         }
1168                 }
1169                 if (parts.size() < 8)
1170                         pressed_texture = texture;
1171
1172                 gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
1173                 e->setUseAlphaChannel(true);
1174                 e->setImage(texture);
1175                 e->setPressedImage(pressed_texture);
1176                 e->setScaleImage(true);
1177                 e->setNotClipped(noclip);
1178                 e->setDrawBorder(drawborder);
1179
1180                 m_fields.push_back(spec);
1181                 return;
1182         }
1183
1184         errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
1185 }
1186
1187 void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) {
1188         std::vector<std::string> parts = split(element,';');
1189
1190         if ((parts.size() == 4) || (parts.size() == 6)) {
1191                 std::vector<std::string> v_pos = split(parts[0],',');
1192                 std::string name = parts[1];
1193                 std::vector<std::string> buttons = split(parts[2],',');
1194                 std::string str_index = parts[3];
1195                 bool show_background = true;
1196                 bool show_border = true;
1197                 int tab_index = stoi(str_index) -1;
1198
1199                 MY_CHECKPOS("tabheader",0);
1200
1201                 if (parts.size() == 6) {
1202                         if (parts[4] == "true")
1203                                 show_background = false;
1204                         if (parts[5] == "false")
1205                                 show_border = false;
1206                 }
1207
1208                 FieldSpec spec = FieldSpec(
1209                         narrow_to_wide(name.c_str()),
1210                         L"",
1211                         L"",
1212                         258+m_fields.size()
1213                 );
1214
1215                 spec.ftype = f_TabHeader;
1216
1217                 v2s32 pos = padding;
1218                 pos.X += stof(v_pos[0]) * (float)spacing.X;
1219                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1220                 v2s32 geom;
1221                 geom.X = data->screensize.Y;
1222                 geom.Y = 30;
1223
1224                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1225
1226                 gui::IGUITabControl *e = Environment->addTabControl(rect,this,show_background,show_border,spec.fid);
1227
1228                 e->setNotClipped(true);
1229
1230                 for (unsigned int i=0; i< buttons.size(); i++) {
1231                         wchar_t* wbutton = 0;
1232
1233                         if (m_use_gettext)
1234                                 wbutton = wgettext(buttons[i].c_str());
1235                         else
1236                                 wbutton = (wchar_t*) narrow_to_wide(buttons[i].c_str()).c_str();
1237
1238                         e->addTab(wbutton,-1);
1239
1240                         if (m_use_gettext)
1241                                 delete[] wbutton;
1242                 }
1243
1244                 if ((tab_index >= 0) &&
1245                                 (buttons.size() < INT_MAX) &&
1246                                 (tab_index < (int) buttons.size()))
1247                         e->setActiveTab(tab_index);
1248
1249                 m_fields.push_back(spec);
1250                 return;
1251         }
1252         errorstream<< "Invalid TabHeader element(" << parts.size() << "): '" << element << "'"  << std::endl;
1253 }
1254
1255 void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) {
1256
1257         if (m_gamedef == 0) {
1258                 errorstream<<"WARNING: invalid use of item_image_button with m_gamedef==0"<<std::endl;
1259                 return;
1260         }
1261
1262         std::vector<std::string> parts = split(element,';');
1263
1264         if (parts.size() == 5) {
1265                 std::vector<std::string> v_pos = split(parts[0],',');
1266                 std::vector<std::string> v_geom = split(parts[1],',');
1267                 std::string item_name = parts[2];
1268                 std::string name = parts[3];
1269                 std::string label = parts[4];
1270
1271                 MY_CHECKPOS("itemimagebutton",0);
1272                 MY_CHECKGEOM("itemimagebutton",1);
1273
1274                 v2s32 pos = padding;
1275                 pos.X += stof(v_pos[0]) * (float)spacing.X;
1276                 pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1277                 v2s32 geom;
1278                 geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
1279                 geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
1280
1281                 core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
1282
1283                 if(data->bp_set != 2)
1284                         errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
1285
1286                 IItemDefManager *idef = m_gamedef->idef();
1287                 ItemStack item;
1288                 item.deSerialize(item_name, idef);
1289                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
1290                 std::string tooltip = item.getDefinition(idef).description;
1291
1292                 label = unescape_string(label);
1293                 FieldSpec spec = FieldSpec(
1294                         narrow_to_wide(name.c_str()),
1295                         narrow_to_wide(label.c_str()),
1296                         narrow_to_wide(item_name.c_str()),
1297                         258+m_fields.size()
1298                 );
1299
1300                 gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
1301                 e->setUseAlphaChannel(true);
1302                 e->setImage(texture);
1303                 e->setPressedImage(texture);
1304                 e->setScaleImage(true);
1305                 spec.ftype = f_Button;
1306                 rect+=data->basepos-padding;
1307                 spec.rect=rect;
1308                 if (tooltip!="")
1309                         spec.tooltip=tooltip;
1310                 m_fields.push_back(spec);
1311                 return;
1312         }
1313         errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
1314 }
1315
1316 void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
1317         std::vector<std::string> parts = split(element,';');
1318
1319         if (parts.size() == 3) {
1320                 std::vector<std::string> v_pos = split(parts[0],',');
1321                 std::vector<std::string> v_geom = split(parts[1],',');
1322                 std::string color_str = parts[2];
1323
1324                 MY_CHECKPOS("box",0);
1325                 MY_CHECKGEOM("box",1);
1326
1327                 v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
1328                 pos.X += stof(v_pos[0]) * (float) spacing.X;
1329                 pos.Y += stof(v_pos[1]) * (float) spacing.Y;
1330
1331                 v2s32 geom;
1332                 geom.X = stof(v_geom[0]) * (float)spacing.X;
1333                 geom.Y = stof(v_geom[1]) * (float)spacing.Y;
1334
1335                 irr::video::SColor color;
1336
1337                 if (parseColor(color_str, color)) {
1338                         BoxDrawSpec spec(pos,geom,color);
1339
1340                         m_boxes.push_back(spec);
1341                 }
1342                 else {
1343                         errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'  INVALID COLOR"  << std::endl;
1344                 }
1345                 return;
1346         }
1347         errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'"  << std::endl;
1348 }
1349
1350 void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
1351
1352         //some prechecks
1353         if (element == "")
1354                 return;
1355
1356         std::vector<std::string> parts = split(element,'[', true);
1357
1358         // ugly workaround to keep compatibility
1359         if (parts.size() > 2) {
1360                 if (trim(parts[0]) == "image") {
1361                         for (unsigned int i=2;i< parts.size(); i++) {
1362                                 parts[1] += "[" + parts[i];
1363                         }
1364                 }
1365                 else { return; }
1366         }
1367
1368         if (parts.size() < 2) {
1369                 return;
1370         }
1371
1372         std::string type = trim(parts[0]);
1373         std::string description = trim(parts[1]);
1374
1375         if ((type == "size") || (type == "invsize")){
1376                 parseSize(data,description);
1377                 return;
1378         }
1379
1380         if (type == "list") {
1381                 parseList(data,description);
1382                 return;
1383         }
1384
1385         if (type == "checkbox") {
1386                 parseCheckbox(data,description);
1387                 return;
1388         }
1389
1390         if (type == "image") {
1391                 parseImage(data,description);
1392                 return;
1393         }
1394
1395         if (type == "item_image") {
1396                 parseItemImage(data,description);
1397                 return;
1398         }
1399
1400         if ((type == "button") || (type == "button_exit")) {
1401                 parseButton(data,description,type);
1402                 return;
1403         }
1404
1405         if (type == "background") {
1406                 parseBackground(data,description);
1407                 return;
1408         }
1409
1410         if (type == "textlist"){
1411                 parseTextList(data,description);
1412                 return;
1413         }
1414
1415         if (type == "dropdown"){
1416                 parseDropDown(data,description);
1417                 return;
1418         }
1419
1420         if (type == "pwdfield") {
1421                 parsePwdField(data,description);
1422                 return;
1423         }
1424
1425         if ((type == "field") || (type == "textarea")){
1426                 parseField(data,description,type);
1427                 return;
1428         }
1429
1430         if (type == "label") {
1431                 parseLabel(data,description);
1432                 return;
1433         }
1434
1435         if (type == "vertlabel") {
1436                 parseVertLabel(data,description);
1437                 return;
1438         }
1439
1440         if (type == "item_image_button") {
1441                 parseItemImageButton(data,description);
1442                 return;
1443         }
1444
1445         if ((type == "image_button") || (type == "image_button_exit")) {
1446                 parseImageButton(data,description,type);
1447                 return;
1448         }
1449
1450         if (type == "tabheader") {
1451                 parseTabHeader(data,description);
1452                 return;
1453         }
1454
1455         if (type == "box") {
1456                 parseBox(data,description);
1457                 return;
1458         }
1459
1460         // Ignore others
1461         infostream
1462                 << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
1463                 <<std::endl;
1464 }
1465
1466
1467
1468 void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
1469 {
1470         parserData mydata;
1471
1472         //preserve listboxes
1473         for (unsigned int i = 0; i < m_listboxes.size(); i++) {
1474                 int selection = m_listboxes[i].second->getSelected();
1475                 if (selection != -1) {
1476                         std::wstring listboxname = m_listboxes[i].first.fname;
1477                         mydata.listbox_selections[listboxname] = selection;
1478                 }
1479         }
1480
1481         // Remove children
1482         removeChildren();
1483
1484         mydata.size= v2s32(100,100);
1485         mydata.helptext_h = 15;
1486         mydata.screensize = screensize;
1487
1488         // Base position of contents of form
1489         mydata.basepos = getBasePos();
1490
1491         // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
1492         // Used to adjust form size automatically if needed
1493         // A proceed button is added if there is no size[] element
1494         mydata.bp_set = 0;
1495
1496         
1497         /* Convert m_init_draw_spec to m_inventorylists */
1498         
1499         m_inventorylists.clear();
1500         m_images.clear();
1501         m_backgrounds.clear();
1502         m_itemimages.clear();
1503         m_listboxes.clear();
1504         m_checkboxes.clear();
1505         m_fields.clear();
1506         m_boxes.clear();
1507
1508
1509         std::vector<std::string> elements = split(m_formspec_string,']',true);
1510
1511         for (unsigned int i=0;i< elements.size();i++) {
1512                 parseElement(&mydata,elements[i]);
1513         }
1514
1515         // If there's inventory, put the usage string at the bottom
1516         if (m_inventorylists.size())
1517         {
1518                 changeCtype("");
1519                 core::rect<s32> rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h);
1520                 rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5,
1521                                 mydata.size.Y-5-mydata.helptext_h);
1522                 const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
1523                 Environment->addStaticText(text, rect, false, true, this, 256);
1524                 delete[] text;
1525                 changeCtype("C");
1526         }
1527         // If there's fields, add a Proceed button
1528         if (m_fields.size() && mydata.bp_set != 2)
1529         {
1530                 // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
1531                 mydata.rect = core::rect<s32>(
1532                                 mydata.screensize.X/2 - 580/2,
1533                                 mydata.screensize.Y/2 - 300/2,
1534                                 mydata.screensize.X/2 + 580/2,
1535                                 mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
1536                 );
1537                 DesiredRect = mydata.rect;
1538                 recalculateAbsolutePosition(false);
1539                 mydata.basepos = getBasePos();
1540
1541                 changeCtype("");
1542                 {
1543                         v2s32 pos = mydata.basepos;
1544                         pos.Y = ((m_fields.size()+2)*60);
1545
1546                         v2s32 size = DesiredRect.getSize();
1547                         mydata.rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
1548                         wchar_t* text = wgettext("Proceed");
1549                         Environment->addButton(mydata.rect, this, 257, text);
1550                         delete[] text;
1551                 }
1552                 changeCtype("C");
1553         }
1554         // Add tooltip
1555         {
1556                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
1557                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
1558                 m_tooltip_element->enableOverrideColor(true);
1559                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
1560                 m_tooltip_element->setDrawBackground(true);
1561                 m_tooltip_element->setDrawBorder(true);
1562                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
1563                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
1564                 m_tooltip_element->setWordWrap(false);
1565         }
1566 }
1567
1568 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
1569 {
1570         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1571         
1572         for(u32 i=0; i<m_inventorylists.size(); i++)
1573         {
1574                 const ListDrawSpec &s = m_inventorylists[i];
1575
1576                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
1577                 {
1578                         s32 item_i = i + s.start_item_i;
1579                         s32 x = (i%s.geom.X) * spacing.X;
1580                         s32 y = (i/s.geom.X) * spacing.Y;
1581                         v2s32 p0(x,y);
1582                         core::rect<s32> rect = imgrect + s.pos + p0;
1583                         if(rect.isPointInside(p))
1584                         {
1585                                 return ItemSpec(s.inventoryloc, s.listname, item_i);
1586                         }
1587                 }
1588         }
1589
1590         return ItemSpec(InventoryLocation(), "", -1);
1591 }
1592
1593 void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
1594 {
1595         video::IVideoDriver* driver = Environment->getVideoDriver();
1596
1597         // Get font
1598         gui::IGUIFont *font = NULL;
1599         gui::IGUISkin* skin = Environment->getSkin();
1600         if (skin)
1601                 font = skin->getFont();
1602         
1603         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1604         if(!inv){
1605                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
1606                                 <<"The inventory location "
1607                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1608                                 <<std::endl;
1609                 return;
1610         }
1611         InventoryList *ilist = inv->getList(s.listname);
1612         if(!ilist){
1613                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
1614                                 <<"The inventory list \""<<s.listname<<"\" @ \""
1615                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
1616                                 <<std::endl;
1617                 return;
1618         }
1619         
1620         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1621         
1622         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
1623         {
1624                 s32 item_i = i + s.start_item_i;
1625                 if(item_i >= (s32) ilist->getSize())
1626                         break;
1627                 s32 x = (i%s.geom.X) * spacing.X;
1628                 s32 y = (i/s.geom.X) * spacing.Y;
1629                 v2s32 p(x,y);
1630                 core::rect<s32> rect = imgrect + s.pos + p;
1631                 ItemStack item;
1632                 if(ilist)
1633                         item = ilist->getItem(item_i);
1634
1635                 bool selected = m_selected_item
1636                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
1637                         && m_selected_item->listname == s.listname
1638                         && m_selected_item->i == item_i;
1639                 bool hovering = rect.isPointInside(m_pointer);
1640
1641                 if(phase == 0)
1642                 {
1643                         if(hovering && m_selected_item)
1644                         {
1645                                 video::SColor bgcolor(255,192,192,192);
1646                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
1647                         }
1648                         else
1649                         {
1650                                 video::SColor bgcolor(255,128,128,128);
1651                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
1652                         }
1653                 }
1654
1655                 if(phase == 1)
1656                 {
1657                         // Draw item stack
1658                         if(selected)
1659                         {
1660                                 item.takeItem(m_selected_amount);
1661                         }
1662                         if(!item.empty())
1663                         {
1664                                 drawItemStack(driver, font, item,
1665                                                 rect, &AbsoluteClippingRect, m_gamedef);
1666                         }
1667
1668                         // Draw tooltip
1669                         std::string tooltip_text = "";
1670                         if(hovering && !m_selected_item)
1671                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
1672                         if(tooltip_text != "")
1673                         {
1674                                 m_tooltip_element->setVisible(true);
1675                                 this->bringToFront(m_tooltip_element);
1676                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
1677                                 s32 tooltip_x = m_pointer.X + 15;
1678                                 s32 tooltip_y = m_pointer.Y + 15;
1679                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
1680                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
1681                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
1682                                                 core::position2d<s32>(tooltip_x, tooltip_y),
1683                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
1684                         }
1685                 }
1686         }
1687 }
1688
1689 void GUIFormSpecMenu::drawSelectedItem()
1690 {
1691         if(!m_selected_item)
1692                 return;
1693
1694         video::IVideoDriver* driver = Environment->getVideoDriver();
1695
1696         // Get font
1697         gui::IGUIFont *font = NULL;
1698         gui::IGUISkin* skin = Environment->getSkin();
1699         if (skin)
1700                 font = skin->getFont();
1701         
1702         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1703         assert(inv);
1704         InventoryList *list = inv->getList(m_selected_item->listname);
1705         assert(list);
1706         ItemStack stack = list->getItem(m_selected_item->i);
1707         stack.count = m_selected_amount;
1708
1709         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1710         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
1711         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
1712 }
1713
1714 void GUIFormSpecMenu::drawMenu()
1715 {
1716         if(m_form_src){
1717                 std::string newform = m_form_src->getForm();
1718                 if(newform != m_formspec_string){
1719                         m_formspec_string = newform;
1720                         regenerateGui(m_screensize_old);
1721                 }
1722         }
1723
1724         m_pointer = m_device->getCursorControl()->getPosition();
1725
1726         updateSelectedItem();
1727
1728         gui::IGUISkin* skin = Environment->getSkin();
1729         if (!skin)
1730                 return;
1731         video::IVideoDriver* driver = Environment->getVideoDriver();
1732         
1733         video::SColor bgcolor(140,0,0,0);
1734         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
1735
1736         m_tooltip_element->setVisible(false);
1737
1738         /*
1739                 Draw backgrounds
1740         */
1741         for(u32 i=0; i<m_backgrounds.size(); i++)
1742         {
1743                 const ImageDrawSpec &spec = m_backgrounds[i];
1744                 video::ITexture *texture = 0;
1745
1746                 if (m_gamedef != 0)
1747                         texture = m_gamedef->tsrc()->getTexture(spec.name);
1748                 else
1749                 {
1750                         texture = driver->getTexture(spec.name.c_str());
1751                         m_Textures.push_back(texture);
1752                 }
1753
1754                 if (texture != 0) {
1755                         // Image size on screen
1756                         core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
1757                         // Image rectangle on screen
1758                         core::rect<s32> rect = imgrect + spec.pos;
1759                         const video::SColor color(255,255,255,255);
1760                         const video::SColor colors[] = {color,color,color,color};
1761                         driver->draw2DImage(texture, rect,
1762                                 core::rect<s32>(core::position2d<s32>(0,0),
1763                                                 core::dimension2di(texture->getOriginalSize())),
1764                                 NULL/*&AbsoluteClippingRect*/, colors, true);
1765                 }
1766                 else {
1767                         errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
1768                         errorstream << "\t" << spec.name << std::endl;
1769                 }
1770         }
1771         
1772         /*
1773                 Draw Boxes
1774         */
1775         for(u32 i=0; i<m_boxes.size(); i++)
1776         {
1777                 const BoxDrawSpec &spec = m_boxes[i];
1778
1779                 irr::video::SColor todraw = spec.color;
1780
1781                 todraw.setAlpha(140);
1782
1783                 core::rect<s32> rect(spec.pos.X,spec.pos.Y,
1784                                                         spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
1785
1786                 driver->draw2DRectangle(todraw, rect, 0);
1787         }
1788         /*
1789                 Draw images
1790         */
1791         for(u32 i=0; i<m_images.size(); i++)
1792         {
1793                 const ImageDrawSpec &spec = m_images[i];
1794                 video::ITexture *texture = 0;
1795
1796                 if (m_gamedef != 0)
1797                         texture = m_gamedef->tsrc()->getTexture(spec.name);
1798                 else
1799                 {
1800                         texture = driver->getTexture(spec.name.c_str());
1801                         m_Textures.push_back(texture);
1802                 }
1803                 if (texture != 0) {
1804                         const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
1805                         // Image size on screen
1806                         core::rect<s32> imgrect;
1807
1808                         if (spec.scale)
1809                                 imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
1810                         else {
1811
1812                                 imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
1813                         }
1814                         // Image rectangle on screen
1815                         core::rect<s32> rect = imgrect + spec.pos;
1816                         const video::SColor color(255,255,255,255);
1817                         const video::SColor colors[] = {color,color,color,color};
1818                         driver->draw2DImage(texture, rect,
1819                                 core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
1820                                 NULL/*&AbsoluteClippingRect*/, colors, true);
1821                 }
1822                 else {
1823                         errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
1824                         errorstream << "\t" << spec.name << std::endl;
1825                 }
1826         }
1827         
1828         /*
1829                 Draw item images
1830         */
1831         for(u32 i=0; i<m_itemimages.size(); i++)
1832         {
1833                 if (m_gamedef == 0)
1834                         break;
1835
1836                 const ImageDrawSpec &spec = m_itemimages[i];
1837                 IItemDefManager *idef = m_gamedef->idef();
1838                 ItemStack item;
1839                 item.deSerialize(spec.name, idef);
1840                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);         
1841                 // Image size on screen
1842                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
1843                 // Image rectangle on screen
1844                 core::rect<s32> rect = imgrect + spec.pos;
1845                 const video::SColor color(255,255,255,255);
1846                 const video::SColor colors[] = {color,color,color,color};
1847                 driver->draw2DImage(texture, rect,
1848                         core::rect<s32>(core::position2d<s32>(0,0),
1849                                         core::dimension2di(texture->getOriginalSize())),
1850                         NULL/*&AbsoluteClippingRect*/, colors, true);
1851         }
1852         
1853         /*
1854                 Draw items
1855                 Phase 0: Item slot rectangles
1856                 Phase 1: Item images; prepare tooltip
1857                 If backgrounds used, do not draw Item slot rectangles
1858         */
1859         int start_phase=0;
1860         if (m_backgrounds.size() > 0) start_phase=1;
1861         for(int phase=start_phase; phase<=1; phase++)
1862         for(u32 i=0; i<m_inventorylists.size(); i++)
1863         {
1864                 drawList(m_inventorylists[i], phase);
1865         }
1866
1867         /*
1868                 Call base class
1869         */
1870         gui::IGUIElement::draw();
1871         
1872         /*
1873                 Draw fields/buttons tooltips
1874         */
1875         for(u32 i=0; i<m_fields.size(); i++)
1876         {
1877                 const FieldSpec &spec = m_fields[i];
1878                 if (spec.tooltip != "")
1879                 {
1880                         core::rect<s32> rect = spec.rect;
1881                         if (rect.isPointInside(m_pointer)) 
1882                         {
1883                                 m_tooltip_element->setVisible(true);
1884                                 this->bringToFront(m_tooltip_element);
1885                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
1886                                 s32 tooltip_x = m_pointer.X + 15;
1887                                 s32 tooltip_y = m_pointer.Y + 15;
1888                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
1889                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
1890                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
1891                                 core::position2d<s32>(tooltip_x, tooltip_y),
1892                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
1893                         }
1894                 }
1895         }
1896         
1897         /*
1898                 Draw dragged item stack
1899         */
1900         drawSelectedItem();
1901 }
1902
1903 void GUIFormSpecMenu::updateSelectedItem()
1904 {
1905         // If the selected stack has become empty for some reason, deselect it.
1906         // If the selected stack has become inaccessible, deselect it.
1907         // If the selected stack has become smaller, adjust m_selected_amount.
1908         ItemStack selected = verifySelectedItem();
1909
1910         // WARNING: BLACK MAGIC
1911         // See if there is a stack suited for our current guess.
1912         // If such stack does not exist, clear the guess.
1913         if(m_selected_content_guess.name != "" &&
1914                         selected.name == m_selected_content_guess.name &&
1915                         selected.count == m_selected_content_guess.count){
1916                 // Selected item fits the guess. Skip the black magic.
1917         }
1918         else if(m_selected_content_guess.name != ""){
1919                 bool found = false;
1920                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
1921                         const ListDrawSpec &s = m_inventorylists[i];
1922                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1923                         if(!inv)
1924                                 continue;
1925                         InventoryList *list = inv->getList(s.listname);
1926                         if(!list)
1927                                 continue;
1928                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
1929                                 u32 item_i = i + s.start_item_i;
1930                                 if(item_i >= list->getSize())
1931                                         continue;
1932                                 ItemStack stack = list->getItem(item_i);
1933                                 if(stack.name == m_selected_content_guess.name &&
1934                                                 stack.count == m_selected_content_guess.count){
1935                                         found = true;
1936                                         infostream<<"Client: Changing selected content guess to "
1937                                                         <<s.inventoryloc.dump()<<" "<<s.listname
1938                                                         <<" "<<item_i<<std::endl;
1939                                         delete m_selected_item;
1940                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
1941                                         m_selected_amount = stack.count;
1942                                 }
1943                         }
1944                 }
1945                 if(!found){
1946                         infostream<<"Client: Discarding selected content guess: "
1947                                         <<m_selected_content_guess.getItemString()<<std::endl;
1948                         m_selected_content_guess.name = "";
1949                 }
1950         }
1951
1952         // If craftresult is nonempty and nothing else is selected, select it now.
1953         if(!m_selected_item)
1954         {
1955                 for(u32 i=0; i<m_inventorylists.size(); i++)
1956                 {
1957                         const ListDrawSpec &s = m_inventorylists[i];
1958                         if(s.listname == "craftpreview")
1959                         {
1960                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1961                                 InventoryList *list = inv->getList("craftresult");
1962                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
1963                                 {
1964                                         m_selected_item = new ItemSpec;
1965                                         m_selected_item->inventoryloc = s.inventoryloc;
1966                                         m_selected_item->listname = "craftresult";
1967                                         m_selected_item->i = 0;
1968                                         m_selected_amount = 0;
1969                                         m_selected_dragging = false;
1970                                         break;
1971                                 }
1972                         }
1973                 }
1974         }
1975
1976         // If craftresult is selected, keep the whole stack selected
1977         if(m_selected_item && m_selected_item->listname == "craftresult")
1978         {
1979                 m_selected_amount = verifySelectedItem().count;
1980         }
1981 }
1982
1983 ItemStack GUIFormSpecMenu::verifySelectedItem()
1984 {
1985         // If the selected stack has become empty for some reason, deselect it.
1986         // If the selected stack has become inaccessible, deselect it.
1987         // If the selected stack has become smaller, adjust m_selected_amount.
1988         // Return the selected stack.
1989
1990         if(m_selected_item)
1991         {
1992                 if(m_selected_item->isValid())
1993                 {
1994                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1995                         if(inv)
1996                         {
1997                                 InventoryList *list = inv->getList(m_selected_item->listname);
1998                                 if(list && (u32) m_selected_item->i < list->getSize())
1999                                 {
2000                                         ItemStack stack = list->getItem(m_selected_item->i);
2001                                         if(m_selected_amount > stack.count)
2002                                                 m_selected_amount = stack.count;
2003                                         if(!stack.empty())
2004                                                 return stack;
2005                                 }
2006                         }
2007                 }
2008
2009                 // selection was not valid
2010                 delete m_selected_item;
2011                 m_selected_item = NULL;
2012                 m_selected_amount = 0;
2013                 m_selected_dragging = false;
2014         }
2015         return ItemStack();
2016 }
2017
2018 void GUIFormSpecMenu::acceptInput(int eventtype)
2019 {
2020         if(m_text_dst)
2021         {
2022                 std::map<std::string, std::string> fields;
2023
2024                 if (current_keys_pending.key_down) {
2025                         fields["key_down"] = "true";
2026                         current_keys_pending.key_down = false;
2027                 }
2028
2029                 if (current_keys_pending.key_up) {
2030                         fields["key_up"] = "true";
2031                         current_keys_pending.key_up = false;
2032                 }
2033
2034                 if (current_keys_pending.key_enter) {
2035                         fields["key_enter"] = "true";
2036                         current_keys_pending.key_enter = false;
2037                 }
2038
2039                 if (current_keys_pending.key_escape) {
2040                         fields["key_escape"] = "true";
2041                         current_keys_pending.key_escape = false;
2042                 }
2043
2044                 for(u32 i=0; i<m_fields.size(); i++)
2045                 {
2046                         const FieldSpec &s = m_fields[i];
2047                         if(s.send) 
2048                         {
2049                                 if(s.ftype == f_Button)
2050                                 {
2051                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
2052                                 }
2053                                 else if(s.ftype == f_ListBox) {
2054                                         std::stringstream ss;
2055                                         if (eventtype == gui::EGET_LISTBOX_CHANGED) {
2056                                                 ss << "CHG:";
2057                                         }
2058                                         else {
2059                                                 ss << "DCL:";
2060                                         }
2061                                         ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1);
2062                                         fields[wide_to_narrow(s.fname.c_str())] = ss.str();
2063                                 }
2064                                 else if(s.ftype == f_DropDown) {
2065                                         // no dynamic cast possible due to some distributions shipped
2066                                         // without rtti support in irrlicht
2067                                         IGUIElement * element = getElementFromId(s.fid);
2068                                         gui::IGUIComboBox *e = NULL;
2069                                         if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
2070                                                 e = static_cast<gui::IGUIComboBox*>(element);
2071                                         }
2072                                         fields[wide_to_narrow(s.fname.c_str())] =
2073                                                         wide_to_narrow(e->getItem(e->getSelected()));
2074                                 }
2075                                 else if (s.ftype == f_TabHeader) {
2076                                         // no dynamic cast possible due to some distributions shipped
2077                                         // without rtti support in irrlicht
2078                                         IGUIElement * element = getElementFromId(s.fid);
2079                                         gui::IGUITabControl *e = NULL;
2080                                         if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
2081                                                 e = static_cast<gui::IGUITabControl*>(element);
2082                                         }
2083
2084                                         if (e != 0) {
2085                                                 std::stringstream ss;
2086                                                 ss << (e->getActiveTab() +1);
2087                                                 fields[wide_to_narrow(s.fname.c_str())] = ss.str();
2088                                         }
2089                                 }
2090                                 else if (s.ftype == f_CheckBox) {
2091                                         // no dynamic cast possible due to some distributions shipped
2092                                         // without rtti support in irrlicht
2093                                         IGUIElement * element = getElementFromId(s.fid);
2094                                         gui::IGUICheckBox *e = NULL;
2095                                         if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
2096                                                 e = static_cast<gui::IGUICheckBox*>(element);
2097                                         }
2098
2099                                         if (e != 0) {
2100                                                 if (e->isChecked())
2101                                                         fields[wide_to_narrow(s.fname.c_str())] = "true";
2102                                                 else
2103                                                         fields[wide_to_narrow(s.fname.c_str())] = "false";
2104                                         }
2105                                 }
2106                                 else
2107                                 {
2108                                         IGUIElement* e = getElementFromId(s.fid);
2109                                         if(e != NULL)
2110                                         {
2111                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
2112                                         }
2113                                 }
2114                         }
2115                 }
2116
2117                 m_text_dst->gotText(fields);
2118         }
2119 }
2120
2121 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
2122 {
2123         if(event.EventType==EET_KEY_INPUT_EVENT)
2124         {
2125                 KeyPress kp(event.KeyInput);
2126                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
2127                         kp == getKeySetting("keymap_inventory")))
2128                 {
2129                         if (m_allowclose)
2130                                 quitMenu();
2131                         else
2132                                 m_text_dst->gotText(narrow_to_wide("MenuQuit"));
2133                         return true;
2134                 }
2135                 if (event.KeyInput.PressedDown &&
2136                         (event.KeyInput.Key==KEY_RETURN ||
2137                          event.KeyInput.Key==KEY_UP ||
2138                          event.KeyInput.Key==KEY_DOWN)
2139                         ) {
2140
2141
2142                         switch (event.KeyInput.Key) {
2143                                 case KEY_RETURN:
2144                                         if (m_allowclose) {
2145                                                 acceptInput();
2146                                                 quitMenu();
2147                                         }
2148                                         else
2149                                                 current_keys_pending.key_enter = true;
2150                                         break;
2151                                 case KEY_UP:
2152                                         current_keys_pending.key_up = true;
2153                                         break;
2154                                 case KEY_DOWN:
2155                                         current_keys_pending.key_down = true;
2156                                         break;
2157                                 break;
2158                                 default:
2159                                         //can't happen at all!
2160                                         assert("reached a source line that can't ever been reached" == 0);
2161                                         break;
2162                         }
2163                         acceptInput();
2164                         return true;
2165                 }
2166
2167         }
2168         if(event.EventType==EET_MOUSE_INPUT_EVENT
2169                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
2170         {
2171                 // Mouse event other than movement
2172
2173                 // Get selected item and hovered/clicked item (s)
2174
2175                 updateSelectedItem();
2176                 ItemSpec s = getItemAtPos(m_pointer);
2177
2178                 Inventory *inv_selected = NULL;
2179                 Inventory *inv_s = NULL;
2180
2181                 if(m_selected_item)
2182                 {
2183                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
2184                         assert(inv_selected);
2185                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
2186                 }
2187
2188                 u32 s_count = 0;
2189
2190                 if(s.isValid())
2191                 do{ // breakable
2192                         inv_s = m_invmgr->getInventory(s.inventoryloc);
2193
2194                         if(!inv_s){
2195                                 errorstream<<"InventoryMenu: The selected inventory location "
2196                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
2197                                                 <<std::endl;
2198                                 s.i = -1;  // make it invalid again
2199                                 break;
2200                         }
2201
2202                         InventoryList *list = inv_s->getList(s.listname);
2203                         if(list == NULL){
2204                                 verbosestream<<"InventoryMenu: The selected inventory list \""
2205                                                 <<s.listname<<"\" does not exist"<<std::endl;
2206                                 s.i = -1;  // make it invalid again
2207                                 break;
2208                         }
2209
2210                         if((u32)s.i >= list->getSize()){
2211                                 infostream<<"InventoryMenu: The selected inventory list \""
2212                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
2213                                                 <<list->getSize()<<")"<<std::endl;
2214                                 s.i = -1;  // make it invalid again
2215                                 break;
2216                         }
2217
2218                         s_count = list->getItem(s.i).count;
2219                 }while(0);
2220
2221                 bool identical = (m_selected_item != NULL) && s.isValid() &&
2222                         (inv_selected == inv_s) &&
2223                         (m_selected_item->listname == s.listname) &&
2224                         (m_selected_item->i == s.i);
2225
2226                 // buttons: 0 = left, 1 = right, 2 = middle
2227                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
2228                 int button = 0;
2229                 int updown = 2;
2230                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
2231                         { button = 0; updown = 0; }
2232                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
2233                         { button = 1; updown = 0; }
2234                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
2235                         { button = 2; updown = 0; }
2236                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
2237                         { button = 0; updown = 1; }
2238                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
2239                         { button = 1; updown = 1; }
2240                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
2241                         { button = 2; updown = 1; }
2242
2243                 // Set this number to a positive value to generate a move action
2244                 // from m_selected_item to s.
2245                 u32 move_amount = 0;
2246
2247                 // Set this number to a positive value to generate a drop action
2248                 // from m_selected_item.
2249                 u32 drop_amount = 0;
2250
2251                 // Set this number to a positive value to generate a craft action at s.
2252                 u32 craft_amount = 0;
2253
2254                 if(updown == 0)
2255                 {
2256                         // Some mouse button has been pressed
2257
2258                         //infostream<<"Mouse button "<<button<<" pressed at p=("
2259                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
2260
2261                         m_selected_dragging = false;
2262
2263                         if(s.isValid() && s.listname == "craftpreview")
2264                         {
2265                                 // Craft preview has been clicked: craft
2266                                 craft_amount = (button == 2 ? 10 : 1);
2267                         }
2268                         else if(m_selected_item == NULL)
2269                         {
2270                                 if(s_count != 0)
2271                                 {
2272                                         // Non-empty stack has been clicked: select it
2273                                         m_selected_item = new ItemSpec(s);
2274
2275                                         if(button == 1)  // right
2276                                                 m_selected_amount = (s_count + 1) / 2;
2277                                         else if(button == 2)  // middle
2278                                                 m_selected_amount = MYMIN(s_count, 10);
2279                                         else  // left
2280                                                 m_selected_amount = s_count;
2281
2282                                         m_selected_dragging = true;
2283                                 }
2284                         }
2285                         else  // m_selected_item != NULL
2286                         {
2287                                 assert(m_selected_amount >= 1);
2288
2289                                 if(s.isValid())
2290                                 {
2291                                         // Clicked a slot: move
2292                                         if(button == 1)  // right
2293                                                 move_amount = 1;
2294                                         else if(button == 2)  // middle
2295                                                 move_amount = MYMIN(m_selected_amount, 10);
2296                                         else  // left
2297                                                 move_amount = m_selected_amount;
2298
2299                                         if(identical)
2300                                         {
2301                                                 if(move_amount >= m_selected_amount)
2302                                                         m_selected_amount = 0;
2303                                                 else
2304                                                         m_selected_amount -= move_amount;
2305                                                 move_amount = 0;
2306                                         }
2307                                 }
2308                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
2309                                 {
2310                                         // Clicked somewhere else: deselect
2311                                         m_selected_amount = 0;
2312                                 }
2313                                 else
2314                                 {
2315                                         // Clicked outside of the window: drop
2316                                         if(button == 1)  // right
2317                                                 drop_amount = 1;
2318                                         else if(button == 2)  // middle
2319                                                 drop_amount = MYMIN(m_selected_amount, 10);
2320                                         else  // left
2321                                                 drop_amount = m_selected_amount;
2322                                 }
2323                         }
2324                 }
2325                 else if(updown == 1)
2326                 {
2327                         // Some mouse button has been released
2328
2329                         //infostream<<"Mouse button "<<button<<" released at p=("
2330                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
2331
2332                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
2333                         {
2334                                 if(!identical)
2335                                 {
2336                                         // Dragged to different slot: move all selected
2337                                         move_amount = m_selected_amount;
2338                                 }
2339                         }
2340                         else if(m_selected_item != NULL && m_selected_dragging &&
2341                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
2342                         {
2343                                 // Dragged outside of window: drop all selected
2344                                 drop_amount = m_selected_amount;
2345                         }
2346
2347                         m_selected_dragging = false;
2348                 }
2349
2350                 // Possibly send inventory action to server
2351                 if(move_amount > 0)
2352                 {
2353                         // Send IACTION_MOVE
2354
2355                         assert(m_selected_item && m_selected_item->isValid());
2356                         assert(s.isValid());
2357
2358                         assert(inv_selected && inv_s);
2359                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
2360                         InventoryList *list_to = inv_s->getList(s.listname);
2361                         assert(list_from && list_to);
2362                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
2363                         ItemStack stack_to = list_to->getItem(s.i);
2364
2365                         // Check how many items can be moved
2366                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
2367                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
2368                         // If source stack cannot be added to destination stack at all,
2369                         // they are swapped
2370                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
2371                         {
2372                                 m_selected_amount = stack_to.count;
2373                                 // In case the server doesn't directly swap them but instead
2374                                 // moves stack_to somewhere else, set this
2375                                 m_selected_content_guess = stack_to;
2376                                 m_selected_content_guess_inventory = s.inventoryloc;
2377                         }
2378                         // Source stack goes fully into destination stack
2379                         else if(leftover.empty())
2380                         {
2381                                 m_selected_amount -= move_amount;
2382                                 m_selected_content_guess = ItemStack(); // Clear
2383                         }
2384                         // Source stack goes partly into destination stack
2385                         else
2386                         {
2387                                 move_amount -= leftover.count;
2388                                 m_selected_amount -= move_amount;
2389                                 m_selected_content_guess = ItemStack(); // Clear
2390                         }
2391
2392                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
2393                         IMoveAction *a = new IMoveAction();
2394                         a->count = move_amount;
2395                         a->from_inv = m_selected_item->inventoryloc;
2396                         a->from_list = m_selected_item->listname;
2397                         a->from_i = m_selected_item->i;
2398                         a->to_inv = s.inventoryloc;
2399                         a->to_list = s.listname;
2400                         a->to_i = s.i;
2401                         m_invmgr->inventoryAction(a);
2402                 }
2403                 else if(drop_amount > 0)
2404                 {
2405                         m_selected_content_guess = ItemStack(); // Clear
2406
2407                         // Send IACTION_DROP
2408
2409                         assert(m_selected_item && m_selected_item->isValid());
2410                         assert(inv_selected);
2411                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
2412                         assert(list_from);
2413                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
2414
2415                         // Check how many items can be dropped
2416                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
2417                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
2418                         m_selected_amount -= drop_amount;
2419
2420                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
2421                         IDropAction *a = new IDropAction();
2422                         a->count = drop_amount;
2423                         a->from_inv = m_selected_item->inventoryloc;
2424                         a->from_list = m_selected_item->listname;
2425                         a->from_i = m_selected_item->i;
2426                         m_invmgr->inventoryAction(a);
2427                 }
2428                 else if(craft_amount > 0)
2429                 {
2430                         m_selected_content_guess = ItemStack(); // Clear
2431
2432                         // Send IACTION_CRAFT
2433
2434                         assert(s.isValid());
2435                         assert(inv_s);
2436
2437                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
2438                         ICraftAction *a = new ICraftAction();
2439                         a->count = craft_amount;
2440                         a->craft_inv = s.inventoryloc;
2441                         m_invmgr->inventoryAction(a);
2442                 }
2443
2444                 // If m_selected_amount has been decreased to zero, deselect
2445                 if(m_selected_amount == 0)
2446                 {
2447                         delete m_selected_item;
2448                         m_selected_item = NULL;
2449                         m_selected_amount = 0;
2450                         m_selected_dragging = false;
2451                         m_selected_content_guess = ItemStack();
2452                 }
2453         }
2454         if(event.EventType==EET_GUI_EVENT)
2455         {
2456
2457                 if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
2458                                                 && isVisible())
2459                 {
2460                         // find the element that was clicked
2461                         for(u32 i=0; i<m_fields.size(); i++)
2462                         {
2463                                 FieldSpec &s = m_fields[i];
2464                                 // if its a button, set the send field so
2465                                 // lua knows which button was pressed
2466                                 if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID()))
2467                                 {
2468                                         s.send = true;
2469                                         acceptInput();
2470                                         s.send = false;
2471                                         // Restore focus to the full form
2472                                         Environment->setFocus(this);
2473                                         return true;
2474                                 }
2475                         }
2476                 }
2477                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
2478                                 && isVisible())
2479                 {
2480                         if(!canTakeFocus(event.GUIEvent.Element))
2481                         {
2482                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
2483                                                 <<std::endl;
2484                                 // Returning true disables focus change
2485                                 return true;
2486                         }
2487                 }
2488                 if((event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) ||
2489                                 (event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED))
2490                 {
2491                         unsigned int btn_id = event.GUIEvent.Caller->getID();
2492
2493                         if (btn_id == 257) {
2494                                 acceptInput();
2495                                 if (m_allowclose)
2496                                         quitMenu();
2497                                 else
2498                                         m_text_dst->gotText(narrow_to_wide("ExitButton"));
2499                                 // quitMenu deallocates menu
2500                                 return true;
2501                         }
2502
2503                         // find the element that was clicked
2504                         for(u32 i=0; i<m_fields.size(); i++)
2505                         {
2506                                 FieldSpec &s = m_fields[i];
2507                                 // if its a button, set the send field so 
2508                                 // lua knows which button was pressed
2509                                 if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
2510                                                 (s.fid == event.GUIEvent.Caller->getID()))
2511                                 {
2512                                         s.send = true;
2513                                         acceptInput();
2514                                         if(s.is_exit){
2515                                                 if (m_allowclose)
2516                                                         quitMenu();
2517                                                 else
2518                                                         m_text_dst->gotText(narrow_to_wide("ExitButton"));
2519                                                 return true;
2520                                         }else{
2521                                                 s.send = false;
2522                                                 // Restore focus to the full form
2523                                                 Environment->setFocus(this);
2524                                                 return true;
2525                                         }
2526                                 }
2527                         }
2528                 }
2529                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
2530                 {
2531                         if(event.GUIEvent.Caller->getID() > 257)
2532                         {
2533
2534                                 if (m_allowclose) {
2535                                         acceptInput();
2536                                         quitMenu();
2537                                 }
2538                                 else {
2539                                         current_keys_pending.key_enter = true;
2540                                         acceptInput();
2541                                 }
2542                                 // quitMenu deallocates menu
2543                                 return true;
2544                         }
2545                 }
2546
2547                 if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) ||
2548                         (event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED))
2549                 {
2550                         int current_id = event.GUIEvent.Caller->getID();
2551                         if(current_id > 257)
2552                         {
2553                                 // find the element that was clicked
2554                                 for(u32 i=0; i<m_fields.size(); i++)
2555                                 {
2556                                         FieldSpec &s = m_fields[i];
2557                                         // if its a button, set the send field so
2558                                         // lua knows which button was pressed
2559                                         if ((s.ftype == f_ListBox) && (s.fid == current_id))
2560                                         {
2561                                                 s.send = true;
2562                                                 acceptInput(event.GUIEvent.EventType);
2563                                                 s.send=false;
2564                                                 // Restore focus to the full form
2565                                                 Environment->setFocus(this);
2566                                         }
2567                                 }
2568                                 return true;
2569                         }
2570                 }
2571         }
2572
2573         return Parent ? Parent->OnEvent(event) : false;
2574 }
2575
2576 bool GUIFormSpecMenu::parseColor(std::string color, irr::video::SColor& outcolor) {
2577         outcolor = irr::video::SColor(0,0,0,0);
2578
2579         if (!string_allowed(color, "0123456789abcdefABCDEF"))
2580                 return false;
2581
2582         u32 color_value;
2583         std::istringstream iss(color);
2584         iss >> std::hex >> color_value;
2585         
2586         outcolor = irr::video::SColor(color_value);
2587
2588         outcolor.setAlpha(255);
2589         return true;
2590 }