Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "readcontext.h"
24 :
25 : #include "global/containers.h"
26 :
27 : #include "engraving/dom/beam.h"
28 : #include "engraving/dom/linkedobjects.h"
29 : #include "engraving/dom/measure.h"
30 : #include "engraving/dom/score.h"
31 : #include "engraving/dom/staff.h"
32 : #include "engraving/dom/tuplet.h"
33 : #include "engraving/editing/transaction/undostack.h"
34 : #include "engraving/types/types.h"
35 :
36 : #include "connectorinforeader.h"
37 :
38 : #include "log.h"
39 :
40 : using namespace mu;
41 : using namespace mu::engraving;
42 : using namespace mu::engraving::read400;
43 :
44 0 : ReadContext::ReadContext(const muse::modularity::ContextPtr& iocCtx)
45 0 : : muse::Contextable(iocCtx)
46 : {
47 0 : }
48 :
49 12968 : ReadContext::ReadContext(Score* score)
50 12968 : : muse::Contextable(score ? score->iocContext() : muse::modularity::globalCtx()), m_score(score)
51 : {
52 12968 : }
53 :
54 12968 : ReadContext::~ReadContext()
55 : {
56 12968 : if (!_connectors.empty() || !_pendingConnectors.empty()) {
57 0 : LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
58 0 : for (auto& c : _connectors) {
59 0 : EngravingItem* conn = c->releaseConnector();
60 0 : if (conn && !conn->isTuplet()) { // tuplets are added to score even when not finished
61 0 : delete conn;
62 : }
63 : }
64 0 : for (auto& c : _pendingConnectors) {
65 0 : delete c->releaseConnector();
66 : }
67 : }
68 12968 : }
69 :
70 32 : void ReadContext::setScore(Score* score)
71 : {
72 32 : m_score = score;
73 32 : }
74 :
75 10463 : Score* ReadContext::score() const
76 : {
77 10463 : return m_score;
78 : }
79 :
80 785 : const MStyle& ReadContext::style() const
81 : {
82 785 : return score()->style();
83 : }
84 :
85 156 : String ReadContext::mscoreVersion() const
86 : {
87 156 : return m_score->mscoreVersion();
88 : }
89 :
90 4248 : int ReadContext::mscVersion() const
91 : {
92 4248 : return m_score->mscVersion();
93 : }
94 :
95 26 : int ReadContext::fileDivision() const
96 : {
97 26 : return m_score->fileDivision();
98 : }
99 :
100 127 : int ReadContext::fileDivision(int t) const
101 : {
102 127 : return m_score->fileDivision(t);
103 : }
104 :
105 2660 : double ReadContext::spatium() const
106 : {
107 2660 : return m_score->style().spatium();
108 : }
109 :
110 0 : void ReadContext::setSpatium(double v)
111 : {
112 0 : m_score->style().set(Sid::spatium, v);
113 0 : }
114 :
115 10388 : DummyParent* ReadContext::dummy() const
116 : {
117 10388 : return m_score->dummy();
118 : }
119 :
120 6213 : Staff* ReadContext::staff(staff_idx_t n)
121 : {
122 6213 : return m_score->staff(n);
123 : }
124 :
125 93 : void ReadContext::appendStaff(Staff* staff)
126 : {
127 93 : m_score->appendStaff(staff);
128 93 : }
129 :
130 22 : void ReadContext::addSpanner(Spanner* s)
131 : {
132 22 : m_score->addSpanner(s);
133 22 : }
134 :
135 136 : bool ReadContext::undoStackActive() const
136 : {
137 136 : return m_score->undoStack()->hasActiveTransaction();
138 : }
139 :
140 5112 : bool ReadContext::isSameScore(const EngravingObject* obj) const
141 : {
142 5112 : return obj->score() == m_score;
143 : }
144 :
145 73 : rw::ReadLinks ReadContext::readLinks() const
146 : {
147 73 : rw::ReadLinks l;
148 73 : l.linksIndexer = m_linksIndexer;
149 73 : l.staffLinkedElements = m_staffLinkedElements;
150 73 : return l;
151 0 : }
152 :
153 0 : void ReadContext::initLinks(const rw::ReadLinks& l)
154 : {
155 0 : m_linksIndexer = l.linksIndexer;
156 0 : m_staffLinkedElements = l.staffLinkedElements;
157 0 : }
158 :
159 546 : void ReadContext::addLink(Staff* staff, LinkedObjects* link, const Location& location)
160 : {
161 546 : int staffIndex = static_cast<int>(staff->idx());
162 546 : const bool isMasterScore = staff->score()->isMaster();
163 546 : if (!isMasterScore) {
164 3 : staffIndex *= -1;
165 : }
166 :
167 546 : std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
168 546 : if (!isMasterScore) {
169 3 : if (!staffLinks.empty()
170 3 : && (link->mainElement()->score() != staffLinks.front().first->mainElement()->score())
171 : ) {
172 3 : staffLinks.clear();
173 : }
174 : }
175 :
176 546 : m_linksIndexer.assignLocalIndex(location);
177 546 : staffLinks.push_back(std::make_pair(link, location));
178 546 : }
179 :
180 547 : LinkedObjects* ReadContext::getLink(bool isMasterScore, const Location& location, int localIndexDiff)
181 : {
182 547 : int staffIndex = location.staff();
183 547 : if (!isMasterScore) {
184 10 : staffIndex *= -1;
185 : }
186 :
187 547 : const int localIndex = m_linksIndexer.assignLocalIndex(location) + localIndexDiff;
188 547 : std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
189 :
190 547 : if (!staffLinks.empty() && staffLinks.back().second == location) {
191 : // This element potentially affects local index for "main"
192 : // elements that may go afterwards at the same tick, so
193 : // append it to staffLinks as well.
194 42 : staffLinks.push_back(staffLinks.back()); // nothing should reference exactly this local index, so it shouldn't matter what to append
195 : }
196 :
197 12061 : for (size_t i = 0; i < staffLinks.size(); ++i) {
198 12061 : if (staffLinks[i].second == location) {
199 547 : if (localIndex == 0) {
200 239 : return staffLinks[i].first;
201 : }
202 :
203 308 : i += localIndex;
204 308 : if (i >= staffLinks.size()) {
205 0 : return nullptr;
206 : }
207 :
208 308 : if (staffLinks[i].second == location) {
209 308 : return staffLinks[i].first;
210 : }
211 :
212 0 : return nullptr;
213 : }
214 : }
215 :
216 0 : return nullptr;
217 : }
218 :
219 3143 : Fraction ReadContext::rtick() const
220 : {
221 3143 : return _curMeasure ? _tick - _curMeasure->tick() : _tick;
222 : }
223 :
224 12438 : void ReadContext::setTick(const Fraction& f)
225 : {
226 12438 : _tick = f.reduced();
227 12438 : _intTick = _tick.ticks();
228 12438 : }
229 :
230 13593 : void ReadContext::incTick(const Fraction& f)
231 : {
232 13593 : _tick += f;
233 13593 : _tick.reduce();
234 13593 : _intTick += f.ticks();
235 13593 : }
236 :
237 1174 : Location ReadContext::location(bool forceAbsFrac) const
238 : {
239 1174 : Location l = Location::absolute();
240 1174 : fillLocation(l, forceAbsFrac);
241 1174 : return l;
242 : }
243 :
244 : //---------------------------------------------------------
245 : // fillLocation
246 : // fills location fields which have default values with
247 : // values relevant for the current reader's position.
248 : // When in paste mode (or forceAbsFrac is true) absolute
249 : // fraction values are used and measure number is set to
250 : // zero.
251 : //---------------------------------------------------------
252 :
253 4236 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
254 : {
255 4236 : constexpr Location defaults = Location::absolute();
256 4236 : const bool absFrac = (pasteMode() || forceAbsFrac);
257 4236 : if (l.track() == defaults.track()) {
258 1174 : l.setTrack(static_cast<int>(track()));
259 : }
260 4236 : if (l.frac() == defaults.frac()) {
261 4236 : l.setFrac(absFrac ? tick() : rtick());
262 : }
263 4236 : if (l.measure() == defaults.measure()) {
264 4236 : l.setMeasure(absFrac ? 0 : currentMeasureIndex());
265 : }
266 4236 : }
267 :
268 : //---------------------------------------------------------
269 : // setLocation
270 : // sets a new reading location, taking into account its
271 : // type (absolute or relative).
272 : //---------------------------------------------------------
273 :
274 162 : void ReadContext::setLocation(const Location& l)
275 : {
276 162 : if (l.isRelative()) {
277 81 : Location newLoc = l;
278 81 : newLoc.toAbsolute(location());
279 81 : int intTicks = l.frac().ticks();
280 81 : if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
281 0 : _intTick += intTicks;
282 0 : setTrack(newLoc.track() - _trackOffset);
283 0 : return;
284 : }
285 81 : setLocation(newLoc); // recursion
286 81 : return;
287 : }
288 81 : setTrack(l.track() - _trackOffset);
289 81 : setTick(l.frac() - _tickOffset);
290 81 : if (!pasteMode()) {
291 81 : assert(l.measure() == currentMeasureIndex());
292 81 : incTick(currentMeasure()->tick());
293 : }
294 : }
295 :
296 87 : size_t ReadContext::getStaffBarLineSpan(const staff_idx_t staffIdx) const
297 : {
298 87 : return muse::value(m_staffBarLineSpanValues, staffIdx, 0);
299 : }
300 :
301 9 : void ReadContext::setStaffBarLineSpan(const staff_idx_t staffIdx, const size_t barLineSpan)
302 : {
303 9 : const auto [it, didInsert] = m_staffBarLineSpanValues.emplace(staffIdx, barLineSpan);
304 9 : DO_ASSERT(didInsert)
305 9 : }
306 :
307 55 : std::optional<size_t> ReadContext::getBarLineSpan(const BarLine* barLine)
308 : {
309 55 : if (!muse::contains(m_barLineSpanValues, barLine)) {
310 0 : return std::nullopt;
311 : }
312 :
313 55 : return muse::value(m_barLineSpanValues, barLine);
314 : }
315 :
316 64 : void ReadContext::setBarLineSpan(const BarLine* barLine, const size_t barLineSpan)
317 : {
318 64 : const auto [it, didInsert] = m_barLineSpanValues.emplace(barLine, barLineSpan);
319 64 : DO_ASSERT(didInsert)
320 64 : }
321 :
322 22 : void ReadContext::addBeam(int beamId, Beam* s)
323 : {
324 22 : _beams.insert_or_assign(beamId, s);
325 22 : }
326 :
327 27 : void ReadContext::addTuplet(int tupletId, Tuplet* s)
328 : {
329 27 : _tuplets.insert_or_assign(tupletId, s);
330 27 : }
331 :
332 887 : void ReadContext::checkTuplets()
333 : {
334 914 : for (auto& p : tuplets()) {
335 27 : Tuplet* tuplet = p.second;
336 27 : if (tuplet->elements().empty()) {
337 : // this should not happen and is a sign of input file corruption
338 0 : LOGD("Measure:read(): empty tuplet id %d (%p), input file corrupted?", p.first, tuplet);
339 0 : delete tuplet;
340 : } else {
341 : //sort tuplet elements. Needed for nested tuplets #22537
342 27 : tuplet->sortElements();
343 27 : tuplet->sanitizeTuplet();
344 : }
345 : }
346 : // This requires a separate pass in case of nested tuplets that required sanitizing
347 914 : for (auto& p : tuplets()) {
348 27 : Tuplet* tuplet = p.second;
349 27 : tuplet->addMissingElements();
350 : }
351 887 : }
352 :
353 22 : void ReadContext::addSpanner(int id, Spanner* s)
354 : {
355 22 : _spanner.push_back(std::pair<int, Spanner*>(id, s));
356 22 : }
357 :
358 0 : void ReadContext::removeSpanner(const Spanner* s)
359 : {
360 0 : for (auto i : _spanner) {
361 0 : if (i.second == s) {
362 0 : muse::remove(_spanner, i);
363 0 : return;
364 : }
365 : }
366 : }
367 :
368 38 : Spanner* ReadContext::findSpanner(int id)
369 : {
370 284 : for (auto i : _spanner) {
371 284 : if (i.first == id) {
372 38 : return i.second;
373 : }
374 : }
375 0 : return nullptr;
376 : }
377 :
378 6 : int ReadContext::spannerId(const Spanner* s)
379 : {
380 12 : for (auto i : _spanner) {
381 12 : if (i.second == s) {
382 6 : return i.first;
383 : }
384 : }
385 0 : LOGD() << "spannerId not found";
386 0 : return -1;
387 : }
388 :
389 6 : const SpannerValues* ReadContext::spannerValues(int id) const
390 : {
391 6 : for (const SpannerValues& v : _spannerValues) {
392 0 : if (v.spannerId == id) {
393 0 : return &v;
394 : }
395 : }
396 6 : return 0;
397 : }
398 :
399 1531 : void ReadContext::removeConnector(const ConnectorInfoReader* c)
400 : {
401 1540 : while (c->prev()) {
402 9 : c = c->prev();
403 : }
404 4593 : while (c) {
405 3062 : ConnectorInfoReader* next = c->next();
406 3936 : for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
407 3936 : if (it->get() == c) {
408 3062 : _connectors.erase(it);
409 3062 : break;
410 : }
411 : }
412 3062 : c = next;
413 : }
414 1531 : }
415 :
416 3062 : void ReadContext::addConnectorInfoLater(std::shared_ptr<read400::ConnectorInfoReader> c)
417 : {
418 3062 : _pendingConnectors.push_back(c);
419 3062 : }
420 :
421 5112 : void ReadContext::checkConnectors()
422 : {
423 8174 : for (std::shared_ptr<ConnectorInfoReader>& c : _pendingConnectors) {
424 3062 : addConnectorInfo(c);
425 : }
426 5112 : _pendingConnectors.clear();
427 5112 : }
428 :
429 3062 : void ReadContext::addConnectorInfo(std::shared_ptr<ConnectorInfoReader> c)
430 : {
431 3062 : _connectors.push_back(std::move(c));
432 3062 : ConnectorInfoReader* c1 = _connectors.back().get();
433 3062 : c1->update();
434 5462 : for (std::shared_ptr<ConnectorInfoReader>& c2 : _connectors) {
435 3929 : if (c2->connect(c1)) {
436 1529 : if (c2->finished()) {
437 1529 : c2->addToScore(pasteMode());
438 1529 : removeConnector(c2.get());
439 : }
440 1529 : break;
441 : }
442 : }
443 3062 : }
444 :
445 14 : static bool distanceSort(const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p1,
446 : const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p2)
447 : {
448 14 : return p1.first < p2.first;
449 : }
450 :
451 : //---------------------------------------------------------
452 : // reconnectBrokenConnectors
453 : //---------------------------------------------------------
454 :
455 503 : void ReadContext::reconnectBrokenConnectors()
456 : {
457 503 : if (_connectors.empty()) {
458 502 : return;
459 : }
460 1 : LOGD("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
461 1 : std::vector<std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> > > brokenPairs;
462 4 : for (size_t i = 1; i < _connectors.size(); ++i) {
463 9 : for (size_t j = 0; j < i; ++j) {
464 6 : ConnectorInfoReader* c1 = _connectors[i].get();
465 6 : ConnectorInfoReader* c2 = _connectors[j].get();
466 6 : int d = c1->connectionDistance(*c2);
467 6 : if (d >= 0) {
468 4 : brokenPairs.push_back(std::make_pair(d, std::make_pair(c1, c2)));
469 : } else {
470 2 : brokenPairs.push_back(std::make_pair(-d, std::make_pair(c2, c1)));
471 : }
472 : }
473 : }
474 1 : std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
475 7 : for (auto& distPair : brokenPairs) {
476 6 : if (distPair.first == INT_MAX) {
477 4 : continue;
478 : }
479 2 : auto& pair = distPair.second;
480 2 : if (pair.first->next() || pair.second->prev()) {
481 0 : continue;
482 : }
483 2 : pair.first->forceConnect(pair.second);
484 : }
485 1 : std::set<ConnectorInfoReader*> reconnected;
486 5 : for (auto& conn : _connectors) {
487 4 : ConnectorInfoReader* c = conn.get();
488 4 : if (c->finished()) {
489 4 : reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
490 : }
491 : }
492 3 : for (ConnectorInfoReader* cptr : reconnected) {
493 2 : cptr->addToScore(pasteMode());
494 2 : removeConnector(cptr);
495 : }
496 1 : LOGD() << "reconnected broken connectors: " << reconnected.size();
497 1 : }
498 :
499 73 : void ReadContext::clearOrphanedConnectors()
500 : {
501 73 : if (_connectors.empty() && _pendingConnectors.empty()) {
502 73 : return;
503 : }
504 :
505 0 : LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
506 :
507 0 : std::set<LinkedObjects*> deletedLinks;
508 :
509 0 : auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
510 0 : EngravingItem* conn = c ? c->releaseConnector() : nullptr;
511 0 : if (!conn) {
512 0 : return;
513 : }
514 :
515 0 : LinkedObjects* links = conn->links();
516 0 : bool linksWillBeDeleted = links && links->size() == 1;
517 :
518 0 : if (!conn->isTuplet()) { // tuplets are added to score even when not finished
519 0 : if (linksWillBeDeleted) {
520 0 : deletedLinks.insert(links);
521 : }
522 :
523 0 : delete conn;
524 : }
525 0 : };
526 :
527 0 : if (!_connectors.empty()) {
528 0 : for (auto& c : _connectors) {
529 0 : deleteConnectors(c);
530 : }
531 0 : _connectors.clear();
532 : }
533 :
534 0 : if (!_pendingConnectors.empty()) {
535 0 : for (auto& c : _pendingConnectors) {
536 0 : deleteConnectors(c);
537 : }
538 0 : _pendingConnectors.clear();
539 : }
540 :
541 0 : for (auto& it : m_staffLinkedElements) {
542 0 : std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
543 0 : muse::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
544 0 : return deletedLinks.count(pair.first);
545 : });
546 : }
547 0 : }
548 :
549 : //---------------------------------------------------------
550 : // addUserTextStyle
551 : // return false if mapping is not possible
552 : // (too many user text styles)
553 : //---------------------------------------------------------
554 :
555 30 : TextStyleType ReadContext::addUserTextStyle(const String& name)
556 : {
557 30 : LOGD() << name;
558 30 : TextStyleType id = TextStyleType::TEXT_TYPES;
559 30 : if (userTextStyles.size() == 0) {
560 5 : id = TextStyleType::USER1;
561 25 : } else if (userTextStyles.size() == 1) {
562 5 : id = TextStyleType::USER2;
563 20 : } else if (userTextStyles.size() == 2) {
564 4 : id = TextStyleType::USER3;
565 16 : } else if (userTextStyles.size() == 3) {
566 4 : id = TextStyleType::USER4;
567 12 : } else if (userTextStyles.size() == 4) {
568 4 : id = TextStyleType::USER5;
569 8 : } else if (userTextStyles.size() == 5) {
570 4 : id = TextStyleType::USER6;
571 4 : } else if (userTextStyles.size() == 6) {
572 4 : id = TextStyleType::USER7;
573 0 : } else if (userTextStyles.size() == 7) {
574 0 : id = TextStyleType::USER8;
575 0 : } else if (userTextStyles.size() == 8) {
576 0 : id = TextStyleType::USER9;
577 0 : } else if (userTextStyles.size() == 9) {
578 0 : id = TextStyleType::USER10;
579 0 : } else if (userTextStyles.size() == 10) {
580 0 : id = TextStyleType::USER11;
581 0 : } else if (userTextStyles.size() == 11) {
582 0 : id = TextStyleType::USER12;
583 : } else {
584 0 : LOGD() << "too many user defined textstyles";
585 : }
586 30 : if (id != TextStyleType::TEXT_TYPES) {
587 30 : userTextStyles.push_back({ name, id });
588 : }
589 30 : return id;
590 30 : }
591 :
592 73 : TextStyleType ReadContext::lookupUserTextStyle(const String& name) const
593 : {
594 186 : for (const auto& i : userTextStyles) {
595 141 : if (i.name == name) {
596 28 : return i.ss;
597 : }
598 : }
599 45 : return TextStyleType::TEXT_TYPES; // not found
600 : }
601 :
602 13444 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
603 : {
604 13444 : if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
605 13267 : _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
606 : }
607 13444 : }
608 :
609 177 : TimeSigMap* ReadContext::compatTimeSigMap()
610 : {
611 177 : return &m_compatTimeSigMap;
612 : }
|