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