Files
oboe/src/dialogxml/widgets/control.cpp
2022-06-30 01:08:47 -04:00

692 lines
21 KiB
C++

/*
* control.cpp
* BoE
*
* Created by Celtic Minstrel on 11/05/09.
*
*/
#include "control.hpp"
#include <sstream>
#include "dialog.hpp"
#include "sounds.hpp"
#include "button.hpp"
#include "render_shapes.hpp"
#include "res_image.hpp"
#include "mathutil.hpp"
#include "prefs.hpp"
#include "cursors.hpp"
void cControl::setText(std::string l){
lbl = l;
}
std::string cControl::getText(){
return lbl;
}
rectangle cControl::getBounds() {
return frame;
}
void cControl::setBounds(rectangle newFrame) {
frame = newFrame;
}
void cControl::relocate(location to) {
frame.offset(to.x - frame.left, to.y - frame.top);
}
void cControl::relocateRelative(location to, cControl* anchor, ePosition h, ePosition v) {
if(anchor == nullptr) anchor = this;
// Determine the anchor point of the relocation
location anchorPoint;
switch(h) {
case POS_ABS: anchorPoint.x = 0; break;
case POS_REL_PLUS: anchorPoint.x = anchor->frame.right; break;
case POS_REL_NEG: anchorPoint.x = anchor->frame.left; to.x = -to.x; break;
case POS_CONT_PLUS: anchorPoint.x = anchor->frame.left; break;
case POS_CONT_NEG: anchorPoint.x = anchor->frame.right; to.x = -to.x; break;
}
switch(v) {
case POS_ABS: anchorPoint.y = 0; break;
case POS_REL_PLUS: anchorPoint.y = anchor->frame.bottom; break;
case POS_REL_NEG: anchorPoint.y = anchor->frame.top; to.y = -to.y; break;
case POS_CONT_PLUS: anchorPoint.y = anchor->frame.top; break;
case POS_CONT_NEG: anchorPoint.y = anchor->frame.bottom; to.y = -to.y; break;
}
to.x += anchorPoint.x;
to.y += anchorPoint.y;
relocate(to);
}
const char* xHandlerNotSupported::msg[4] = {
"This control cannot handle click events.\n",
"This control cannot handle focus events.\n",
"This control cannot handle defocus events.\n",
"This control cannot handle scroll events.\n",
};
xHandlerNotSupported::xHandlerNotSupported(eDlogEvt t){
this->evt = t;
}
const char* xHandlerNotSupported::what() const throw() {
assert("A handler not supported message is missing!" && evt < 4);
return msg[evt];
}
xUnsupportedProp::xUnsupportedProp(eFormat prop) throw(){
whichProp = prop;
msg = nullptr;
}
xUnsupportedProp::~xUnsupportedProp() throw(){
if(msg != nullptr) delete msg;
}
const char* xUnsupportedProp::what() const throw(){
if(msg == nullptr){
msg = new char[62];
std::string s;
switch(whichProp){
case TXT_FRAME:
s = "TXT_FRAME";
break;
case TXT_FONT:
s = "TXT_FONT";
break;
case TXT_SIZE:
s = "TXT_SIZE";
break;
case TXT_WRAP:
s = "TXT_WRAP";
break;
case TXT_COLOUR:
s = "TXT_COLOUR";
break;
}
sprintf(msg,"Format property %s not valid for this control.\n",s.c_str());
}
return msg;
}
eKeyMod operator + (eKeyMod lhs, eKeyMod rhs){
if(lhs == rhs) return lhs;
else if(lhs == mod_none) return rhs;
else if(lhs == mod_alt){
if(rhs == mod_shift || rhs == mod_altshift) return mod_altshift;
else if(rhs == mod_ctrl || rhs == mod_altctrl) return mod_altctrl;
else if(rhs == mod_shiftctrl || rhs == mod_all) return mod_all;
else return mod_alt;
}else if(lhs == mod_shift){
if(rhs == mod_alt || rhs == mod_altshift) return mod_altshift;
else if(rhs == mod_ctrl || rhs == mod_shiftctrl) return mod_shiftctrl;
else if(rhs == mod_altctrl || rhs == mod_all) return mod_all;
else return mod_shift;
}else if(lhs == mod_ctrl){
if(rhs == mod_alt || rhs == mod_altctrl) return mod_altctrl;
else if(rhs == mod_shift || rhs == mod_shiftctrl) return mod_shiftctrl;
else if(rhs == mod_altshift || rhs == mod_all) return mod_all;
else return mod_ctrl;
}else return rhs + lhs;
}
eKeyMod operator - (eKeyMod lhs, eKeyMod rhs){
if(lhs == rhs || lhs == mod_none || rhs == mod_all) return mod_none;
else if(rhs == mod_none) return lhs;
else if(lhs == mod_all){
if(rhs == mod_alt) return mod_shiftctrl;
else if(rhs == mod_shift) return mod_altctrl;
else if(rhs == mod_ctrl) return mod_altshift;
else if(rhs == mod_altshift) return mod_ctrl;
else if(rhs == mod_altctrl) return mod_shift;
else if(rhs == mod_shiftctrl) return mod_alt;
else return mod_all;
}else if(lhs == mod_shiftctrl){
if(rhs == mod_shift || rhs == mod_altshift) return mod_ctrl;
else if(rhs == mod_ctrl || rhs == mod_altctrl) return mod_shift;
else return mod_shiftctrl;
}else if(lhs == mod_altctrl){
if(rhs == mod_alt || rhs == mod_altshift) return mod_ctrl;
else if(rhs == mod_ctrl || rhs == mod_shiftctrl) return mod_alt;
else return mod_altctrl;
}else if(lhs == mod_altshift){
if(rhs == mod_alt || rhs == mod_altctrl) return mod_shift;
else if(rhs == mod_shift || rhs == mod_shiftctrl) return mod_alt;
else return mod_altshift;
}else if(lhs == mod_alt && (rhs == mod_altshift || rhs == mod_altctrl))
return mod_none;
else if(lhs == mod_shift && (rhs == mod_altshift || rhs == mod_shiftctrl))
return mod_none;
else if(lhs == mod_ctrl && (rhs == mod_altctrl || rhs == mod_shiftctrl))
return mod_none;
else return lhs;
}
eKeyMod& operator += (eKeyMod&lhs, eKeyMod rhs){
lhs = lhs + rhs;
return lhs;
}
eKeyMod& operator -= (eKeyMod&lhs, eKeyMod rhs){
lhs = lhs - rhs;
return lhs;
}
bool operator== (cKey a, cKey b){
if(a.spec != b.spec) return false;
if(a.mod != b.mod) return false;
return a.spec ? a.k == b.k : (a.c == 0 ? false : a.c == b.c);
}
bool mod_contains(eKeyMod mods, eKeyMod mod) {
if((mods & mod) != 0) return true;
return false;
}
void cControl::show(){
visible = true;
if(labelCtrl) labelCtrl->show();
}
void cControl::hide(){
visible = false;
if(labelCtrl) labelCtrl->hide();
}
bool cControl::isVisible(){
if(!parent || parent->dialogNotToast)
return visible;
else return false;
}
void cControl::setLabelCtrl(cControl* label) {
labelCtrl = label;
}
cKey cControl::getAttachedKey() {
return key;
}
void cControl::setActive(bool active) {
depressed = active;
}
void cControl::setFormat(eFormat prop, short val) {
boost::any newVal;
switch(prop) {
case TXT_WRAP:
newVal = bool(val);
break;
case TXT_FONT:
newVal = eFont(val);
break;
case TXT_SIZE:
newVal = val;
break;
case TXT_FRAME:
newVal = eFrameStyle(val);
break;
case TXT_COLOUR: // Interpret as a shade of grey
newVal = sf::Color{sf::Uint8(val), sf::Uint8(val), sf::Uint8(val)};
break;
}
if(!manageFormat(prop, true, &newVal))
throw xUnsupportedProp(prop);
}
short cControl::getFormat(eFormat prop) {
boost::any curVal;
if(!manageFormat(prop, false, &curVal))
throw xUnsupportedProp(prop);
switch(prop) {
case TXT_WRAP:
return boost::any_cast<bool>(curVal);
case TXT_FONT:
return boost::any_cast<eFont>(curVal);
case TXT_SIZE:
return boost::any_cast<short>(curVal);
case TXT_FRAME:
return boost::any_cast<eFrameStyle>(curVal);
case TXT_COLOUR: // Interpret as a shade of grey
return boost::any_cast<sf::Color>(curVal).toInteger();
}
return 0;
}
bool cControl::canFormat(eFormat prop) {
return manageFormat(prop, false, nullptr);
}
void cControl::setColour(sf::Color clr) {
boost::any newVal = clr;
if(!manageFormat(TXT_COLOUR, true, &newVal))
throw xUnsupportedProp(TXT_COLOUR);
}
sf::Color cControl::getColour() {
boost::any curVal;
if(!manageFormat(TXT_COLOUR, false, &curVal))
throw xUnsupportedProp(TXT_COLOUR);
return boost::any_cast<sf::Color>(curVal);
}
bool cControl::manageFormat(eFormat, bool, boost::any*) {
return false;
}
void cControl::redraw() {
// If there's no parent dialog, we're not responsible for redrawing
if(parent) parent->draw();
}
bool cControl::handleClick(location){
sf::Event e;
bool done = false, clicked = false;
inWindow->setActive();
depressed = true;
while(!done){
redraw();
if(!inWindow->pollEvent(e)) continue;
if(e.type == sf::Event::MouseButtonReleased){
done = true;
location clickPos(e.mouseButton.x, e.mouseButton.y);
clickPos = inWindow->mapPixelToCoords(clickPos);
clicked = frame.contains(clickPos);
depressed = false;
} else if(e.type == sf::Event::MouseMoved){
restore_cursor();
location toPos(e.mouseMove.x, e.mouseMove.y);
toPos = inWindow->mapPixelToCoords(toPos);
depressed = frame.contains(toPos);
}
}
if(get_bool_pref("PlaySounds", true)) {
if(typeid(this) == typeid(cLed*))
play_sound(34);
else play_sound(37);
sf::sleep(time_in_ticks(6));
}
else sf::sleep(time_in_ticks(14));
redraw();
return clicked;
}
static unsigned char applyShift(unsigned char c){
static const char afterShift[] = {
' ', '!', '"', '#', '$', '%', '&', '"', '(', ')', '*', '+', '<', '_', '>', '?',
')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ':', ':', '<', '+', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '^', '_',
'~', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~',
};
if (c<' ')
return ' ';
if (c>=0x7f)
return c;
return afterShift[c - ' '];
}
static unsigned char removeShift(unsigned char c){
static const char afterUnShift[] = {
' ', '1', '\'','3', '4', '5', '7', '\'','9', '0', '8', '=', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';', ';', ',', '=', '.', '/',
'2', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\',']', '6', '-',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\',']', '`',
};
// ASAN: c can be called with 0 by cControl::getAttachedKeyDescription()
if (c<' ')
return ' ';
if (c>=0x7f)
return c;
return afterUnShift[c - ' '];
}
std::string cControl::getAttachedKeyDescription() {
std::string s;
if(key.spec) {
auto mod = key.mod;
std::string keyName;
switch(key.k) {
case key_left: keyName = "left"; break;
case key_right: keyName = "right"; break;
case key_up: keyName = "up"; break;
case key_down: keyName = "down"; break;
case key_esc: keyName = "esc"; break;
case key_enter: keyName = "enter"; break;
case key_tab: keyName = "tab"; break;
case key_help: keyName = "help"; break;
case key_bsp: keyName = "backspace"; break;
case key_del: keyName = "delete"; break;
case key_home: keyName = "home"; break;
case key_end: keyName = "end"; break;
case key_pgup: keyName = "pgup"; break;
case key_pgdn: keyName = "pgdn"; break;
case key_insert: keyName = "insert"; break;
case key_copy: keyName = "c"; mod += mod_ctrl; break;
case key_cut: keyName = "x"; mod += mod_ctrl; break;
case key_paste: keyName = "v"; mod += mod_ctrl; break;
case key_selectall: keyName = "a"; mod += mod_ctrl; break;
case key_undo: keyName = "z"; mod += mod_ctrl; break;
case key_redo: keyName = "y"; mod += mod_ctrl; break;
case key_top: keyName = "home"; mod += mod_ctrl; break;
case key_bottom: keyName = "end"; mod += mod_ctrl; break;
#ifdef __APPLE__
case key_word_left: keyName = "left"; mod += mod_alt; break;
case key_word_right: keyName = "right"; mod += mod_alt; break;
case key_word_bsp: keyName = "backspace"; mod += mod_alt; break;
case key_word_del: keyName = "delete"; mod += mod_alt; break;
#else
case key_word_left: keyName = "left"; mod += mod_ctrl; break;
case key_word_right: keyName = "right"; mod += mod_ctrl; break;
case key_word_bsp: keyName = "backspace"; mod += mod_ctrl; break;
case key_word_del: keyName = "delete"; mod += mod_ctrl; break;
#endif
}
if(mod_contains(mod, mod_ctrl)) s += '^';
if(mod_contains(mod, mod_alt)) s = '#';
if(mod_contains(mod, mod_shift)) s += '$';
} else {
unsigned char c = key.c;
if(mod_contains(key.mod, mod_shift)) c = applyShift(c);
else c = removeShift(c);
if(mod_contains(key.mod, mod_ctrl)) s += '^';
if(mod_contains(key.mod, mod_alt)) s = '#';
if(c == ' ') s += "space";
else s += c;
}
return s;
}
void cControl::attachKey(cKey key){
this->key = key;
}
void cControl::detachKey(){
this->key.spec = false;
this->key.c = 0;
}
cControl::cControl(eControlType t, cDialog& p) : parent(&p), inWindow(&p.win), type(t), visible(true), key({false, 0, mod_none}), frameStyle(FRM_INSET) {}
cControl::cControl(eControlType t, sf::RenderWindow& p) : parent(nullptr), inWindow(&p), type(t), visible(true), key({false, 0, mod_none}), frameStyle(FRM_INSET) {}
void cControl::attachClickHandler(std::function<bool(cDialog&,std::string,eKeyMod)> f) {
if(!f) {
attachEventHandler<EVT_CLICK>(nullptr);
return;
}
attachEventHandler<EVT_CLICK>([f](cDialog& me, std::string id, eKeyMod mods) {
f(me, id, mods);
});
}
void cControl::attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) {
if(!f) {
attachEventHandler<EVT_FOCUS>(nullptr);
attachEventHandler<EVT_DEFOCUS>(nullptr);
return;
}
using namespace std::placeholders;
attachEventHandler<EVT_FOCUS>([f](cDialog& me, std::string id) {
f(me, id, false);
});
attachEventHandler<EVT_DEFOCUS>(std::bind(f, _1, _2, true));
}
bool cControl::triggerClickHandler(cDialog& dlg, std::string id, eKeyMod mods){
triggerEvent<EVT_CLICK>(dlg, id, mods);
return true;
}
bool cControl::triggerFocusHandler(cDialog& dlg, std::string id, bool losing){
if(losing) return triggerEvent<EVT_DEFOCUS>(dlg, id);
triggerEvent<EVT_FOCUS>(dlg, id);
return true;
}
void cControl::drawFrame(short amt, eFrameStyle frameStyle){
if(frameStyle == FRM_NONE) return;
// dk_gray had a 0..65535 component of 12287, and med_gray had a 0..65535 component of 24574
static sf::Color lt_gray = {224,224,224},dk_gray = {48,48,48};
rectangle rect = frame, ul_rect;
inWindow->setActive();
rect.inset(-amt,-amt);
ul_rect = rect;
if(frameStyle == FRM_OUTSET) {
ul_rect.right -= 1;
ul_rect.bottom -= 1;
} else if(frameStyle == FRM_INSET) {
ul_rect.top += 1;
ul_rect.left += 1;
}
frame_rect(*inWindow, rect, dk_gray);
if(frameStyle == FRM_OUTSET || frameStyle == FRM_INSET) {
clip_rect(*inWindow, ul_rect);
frame_rect(*inWindow, rect, lt_gray);
undo_clip(*inWindow);
} else if(frameStyle == FRM_DOUBLE) {
rect.inset(-amt, -amt);
frame_rect(*inWindow, rect, dk_gray);
}
}
std::string cControl::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
std::string tagName, id;
who.GetValue(&tagName);
Iterator<Attribute> attr;
Iterator<Node> node;
std::set<std::string> foundAttrs;
std::multiset<std::string> foundNodes;
int width = 0, height = 0;
rectangle frame;
for(attr = attr.begin(&who); attr != attr.end(); attr++){
std::string attrName = attr->Name();
foundAttrs.insert(attrName);
if(attrName == "name") attr->GetValue(&id);
else if(attrName == "top") attr->GetValue(&frame.top);
else if(attrName == "left") attr->GetValue(&frame.left);
else if(attrName == "width") attr->GetValue(&width);
else if(attrName == "height") attr->GetValue(&height);
else if(!parseAttribute(*attr, tagName, fname))
throw xBadAttr(tagName, attrName, attr->Row(), attr->Column(), fname);
}
std::string text;
for(node = node.begin(&who); node != node.end(); node++){
int type = node->Type();
std::string nodeTag;
if(type == TiXmlNode::ELEMENT)
nodeTag = node->Value();
if(type == TiXmlNode::COMMENT) continue;
else if(!parseContent(*node, foundNodes.count(nodeTag), tagName, fname, text)) {
std::string val = nodeTag.empty() ? nodeTag : xBadVal::CONTENT;
throw xBadVal(tagName, xBadVal::CONTENT, val, node->Row(), node->Column(), fname);
}
foundNodes.insert(nodeTag);
}
setText(text);
location bestSz = getPreferredSize();
frame.width() = width > 0 ? width : bestSz.x;
frame.height() = height > 0 ? height : bestSz.y;
setBounds(frame);
validatePostParse(who, fname, foundAttrs, foundNodes);
return id;
}
bool cControl::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
std::string name;
attr.GetName(&name);
// Relative positioning
if(name == "relative") {
static auto space = " \t";
std::string rel = attr.Value();
const xBadVal err(tagName, name, rel, attr.Row(), attr.Column(), fname);
size_t border = rel.find_first_of(space);
if(border != std::string::npos) {
size_t border_end = rel.find_last_of(space);
// Error if any of [border, border_end] are not spaces
for(size_t i = border + 1; i < border_end; i++) {
if(rel[i] != ' ' && rel[i] != '\t') throw err;
}
std::string h = rel.substr(0, border), v = rel.substr(border_end + 1);
if(h == "abs") horz = POS_ABS;
else if(h == "pos") horz = POS_REL_PLUS;
else if(h == "neg") horz = POS_REL_NEG;
else if(h == "pos-in") horz = POS_CONT_PLUS;
else if(h == "neg-in") horz = POS_CONT_NEG;
else throw err;
if(v == "abs") vert = POS_ABS;
else if(v == "pos") vert = POS_REL_PLUS;
else if(v == "neg") vert = POS_REL_NEG;
else if(v == "pos-in") vert = POS_CONT_PLUS;
else if(v == "neg-in") vert = POS_CONT_NEG;
else throw err;
}
else if(rel == "abs") horz = vert = POS_ABS;
else if(rel == "pos") horz = vert = POS_REL_PLUS;
else if(rel == "neg") horz = vert = POS_REL_NEG;
else if(rel == "pos-in") horz = vert = POS_CONT_PLUS;
else if(rel == "neg-in") horz = vert = POS_CONT_NEG;
else throw err;
return true;
}
if(name == "anchor") {
anchor = attr.Value();
return true;
}
if(name == "rel-anchor") {
std::string val = attr.Value();
if(val == "next") anchor = "$$next$$";
else if(val == "prev") anchor = "$$prev$$";
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
// Colour and formatting, if supported
if(name == "framed" && canFormat(TXT_FRAME)) {
std::string val;
attr.GetValue(&val);
if(val == "true") setFormat(TXT_FRAME, FRM_SOLID);
else if(val == "false") setFormat(TXT_FRAME, FRM_NONE);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "outline" && canFormat(TXT_FRAME)) {
std::string val;
attr.GetValue(&val);
if(val == "none") setFormat(TXT_FRAME, FRM_NONE);
else if(val == "solid") setFormat(TXT_FRAME, FRM_SOLID);
else if(val == "inset") setFormat(TXT_FRAME, FRM_INSET);
else if(val == "outset") setFormat(TXT_FRAME, FRM_OUTSET);
else if(val == "double") setFormat(TXT_FRAME, FRM_DOUBLE);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "font" && canFormat(TXT_FONT)) {
std::string val;
attr.GetValue(&val);
if(val == "plain") setFormat(TXT_FONT, FONT_PLAIN);
else if(val == "bold") setFormat(TXT_FONT, FONT_BOLD);
else if(val == "dungeon") setFormat(TXT_FONT, FONT_DUNGEON);
else if(val == "maidenword") setFormat(TXT_FONT, FONT_MAIDWORD);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "size" && canFormat(TXT_SIZE)) {
std::string val;
attr.GetValue(&val);
if(val == "small") setFormat(TXT_SIZE, 10);
else if(val == "large") setFormat(TXT_SIZE, 12);
else if(val == "title") setFormat(TXT_SIZE, 18);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if(name == "wrap" && canFormat(TXT_WRAP)) {
std::string val;
attr.GetValue(&val);
if(val == "true") setFormat(TXT_WRAP, true);
else if(val == "false") setFormat(TXT_WRAP, false);
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
return true;
}
if((name == "color" || name == "colour") && canFormat(TXT_COLOUR)) {
std::string val = attr.Value();
if(val == "link") {
// TODO: Would be nice if it could work for other backgrounds too...
if(parent->getBg() == cDialog::BG_DARK)
setColour({0x7f, 0xd7, 0xFF});
if(parent->getBg() == cDialog::BG_LIGHT)
setColour({0x00, 0x00, 0xFF});
} else try {
sf::Color clr = parseColor(val);
setColour(clr);
} catch(int) {
throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
}
return true;
}
return false;
}
bool cControl::parseContent(ticpp::Node&, int, std::string, std::string, std::string&) {
return false;
}
void cControl::validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>&) {
if(!attrs.count("left")) throw xMissingAttr(elem.Value(), "left", elem.Row(), elem.Column(), fname);
if(!attrs.count("top")) throw xMissingAttr(elem.Value(), "top", elem.Row(), elem.Column(), fname);
if(attrs.count("relative") && !attrs.count("anchor") && !attrs.count("rel-anchor")) {
// If relative is specified, an anchor is required... unless it's abs or neg
if((horz != POS_ABS && horz != POS_REL_NEG) || (vert != POS_ABS && vert != POS_REL_NEG))
throw xMissingAttr(elem.Value(), "anchor", elem.Row(), elem.Column(), fname);
}
if(attrs.count("anchor") && attrs.count("rel-anchor"))
throw xBadAttr(elem.Value(), "(rel-)anchor", elem.Row(), elem.Column(), fname);
}
cControl::~cControl() {}
eControlType cControl::getType(){
return type;
}
void cControl::setTextToNum(long long what){
std::ostringstream sout;
sout << what;
setText(sout.str());
}
long long cControl::getTextAsNum(){
std::istringstream sin(getText());
long long n;
sin >> n;
return n;
}
bool cControl::hasKey(){
if(key.spec) return true;
return key.c != 0;
}
cControl::storage_t cControl::store() {
storage_t storage;
storage["text"] = lbl;
storage["visible"] = visible;
return storage;
}
void cControl::restore(storage_t to) {
if(to.find("text") != to.end())
lbl = boost::any_cast<std::string>(to["text"]);
else lbl = "";
if(to.find("visible") != to.end())
boost::any_cast<bool>(to["visible"]) ? show() : hide();
}