diff --git a/src/dialogxml/button.cpp b/src/dialogxml/button.cpp index 20a3a2b92..3798b096c 100644 --- a/src/dialogxml/button.cpp +++ b/src/dialogxml/button.cpp @@ -50,8 +50,8 @@ cButton::cButton(cDialog& parent) : textClr(parent.getDefTextClr()), fromList("none") {} -cButton::cButton(cDialog* parent,eControlType t) : - cControl(t,*parent), +cButton::cButton(cDialog& parent,eControlType t) : + cControl(t,parent), fromList("none"), wrapLabel(false) {/* This constructor is only called for LEDs. */} @@ -116,6 +116,162 @@ sf::Color cButton::getColour() throw(xUnsupportedProp) { return textClr; } +std::string cButton::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + int width = 0, height = 0; + bool foundType = false, foundTop = false, foundLeft = false; // required attributes + bool foundKey = false; + std::string keyMod, keyMain; + int keyModRow, keyModCol, keyMainRow, keyMainCol; + rectangle frame; + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "wrap"){ + std::string val; + attr->GetValue(&val); + if(val == "true") setFormat(TXT_WRAP, true); + }else if(name == "type"){ + std::string val; + foundType = true; + attr->GetValue(&val); + if(val == "small") + setBtnType(BTN_SM); + else if(val == "regular") + setBtnType(BTN_REG); + else if(val == "large") + setBtnType(BTN_LG); + else if(val == "help") + setBtnType(BTN_HELP); + else if(val == "left") + setBtnType(BTN_LEFT); + else if(val == "right") + setBtnType(BTN_RIGHT); + else if(val == "up") + setBtnType(BTN_UP); + else if(val == "down") + setBtnType(BTN_DOWN); + else if(val == "tiny") + setBtnType(BTN_TINY); + else if(val == "done") + setBtnType(BTN_DONE); + else if(val == "tall") + setBtnType(BTN_TALL); + else if(val == "trait") + setBtnType(BTN_TRAIT); + else if(val == "push") + setBtnType(BTN_PUSH); + }else if(name == "color" || name == "colour"){ + std::string val; + attr->GetValue(&val); + sf::Color clr; + try{ + clr = parseColor(val); + }catch(int){ + throw xBadVal("button",name,val,attr->Row(),attr->Column(),fname); + } + setColour(clr); + }else if(name == "def-key"){ + attr->GetValue(&keyMain); + foundKey = true; + keyMainRow = attr->Row(); + keyMainCol = attr->Column(); + }else if(name == "key-mod"){ + attr->GetValue(&keyMod); + foundKey = true; + keyModRow = attr->Row(); + keyModCol = attr->Column(); + // }else if(name == "fromlist"){ + // attr->GetValue(&fromList); + }else if(name == "top"){ + attr->GetValue(&frame.top), foundTop = true; + }else if(name == "left"){ + attr->GetValue(&frame.left), foundLeft = true; + }else if(name == "width"){ + attr->GetValue(&width); + }else if(name == "height"){ + attr->GetValue(&height); + }else throw xBadAttr("button",name,attr->Row(),attr->Column(),fname); + } + if(parent->getBg() == cDialog::BG_DARK && getBtnType() == BTN_TINY) + setColour(sf::Color::White); + if(!foundType) throw xMissingAttr("button","type",who.Row(),who.Column(),fname); + if(!foundTop) throw xMissingAttr("button","top",who.Row(),who.Column(),fname); + if(!foundLeft) throw xMissingAttr("button","left",who.Row(),who.Column(),fname); + if(foundKey) { + cKey theKey; + try{ + theKey = parseKey(keyMod + " " + keyMain); + }catch(int){ + try { + theKey = parseKey(keyMain); + }catch(int){ + throw xBadVal("button","def-key",keyMain,keyMainRow,keyMainCol,fname); + } + throw xBadVal("button","key-mod",keyMod,keyModRow,keyModCol,fname); + } + attachKey(theKey); + } + switch(getBtnType()){ + case BTN_SM: + frame.right = frame.left + 23; + frame.bottom = frame.top + 23; + break; + case BTN_LG: + frame.right = frame.left + 102; + frame.bottom = frame.top + 23; + break; + case BTN_HELP: + frame.right = frame.left + 16; + frame.bottom = frame.top + 13; + break; + case BTN_TINY: + case BTN_LED: // this should never happen + frame.right = frame.left + 14; + frame.bottom = frame.top + 10; + break; + case BTN_TALL: + case BTN_TRAIT: + frame.right = frame.left + 63; + frame.bottom = frame.top + 40; + break; + case BTN_PUSH: + frame.right = frame.left + 30; + frame.bottom = frame.top + 30; + break; + default: + frame.right = frame.left + 63; + frame.bottom = frame.top + 23; + } + if(width > 0) + frame.right = frame.left + width; + if(height > 0) + frame.bottom = frame.top + height; + setBounds(frame); + std::string content; + for(node = node.begin(&who); node != node.end(); node++){ + std::string val; + int type = node->Type(); + node->GetValue(&val); + if(type == TiXmlNode::ELEMENT && val == "key"){ + // TODO: There's surely a better way to do this + if(content.length() > 0) throw xBadVal("button",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); + // labelWithKey = true; + }else if(type == TiXmlNode::TEXT) + content += dlogStringFilter(val); + else if(type != TiXmlNode::COMMENT) { + val = '<' + val + '>'; + throw xBadVal("button",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); + } + } + setText(content); + return id; +} + // Indices within the buttons array. size_t cButton::btnGW[14] = { 0, // BTN_SM @@ -190,7 +346,7 @@ void cLed::init(){ } } -cLed::cLed(cDialog* parent) : +cLed::cLed(cDialog& parent) : cButton(parent,CTRL_LED), state(led_off), textFont(FONT_BOLD), @@ -282,8 +438,8 @@ void cLed::restore(storage_t to) { else setState(led_off); } -cLedGroup::cLedGroup(cDialog* parent) : - cControl(CTRL_GROUP,*parent), +cLedGroup::cLedGroup(cDialog& parent) : + cControl(CTRL_GROUP,parent), fromList("none") {} cButton::~cButton() {} @@ -332,6 +488,106 @@ eLedState cLed::getState(){ return state; } +std::string cLed::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + int width = 0, height = 0; + bool foundTop = false, foundLeft = false; // requireds + rectangle frame; + if(parent->getBg() == cDialog::BG_DARK) + setColour(sf::Color::White); + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "state"){ + std::string val; + attr->GetValue(&val); + if(val == "red") setState(led_red); + else if(val == "green") setState(led_green); + else if(val == "off") setState(led_off); + else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); + // }else if(name == "fromlist"){ + // attr->GetValue(&fromList); + }else if(name == "font"){ + std::string val; + attr->GetValue(&val); + if(val == "dungeon") + setFormat(TXT_FONT, FONT_DUNGEON); + else if(val == "plain") + setFormat(TXT_FONT, FONT_PLAIN); + else if(val == "bold") + setFormat(TXT_FONT, FONT_BOLD); + else if(val == "maidenword") + setFormat(TXT_FONT, FONT_MAIDWORD); + else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "size"){ + std::string val; + attr->GetValue(&val); + if(val == "large") + setFormat(TXT_SIZE, 12); + else if(val == "small") + setFormat(TXT_SIZE, 10); + else if(val == "title") + setFormat(TXT_SIZE, 18); + else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "color" || name == "colour"){ + std::string val; + attr->GetValue(&val); + sf::Color clr; + try{ + clr = parseColor(val); + }catch(int){ + throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); + } + setColour(clr); + } else if(name == "wrap") { + std::string val; + attr->GetValue(&val); + if(val == "true") + setFormat(TXT_WRAP, true); + else setFormat(TXT_WRAP, false); + }else if(name == "top"){ + attr->GetValue(&frame.top), foundTop = true; + }else if(name == "left"){ + attr->GetValue(&frame.left), foundLeft = true; + }else if(name == "width"){ + attr->GetValue(&width); + }else if(name == "height"){ + attr->GetValue(&height); + }else throw xBadAttr("led",name,attr->Row(),attr->Column(),fname); + } + if(!foundTop) throw xMissingAttr("led","top",who.Row(),who.Column(),fname); + if(!foundLeft) throw xMissingAttr("led","left",who.Row(),who.Column(),fname); + if(width > 0) { + frame.right = frame.left + width; + }else{ + frame.right = frame.left + 14; + } + if(height > 0) { + frame.bottom = frame.top + height; + }else{ + frame.bottom = frame.top + 10; + } + setBounds(frame); + std::string content; + for(node = node.begin(&who); node != node.end(); node++){ + std::string val; + int type = node->Type(); + node->GetValue(&val); + if(type == TiXmlNode::TEXT) + content += dlogStringFilter(val); + else if(type != TiXmlNode::COMMENT) { + val = '<' + val + '>'; + throw xBadVal("led",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); + } + } + setText(content); + return id; +} + void cLedGroup::addChoice(cLed* ctrl, std::string key) { choices[key] = ctrl; if(ctrl->getState() != led_off) @@ -553,3 +809,32 @@ void cLedGroup::restore(storage_t to) { setSelected(boost::any_cast(to["led-select"])); else setSelected(""); } + +std::string cLedGroup::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); +// else if(name == "fromlist") +// attr->GetValue(&fromList); + else throw xBadAttr("group",name,attr->Row(),attr->Column(),fname); + } + for(node = node.begin(&who); node != node.end(); node++){ + std::string val; + int type = node->Type(); + node->GetValue(&val); + if(type == TiXmlNode::ELEMENT && val == "led"){ + auto led = parent->parse(*node); + addChoice(led.second, led.first); + }else if(type != TiXmlNode::COMMENT) { + val = '<' + val + '>'; + throw xBadVal("group",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); + } + } + recalcRect(); + return id; +} diff --git a/src/dialogxml/button.hpp b/src/dialogxml/button.hpp index e23a8a39c..2db15939a 100644 --- a/src/dialogxml/button.hpp +++ b/src/dialogxml/button.hpp @@ -50,6 +50,7 @@ class cButton : public cControl { public: /// @copydoc cDialog::init() static void init(); + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(); void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods); @@ -83,7 +84,7 @@ protected: /// Construct a new button. /// @param parent The parent dialog. /// @param t The type of control. Should be either CTRL_LED or CTRL_BTN. - cButton(cDialog* parent,eControlType t); + cButton(cDialog& parent,eControlType t); private: bool labelWithKey; std::string fromList; @@ -111,6 +112,7 @@ public: /// default toggle-selected action of an LED. /// @return true to indicate the event should continue. static bool noAction(cDialog&,std::string,eKeyMod) {return true;} + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(); void attachFocusHandler(focus_callback_t f) throw(); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods); @@ -121,7 +123,7 @@ public: void restore(storage_t to); /// Create a new LED button. /// @param parent The parent dialog. - explicit cLed(cDialog* parent); + explicit cLed(cDialog& parent); virtual ~cLed(); /// Set the LED's current state,. /// @param to The new state. @@ -169,6 +171,7 @@ class cLedGroup : public cControl { cLedGroup& operator=(cLedGroup& other) = delete; cLedGroup(cLedGroup& other) = delete; public: + std::string parse(ticpp::Element& who, std::string fname); /// @copydoc cControl::attachClickHandler() /// /// The click handler is called whenever an LED in the group is clicked, even if it's the currently selected LED. @@ -209,7 +212,7 @@ public: sf::Color getColour() throw(xUnsupportedProp); /// Create a new LED group. /// @param parent The parent dialog. - explicit cLedGroup(cDialog* parent); + explicit cLedGroup(cDialog& parent); bool isClickable(); bool handleClick(location where); virtual ~cLedGroup(); diff --git a/src/dialogxml/control.hpp b/src/dialogxml/control.hpp index 2b860b13a..6f0ef5656 100644 --- a/src/dialogxml/control.hpp +++ b/src/dialogxml/control.hpp @@ -86,9 +86,14 @@ public: /// to be drawn, handleClick() when a mousedown event is received within the control's bounding rect, and /// triggerClickHandler() if a click occurs, either because handleClick() returns true or because /// a keyboard event is received that should trigger the control. +/// +/// All control subclasses must have a constructor that takes a single cDialog& parameter, +/// in order for the parsing template to work. class cControl { public: using storage_t = std::map; + /// Parses the control from an XML element + virtual std::string parse(ticpp::Element& who, std::string fname) = 0; /// Attach a keyboard shortcut to a control. Pressing the keyboard shortcut is equivalent to clicking the control. /// @param key The desired keyboard shortcut. void attachKey(cKey key); @@ -226,6 +231,16 @@ public: cControl& operator=(cControl& other) = delete; cControl(cControl& other) = delete; protected: + /// Parses an HTML colour code. + /// Recognizes three-digit hex, six-digit hex, and HTML colour names. + /// @param code The colour code to parse. + static sf::Color parseColor(std::string code); + /// Parses a key code. + /// @param what The code to parse. + static cKey parseKey(std::string what); + /// Filters newlines and tabs from a string, leaving spaces intact. + /// @param toFilter The string to filter. + static std::string dlogStringFilter(std::string toFilter); /// The parent dialog of the control. /// May be null, if the control was created via cControl(eControlType,sf::RenderWindow&). cDialog* parent; @@ -253,6 +268,7 @@ protected: /// Intended to be called from handleClick(), where there is usually a minor event loop happening. void redraw(); private: + friend class cDialog; // TODO: This is only so it can access parseColour... hack! eControlType type; }; diff --git a/src/dialogxml/dialog.cpp b/src/dialogxml/dialog.cpp index 985ab1e8c..74b07f654 100644 --- a/src/dialogxml/dialog.cpp +++ b/src/dialogxml/dialog.cpp @@ -33,7 +33,7 @@ const short cDialog::BG_DARK = 5, cDialog::BG_LIGHT = 16; short cDialog::defaultBackground = cDialog::BG_DARK; cDialog* cDialog::topWindow = nullptr; -static std::string generateRandomString(){ +std::string cDialog::generateRandomString(){ // Not bothering to seed, because it doesn't actually matter if it's truly random. // Though, this will be called after srand() is called in main() anyway. int n_chars = rand() % 100; @@ -45,144 +45,7 @@ static std::string generateRandomString(){ return s; } -template<> pair cDialog::parse(Element& who /*pict*/){ - std::pair p; - Iterator attr; - std::string name; - ePicType type; - bool wide = false, tall = false, custom = false, tiny = false; - bool foundTop = false, foundLeft = false, foundType = false, foundNum = false; // required attributes - rectangle frame; - int width = 0, height = 0; - p.second = new cPict(*this); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "type"){ - std::string val; - foundType = true; - attr->GetValue(&val); - if(val == "blank"){ - p.second->setPict(cPict::BLANK, PIC_TER); - foundNum = true; - continue; - }else if(val == "ter") - type = PIC_TER; - else if(val == "teranim") - type = PIC_TER_ANIM; - else if(val == "monst") - type = PIC_MONST; - else if(val == "dlog") - type = PIC_DLOG; - else if(val == "talk") - type = PIC_TALK; - else if(val == "scen") - type = PIC_SCEN; - else if(val == "item") - type = PIC_ITEM; - else if(val == "pc") - type = PIC_PC; - else if(val == "field") - type = PIC_FIELD; - else if(val == "boom") - type = PIC_BOOM; - else if(val == "missile") - type = PIC_MISSILE; - else if(val == "full") - type = PIC_FULL; - else if(val == "map") - type = PIC_TER_MAP; - else if(val == "status") - type = PIC_STATUS; - else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); - if(foundNum) { - pic_num_t wasPic = p.second->getPicNum(); - p.second->setPict(wasPic, type); - } - }else if(name == "custom"){ - std::string val; - attr->GetValue(&val); - if(val == "true") custom = true; - }else if(name == "scaled"){ - std::string val; - attr->GetValue(&val); - if(val == "true") p.second->setFormat(TXT_WRAP, false); - }else if(name == "size"){ - std::string val; - attr->GetValue(&val); - if(val == "wide") wide = true; - else if(val == "tall") tall = true; - else if(val == "large") wide = tall = true; - else if(val == "small") tiny = true; - else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "def-key"){ - std::string val; - attr->GetValue(&val); - // TODO: The modifiers are now in key-mod, so this needs to be updated - try{ - p.second->attachKey(parseKey(val)); - }catch(int){ - throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); - } - }else if(name == "num"){ - pic_num_t newPic; - attr->GetValue(&newPic), foundNum = true; - if(foundType) p.second->setPict(newPic, type); - else p.second->setPict(newPic); - }else if(name == "top"){ - attr->GetValue(&frame.top), foundTop = true; - }else if(name == "left"){ - attr->GetValue(&frame.left), foundLeft = true; - }else if(name == "width"){ - attr->GetValue(&width); - }else if(name == "height"){ - attr->GetValue(&height); - }else if(name == "framed"){ - std::string val; - attr->GetValue(&val); - if(val == "true") p.second->setFormat(TXT_FRAME, true); - else p.second->setFormat(TXT_FRAME, false); - }else throw xBadAttr("pict",name,attr->Row(),attr->Column(),fname); - } - if(!foundType) throw xMissingAttr("pict","type",who.Row(),who.Column(),fname); - if(!foundNum) throw xMissingAttr("pict","num",who.Row(),who.Column(),fname); - if(!foundTop) throw xMissingAttr("pict","top",who.Row(),who.Column(),fname); - if(!foundLeft) throw xMissingAttr("pict","left",who.Row(),who.Column(),fname); - if(wide || tall) { - pic_num_t wasPic = p.second->getPicNum(); - if(wide && !tall && p.second->getPicType() == PIC_MONST) p.second->setPict(wasPic, PIC_MONST_WIDE); - else if(!wide && tall && p.second->getPicType() == PIC_MONST) p.second->setPict(wasPic, PIC_MONST_TALL); - else if(wide && tall){ - if(p.second->getPicType() == PIC_MONST) p.second->setPict(wasPic, PIC_MONST_LG); - else if(p.second->getPicType() == PIC_SCEN) p.second->setPict(wasPic, PIC_SCEN_LG); - else if(p.second->getPicType() == PIC_DLOG) p.second->setPict(wasPic, PIC_DLOG_LG); - } - } else if(tiny && type == PIC_ITEM) { - pic_num_t wasPic = p.second->getPicNum(); - p.second->setPict(wasPic, PIC_TINY_ITEM); - } - frame.right = frame.left; - frame.bottom = frame.top; - if(width > 0 || height > 0 || p.second->getPicType() == PIC_FULL){ - frame.right = frame.left + width; - frame.bottom = frame.top + height; - } - p.second->setBounds(frame); - pic_num_t wasPic = p.second->getPicNum(); - if(custom) { - p.second->setPict(wasPic, p.second->getPicType() + PIC_CUSTOM); - } else p.second->setPict(wasPic, p.second->getPicType()); - // The above line also sets the graphic's bounding rect, if necessary - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -static string dlogStringFilter(string toFilter) { +string cControl::dlogStringFilter(string toFilter) { string filtered; bool found_nl = false; for(char c : toFilter) { @@ -196,108 +59,7 @@ static string dlogStringFilter(string toFilter) { return filtered; } -template<> pair cDialog::parse(Element& who /*text*/){ - pair p; - Iterator attr; - Iterator node; - string name; - int width = 0, height = 0; - bool foundTop = false, foundLeft = false; // top and left are required attributes - rectangle frame; - p.second = new cTextMsg(*this); - if(bg == BG_DARK) p.second->setColour(sf::Color::White); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "framed"){ - std::string val; - attr->GetValue(&val); - if(val == "true") p.second->setFormat(TXT_FRAME, true); - }else if(name == "font"){ - std::string val; - attr->GetValue(&val); - if(val == "dungeon") - p.second->setFormat(TXT_FONT, FONT_DUNGEON); - else if(val == "plain") - p.second->setFormat(TXT_FONT, FONT_PLAIN); - else if(val == "bold") - p.second->setFormat(TXT_FONT, FONT_BOLD); - else if(val == "maidenword") - p.second->setFormat(TXT_FONT, FONT_MAIDWORD); - else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "size"){ - std::string val; - attr->GetValue(&val); - if(val == "large") - p.second->setFormat(TXT_SIZE, 12); - else if(val == "small") - p.second->setFormat(TXT_SIZE, 10); - else if(val == "title") - p.second->setFormat(TXT_SIZE, 18); - else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "color" || name == "colour"){ - std::string val; - attr->GetValue(&val); - sf::Color clr; - try{ - clr = parseColor(val); - }catch(int){ - throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); - } - p.second->setColour(clr); - }else if(name == "def-key"){ - std::string val; - attr->GetValue(&val); - try{ - p.second->attachKey(parseKey(val)); - }catch(int){ - throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); - } - }else if(name == "top"){ - attr->GetValue(&frame.top), foundTop = true; - }else if(name == "left"){ - attr->GetValue(&frame.left), foundLeft = true; - }else if(name == "width"){ - attr->GetValue(&width); - }else if(name == "height"){ - attr->GetValue(&height); -// }else if(name == "fromlist"){ -// attr->GetValue(&p.second->fromList); - }else throw xBadAttr("text",name,attr->Row(),attr->Column(),fname); - } - if(!foundTop) throw xMissingAttr("text","top",who.Row(),who.Column(),fname); - if(!foundLeft) throw xMissingAttr("text","left",who.Row(),who.Column(),fname); - frame.right = frame.left + width; - frame.bottom = frame.top + height; - p.second->setBounds(frame); - string content; - for(node = node.begin(&who); node != node.end(); node++){ - string val; - int type = node->Type(); - node->GetValue(&val); - // TODO: De-magic the | character - if(type == TiXmlNode::ELEMENT && val == "br") content += '|'; // because vertical bar is replaced by a newline when drawing strings - else if(type == TiXmlNode::TEXT) - content += dlogStringFilter(val); - else if(type != TiXmlNode::COMMENT) { - val = '<' + val + '>'; - throw xBadVal("text",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); - } - } - p.second->setText(content); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -/** - * Parses an HTML-style colour string, recognizing three-digit hex, six-digit hex, and HTML colour names. - */ -sf::Color cDialog::parseColor(string what){ +sf::Color cControl::parseColor(string what){ sf::Color clr; if(what[0] == '#'){ unsigned int r,g,b; @@ -347,168 +109,7 @@ sf::Color cDialog::parseColor(string what){ return clr; } -template<> pair cDialog::parse(Element& who /*button*/){ - pair p; - Iterator attr; - Iterator node; - string name; - int width = 0, height = 0; - bool foundType = false, foundTop = false, foundLeft = false; // required attributes - bool foundKey = false; - std::string keyMod, keyMain; - int keyModRow, keyModCol, keyMainRow, keyMainCol; - rectangle frame; - p.second = new cButton(*this); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "wrap"){ - std::string val; - attr->GetValue(&val); - if(val == "true") p.second->setFormat(TXT_WRAP, true); - }else if(name == "type"){ - std::string val; - foundType = true; - attr->GetValue(&val); - if(val == "small") - p.second->setBtnType(BTN_SM); - else if(val == "regular") - p.second->setBtnType(BTN_REG); - else if(val == "large") - p.second->setBtnType(BTN_LG); - else if(val == "help") - p.second->setBtnType(BTN_HELP); - else if(val == "left") - p.second->setBtnType(BTN_LEFT); - else if(val == "right") - p.second->setBtnType(BTN_RIGHT); - else if(val == "up") - p.second->setBtnType(BTN_UP); - else if(val == "down") - p.second->setBtnType(BTN_DOWN); - else if(val == "tiny") - p.second->setBtnType(BTN_TINY); - else if(val == "done") - p.second->setBtnType(BTN_DONE); - else if(val == "tall") - p.second->setBtnType(BTN_TALL); - else if(val == "trait") - p.second->setBtnType(BTN_TRAIT); - else if(val == "push") - p.second->setBtnType(BTN_PUSH); - }else if(name == "color" || name == "colour"){ - std::string val; - attr->GetValue(&val); - sf::Color clr; - try{ - clr = parseColor(val); - }catch(int){ - throw xBadVal("button",name,val,attr->Row(),attr->Column(),fname); - } - p.second->setColour(clr); - }else if(name == "def-key"){ - attr->GetValue(&keyMain); - foundKey = true; - keyMainRow = attr->Row(); - keyMainCol = attr->Column(); - }else if(name == "key-mod"){ - attr->GetValue(&keyMod); - foundKey = true; - keyModRow = attr->Row(); - keyModCol = attr->Column(); -// }else if(name == "fromlist"){ -// attr->GetValue(&p.second->fromList); - }else if(name == "top"){ - attr->GetValue(&frame.top), foundTop = true; - }else if(name == "left"){ - attr->GetValue(&frame.left), foundLeft = true; - }else if(name == "width"){ - attr->GetValue(&width); - }else if(name == "height"){ - attr->GetValue(&height); - }else throw xBadAttr("button",name,attr->Row(),attr->Column(),fname); - } - if(bg == BG_DARK && p.second->getBtnType() == BTN_TINY) p.second->setColour(sf::Color::White); - if(!foundType) throw xMissingAttr("button","type",who.Row(),who.Column(),fname); - if(!foundTop) throw xMissingAttr("button","top",who.Row(),who.Column(),fname); - if(!foundLeft) throw xMissingAttr("button","left",who.Row(),who.Column(),fname); - if(foundKey) { - cKey theKey; - try{ - theKey = parseKey(keyMod + " " + keyMain); - }catch(int){ - try { - theKey = parseKey(keyMain); - }catch(int){ - throw xBadVal("button","def-key",keyMain,keyMainRow,keyMainCol,fname); - } - throw xBadVal("button","key-mod",keyMod,keyModRow,keyModCol,fname); - } - p.second->attachKey(theKey); - } - switch(p.second->getBtnType()){ - case BTN_SM: - frame.right = frame.left + 23; - frame.bottom = frame.top + 23; - break; - case BTN_LG: - frame.right = frame.left + 102; - frame.bottom = frame.top + 23; - break; - case BTN_HELP: - frame.right = frame.left + 16; - frame.bottom = frame.top + 13; - break; - case BTN_TINY: - case BTN_LED: // this should never happen - frame.right = frame.left + 14; - frame.bottom = frame.top + 10; - break; - case BTN_TALL: - case BTN_TRAIT: - frame.right = frame.left + 63; - frame.bottom = frame.top + 40; - break; - case BTN_PUSH: - frame.right = frame.left + 30; - frame.bottom = frame.top + 30; - break; - default: - frame.right = frame.left + 63; - frame.bottom = frame.top + 23; - } - if(width > 0) - frame.right = frame.left + width; - if(height > 0) - frame.bottom = frame.top + height; - p.second->setBounds(frame); - string content; - for(node = node.begin(&who); node != node.end(); node++){ - string val; - int type = node->Type(); - node->GetValue(&val); - if(type == TiXmlNode::ELEMENT && val == "key"){ - // TODO: There's surely a better way to do this - if(content.length() > 0) throw xBadVal("button",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); -// p.second->labelWithKey = true; - }else if(type == TiXmlNode::TEXT) - content += dlogStringFilter(val); - else if(type != TiXmlNode::COMMENT) { - val = '<' + val + '>'; - throw xBadVal("button",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); - } - } - p.second->setText(content); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -cKey cDialog::parseKey(string what){ +cKey cControl::parseKey(string what){ cKey key; key.spec = false; key.c = 0; @@ -564,271 +165,6 @@ cKey cDialog::parseKey(string what){ return key; } -template<> pair cDialog::parse(Element& who /*LED*/){ - pair p; - Iterator attr; - Iterator node; - string name; - int width = 0, height = 0; - bool foundTop = false, foundLeft = false; // requireds - rectangle frame; - p.second = new cLed(this); - if(bg == BG_DARK) p.second->setColour(sf::Color::White); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "state"){ - std::string val; - attr->GetValue(&val); - if(val == "red") p.second->setState(led_red); - else if(val == "green") p.second->setState(led_green); - else if(val == "off") p.second->setState(led_off); - else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); -// }else if(name == "fromlist"){ -// attr->GetValue(&p.second->fromList); - }else if(name == "font"){ - std::string val; - attr->GetValue(&val); - if(val == "dungeon") - p.second->setFormat(TXT_FONT, FONT_DUNGEON); - else if(val == "plain") - p.second->setFormat(TXT_FONT, FONT_PLAIN); - else if(val == "bold") - p.second->setFormat(TXT_FONT, FONT_BOLD); - else if(val == "maidenword") - p.second->setFormat(TXT_FONT, FONT_MAIDWORD); - else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "size"){ - std::string val; - attr->GetValue(&val); - if(val == "large") - p.second->setFormat(TXT_SIZE, 12); - else if(val == "small") - p.second->setFormat(TXT_SIZE, 10); - else if(val == "title") - p.second->setFormat(TXT_SIZE, 18); - else throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "color" || name == "colour"){ - std::string val; - attr->GetValue(&val); - sf::Color clr; - try{ - clr = parseColor(val); - }catch(int){ - throw xBadVal("led",name,val,attr->Row(),attr->Column(),fname); - } - p.second->setColour(clr); - } else if(name == "wrap") { - std::string val; - attr->GetValue(&val); - if(val == "true") - p.second->setFormat(TXT_WRAP, true); - else p.second->setFormat(TXT_WRAP, false); - }else if(name == "top"){ - attr->GetValue(&frame.top), foundTop = true; - }else if(name == "left"){ - attr->GetValue(&frame.left), foundLeft = true; - }else if(name == "width"){ - attr->GetValue(&width); - }else if(name == "height"){ - attr->GetValue(&height); - }else throw xBadAttr("led",name,attr->Row(),attr->Column(),fname); - } - if(!foundTop) throw xMissingAttr("led","top",who.Row(),who.Column(),fname); - if(!foundLeft) throw xMissingAttr("led","left",who.Row(),who.Column(),fname); - if(width > 0) { - frame.right = frame.left + width; - }else{ - frame.right = frame.left + 14; - } - if(height > 0) { - frame.bottom = frame.top + height; - }else{ - frame.bottom = frame.top + 10; - } - p.second->setBounds(frame); - string content; - for(node = node.begin(&who); node != node.end(); node++){ - string val; - int type = node->Type(); - node->GetValue(&val); - if(type == TiXmlNode::TEXT) - content += dlogStringFilter(val); - else if(type != TiXmlNode::COMMENT) { - val = '<' + val + '>'; - throw xBadVal("led",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); - } - } - p.second->setText(content); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -template<> pair cDialog::parse(Element& who /*group*/){ - pair p; - Iterator attr; - Iterator node; - string name; - p.second = new cLedGroup(this); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); -// else if(name == "fromlist") -// attr->GetValue(&p.second->fromList); - else throw xBadAttr("group",name,attr->Row(),attr->Column(),fname); - } - for(node = node.begin(&who); node != node.end(); node++){ - string val; - int type = node->Type(); - node->GetValue(&val); - if(type == TiXmlNode::ELEMENT && val == "led"){ - auto led = parse(*node); - p.second->addChoice(led.second, led.first); - }else if(type != TiXmlNode::COMMENT) { - val = '<' + val + '>'; - throw xBadVal("group",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); - } - } - p.second->recalcRect(); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -template<> pair cDialog::parse(Element& who /*field*/){ - pair p; - Iterator attr; - Iterator node; - string name; - int width = 0, height = 0; - bool foundTop = false, foundLeft = false; // requireds - rectangle frame; - p.second = new cTextField(this); - for(attr = attr.begin(&who); attr != attr.end(); attr++){ - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "type"){ - std::string val; - attr->GetValue(&val); - if(val == "int") - p.second->setInputType(FLD_INT); - else if(val == "uint") - p.second->setInputType(FLD_UINT); - else if(val == "real") - p.second->setInputType(FLD_REAL); - else if(val == "text") - p.second->setInputType(FLD_TEXT); - else throw xBadVal("field",name,val,attr->Row(),attr->Column(),fname); - }else if(name == "top"){ - attr->GetValue(&frame.top), foundTop = true; - }else if(name == "left"){ - attr->GetValue(&frame.left), foundLeft = true; - }else if(name == "width"){ - attr->GetValue(&width); - }else if(name == "height"){ - attr->GetValue(&height); - }else if(name == "tab-order"){ - attr->GetValue(&p.second->tabOrder); - }else throw xBadAttr("field",name,attr->Row(),attr->Column(),fname); - } - if(!foundTop) throw xMissingAttr("field","top",attr->Row(),attr->Column(),fname); - if(!foundLeft) throw xMissingAttr("field","left",attr->Row(),attr->Column(),fname); - frame.right = frame.left + width; - frame.bottom = frame.top + height; - p.second->setBounds(frame); - string content; - for(node = node.begin(&who); node != node.end(); node++){ - string val; - int type = node->Type(); - node->GetValue(&val); - if(type == TiXmlNode::TEXT) - content += dlogStringFilter(val); - else if(type != TiXmlNode::COMMENT) { - val = '<' + val + '>'; - throw xBadVal("field",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); - } - } - p.second->setText(content); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - -// Note: This specialization must come last because it requires the other specializations -template<> pair cDialog::parse(Element& who /*stack*/) { - pair p; - Iterator attr; - Iterator node; - string name; - p.second = new cStack(*this); - for(attr = attr.begin(&who); attr != attr.end(); attr++) { - attr->GetName(&name); - if(name == "name") - attr->GetValue(&p.first); - else if(name == "pages") { - size_t val; - attr->GetValue(&val); - p.second->setPageCount(val); - } else throw xBadAttr("stack",name,attr->Row(),attr->Column(),fname); - } - vector stack; - for(node = node.begin(&who); node != node.end(); node++) { - string val; - int type = node->Type(); - node->GetValue(&val); - if(type == TiXmlNode::ELEMENT) { - if(val == "field") { - auto field = parse(*node); - controls.insert(field); - stack.push_back(field.first); - tabOrder.push_back(field); - } else if(val == "text") { - auto text = parse(*node); - controls.insert(text); - stack.push_back(text.first); - } else if(val == "pict") { - auto pict = parse(*node); - controls.insert(pict); - stack.push_back(pict.first); - } else if(val == "button") { - auto button = parse(*node); - controls.insert(button); - stack.push_back(button.first); - } else if(val == "led") { - auto led = parse(*node); - controls.insert(led); - stack.push_back(led.first); - } else if(val == "group") { - auto group = parse(*node); - controls.insert(group); - stack.push_back(group.first); - } else throw xBadNode(val,node->Row(),node->Column(),fname); - } else if(type != TiXmlNode::COMMENT) - throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); - } - p.second->controls = stack; - p.second->recalcRect(); - if(p.first == ""){ - do{ - p.first = generateRandomString(); - }while(controls.find(p.first) != controls.end()); - } - return p; -} - cDialog::cDialog(cDialog* p) : parent(p) {} cDialog::cDialog(std::string path, cDialog* p) : parent(p) { @@ -865,7 +201,7 @@ void cDialog::loadFromFile(std::string path){ }else if(name == "fore"){ sf::Color clr; try{ - clr = parseColor(val); + clr = cControl::parseColor(val); }catch(int){ throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); } diff --git a/src/dialogxml/dialog.hpp b/src/dialogxml/dialog.hpp index 6f88c037d..3cea4822e 100644 --- a/src/dialogxml/dialog.hpp +++ b/src/dialogxml/dialog.hpp @@ -39,12 +39,10 @@ class cDialog { std::map controls; short bg; sf::Color defTextClr; - template std::pair parse(ticpp::Element& who); - sf::Color parseColor(std::string what); - cKey parseKey(std::string what); sf::RenderWindow win; std::string currentFocus; cDialog* parent; + std::string generateRandomString(); void loadFromFile(std::string path); template void handleTabOrder(std::string& itemHit, Iter begin, Iter end); std::vector> tabOrder; @@ -188,6 +186,22 @@ public: static bool sendInput(cKey key); /// Sets whether to animate graphics in dialogs. static bool doAnimations; + /// Adds a new control described by the passed XML element. + /// @tparam Ctrl The type of control to add. + /// @param who The XML element describing the control. + /// @note It is up to the caller to ensure that that the element + /// passed describes the type of control being requested. + template std::pair parse(ticpp::Element& who) { + std::pair p; + p.second = new Ctrl(*this); + p.first = p.second->parse(who, fname); + if(p.first == ""){ + do{ + p.first = generateRandomString(); + }while(controls.find(p.first) != controls.end()); + } + return p; + } cDialog& operator=(cDialog& other) = delete; cDialog(cDialog& other) = delete; private: diff --git a/src/dialogxml/field.cpp b/src/dialogxml/field.cpp index c1c50adf6..af57e1746 100644 --- a/src/dialogxml/field.cpp +++ b/src/dialogxml/field.cpp @@ -197,8 +197,8 @@ bool cTextField::hasFocus() { return haveFocus; } -cTextField::cTextField(cDialog* parent) : - cControl(CTRL_FIELD,*parent), +cTextField::cTextField(cDialog& parent) : + cControl(CTRL_FIELD,parent), color(sf::Color::Black), insertionPoint(-1), selectionPoint(0), @@ -630,6 +630,63 @@ void cTextField::handleInput(cKey key) { selectionPoint = sp; } +std::string cTextField::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + int width = 0, height = 0; + bool foundTop = false, foundLeft = false; // requireds + rectangle frame; + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "type"){ + std::string val; + attr->GetValue(&val); + if(val == "int") + setInputType(FLD_INT); + else if(val == "uint") + setInputType(FLD_UINT); + else if(val == "real") + setInputType(FLD_REAL); + else if(val == "text") + setInputType(FLD_TEXT); + else throw xBadVal("field",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "top"){ + attr->GetValue(&frame.top), foundTop = true; + }else if(name == "left"){ + attr->GetValue(&frame.left), foundLeft = true; + }else if(name == "width"){ + attr->GetValue(&width); + }else if(name == "height"){ + attr->GetValue(&height); + }else if(name == "tab-order"){ + attr->GetValue(&tabOrder); + }else throw xBadAttr("field",name,attr->Row(),attr->Column(),fname); + } + if(!foundTop) throw xMissingAttr("field","top",attr->Row(),attr->Column(),fname); + if(!foundLeft) throw xMissingAttr("field","left",attr->Row(),attr->Column(),fname); + frame.right = frame.left + width; + frame.bottom = frame.top + height; + setBounds(frame); + std::string content; + for(node = node.begin(&who); node != node.end(); node++){ + std::string val; + int type = node->Type(); + node->GetValue(&val); + if(type == TiXmlNode::TEXT) + content += dlogStringFilter(val); + else if(type != TiXmlNode::COMMENT) { + val = '<' + val + '>'; + throw xBadVal("field",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); + } + } + setText(content); + return id; +} + aTextInsert::aTextInsert(cTextField& in, int at, std::string text) : cAction("insert text"), in(in), at(at), text(text) {} void aTextInsert::undo() { diff --git a/src/dialogxml/field.hpp b/src/dialogxml/field.hpp index bbf22ccdf..a2061e02d 100644 --- a/src/dialogxml/field.hpp +++ b/src/dialogxml/field.hpp @@ -33,6 +33,7 @@ enum eFldType { /// There is no Unicode support. class cTextField : public cControl { public: + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported); /// @copydoc cControl::attachFocusHandler() /// For text fields, this is triggered when it loses or gains the input focus. @@ -54,7 +55,7 @@ public: sf::Color getColour() throw(xUnsupportedProp); /// Create a new editable text field. /// @param parent The parent dialog. - explicit cTextField(cDialog* parent); + explicit cTextField(cDialog& parent); bool isClickable(); virtual ~cTextField(); void draw(); diff --git a/src/dialogxml/message.cpp b/src/dialogxml/message.cpp index 58611a4b9..35cc9df87 100644 --- a/src/dialogxml/message.cpp +++ b/src/dialogxml/message.cpp @@ -73,6 +73,99 @@ short cTextMsg::getFormat(eFormat prop) throw(xUnsupportedProp){ return 0; } +std::string cTextMsg::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + int width = 0, height = 0; + bool foundTop = false, foundLeft = false; // top and left are required attributes + rectangle frame; + if(parent->getBg() == cDialog::BG_DARK) + setColour(sf::Color::White); + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "framed"){ + std::string val; + attr->GetValue(&val); + if(val == "true") setFormat(TXT_FRAME, true); + }else if(name == "font"){ + std::string val; + attr->GetValue(&val); + if(val == "dungeon") + setFormat(TXT_FONT, FONT_DUNGEON); + else if(val == "plain") + setFormat(TXT_FONT, FONT_PLAIN); + else if(val == "bold") + setFormat(TXT_FONT, FONT_BOLD); + else if(val == "maidenword") + setFormat(TXT_FONT, FONT_MAIDWORD); + else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "size"){ + std::string val; + attr->GetValue(&val); + if(val == "large") + setFormat(TXT_SIZE, 12); + else if(val == "small") + setFormat(TXT_SIZE, 10); + else if(val == "title") + setFormat(TXT_SIZE, 18); + else throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "color" || name == "colour"){ + std::string val; + attr->GetValue(&val); + sf::Color clr; + try{ + clr = parseColor(val); + }catch(int){ + throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); + } + setColour(clr); + }else if(name == "def-key"){ + std::string val; + attr->GetValue(&val); + try{ + attachKey(parseKey(val)); + }catch(int){ + throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname); + } + }else if(name == "top"){ + attr->GetValue(&frame.top), foundTop = true; + }else if(name == "left"){ + attr->GetValue(&frame.left), foundLeft = true; + }else if(name == "width"){ + attr->GetValue(&width); + }else if(name == "height"){ + attr->GetValue(&height); +// }else if(name == "fromlist"){ +// attr->GetValue(&p.second->fromList); + }else throw xBadAttr("text",name,attr->Row(),attr->Column(),fname); + } + if(!foundTop) throw xMissingAttr("text","top",who.Row(),who.Column(),fname); + if(!foundLeft) throw xMissingAttr("text","left",who.Row(),who.Column(),fname); + frame.right = frame.left + width; + frame.bottom = frame.top + height; + setBounds(frame); + std::string content; + for(node = node.begin(&who); node != node.end(); node++){ + std::string val; + int type = node->Type(); + node->GetValue(&val); + // TODO: De-magic the | character + if(type == TiXmlNode::ELEMENT && val == "br") content += '|'; // because vertical bar is replaced by a newline when drawing strings + else if(type == TiXmlNode::TEXT) + content += dlogStringFilter(val); + else if(type != TiXmlNode::COMMENT) { + val = '<' + val + '>'; + throw xBadVal("text",xBadVal::CONTENT,content + val,node->Row(),node->Column(),fname); + } + } + setText(content); + return id; +} + cTextMsg::cTextMsg(cDialog& parent) : cControl(CTRL_TEXT,parent), drawFramed(false), diff --git a/src/dialogxml/message.hpp b/src/dialogxml/message.hpp index 09ed29b61..47e480ac7 100644 --- a/src/dialogxml/message.hpp +++ b/src/dialogxml/message.hpp @@ -22,6 +22,7 @@ /// This class can also create a frame for grouping controls or a clickable area. class cTextMsg : public cControl { public: + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(); void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods); diff --git a/src/dialogxml/pict.cpp b/src/dialogxml/pict.cpp index 900809df5..1191a60fc 100644 --- a/src/dialogxml/pict.cpp +++ b/src/dialogxml/pict.cpp @@ -443,6 +443,137 @@ void cPict::advanceAnim() { if(animFrame >= 256) animFrame = 0; } +std::string cPict::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + std::string name, id; + ePicType type; + bool wide = false, tall = false, custom = false, tiny = false; + bool foundTop = false, foundLeft = false, foundType = false, foundNum = false; // required attributes + rectangle frame; + int width = 0, height = 0; + for(attr = attr.begin(&who); attr != attr.end(); attr++){ + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "type"){ + std::string val; + foundType = true; + attr->GetValue(&val); + if(val == "blank"){ + setPict(cPict::BLANK, PIC_TER); + foundNum = true; + continue; + }else if(val == "ter") + type = PIC_TER; + else if(val == "teranim") + type = PIC_TER_ANIM; + else if(val == "monst") + type = PIC_MONST; + else if(val == "dlog") + type = PIC_DLOG; + else if(val == "talk") + type = PIC_TALK; + else if(val == "scen") + type = PIC_SCEN; + else if(val == "item") + type = PIC_ITEM; + else if(val == "pc") + type = PIC_PC; + else if(val == "field") + type = PIC_FIELD; + else if(val == "boom") + type = PIC_BOOM; + else if(val == "missile") + type = PIC_MISSILE; + else if(val == "full") + type = PIC_FULL; + else if(val == "map") + type = PIC_TER_MAP; + else if(val == "status") + type = PIC_STATUS; + else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); + if(foundNum) { + pic_num_t wasPic = getPicNum(); + setPict(wasPic, type); + } + }else if(name == "custom"){ + std::string val; + attr->GetValue(&val); + if(val == "true") custom = true; + }else if(name == "scaled"){ + std::string val; + attr->GetValue(&val); + if(val == "true") setFormat(TXT_WRAP, false); + }else if(name == "size"){ + std::string val; + attr->GetValue(&val); + if(val == "wide") wide = true; + else if(val == "tall") tall = true; + else if(val == "large") wide = tall = true; + else if(val == "small") tiny = true; + else throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); + }else if(name == "def-key"){ + std::string val; + attr->GetValue(&val); + // TODO: The modifiers are now in key-mod, so this needs to be updated + try{ + attachKey(parseKey(val)); + }catch(int){ + throw xBadVal("pict",name,val,attr->Row(),attr->Column(),fname); + } + }else if(name == "num"){ + pic_num_t newPic; + attr->GetValue(&newPic), foundNum = true; + if(foundType) setPict(newPic, type); + else setPict(newPic); + }else if(name == "top"){ + attr->GetValue(&frame.top), foundTop = true; + }else if(name == "left"){ + attr->GetValue(&frame.left), foundLeft = true; + }else if(name == "width"){ + attr->GetValue(&width); + }else if(name == "height"){ + attr->GetValue(&height); + }else if(name == "framed"){ + std::string val; + attr->GetValue(&val); + if(val == "true") setFormat(TXT_FRAME, true); + else setFormat(TXT_FRAME, false); + }else throw xBadAttr("pict",name,attr->Row(),attr->Column(),fname); + } + if(!foundType) throw xMissingAttr("pict","type",who.Row(),who.Column(),fname); + if(!foundNum) throw xMissingAttr("pict","num",who.Row(),who.Column(),fname); + if(!foundTop) throw xMissingAttr("pict","top",who.Row(),who.Column(),fname); + if(!foundLeft) throw xMissingAttr("pict","left",who.Row(),who.Column(),fname); + if(wide || tall) { + pic_num_t wasPic = getPicNum(); + if(wide && !tall && getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_WIDE); + else if(!wide && tall && getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_TALL); + else if(wide && tall){ + if(getPicType() == PIC_MONST) setPict(wasPic, PIC_MONST_LG); + else if(getPicType() == PIC_SCEN) setPict(wasPic, PIC_SCEN_LG); + else if(getPicType() == PIC_DLOG) setPict(wasPic, PIC_DLOG_LG); + } + } else if(tiny && type == PIC_ITEM) { + pic_num_t wasPic = getPicNum(); + setPict(wasPic, PIC_TINY_ITEM); + } + frame.right = frame.left; + frame.bottom = frame.top; + if(width > 0 || height > 0 || getPicType() == PIC_FULL){ + frame.right = frame.left + width; + frame.bottom = frame.top + height; + } + setBounds(frame); + pic_num_t wasPic = getPicNum(); + if(custom) { + setPict(wasPic, getPicType() + PIC_CUSTOM); + } else setPict(wasPic, getPicType()); + // The above line also sets the graphic's bounding rect, if necessary + return id; +} + void cPict::recalcRect() { rectangle bounds = getBounds(); switch(picType) { diff --git a/src/dialogxml/pict.hpp b/src/dialogxml/pict.hpp index 1cbb2285a..ace170597 100644 --- a/src/dialogxml/pict.hpp +++ b/src/dialogxml/pict.hpp @@ -25,6 +25,7 @@ class cPict : public cControl { public: /// @copydoc cDialog::init() static void init(); + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(); void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods); diff --git a/src/dialogxml/scrollbar.cpp b/src/dialogxml/scrollbar.cpp index 15de8a610..0043216b0 100644 --- a/src/dialogxml/scrollbar.cpp +++ b/src/dialogxml/scrollbar.cpp @@ -201,6 +201,12 @@ void cScrollbar::draw() { rect_draw_some_item(scroll_gw, from_rect, *inWindow, draw_rect); } +std::string cScrollbar::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + // TODO: Parse scrollbars + return ""; +} + cControl::storage_t cScrollbar::store() { storage_t storage = cControl::store(); storage["scroll-pos"] = pos; diff --git a/src/dialogxml/scrollbar.hpp b/src/dialogxml/scrollbar.hpp index 09a5231fb..be5e4c9ff 100644 --- a/src/dialogxml/scrollbar.hpp +++ b/src/dialogxml/scrollbar.hpp @@ -32,6 +32,7 @@ class cScrollbar : public cControl { public: /// @copydoc cDialog::init() static void init(); + std::string parse(ticpp::Element& who, std::string fname); /// Create a new scrollbar without a parent dialog. /// @param parent The parent window. explicit cScrollbar(sf::RenderWindow& parent); diff --git a/src/dialogxml/stack.cpp b/src/dialogxml/stack.cpp index c5448de64..532daeae9 100644 --- a/src/dialogxml/stack.cpp +++ b/src/dialogxml/stack.cpp @@ -8,6 +8,9 @@ #include "stack.hpp" #include "field.hpp" +#include "button.hpp" +#include "message.hpp" +#include "pict.hpp" void cStack::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) { onClick = f; @@ -151,3 +154,59 @@ void cStack::fillTabOrder(std::vector& specificTabs, std::vector& reve cStack::cStack(cDialog& parent) : cControl(CTRL_STACK, parent), curPage(0), nPages(0) {} +std::string cStack::parse(ticpp::Element& who, std::string fname) { + using namespace ticpp; + Iterator attr; + Iterator node; + std::string name, id; + for(attr = attr.begin(&who); attr != attr.end(); attr++) { + attr->GetName(&name); + if(name == "name") + attr->GetValue(&id); + else if(name == "pages") { + size_t val; + attr->GetValue(&val); + setPageCount(val); + } else throw xBadAttr("stack",name,attr->Row(),attr->Column(),fname); + } + std::vector stack; + for(node = node.begin(&who); node != node.end(); node++) { + std::string val; + int type = node->Type(); + node->GetValue(&val); + if(type == TiXmlNode::ELEMENT) { + if(val == "field") { + auto field = parent->parse(*node); + parent->add(field.second, field.second->getBounds(), field.first); + stack.push_back(field.first); + // TODO: Add field to tab order + //tabOrder.push_back(field); + } else if(val == "text") { + auto text = parent->parse(*node); + parent->add(text.second, text.second->getBounds(), text.first); + stack.push_back(text.first); + } else if(val == "pict") { + auto pict = parent->parse(*node); + parent->add(pict.second, pict.second->getBounds(), pict.first); + stack.push_back(pict.first); + } else if(val == "button") { + auto button = parent->parse(*node); + parent->add(button.second, button.second->getBounds(), button.first); + stack.push_back(button.first); + } else if(val == "led") { + auto led = parent->parse(*node); + parent->add(led.second, led.second->getBounds(), led.first); + stack.push_back(led.first); + } else if(val == "group") { + auto group = parent->parse(*node); + parent->add(group.second, group.second->getBounds(), group.first); + stack.push_back(group.first); + } else throw xBadNode(val,node->Row(),node->Column(),fname); + } else if(type != TiXmlNode::COMMENT) + throw xBadVal("stack",xBadVal::CONTENT,val,node->Row(),node->Column(),fname); + } + controls = stack; + recalcRect(); + return id; +} + diff --git a/src/dialogxml/stack.hpp b/src/dialogxml/stack.hpp index c1972ef8b..673dadc69 100644 --- a/src/dialogxml/stack.hpp +++ b/src/dialogxml/stack.hpp @@ -42,6 +42,7 @@ class cStack : public cControl { std::vector controls; click_callback_t onClick; public: + std::string parse(ticpp::Element& who, std::string fname); void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported); void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported); bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);