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