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 "dom/linkedobjects.h"
26 : #include "dom/measure.h"
27 : #include "dom/score.h"
28 : #include "dom/staff.h"
29 : #include "dom/trill.h"
30 :
31 : #include "connectorinforeader.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace mu::engraving;
36 : using namespace mu::engraving::read410;
37 :
38 186 : ReadContext::ReadContext(Score* score)
39 186 : : m_score(score)
40 : {
41 186 : }
42 :
43 186 : ReadContext::~ReadContext()
44 : {
45 186 : if (!_connectors.empty() || !_pendingConnectors.empty()) {
46 0 : LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
47 0 : for (auto& c : _connectors) {
48 0 : EngravingItem* conn = c->releaseConnector();
49 0 : if (conn && !conn->isTuplet()) { // tuplets are added to score even when not finished
50 0 : delete conn;
51 : }
52 : }
53 0 : for (auto& c : _pendingConnectors) {
54 0 : delete c->releaseConnector();
55 : }
56 : }
57 186 : }
58 :
59 2963 : Score* ReadContext::score() const
60 : {
61 2963 : return m_score;
62 : }
63 :
64 2 : void ReadContext::setScore(Score* score)
65 : {
66 2 : m_score = score;
67 2 : }
68 :
69 237 : const MStyle& ReadContext::style() const
70 : {
71 237 : return score()->style();
72 : }
73 :
74 0 : std::shared_ptr<IEngravingFontsProvider> ReadContext::engravingFonts() const
75 : {
76 0 : return score()->engravingFonts();
77 : }
78 :
79 0 : String ReadContext::mscoreVersion() const
80 : {
81 0 : return m_score->mscoreVersion();
82 : }
83 :
84 0 : int ReadContext::mscVersion() const
85 : {
86 0 : return m_score->mscVersion();
87 : }
88 :
89 0 : int ReadContext::fileDivision() const
90 : {
91 0 : return m_score->fileDivision();
92 : }
93 :
94 0 : int ReadContext::fileDivision(int t) const
95 : {
96 0 : return m_score->fileDivision(t);
97 : }
98 :
99 578 : double ReadContext::spatium() const
100 : {
101 578 : return m_score->style().spatium();
102 : }
103 :
104 0 : void ReadContext::setSpatium(double v)
105 : {
106 0 : m_score->style().set(Sid::spatium, v);
107 0 : }
108 :
109 2819 : DummyParent* ReadContext::dummy() const
110 : {
111 2819 : return m_score->dummy();
112 : }
113 :
114 1797 : Staff* ReadContext::staff(staff_idx_t n)
115 : {
116 1797 : return m_score->staff(n);
117 : }
118 :
119 1737 : bool ReadContext::isSameScore(const EngravingObject* obj) const
120 : {
121 1737 : return obj->score() == m_score;
122 : }
123 :
124 186 : rw::ReadLinks ReadContext::readLinks() const
125 : {
126 186 : rw::ReadLinks l;
127 186 : l.linksIndexer = m_linksIndexer;
128 186 : l.staffLinkedElements = m_staffLinkedElements;
129 186 : return l;
130 0 : }
131 :
132 9 : void ReadContext::initLinks(const rw::ReadLinks& l)
133 : {
134 9 : m_linksIndexer = l.linksIndexer;
135 9 : m_staffLinkedElements = l.staffLinkedElements;
136 9 : }
137 :
138 577 : void ReadContext::addLink(Staff* staff, LinkedObjects* link, const Location& location)
139 : {
140 577 : int staffIndex = static_cast<int>(staff->idx());
141 577 : const bool isMasterScore = staff->score()->isMaster();
142 577 : if (!isMasterScore) {
143 0 : staffIndex *= -1;
144 : }
145 :
146 577 : std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
147 577 : if (!isMasterScore) {
148 0 : if (!staffLinks.empty()
149 0 : && (link->mainElement()->score() != staffLinks.front().first->mainElement()->score())
150 : ) {
151 0 : staffLinks.clear();
152 : }
153 : }
154 :
155 577 : m_linksIndexer.assignLocalIndex(location);
156 577 : staffLinks.push_back(std::make_pair(link, location));
157 577 : }
158 :
159 486 : LinkedObjects* ReadContext::getLink(bool isMasterScore, const Location& location, int localIndexDiff)
160 : {
161 486 : int staffIndex = location.staff();
162 486 : if (!isMasterScore) {
163 4 : staffIndex *= -1;
164 : }
165 :
166 486 : const int localIndex = m_linksIndexer.assignLocalIndex(location) + localIndexDiff;
167 486 : std::vector<std::pair<LinkedObjects*, Location> >& staffLinks = m_staffLinkedElements[staffIndex];
168 :
169 486 : if (!staffLinks.empty() && staffLinks.back().second == location) {
170 : // This element potentially affects local index for "main"
171 : // elements that may go afterwards at the same tick, so
172 : // append it to staffLinks as well.
173 55 : staffLinks.push_back(staffLinks.back()); // nothing should reference exactly this local index, so it shouldn't matter what to append
174 : }
175 :
176 4618 : for (size_t i = 0; i < staffLinks.size(); ++i) {
177 4617 : if (staffLinks[i].second == location) {
178 485 : if (localIndex == 0) {
179 290 : return staffLinks[i].first;
180 : }
181 :
182 195 : i += localIndex;
183 195 : if (i >= staffLinks.size()) {
184 0 : return nullptr;
185 : }
186 :
187 195 : if (staffLinks[i].second == location) {
188 195 : return staffLinks[i].first;
189 : }
190 :
191 0 : return nullptr;
192 : }
193 : }
194 :
195 1 : return nullptr;
196 : }
197 :
198 540 : Fraction ReadContext::rtick() const
199 : {
200 540 : return _curMeasure ? _tick - _curMeasure->tick() : _tick;
201 : }
202 :
203 3871 : void ReadContext::setTick(const Fraction& f)
204 : {
205 3871 : _tick = f.reduced();
206 3871 : _intTick = _tick.ticks();
207 3871 : }
208 :
209 3446 : void ReadContext::incTick(const Fraction& f)
210 : {
211 3446 : _tick += f;
212 3446 : _tick.reduce();
213 3446 : _intTick += f.ticks();
214 3446 : }
215 :
216 1094 : Location ReadContext::location(bool forceAbsFrac) const
217 : {
218 1094 : Location l = Location::absolute();
219 1094 : fillLocation(l, forceAbsFrac);
220 1094 : return l;
221 : }
222 :
223 : //---------------------------------------------------------
224 : // fillLocation
225 : // fills location fields which have default values with
226 : // values relevant for the current reader's position.
227 : // When in paste mode (or forceAbsFrac is true) absolute
228 : // fraction values are used and measure number is set to
229 : // zero.
230 : //---------------------------------------------------------
231 :
232 1603 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
233 : {
234 1603 : constexpr Location defaults = Location::absolute();
235 1603 : const bool absFrac = (pasteMode() || forceAbsFrac);
236 1603 : if (l.track() == defaults.track()) {
237 1094 : l.setTrack(static_cast<int>(track()));
238 : }
239 1603 : if (l.frac() == defaults.frac()) {
240 1603 : l.setFrac(absFrac ? tick() : rtick());
241 : }
242 1603 : if (l.measure() == defaults.measure()) {
243 1603 : l.setMeasure(absFrac ? 0 : currentMeasureIndex());
244 : }
245 1603 : }
246 :
247 : //---------------------------------------------------------
248 : // setLocation
249 : // sets a new reading location, taking into account its
250 : // type (absolute or relative).
251 : //---------------------------------------------------------
252 :
253 62 : void ReadContext::setLocation(const Location& l)
254 : {
255 62 : if (l.isRelative()) {
256 31 : Location newLoc = l;
257 31 : newLoc.toAbsolute(location());
258 31 : int intTicks = l.frac().ticks();
259 31 : if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
260 0 : _intTick += intTicks;
261 0 : setTrack(newLoc.track() - _trackOffset);
262 0 : return;
263 : }
264 31 : setLocation(newLoc); // recursion
265 31 : return;
266 : }
267 31 : setTrack(l.track() - _trackOffset);
268 31 : setTick(l.frac() - _tickOffset);
269 31 : if (!pasteMode()) {
270 31 : assert(l.measure() == currentMeasureIndex());
271 31 : incTick(currentMeasure()->tick());
272 : }
273 : }
274 :
275 254 : void ReadContext::removeConnector(const ConnectorInfoReader* c)
276 : {
277 256 : while (c->prev()) {
278 2 : c = c->prev();
279 : }
280 762 : while (c) {
281 508 : ConnectorInfoReader* next = c->next();
282 676 : for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
283 676 : if (it->get() == c) {
284 508 : _connectors.erase(it);
285 508 : break;
286 : }
287 : }
288 508 : c = next;
289 : }
290 254 : }
291 :
292 509 : void ReadContext::addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c)
293 : {
294 509 : _pendingConnectors.push_back(c);
295 509 : }
296 :
297 1737 : void ReadContext::checkConnectors()
298 : {
299 2246 : for (std::shared_ptr<ConnectorInfoReader>& c : _pendingConnectors) {
300 509 : addConnectorInfo(c);
301 : }
302 1737 : _pendingConnectors.clear();
303 1737 : }
304 :
305 509 : void ReadContext::addConnectorInfo(std::shared_ptr<ConnectorInfoReader> c)
306 : {
307 509 : _connectors.push_back(std::move(c));
308 509 : ConnectorInfoReader* c1 = _connectors.back().get();
309 509 : c1->update();
310 930 : for (std::shared_ptr<ConnectorInfoReader>& c2 : _connectors) {
311 675 : if (c2->connect(c1)) {
312 254 : if (c2->finished()) {
313 254 : c2->addToScore(pasteMode());
314 254 : removeConnector(c2.get());
315 : }
316 254 : break;
317 : }
318 : }
319 509 : }
320 :
321 0 : static bool distanceSort(const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p1,
322 : const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p2)
323 : {
324 0 : return p1.first < p2.first;
325 : }
326 :
327 : //---------------------------------------------------------
328 : // reconnectBrokenConnectors
329 : //---------------------------------------------------------
330 :
331 187 : void ReadContext::reconnectBrokenConnectors()
332 : {
333 187 : if (_connectors.empty()) {
334 186 : return;
335 : }
336 1 : LOGD("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
337 1 : std::vector<std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> > > brokenPairs;
338 1 : for (size_t i = 1; i < _connectors.size(); ++i) {
339 0 : for (size_t j = 0; j < i; ++j) {
340 0 : ConnectorInfoReader* c1 = _connectors[i].get();
341 0 : ConnectorInfoReader* c2 = _connectors[j].get();
342 0 : int d = c1->connectionDistance(*c2);
343 0 : if (d >= 0) {
344 0 : brokenPairs.push_back(std::make_pair(d, std::make_pair(c1, c2)));
345 : } else {
346 0 : brokenPairs.push_back(std::make_pair(-d, std::make_pair(c2, c1)));
347 : }
348 : }
349 : }
350 1 : std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
351 1 : std::set<ConnectorInfoReader*> processed;
352 1 : for (auto& distPair : brokenPairs) {
353 0 : if (distPair.first == INT_MAX) {
354 0 : continue;
355 : }
356 0 : auto& pair = distPair.second;
357 0 : if (processed.count(pair.first) || processed.count(pair.second)) {
358 0 : continue;
359 : }
360 0 : pair.first->forceConnect(pair.second);
361 0 : processed.insert(pair.first);
362 0 : processed.insert(pair.second);
363 : }
364 1 : std::set<ConnectorInfoReader*> reconnected;
365 2 : for (auto& conn : _connectors) {
366 1 : ConnectorInfoReader* c = conn.get();
367 1 : if (c->finished()) {
368 0 : reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
369 : }
370 : }
371 1 : for (ConnectorInfoReader* cptr : reconnected) {
372 0 : cptr->addToScore(pasteMode());
373 0 : removeConnector(cptr);
374 : }
375 1 : LOGD() << "reconnected broken connectors: " << reconnected.size();
376 1 : }
377 :
378 186 : void ReadContext::clearOrphanedConnectors()
379 : {
380 186 : if (_connectors.empty() && _pendingConnectors.empty()) {
381 185 : return;
382 : }
383 :
384 1 : LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
385 :
386 1 : std::set<LinkedObjects*> deletedLinks;
387 :
388 1 : auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
389 1 : EngravingItem* conn = c ? c->releaseConnector() : nullptr;
390 1 : if (!conn) {
391 1 : return;
392 : }
393 :
394 0 : LinkedObjects* links = conn->links();
395 0 : bool linksWillBeDeleted = links && links->size() == 1;
396 :
397 0 : if (!conn->isTuplet()) { // tuplets are added to score even when not finished
398 0 : if (linksWillBeDeleted) {
399 0 : deletedLinks.insert(links);
400 0 : if (conn->isTrill()) {
401 0 : EngravingItem* ornament = (EngravingItem*)toTrill(conn)->ornament();
402 0 : if (ornament && ornament->links()) {
403 0 : deletedLinks.insert(ornament->links());
404 : }
405 : }
406 : }
407 :
408 0 : delete conn;
409 : }
410 1 : };
411 :
412 1 : if (!_connectors.empty()) {
413 2 : for (auto& c : _connectors) {
414 1 : deleteConnectors(c);
415 : }
416 1 : _connectors.clear();
417 : }
418 :
419 1 : if (!_pendingConnectors.empty()) {
420 0 : for (auto& c : _pendingConnectors) {
421 0 : deleteConnectors(c);
422 : }
423 0 : _pendingConnectors.clear();
424 : }
425 :
426 1 : for (auto& it : m_staffLinkedElements) {
427 0 : std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
428 0 : muse::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
429 0 : return deletedLinks.count(pair.first);
430 : });
431 : }
432 1 : }
433 :
434 321 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
435 : {
436 321 : if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
437 235 : _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
438 : }
439 321 : }
|