/* Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include "guiFormSpecMenu.h" #include "constants.h" #include "gamedef.h" #include "keycode.h" #include "strfnd.h" #include #include #include #include #include #include #include #include #include #include "log.h" #include "tile.h" // ITextureSource #include "util/string.h" #include "util/numeric.h" #include "filesys.h" #include "gettext.h" #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ errorstream<< "Invalid pos for element " << a << "specified: \"" \ << parts[b] << "\"" << std::endl; \ return; \ } #define MY_CHECKGEOM(a,b) \ if (v_geom.size() != 2) { \ errorstream<< "Invalid pos for element " << a << "specified: \"" \ << parts[b] << "\"" << std::endl; \ return; \ } void drawItemStack(video::IVideoDriver *driver, gui::IGUIFont *font, const ItemStack &item, const core::rect &rect, const core::rect *clip, IGameDef *gamedef) { if(item.empty()) return; const ItemDefinition &def = item.getDefinition(gamedef->idef()); video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef); // Draw the inventory texture if(texture != NULL) { const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; driver->draw2DImage(texture, rect, core::rect(core::position2d(0,0), core::dimension2di(texture->getOriginalSize())), clip, colors, true); } if(def.type == ITEM_TOOL && item.wear != 0) { // Draw a progressbar float barheight = rect.getHeight()/16; float barpad_x = rect.getWidth()/16; float barpad_y = rect.getHeight()/16; core::rect progressrect( rect.UpperLeftCorner.X + barpad_x, rect.LowerRightCorner.Y - barpad_y - barheight, rect.LowerRightCorner.X - barpad_x, rect.LowerRightCorner.Y - barpad_y); // Shrink progressrect by amount of tool damage float wear = item.wear / 65535.0; int progressmid = wear * progressrect.UpperLeftCorner.X + (1-wear) * progressrect.LowerRightCorner.X; // Compute progressbar color // wear = 0.0: green // wear = 0.5: yellow // wear = 1.0: red video::SColor color(255,255,255,255); int wear_i = MYMIN(floor(wear * 600), 511); wear_i = MYMIN(wear_i + 10, 511); if(wear_i <= 255) color.set(255, wear_i, 255, 0); else color.set(255, 255, 511-wear_i, 0); core::rect progressrect2 = progressrect; progressrect2.LowerRightCorner.X = progressmid; driver->draw2DRectangle(color, progressrect2, clip); color = video::SColor(255,0,0,0); progressrect2 = progressrect; progressrect2.UpperLeftCorner.X = progressmid; driver->draw2DRectangle(color, progressrect2, clip); } if(font != NULL && item.count >= 2) { // Get the item count as a string std::string text = itos(item.count); v2u32 dim = font->getDimension(narrow_to_wide(text).c_str()); v2s32 sdim(dim.X,dim.Y); core::rect rect2( /*rect.UpperLeftCorner, core::dimension2d(rect.getWidth(), 15)*/ rect.LowerRightCorner - sdim, sdim ); video::SColor bgcolor(128,0,0,0); driver->draw2DRectangle(bgcolor, rect2, clip); video::SColor color(255,255,255,255); font->draw(text.c_str(), rect2, color, false, false, clip); } } /* GUIFormSpecMenu */ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, InventoryManager *invmgr, IGameDef *gamedef ): GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), m_device(dev), m_invmgr(invmgr), m_gamedef(gamedef), m_form_src(NULL), m_text_dst(NULL), m_selected_item(NULL), m_selected_amount(0), m_selected_dragging(false), m_tooltip_element(NULL), m_allowclose(true), m_use_gettext(false), m_lock(false) { current_keys_pending.key_down = false; current_keys_pending.key_up = false; current_keys_pending.key_enter = false; } GUIFormSpecMenu::~GUIFormSpecMenu() { removeChildren(); delete m_selected_item; delete m_form_src; delete m_text_dst; } void GUIFormSpecMenu::removeChildren() { const core::list &children = getChildren(); core::list children_copy; for(core::list::ConstIterator i = children.begin(); i != children.end(); i++) { children_copy.push_back(*i); } for(core::list::Iterator i = children_copy.begin(); i != children_copy.end(); i++) { (*i)->remove(); } /*{ gui::IGUIElement *e = getElementFromId(256); if(e != NULL) e->remove(); }*/ if(m_tooltip_element) { m_tooltip_element->remove(); m_tooltip_element = NULL; } } int GUIFormSpecMenu::getListboxIndex(std::string listboxname) { std::wstring wlistboxname = narrow_to_wide(listboxname.c_str()); for(unsigned int i=0; i < m_listboxes.size(); i++) { std::wstring name(m_listboxes[i].first.fname.c_str()); if ( name == wlistboxname) { return m_listboxes[i].second->getSelected(); } } return -1; } std::vector split(const std::string &s, char delim, bool escape=false) { std::vector tokens; if (!escape) { int startpos = 0; size_t nextpos = s.find(delim); while(nextpos != std::string::npos) { std::string toadd = s.substr(startpos,nextpos-startpos); tokens.push_back(toadd); startpos = nextpos+1; nextpos = s.find(delim,nextpos+1); } //push last element tokens.push_back(s.substr(startpos)); } else { std::string current = ""; current += s.c_str()[0]; bool last_was_escape = false; for(unsigned int i=1; i < s.size(); i++) { if (last_was_escape) { current += '\\'; current += s.c_str()[i]; last_was_escape = false; } else { if (s.c_str()[i] == delim) { tokens.push_back(current); current = ""; last_was_escape = false; } else if (s.c_str()[i] == '\\'){ last_was_escape = true; } else { current += s.c_str()[i]; last_was_escape = false; } } } //push last element tokens.push_back(current); } return tokens; } void GUIFormSpecMenu::parseSize(parserData* data,std::string element) { std::vector parts = split(element,','); if (parts.size() == 2) { v2f invsize; if (parts[1].find(';') != std::string::npos) parts[1] = parts[1].substr(0,parts[1].find(';')); invsize.X = stof(parts[0]); invsize.Y = stof(parts[1]); infostream<<"Form size ("<getVideoDriver()->getScreenSize(); v2u32 delta = current_screensize - m_lockscreensize; if (current_screensize.Y > m_lockscreensize.Y) delta.Y /= 2; else delta.Y = 0; if (current_screensize.X > m_lockscreensize.X) delta.X /= 2; else delta.X = 0; offset = v2s32(delta.X,delta.Y); data->screensize = m_lockscreensize; } else { offset = v2s32(0,0); } padding = v2s32(data->screensize.Y/40, data->screensize.Y/40); spacing = v2s32(data->screensize.Y/12, data->screensize.Y/13); imgsize = v2s32(data->screensize.Y/15, data->screensize.Y/15); data->size = v2s32( padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X, padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (data->helptext_h-5) ); data->rect = core::rect( data->screensize.X/2 - data->size.X/2 + offset.X, data->screensize.Y/2 - data->size.Y/2 + offset.Y, data->screensize.X/2 + data->size.X/2 + offset.X, data->screensize.Y/2 + data->size.Y/2 + offset.Y ); DesiredRect = data->rect; recalculateAbsolutePosition(false); data->basepos = getBasePos(); data->bp_set = 2; return; } errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseList(parserData* data,std::string element) { if (m_gamedef == 0) { errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"< parts = split(element,';'); if ((parts.size() == 4) || (parts.size() == 5)) { std::string location = parts[0]; std::string listname = parts[1]; std::vector v_pos = split(parts[2],','); std::vector v_geom = split(parts[3],','); std::string startindex = ""; if (parts.size() == 5) startindex = parts[4]; MY_CHECKPOS("list",2); MY_CHECKGEOM("list",3); InventoryLocation loc; if(location == "context" || location == "current_name") loc = m_current_inventory_location; else loc.deSerialize(location); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = stoi(v_geom[0]); geom.Y = stoi(v_geom[1]); infostream<<"list inv="<bp_set != 2) errorstream<<"WARNING: invalid use of list without a size[] element"< parts = split(element,';'); if ((parts.size() == 3) || (parts.size() == 4)) { std::vector v_pos = split(parts[0],','); std::string name = parts[1]; std::string label = parts[2]; std::string selected = ""; if (parts.size() == 4) selected = parts[3]; MY_CHECKPOS("checkbox",0); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; core::rect rect = core::rect(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15)); bool fselected = false; if (selected == "true") fselected = true; wchar_t* wlabel = 0; if (m_use_gettext) wlabel = wgettext(label.c_str()); else wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str(); FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), narrow_to_wide(""), wlabel, 258+m_fields.size() ); spec.ftype = f_CheckBox; gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, spec.fid, wlabel); m_checkboxes.push_back(std::pair(spec,e)); m_fields.push_back(spec); if (m_use_gettext) delete[] wlabel; return; } errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseImage(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 3) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; MY_CHECKPOS("image",0); MY_CHECKGEOM("image",1); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; v2s32 geom; geom.X = stoi(v_geom[0]) * (float)imgsize.X; geom.Y = stoi(v_geom[1]) * (float)imgsize.Y; infostream<<"image name="<bp_set != 2) errorstream<<"WARNING: invalid use of image without a size[] element"< v_pos = split(parts[0],','); std::string name = parts[1]; MY_CHECKPOS("image",0); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; std::cout<<"image name="<bp_set != 2) errorstream<<"WARNING: invalid use of image without a size[] element"< parts = split(element,';'); if (parts.size() == 3) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; MY_CHECKPOS("itemimage",0); MY_CHECKGEOM("itemimage",1); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; v2s32 geom; geom.X = stoi(v_geom[0]) * (float)imgsize.X; geom.Y = stoi(v_geom[1]) * (float)imgsize.Y; infostream<<"item name="<bp_set != 2) errorstream<<"WARNING: invalid use of item_image without a size[] element"< parts = split(element,';'); if (parts.size() == 4) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::string label = parts[3]; MY_CHECKPOS("button",0); MY_CHECKGEOM("button",1); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X); pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; core::rect rect = core::rect(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of button without a size[] element"<addButton(rect, this, spec.fid, spec.flabel.c_str()); m_fields.push_back(spec); if (m_use_gettext) delete[] wlabel; return; } errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 3) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; MY_CHECKPOS("background",0); MY_CHECKGEOM("background",1); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2; pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2; v2s32 geom; geom.X = stof(v_geom[0]) * (float)spacing.X; geom.Y = stof(v_geom[1]) * (float)spacing.Y; infostream<<"image name="<bp_set != 2) errorstream<<"WARNING: invalid use of background without a size[] element"< parts = split(element,';'); if ((parts.size() == 5) || (parts.size() == 6)) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::vector items = split(parts[3],',',true); std::string str_initial_selection = ""; std::string str_transparent = "false"; if (parts.size() >= 5) str_initial_selection = parts[4]; if (parts.size() >= 6) str_transparent = parts[5]; MY_CHECKPOS("textlist",0); MY_CHECKGEOM("textlist",1); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = stof(v_geom[0]) * (float)spacing.X; geom.Y = stof(v_geom[1]) * (float)spacing.Y; core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); std::wstring fname_w = narrow_to_wide(name.c_str()); FieldSpec spec = FieldSpec( fname_w, narrow_to_wide(""), narrow_to_wide(""), 258+m_fields.size() ); spec.ftype = f_ListBox; //now really show list gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid); //don't reset if we already have a user specified selection if (data->listbox_selections.find(fname_w) == data->listbox_selections.end()) { e->setAutoScrollEnabled(false); } if (str_transparent == "false") e->setDrawBackground(true); for (unsigned int i=0; i < items.size(); i++) { if (items[i].c_str()[0] == '#') { if (items[i].c_str()[1] == '#') { e->addItem(narrow_to_wide(unescape_string(items[i])).c_str() +1); } else { std::string color = items[i].substr(1,6); std::wstring toadd = narrow_to_wide(unescape_string(items[i]).c_str() + 7); e->addItem(toadd.c_str()); irr::video::SColor toset; if (parseColor(color, toset)) e->setItemOverrideColor(i,toset); } } else { e->addItem(narrow_to_wide(unescape_string(items[i])).c_str()); } } if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) { e->setSelected(data->listbox_selections[fname_w]); } if (str_initial_selection != "") e->setSelected(stoi(str_initial_selection.c_str())-1); m_listboxes.push_back(std::pair(spec,e)); m_fields.push_back(spec); return; } errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 5) { std::vector v_pos = split(parts[0],','); std::string name = parts[2]; std::vector items = split(parts[3],','); std::string str_initial_selection = ""; str_initial_selection = parts[4]; MY_CHECKPOS("dropdown",0); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; s32 width = stof(parts[1]) * (float)spacing.Y; core::rect rect = core::rect(pos.X, pos.Y, pos.X+width, pos.Y+30); std::wstring fname_w = narrow_to_wide(name.c_str()); FieldSpec spec = FieldSpec( fname_w, narrow_to_wide(""), narrow_to_wide(""), 258+m_fields.size() ); spec.ftype = f_DropDown; spec.send = true; //now really show list gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid); //don't reset if we already have a user specified selection //if (data->combobox_selections.find(fname_w) == data->listbox_selections.end()) { // e->setAutoScrollEnabled(false); //} for (unsigned int i=0; i < items.size(); i++) { e->addItem(narrow_to_wide(items[i]).c_str()); } if (str_initial_selection != "") e->setSelected(stoi(str_initial_selection.c_str())-1); //if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) { // e->setSelected(data->listbox_selections[fname_w]); //} //m_listboxes.push_back(std::pair(spec,e)); m_fields.push_back(spec); return; } errorstream << "Invalid dropdown element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 4) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::string label = parts[3]; MY_CHECKPOS("pwdfield",0); MY_CHECKGEOM("pwdfield",1); v2s32 pos; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X); pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; pos.Y -= 15; geom.Y = 30; core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); label = unescape_string(label); wchar_t* wlabel = 0; if (m_use_gettext) { if (label.length() > 1) wlabel = wgettext(label.c_str()); else wlabel = (wchar_t*) narrow_to_wide("").c_str(); } else wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str(); FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), wlabel, narrow_to_wide(""), 258+m_fields.size() ); spec.send = true; gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid); Environment->setFocus(e); if (label.length() > 1) { rect.UpperLeftCorner.Y -= 15; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); } e->setPasswordBox(true,L'*'); irr::SEvent evt; evt.KeyInput.Key = KEY_END; evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.PressedDown = true; e->OnEvent(evt); m_fields.push_back(spec); if ((m_use_gettext) && (label.length() >1)) delete[] wlabel; return; } errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseSimpleField(parserData* data,std::vector &parts) { std::string name = parts[0]; std::string label = parts[1]; std::string default_val = parts[2]; core::rect rect; if(!data->bp_set) { rect = core::rect( data->screensize.X/2 - 580/2, data->screensize.Y/2 - 300/2, data->screensize.X/2 + 580/2, data->screensize.Y/2 + 300/2 ); DesiredRect = rect; recalculateAbsolutePosition(false); data->basepos = getBasePos(); data->bp_set = 1; } else if(data->bp_set == 2) errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30); if(m_form_src) default_val = m_form_src->resolveText(default_val); default_val = unescape_string(default_val); label = unescape_string(label); wchar_t* wlabel = 0; if (m_use_gettext) { if (label.length() > 1) wlabel = wgettext(label.c_str()); else wlabel = (wchar_t*) narrow_to_wide("").c_str(); } else wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str(); FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), wlabel, narrow_to_wide(default_val.c_str()), 258+m_fields.size() ); if (name == "") { // spec field id to 0, this stops submit searching for a value that isn't there Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); } else { spec.send = true; gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); Environment->setFocus(e); irr::SEvent evt; evt.KeyInput.Key = KEY_END; evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.PressedDown = true; e->OnEvent(evt); if (label.length() > 1) { rect.UpperLeftCorner.Y -= 15; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); } } if (m_use_gettext && (label.length() > 1)) delete[] wlabel; m_fields.push_back(spec); } void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector& parts,std::string type) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::string label = parts[3]; std::string default_val = parts[4]; MY_CHECKPOS(type,0); MY_CHECKGEOM(type,1); v2s32 pos; pos.X = stof(v_pos[0]) * (float) spacing.X; pos.Y = stof(v_pos[1]) * (float) spacing.Y; v2s32 geom; geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X); if (type == "textarea") { geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y); pos.Y += 15; } else { pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; pos.Y -= 15; geom.Y = 30; } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of positioned "<resolveText(default_val); default_val = unescape_string(default_val); label = unescape_string(label); wchar_t* wlabel = 0; if (m_use_gettext) { if (label.length() > 1) wlabel = wgettext(label.c_str()); else wlabel = (wchar_t*) narrow_to_wide("").c_str(); } else wlabel = (wchar_t*) narrow_to_wide(label.c_str()).c_str(); FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), wlabel, narrow_to_wide(default_val.c_str()), 258+m_fields.size() ); if (name == "") { // spec field id to 0, this stops submit searching for a value that isn't there Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); } else { spec.send = true; gui::IGUIEditBox *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); Environment->setFocus(e); if (type == "textarea") { e->setMultiLine(true); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT); } else { irr::SEvent evt; evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; evt.KeyInput.Char = 0; evt.KeyInput.Control = 0; evt.KeyInput.Shift = 0; evt.KeyInput.PressedDown = true; e->OnEvent(evt); } if (label.length() > 1) { rect.UpperLeftCorner.Y -= 15; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); } } if (m_use_gettext && (label.length() > 1)) delete[] wlabel; m_fields.push_back(spec); } void GUIFormSpecMenu::parseField(parserData* data,std::string element,std::string type) { std::vector parts = split(element,';',true); if (parts.size() == 3) { parseSimpleField(data,parts); return; } if (parts.size() == 5) { parseTextArea(data,parts,type); return; } errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 2) { std::vector v_pos = split(parts[0],','); std::string text = parts[1]; MY_CHECKPOS("label",0); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; core::rect rect = core::rect(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15)); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of label without a size[] element"<addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); m_fields.push_back(spec); if (m_use_gettext) delete[] wlabel; return; } errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 2) { std::vector v_pos = split(parts[0],','); std::string text = parts[1]; MY_CHECKPOS("vertlabel",1); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; core::rect rect = core::rect(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+15, pos.Y+300); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of label without a size[] element"<addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_fields.push_back(spec); return; } errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,std::string type) { std::vector parts = split(element,';'); if ((parts.size() == 5) || (parts.size() == 7)) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string image_name = parts[2]; std::string name = parts[3]; std::string label = parts[4]; MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X); geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y); bool noclip = false; bool drawborder = true; if ((parts.size() == 7)) { if (parts[5] == "true") noclip = true; if (parts[6] == "false") drawborder = false; } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<tsrc()->getTexture(image_name); else { if (fs::PathExists(image_name)) { texture = Environment->getVideoDriver()->getTexture(image_name.c_str()); m_Textures.push_back(texture); } } gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); e->setUseAlphaChannel(true); e->setImage(texture); e->setPressedImage(texture); e->setScaleImage(true); e->setNotClipped(noclip); e->setDrawBorder(drawborder); m_fields.push_back(spec); if (m_use_gettext) delete[] wlabel; return; } errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) { std::vector parts = split(element,';'); if ((parts.size() == 4) || (parts.size() == 6)) { std::vector v_pos = split(parts[0],','); std::string name = parts[1]; std::vector buttons = split(parts[2],','); std::string str_index = parts[3]; bool show_background = true; bool show_border = true; int tab_index = stoi(str_index) -1; MY_CHECKPOS("tabheader",0); if (parts.size() == 6) { if (parts[4] == "true") show_background = false; if (parts[5] == "false") show_border = false; } FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), narrow_to_wide(""), narrow_to_wide(""), 258+m_fields.size() ); spec.ftype = f_TabHeader; v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = data->screensize.Y; geom.Y = 30; core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); gui::IGUITabControl *e = Environment->addTabControl(rect,this,show_background,show_border,spec.fid); e->setNotClipped(true); for (unsigned int i=0; i< buttons.size(); i++) { wchar_t* wbutton = 0; if (m_use_gettext) wbutton = wgettext(buttons[i].c_str()); else wbutton = (wchar_t*) narrow_to_wide(buttons[i].c_str()).c_str(); e->addTab(wbutton,-1); if (m_use_gettext) delete[] wbutton; } if ((tab_index >= 0) && (buttons.size() < INT_MAX) && (tab_index < (int) buttons.size())) e->setActiveTab(tab_index); m_fields.push_back(spec); return; } errorstream<< "Invalid TabHeader element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) { if (m_gamedef == 0) { errorstream<<"WARNING: invalid use of item_image_button with m_gamedef==0"< parts = split(element,';'); if (parts.size() == 5) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string item_name = parts[2]; std::string name = parts[3]; std::string label = parts[4]; MY_CHECKPOS("itemimagebutton",0); MY_CHECKGEOM("itemimagebutton",1); v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X); geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y); core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(data->bp_set != 2) errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<idef(); ItemStack item; item.deSerialize(item_name, idef); video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); std::string tooltip = item.getDefinition(idef).description; label = unescape_string(label); FieldSpec spec = FieldSpec( narrow_to_wide(name.c_str()), narrow_to_wide(label.c_str()), narrow_to_wide(item_name.c_str()), 258+m_fields.size() ); gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); e->setUseAlphaChannel(true); e->setImage(texture); e->setPressedImage(texture); e->setScaleImage(true); spec.ftype = f_Button; rect+=data->basepos-padding; spec.rect=rect; if (tooltip!="") spec.tooltip=tooltip; m_fields.push_back(spec); return; } errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseBox(parserData* data,std::string element) { std::vector parts = split(element,';'); if (parts.size() == 3) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string color_str = parts[2]; MY_CHECKPOS("box",0); MY_CHECKGEOM("box",1); v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; v2s32 geom; geom.X = stof(v_geom[0]) * (float)spacing.X; geom.Y = stof(v_geom[1]) * (float)spacing.Y; irr::video::SColor color; if (parseColor(color_str, color)) { BoxDrawSpec spec(pos,geom,color); m_boxes.push_back(spec); } else { errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl; } return; } errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseElement(parserData* data,std::string element) { //some prechecks if (element == "") return; std::vector parts = split(element,'[', true); // ugly workaround to keep compatibility if (parts.size() > 2) { if (trim(parts[0]) == "image") { for (unsigned int i=2;i< parts.size(); i++) { parts[1] += "[" + parts[i]; } } else { return; } } if (parts.size() < 2) { return; } std::string type = trim(parts[0]); std::string description = trim(parts[1]); if ((type == "size") || (type == "invsize")){ parseSize(data,description); return; } if (type == "list") { parseList(data,description); return; } if (type == "checkbox") { parseCheckbox(data,description); return; } if (type == "image") { parseImage(data,description); return; } if (type == "item_image") { parseItemImage(data,description); return; } if ((type == "button") || (type == "button_exit")) { parseButton(data,description,type); return; } if (type == "background") { parseBackground(data,description); return; } if (type == "textlist"){ parseTextList(data,description); return; } if (type == "dropdown"){ parseDropDown(data,description); return; } if (type == "pwdfield") { parsePwdField(data,description); return; } if ((type == "field") || (type == "textarea")){ parseField(data,description,type); return; } if (type == "label") { parseLabel(data,description); return; } if (type == "vertlabel") { parseVertLabel(data,description); return; } if (type == "item_image_button") { parseItemImageButton(data,description); return; } if ((type == "image_button") || (type == "image_button_exit")) { parseImageButton(data,description,type); return; } if (type == "tabheader") { parseTabHeader(data,description); return; } if (type == "box") { parseBox(data,description); return; } // Ignore others infostream << "Unknown DrawSpec: type="<getSelected(); if (selection != -1) { std::wstring listboxname = m_listboxes[i].first.fname; mydata.listbox_selections[listboxname] = selection; } } // Remove children removeChildren(); mydata.size= v2s32(100,100); mydata.helptext_h = 15; mydata.screensize = screensize; // Base position of contents of form mydata.basepos = getBasePos(); // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element // Used to adjust form size automatically if needed // A proceed button is added if there is no size[] element mydata.bp_set = 0; /* Convert m_init_draw_spec to m_inventorylists */ m_inventorylists.clear(); m_images.clear(); m_backgrounds.clear(); m_itemimages.clear(); m_listboxes.clear(); m_checkboxes.clear(); m_fields.clear(); m_boxes.clear(); std::vector elements = split(m_formspec_string,']',true); for (unsigned int i=0;i< elements.size();i++) { parseElement(&mydata,elements[i]); } // If there's inventory, put the usage string at the bottom if (m_inventorylists.size()) { changeCtype(""); core::rect rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h); rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5, mydata.size.Y-5-mydata.helptext_h); const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item"); Environment->addStaticText(text, rect, false, true, this, 256); delete[] text; changeCtype("C"); } // If there's fields, add a Proceed button if (m_fields.size() && mydata.bp_set != 2) { // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields mydata.rect = core::rect( mydata.screensize.X/2 - 580/2, mydata.screensize.Y/2 - 300/2, mydata.screensize.X/2 + 580/2, mydata.screensize.Y/2 + 240/2+(m_fields.size()*60) ); DesiredRect = mydata.rect; recalculateAbsolutePosition(false); mydata.basepos = getBasePos(); changeCtype(""); { v2s32 pos = mydata.basepos; pos.Y = ((m_fields.size()+2)*60); v2s32 size = DesiredRect.getSize(); mydata.rect = core::rect(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30); wchar_t* text = wgettext("Proceed"); Environment->addButton(mydata.rect, this, 257, text); delete[] text; } changeCtype("C"); } // Add tooltip { // Note: parent != this so that the tooltip isn't clipped by the menu rectangle m_tooltip_element = Environment->addStaticText(L"",core::rect(0,0,110,18)); m_tooltip_element->enableOverrideColor(true); m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60)); m_tooltip_element->setDrawBackground(true); m_tooltip_element->setDrawBorder(true); m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255)); m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_tooltip_element->setWordWrap(false); } } GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const { core::rect imgrect(0,0,imgsize.X,imgsize.Y); for(u32 i=0; i rect = imgrect + s.pos + p0; if(rect.isPointInside(p)) { return ItemSpec(s.inventoryloc, s.listname, item_i); } } } return ItemSpec(InventoryLocation(), "", -1); } void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) { video::IVideoDriver* driver = Environment->getVideoDriver(); // Get font gui::IGUIFont *font = NULL; gui::IGUISkin* skin = Environment->getSkin(); if (skin) font = skin->getFont(); Inventory *inv = m_invmgr->getInventory(s.inventoryloc); if(!inv){ infostream<<"GUIFormSpecMenu::drawList(): WARNING: " <<"The inventory location " <<"\""<getList(s.listname); if(!ilist){ infostream<<"GUIFormSpecMenu::drawList(): WARNING: " <<"The inventory list \""< imgrect(0,0,imgsize.X,imgsize.Y); for(s32 i=0; i= (s32) ilist->getSize()) break; s32 x = (i%s.geom.X) * spacing.X; s32 y = (i/s.geom.X) * spacing.Y; v2s32 p(x,y); core::rect rect = imgrect + s.pos + p; ItemStack item; if(ilist) item = ilist->getItem(item_i); bool selected = m_selected_item && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv && m_selected_item->listname == s.listname && m_selected_item->i == item_i; bool hovering = rect.isPointInside(m_pointer); if(phase == 0) { if(hovering && m_selected_item) { video::SColor bgcolor(255,192,192,192); driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); } else { video::SColor bgcolor(255,128,128,128); driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); } } if(phase == 1) { // Draw item stack if(selected) { item.takeItem(m_selected_amount); } if(!item.empty()) { drawItemStack(driver, font, item, rect, &AbsoluteClippingRect, m_gamedef); } // Draw tooltip std::string tooltip_text = ""; if(hovering && !m_selected_item) tooltip_text = item.getDefinition(m_gamedef->idef()).description; if(tooltip_text != "") { m_tooltip_element->setVisible(true); this->bringToFront(m_tooltip_element); m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str()); s32 tooltip_x = m_pointer.X + 15; s32 tooltip_y = m_pointer.Y + 15; s32 tooltip_width = m_tooltip_element->getTextWidth() + 15; s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; m_tooltip_element->setRelativePosition(core::rect( core::position2d(tooltip_x, tooltip_y), core::dimension2d(tooltip_width, tooltip_height))); } } } } void GUIFormSpecMenu::drawSelectedItem() { if(!m_selected_item) return; video::IVideoDriver* driver = Environment->getVideoDriver(); // Get font gui::IGUIFont *font = NULL; gui::IGUISkin* skin = Environment->getSkin(); if (skin) font = skin->getFont(); Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); assert(inv); InventoryList *list = inv->getList(m_selected_item->listname); assert(list); ItemStack stack = list->getItem(m_selected_item->i); stack.count = m_selected_amount; core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); drawItemStack(driver, font, stack, rect, NULL, m_gamedef); } void GUIFormSpecMenu::drawMenu() { if(m_form_src){ std::string newform = m_form_src->getForm(); if(newform != m_formspec_string){ m_formspec_string = newform; regenerateGui(m_screensize_old); } } m_pointer = m_device->getCursorControl()->getPosition(); updateSelectedItem(); gui::IGUISkin* skin = Environment->getSkin(); if (!skin) return; video::IVideoDriver* driver = Environment->getVideoDriver(); video::SColor bgcolor(140,0,0,0); driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); m_tooltip_element->setVisible(false); /* Draw backgrounds */ for(u32 i=0; itsrc()->getTexture(spec.name); else { texture = driver->getTexture(spec.name.c_str()); m_Textures.push_back(texture); } if (texture != 0) { // Image size on screen core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); // Image rectangle on screen core::rect rect = imgrect + spec.pos; const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; driver->draw2DImage(texture, rect, core::rect(core::position2d(0,0), core::dimension2di(texture->getOriginalSize())), NULL/*&AbsoluteClippingRect*/, colors, true); } else { errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl; errorstream << "\t" << spec.name << std::endl; } } /* Draw Boxes */ for(u32 i=0; i rect(spec.pos.X,spec.pos.Y, spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y); driver->draw2DRectangle(todraw, rect, 0); } /* Draw images */ for(u32 i=0; itsrc()->getTexture(spec.name); else { texture = driver->getTexture(spec.name.c_str()); m_Textures.push_back(texture); } if (texture != 0) { const core::dimension2d& img_origsize = texture->getOriginalSize(); // Image size on screen core::rect imgrect; if (spec.scale) imgrect = core::rect(0,0,spec.geom.X, spec.geom.Y); else { imgrect = core::rect(0,0,img_origsize.Width,img_origsize.Height); } // Image rectangle on screen core::rect rect = imgrect + spec.pos; const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; driver->draw2DImage(texture, rect, core::rect(core::position2d(0,0),img_origsize), NULL/*&AbsoluteClippingRect*/, colors, true); } else { errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl; errorstream << "\t" << spec.name << std::endl; } } /* Draw item images */ for(u32 i=0; iidef(); ItemStack item; item.deSerialize(spec.name, idef); video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); // Image size on screen core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); // Image rectangle on screen core::rect rect = imgrect + spec.pos; const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; driver->draw2DImage(texture, rect, core::rect(core::position2d(0,0), core::dimension2di(texture->getOriginalSize())), NULL/*&AbsoluteClippingRect*/, colors, true); } /* Draw items Phase 0: Item slot rectangles Phase 1: Item images; prepare tooltip If backgrounds used, do not draw Item slot rectangles */ int start_phase=0; if (m_backgrounds.size() > 0) start_phase=1; for(int phase=start_phase; phase<=1; phase++) for(u32 i=0; i rect = spec.rect; if (rect.isPointInside(m_pointer)) { m_tooltip_element->setVisible(true); this->bringToFront(m_tooltip_element); m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str()); s32 tooltip_x = m_pointer.X + 15; s32 tooltip_y = m_pointer.Y + 15; s32 tooltip_width = m_tooltip_element->getTextWidth() + 15; s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; m_tooltip_element->setRelativePosition(core::rect( core::position2d(tooltip_x, tooltip_y), core::dimension2d(tooltip_width, tooltip_height))); } } } /* Draw dragged item stack */ drawSelectedItem(); } void GUIFormSpecMenu::updateSelectedItem() { // If the selected stack has become empty for some reason, deselect it. // If the selected stack has become inaccessible, deselect it. // If the selected stack has become smaller, adjust m_selected_amount. ItemStack selected = verifySelectedItem(); // WARNING: BLACK MAGIC // See if there is a stack suited for our current guess. // If such stack does not exist, clear the guess. if(m_selected_content_guess.name != "" && selected.name == m_selected_content_guess.name && selected.count == m_selected_content_guess.count){ // Selected item fits the guess. Skip the black magic. } else if(m_selected_content_guess.name != ""){ bool found = false; for(u32 i=0; igetInventory(s.inventoryloc); if(!inv) continue; InventoryList *list = inv->getList(s.listname); if(!list) continue; for(s32 i=0; i= list->getSize()) continue; ItemStack stack = list->getItem(item_i); if(stack.name == m_selected_content_guess.name && stack.count == m_selected_content_guess.count){ found = true; infostream<<"Client: Changing selected content guess to " <getInventory(s.inventoryloc); InventoryList *list = inv->getList("craftresult"); if(list && list->getSize() >= 1 && !list->getItem(0).empty()) { m_selected_item = new ItemSpec; m_selected_item->inventoryloc = s.inventoryloc; m_selected_item->listname = "craftresult"; m_selected_item->i = 0; m_selected_amount = 0; m_selected_dragging = false; break; } } } } // If craftresult is selected, keep the whole stack selected if(m_selected_item && m_selected_item->listname == "craftresult") { m_selected_amount = verifySelectedItem().count; } } ItemStack GUIFormSpecMenu::verifySelectedItem() { // If the selected stack has become empty for some reason, deselect it. // If the selected stack has become inaccessible, deselect it. // If the selected stack has become smaller, adjust m_selected_amount. // Return the selected stack. if(m_selected_item) { if(m_selected_item->isValid()) { Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); if(inv) { InventoryList *list = inv->getList(m_selected_item->listname); if(list && (u32) m_selected_item->i < list->getSize()) { ItemStack stack = list->getItem(m_selected_item->i); if(m_selected_amount > stack.count) m_selected_amount = stack.count; if(!stack.empty()) return stack; } } } // selection was not valid delete m_selected_item; m_selected_item = NULL; m_selected_amount = 0; m_selected_dragging = false; } return ItemStack(); } void GUIFormSpecMenu::acceptInput(int eventtype) { if(m_text_dst) { std::map fields; if (current_keys_pending.key_down) { fields["key_down"] = "true"; current_keys_pending.key_down = false; } if (current_keys_pending.key_up) { fields["key_up"] = "true"; current_keys_pending.key_up = false; } if (current_keys_pending.key_enter) { fields["key_enter"] = "true"; current_keys_pending.key_enter = false; } if (current_keys_pending.key_escape) { fields["key_escape"] = "true"; current_keys_pending.key_escape = false; } for(u32 i=0; igetType() == gui::EGUIET_COMBO_BOX)) { e = static_cast(element); } fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getItem(e->getSelected())); } else if (s.ftype == f_TabHeader) { // no dynamic cast possible due to some distributions shipped // without rtti support in irrlicht IGUIElement * element = getElementFromId(s.fid); gui::IGUITabControl *e = NULL; if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) { e = static_cast(element); } if (e != 0) { std::stringstream ss; ss << (e->getActiveTab() +1); fields[wide_to_narrow(s.fname.c_str())] = ss.str(); } } else if (s.ftype == f_CheckBox) { // no dynamic cast possible due to some distributions shipped // without rtti support in irrlicht IGUIElement * element = getElementFromId(s.fid); gui::IGUICheckBox *e = NULL; if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) { e = static_cast(element); } if (e != 0) { if (e->isChecked()) fields[wide_to_narrow(s.fname.c_str())] = "true"; else fields[wide_to_narrow(s.fname.c_str())] = "false"; } } else { IGUIElement* e = getElementFromId(s.fid); if(e != NULL) { fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText()); } } } } m_text_dst->gotText(fields); } } bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); if (event.KeyInput.PressedDown && (kp == EscapeKey || kp == getKeySetting("keymap_inventory"))) { if (m_allowclose) quitMenu(); else m_text_dst->gotText(narrow_to_wide("MenuQuit")); return true; } if (event.KeyInput.PressedDown && (event.KeyInput.Key==KEY_RETURN || event.KeyInput.Key==KEY_UP || event.KeyInput.Key==KEY_DOWN) ) { switch (event.KeyInput.Key) { case KEY_RETURN: if (m_allowclose) { acceptInput(); quitMenu(); } else current_keys_pending.key_enter = true; break; case KEY_UP: current_keys_pending.key_up = true; break; case KEY_DOWN: current_keys_pending.key_down = true; break; break; default: //can't happen at all! assert("reached a source line that can't ever been reached" == 0); break; } acceptInput(); return true; } } if(event.EventType==EET_MOUSE_INPUT_EVENT && event.MouseInput.Event != EMIE_MOUSE_MOVED) { // Mouse event other than movement // Get selected item and hovered/clicked item (s) updateSelectedItem(); ItemSpec s = getItemAtPos(m_pointer); Inventory *inv_selected = NULL; Inventory *inv_s = NULL; if(m_selected_item) { inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc); assert(inv_selected); assert(inv_selected->getList(m_selected_item->listname) != NULL); } u32 s_count = 0; if(s.isValid()) do{ // breakable inv_s = m_invmgr->getInventory(s.inventoryloc); if(!inv_s){ errorstream<<"InventoryMenu: The selected inventory location " <<"\""<getList(s.listname); if(list == NULL){ verbosestream<<"InventoryMenu: The selected inventory list \"" <= list->getSize()){ infostream<<"InventoryMenu: The selected inventory list \"" <getItem(s.i).count; }while(0); bool identical = (m_selected_item != NULL) && s.isValid() && (inv_selected == inv_s) && (m_selected_item->listname == s.listname) && (m_selected_item->i == s.i); // buttons: 0 = left, 1 = right, 2 = middle // up/down: 0 = down (press), 1 = up (release), 2 = unknown event int button = 0; int updown = 2; if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { button = 0; updown = 0; } else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { button = 1; updown = 0; } else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) { button = 2; updown = 0; } else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { button = 0; updown = 1; } else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { button = 1; updown = 1; } else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) { button = 2; updown = 1; } // Set this number to a positive value to generate a move action // from m_selected_item to s. u32 move_amount = 0; // Set this number to a positive value to generate a drop action // from m_selected_item. u32 drop_amount = 0; // Set this number to a positive value to generate a craft action at s. u32 craft_amount = 0; if(updown == 0) { // Some mouse button has been pressed //infostream<<"Mouse button "<= 1); if(s.isValid()) { // Clicked a slot: move if(button == 1) // right move_amount = 1; else if(button == 2) // middle move_amount = MYMIN(m_selected_amount, 10); else // left move_amount = m_selected_amount; if(identical) { if(move_amount >= m_selected_amount) m_selected_amount = 0; else m_selected_amount -= move_amount; move_amount = 0; } } else if(getAbsoluteClippingRect().isPointInside(m_pointer)) { // Clicked somewhere else: deselect m_selected_amount = 0; } else { // Clicked outside of the window: drop if(button == 1) // right drop_amount = 1; else if(button == 2) // middle drop_amount = MYMIN(m_selected_amount, 10); else // left drop_amount = m_selected_amount; } } } else if(updown == 1) { // Some mouse button has been released //infostream<<"Mouse button "< 0) { // Send IACTION_MOVE assert(m_selected_item && m_selected_item->isValid()); assert(s.isValid()); assert(inv_selected && inv_s); InventoryList *list_from = inv_selected->getList(m_selected_item->listname); InventoryList *list_to = inv_s->getList(s.listname); assert(list_from && list_to); ItemStack stack_from = list_from->getItem(m_selected_item->i); ItemStack stack_to = list_to->getItem(s.i); // Check how many items can be moved move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); // If source stack cannot be added to destination stack at all, // they are swapped if(leftover.count == stack_from.count && leftover.name == stack_from.name) { m_selected_amount = stack_to.count; // In case the server doesn't directly swap them but instead // moves stack_to somewhere else, set this m_selected_content_guess = stack_to; m_selected_content_guess_inventory = s.inventoryloc; } // Source stack goes fully into destination stack else if(leftover.empty()) { m_selected_amount -= move_amount; m_selected_content_guess = ItemStack(); // Clear } // Source stack goes partly into destination stack else { move_amount -= leftover.count; m_selected_amount -= move_amount; m_selected_content_guess = ItemStack(); // Clear } infostream<<"Handing IACTION_MOVE to manager"<count = move_amount; a->from_inv = m_selected_item->inventoryloc; a->from_list = m_selected_item->listname; a->from_i = m_selected_item->i; a->to_inv = s.inventoryloc; a->to_list = s.listname; a->to_i = s.i; m_invmgr->inventoryAction(a); } else if(drop_amount > 0) { m_selected_content_guess = ItemStack(); // Clear // Send IACTION_DROP assert(m_selected_item && m_selected_item->isValid()); assert(inv_selected); InventoryList *list_from = inv_selected->getList(m_selected_item->listname); assert(list_from); ItemStack stack_from = list_from->getItem(m_selected_item->i); // Check how many items can be dropped drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count); assert(drop_amount > 0 && drop_amount <= m_selected_amount); m_selected_amount -= drop_amount; infostream<<"Handing IACTION_DROP to manager"<count = drop_amount; a->from_inv = m_selected_item->inventoryloc; a->from_list = m_selected_item->listname; a->from_i = m_selected_item->i; m_invmgr->inventoryAction(a); } else if(craft_amount > 0) { m_selected_content_guess = ItemStack(); // Clear // Send IACTION_CRAFT assert(s.isValid()); assert(inv_s); infostream<<"Handing IACTION_CRAFT to manager"<count = craft_amount; a->craft_inv = s.inventoryloc; m_invmgr->inventoryAction(a); } // If m_selected_amount has been decreased to zero, deselect if(m_selected_amount == 0) { delete m_selected_item; m_selected_item = NULL; m_selected_amount = 0; m_selected_dragging = false; m_selected_content_guess = ItemStack(); } } if(event.EventType==EET_GUI_EVENT) { if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED && isVisible()) { // find the element that was clicked for(u32 i=0; igetID())) { s.send = true; acceptInput(); s.send = false; // Restore focus to the full form Environment->setFocus(this); return true; } } } if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) { if(!canTakeFocus(event.GUIEvent.Element)) { infostream<<"GUIFormSpecMenu: Not allowing focus change." <getID(); if (btn_id == 257) { acceptInput(); if (m_allowclose) quitMenu(); else m_text_dst->gotText(narrow_to_wide("ExitButton")); // quitMenu deallocates menu return true; } // find the element that was clicked for(u32 i=0; igetID())) { s.send = true; acceptInput(); if(s.is_exit){ if (m_allowclose) quitMenu(); else m_text_dst->gotText(narrow_to_wide("ExitButton")); return true; }else{ s.send = false; // Restore focus to the full form Environment->setFocus(this); return true; } } } } if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER) { if(event.GUIEvent.Caller->getID() > 257) { if (m_allowclose) { acceptInput(); quitMenu(); } else { current_keys_pending.key_enter = true; acceptInput(); } // quitMenu deallocates menu return true; } } if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) || (event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)) { int current_id = event.GUIEvent.Caller->getID(); if(current_id > 257) { // find the element that was clicked for(u32 i=0; isetFocus(this); } } return true; } } } return Parent ? Parent->OnEvent(event) : false; } bool GUIFormSpecMenu::parseColor(std::string color, irr::video::SColor& outcolor) { outcolor = irr::video::SColor(0,0,0,0); if (!string_allowed(color, "0123456789abcdefABCDEF")) return false; u32 color_value; std::istringstream iss(color); iss >> std::hex >> color_value; outcolor = irr::video::SColor(color_value); outcolor.setAlpha(255); return true; }