X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2FguiFormSpecMenu.cpp;h=b162e8e992b2841e937e7e210604fd21260dbe3c;hb=7860097eda449f0bb99971a037967bd338441133;hp=1754422d08637eeee0f2e1126030a1b2d6742e98;hpb=ba78194636a9a498f6979cc21cd39399f23d658a;p=dragonfireclient.git diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 1754422d0..b162e8e99 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 +#include +#include +#include +#include #include "guiFormSpecMenu.h" #include "constants.h" #include "gamedef.h" @@ -28,98 +33,33 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include +#include +#include +#include #include "log.h" #include "tile.h" // ITextureSource +#include "hud.h" // drawItemStack #include "util/string.h" #include "util/numeric.h" - +#include "filesys.h" +#include "gettime.h" #include "gettext.h" -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); +#define MY_CHECKPOS(a,b) \ + if (v_pos.size() != 2) { \ + errorstream<< "Invalid pos for element " << a << "specified: \"" \ + << parts[b] << "\"" << std::endl; \ + return; \ } - 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); +#define MY_CHECKGEOM(a,b) \ + if (v_geom.size() != 2) { \ + errorstream<< "Invalid pos for element " << a << "specified: \"" \ + << parts[b] << "\"" << std::endl; \ + return; \ } - 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 @@ -140,8 +80,19 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_selected_item(NULL), m_selected_amount(0), m_selected_dragging(false), - m_tooltip_element(NULL) + m_listbox_click_fname(), + m_listbox_click_index(-1), + m_listbox_click_time(0), + m_listbox_doubleclick(false), + m_tooltip_element(NULL), + m_allowclose(true), + m_lock(false) { + current_keys_pending.key_down = false; + current_keys_pending.key_up = false; + current_keys_pending.key_enter = false; + current_keys_pending.key_escape = false; + } GUIFormSpecMenu::~GUIFormSpecMenu() @@ -180,431 +131,1467 @@ void GUIFormSpecMenu::removeChildren() } } -void GUIFormSpecMenu::regenerateGui(v2u32 screensize) +void GUIFormSpecMenu::setInitialFocus() { - // Remove children - removeChildren(); - - v2s32 size(100,100); - s32 helptext_h = 15; - core::rect rect; + // Set initial focus according to following order of precedence: + // 1. first empty editbox + // 2. first editbox + // 3. first listbox + // 4. last button + // 5. first focusable (not statictext, not tabheader) + // 6. first child element + + core::list children = getChildren(); + + // in case "children" contains any NULL elements, remove them + for (core::list::Iterator it = children.begin(); + it != children.end();) { + if (*it) + ++it; + else + it = children.erase(it); + } - // 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(); + // 1. first empty editbox + for (core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + if ((*it)->getType() == gui::EGUIET_EDIT_BOX + && (*it)->getText()[0] == 0) { + Environment->setFocus(*it); + return; + } + } - 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("]")); + // 2. first editbox + for (core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + if ((*it)->getType() == gui::EGUIET_EDIT_BOX) { + Environment->setFocus(*it); + return; + } + } + + // 3. first listbox + for (core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + if ((*it)->getType() == gui::EGUIET_LIST_BOX) { + Environment->setFocus(*it); + return; + } + } + + // 4. last button + for (core::list::Iterator it = children.getLast(); + it != children.end(); --it) { + if ((*it)->getType() == gui::EGUIET_BUTTON) { + Environment->setFocus(*it); + return; + } + } + + // 5. first focusable (not statictext, not tabheader) + for (core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + if ((*it)->getType() != gui::EGUIET_STATIC_TEXT && + (*it)->getType() != gui::EGUIET_TAB_CONTROL) { + Environment->setFocus(*it); + return; + } + } + + // 6. first child element + if (children.empty()) + Environment->setFocus(this); + else + Environment->setFocus(*(children.begin())); +} + +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; +} + +bool GUIFormSpecMenu::checkListboxClick(std::wstring wlistboxname, + int eventtype) +{ + // WARNING: BLACK IRRLICHT MAGIC + // Used to fix Irrlicht's subpar reporting of single clicks and double + // clicks in listboxes (gui::EGET_LISTBOX_CHANGED, + // gui::EGET_LISTBOX_SELECTED_AGAIN): + // 1. IGUIListBox::setSelected() is counted as a click. + // Including the initial setSelected() done by parseTextList(). + // 2. Clicking on a the selected item and then dragging for less + // than 500ms is counted as a doubleclick, no matter when the + // item was previously selected (e.g. more than 500ms ago) + + // So when Irrlicht reports a doubleclick, we need to check + // for ourselves if really was a doubleclick. Or just a fake. + + for(unsigned int i=0; i < m_listboxes.size(); i++) { + std::wstring name(m_listboxes[i].first.fname.c_str()); + int selected = m_listboxes[i].second->getSelected(); + if (name == wlistboxname && selected >= 0) { + u32 now = getTimeMs(); + bool doubleclick = + (eventtype == gui::EGET_LISTBOX_SELECTED_AGAIN) + && (name == m_listbox_click_fname) + && (selected == m_listbox_click_index) + && (m_listbox_click_time >= now - 500); + m_listbox_click_fname = name; + m_listbox_click_index = selected; + m_listbox_click_time = now; + m_listbox_doubleclick = doubleclick; + return true; + } + } + return false; +} + +gui::IGUIScrollBar* GUIFormSpecMenu::getListboxScrollbar( + gui::IGUIListBox *listbox) +{ + // WARNING: BLACK IRRLICHT MAGIC + // Ordinarily, due to how formspecs work (recreating the entire GUI + // when something changes), when you select an item in a textlist + // with more items than fit in the visible area, the newly selected + // item is scrolled to the bottom of the visible area. This is + // annoying and breaks GUI designs that use double clicks. + + // This function helps fixing this problem by giving direct access + // to a listbox's scrollbar. This works because CGUIListBox doesn't + // cache the scrollbar position anywhere. + + // If this stops working in a future irrlicht version, consider + // maintaining a local copy of irr::gui::CGUIListBox, possibly also + // fixing the other reasons why black irrlicht magic is needed. + + core::list children = listbox->getChildren(); + for(core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + gui::IGUIElement* child = *it; + if (child && child->getType() == gui::EGUIET_SCROLL_BAR) { + return static_cast(child); + } + } + + verbosestream<<"getListboxScrollbar: WARNING: " + <<"listbox has no scrollbar"< split(const std::string &s, char delim) { + std::vector tokens; + + std::string current = ""; + bool last_was_escape = false; + for(unsigned int i=0; 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{ - invsize.Y = stof(f.next_esc(";")); - f.next_esc("]"); + else if (s.c_str()[i] == '\\'){ + last_was_escape = true; + } + else { + current += s.c_str()[i]; + last_was_escape = false; } - infostream<<"Form size ("<( - 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="<( - 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 "< parts = split(element,','); - v2s32 pos = basepos; - pos.Y = ((m_fields.size()+2)*60); - v2s32 size = DesiredRect.getSize(); - rect = core::rect(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30); - } + 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]); + + if (m_lock) { + v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize(); + v2u32 delta = current_screensize - m_lockscreensize; + + if (current_screensize.Y > m_lockscreensize.Y) + delta.Y /= 2; 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; + 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]); + + 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"< 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; + + std::wstring wlabel = narrow_to_wide(label.c_str()); + + FieldSpec spec = FieldSpec( + narrow_to_wide(name.c_str()), + L"", + wlabel, + 258+m_fields.size() + ); + + spec.ftype = f_CheckBox; + spec.flabel = wlabel; //Needed for displaying text on MSVC + gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, + spec.fid, spec.flabel.c_str()); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + m_checkboxes.push_back(std::pair(spec,e)); + m_fields.push_back(spec); + 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 = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + + if(data->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; + + if(data->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; + + if(data->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()); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + m_fields.push_back(spec); + 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; + + if(data->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],','); + 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, + L"", + L"", + 258+m_fields.size() + ); + + spec.ftype = f_ListBox; + + //now really show list + gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + 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 - { - pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2; - pos.Y -= 15; - geom.Y = 30; + 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 (data->listbox_scroll.find(fname_w) != data->listbox_scroll.end()) { + gui::IGUIScrollBar *scrollbar = getListboxScrollbar(e); + if (scrollbar) { + scrollbar->setPos(data->listbox_scroll[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, + L"", + L"", + 258+m_fields.size() + ); + + spec.ftype = f_DropDown; + spec.send = true; + + //now really show list + gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + 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; +} - rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); +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); + + std::wstring wlabel = narrow_to_wide(label.c_str()); + + FieldSpec spec = FieldSpec( + narrow_to_wide(name.c_str()), + wlabel, + L"", + 258+m_fields.size() + ); + + spec.send = true; + gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid); + + if (spec.fname == data->focused_fieldname) { + 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.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); + m_fields.push_back(spec); + 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); + + std::wstring wlabel = narrow_to_wide(label.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); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + 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); + } + } + + 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); + + std::wstring wlabel = narrow_to_wide(label.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); + + if (spec.fname == data->focused_fieldname) { + 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); + } + } + m_fields.push_back(spec); +} + +void GUIFormSpecMenu::parseField(parserData* data,std::string element,std::string type) { + std::vector 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 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); + 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::wstring text = narrow_to_wide(unescape_string(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) || (parts.size() == 8)) { + 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; + } + + std::string pressed_image_name = ""; + + if ((parts.size() == 8)) { + pressed_image_name = parts[7]; + } + + 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); + if ((parts.size() == 8)) { + pressed_texture = m_gamedef->tsrc()->getTexture(pressed_image_name); + } + } else { + if (fs::PathExists(image_name)) { + texture = Environment->getVideoDriver()->getTexture(image_name.c_str()); + m_Textures.push_back(texture); + } + if (fs::PathExists(pressed_image_name)) { + pressed_texture = Environment->getVideoDriver()->getTexture(pressed_image_name.c_str()); + m_Textures.push_back(pressed_texture); + } + } + if (parts.size() < 8) + pressed_texture = texture; + + gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + e->setUseAlphaChannel(true); + e->setImage(texture); + e->setPressedImage(pressed_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 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()), + L"", + L"", + 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); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + e->setNotClipped(true); + + for (unsigned int i=0; i< buttons.size(); i++) { + wchar_t* wbutton = 0; + + std::wstring wlabel = narrow_to_wide(buttons[i]); //Needed for displaying text on windows + wbutton = (wchar_t*) wlabel.c_str(); + + e->addTab(wbutton,-1); + } + + 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()); + + if (spec.fname == data->focused_fieldname) { + Environment->setFocus(e); + } + + 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,'['); + + // 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) { + mydata.listbox_selections[listboxname] = selection; + } + gui::IGUIScrollBar *scrollbar = getListboxScrollbar(listbox); + if (scrollbar) { + mydata.listbox_scroll[listboxname] = scrollbar->getPos(); + } + } - - fname = f.next_esc(";"); - flabel = f.next_esc(";"); - if(bp_set != 2) - errorstream<<"WARNING: invalid use of positioned "<getFocus(); + if (focused_element && focused_element->getParent() == this) { + s32 focused_id = focused_element->getID(); + if (focused_id > 257) { + for (u32 i=0; iresolveText(odefault); - else - fdefault = odefault; + mydata.size= v2s32(100,100); + mydata.helptext_h = 15; + mydata.screensize = screensize; - fdefault = unescape_string(fdefault); - flabel = unescape_string(flabel); + // Base position of contents of form + mydata.basepos = getBasePos(); - 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() - ); + // 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; - // 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); + + /* 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(); - if (type == "textarea") - { - e->setMultiLine(true); - e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT); - } else { - irr::SEvent evt; - evt.KeyInput.Key = KEY_END; - evt.EventType = EET_KEY_INPUT_EVENT; - evt.KeyInput.PressedDown = true; - e->OnEvent(evt); - } - if (flabel != "") - { - rect.UpperLeftCorner.Y -= 15; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); - } - } + std::vector elements = split(m_formspec_string,']'); - m_fields.push_back(spec); - } - 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(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"<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_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(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"<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_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(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"<tsrc()->getTextureRaw(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); - } - else if(type == "item_image_button") - { - 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(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"<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); - } - else - { - // Ignore others - std::string ts = f.next_esc("]"); - infostream<<"Unknown DrawSpec: type="< 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 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( - 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( + 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(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30); - Environment->addButton(rect, this, 257, wgettext("Proceed")); + 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"); } @@ -620,6 +1607,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_tooltip_element->setWordWrap(false); } + + //set initial focus if parser didn't set it + focused_element = Environment->getFocus(); + if (!focused_element + || !isMyChild(focused_element) + || focused_element->getType() == gui::EGUIET_TAB_CONTROL) + setInitialFocus(); } GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const @@ -678,8 +1672,8 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) for(s32 i=0; 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; @@ -692,7 +1686,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) bool selected = m_selected_item && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv && m_selected_item->listname == s.listname - && m_selected_item->i == i; + && m_selected_item->i == item_i; bool hovering = rect.isPointInside(m_pointer); if(phase == 0) @@ -798,38 +1792,88 @@ void GUIFormSpecMenu::drawMenu() for(u32 i=0; itsrc()->getTextureRaw(spec.name); - // 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); + 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 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()->getTextureRaw(spec.name); - // 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); + 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& 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; + } } /* @@ -837,6 +1881,9 @@ void GUIFormSpecMenu::drawMenu() */ for(u32 i=0; iidef(); ItemStack item; @@ -906,11 +1953,20 @@ void GUIFormSpecMenu::drawMenu() 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 != "") - { + 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; iinventoryloc == s.inventoryloc && - m_selected_item->listname == s.listname && - m_selected_item->i == (s32)item_i && - m_selected_amount == stack.count){ - break; - } - delete m_selected_item; - m_selected_item = NULL; - } infostream<<"Client: Changing selected content guess to " <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()) - selection_valid = true; - } - } - } - if(!selection_valid) - { - delete m_selected_item; - m_selected_item = NULL; - m_selected_amount = 0; - m_selected_dragging = false; - } - } // If craftresult is nonempty and nothing else is selected, select it now. if(!m_selected_item) @@ -1011,12 +2027,43 @@ void GUIFormSpecMenu::updateSelectedItem() // If craftresult is selected, keep the whole stack selected if(m_selected_item && m_selected_item->listname == "craftresult") { - Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); - assert(inv); - InventoryList *list = inv->getList(m_selected_item->listname); - assert(list); - m_selected_amount = list->getItem(m_selected_item->i).count; + 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() @@ -1024,19 +2071,93 @@ void GUIFormSpecMenu::acceptInput() if(m_text_dst) { std::map fields; - gui::IGUIElement *e; + + 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 { - 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()); @@ -1044,10 +2165,46 @@ void GUIFormSpecMenu::acceptInput() } } } + m_text_dst->gotText(fields); } } +bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) +{ + // Fix Esc/Return key being eaten by checkboxen and listboxen + if(event.EventType==EET_KEY_INPUT_EVENT) + { + KeyPress kp(event.KeyInput); + if (kp == EscapeKey || kp == getKeySetting("keymap_inventory") + || event.KeyInput.Key==KEY_RETURN) + { + gui::IGUIElement *focused = Environment->getFocus(); + if (focused && isMyChild(focused) && + (focused->getType() == gui::EGUIET_LIST_BOX || + focused->getType() == gui::EGUIET_CHECK_BOX)) { + OnEvent(event); + return true; + } + } + } + // Mouse wheel events: send to hovered element instead of focused + if(event.EventType==EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event == EMIE_MOUSE_WHEEL) + { + s32 x = event.MouseInput.X; + s32 y = event.MouseInput.Y; + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint( + core::position2d(x, y)); + if (hovered && isMyChild(hovered)) { + hovered->OnEvent(event); + return true; + } + } + return false; +} + bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) @@ -1056,15 +2213,44 @@ 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) - { + 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(); - quitMenu(); return true; } + } if(event.EventType==EET_MOUSE_INPUT_EVENT && event.MouseInput.Event != EMIE_MOUSE_MOVED) @@ -1354,6 +2540,25 @@ 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; igetID())) + { + s.send = true; + acceptInput(); + s.send = false; + return true; + } + } + } if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) { @@ -1365,33 +2570,40 @@ 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; igetID()) + 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; - // Restore focus to the full form - Environment->setFocus(this); return true; } } @@ -1401,14 +2613,63 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if(event.GUIEvent.Caller->getID() > 257) { - acceptInput(); - quitMenu(); + + 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; iOnEvent(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; +}