]> git.lizzy.rs Git - dragonfireclient.git/blob - src/guiFormSpecMenu.cpp
1a99d696be3d0a02b246daae8454f875a8c83f3b
[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],',',true);
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
1356         //some prechecks
1357         if (element == "")
1358                 return;
1359
1360         std::vector<std::string> parts = split(element,'[', true);
1361
1362         // ugly workaround to keep compatibility
1363         if (parts.size() > 2) {
1364                 if (trim(parts[0]) == "image") {
1365                         for (unsigned int i=2;i< parts.size(); i++) {
1366                                 parts[1] += "[" + parts[i];
1367                         }
1368                 }
1369                 else { return; }
1370         }
1371
1372         if (parts.size() < 2) {
1373                 return;
1374         }
1375
1376         std::string type = trim(parts[0]);
1377         std::string description = trim(parts[1]);
1378
1379         if ((type == "size") || (type == "invsize")){
1380                 parseSize(data,description);
1381                 return;
1382         }
1383
1384         if (type == "list") {
1385                 parseList(data,description);
1386                 return;
1387         }
1388
1389         if (type == "checkbox") {
1390                 parseCheckbox(data,description);
1391                 return;
1392         }
1393
1394         if (type == "image") {
1395                 parseImage(data,description);
1396                 return;
1397         }
1398
1399         if (type == "item_image") {
1400                 parseItemImage(data,description);
1401                 return;
1402         }
1403
1404         if ((type == "button") || (type == "button_exit")) {
1405                 parseButton(data,description,type);
1406                 return;
1407         }
1408
1409         if (type == "background") {
1410                 parseBackground(data,description);
1411                 return;
1412         }
1413
1414         if (type == "textlist"){
1415                 parseTextList(data,description);
1416                 return;
1417         }
1418
1419         if (type == "dropdown"){
1420                 parseDropDown(data,description);
1421                 return;
1422         }
1423
1424         if (type == "pwdfield") {
1425                 parsePwdField(data,description);
1426                 return;
1427         }
1428
1429         if ((type == "field") || (type == "textarea")){
1430                 parseField(data,description,type);
1431                 return;
1432         }
1433
1434         if (type == "label") {
1435                 parseLabel(data,description);
1436                 return;
1437         }
1438
1439         if (type == "vertlabel") {
1440                 parseVertLabel(data,description);
1441                 return;
1442         }
1443
1444         if (type == "item_image_button") {
1445                 parseItemImageButton(data,description);
1446                 return;
1447         }
1448
1449         if ((type == "image_button") || (type == "image_button_exit")) {
1450                 parseImageButton(data,description,type);
1451                 return;
1452         }
1453
1454         if (type == "tabheader") {
1455                 parseTabHeader(data,description);
1456                 return;
1457         }
1458
1459         if (type == "box") {
1460                 parseBox(data,description);
1461                 return;
1462         }
1463
1464         // Ignore others
1465         infostream
1466                 << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
1467                 <<std::endl;
1468 }
1469
1470
1471
1472 void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
1473 {
1474         parserData mydata;
1475
1476         //preserve listboxes
1477         for (unsigned int i = 0; i < m_listboxes.size(); i++) {
1478                 int selection = m_listboxes[i].second->getSelected();
1479                 if (selection != -1) {
1480                         std::wstring listboxname = m_listboxes[i].first.fname;
1481                         mydata.listbox_selections[listboxname] = selection;
1482                 }
1483         }
1484
1485         // Remove children
1486         removeChildren();
1487
1488         mydata.size= v2s32(100,100);
1489         mydata.helptext_h = 15;
1490         mydata.screensize = screensize;
1491
1492         // Base position of contents of form
1493         mydata.basepos = getBasePos();
1494
1495         // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
1496         // Used to adjust form size automatically if needed
1497         // A proceed button is added if there is no size[] element
1498         mydata.bp_set = 0;
1499
1500         
1501         /* Convert m_init_draw_spec to m_inventorylists */
1502         
1503         m_inventorylists.clear();
1504         m_images.clear();
1505         m_backgrounds.clear();
1506         m_itemimages.clear();
1507         m_listboxes.clear();
1508         m_checkboxes.clear();
1509         m_fields.clear();
1510         m_boxes.clear();
1511
1512
1513         std::vector<std::string> elements = split(m_formspec_string,']',true);
1514
1515         for (unsigned int i=0;i< elements.size();i++) {
1516                 parseElement(&mydata,elements[i]);
1517         }
1518
1519         // If there's inventory, put the usage string at the bottom
1520         if (m_inventorylists.size())
1521         {
1522                 changeCtype("");
1523                 core::rect<s32> rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h);
1524                 rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5,
1525                                 mydata.size.Y-5-mydata.helptext_h);
1526                 const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
1527                 Environment->addStaticText(text, rect, false, true, this, 256);
1528                 delete[] text;
1529                 changeCtype("C");
1530         }
1531         // If there's fields, add a Proceed button
1532         if (m_fields.size() && mydata.bp_set != 2)
1533         {
1534                 // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
1535                 mydata.rect = core::rect<s32>(
1536                                 mydata.screensize.X/2 - 580/2,
1537                                 mydata.screensize.Y/2 - 300/2,
1538                                 mydata.screensize.X/2 + 580/2,
1539                                 mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
1540                 );
1541                 DesiredRect = mydata.rect;
1542                 recalculateAbsolutePosition(false);
1543                 mydata.basepos = getBasePos();
1544
1545                 changeCtype("");
1546                 {
1547                         v2s32 pos = mydata.basepos;
1548                         pos.Y = ((m_fields.size()+2)*60);
1549
1550                         v2s32 size = DesiredRect.getSize();
1551                         mydata.rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
1552                         wchar_t* text = wgettext("Proceed");
1553                         Environment->addButton(mydata.rect, this, 257, text);
1554                         delete[] text;
1555                 }
1556                 changeCtype("C");
1557         }
1558         // Add tooltip
1559         {
1560                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
1561                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
1562                 m_tooltip_element->enableOverrideColor(true);
1563                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
1564                 m_tooltip_element->setDrawBackground(true);
1565                 m_tooltip_element->setDrawBorder(true);
1566                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
1567                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
1568                 m_tooltip_element->setWordWrap(false);
1569         }
1570 }
1571
1572 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
1573 {
1574         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1575         
1576         for(u32 i=0; i<m_inventorylists.size(); i++)
1577         {
1578                 const ListDrawSpec &s = m_inventorylists[i];
1579
1580                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
1581                 {
1582                         s32 item_i = i + s.start_item_i;
1583                         s32 x = (i%s.geom.X) * spacing.X;
1584                         s32 y = (i/s.geom.X) * spacing.Y;
1585                         v2s32 p0(x,y);
1586                         core::rect<s32> rect = imgrect + s.pos + p0;
1587                         if(rect.isPointInside(p))
1588                         {
1589                                 return ItemSpec(s.inventoryloc, s.listname, item_i);
1590                         }
1591                 }
1592         }
1593
1594         return ItemSpec(InventoryLocation(), "", -1);
1595 }
1596
1597 void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
1598 {
1599         video::IVideoDriver* driver = Environment->getVideoDriver();
1600
1601         // Get font
1602         gui::IGUIFont *font = NULL;
1603         gui::IGUISkin* skin = Environment->getSkin();
1604         if (skin)
1605                 font = skin->getFont();
1606         
1607         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1608         if(!inv){
1609                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
1610                                 <<"The inventory location "
1611                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1612                                 <<std::endl;
1613                 return;
1614         }
1615         InventoryList *ilist = inv->getList(s.listname);
1616         if(!ilist){
1617                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
1618                                 <<"The inventory list \""<<s.listname<<"\" @ \""
1619                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
1620                                 <<std::endl;
1621                 return;
1622         }
1623         
1624         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1625         
1626         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
1627         {
1628                 s32 item_i = i + s.start_item_i;
1629                 if(item_i >= (s32) ilist->getSize())
1630                         break;
1631                 s32 x = (i%s.geom.X) * spacing.X;
1632                 s32 y = (i/s.geom.X) * spacing.Y;
1633                 v2s32 p(x,y);
1634                 core::rect<s32> rect = imgrect + s.pos + p;
1635                 ItemStack item;
1636                 if(ilist)
1637                         item = ilist->getItem(item_i);
1638
1639                 bool selected = m_selected_item
1640                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
1641                         && m_selected_item->listname == s.listname
1642                         && m_selected_item->i == item_i;
1643                 bool hovering = rect.isPointInside(m_pointer);
1644
1645                 if(phase == 0)
1646                 {
1647                         if(hovering && m_selected_item)
1648                         {
1649                                 video::SColor bgcolor(255,192,192,192);
1650                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
1651                         }
1652                         else
1653                         {
1654                                 video::SColor bgcolor(255,128,128,128);
1655                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
1656                         }
1657                 }
1658
1659                 if(phase == 1)
1660                 {
1661                         // Draw item stack
1662                         if(selected)
1663                         {
1664                                 item.takeItem(m_selected_amount);
1665                         }
1666                         if(!item.empty())
1667                         {
1668                                 drawItemStack(driver, font, item,
1669                                                 rect, &AbsoluteClippingRect, m_gamedef);
1670                         }
1671
1672                         // Draw tooltip
1673                         std::string tooltip_text = "";
1674                         if(hovering && !m_selected_item)
1675                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
1676                         if(tooltip_text != "")
1677                         {
1678                                 m_tooltip_element->setVisible(true);
1679                                 this->bringToFront(m_tooltip_element);
1680                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
1681                                 s32 tooltip_x = m_pointer.X + 15;
1682                                 s32 tooltip_y = m_pointer.Y + 15;
1683                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
1684                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
1685                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
1686                                                 core::position2d<s32>(tooltip_x, tooltip_y),
1687                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
1688                         }
1689                 }
1690         }
1691 }
1692
1693 void GUIFormSpecMenu::drawSelectedItem()
1694 {
1695         if(!m_selected_item)
1696                 return;
1697
1698         video::IVideoDriver* driver = Environment->getVideoDriver();
1699
1700         // Get font
1701         gui::IGUIFont *font = NULL;
1702         gui::IGUISkin* skin = Environment->getSkin();
1703         if (skin)
1704                 font = skin->getFont();
1705         
1706         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1707         assert(inv);
1708         InventoryList *list = inv->getList(m_selected_item->listname);
1709         assert(list);
1710         ItemStack stack = list->getItem(m_selected_item->i);
1711         stack.count = m_selected_amount;
1712
1713         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
1714         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
1715         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
1716 }
1717
1718 void GUIFormSpecMenu::drawMenu()
1719 {
1720         if(m_form_src){
1721                 std::string newform = m_form_src->getForm();
1722                 if(newform != m_formspec_string){
1723                         m_formspec_string = newform;
1724                         regenerateGui(m_screensize_old);
1725                 }
1726         }
1727
1728         m_pointer = m_device->getCursorControl()->getPosition();
1729
1730         updateSelectedItem();
1731
1732         gui::IGUISkin* skin = Environment->getSkin();
1733         if (!skin)
1734                 return;
1735         video::IVideoDriver* driver = Environment->getVideoDriver();
1736         
1737         video::SColor bgcolor(140,0,0,0);
1738         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
1739
1740         m_tooltip_element->setVisible(false);
1741
1742         /*
1743                 Draw backgrounds
1744         */
1745         for(u32 i=0; i<m_backgrounds.size(); i++)
1746         {
1747                 const ImageDrawSpec &spec = m_backgrounds[i];
1748                 video::ITexture *texture = 0;
1749
1750                 if (m_gamedef != 0)
1751                         texture = m_gamedef->tsrc()->getTexture(spec.name);
1752                 else
1753                 {
1754                         texture = driver->getTexture(spec.name.c_str());
1755                         m_Textures.push_back(texture);
1756                 }
1757
1758                 if (texture != 0) {
1759                         // Image size on screen
1760                         core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
1761                         // Image rectangle on screen
1762                         core::rect<s32> rect = imgrect + spec.pos;
1763                         const video::SColor color(255,255,255,255);
1764                         const video::SColor colors[] = {color,color,color,color};
1765                         driver->draw2DImage(texture, rect,
1766                                 core::rect<s32>(core::position2d<s32>(0,0),
1767                                                 core::dimension2di(texture->getOriginalSize())),
1768                                 NULL/*&AbsoluteClippingRect*/, colors, true);
1769                 }
1770                 else {
1771                         errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
1772                         errorstream << "\t" << spec.name << std::endl;
1773                 }
1774         }
1775         
1776         /*
1777                 Draw Boxes
1778         */
1779         for(u32 i=0; i<m_boxes.size(); i++)
1780         {
1781                 const BoxDrawSpec &spec = m_boxes[i];
1782
1783                 irr::video::SColor todraw = spec.color;
1784
1785                 todraw.setAlpha(140);
1786
1787                 core::rect<s32> rect(spec.pos.X,spec.pos.Y,
1788                                                         spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
1789
1790                 driver->draw2DRectangle(todraw, rect, 0);
1791         }
1792         /*
1793                 Draw images
1794         */
1795         for(u32 i=0; i<m_images.size(); i++)
1796         {
1797                 const ImageDrawSpec &spec = m_images[i];
1798                 video::ITexture *texture = 0;
1799
1800                 if (m_gamedef != 0)
1801                         texture = m_gamedef->tsrc()->getTexture(spec.name);
1802                 else
1803                 {
1804                         texture = driver->getTexture(spec.name.c_str());
1805                         m_Textures.push_back(texture);
1806                 }
1807                 if (texture != 0) {
1808                         const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
1809                         // Image size on screen
1810                         core::rect<s32> imgrect;
1811
1812                         if (spec.scale)
1813                                 imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
1814                         else {
1815
1816                                 imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
1817                         }
1818                         // Image rectangle on screen
1819                         core::rect<s32> rect = imgrect + spec.pos;
1820                         const video::SColor color(255,255,255,255);
1821                         const video::SColor colors[] = {color,color,color,color};
1822                         driver->draw2DImage(texture, rect,
1823                                 core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
1824                                 NULL/*&AbsoluteClippingRect*/, colors, true);
1825                 }
1826                 else {
1827                         errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
1828                         errorstream << "\t" << spec.name << std::endl;
1829                 }
1830         }
1831         
1832         /*
1833                 Draw item images
1834         */
1835         for(u32 i=0; i<m_itemimages.size(); i++)
1836         {
1837                 if (m_gamedef == 0)
1838                         break;
1839
1840                 const ImageDrawSpec &spec = m_itemimages[i];
1841                 IItemDefManager *idef = m_gamedef->idef();
1842                 ItemStack item;
1843                 item.deSerialize(spec.name, idef);
1844                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);         
1845                 // Image size on screen
1846                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
1847                 // Image rectangle on screen
1848                 core::rect<s32> rect = imgrect + spec.pos;
1849                 const video::SColor color(255,255,255,255);
1850                 const video::SColor colors[] = {color,color,color,color};
1851                 driver->draw2DImage(texture, rect,
1852                         core::rect<s32>(core::position2d<s32>(0,0),
1853                                         core::dimension2di(texture->getOriginalSize())),
1854                         NULL/*&AbsoluteClippingRect*/, colors, true);
1855         }
1856         
1857         /*
1858                 Draw items
1859                 Phase 0: Item slot rectangles
1860                 Phase 1: Item images; prepare tooltip
1861                 If backgrounds used, do not draw Item slot rectangles
1862         */
1863         int start_phase=0;
1864         if (m_backgrounds.size() > 0) start_phase=1;
1865         for(int phase=start_phase; phase<=1; phase++)
1866         for(u32 i=0; i<m_inventorylists.size(); i++)
1867         {
1868                 drawList(m_inventorylists[i], phase);
1869         }
1870
1871         /*
1872                 Call base class
1873         */
1874         gui::IGUIElement::draw();
1875         
1876         /*
1877                 Draw fields/buttons tooltips
1878         */
1879         for(u32 i=0; i<m_fields.size(); i++)
1880         {
1881                 const FieldSpec &spec = m_fields[i];
1882                 if (spec.tooltip != "")
1883                 {
1884                         core::rect<s32> rect = spec.rect;
1885                         if (rect.isPointInside(m_pointer)) 
1886                         {
1887                                 m_tooltip_element->setVisible(true);
1888                                 this->bringToFront(m_tooltip_element);
1889                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
1890                                 s32 tooltip_x = m_pointer.X + 15;
1891                                 s32 tooltip_y = m_pointer.Y + 15;
1892                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
1893                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
1894                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
1895                                 core::position2d<s32>(tooltip_x, tooltip_y),
1896                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
1897                         }
1898                 }
1899         }
1900         
1901         /*
1902                 Draw dragged item stack
1903         */
1904         drawSelectedItem();
1905 }
1906
1907 void GUIFormSpecMenu::updateSelectedItem()
1908 {
1909         // If the selected stack has become empty for some reason, deselect it.
1910         // If the selected stack has become inaccessible, deselect it.
1911         // If the selected stack has become smaller, adjust m_selected_amount.
1912         ItemStack selected = verifySelectedItem();
1913
1914         // WARNING: BLACK MAGIC
1915         // See if there is a stack suited for our current guess.
1916         // If such stack does not exist, clear the guess.
1917         if(m_selected_content_guess.name != "" &&
1918                         selected.name == m_selected_content_guess.name &&
1919                         selected.count == m_selected_content_guess.count){
1920                 // Selected item fits the guess. Skip the black magic.
1921         }
1922         else if(m_selected_content_guess.name != ""){
1923                 bool found = false;
1924                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
1925                         const ListDrawSpec &s = m_inventorylists[i];
1926                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1927                         if(!inv)
1928                                 continue;
1929                         InventoryList *list = inv->getList(s.listname);
1930                         if(!list)
1931                                 continue;
1932                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
1933                                 u32 item_i = i + s.start_item_i;
1934                                 if(item_i >= list->getSize())
1935                                         continue;
1936                                 ItemStack stack = list->getItem(item_i);
1937                                 if(stack.name == m_selected_content_guess.name &&
1938                                                 stack.count == m_selected_content_guess.count){
1939                                         found = true;
1940                                         infostream<<"Client: Changing selected content guess to "
1941                                                         <<s.inventoryloc.dump()<<" "<<s.listname
1942                                                         <<" "<<item_i<<std::endl;
1943                                         delete m_selected_item;
1944                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
1945                                         m_selected_amount = stack.count;
1946                                 }
1947                         }
1948                 }
1949                 if(!found){
1950                         infostream<<"Client: Discarding selected content guess: "
1951                                         <<m_selected_content_guess.getItemString()<<std::endl;
1952                         m_selected_content_guess.name = "";
1953                 }
1954         }
1955
1956         // If craftresult is nonempty and nothing else is selected, select it now.
1957         if(!m_selected_item)
1958         {
1959                 for(u32 i=0; i<m_inventorylists.size(); i++)
1960                 {
1961                         const ListDrawSpec &s = m_inventorylists[i];
1962                         if(s.listname == "craftpreview")
1963                         {
1964                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
1965                                 InventoryList *list = inv->getList("craftresult");
1966                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
1967                                 {
1968                                         m_selected_item = new ItemSpec;
1969                                         m_selected_item->inventoryloc = s.inventoryloc;
1970                                         m_selected_item->listname = "craftresult";
1971                                         m_selected_item->i = 0;
1972                                         m_selected_amount = 0;
1973                                         m_selected_dragging = false;
1974                                         break;
1975                                 }
1976                         }
1977                 }
1978         }
1979
1980         // If craftresult is selected, keep the whole stack selected
1981         if(m_selected_item && m_selected_item->listname == "craftresult")
1982         {
1983                 m_selected_amount = verifySelectedItem().count;
1984         }
1985 }
1986
1987 ItemStack GUIFormSpecMenu::verifySelectedItem()
1988 {
1989         // If the selected stack has become empty for some reason, deselect it.
1990         // If the selected stack has become inaccessible, deselect it.
1991         // If the selected stack has become smaller, adjust m_selected_amount.
1992         // Return the selected stack.
1993
1994         if(m_selected_item)
1995         {
1996                 if(m_selected_item->isValid())
1997                 {
1998                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1999                         if(inv)
2000                         {
2001                                 InventoryList *list = inv->getList(m_selected_item->listname);
2002                                 if(list && (u32) m_selected_item->i < list->getSize())
2003                                 {
2004                                         ItemStack stack = list->getItem(m_selected_item->i);
2005                                         if(m_selected_amount > stack.count)
2006                                                 m_selected_amount = stack.count;
2007                                         if(!stack.empty())
2008                                                 return stack;
2009                                 }
2010                         }
2011                 }
2012
2013                 // selection was not valid
2014                 delete m_selected_item;
2015                 m_selected_item = NULL;
2016                 m_selected_amount = 0;
2017                 m_selected_dragging = false;
2018         }
2019         return ItemStack();
2020 }
2021
2022 void GUIFormSpecMenu::acceptInput(int eventtype)
2023 {
2024         if(m_text_dst)
2025         {
2026                 std::map<std::string, std::string> fields;
2027
2028                 if (current_keys_pending.key_down) {
2029                         fields["key_down"] = "true";
2030                         current_keys_pending.key_down = false;
2031                 }
2032
2033                 if (current_keys_pending.key_up) {
2034                         fields["key_up"] = "true";
2035                         current_keys_pending.key_up = false;
2036                 }
2037
2038                 if (current_keys_pending.key_enter) {
2039                         fields["key_enter"] = "true";
2040                         current_keys_pending.key_enter = false;
2041                 }
2042
2043                 if (current_keys_pending.key_escape) {
2044                         fields["key_escape"] = "true";
2045                         current_keys_pending.key_escape = false;
2046                 }
2047
2048                 for(u32 i=0; i<m_fields.size(); i++)
2049                 {
2050                         const FieldSpec &s = m_fields[i];
2051                         if(s.send) 
2052                         {
2053                                 if(s.ftype == f_Button)
2054                                 {
2055                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
2056                                 }
2057                                 else if(s.ftype == f_ListBox) {
2058                                         std::stringstream ss;
2059                                         if (eventtype == gui::EGET_LISTBOX_CHANGED) {
2060                                                 ss << "CHG:";
2061                                         }
2062                                         else {
2063                                                 ss << "DCL:";
2064                                         }
2065                                         ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1);
2066                                         fields[wide_to_narrow(s.fname.c_str())] = ss.str();
2067                                 }
2068                                 else if(s.ftype == f_DropDown) {
2069                                         // no dynamic cast possible due to some distributions shipped
2070                                         // without rtti support in irrlicht
2071                                         IGUIElement * element = getElementFromId(s.fid);
2072                                         gui::IGUIComboBox *e = NULL;
2073                                         if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
2074                                                 e = static_cast<gui::IGUIComboBox*>(element);
2075                                         }
2076                                         fields[wide_to_narrow(s.fname.c_str())] =
2077                                                         wide_to_narrow(e->getItem(e->getSelected()));
2078                                 }
2079                                 else if (s.ftype == f_TabHeader) {
2080                                         // no dynamic cast possible due to some distributions shipped
2081                                         // without rtti support in irrlicht
2082                                         IGUIElement * element = getElementFromId(s.fid);
2083                                         gui::IGUITabControl *e = NULL;
2084                                         if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
2085                                                 e = static_cast<gui::IGUITabControl*>(element);
2086                                         }
2087
2088                                         if (e != 0) {
2089                                                 std::stringstream ss;
2090                                                 ss << (e->getActiveTab() +1);
2091                                                 fields[wide_to_narrow(s.fname.c_str())] = ss.str();
2092                                         }
2093                                 }
2094                                 else if (s.ftype == f_CheckBox) {
2095                                         // no dynamic cast possible due to some distributions shipped
2096                                         // without rtti support in irrlicht
2097                                         IGUIElement * element = getElementFromId(s.fid);
2098                                         gui::IGUICheckBox *e = NULL;
2099                                         if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
2100                                                 e = static_cast<gui::IGUICheckBox*>(element);
2101                                         }
2102
2103                                         if (e != 0) {
2104                                                 if (e->isChecked())
2105                                                         fields[wide_to_narrow(s.fname.c_str())] = "true";
2106                                                 else
2107                                                         fields[wide_to_narrow(s.fname.c_str())] = "false";
2108                                         }
2109                                 }
2110                                 else
2111                                 {
2112                                         IGUIElement* e = getElementFromId(s.fid);
2113                                         if(e != NULL)
2114                                         {
2115                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
2116                                         }
2117                                 }
2118                         }
2119                 }
2120
2121                 m_text_dst->gotText(fields);
2122         }
2123 }
2124
2125 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
2126 {
2127         if(event.EventType==EET_KEY_INPUT_EVENT)
2128         {
2129                 KeyPress kp(event.KeyInput);
2130                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
2131                         kp == getKeySetting("keymap_inventory")))
2132                 {
2133                         if (m_allowclose)
2134                                 quitMenu();
2135                         else
2136                                 m_text_dst->gotText(narrow_to_wide("MenuQuit"));
2137                         return true;
2138                 }
2139                 if (event.KeyInput.PressedDown &&
2140                         (event.KeyInput.Key==KEY_RETURN ||
2141                          event.KeyInput.Key==KEY_UP ||
2142                          event.KeyInput.Key==KEY_DOWN)
2143                         ) {
2144
2145
2146                         switch (event.KeyInput.Key) {
2147                                 case KEY_RETURN:
2148                                         if (m_allowclose) {
2149                                                 acceptInput();
2150                                                 quitMenu();
2151                                         }
2152                                         else
2153                                                 current_keys_pending.key_enter = true;
2154                                         break;
2155                                 case KEY_UP:
2156                                         current_keys_pending.key_up = true;
2157                                         break;
2158                                 case KEY_DOWN:
2159                                         current_keys_pending.key_down = true;
2160                                         break;
2161                                 break;
2162                                 default:
2163                                         //can't happen at all!
2164                                         assert("reached a source line that can't ever been reached" == 0);
2165                                         break;
2166                         }
2167                         acceptInput();
2168                         return true;
2169                 }
2170
2171         }
2172         if(event.EventType==EET_MOUSE_INPUT_EVENT
2173                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
2174         {
2175                 // Mouse event other than movement
2176
2177                 // Get selected item and hovered/clicked item (s)
2178
2179                 updateSelectedItem();
2180                 ItemSpec s = getItemAtPos(m_pointer);
2181
2182                 Inventory *inv_selected = NULL;
2183                 Inventory *inv_s = NULL;
2184
2185                 if(m_selected_item)
2186                 {
2187                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
2188                         assert(inv_selected);
2189                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
2190                 }
2191
2192                 u32 s_count = 0;
2193
2194                 if(s.isValid())
2195                 do{ // breakable
2196                         inv_s = m_invmgr->getInventory(s.inventoryloc);
2197
2198                         if(!inv_s){
2199                                 errorstream<<"InventoryMenu: The selected inventory location "
2200                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
2201                                                 <<std::endl;
2202                                 s.i = -1;  // make it invalid again
2203                                 break;
2204                         }
2205
2206                         InventoryList *list = inv_s->getList(s.listname);
2207                         if(list == NULL){
2208                                 verbosestream<<"InventoryMenu: The selected inventory list \""
2209                                                 <<s.listname<<"\" does not exist"<<std::endl;
2210                                 s.i = -1;  // make it invalid again
2211                                 break;
2212                         }
2213
2214                         if((u32)s.i >= list->getSize()){
2215                                 infostream<<"InventoryMenu: The selected inventory list \""
2216                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
2217                                                 <<list->getSize()<<")"<<std::endl;
2218                                 s.i = -1;  // make it invalid again
2219                                 break;
2220                         }
2221
2222                         s_count = list->getItem(s.i).count;
2223                 }while(0);
2224
2225                 bool identical = (m_selected_item != NULL) && s.isValid() &&
2226                         (inv_selected == inv_s) &&
2227                         (m_selected_item->listname == s.listname) &&
2228                         (m_selected_item->i == s.i);
2229
2230                 // buttons: 0 = left, 1 = right, 2 = middle
2231                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
2232                 int button = 0;
2233                 int updown = 2;
2234                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
2235                         { button = 0; updown = 0; }
2236                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
2237                         { button = 1; updown = 0; }
2238                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
2239                         { button = 2; updown = 0; }
2240                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
2241                         { button = 0; updown = 1; }
2242                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
2243                         { button = 1; updown = 1; }
2244                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
2245                         { button = 2; updown = 1; }
2246
2247                 // Set this number to a positive value to generate a move action
2248                 // from m_selected_item to s.
2249                 u32 move_amount = 0;
2250
2251                 // Set this number to a positive value to generate a drop action
2252                 // from m_selected_item.
2253                 u32 drop_amount = 0;
2254
2255                 // Set this number to a positive value to generate a craft action at s.
2256                 u32 craft_amount = 0;
2257
2258                 if(updown == 0)
2259                 {
2260                         // Some mouse button has been pressed
2261
2262                         //infostream<<"Mouse button "<<button<<" pressed at p=("
2263                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
2264
2265                         m_selected_dragging = false;
2266
2267                         if(s.isValid() && s.listname == "craftpreview")
2268                         {
2269                                 // Craft preview has been clicked: craft
2270                                 craft_amount = (button == 2 ? 10 : 1);
2271                         }
2272                         else if(m_selected_item == NULL)
2273                         {
2274                                 if(s_count != 0)
2275                                 {
2276                                         // Non-empty stack has been clicked: select it
2277                                         m_selected_item = new ItemSpec(s);
2278
2279                                         if(button == 1)  // right
2280                                                 m_selected_amount = (s_count + 1) / 2;
2281                                         else if(button == 2)  // middle
2282                                                 m_selected_amount = MYMIN(s_count, 10);
2283                                         else  // left
2284                                                 m_selected_amount = s_count;
2285
2286                                         m_selected_dragging = true;
2287                                 }
2288                         }
2289                         else  // m_selected_item != NULL
2290                         {
2291                                 assert(m_selected_amount >= 1);
2292
2293                                 if(s.isValid())
2294                                 {
2295                                         // Clicked a slot: move
2296                                         if(button == 1)  // right
2297                                                 move_amount = 1;
2298                                         else if(button == 2)  // middle
2299                                                 move_amount = MYMIN(m_selected_amount, 10);
2300                                         else  // left
2301                                                 move_amount = m_selected_amount;
2302
2303                                         if(identical)
2304                                         {
2305                                                 if(move_amount >= m_selected_amount)
2306                                                         m_selected_amount = 0;
2307                                                 else
2308                                                         m_selected_amount -= move_amount;
2309                                                 move_amount = 0;
2310                                         }
2311                                 }
2312                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
2313                                 {
2314                                         // Clicked somewhere else: deselect
2315                                         m_selected_amount = 0;
2316                                 }
2317                                 else
2318                                 {
2319                                         // Clicked outside of the window: drop
2320                                         if(button == 1)  // right
2321                                                 drop_amount = 1;
2322                                         else if(button == 2)  // middle
2323                                                 drop_amount = MYMIN(m_selected_amount, 10);
2324                                         else  // left
2325                                                 drop_amount = m_selected_amount;
2326                                 }
2327                         }
2328                 }
2329                 else if(updown == 1)
2330                 {
2331                         // Some mouse button has been released
2332
2333                         //infostream<<"Mouse button "<<button<<" released at p=("
2334                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
2335
2336                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
2337                         {
2338                                 if(!identical)
2339                                 {
2340                                         // Dragged to different slot: move all selected
2341                                         move_amount = m_selected_amount;
2342                                 }
2343                         }
2344                         else if(m_selected_item != NULL && m_selected_dragging &&
2345                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
2346                         {
2347                                 // Dragged outside of window: drop all selected
2348                                 drop_amount = m_selected_amount;
2349                         }
2350
2351                         m_selected_dragging = false;
2352                 }
2353
2354                 // Possibly send inventory action to server
2355                 if(move_amount > 0)
2356                 {
2357                         // Send IACTION_MOVE
2358
2359                         assert(m_selected_item && m_selected_item->isValid());
2360                         assert(s.isValid());
2361
2362                         assert(inv_selected && inv_s);
2363                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
2364                         InventoryList *list_to = inv_s->getList(s.listname);
2365                         assert(list_from && list_to);
2366                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
2367                         ItemStack stack_to = list_to->getItem(s.i);
2368
2369                         // Check how many items can be moved
2370                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
2371                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
2372                         // If source stack cannot be added to destination stack at all,
2373                         // they are swapped
2374                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
2375                         {
2376                                 m_selected_amount = stack_to.count;
2377                                 // In case the server doesn't directly swap them but instead
2378                                 // moves stack_to somewhere else, set this
2379                                 m_selected_content_guess = stack_to;
2380                                 m_selected_content_guess_inventory = s.inventoryloc;
2381                         }
2382                         // Source stack goes fully into destination stack
2383                         else if(leftover.empty())
2384                         {
2385                                 m_selected_amount -= move_amount;
2386                                 m_selected_content_guess = ItemStack(); // Clear
2387                         }
2388                         // Source stack goes partly into destination stack
2389                         else
2390                         {
2391                                 move_amount -= leftover.count;
2392                                 m_selected_amount -= move_amount;
2393                                 m_selected_content_guess = ItemStack(); // Clear
2394                         }
2395
2396                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
2397                         IMoveAction *a = new IMoveAction();
2398                         a->count = move_amount;
2399                         a->from_inv = m_selected_item->inventoryloc;
2400                         a->from_list = m_selected_item->listname;
2401                         a->from_i = m_selected_item->i;
2402                         a->to_inv = s.inventoryloc;
2403                         a->to_list = s.listname;
2404                         a->to_i = s.i;
2405                         m_invmgr->inventoryAction(a);
2406                 }
2407                 else if(drop_amount > 0)
2408                 {
2409                         m_selected_content_guess = ItemStack(); // Clear
2410
2411                         // Send IACTION_DROP
2412
2413                         assert(m_selected_item && m_selected_item->isValid());
2414                         assert(inv_selected);
2415                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
2416                         assert(list_from);
2417                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
2418
2419                         // Check how many items can be dropped
2420                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
2421                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
2422                         m_selected_amount -= drop_amount;
2423
2424                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
2425                         IDropAction *a = new IDropAction();
2426                         a->count = drop_amount;
2427                         a->from_inv = m_selected_item->inventoryloc;
2428                         a->from_list = m_selected_item->listname;
2429                         a->from_i = m_selected_item->i;
2430                         m_invmgr->inventoryAction(a);
2431                 }
2432                 else if(craft_amount > 0)
2433                 {
2434                         m_selected_content_guess = ItemStack(); // Clear
2435
2436                         // Send IACTION_CRAFT
2437
2438                         assert(s.isValid());
2439                         assert(inv_s);
2440
2441                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
2442                         ICraftAction *a = new ICraftAction();
2443                         a->count = craft_amount;
2444                         a->craft_inv = s.inventoryloc;
2445                         m_invmgr->inventoryAction(a);
2446                 }
2447
2448                 // If m_selected_amount has been decreased to zero, deselect
2449                 if(m_selected_amount == 0)
2450                 {
2451                         delete m_selected_item;
2452                         m_selected_item = NULL;
2453                         m_selected_amount = 0;
2454                         m_selected_dragging = false;
2455                         m_selected_content_guess = ItemStack();
2456                 }
2457         }
2458         if(event.EventType==EET_GUI_EVENT)
2459         {
2460
2461                 if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
2462                                                 && isVisible())
2463                 {
2464                         // find the element that was clicked
2465                         for(u32 i=0; i<m_fields.size(); i++)
2466                         {
2467                                 FieldSpec &s = m_fields[i];
2468                                 // if its a button, set the send field so
2469                                 // lua knows which button was pressed
2470                                 if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID()))
2471                                 {
2472                                         s.send = true;
2473                                         acceptInput();
2474                                         s.send = false;
2475                                         // Restore focus to the full form
2476                                         Environment->setFocus(this);
2477                                         return true;
2478                                 }
2479                         }
2480                 }
2481                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
2482                                 && isVisible())
2483                 {
2484                         if(!canTakeFocus(event.GUIEvent.Element))
2485                         {
2486                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
2487                                                 <<std::endl;
2488                                 // Returning true disables focus change
2489                                 return true;
2490                         }
2491                 }
2492                 if((event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) ||
2493                                 (event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED))
2494                 {
2495                         unsigned int btn_id = event.GUIEvent.Caller->getID();
2496
2497                         if (btn_id == 257) {
2498                                 acceptInput();
2499                                 if (m_allowclose)
2500                                         quitMenu();
2501                                 else
2502                                         m_text_dst->gotText(narrow_to_wide("ExitButton"));
2503                                 // quitMenu deallocates menu
2504                                 return true;
2505                         }
2506
2507                         // find the element that was clicked
2508                         for(u32 i=0; i<m_fields.size(); i++)
2509                         {
2510                                 FieldSpec &s = m_fields[i];
2511                                 // if its a button, set the send field so 
2512                                 // lua knows which button was pressed
2513                                 if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
2514                                                 (s.fid == event.GUIEvent.Caller->getID()))
2515                                 {
2516                                         s.send = true;
2517                                         acceptInput();
2518                                         if(s.is_exit){
2519                                                 if (m_allowclose)
2520                                                         quitMenu();
2521                                                 else
2522                                                         m_text_dst->gotText(narrow_to_wide("ExitButton"));
2523                                                 return true;
2524                                         }else{
2525                                                 s.send = false;
2526                                                 // Restore focus to the full form
2527                                                 Environment->setFocus(this);
2528                                                 return true;
2529                                         }
2530                                 }
2531                         }
2532                 }
2533                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
2534                 {
2535                         if(event.GUIEvent.Caller->getID() > 257)
2536                         {
2537
2538                                 if (m_allowclose) {
2539                                         acceptInput();
2540                                         quitMenu();
2541                                 }
2542                                 else {
2543                                         current_keys_pending.key_enter = true;
2544                                         acceptInput();
2545                                 }
2546                                 // quitMenu deallocates menu
2547                                 return true;
2548                         }
2549                 }
2550
2551                 if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) ||
2552                         (event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED))
2553                 {
2554                         int current_id = event.GUIEvent.Caller->getID();
2555                         if(current_id > 257)
2556                         {
2557                                 // find the element that was clicked
2558                                 for(u32 i=0; i<m_fields.size(); i++)
2559                                 {
2560                                         FieldSpec &s = m_fields[i];
2561                                         // if its a button, set the send field so
2562                                         // lua knows which button was pressed
2563                                         if ((s.ftype == f_ListBox) && (s.fid == current_id))
2564                                         {
2565                                                 s.send = true;
2566                                                 acceptInput(event.GUIEvent.EventType);
2567                                                 s.send=false;
2568                                                 // Restore focus to the full form
2569                                                 Environment->setFocus(this);
2570                                         }
2571                                 }
2572                                 return true;
2573                         }
2574                 }
2575         }
2576
2577         return Parent ? Parent->OnEvent(event) : false;
2578 }
2579
2580 bool GUIFormSpecMenu::parseColor(std::string color, irr::video::SColor& outcolor) {
2581         outcolor = irr::video::SColor(0,0,0,0);
2582
2583         if (!string_allowed(color, "0123456789abcdefABCDEF"))
2584                 return false;
2585
2586         u32 color_value;
2587         std::istringstream iss(color);
2588         iss >> std::hex >> color_value;
2589         
2590         outcolor = irr::video::SColor(color_value);
2591
2592         outcolor.setAlpha(255);
2593         return true;
2594 }