/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file style.cpp The implementation of styles (MscStyle) and contexts (ContextBase).
 * @ingroup libmscgen_files */


#include "msc.h"

using namespace msc;

/** Returns true if value `v` is valid for side type `t` */
bool IsValidSideValue(ESideType t, ESide v)
{
    return ((v==ESide::LEFT || v==ESide::RIGHT) && (t==ESideType::ANY || t==ESideType::LEFT_RIGHT)) ||
                                                     (v==ESide::END && t==ESideType::ANY);
}


/** Create an empty style that contains all possible attributes.*/
MscStyle::MscStyle(EStyleType tt, EColorMeaning cm) : 
    SimpleStyleWithArrow<MscArrowHeads>(tt, cm)
{
    f_vline=f_vfill=true;
    f_solid=f_vspacing=f_indicator=true;
    f_makeroom=f_note=f_lost=f_lsym=true;
    f_alt = false;
    f_side = ESideType::ANY;
    f_tag = true;
    Empty();
}

/** Create an empty style that contains only some of the attributes.
 * @param [in] tt The type of style instance.
 * @param [in] cm How an unqualified "color" attribute shall be interpreted.
 * @param [in] a What type of arrows we accept.
 * @param [in] alt True if the style shall contain aline and atext attributes.
 * @param [in] t True if the style shall contain text attributes.
 * @param [in] l True if the style shall contain line attributes.
 * @param [in] f True if the style shall contain fill attributes.
 * @param [in] s True if the style shall contain shadow attributes.
 * @param [in] vl True if the style shall contain vline attributes.
 * @param [in] so True if the style shall contain the `solid` attribute.
 * @param [in] nu True if the style shall contain the `number` attribute.
 * @param [in] co True if the style shall contain the `compress`/`vspacing` attribute.
 * @param [in] si True if the style shall contain the `side` attribute.
 * @param [in] i True if the style shall contain the `indicator` attribute.
 * @param [in] vf True if the style shall contain vfill attributes.
 * @param [in] mr True if the style shall contain the `makeroom` attribute.
 * @param [in] n True if the style shall contain note attributes.
 * @param [in] lo True if the style shall contain lost.* attributes.
 * @param [in] lsym True if the style shall contain loss symbol attributes (x.*)
 * @param [in] shp True if the style shall contain entity related attributes (shape, shape.size)
 * @param [in] tag True if the style shall contain box tag related attributes (tag.*)
 */
MscStyle::MscStyle(EStyleType tt, EColorMeaning cm, EArcArrowType a, 
                   bool alt, bool t, bool l, bool f, bool s, bool vl,
                   bool so, bool nu, bool co, ESideType si, bool i, bool vf, bool mr, bool n, 
                   bool lo, bool lsym, bool shp, bool tag) :
    SimpleStyleWithArrow<MscArrowHeads>(tt, cm, a, t, l, f, s, nu, shp),
    lost_arrow(EArcArrowType::ARROW), 
    f_vline(vl), f_vfill(vf), 
    f_solid(so), f_vspacing(co), f_side(si),
    f_indicator(i), f_makeroom(mr), f_note(n), f_alt(alt),
    f_lost(lo), f_lsym(lsym), f_tag(tag)
{
    Empty();
}

/** Make a style complete by setting the default values - but leave text & lost attributes empty.
 * Default attributes are half solid, right side, no numbering, no compress, indicator yes,
 * makeroom yes and the default of other attribute classes (specified there).
 * We skip text styles, since we have a global text attribute per chart and 
 * we set the default there. We also skip loss attributes as they do not have to be
 * fully specified.
 */
void MscStyle::MakeCompleteButText() 
{
    SimpleStyleWithArrow<MscArrowHeads>::MakeCompleteButText();
    if (f_vline) vline.MakeComplete();
    else vline.Empty();
    if (f_vfill) vfill.MakeComplete();
    else vfill.Empty();
    //no change to aline and atext - those may remain incomplete
    solid = 128;
    if (f_side != ESideType::NO) side = ESide::RIGHT;
    else side.reset();
    if (f_vspacing) vspacing = 0;
    else vspacing.reset();
    if (f_indicator) indicator = true;
    else indicator.reset();
    if (f_makeroom) makeroom = true;
    else makeroom.reset();
    if (f_note) note.MakeComplete();
    else note.Empty();
    //no change to lost fields - those may remain incomplete, 
    lsym_line.MakeComplete(); 
    lsym_line.color = ColorType(255, 0, 0);
    lsym_line.width = 2;
    lsym_size = EArrowSize::NORMAL;
    tag_line.MakeComplete();
    tag_fill.MakeComplete();
    //tag_text unchanged
}

/** Make the style empty be unsetting all attributes it contains.*/
void MscStyle::Empty()
{
    SimpleStyleWithArrow<MscArrowHeads>::Empty();
    vline.Empty();
    vfill.Empty();
    aline.Empty();
    atext.Empty();
    solid.reset();
    side.reset();
    vspacing.reset();
    indicator.reset();
    makeroom.reset();
    note.Empty();
    lost_line.Empty();
    lost_arrow.Empty();
    lost_text.Empty();
    lsym_line.Empty();
    lsym_size.reset();
    tag_line.Empty();
    tag_fill.Empty();
    tag_text.Empty();
}

/** Make the style empty be unsetting all attributes it contains.*/
bool MscStyle::IsEmpty() const noexcept 
{
    return SimpleStyleWithArrow<MscArrowHeads>::IsEmpty()
        && (!f_vline || vline.IsEmpty())
        && (!f_vfill || vfill.IsEmpty())
        && (!f_alt || (aline.IsEmpty() && atext.IsEmpty()))
        && (!f_solid || !solid)
        && (f_side== ESideType::NO || !side)
        && (!f_vspacing || !vspacing)
        && (f_indicator ||!indicator)
        && (f_makeroom || !makeroom)
        && (!f_note || note.IsEmpty())
        && (!f_lost || (lost_line.IsEmpty() && lost_arrow.read().IsEmpty() && lost_text.IsEmpty()))
        && (!f_lsym || (lsym_line.IsEmpty() && (!lsym_size || !lsym_size)))
        && (!f_tag || (tag_line.IsEmpty() && tag_fill.IsEmpty() && tag_text.IsEmpty()));
}


/** Merge another style to us by copying the value of those attributes which are contained by both and are set in `toadd`.*/
Style & MscStyle::operator +=(const Style &t)
{
    SimpleStyleWithArrow<MscArrowHeads>::operator+=(t);
    const MscStyle *p = static_cast<const MscStyle*>(&t);
    if (p==nullptr) return *this;

    if (p->f_vline && f_vline) vline += p->vline;
    if (p->f_vfill && f_vfill) vfill += p->vfill;
    if (p->f_alt && f_alt) { 
        aline += p->aline; 
        atext += p->atext; 
    }
    if (p->f_solid && f_solid && p->solid) solid = p->solid;
    if (p->f_side != ESideType::NO && p->side && IsValidSideValue(f_side, *p->side)) side = p->side;
    if (p->f_vspacing && f_vspacing && p->vspacing) vspacing = p->vspacing;
    if (p->f_indicator && f_indicator && p->indicator) indicator = p->indicator;
    if (p->f_makeroom && f_makeroom && p->makeroom) makeroom = p->makeroom;
    if (p->f_note && f_note) note += p->note;
    if (p->f_lost && f_lost) {
        lost_line += p->lost_line;
        lost_arrow += p->lost_arrow;
        lost_text += p->lost_text;
    }
    if (p->f_lsym && f_lsym) {
        lsym_line += p->lsym_line;
        if (p->lsym_size) lsym_size = p->lsym_size;
    }
    if (p->f_tag && f_tag) {
        tag_line += p->tag_line;
        tag_fill += p->tag_fill;
        tag_text += p->tag_text;
    }
    return *this;
}

/** Possible values for the 'side' attribute.*/
template<> const char EnumEncapsulator<ESide>::names[][ENUM_STRING_LEN] =
    {"invalid", "left", "right", "end", ""};
/** Possible values for the 'side' attribute.*/
template<> const char * const EnumEncapsulator<ESide>::descriptions[] =
    {nullptr, "Read from left/place on left side.", "Read from right/place on right side.", 
    "Read from bottom/place to document end.", ""};


/** Apply an attribute to us.
 * Generate an error if the we recognize the attribute, but bad value.
 * Do not recognize attribute names that correspond to attributes we do not contain.*/
bool MscStyle::AddAttribute(const Attribute &a, Chart *c)
{
    MscChart * chart = dynamic_cast<MscChart*>(c);
    _ASSERT(chart);
    if (a.type == EAttrType::STYLE) {
        const char *newname = a.name == "emphasis"?"box":"emptybox";
        if (a.name == "emphasis" || a.name == "emptyemphasis") {
            chart->Error.Warning(a, false, "Stylename '" + a.name + "' is deprecated, using " + newname + " instead.");
            operator +=(chart->MyCurrentContext().styles[newname].read());
            return true;
        }
        if (chart->SkipContent()) return true; //if we parse a procedure silently accept any style
        if (chart->MyCurrentContext().styles.find(a.name) == chart->MyCurrentContext().styles.end()) {
            a.InvalidStyleError(chart->Error);
            return true;
        }
        operator +=(chart->MyCurrentContext().styles[a.name].read());
        return true;
    }
    if (a.Is("color") && color_meaning==EColorMeaning::LINE_VLINE_TEXT) {
        unsigned num = 0;
        if (f_line)
            num += line.AddAttribute(a, chart, type);
        if (f_text)
            num += text.AddAttribute(a, chart, type);
        if (f_vline)
            num += vline.AddAttribute(a, chart, type);
        return num;
    }
    if (f_text) {
        if (a.StartsWith("text") || a.Is("ident"))
            return text.AddAttribute(a, chart, type);
        if (a.Is("textcolor") || a.Is("textcolour")) {
            chart->Error.WarnMscgenAttr(a, false, "text.color");
            return text.AddAttribute(Attribute("text.color", a), chart, type);
        }
        if (a.Is("textbgcolor") || a.Is("textbgcolour")) {
            chart->Error.WarnMscgenAttr(a, false, "text.bgcolor");
            return text.AddAttribute(Attribute("text.bgcolor", a), chart, type);
        }
    }
    if (f_line) {
        if (a.StartsWith("line"))
            return line.AddAttribute(a, chart, type);
        if (a.Is("linecolor") || a.Is("linecolour")) {
            chart->Error.WarnMscgenAttr(a, false, "line.color");
            return line.AddAttribute(Attribute("line.color", a), chart, type);
        }
    }
    if (f_vline && a.StartsWith("vline"))
        return vline.AddAttribute(a, chart, type);
    if (f_vfill && a.StartsWith("vfill"))
        return vfill.AddAttribute(a, chart, type);
    if (f_alt) {
        if (a.StartsWith("aline"))
            return aline.AddAttribute(a, chart, type);
        if (a.StartsWith("atext"))
            return atext.AddAttribute(a, chart, type);
        if (a.Is("arclinecolor") || a.Is("arclinecolour")) {
            chart->Error.WarnMscgenAttr(a, false, "aline.color");
            line.AddAttribute(Attribute("aline.color", a), chart, type);
        }
        if (a.Is("arctextcolor") || a.Is("arctextcolour")) {
            chart->Error.WarnMscgenAttr(a, false, "atext.color");
            text.AddAttribute(Attribute("atext.color", a), chart, type);
        }
        if (a.Is("arctextbgcolor") || a.Is("arctextbgcolour")) {
            chart->Error.WarnMscgenAttr(a, false, "atext.bgcolor");
            text.AddAttribute(Attribute("atext.bgcolor", a), chart, type);
        }
    }
    if (f_note && a.StartsWith("note"))
        return note.AddAttribute(a, chart, type);
    if (a.Is("solid") && f_solid) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                solid.reset();
            return true;
        }
        if (a.type == EAttrType::NUMBER && a.number>=0 && a.number <= 255) {
            solid = a.number<=1 ? (unsigned char)(a.number*255) : (unsigned char)a.number;
            return true;
        }
        a.InvalidValueError("0..1' or '0..255", chart->Error);
    }
    if (a.Is("side") && f_side != ESideType::NO) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                side.reset();
            return true;
        }
        ESide tmp_side;
        if (a.type == EAttrType::STRING && Convert(a.value, tmp_side)) 
            if (IsValidSideValue(f_side, tmp_side)) {
                side = tmp_side;
                return true;
            }
        a.InvalidValueError(f_side == ESideType::LEFT_RIGHT ? "left/right" : "left/right/end", chart->Error);
        return true;
    }
    if (a.Is("compress") && f_vspacing) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                vspacing.reset();
            return true;
        }
        if (!a.CheckType(EAttrType::BOOL, chart->Error)) return true;
        vspacing = a.yes ? DBL_MIN : 0;
        return true;
    }
    if (a.Is("vspacing") && f_vspacing) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                vspacing.reset();
        } else if (a.type == EAttrType::STRING && a.value == "compress") {
            vspacing = DBL_MIN;
        } else if (a.CheckType(EAttrType::NUMBER, chart->Error)) {
            vspacing = a.number;
        }
        return true;
    }
    if (a.Is("indicator") && f_indicator) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                indicator.reset();
            return true;
        }
        if (!a.CheckType(EAttrType::BOOL, chart->Error)) return true;
        indicator = a.yes;
        return true;
    }
    if (a.Is("makeroom") && f_makeroom) {
        if (a.type == EAttrType::CLEAR) {
            if (a.EnsureNotClear(chart->Error, type))
                makeroom.reset();
            return true;
        }
        if (!a.CheckType(EAttrType::BOOL, chart->Error)) return true;
        makeroom = a.yes;
        return true;
    }
    if (f_lost) {
        if (a.StartsWith("lost.line"))
            return lost_line.AddAttribute(a, chart, type);
        if (a.StartsWith("lost.arrow"))
            return lost_arrow.AddAttribute(a, chart, type);
        if (a.StartsWith("lost.text"))
            return lost_text.AddAttribute(a, chart, type);
    }
    if (f_lsym) {
        if (a.StartsWith("x.line"))
            return lsym_line.AddAttribute(a, chart, type);
        if (a.Is("x.size")) {
            if (EArrowSize s; a.type == EAttrType::STRING && Convert(a.value, s)) {
                lsym_size = s;
                return true;
            }
            a.InvalidValueError(CandidatesFor(*lsym_size), chart->Error);
            return true;
        }
    }
    if (f_tag && a.StartsWith("tag.line"))
        return tag_line.AddAttribute(a, chart, type);
    if (f_tag && a.StartsWith("tag.fill"))
        return tag_fill.AddAttribute(a, chart, type);
    if (f_tag && a.StartsWith("tag.text"))
        return tag_text.AddAttribute(a, chart, type);
    return SimpleStyleWithArrow<MscArrowHeads>::AddAttribute(a, chart);
}

bool MscStyle::DoIAcceptUnqualifiedColorAttr() const
{
    if (color_meaning==EColorMeaning::LINE_VLINE_TEXT && f_vline)
        return true;
    return SimpleStyleWithArrow<MscArrowHeads>::DoIAcceptUnqualifiedColorAttr();
}


/** Add the attribute names we take to `csh`.*/
void MscStyle::AttributeNames(Csh &csh) const
{
    if (DoIAcceptUnqualifiedColorAttr())
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"color",
          "Set the color of the element.", 
          EHintType::ATTR_NAME));
    if (f_alt) {
        LineAttr::AttributeNames(csh, "aline.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"aline.*",
            "Use these attributes to influence the style of arrows starting from this entity.",
            EHintType::ATTR_NAME));
        StringFormat::AttributeNames(csh, "atext.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"atext.*",
            "Use these attributes to influence the style of arrows starting from this entity.",
            EHintType::ATTR_NAME));
    }
    if (f_solid)
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"solid",
        "Specifies, how much the pipe covers its content. Use a value between [0..255] or [0..1], where 0 is fully transparent.",
        EHintType::ATTR_NAME));
    if (f_side != ESideType::NO)
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"side", 
        "Specify which side the element ends up.", 
        EHintType::ATTR_NAME));
    if (f_indicator) csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"indicator", 
        "Enable or disable indicators for this element (in case of a collapsed group entity or box).",
        EHintType::ATTR_NAME));
    if (f_makeroom) csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"makeroom", 
        "Specify if this vertical makes more horizontal space to avoid overlap.",
        EHintType::ATTR_NAME));
    if (f_vspacing) {
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"compress", 
            "Turning this on will push this element upwards until it bumps into the one above it in order to compress the chart vertically.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"vspacing", 
            "Specify the vertical spacing above this element.",
            EHintType::ATTR_NAME));
    }
    if (f_vline) {
        LineAttr::AttributeNames(csh, "vline.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"vline.*",
            "Set how the entity lines look like for this entity (or around this element).",
            EHintType::ATTR_NAME));
    }
    if (f_vfill) {
        FillAttr::AttributeNames(csh, "vfill.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"vfill.*",
            "Set how to fill the activated entity lines for this entity (or around this element).",
            EHintType::ATTR_NAME));
    }
    if (f_note) NoteAttr::AttributeNames(csh);
    if (f_lost) {
        LineAttr::AttributeNames(csh, "lost.line.");
        MscArrowHeads::AttributeNames(csh, "lost.arrow.");
        StringFormat::AttributeNames(csh, "lost.text.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"lost.line.*",
            "Options for how the line of the lost part of a lost message appears.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"lost.arrow.*",
            "Options for how the arrowhead of the lost part of a lost message appears.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"lost.text.*",
            "Options for how the label at the lost part of a lost message appears.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"lost.*",
            "Options for how the lost part of the lost message appears.",
            EHintType::ATTR_NAME));
    }
    if (f_lsym) {
        LineAttr::AttributeNames(csh, "x.line.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"x.size", 
            "Specify the size of the loss symbol.", 
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"x.*",
            "Options for how the loss symbol appears.",
            EHintType::ATTR_NAME));
    }
    if (f_tag) {
        LineAttr::AttributeNames(csh, "tag.line.");
        FillAttr::AttributeNames(csh, "tag.fill.");
        StringFormat::AttributeNames(csh, "tag.text.");
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"tag.line.*",
            "Set the line style of the tag of the box.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"tag.fill.*",
            "Set the fill style of the tag of the box.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"tag.text.*",
            "Set the text format of the tag label.",
            EHintType::ATTR_NAME));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRNAME)+"tag.*",
            "Options to control how box tags appear.",
            EHintType::ATTR_NAME));
    }
    SimpleStyleWithArrow<MscArrowHeads>::AttributeNames(csh);
}

/** Callback for drawing a symbol before side values in the hints popup list box.
 * @ingroup hintpopup_callbacks*/
bool CshHintGraphicCallbackForSide(Canvas *canvas, CshHintGraphicParam p, CshHintStore &)
{
    if (!canvas) return false;
    const ESide t = (ESide)(int)p;
    XY bounds(HINT_GRAPHIC_SIZE_X, HINT_GRAPHIC_SIZE_Y);
    if (t==ESide::END) bounds.SwapXY();
    std::vector<double> xPos(2); 
    xPos[0] = 0;
    xPos[1] = bounds.x*0.7;
    LineAttr eLine(ELineType::SOLID, ColorType(0,0,0), 1, ECornerType::NONE, 0);
    MscArrowHeads aa(EArcArrowType::BIGARROW);
    aa.SetLineColor(ColorType(0,32,192)); //blue-green
    aa.SetType(EArrowEnd::END, EArrowType::SOLID);
    aa.SetType(EArrowEnd::START, EArrowType::NONE);
    aa.SetSize(EArrowSize::SPOT);
    aa.CreateArrowHeads();
    ShadowAttr shadow;
    FillAttr fill(aa.GetLine().color->Lighter(0.7), t==ESide::RIGHT ? EGradientType::DOWN : EGradientType::UP);
    std::vector<double> active(2,0.);
    canvas->Transform_Rotate(bounds/2, t==ESide::LEFT ? 0 : t==ESide::RIGHT ? M_PI : M_PI/2);
    canvas->Clip(XY(bounds.x*0.1,1), XY(bounds.x-1, bounds.y-1));
    aa.BigCalculateAndDraw(xPos, active, bounds.y*0.3, bounds.y*0.7, 
                           true, false, &aa.GetLine(), nullptr, fill, shadow, *canvas);
    canvas->UnClip();
    canvas->UnTransform();
    return true;
}

/** Add a list of possible attribute value names to `csh` for attribute `attr`.*/
bool MscStyle::AttributeValues(std::string_view attr, Csh &csh) const
{
    if (CaseInsensitiveBeginsWith(attr, "vline") && f_vline)
        return vline.AttributeValues(attr, csh);
    if (CaseInsensitiveBeginsWith(attr, "vfill") && f_vfill)
        return vfill.AttributeValues(attr, csh);
    if (f_alt) {
        if (CaseInsensitiveBeginsWith(attr, "afill"))
            return fill.AttributeValues(attr, csh);
        if (CaseInsensitiveBeginsWith(attr, "atext"))
            StringFormat::AttributeValues(attr, csh);
    }
    if (CaseInsensitiveEqual(attr, "solid") && f_solid) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number: \b0.0..1.0\b>", 
            "Specify the opacity as a floating point number. 0 is fully transparent, 1 is fully opaque.",
            EHintType::ATTR_VALUE, false));
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number: \b0..255\b>", 
            "Specify the opacity as an integer. 0 is fully transparent, 255 is fully opaque.",
            EHintType::ATTR_VALUE, false));
        return true;
    }
    if ((CaseInsensitiveEqual(attr, "compress") && f_vspacing) ||
        (CaseInsensitiveEqual(attr, "indicator") && f_indicator) ||
        (CaseInsensitiveEqual(attr, "makeroom") && f_makeroom)) {
        csh.AddYesNoToHints();
        return true;
    }
    if (CaseInsensitiveEqual(attr, "vspacing") && f_vspacing) {
        csh.AddToHints(CshHint(csh.HintPrefixNonSelectable() + "<number>", 
            "Specify extra spading above this element in pixels. 0 means no extra space.",
            EHintType::ATTR_VALUE, false));
        csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRVALUE)+"compress", 
            "Specifying 'compress' will auto-adjust vertical spacing to be as little as possible by moving the element up until it bumps into the ones above.",
            EHintType::ATTR_VALUE, true));
        return true;
    }
    if (CaseInsensitiveEndsWith(attr, "side")) {
        for (auto s = ESide::LEFT; s<=ESide::END; s = ESide(int(s)+1))
            if (IsValidSideValue(f_side, s))
                csh.AddToHints(CshHint(csh.HintPrefix(COLOR_ATTRVALUE)+EnumEncapsulator<ESide>::names[unsigned(s)], 
                                       EnumEncapsulator<ESide>::descriptions[unsigned(s)],
                                       EHintType::ATTR_VALUE, true, CshHintGraphicCallbackForSide, 
                                       CshHintGraphicParam(s)));
        return true;
    }
    if (f_note && CaseInsensitiveBeginsWith(attr, "note"))
        return note.AttributeValues(attr, csh);
    if (f_lost) {
        if (CaseInsensitiveEqual(attr, "lost.line"))
            return line.AttributeValues(attr, csh);
        if (CaseInsensitiveEqual(attr, "lost.arrow"))
            return arrow.AttributeValues(attr, csh, EArcArrowType::ARROW);
        if (CaseInsensitiveEqual(attr, "lost.text"))
            StringFormat::AttributeValues(attr, csh);
    }
    if (f_lsym) {
        if (CaseInsensitiveEqual(attr, "x.line"))
            return line.AttributeValues(attr, csh);
        if (CaseInsensitiveEqual(attr, "x.size")) {
            arrow.AttributeValues(attr, csh, EArcArrowType::ANY);
            return true;
        }
    }
    if (f_tag) {
        if (CaseInsensitiveBeginsWith(attr, "tag_text"))
            return tag_text.AttributeValues(attr, csh);
        if (CaseInsensitiveBeginsWith(attr, "tag.line"))
            return tag_line.AttributeValues(attr, csh);
        if (CaseInsensitiveBeginsWith(attr, "tag.fill"))
            return tag_fill.AttributeValues(attr, csh);
    }
    return SimpleStyleWithArrow<MscArrowHeads>::AttributeValues(attr, csh);
}

//////////////////////////////////////////////////////////////////////

void msc::MscContext::ApplyContextContent(const MscContext & o)
{
    ContextBase::ApplyContextContent(o);
    if (o.is_full) {
        hscale = o.hscale;
        numbering = o.numbering;
        vspacing = o.vspacing;
        indicator = o.indicator;
        slant_angle = o.slant_angle;
        slant_depth = o.slant_depth;
        auto_heading = o.auto_heading;
        defCommentLine = o.defCommentLine;
        defCommentFill = o.defCommentFill;
        defBackground = o.defBackground;
    } else {
        if (o.hscale) hscale = o.hscale;
        if (o.numbering) numbering = o.numbering;
        if (o.vspacing) vspacing = o.vspacing;
        if (o.indicator) indicator = o.indicator;
        if (o.slant_angle) slant_angle = o.slant_angle;
        if (o.slant_depth) slant_depth = o.slant_depth;
        if (o.auto_heading) auto_heading = o.auto_heading;
        defCommentLine += o.defCommentLine;
        defCommentFill += o.defCommentFill;
        defBackground += o.defBackground;
    }
}

void msc::MscContext::ApplyContextContent(MscContext && o)
{
    if (o.is_full) {
        hscale = o.hscale;
        numbering = o.numbering;
        vspacing = o.vspacing;
        indicator = o.indicator;
        slant_angle = o.slant_angle;
        slant_depth = o.slant_depth;
        auto_heading = o.auto_heading;
        defCommentLine = o.defCommentLine;
        defCommentFill = o.defCommentFill;
        defBackground = o.defBackground;
    } else {
        if (o.hscale) hscale = o.hscale;
        if (o.numbering) numbering = o.numbering;
        if (o.vspacing) vspacing = o.vspacing;
        if (o.indicator) indicator = o.indicator;
        if (o.slant_angle) slant_angle = o.slant_angle;
        if (o.slant_depth) slant_depth = o.slant_depth;
        if (o.auto_heading) auto_heading = o.auto_heading;
        defCommentLine += o.defCommentLine;
        defCommentFill += o.defCommentFill;
        defBackground += o.defBackground;
    }
    ContextBase::ApplyContextContent(std::move(o));
}

void MscContext::Empty()
{
    ContextBase::Empty();
    Procedures.clear();
    hscale.reset();
    numbering.reset();
    vspacing.reset();
    indicator.reset();
    slant_angle.reset();
    slant_depth.reset();
    auto_heading.reset();
    defCommentLine.Empty();
    defCommentFill.Empty();
    defBackground.Empty();

    //Now add default styles, but all empty
    
    styles["arrow"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::ARROW,
                 false, true, true, false, false, false, 
                 false, true, true, ESideType::LEFT_RIGHT, true, false, false, false, 
                 true, true, false, false); 
                 //no alt fill, shadow, vline solid vfill, makeroom, note, shape, tag
    styles["arrow_self"] = styles["arrow"];
    styles["->"] = styles["arrow"]; 
    styles["->"].write().type = EStyleType::DEF_ADD;
    styles[">"]  = styles["->"];
    styles[">>"] = styles["->"];
    styles["=>"] = styles["->"];
    styles["=>>"] = styles["->"];
    styles["==>"] = styles["->"];

    styles["blockarrow"] =
        MscStyle(EStyleType::DEFAULT, EColorMeaning::FILL, EArcArrowType::BIGARROW,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::NO, true, false, false, false, 
                 false, false, false, false);
                 //no alt vline solid side vfill makeroom note loss lsym shape, tag
    styles["box_collapsed_arrow"] = styles["blockarrow"];
    styles["block->"] = styles["blockarrow"];
    styles["block->"].write().type = EStyleType::DEF_ADD;
    styles["block>"] = styles["block->"];
    styles["block>>"] = styles["block->"];
    styles["block=>"] = styles["block->"];
    styles["block=>>"] = styles["block->"];
    styles["block==>"] = styles["block->"];

    styles["vertical"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::FILL, EArcArrowType::BIGARROW,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::ANY, false, false, true, false, 
                 false, false, false, false);
                 //no alt vline solid indicator vfill note loss lsym shape tag
    styles["vertical_range"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::ARROW,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::ANY, false, false, true, false, 
                 false, false, false, false);
                 //no alt vline solid indicator vfill note loss lsym shape tag
    styles["vertical_pointer"] =
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::ARROW,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::ANY, false, false, true, false, 
                 true, true, false, false);
                 //no alt vline solid indicator vfill note shape tag
    styles["vertical_bracket"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::NONE,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::ANY, false, false, true, false, 
                 false, false, false, false);
                 //no alt arrow vline solid indicator vfill note loss lsym shape tag
    styles["vertical_brace"] = styles["vertical_bracket"];

    styles["vertical->"] =
        MscStyle(EStyleType::DEF_ADD, EColorMeaning::NOHOW, EArcArrowType::ANY,
                false, true, true, true, true, false,
                false, true, true, ESideType::ANY, false, false, true, false,
                true, true, false, false);
                //no alt vline solid indicator vfill note  shape tag
    styles["vertical>"] = styles["vertical->"];
    styles["vertical>>"] = styles["vertical->"];
    styles["vertical=>"] = styles["vertical->"];
    styles["vertical=>>"] = styles["vertical->"];
    styles["vertical==>"] = styles["vertical->"];
    styles["vertical--"] = styles["vertical->"];
    styles["vertical++"] = styles["vertical->"];
    styles["vertical.."] = styles["vertical->"];
    styles["vertical=="] = styles["vertical->"];

    styles["divider"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::NONE,
                 false, true, true, false, false, true,
                 false, true, true, ESideType::NO, false, false, false, false, 
                 false, false, false, false);
                 //no alt arrow, fill, shadow solid side indicator vfill makeroom note loss lsym shape tag
    styles["---"] = styles["divider"];
    styles["---"].write().type = EStyleType::DEF_ADD;
    styles["..."] = styles["---"];

    styles["box"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::FILL, EArcArrowType::NONE,
                 false, true, true, true, true, false,
                 false, true, true, ESideType::NO, true, false, false, false, 
                 false, false, false, true);
                 //no alt arrow, vline solid side vfill makeroom note loss lsym shape
    styles["box_collapsed"] = styles["box"];
    styles["emptybox"] = styles["box"];
    styles["--"] = styles["box"];
    styles["--"].write().type = EStyleType::DEF_ADD;
    styles["++"] = styles["--"];
    styles[".."] = styles["--"];
    styles["=="] = styles["--"];

    styles["pipe"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::FILL, EArcArrowType::NONE,
                 false, true, true, true, true, false,
                 true, true, true, ESideType::LEFT_RIGHT, false, false, false, false, 
                 false, false, false, false);
                 //no alt arrow, vline indicator vfill makeroom note loss lsym shape tag
    styles["pipe--"] = styles["pipe"];
    styles["pipe--"].write().type = EStyleType::DEF_ADD;
    styles["pipe++"] = styles["pipe--"];
    styles["pipe.."] = styles["pipe--"];
    styles["pipe=="] = styles["pipe--"];

    styles["entity"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_VLINE_TEXT, EArcArrowType::ARROW,
                 true, true, true, true, true, true,
                 false, false, false, ESideType::NO, true, true, false, false, 
                 false, false, true, false);
                 //no solid numbering compress side makeroom note loss lsym tag
    styles["entitygroup_collapsed"] = styles["entity"];
    styles["entitygroup"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_VLINE_TEXT, EArcArrowType::NONE,  //no point in using a different color meaning, as attributes are always added to "entity"
                 false, true, true, true, true, false,
                 false, false, false, ESideType::NO, true, false, false, false, 
                 false, false, false, false);
                 //only line, fill, text and shadow
    styles["entitygroup_large"] = styles["entitygroup"];

    styles["indicator"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::NONE,
                 false, false, true, true, true, false,
                 false, false, false, ESideType::NO, false, false, false, false, 
                 false, false, false, false);
                 //fill line shadow only
    styles["symbol"] =
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_FILL, EArcArrowType::NONE,
                 false, true, true, true, true, false,
                 false, false, false, ESideType::NO, false, false, true, false, 
                 false, false, true, false);
                 //only line fill shadow text makeroom shape
    styles["inlined_chart"] =
        MscStyle(EStyleType::DEFAULT, EColorMeaning::NOHOW, EArcArrowType::NONE,
                 false, false, true, true, true, false,
                 false, false, false, ESideType::NO, false, false, true, false,
                 false, false, false, false);
                 //only line fill shadow makeroom 
    styles["text"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::TEXT, EArcArrowType::NONE,
                 false, true, true, true, true, false,
                 false, false, false, ESideType::NO, false, false, true, false, 
                 false, false, false, false);
                 //only line fill shadow text makeroom; color is for text


    styles["note"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::NOHOW, EArcArrowType::ANY,
                 false, true, true, true, true, false, 
                 false, true, false, ESideType::NO, false, false, false, true, 
                 false, false, false, false);
                 //no alt vline side solid indicator compress vfill makreoom loss lsym shape tag
    
    styles["comment"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::LINE_ARROW_TEXT, EArcArrowType::NONE,
                 false, true, false, false, false, false,
                 false, true, false, ESideType::ANY, false, false, false, false, 
                 false, false, false, false);
                 //only text numbering and side
    styles["endnote"] = styles["comment"];
    //styles["footnote"] = styles["comment"];

    styles["title"] = 
        MscStyle(EStyleType::DEFAULT, EColorMeaning::TEXT, EArcArrowType::NONE,
                 false, true, true, true, true, true,
                 false, false, false, ESideType::NO, false, false, false, false, 
                 false, false, false, false);
                 //line, fill, shadow, vline text  
    styles["subtitle"] = styles["title"];
}

void MscContext::Plain()
{
    Empty();
    ContextBase::Plain();
    hscale = 1.0;
    numbering = false;
    vspacing = 0;
    indicator = true;
    slant_angle = 0;
    slant_depth = 0;
    auto_heading = false;
    defCommentLine.MakeComplete();
    defCommentFill.MakeComplete();
    defBackground.MakeComplete();
    defCommentLine.width = 3;
    defCommentFill.color = ColorType(0,0,0,0); //fully transparent
    defBackground.color = ColorType::white();

    MscStyle *p;
    const ColorType faint(255, 255, 255, 224, ColorType::OVERLAY);

    p = &styles["arrow"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->line.radius = 10;
    p->line.corner = ECornerType::ROUND;
    p->lost_line.color = faint;
    p->lost_arrow.write().SetLineColor(faint);
    //p->lost_text.SetColor(faint);  do not change label in the lost part

    styles["arrow_self"].write().line.radius = -1;
    styles["arrow_self"].write().side = ESide::RIGHT;

    styles["->"].write().line.type = ELineType::SOLID;
    styles[">"].write().line.type = ELineType::DOTTED;
    styles[">>"].write().line.type = ELineType::DASHED;
    styles["=>"].write().line.type = ELineType::DOUBLE;
    styles["=>>"].write().line.type = ELineType::DOUBLE;
    styles["=>>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SHARP);
    styles["=>>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SHARP);
    styles["==>"].write().line.type = ELineType::DOUBLE;

    p = &styles["blockarrow"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->line.radius = -1;
    styles["box_collapsed_arrow"] = *p;
    styles["box_collapsed_arrow"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::DOT);

    styles["block->"].write().line.type = ELineType::SOLID;
    styles["block>"].write().line.type = ELineType::DOTTED;
    styles["block>>"].write().line.type = ELineType::DASHED;
    styles["block=>"].write().line.type = ELineType::DOUBLE;
    styles["block=>>"].write().line.type = ELineType::DOUBLE;
    styles["block=>>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SHARP);
    styles["block=>>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SHARP);
    styles["block==>"].write().line.type = ELineType::DOUBLE;

    p = &styles["vertical"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->makeroom = true;
    p->side.reset();
    p->line.radius = -1;
    p->text.UnsetWordWrap();
    //We need this otherwise setting the global text style to wrap
    //would cause warnings for all horizontally set verticals which
    //have no text.width attribute. Better if the user sets this explicitly.

    p = &styles["vertical_range"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->makeroom = true;
    p->side.reset();
    p->line.radius = 8;
    p->text.UnsetWordWrap();

    p = &styles["vertical_bracket"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->makeroom = true;
    p->side.reset();
    p->line.radius = 8;
    p->line.corner = ECornerType::NONE;
    p->text.UnsetWordWrap();
    
    p = &styles["vertical_pointer"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->makeroom = true;
    p->side.reset();
    p->line.radius = 8;
    p->line.corner = ECornerType::NONE;
    p->text.UnsetWordWrap();
    p->lost_line.color = faint;
    p->lost_arrow.write().SetLineColor(faint);
    //p->lost_text.SetColor(faint); do not change label in lost part

    p = &styles["vertical_brace"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->makeroom = true;
    p->side.reset();
    p->line.radius = 8;
    p->line.corner = ECornerType::ROUND;
    p->text.UnsetWordWrap();

    styles["vertical->"].write().line.type = ELineType::SOLID;
    styles["vertical>"].write().line.type = ELineType::DOTTED;
    styles["vertical>>"].write().line.type = ELineType::DASHED;
    styles["vertical=>"].write().line.type = ELineType::DOUBLE;
    styles["vertical=>>"].write().line.type = ELineType::DOUBLE;
    styles["vertical=>>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SHARP);
    styles["vertical=>>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SHARP);
    styles["vertical==>"].write().line.type = ELineType::DOUBLE;

    styles["vertical--"].write().arrow.write().SetType(EArrowEnd::START, EArrowType::NONE);
    styles["vertical--"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::NONE);
    styles["vertical--"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::NONE);
    styles["vertical--"].write().line.type = ELineType::SOLID;
    styles["vertical++"] = styles["vertical--"];
    styles["vertical++"].write().line.type = ELineType::DASHED;
    styles["vertical.."] = styles["vertical--"];
    styles["vertical.."].write().line.type = ELineType::DOTTED;
    styles["vertical=="] = styles["vertical--"];
    styles["vertical=="].write().line.type = ELineType::DOUBLE;

    p = &styles["divider"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->vline.Empty();
    p->line.type = ELineType::NONE;

    p = &styles["---"].write();
    p->line.type = ELineType::DOTTED;
    p = &styles["..."].write();
    p->vline.type = ELineType::DOTTED;
    p->text.Apply("\\mu(10)\\md(10)");

    p = &styles["emptybox"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->tag_line.corner = ECornerType::BEVEL;
    p->tag_line.radius = 10;
    p->tag_text.Apply("\\B\\-");
    styles["box_collapsed"] = styles["emptybox"];
    styles["box"] = styles["emptybox"];
    styles["box"].write().text.Apply("\\pl");
    styles["box"].write().line.type.reset();

    styles["--"].write().line.type = ELineType::SOLID;
    styles["++"].write().line.type = ELineType::DASHED;
    styles[".."].write().line.type = ELineType::DOTTED;
    styles["=="].write().line.type = ELineType::DOUBLE;

    p = &styles["pipe"].write();
    p->MakeCompleteButText();
    p->vspacing.reset();
    p->numbering.reset();
    p->line.radius = 5;

    styles["pipe--"].write().line.type = ELineType::SOLID;
    styles["pipe++"].write().line.type = ELineType::DASHED;
    styles["pipe.."].write().line.type = ELineType::DOTTED;
    styles["pipe=="].write().line.type = ELineType::DOUBLE;

    styles["entity"].write().MakeCompleteButText();
    styles["entity"].write().arrow.Empty(); //we have to ensure this is empty. aline and atext is left empty by MakeCompleteButText()
    styles["entitygroup_collapsed"].write().MakeCompleteButText();
    styles["entitygroup_collapsed"].write().arrow.Empty(); 
    styles["entitygroup"].write().MakeCompleteButText();
    styles["entitygroup"].write().line.type = ELineType::DASHED;
    styles["entitygroup_large"].write().MakeCompleteButText();
    styles["entitygroup_large"].write().line.type = ELineType::NONE;
    styles["entitygroup_large"].write().fill.color = ColorType(192,192,192);

    styles["indicator"].write().MakeCompleteButText();
    styles["indicator"].write().line.width = 2;

    p = &styles["symbol"].write();
    p->MakeCompleteButText();
    p->makeroom = true;

    styles["text"] = styles["symbol"];
    styles["text"].write().line.type = ELineType::NONE;
    styles["text"].write().fill.color = ColorType::none(); //fully transparent
    styles["text"].write().text += "\\mn(10)\\ms(6)"; //small font to 6, normal to 10

    p = &styles["inlined_chart"].write();
    p->MakeCompleteButText();
    p->makeroom = true;
    p->line.type = ELineType::NONE;
    p->fill.color = ColorType::none();

    styles["note"].write().MakeCompleteButText();
    styles["note"].write().numbering.reset();
    styles["note"].write().text += "\\mn(10)\\ms(6)"; //small font to 6, normal to 10

    styles["comment"].write().MakeCompleteButText();
    styles["comment"].write().numbering.reset();
    styles["comment"].write().text += "\\mn(10)\\ms(6)\\pl"; //small font to 6, normal to 10, left para

    p = &styles["endnote"].write();
    p->MakeCompleteButText();
    p->numbering.reset();
    p->text += "\\mn(10)\\ms(6)\\pl"; //small font to 6, normal to 10, left para
    p->side = ESide::END;

    //styles["footnote"].write().MakeCompleteButText();
    //styles["footnote"].write().numbering.reset();
    //styles["footnote"].write().text += "\\mn(10)\\ms(6)\\pl"; //small font to 6, normal to 10, left para
    //styles["footnote"].write().side.value = ESide::END;

    p = &styles["title"].write();
    p->MakeCompleteButText();
    p->vline.type = ELineType::NONE;
    p->line.type = ELineType::NONE;
    p->fill.color = ColorType(0,0,0,0); //no fill
    p->text += "\\mn(28)\\ms(18)\\B";
    styles["subtitle"] = styles["title"];
    styles["subtitle"].write().text += "\\mn(22)\\ms(14)\\B";

    const OptAttr<ColorType> weak = ColorType(255, 255, 255, 128, ColorType::OVERLAY);
    styles["weak"].write().line.color = weak;
    styles["weak"].write().arrow.write().SetLineColor(*weak);
    styles["weak"].write().text.SetColor(*weak);
    styles["weak"].write().text.Apply("\\i");
    const OptAttr<double> w2 = 2.0;
    styles["strong"].write().line.width = w2;
    LineAttr l = styles["strong"].read().arrow.read().GetLine();
    l.width = w2;
    styles["strong"].write().arrow.write().SetLine(l);
    styles["strong"].write().text.Apply("\\b");
}

void MscContext::MscgenCompat()
{
    Empty();
    is_full = false;

    MscStyle *p = &styles["arrow"].write();
    p->lost_line.color = ColorType::none();
    p->lost_arrow.write().SetLineColor(ColorType::none());
    p->lsym_line.width = 1;
    p->lsym_line.color = ColorType::black();

    styles["->"].write().line.type = ELineType::SOLID;
    styles["->"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::HALF);
    styles["->"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::HALF);
    styles[">"].write().line.type = ELineType::DOTTED;
    styles[">"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SOLID);
    styles[">"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SOLID);
    styles[">>"].write().line.type = ELineType::DOTTED;
    styles[">>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SOLID);
    styles[">>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SOLID);
    styles["=>"].write().line.type = ELineType::SOLID;
    styles["=>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SOLID);
    styles["=>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SOLID);
    styles["=>>"].write().line.type = ELineType::SOLID;
    styles["=>>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::LINE);
    styles["=>>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::LINE);
    //==> is the same as in plain: double line, solid arrow
    styles[":>"].write().line.type = ELineType::DOUBLE;
    styles[":>"].write().arrow.write().SetType(EArrowEnd::END, EArrowType::SOLID);
    styles[":>"].write().arrow.write().SetType(EArrowEnd::MIDDLE, EArrowType::SOLID);
}

