//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2002-2011 Werner Schweer // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 // as published by the Free Software Foundation and appearing in // the file LICENCE.GPL //============================================================================= #include "xml.h" #include "layoutbreak.h" #include "spanner.h" #include "beam.h" #include "tuplet.h" #include "sym.h" #include "note.h" #include "barline.h" namespace Ms { QString docName; //--------------------------------------------------------- // intAttribute //--------------------------------------------------------- int XmlReader::intAttribute(const char* s, int _default) const { if (attributes().hasAttribute(s)) // return attributes().value(s).toString().toInt(); return attributes().value(s).toInt(); else return _default; } int XmlReader::intAttribute(const char* s) const { return attributes().value(s).toInt(); } //--------------------------------------------------------- // doubleAttribute //--------------------------------------------------------- double XmlReader::doubleAttribute(const char* s) const { return attributes().value(s).toDouble(); } double XmlReader::doubleAttribute(const char* s, double _default) const { if (attributes().hasAttribute(s)) return attributes().value(s).toDouble(); else return _default; } //--------------------------------------------------------- // attribute //--------------------------------------------------------- QString XmlReader::attribute(const char* s, const QString& _default) const { if (attributes().hasAttribute(s)) return attributes().value(s).toString(); else return _default; } //--------------------------------------------------------- // hasAttribute //--------------------------------------------------------- bool XmlReader::hasAttribute(const char* s) const { return attributes().hasAttribute(s); } //--------------------------------------------------------- // readPoint //--------------------------------------------------------- QPointF XmlReader::readPoint() { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); #ifndef NDEBUG if (!attributes().hasAttribute("x")) { QXmlStreamAttributes map = attributes(); qDebug("XmlReader::readPoint: x attribute missing: %s (%d)", name().toUtf8().data(), map.size()); for (int i = 0; i < map.size(); ++i) { const QXmlStreamAttribute& a = map.at(i); qDebug(" attr <%s> <%s>", a.name().toUtf8().data(), a.value().toUtf8().data()); } unknown(); } if (!attributes().hasAttribute("y")) { qDebug("XmlReader::readPoint: y attribute missing: %s", name().toUtf8().data()); unknown(); } #endif qreal x = doubleAttribute("x", 0.0); qreal y = doubleAttribute("y", 0.0); readNext(); return QPointF(x, y); } //--------------------------------------------------------- // readColor //--------------------------------------------------------- QColor XmlReader::readColor() { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); QColor c; c.setRed(intAttribute("r")); c.setGreen(intAttribute("g")); c.setBlue(intAttribute("b")); c.setAlpha(intAttribute("a", 255)); skipCurrentElement(); return c; } //--------------------------------------------------------- // readSize //--------------------------------------------------------- QSizeF XmlReader::readSize() { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); QSizeF p; p.setWidth(doubleAttribute("w", 0.0)); p.setHeight(doubleAttribute("h", 0.0)); skipCurrentElement(); return p; } //--------------------------------------------------------- // readRect //--------------------------------------------------------- QRectF XmlReader::readRect() { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); QRectF p; p.setX(doubleAttribute("x", 0.0)); p.setY(doubleAttribute("y", 0.0)); p.setWidth(doubleAttribute("w", 0.0)); p.setHeight(doubleAttribute("h", 0.0)); skipCurrentElement(); return p; } //--------------------------------------------------------- // readFraction //--------------------------------------------------------- Fraction XmlReader::readFraction() { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); int z = attribute("z", "0").toInt(); int n = attribute("n", "0").toInt(); skipCurrentElement(); return Fraction(z, n); } //--------------------------------------------------------- // unknown // unknown tag read //--------------------------------------------------------- void XmlReader::unknown() { if (QXmlStreamReader::error()) qDebug("StreamReaderError: %s", qPrintable(errorString())); qDebug("%s: xml read error at line %lld col %lld: %s", qPrintable(docName), lineNumber(), columnNumber(), name().toUtf8().data()); skipCurrentElement(); } //--------------------------------------------------------- // addBeam //--------------------------------------------------------- void XmlReader::addBeam(Beam* s) { _beams.insert(s->id(), s); } //--------------------------------------------------------- // addTuplet //--------------------------------------------------------- void XmlReader::addTuplet(Tuplet* s) { _tuplets.insert(s->id(), s); } //--------------------------------------------------------- // readDouble //--------------------------------------------------------- double XmlReader::readDouble(double min, double max) { double val = readElementText().toDouble(); if (val < min) val = min; else if (val > max) val = max; return val; } //--------------------------------------------------------- // readBool //--------------------------------------------------------- bool XmlReader::readBool() { bool val; QXmlStreamReader::TokenType tt = readNext(); if (tt == QXmlStreamReader::Characters) { val = text().toInt() != 0; readNext(); } else val = true; return val; } //--------------------------------------------------------- // compareProperty //--------------------------------------------------------- template bool compareProperty(void* val, void* defaultVal) { return (defaultVal == 0) || (*(T*)val != *(T*)defaultVal); } //--------------------------------------------------------- // Xml //--------------------------------------------------------- Xml::Xml() { setCodec("UTF-8"); } Xml::Xml(QIODevice* device) : QTextStream(device) { setCodec("UTF-8"); } //--------------------------------------------------------- // pTag //--------------------------------------------------------- void Xml::pTag(const char* name, PlaceText place) { const char* tags[] = { "auto", "above", "below", "left" }; tag(name, tags[int(place)]); } //--------------------------------------------------------- // readPlacement //--------------------------------------------------------- PlaceText readPlacement(XmlReader& e) { const QString& s(e.readElementText()); if (s == "auto" || s == "0") return PlaceText::AUTO; if (s == "above" || s == "1") return PlaceText::ABOVE; if (s == "below" || s == "2") return PlaceText::BELOW; if (s == "left" || s == "3") return PlaceText::LEFT; qDebug("unknown placement value <%s>", qPrintable(s)); return PlaceText::AUTO; } //--------------------------------------------------------- // fTag //--------------------------------------------------------- void Xml::fTag(const char* name, const Fraction& f) { tagE(QString("%1 z=\"%2\" n=\"%3\"").arg(name).arg(f.numerator()).arg(f.denominator())); } //--------------------------------------------------------- // putLevel //--------------------------------------------------------- void Xml::putLevel() { int level = stack.size(); for (int i = 0; i < level * 2; ++i) *this << ' '; } //--------------------------------------------------------- // header //--------------------------------------------------------- void Xml::header() { *this << "\n"; } //--------------------------------------------------------- // stag // //--------------------------------------------------------- void Xml::stag(const QString& s) { putLevel(); *this << '<' << s << '>' << endl; stack.append(s.split(' ')[0]); } //--------------------------------------------------------- // etag // //--------------------------------------------------------- void Xml::etag() { putLevel(); *this << "' << endl; } //--------------------------------------------------------- // tagE // //--------------------------------------------------------- void Xml::tagE(const char* format, ...) { va_list args; va_start(args, format); putLevel(); *this << '<'; char buffer[BS]; vsnprintf(buffer, BS, format, args); *this << buffer; va_end(args); *this << "/>" << endl; } //--------------------------------------------------------- // tagE //--------------------------------------------------------- void Xml::tagE(const QString& s) { putLevel(); *this << '<' << s << "/>\n"; } //--------------------------------------------------------- // ntag // without newline //--------------------------------------------------------- void Xml::ntag(const char* name) { putLevel(); *this << "<" << name << ">"; } //--------------------------------------------------------- // netag // without indentation //--------------------------------------------------------- void Xml::netag(const char* s) { *this << "' << endl; } //--------------------------------------------------------- // tag //--------------------------------------------------------- void Xml::tag(P_ID id, QVariant data, QVariant defaultData) { if (data == defaultData) return; const char* name = propertyName(id); if (name == 0) return; switch (propertyType(id)) { case P_TYPE::BOOL: case P_TYPE::SUBTYPE: case P_TYPE::INT: case P_TYPE::SPATIUM: case P_TYPE::SP_REAL: case P_TYPE::REAL: case P_TYPE::SCALE: case P_TYPE::POINT: case P_TYPE::SIZE: case P_TYPE::COLOR: tag(name, data); break; case P_TYPE::ORNAMENT_STYLE: switch (MScore::OrnamentStyle(data.toInt())) { case MScore::OrnamentStyle::BAROQUE: tag(name, QVariant("baroque")); break; default: // tag(name, QVariant("default")); break; } break; case P_TYPE::GLISSANDO_STYLE: switch (MScore::GlissandoStyle(data.toInt())) { case MScore::GlissandoStyle::BLACK_KEYS: tag(name, QVariant("blackkeys")); break; case MScore::GlissandoStyle::WHITE_KEYS: tag(name, QVariant("whitekeys")); break; case MScore::GlissandoStyle::DIATONIC: tag(name, QVariant("diatonic")); break; default: //tag(name, QVariant("Chromatic")); break; } break; case P_TYPE::DIRECTION: tag(name, data.value().toString()); break; case P_TYPE::DIRECTION_H: switch (MScore::DirectionH(data.toInt())) { case MScore::DirectionH::LEFT: tag(name, QVariant("left")); break; case MScore::DirectionH::RIGHT: tag(name, QVariant("right")); break; case MScore::DirectionH::AUTO: break; } break; case P_TYPE::LAYOUT_BREAK: switch (LayoutBreak::Type(data.toInt())) { case LayoutBreak::Type::LINE: tag(name, QVariant("line")); break; case LayoutBreak::Type::PAGE: tag(name, QVariant("page")); break; case LayoutBreak::Type::SECTION: tag(name, QVariant("section")); break; } break; case P_TYPE::VALUE_TYPE: switch (Note::ValueType(data.toInt())) { case Note::ValueType::OFFSET_VAL: tag(name, QVariant("offset")); break; case Note::ValueType::USER_VAL: tag(name, QVariant("user")); break; } break; case P_TYPE::PLACEMENT: switch (Element::Placement(data.toInt())) { case Element::Placement::ABOVE: tag(name, QVariant("above")); break; case Element::Placement::BELOW: tag(name, QVariant("below")); break; } break; case P_TYPE::SYMID: tag(name, Sym::id2name(SymId(data.toInt()))); break; case P_TYPE::BARLINE_TYPE: tag(name, BarLine::barLineTypeName(BarLineType(data.toInt()))); break; default: Q_ASSERT(false); } } //--------------------------------------------------------- // tag // value //--------------------------------------------------------- void Xml::tag(const char* name, QVariant data, QVariant defaultData) { if (data != defaultData) tag(QString(name), data); } void Xml::tag(const QString& name, QVariant data) { QString ename(name.split(' ')[0]); putLevel(); switch(data.type()) { case QVariant::Bool: case QVariant::Char: case QVariant::Int: case QVariant::UInt: *this << "<" << name << ">"; *this << data.toInt(); *this << "\n"; break; case QVariant::Double: *this << "<" << name << ">"; *this << data.value(); *this << "\n"; break; case QVariant::String: *this << "<" << name << ">"; *this << xmlString(data.value()); *this << "\n"; break; case QVariant::Color: { QColor color(data.value()); *this << QString("<%1 r=\"%2\" g=\"%3\" b=\"%4\" a=\"%5\"/>\n") .arg(name).arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha()); } break; case QVariant::Rect: { QRect r(data.value()); *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()); } break; case QVariant::RectF: { QRectF r(data.value()); *this << QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\"/>\n").arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()); } break; case QVariant::PointF: { QPointF p(data.value()); *this << QString("<%1 x=\"%2\" y=\"%3\"/>\n").arg(name).arg(p.x()).arg(p.y()); } break; case QVariant::SizeF: { QSizeF p(data.value()); *this << QString("<%1 w=\"%2\" h=\"%3\"/>\n").arg(name).arg(p.width()).arg(p.height()); } break; default: { const char* type = data.typeName(); if (strcmp(type, "Ms::Spatium") == 0) { *this << "<" << name << ">"; *this << data.value().val(); *this << "\n"; } else { qDebug("Xml::tag: unsupported type %d %s", data.type(), type); // abort(); } } break; } } void Xml::tag(const char* name, const QWidget* g) { tag(name, QRect(g->pos(), g->size())); } //--------------------------------------------------------- // xmlString //--------------------------------------------------------- QString Xml::xmlString(ushort c) { switch(c) { case '<': return QLatin1String("<"); case '>': return QLatin1String(">"); case '&': return QLatin1String("&"); case '\"': return QLatin1String("""); default: // ignore invalid characters in xml 1.0 if ((c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D)) return QString(); return QString(QChar(c)); } } //--------------------------------------------------------- // xmlString //--------------------------------------------------------- QString Xml::xmlString(const QString& s) { QString escaped; escaped.reserve(s.size()); for (int i = 0; i < s.size(); ++i) { ushort c = s.at(i).unicode(); escaped += xmlString(c); } return escaped; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Xml::dump(int len, const unsigned char* p) { putLevel(); int col = 0; setFieldWidth(5); setNumberFlags(numberFlags() | QTextStream::ShowBase); setIntegerBase(16); for (int i = 0; i < len; ++i, ++col) { if (col >= 16) { setFieldWidth(0); *this << endl; col = 0; putLevel(); setFieldWidth(5); } *this << (p[i] & 0xff); } if (col) *this << endl << dec; setFieldWidth(0); setIntegerBase(10); } //--------------------------------------------------------- // htmlToString //--------------------------------------------------------- void XmlReader::htmlToString(int level, QString* s) { *s += QString("<%1").arg(name().toString()); for (const QXmlStreamAttribute& a : attributes()) *s += QString(" %1=\"%2\"").arg(a.name().toString()).arg(a.value().toString()); *s += ">"; ++level; for (;;) { QXmlStreamReader::TokenType t = readNext(); switch(t) { case QXmlStreamReader::StartElement: htmlToString(level, s); break; case QXmlStreamReader::EndElement: *s += QString("").arg(name().toString()); --level; return; case QXmlStreamReader::Characters: if (!isWhitespace()) *s += text().toString().toHtmlEscaped(); break; case QXmlStreamReader::Comment: break; default: qDebug("htmlToString: read token: %s", qPrintable(tokenString())); return; } } } //------------------------------------------------------------------- // readXml // read verbatim until end tag of current level is reached //------------------------------------------------------------------- QString XmlReader::readXml() { QString s; int level = 1; for (;;) { QXmlStreamReader::TokenType t = readNext(); switch(t) { case QXmlStreamReader::StartElement: htmlToString(level, &s); break; case QXmlStreamReader::EndElement: return s; case QXmlStreamReader::Characters: s += text().toString().toHtmlEscaped(); break; case QXmlStreamReader::Comment: break; default: qDebug("htmlToString: read token: %s", qPrintable(tokenString())); return s; } } return s; } //--------------------------------------------------------- // writeXml // string s is already escaped (& -> "&") //--------------------------------------------------------- void Xml::writeXml(const QString& name, QString s) { QString ename(name.split(' ')[0]); putLevel(); for (int i = 0; i < s.size(); ++i) { ushort c = s.at(i).unicode(); if (c < 0x20 && c != 0x09 && c != 0x0A && c != 0x0D) s[i] = '?'; } *this << "<" << name << ">"; *this << s; *this << "\n"; } //--------------------------------------------------------- // spannerValues //--------------------------------------------------------- const SpannerValues* XmlReader::spannerValues(int id) const { for (const SpannerValues& v : _spannerValues) { if (v.spannerId == id) return &v; } return 0; } //--------------------------------------------------------- // addSpanner //--------------------------------------------------------- void XmlReader::addSpanner(int id, Spanner* s) { _spanner.append(std::pair(id, s)); } //--------------------------------------------------------- // removeSpanner //--------------------------------------------------------- void XmlReader::removeSpanner(const Spanner* s) { for (auto i : _spanner) { if (i.second == s) { _spanner.removeOne(i); return; } } } //--------------------------------------------------------- // findSpanner //--------------------------------------------------------- Spanner* XmlReader::findSpanner(int id) { for (auto i : _spanner) { if (i.first == id) return i.second; } return nullptr; } //--------------------------------------------------------- // spannerId //--------------------------------------------------------- int XmlReader::spannerId(const Spanner* s) { for (auto i : _spanner) { if (i.second == s) return i.first; } qDebug("XmlReader::spannerId not found"); return -1; } //--------------------------------------------------------- // addSpanner //--------------------------------------------------------- int Xml::addSpanner(const Spanner* s) { ++_spannerId; _spanner.append(std::pair(_spannerId, s)); return _spannerId; } //--------------------------------------------------------- // findSpanner //--------------------------------------------------------- const Spanner* Xml::findSpanner(int id) { for (auto i : _spanner) { if (i.first == id) return i.second; } return nullptr; } //--------------------------------------------------------- // spannerId //--------------------------------------------------------- int Xml::spannerId(const Spanner* s) { for (auto i : _spanner) { if (i.second == s) return i.first; } return addSpanner(s); } //--------------------------------------------------------- // canWrite //--------------------------------------------------------- bool Xml::canWrite(const Element* e) const { if (!clipboardmode) return true; return _filter.canSelect(e); } //--------------------------------------------------------- // canWriteVoice //--------------------------------------------------------- bool Xml::canWriteVoice(int track) const { if (!clipboardmode) return true; return _filter.canSelectVoice(track); } }