1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2016 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "xml.h"
14 #include "measure.h"
15 #include "score.h"
16 #include "spanner.h"
17 #include "staff.h"
18 #include "beam.h"
19 #include "tuplet.h"
20
21 namespace Ms {
22
23 //---------------------------------------------------------
24 // ~XmlReader
25 //---------------------------------------------------------
26
~XmlReader()27 XmlReader::~XmlReader()
28 {
29 if (!_connectors.empty() || !_pendingConnectors.empty()) {
30 qDebug("XmlReader::~XmlReader: there are unpaired connectors left");
31 for (auto& c : _connectors) {
32 Element* conn = c->releaseConnector();
33 if (conn && !conn->isTuplet()) // tuplets are added to score even when not finished
34 delete conn;
35 }
36 for (auto& c : _pendingConnectors)
37 delete c->releaseConnector();
38 }
39 }
40
41 //---------------------------------------------------------
42 // intAttribute
43 //---------------------------------------------------------
44
intAttribute(const char * s,int _default) const45 int XmlReader::intAttribute(const char* s, int _default) const
46 {
47 if (attributes().hasAttribute(s))
48 // return attributes().value(s).toString().toInt();
49 return attributes().value(s).toInt();
50 else
51 return _default;
52 }
53
intAttribute(const char * s) const54 int XmlReader::intAttribute(const char* s) const
55 {
56 return attributes().value(s).toInt();
57 }
58
59 //---------------------------------------------------------
60 // doubleAttribute
61 //---------------------------------------------------------
62
doubleAttribute(const char * s) const63 double XmlReader::doubleAttribute(const char* s) const
64 {
65 return attributes().value(s).toDouble();
66 }
67
doubleAttribute(const char * s,double _default) const68 double XmlReader::doubleAttribute(const char* s, double _default) const
69 {
70 if (attributes().hasAttribute(s))
71 return attributes().value(s).toDouble();
72 else
73 return _default;
74 }
75
76 //---------------------------------------------------------
77 // attribute
78 //---------------------------------------------------------
79
attribute(const char * s,const QString & _default) const80 QString XmlReader::attribute(const char* s, const QString& _default) const
81 {
82 if (attributes().hasAttribute(s))
83 return attributes().value(s).toString();
84 else
85 return _default;
86 }
87
88 //---------------------------------------------------------
89 // hasAttribute
90 //---------------------------------------------------------
91
hasAttribute(const char * s) const92 bool XmlReader::hasAttribute(const char* s) const
93 {
94 return attributes().hasAttribute(s);
95 }
96
97 //---------------------------------------------------------
98 // readPoint
99 //---------------------------------------------------------
100
readPoint()101 QPointF XmlReader::readPoint()
102 {
103 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
104 #ifndef NDEBUG
105 if (!attributes().hasAttribute("x")) {
106 QXmlStreamAttributes map = attributes();
107 qDebug("XmlReader::readPoint: x attribute missing: %s (%d)",
108 name().toUtf8().data(), map.size());
109 for (int i = 0; i < map.size(); ++i) {
110 const QXmlStreamAttribute& a = map.at(i);
111 qDebug(" attr <%s> <%s>", a.name().toUtf8().data(), a.value().toUtf8().data());
112 }
113 unknown();
114 }
115 if (!attributes().hasAttribute("y")) {
116 qDebug("XmlReader::readPoint: y attribute missing: %s", name().toUtf8().data());
117 unknown();
118 }
119 #endif
120 qreal x = doubleAttribute("x", 0.0);
121 qreal y = doubleAttribute("y", 0.0);
122 readNext();
123 return QPointF(x, y);
124 }
125
126 //---------------------------------------------------------
127 // readColor
128 //---------------------------------------------------------
129
readColor()130 QColor XmlReader::readColor()
131 {
132 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
133 QColor c;
134 c.setRed(intAttribute("r"));
135 c.setGreen(intAttribute("g"));
136 c.setBlue(intAttribute("b"));
137 c.setAlpha(intAttribute("a", 255));
138 skipCurrentElement();
139 return c;
140 }
141
142 //---------------------------------------------------------
143 // readSize
144 //---------------------------------------------------------
145
readSize()146 QSizeF XmlReader::readSize()
147 {
148 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
149 QSizeF p;
150 p.setWidth(doubleAttribute("w", 0.0));
151 p.setHeight(doubleAttribute("h", 0.0));
152 skipCurrentElement();
153 return p;
154 }
155
156 //---------------------------------------------------------
157 // readRect
158 //---------------------------------------------------------
159
readRect()160 QRectF XmlReader::readRect()
161 {
162 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
163 QRectF p;
164 p.setX(doubleAttribute("x", 0.0));
165 p.setY(doubleAttribute("y", 0.0));
166 p.setWidth(doubleAttribute("w", 0.0));
167 p.setHeight(doubleAttribute("h", 0.0));
168 skipCurrentElement();
169 return p;
170 }
171
172 //---------------------------------------------------------
173 // readFraction
174 // recognizes this two styles:
175 // <move z="2" n="4"/> (old style)
176 // <move>2/4</move> (new style)
177 //---------------------------------------------------------
178
readFraction()179 Fraction XmlReader::readFraction()
180 {
181 Q_ASSERT(tokenType() == QXmlStreamReader::StartElement);
182 int z = attribute("z", "0").toInt();
183 int n = attribute("n", "1").toInt();
184 const QString& s(readElementText());
185 if (!s.isEmpty()) {
186 int i = s.indexOf('/');
187 if (i == -1) {
188 return Fraction::fromTicks(s.toInt());
189 }
190 else {
191 z = s.leftRef(i).toInt();
192 n = s.midRef(i+1).toInt();
193 }
194 }
195 return Fraction(z, n);
196 }
197
198 //---------------------------------------------------------
199 // unknown
200 // unknown tag read
201 //---------------------------------------------------------
202
unknown()203 void XmlReader::unknown()
204 {
205 if (QXmlStreamReader::error())
206 qDebug("%s ", qPrintable(errorString()));
207 if (!docName.isEmpty())
208 qDebug("tag in <%s> line %lld col %lld: %s",
209 qPrintable(docName), lineNumber() + _offsetLines, columnNumber(), name().toUtf8().data());
210 else
211 qDebug("line %lld col %lld: %s", lineNumber() + _offsetLines, columnNumber(), name().toUtf8().data());
212 skipCurrentElement();
213 }
214
215 //---------------------------------------------------------
216 // location
217 //---------------------------------------------------------
218
location(bool forceAbsFrac) const219 Location XmlReader::location(bool forceAbsFrac) const
220 {
221 Location l = Location::absolute();
222 fillLocation(l, forceAbsFrac);
223 return l;
224 }
225
226 //---------------------------------------------------------
227 // fillLocation
228 // fills location fields which have default values with
229 // values relevant for the current reader's position.
230 // When in paste mode (or forceAbsFrac is true) absolute
231 // fraction values are used and measure number is set to
232 // zero.
233 //---------------------------------------------------------
234
fillLocation(Location & l,bool forceAbsFrac) const235 void XmlReader::fillLocation(Location& l, bool forceAbsFrac) const
236 {
237 constexpr Location defaults = Location::absolute();
238 const bool absFrac = (pasteMode() || forceAbsFrac);
239 if (l.track() == defaults.track())
240 l.setTrack(track());
241 if (l.frac() == defaults.frac())
242 l.setFrac(absFrac ? tick() : rtick());
243 if (l.measure() == defaults.measure())
244 l.setMeasure(absFrac ? 0 : currentMeasureIndex());
245 }
246
247 //---------------------------------------------------------
248 // setLocation
249 // sets a new reading location, taking into account its
250 // type (absolute or relative).
251 //---------------------------------------------------------
252
setLocation(const Location & l)253 void XmlReader::setLocation(const Location& l)
254 {
255 if (l.isRelative()) {
256 Location newLoc = l;
257 newLoc.toAbsolute(location());
258 int intTicks = l.frac().ticks();
259 if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
260 _intTick += intTicks;
261 setTrack(newLoc.track() - _trackOffset);
262 return;
263 }
264 setLocation(newLoc); // recursion
265 return;
266 }
267 setTrack(l.track() - _trackOffset);
268 setTick(l.frac() - _tickOffset);
269 if (!pasteMode()) {
270 Q_ASSERT(l.measure() == currentMeasureIndex());
271 incTick(currentMeasure()->tick());
272 }
273 }
274
275 //---------------------------------------------------------
276 // addBeam
277 //---------------------------------------------------------
278
addBeam(Beam * s)279 void XmlReader::addBeam(Beam* s)
280 {
281 _beams.insert(s->id(), s);
282 }
283
284 //---------------------------------------------------------
285 // addTuplet
286 //---------------------------------------------------------
287
addTuplet(Tuplet * s)288 void XmlReader::addTuplet(Tuplet* s)
289 {
290 _tuplets.insert(s->id(), s);
291 }
292
293 //---------------------------------------------------------
294 // readDouble
295 //---------------------------------------------------------
296
readDouble(double min,double max)297 double XmlReader::readDouble(double min, double max)
298 {
299 double val = readElementText().toDouble();
300 if (val < min)
301 val = min;
302 else if (val > max)
303 val = max;
304 return val;
305 }
306
307 //---------------------------------------------------------
308 // readBool
309 //---------------------------------------------------------
310
readBool()311 bool XmlReader::readBool()
312 {
313 bool val;
314 QXmlStreamReader::TokenType tt = readNext();
315 if (tt == QXmlStreamReader::Characters) {
316 val = text().toInt() != 0;
317 readNext();
318 }
319 else
320 val = true;
321 return val;
322 }
323
324 //---------------------------------------------------------
325 // checkTuplets
326 //---------------------------------------------------------
327
checkTuplets()328 void XmlReader::checkTuplets()
329 {
330 for (Tuplet* tuplet : tuplets()) {
331 if (tuplet->elements().empty()) {
332 // this should not happen and is a sign of input file corruption
333 qDebug("Measure:read(): empty tuplet id %d (%p), input file corrupted?",
334 tuplet->id(), tuplet);
335 delete tuplet;
336 }
337 else {
338 //sort tuplet elements. Needed for nested tuplets #22537
339 tuplet->sortElements();
340 tuplet->sanitizeTuplet();
341 }
342 }
343 // This requires a separate pass in case of nested tuplets that required sanitizing
344 for (Tuplet* tuplet : tuplets())
345 tuplet->addMissingElements();
346 }
347
348 //---------------------------------------------------------
349 // htmlToString
350 //---------------------------------------------------------
351
htmlToString(int level,QString * s)352 void XmlReader::htmlToString(int level, QString* s)
353 {
354 *s += QString("<%1").arg(name().toString());
355 for (const QXmlStreamAttribute& a : attributes())
356 *s += QString(" %1=\"%2\"").arg(a.name().toString(), a.value().toString());
357 *s += ">";
358 ++level;
359 for (;;) {
360 QXmlStreamReader::TokenType t = readNext();
361 switch(t) {
362 case QXmlStreamReader::StartElement:
363 htmlToString(level, s);
364 break;
365 case QXmlStreamReader::EndElement:
366 *s += QString("</%1>").arg(name().toString());
367 --level;
368 return;
369 case QXmlStreamReader::Characters:
370 if (!isWhitespace())
371 *s += text().toString().toHtmlEscaped();
372 break;
373 case QXmlStreamReader::Comment:
374 break;
375
376 default:
377 qDebug("htmlToString: read token: %s", qPrintable(tokenString()));
378 return;
379 }
380 }
381 }
382
383 //-------------------------------------------------------------------
384 // readXml
385 // read verbatim until end tag of current level is reached
386 //-------------------------------------------------------------------
387
readXml()388 QString XmlReader::readXml()
389 {
390 QString s;
391 int level = 1;
392 for (QXmlStreamReader::TokenType t = readNext(); t != QXmlStreamReader::EndElement; t = readNext()) {
393 switch (t) {
394 case QXmlStreamReader::StartElement:
395 htmlToString(level, &s);
396 break;
397 case QXmlStreamReader::EndElement:
398 break;
399 case QXmlStreamReader::Characters:
400 s += text().toString().toHtmlEscaped();
401 break;
402 case QXmlStreamReader::Comment:
403 break;
404
405 default:
406 qDebug("htmlToString: read token: %s", qPrintable(tokenString()));
407 return s;
408 }
409 }
410 return s;
411 }
412
413 //---------------------------------------------------------
414 // readPlacement
415 //---------------------------------------------------------
416
readPlacement(XmlReader & e)417 PlaceText readPlacement(XmlReader& e)
418 {
419 const QString& s(e.readElementText());
420 if (s == "auto" || s == "0")
421 return PlaceText::AUTO;
422 if (s == "above" || s == "1")
423 return PlaceText::ABOVE;
424 if (s == "below" || s == "2")
425 return PlaceText::BELOW;
426 if (s == "left" || s == "3")
427 return PlaceText::LEFT;
428 qDebug("unknown placement value <%s>", qPrintable(s));
429 return PlaceText::AUTO;
430 }
431
432 //---------------------------------------------------------
433 // spannerValues
434 //---------------------------------------------------------
435
spannerValues(int id) const436 const SpannerValues* XmlReader::spannerValues(int id) const
437 {
438 for (const SpannerValues& v : _spannerValues) {
439 if (v.spannerId == id)
440 return &v;
441 }
442 return 0;
443 }
444
445 //---------------------------------------------------------
446 // addSpanner
447 //---------------------------------------------------------
448
addSpanner(int id,Spanner * s)449 void XmlReader::addSpanner(int id, Spanner* s)
450 {
451 _spanner.append(std::pair<int, Spanner*>(id, s));
452 }
453
454 //---------------------------------------------------------
455 // removeSpanner
456 //---------------------------------------------------------
457
removeSpanner(const Spanner * s)458 void XmlReader::removeSpanner(const Spanner* s)
459 {
460 for (auto i : qAsConst(_spanner)) {
461 if (i.second == s) {
462 _spanner.removeOne(i);
463 return;
464 }
465 }
466 }
467
468 //---------------------------------------------------------
469 // findSpanner
470 //---------------------------------------------------------
471
findSpanner(int id)472 Spanner* XmlReader::findSpanner(int id)
473 {
474 for (auto i : qAsConst(_spanner)) {
475 if (i.first == id)
476 return i.second;
477 }
478 return nullptr;
479 }
480
481 //---------------------------------------------------------
482 // spannerId
483 //---------------------------------------------------------
484
spannerId(const Spanner * s)485 int XmlReader::spannerId(const Spanner* s)
486 {
487 for (auto i : qAsConst(_spanner)) {
488 if (i.second == s)
489 return i.first;
490 }
491 qDebug("XmlReader::spannerId not found");
492 return -1;
493 }
494
495 //---------------------------------------------------------
496 // addUserTextStyle
497 // return false if mapping is not possible
498 // (too many user text styles)
499 //---------------------------------------------------------
500
addUserTextStyle(const QString & name)501 Tid XmlReader::addUserTextStyle(const QString& name)
502 {
503 qDebug("%s", qPrintable(name));
504 Tid id = Tid::TEXT_STYLES;
505 if (userTextStyles.size() == 0)
506 id = Tid::USER1;
507 else if (userTextStyles.size() == 1)
508 id = Tid::USER2;
509 else if (userTextStyles.size() == 2)
510 id = Tid::USER3;
511 else if (userTextStyles.size() == 3)
512 id = Tid::USER4;
513 else if (userTextStyles.size() == 4)
514 id = Tid::USER5;
515 else if (userTextStyles.size() == 5)
516 id = Tid::USER6;
517 else if (userTextStyles.size() == 6)
518 id = Tid::USER7;
519 else if (userTextStyles.size() == 7)
520 id = Tid::USER8;
521 else if (userTextStyles.size() == 8)
522 id = Tid::USER9;
523 else if (userTextStyles.size() == 9)
524 id = Tid::USER10;
525 else if (userTextStyles.size() == 10)
526 id = Tid::USER11;
527 else if (userTextStyles.size() == 11)
528 id = Tid::USER12;
529 else
530 qDebug("too many user defined textstyles");
531 if (id != Tid::TEXT_STYLES)
532 userTextStyles.push_back({name, id});
533 return id;
534 }
535
536 //---------------------------------------------------------
537 // lookupUserTextStyle
538 //---------------------------------------------------------
539
lookupUserTextStyle(const QString & name) const540 Tid XmlReader::lookupUserTextStyle(const QString& name) const
541 {
542 for (const auto& i : userTextStyles) {
543 if (i.name == name)
544 return i.ss;
545 }
546 return Tid::TEXT_STYLES; // not found
547 }
548
549 //---------------------------------------------------------
550 // addConnectorInfo
551 //---------------------------------------------------------
552
addConnectorInfo(std::unique_ptr<ConnectorInfoReader> c)553 void XmlReader::addConnectorInfo(std::unique_ptr<ConnectorInfoReader> c)
554 {
555 _connectors.push_back(std::move(c));
556 ConnectorInfoReader* c1 = _connectors.back().get();
557 c1->update();
558 for (std::unique_ptr<ConnectorInfoReader>& c2 : _connectors) {
559 if (c2->connect(c1)) {
560 if (c2->finished()) {
561 c2->addToScore(pasteMode());
562 removeConnector(c2.get());
563 }
564 break;
565 }
566 }
567 }
568
569 //---------------------------------------------------------
570 // removeConnector
571 //---------------------------------------------------------
572
removeConnector(const ConnectorInfoReader * c)573 void XmlReader::removeConnector(const ConnectorInfoReader* c)
574 {
575 while (c->prev())
576 c = c->prev();
577 while (c) {
578 ConnectorInfoReader* next = c->next();
579 for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
580 if (it->get() == c) {
581 _connectors.erase(it);
582 break;
583 }
584 }
585 c = next;
586 }
587 }
588
589 //---------------------------------------------------------
590 // checkConnectors
591 //---------------------------------------------------------
592
checkConnectors()593 void XmlReader::checkConnectors()
594 {
595 for (std::unique_ptr<ConnectorInfoReader>& c : _pendingConnectors)
596 addConnectorInfo(std::move(c));
597 _pendingConnectors.clear();
598 }
599
600 //---------------------------------------------------------
601 // distanceSort
602 //---------------------------------------------------------
603
distanceSort(const QPair<int,QPair<ConnectorInfoReader *,ConnectorInfoReader * >> & p1,const QPair<int,QPair<ConnectorInfoReader *,ConnectorInfoReader * >> & p2)604 static bool distanceSort(const QPair<int, QPair<ConnectorInfoReader*, ConnectorInfoReader*>>& p1, const QPair<int, QPair<ConnectorInfoReader*, ConnectorInfoReader*>>& p2)
605 {
606 return p1.first < p2.first;
607 }
608
609 //---------------------------------------------------------
610 // reconnectBrokenConnectors
611 //---------------------------------------------------------
612
reconnectBrokenConnectors()613 void XmlReader::reconnectBrokenConnectors()
614 {
615 if (_connectors.empty())
616 return;
617 qDebug("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
618 QList<QPair<int, QPair<ConnectorInfoReader*, ConnectorInfoReader*>>> brokenPairs;
619 for (size_t i = 1; i < _connectors.size(); ++i) {
620 for (size_t j = 0; j < i; ++j) {
621 ConnectorInfoReader* c1 = _connectors[i].get();
622 ConnectorInfoReader* c2 = _connectors[j].get();
623 int d = c1->connectionDistance(*c2);
624 if (d >= 0)
625 brokenPairs.append(qMakePair(d, qMakePair(c1, c2)));
626 else
627 brokenPairs.append(qMakePair(-d, qMakePair(c2, c1)));
628 }
629 }
630 std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
631 for (auto& distPair : brokenPairs) {
632 if (distPair.first == INT_MAX)
633 continue;
634 auto& pair = distPair.second;
635 if (pair.first->next() || pair.second->prev())
636 continue;
637 pair.first->forceConnect(pair.second);
638 }
639 QSet<ConnectorInfoReader*> reconnected;
640 for (auto& conn : _connectors) {
641 ConnectorInfoReader* c = conn.get();
642 if (c->finished())
643 reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
644 }
645 for (ConnectorInfoReader* cptr : reconnected) {
646 cptr->addToScore(pasteMode());
647 removeConnector(cptr);
648 }
649 qDebug("reconnected %d broken connectors", reconnected.count());
650 }
651
652 //---------------------------------------------------------
653 // addLink
654 //---------------------------------------------------------
655
addLink(Staff * s,LinkedElements * link)656 void XmlReader::addLink(Staff* s, LinkedElements* link)
657 {
658 int staff = s->idx();
659 const bool masterScore = s->score()->isMaster();
660 if (!masterScore)
661 staff *= -1;
662
663 QList<QPair<LinkedElements*, Location>>& staffLinks = _staffLinkedElements[staff];
664 if (!masterScore) {
665 if (!staffLinks.empty()
666 && (link->mainElement()->score() != staffLinks.front().first->mainElement()->score())
667 )
668 staffLinks.clear();
669 }
670
671 Location l = location(true);
672 _linksIndexer.assignLocalIndex(l);
673 staffLinks.push_back(qMakePair(link, l));
674 }
675
676 //---------------------------------------------------------
677 // getLink
678 //---------------------------------------------------------
679
getLink(bool masterScore,const Location & l,int localIndexDiff)680 LinkedElements* XmlReader::getLink(bool masterScore, const Location& l, int localIndexDiff)
681 {
682 int staff = l.staff();
683 if (!masterScore)
684 staff *= -1;
685 const int localIndex = _linksIndexer.assignLocalIndex(l) + localIndexDiff;
686 QList<QPair<LinkedElements*, Location>>& staffLinks = _staffLinkedElements[staff];
687
688 if (!staffLinks.isEmpty() && staffLinks.constLast().second == l) {
689 // This element potentially affects local index for "main"
690 // elements that may go afterwards at the same tick, so
691 // append it to staffLinks as well.
692 staffLinks.push_back(staffLinks.constLast()); // nothing should reference exactly this local index, so it shouldn't matter what to append
693 }
694
695 for (int i = 0; i < staffLinks.size(); ++i) {
696 if (staffLinks[i].second == l) {
697 if (localIndex == 0)
698 return staffLinks[i].first;
699 i += localIndex;
700 if ((i < 0) || (i >= staffLinks.size()))
701 return nullptr;
702 if (staffLinks[i].second == l)
703 return staffLinks[i].first;
704 return nullptr;
705 }
706 }
707 return nullptr;
708 }
709
710 //---------------------------------------------------------
711 // assignLocalIndex
712 //---------------------------------------------------------
713
assignLocalIndex(const Location & mainElementLocation)714 int LinksIndexer::assignLocalIndex(const Location& mainElementLocation)
715 {
716 if (_lastLinkedElementLoc == mainElementLocation)
717 return (++_lastLocalIndex);
718 _lastLocalIndex = 0;
719 _lastLinkedElementLoc = mainElementLocation;
720 return 0;
721 }
722
723 //---------------------------------------------------------
724 // rtick
725 // return relative position in measure
726 //---------------------------------------------------------
727
rtick() const728 Fraction XmlReader::rtick() const
729 {
730 return _curMeasure ? _tick - _curMeasure->tick() : _tick;
731 }
732
733 //---------------------------------------------------------
734 // setTick
735 //---------------------------------------------------------
736
setTick(const Fraction & f)737 void XmlReader::setTick(const Fraction& f)
738 {
739 _tick = f.reduced();
740 _intTick = _tick.ticks();
741 }
742
743 //---------------------------------------------------------
744 // incTick
745 //---------------------------------------------------------
746
incTick(const Fraction & f)747 void XmlReader::incTick(const Fraction& f)
748 {
749 _tick += f;
750 _tick.reduce();
751 _intTick += f.ticks();
752 }
753 }
754
755
756