void GUIInventoryMenu::regenerateGui(v2u32 screensize)
{
	// Remove children
	removeChildren();

	padding = v2s32(screensize.Y/40, screensize.Y/40);
	spacing = v2s32(screensize.Y/12, screensize.Y/13);
	imgsize = v2s32(screensize.Y/15, screensize.Y/15);

	s32 helptext_h = 15;

	v2s32 size(
		padding.X*2+spacing.X*(m_menu_size.X-1)+imgsize.X,
		padding.Y*2+spacing.Y*(m_menu_size.Y-1)+imgsize.Y + helptext_h
	);

	core::rect<s32> rect(
			screensize.X/2 - size.X/2,
			screensize.Y/2 - size.Y/2,
			screensize.X/2 + size.X/2,
			screensize.Y/2 + size.Y/2
	);

	DesiredRect = rect;
	recalculateAbsolutePosition(false);

	v2s32 basepos = getBasePos();

	m_draw_spec.clear();
	for(u16 i=0; i<m_init_draw_spec.size(); i++)
	{
		DrawSpec &s = m_init_draw_spec[i];
		if(s.type == "list")
		{
			m_draw_spec.push_back(ListDrawSpec(s.name, s.subname,
					basepos + v2s32(spacing.X*s.pos.X, spacing.Y*s.pos.Y),
					s.geom));
		}
	}

	// Add children
	{
		core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
		rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
				size.Y-rect.getHeight()-15);
		const wchar_t *text =
		L"Left click: Move all items, Right click: Move single item";
		Environment->addStaticText(text, rect, false, true, this, 256);
	}
}
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
	// Remove children
	removeChildren();
	
	v2s32 size(100,100);
	s32 helptext_h = 15;
	core::rect<s32> rect;

	// Base position of contents of form
	v2s32 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
	int bp_set = 0;
	
	/* Convert m_init_draw_spec to m_inventorylists */
	
	m_inventorylists.clear();
	m_images.clear();
	m_fields.clear();

	Strfnd f(m_formspec_string);
	while(f.atend() == false)
	{
		std::string type = trim(f.next("["));
		if(type == "invsize" || type == "size")
		{
			v2f invsize;
			invsize.X = stof(f.next(","));
			if(type == "size")
			{
				invsize.Y = stof(f.next("]"));
			}
			else{
				invsize.Y = stof(f.next(";"));
				f.next("]");
			}
			infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;

			padding = v2s32(screensize.Y/40, screensize.Y/40);
			spacing = v2s32(screensize.Y/12, screensize.Y/13);
			imgsize = v2s32(screensize.Y/15, screensize.Y/15);
			size = v2s32(
				padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
				padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
			);
			rect = core::rect<s32>(
					screensize.X/2 - size.X/2,
					screensize.Y/2 - size.Y/2,
					screensize.X/2 + size.X/2,
					screensize.Y/2 + size.Y/2
			);
			DesiredRect = rect;
			recalculateAbsolutePosition(false);
			basepos = getBasePos();
			bp_set = 2;
		}
		else if(type == "list")
		{
			std::string name = f.next(";");
			InventoryLocation loc;
			if(name == "context" || name == "current_name")
				loc = m_current_inventory_location;
			else
				loc.deSerialize(name);
			std::string listname = f.next(";");
			v2s32 pos = basepos;
			pos.X += stof(f.next(",")) * (float)spacing.X;
			pos.Y += stof(f.next(";")) * (float)spacing.Y;
			v2s32 geom;
			geom.X = stoi(f.next(","));
			geom.Y = stoi(f.next(";"));
			infostream<<"list inv="<<name<<", listname="<<listname
					<<", pos=("<<pos.X<<","<<pos.Y<<")"
					<<", geom=("<<geom.X<<","<<geom.Y<<")"
					<<std::endl;
			std::string start_i_s = f.next("]");
			s32 start_i = 0;
			if(start_i_s != "")
				start_i = stoi(start_i_s);
			if(bp_set != 2)
				errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
			m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
		}
		else if(type == "image")
		{
			v2s32 pos = basepos;
			pos.X += stof(f.next(",")) * (float)spacing.X;
			pos.Y += stof(f.next(";")) * (float)spacing.Y;
			v2s32 geom;
			geom.X = stof(f.next(",")) * (float)imgsize.X;
			geom.Y = stof(f.next(";")) * (float)imgsize.Y;
			std::string name = f.next("]");
			infostream<<"image name="<<name
					<<", pos=("<<pos.X<<","<<pos.Y<<")"
					<<", geom=("<<geom.X<<","<<geom.Y<<")"
					<<std::endl;
			if(bp_set != 2)
				errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
			m_images.push_back(ImageDrawSpec(name, pos, geom));
		}
		else if(type == "field")
		{
			std::string fname = f.next(";");
			std::string flabel = f.next(";");
			
			if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
			{
				if(!bp_set)
				{
					rect = core::rect<s32>(
						screensize.X/2 - 580/2,
						screensize.Y/2 - 300/2,
						screensize.X/2 + 580/2,
						screensize.Y/2 + 300/2
					);
					DesiredRect = rect;
					recalculateAbsolutePosition(false);
					basepos = getBasePos();
					bp_set = 1;
				}
				else if(bp_set == 2)
					errorstream<<"WARNING: invalid use of unpositioned field in inventory"<<std::endl;

				v2s32 pos = basepos;
				pos.Y = ((m_fields.size()+2)*60);
				v2s32 size = DesiredRect.getSize();
				rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
			}
			else
			{
				v2s32 pos;
				pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
				pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
				v2s32 geom;
				geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
				pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;

				rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
				
				fname = f.next(";");
				flabel = f.next(";");
				if(bp_set != 2)
					errorstream<<"WARNING: invalid use of positioned field without a size[] element"<<std::endl;
				
			}

			std::string odefault = f.next("]");
			std::string fdefault;
			
			// fdefault may contain a variable reference, which
			// needs to be resolved from the node metadata
			if(m_form_src)
				fdefault = m_form_src->resolveText(odefault);
			else
				fdefault = odefault;

			FieldSpec spec = FieldSpec(
				narrow_to_wide(fname.c_str()),
				narrow_to_wide(flabel.c_str()),
				narrow_to_wide(fdefault.c_str()),
				258+m_fields.size()
			);
			
			// three cases: field and no label, label and no field, label and field
			if (flabel == "") 
			{
				spec.send = true;
				gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
				Environment->setFocus(e);

				irr::SEvent evt;
				evt.EventType = EET_KEY_INPUT_EVENT;
				evt.KeyInput.Key = KEY_END;
				evt.KeyInput.PressedDown = true;
				e->OnEvent(evt);
			}
			else if (fname == "")
			{
				// set 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::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
				Environment->setFocus(e);
				rect.UpperLeftCorner.Y -= 15;
				rect.LowerRightCorner.Y -= 15;
				Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);

				irr::SEvent evt;
				evt.EventType = EET_KEY_INPUT_EVENT;
				evt.KeyInput.Key = KEY_END;
				evt.KeyInput.PressedDown = true;
				e->OnEvent(evt);
			}
			
			m_fields.push_back(spec);
		}
		else if(type == "label")
		{
			v2s32 pos = padding;
			pos.X += stof(f.next(",")) * (float)spacing.X;
			pos.Y += stof(f.next(";")) * (float)spacing.Y;

			rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
			
			std::string flabel = f.next("]");
			if(bp_set != 2)
				errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;

			FieldSpec spec = FieldSpec(
				narrow_to_wide(""),
				narrow_to_wide(flabel.c_str()),
				narrow_to_wide(""),
				258+m_fields.size()
			);
			Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
			m_fields.push_back(spec);
		}
		else if(type == "button" || type == "button_exit")
		{
			v2s32 pos = padding;
			pos.X += stof(f.next(",")) * (float)spacing.X;
			pos.Y += stof(f.next(";")) * (float)spacing.Y;
			v2s32 geom;
			geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
			pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;

			rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
			
			std::string fname = f.next(";");
			std::string flabel = f.next("]");
			if(bp_set != 2)
				errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;

			FieldSpec spec = FieldSpec(
				narrow_to_wide(fname.c_str()),
				narrow_to_wide(flabel.c_str()),
				narrow_to_wide(""),
				258+m_fields.size()
			);
			spec.is_button = true;
			if(type == "button_exit")
				spec.is_exit = true;
			Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
			m_fields.push_back(spec);
		}
		else if(type == "image_button" || type == "image_button_exit")
		{
			v2s32 pos = padding;
			pos.X += stof(f.next(",")) * (float)spacing.X;
			pos.Y += stof(f.next(";")) * (float)spacing.Y;
			v2s32 geom;
			geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
			geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);

			rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
			
			std::string fimage = f.next(";");
			std::string fname = f.next(";");
			std::string flabel = f.next("]");
			if(bp_set != 2)
				errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;

			FieldSpec spec = FieldSpec(
				narrow_to_wide(fname.c_str()),
				narrow_to_wide(flabel.c_str()),
				narrow_to_wide(fimage.c_str()),
				258+m_fields.size()
			);
			spec.is_button = true;
			if(type == "image_button_exit")
				spec.is_exit = true;
			
			video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
			gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
			e->setImage(texture);
			e->setPressedImage(texture);
			e->setScaleImage(true);
			
			m_fields.push_back(spec);
		}
		else
		{
			// Ignore others
			std::string ts = f.next("]");
			infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
					<<std::endl;
		}
	}

	// If there's inventory, put the usage string at the bottom
	if (m_inventorylists.size())
	{
		changeCtype("");
		core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
		rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
				size.Y-rect.getHeight()-5);
		const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
		Environment->addStaticText(text, rect, false, true, this, 256);
		changeCtype("C");
	}
	// If there's fields, add a Proceed button
	if (m_fields.size() && bp_set != 2) 
	{
		// if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
		rect = core::rect<s32>(
			screensize.X/2 - 580/2,
			screensize.Y/2 - 300/2,
			screensize.X/2 + 580/2,
			screensize.Y/2 + 240/2+(m_fields.size()*60)
		);
		DesiredRect = rect;
		recalculateAbsolutePosition(false);
		basepos = getBasePos();

		changeCtype("");
		{
			v2s32 pos = basepos;
			pos.Y = ((m_fields.size()+2)*60);

			v2s32 size = DesiredRect.getSize();
			rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
			Environment->addButton(rect, this, 257, wgettext("Proceed"));
		}
		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<s32>(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);
	}
}