LCOV - code coverage report
Current view: top level - src - guiFormSpecMenu.cpp (source / functions) Hit Total Coverage
Test: report Lines: 745 1958 38.0 %
Date: 2015-07-11 18:23:49 Functions: 34 56 60.7 %

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

Generated by: LCOV version 1.11