diff options
Diffstat (limited to 'src/guiFormSpecMenu.cpp')
-rw-r--r-- | src/guiFormSpecMenu.cpp | 1978 |
1 files changed, 1571 insertions, 407 deletions
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index ee39df8b7..0aa2c2dcd 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -18,6 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc., */ +#include <cstdlib> +#include <algorithm> +#include <iterator> +#include <sstream> +#include <limits> #include "guiFormSpecMenu.h" #include "constants.h" #include "gamedef.h" @@ -28,13 +33,34 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <IGUIButton.h> #include <IGUIStaticText.h> #include <IGUIFont.h> +#include <IGUIListBox.h> +#include <IGUITabControl.h> +#include <IGUIScrollBar.h> +#include <IGUIComboBox.h> #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, @@ -140,7 +166,10 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_selected_item(NULL), m_selected_amount(0), m_selected_dragging(false), - m_tooltip_element(NULL) + m_tooltip_element(NULL), + m_allowclose(true), + m_use_gettext(false), + m_lock(false) { } @@ -180,436 +209,1337 @@ void GUIFormSpecMenu::removeChildren() } } -void GUIFormSpecMenu::regenerateGui(v2u32 screensize) -{ - // Remove children - removeChildren(); - - v2s32 size(100,100); - s32 helptext_h = 15; - core::rect<s32> rect; +int GUIFormSpecMenu::getListboxIndex(std::string listboxname) { - // 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_backgrounds.clear(); - m_itemimages.clear(); - m_fields.clear(); + std::wstring wlistboxname = narrow_to_wide(listboxname.c_str()); - Strfnd f(m_formspec_string); - while(f.atend() == false) - { - std::string type = trim(f.next_esc("[")); - if(type == "invsize" || type == "size") - { - v2f invsize; - invsize.X = stof(f.next_esc(",")); - if(type == "size") - { - invsize.Y = stof(f.next_esc("]")); - } - else{ - invsize.Y = stof(f.next_esc(";")); - f.next_esc("]"); - } - 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_esc(";"); - InventoryLocation loc; - if(name == "context" || name == "current_name") - loc = m_current_inventory_location; - else - loc.deSerialize(name); - std::string listname = f.next_esc(";"); - v2s32 pos = basepos; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = stoi(f.next_esc(",")); - geom.Y = stoi(f.next_esc(";")); - 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_esc("]"); - 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_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = stof(f.next_esc(",")) * (float)imgsize.X; - geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y; - std::string name = f.next_esc("]"); - 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 image without a size[] element"<<std::endl; - m_images.push_back(ImageDrawSpec(name, pos, geom)); - } - else if(type == "item_image") - { - v2s32 pos = basepos; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = stof(f.next_esc(",")) * (float)imgsize.X; - geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y; - std::string name = f.next_esc("]"); - infostream<<"item name="<<name - <<", pos=("<<pos.X<<","<<pos.Y<<")" - <<", geom=("<<geom.X<<","<<geom.Y<<")" - <<std::endl; - if(bp_set != 2) - errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl; - m_itemimages.push_back(ImageDrawSpec(name, pos, geom)); - } - else if(type == "background") - { - v2s32 pos = basepos; - pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2; - v2s32 geom; - geom.X = stof(f.next_esc(",")) * (float)spacing.X; - geom.Y = stof(f.next_esc(";")) * (float)spacing.Y; - std::string name = f.next_esc("]"); - 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 background without a size[] element"<<std::endl; - m_backgrounds.push_back(ImageDrawSpec(name, pos, geom)); + 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(); } - else if(type == "field" || type == "textarea") - { - std::string fname = f.next_esc(";"); - std::string flabel = f.next_esc(";"); + } + return -1; +} - if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos) - { - if (type == "textarea") - errorstream<<"WARNING: Textarea connot be unpositioned"<<std::endl; +std::vector<std::string> split(const std::string &s, char delim, bool escape=false) { + std::vector<std::string> tokens; - 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; + 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_delim = false; + for(unsigned int i=1; i < s.size(); i++) { + if (last_was_delim) { + if (s.c_str()[i] == delim) { + current += s.c_str()[i]; + last_was_delim = false; } - else if(bp_set == 2) - errorstream<<"WARNING: invalid use of unpositioned "<<type<<" in inventory"<<std::endl; + else { + tokens.push_back(current); - 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); + current = ""; + current += s.c_str()[i]; + last_was_delim = false; + } } - 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); - if (type == "textarea") - { - geom.Y = (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y) - (spacing.Y-imgsize.Y); - pos.Y += 15; + else { + if (s.c_str()[i] == delim) { + last_was_delim = true; } - else - { - pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2; - pos.Y -= 15; - geom.Y = 30; + else { + last_was_delim = false; + current += s.c_str()[i]; } + } + } + //push last element + tokens.push_back(current); + } + + return tokens; +} - rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); +void GUIFormSpecMenu::parseSize(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,','); + if (parts.size() == 2) { + v2f invsize; - - fname = f.next_esc(";"); - flabel = f.next_esc(";"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl; - - } + 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 ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl; + + if (m_lock) { + v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize(); + v2u32 delta = current_screensize - m_lockscreensize; - std::string odefault = f.next_esc("]"); - std::string fdefault; + if (current_screensize.Y > m_lockscreensize.Y) + delta.Y /= 2; + else + delta.Y = 0; - // 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); + if (current_screensize.X > m_lockscreensize.X) + delta.X /= 2; else - fdefault = odefault; + delta.X = 0; + + offset = v2s32(delta.X,delta.Y); + + data->screensize = m_lockscreensize; + } + else { + offset = v2s32(0,0); + } - fdefault = unescape_string(fdefault); - flabel = unescape_string(flabel); + 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<s32>( + 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) { - FieldSpec spec = FieldSpec( - narrow_to_wide(fname.c_str()), - narrow_to_wide(flabel.c_str()), - narrow_to_wide(fdefault.c_str()), + if (m_gamedef == 0) { + errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl; + return; + } + + std::vector<std::string> parts = split(element,';'); + + if ((parts.size() == 4) || (parts.size() == 5)) { + std::string location = parts[0]; + std::string listname = parts[1]; + std::vector<std::string> v_pos = split(parts[2],','); + std::vector<std::string> 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="<<location<<", listname="<<listname + <<", pos=("<<pos.X<<","<<pos.Y<<")" + <<", geom=("<<geom.X<<","<<geom.Y<<")" + <<std::endl; + + s32 start_i = 0; + if(startindex != "") + start_i = stoi(startindex); + if(data->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)); + return; + } + errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,';'); + + if ((parts.size() == 3) || (parts.size() == 4)) { + std::vector<std::string> 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<s32> rect = core::rect<s32>(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() ); - // three cases: field name and no label, label and field, label name and no field - gui::IGUIEditBox *e; - if (fname == "") - { - // 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; - e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); - Environment->setFocus(e); + spec.ftype = f_CheckBox; - 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.PressedDown = true; - evt.KeyInput.Char = 0; - evt.KeyInput.Control = 0; - evt.KeyInput.Shift = 0; - e->OnEvent(evt); + gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, + spec.fid, wlabel); + + m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(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<std::string> parts = split(element,';'); + + if (parts.size() == 3) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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="<<name + <<", pos=("<<pos.X<<","<<pos.Y<<")" + <<", geom=("<<geom.X<<","<<geom.Y<<")" + <<std::endl; + if(data->bp_set != 2) + errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl; + m_images.push_back(ImageDrawSpec(name, pos, geom)); + return; + } + + if (parts.size() == 2) { + std::vector<std::string> 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="<<name + <<", pos=("<<pos.X<<","<<pos.Y<<")" + <<std::endl; + if(data->bp_set != 2) + errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl; + m_images.push_back(ImageDrawSpec(name, pos)); + return; + } + errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,';'); + + if (parts.size() == 3) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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="<<name + <<", pos=("<<pos.X<<","<<pos.Y<<")" + <<", geom=("<<geom.X<<","<<geom.Y<<")" + <<std::endl; + if(data->bp_set != 2) + errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl; + m_itemimages.push_back(ImageDrawSpec(name, pos, geom)); + return; + } + errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseButton(parserData* data,std::string element,std::string type) { + std::vector<std::string> parts = split(element,';'); + + if (parts.size() == 4) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + + label = unescape_string(label); + + 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()), + wlabel, + narrow_to_wide(""), + 258+m_fields.size() + ); + spec.ftype = f_Button; + if(type == "button_exit") + spec.is_exit = true; + + Environment->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<std::string> parts = split(element,';'); + + if (parts.size() == 3) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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="<<name + <<", pos=("<<pos.X<<","<<pos.Y<<")" + <<", geom=("<<geom.X<<","<<geom.Y<<")" + <<std::endl; + if(data->bp_set != 2) + errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl; + m_backgrounds.push_back(ImageDrawSpec(name, pos, geom)); + return; + } + errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,';'); + + if ((parts.size() == 5) || (parts.size() == 6)) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; + std::vector<std::string> items = split(parts[3],','); + 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<s32> rect = core::rect<s32>(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(items[i]).c_str() +1); } + else { + std::wstring toadd = narrow_to_wide(items[i].c_str() + 4); + std::string color = items[i].substr(1,3); - if (flabel != "") - { - rect.UpperLeftCorner.Y -= 15; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + e->addItem(toadd.c_str()); + + bool valid_color = true; + + irr::video::SColor toset = getColor(color,valid_color); + + if (valid_color) + e->setItemOverrideColor(i,toset); } } + else { + e->addItem(narrow_to_wide(items[i]).c_str()); + } + } + + if (str_initial_selection != "") + e->setSelected(stoi(str_initial_selection.c_str())-1); - m_fields.push_back(spec); + if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) { + e->setSelected(data->listbox_selections[fname_w]); } - else if(type == "label") - { - v2s32 pos = padding; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (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_esc("]"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl; + m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e)); + m_fields.push_back(spec); + return; + } + errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl; +} - flabel = unescape_string(flabel); - FieldSpec spec = FieldSpec( - narrow_to_wide(""), - narrow_to_wide(flabel.c_str()), - narrow_to_wide(""), - 258+m_fields.size() +void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,';'); + + if (parts.size() == 5) { + std::vector<std::string> v_pos = split(parts[0],','); + std::string name = parts[2]; + std::vector<std::string> 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<s32> rect = core::rect<s32>(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<FieldSpec,gui::IGUIListBox*>(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<std::string> parts = split(element,';'); + + if (parts.size() == 4) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(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() ); - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); - m_fields.push_back(spec); + + 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); } - else if(type == "button" || type == "button_exit") + + 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<std::string> &parts) { + std::string name = parts[0]; + std::string label = parts[1]; + std::string default_val = parts[2]; + + core::rect<s32> rect; + + if(!data->bp_set) + { + rect = core::rect<s32>( + 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"<<std::endl; + + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + 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); + + + 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) { - v2s32 pos = padding; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - pos.Y += (stof(f.next_esc(";")) * (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_esc(";"); - std::string flabel = f.next_esc("]"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl; - - flabel = unescape_string(flabel); - - 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); + rect.UpperLeftCorner.Y -= 15; + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; + Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); } - else if(type == "image_button" || type == "image_button_exit") + } + if (m_use_gettext && (label.length() > 1)) + delete[] wlabel; + + m_fields.push_back(spec); +} + +void GUIFormSpecMenu::parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type) { + + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + + if(data->bp_set != 2) + errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl; + + 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); + + if (type == "textarea") { - v2s32 pos = padding; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - geom.Y = (stof(f.next_esc(";")) * (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_esc(";"); - std::string fname = f.next_esc(";"); - std::string flabel = f.next_esc("]"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl; - - flabel = unescape_string(flabel); - - 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()->getTexture(fimage); - 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); - - m_fields.push_back(spec); + 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); } - else if(type == "item_image_button") + + if (label.length() > 1) { - v2s32 pos = padding; - pos.X += stof(f.next_esc(",")) * (float)spacing.X; - pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - geom.Y = (stof(f.next_esc(";")) * (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_esc(";"); - std::string fname = f.next_esc(";"); - std::string flabel = f.next_esc("]"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl; - IItemDefManager *idef = m_gamedef->idef(); - ItemStack item; - item.deSerialize(fimage, idef); - video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); - std::string tooltip = item.getDefinition(idef).description; - flabel = unescape_string(flabel); - 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() - ); - 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.is_button = true; - rect+=basepos-padding; - spec.rect=rect; - if (tooltip!="") - spec.tooltip=tooltip; - m_fields.push_back(spec); + 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<std::string> parts = split(element,';'); + + 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<std::string> parts = split(element,';'); + + if (parts.size() == 2) { + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + + text = unescape_string(text); + + wchar_t* wlabel = 0; + + if (m_use_gettext) + wlabel = wgettext(text.c_str()); else - { - // Ignore others - std::string ts = f.next_esc("]"); - infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\"" - <<std::endl; + wlabel = (wchar_t*) narrow_to_wide(text.c_str()).c_str(); + + FieldSpec spec = FieldSpec( + narrow_to_wide(""), + wlabel, + narrow_to_wide(""), + 258+m_fields.size() + ); + Environment->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<std::string> parts = split(element,';'); + + if (parts.size() == 2) { + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + + text = unescape_string(text); + std::string label = ""; + + if (m_use_gettext) { + const char* toset = gettext(text.c_str()); + + text = std::string(toset); + } + + for (unsigned int i=0; i < text.length(); i++) { + label += text.c_str()[i]; + label += "\n"; + } + + FieldSpec spec = FieldSpec( + narrow_to_wide(""), + narrow_to_wide(label.c_str()), + narrow_to_wide(""), + 258+m_fields.size() + ); + gui::IGUIStaticText *t = + Environment->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<std::string> parts = split(element,';'); + + if ((parts.size() == 5) || (parts.size() == 7)) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + + label = unescape_string(label); + + FieldSpec spec = FieldSpec( + narrow_to_wide(name.c_str()), + narrow_to_wide(label.c_str()), + narrow_to_wide(image_name.c_str()), + 258+m_fields.size() + ); + spec.ftype = f_Button; + if(type == "image_button_exit") + spec.is_exit = true; + + video::ITexture *texture = 0; + //if there's no gamedef specified try to get direct + //TODO check for possible texture leak + if (m_gamedef != 0) + texture = m_gamedef->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); + return; + } + + errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) { + std::vector<std::string> parts = split(element,';'); + + if ((parts.size() == 4) || (parts.size() == 6)) { + std::vector<std::string> v_pos = split(parts[0],','); + std::string name = parts[1]; + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + return; + } + + std::vector<std::string> parts = split(element,';'); + + if (parts.size() == 5) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(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"<<std::endl; + + IItemDefManager *idef = m_gamedef->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<std::string> parts = split(element,';'); + + if (parts.size() == 3) { + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> 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; + + bool valid_color = false; + + irr::video::SColor color = getColor(color_str,valid_color); + + if (valid_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<std::string> parts = split(element,'[', true); + + 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="<<type<<", data=\""<<description<<"\"" + <<std::endl; +} + + + +void GUIFormSpecMenu::regenerateGui(v2u32 screensize) +{ + parserData mydata; + + //preserve listboxes + for (unsigned int i = 0; i < m_listboxes.size(); i++) { + int selection = m_listboxes[i].second->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<std::string> 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<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); + core::rect<s32> 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() && bp_set != 2) + 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 - 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) + mydata.rect = core::rect<s32>( + 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 = rect; + DesiredRect = mydata.rect; recalculateAbsolutePosition(false); - basepos = getBasePos(); + mydata.basepos = getBasePos(); changeCtype(""); { - v2s32 pos = basepos; + v2s32 pos = mydata.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); + mydata.rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30); wchar_t* text = wgettext("Proceed"); - Environment->addButton(rect, this, 257, text); + Environment->addButton(mydata.rect, this, 257, text); delete[] text; } changeCtype("C"); @@ -684,8 +1614,8 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) for(s32 i=0; i<s.geom.X*s.geom.Y; i++) { - u32 item_i = i + s.start_item_i; - if(item_i >= ilist->getSize()) + s32 item_i = i + s.start_item_i; + if(item_i >= (s32) ilist->getSize()) break; s32 x = (i%s.geom.X) * spacing.X; s32 y = (i/s.geom.X) * spacing.Y; @@ -804,38 +1734,88 @@ void GUIFormSpecMenu::drawMenu() for(u32 i=0; i<m_backgrounds.size(); i++) { const ImageDrawSpec &spec = m_backgrounds[i]; - video::ITexture *texture = - m_gamedef->tsrc()->getTexture(spec.name); - // Image size on screen - core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y); - // Image rectangle on screen - core::rect<s32> 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<s32>(core::position2d<s32>(0,0), - core::dimension2di(texture->getOriginalSize())), - NULL/*&AbsoluteClippingRect*/, colors, true); + video::ITexture *texture = 0; + + if (m_gamedef != 0) + texture = m_gamedef->tsrc()->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<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y); + // Image rectangle on screen + core::rect<s32> 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<s32>(core::position2d<s32>(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<m_boxes.size(); i++) + { + const BoxDrawSpec &spec = m_boxes[i]; + + irr::video::SColor todraw = spec.color; + + todraw.setAlpha(140); + + core::rect<s32> 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; i<m_images.size(); i++) { const ImageDrawSpec &spec = m_images[i]; - video::ITexture *texture = - m_gamedef->tsrc()->getTexture(spec.name); - // Image size on screen - core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y); - // Image rectangle on screen - core::rect<s32> 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<s32>(core::position2d<s32>(0,0), - core::dimension2di(texture->getOriginalSize())), - NULL/*&AbsoluteClippingRect*/, colors, true); + video::ITexture *texture = 0; + + if (m_gamedef != 0) + texture = m_gamedef->tsrc()->getTexture(spec.name); + else + { + texture = driver->getTexture(spec.name.c_str()); + m_Textures.push_back(texture); + } + if (texture != 0) { + const core::dimension2d<u32>& img_origsize = texture->getOriginalSize(); + // Image size on screen + core::rect<s32> imgrect; + + if (spec.scale) + imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y); + else { + + imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height); + } + // Image rectangle on screen + core::rect<s32> 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<s32>(core::position2d<s32>(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; + } } /* @@ -843,6 +1823,9 @@ void GUIFormSpecMenu::drawMenu() */ for(u32 i=0; i<m_itemimages.size(); i++) { + if (m_gamedef == 0) + break; + const ImageDrawSpec &spec = m_itemimages[i]; IItemDefManager *idef = m_gamedef->idef(); ItemStack item; @@ -1025,24 +2008,77 @@ ItemStack GUIFormSpecMenu::verifySelectedItem() return ItemStack(); } -void GUIFormSpecMenu::acceptInput() +void GUIFormSpecMenu::acceptInput(int eventtype) { if(m_text_dst) { std::map<std::string, std::string> fields; - gui::IGUIElement *e; + for(u32 i=0; i<m_fields.size(); i++) { const FieldSpec &s = m_fields[i]; if(s.send) { - if(s.is_button) + if(s.ftype == f_Button) { fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str()); } + else if(s.ftype == f_ListBox) { + std::stringstream ss; + if (eventtype == gui::EGET_LISTBOX_CHANGED) { + ss << "CHG:"; + } + else { + ss << "DCL:"; + } + ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1); + fields[wide_to_narrow(s.fname.c_str())] = ss.str(); + } + else if(s.ftype == f_DropDown) { + // no dynamic cast possible due to some distributions shipped + // without rtti support in irrlicht + IGUIElement * element = getElementFromId(s.fid); + gui::IGUIComboBox *e = NULL; + if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) { + e = static_cast<gui::IGUIComboBox*>(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<gui::IGUITabControl*>(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<gui::IGUICheckBox*>(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 { - e = getElementFromId(s.fid); + IGUIElement* e = getElementFromId(s.fid); if(e != NULL) { fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText()); @@ -1050,6 +2086,7 @@ void GUIFormSpecMenu::acceptInput() } } } + m_text_dst->gotText(fields); } } @@ -1062,13 +2099,20 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (event.KeyInput.PressedDown && (kp == EscapeKey || kp == getKeySetting("keymap_inventory"))) { - quitMenu(); + if (m_allowclose) + quitMenu(); + else + m_text_dst->gotText(narrow_to_wide("MenuQuit")); return true; } if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown) { acceptInput(); - quitMenu(); + + if (m_allowclose) + quitMenu(); + else + m_text_dst->gotText(narrow_to_wide("KeyEnter")); return true; } } @@ -1360,6 +2404,27 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } 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; i<m_fields.size(); i++) + { + FieldSpec &s = m_fields[i]; + // if its a button, set the send field so + // lua knows which button was pressed + if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID())) + { + 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()) { @@ -1371,28 +2436,37 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) return true; } } - if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) + if((event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) || + (event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED)) { - switch(event.GUIEvent.Caller->getID()) - { - case 257: + unsigned int btn_id = event.GUIEvent.Caller->getID(); + + if (btn_id == 257) { acceptInput(); - quitMenu(); + 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; i<m_fields.size(); i++) { FieldSpec &s = m_fields[i]; // if its a button, set the send field so // lua knows which button was pressed - if (s.is_button && s.fid == event.GUIEvent.Caller->getID()) + if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) && + (s.fid == event.GUIEvent.Caller->getID())) { s.send = true; acceptInput(); if(s.is_exit){ - quitMenu(); + if (m_allowclose) + quitMenu(); + else + m_text_dst->gotText(narrow_to_wide("ExitButton")); return true; }else{ s.send = false; @@ -1408,13 +2482,103 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if(event.GUIEvent.Caller->getID() > 257) { acceptInput(); - quitMenu(); + if (m_allowclose) + quitMenu(); + else + m_text_dst->gotText(narrow_to_wide("EditBoxEnter")); // 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; i<m_fields.size(); i++) + { + FieldSpec &s = m_fields[i]; + // if its a button, set the send field so + // lua knows which button was pressed + if ((s.ftype == f_ListBox) && (s.fid == current_id)) + { + s.send = true; + acceptInput(event.GUIEvent.EventType); + s.send=false; + // Restore focus to the full form + Environment->setFocus(this); + } + } + return true; + } + } } return Parent ? Parent->OnEvent(event) : false; } +irr::video::SColor GUIFormSpecMenu::getColor(std::string color,bool& valid_color) { + + if (color == "YLW") { + valid_color = true; + return irr::video::SColor(255,255,255,0); + } + + if (color == "GRN") { + valid_color = true; + return irr::video::SColor(255,34,249,34); + } + + if (color == "LIM") { + valid_color = true; + return irr::video::SColor(255,50,205,50); + } + + if (color == "RED") { + valid_color = true; + return irr::video::SColor(255,255,0,0); + } + + if (color == "ORN") { + valid_color = true; + return irr::video::SColor(255,255,140,0); + } + + if (color == "BLU") { + valid_color = true; + return irr::video::SColor(255,0,0,255); + } + + if (color == "CYN") { + valid_color = true; + return irr::video::SColor(255,0,255,255); + } + + if (color == "BLK") { + valid_color = true; + return irr::video::SColor(255,0,0,0); + } + + if (color == "BRN") { + valid_color = true; + return irr::video::SColor(255,139,69,19); + } + + if (color == "WHT") { + valid_color = true; + return irr::video::SColor(255,255,255,255); + } + + if (color == "GRY") { + valid_color = true; + return irr::video::SColor(255,205,201,201); + } + + valid_color = false; + + return irr::video::SColor(0,0,0,0); +} + |