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/masterscore.h"
27 : #include "dom/measure.h"
28 : #include "dom/score.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::read460;
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 2739 : Score* ReadContext::score() const
60 : {
61 2739 : return m_score;
62 : }
63 :
64 12 : void ReadContext::setScore(Score* score)
65 : {
66 12 : m_score = score;
67 12 : }
68 :
69 0 : const MStyle& ReadContext::style() const
70 : {
71 0 : 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 211 : int ReadContext::mscVersion() const
85 : {
86 211 : 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 760 : double ReadContext::spatium() const
100 : {
101 760 : 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 3798 : DummyParent* ReadContext::dummy() const
110 : {
111 3798 : return m_score->dummy();
112 : }
113 :
114 1801 : Staff* ReadContext::staff(staff_idx_t n)
115 : {
116 1801 : return m_score->staff(n);
117 : }
118 :
119 1695 : bool ReadContext::isSameScore(const EngravingObject* obj) const
120 : {
121 1695 : return obj->score() == m_score;
122 : }
123 :
124 382 : Fraction ReadContext::rtick() const
125 : {
126 382 : return _curMeasure ? _tick - _curMeasure->tick() : _tick;
127 : }
128 :
129 3713 : void ReadContext::setTick(const Fraction& f)
130 : {
131 3713 : _tick = f.reduced();
132 3713 : _intTick = _tick.ticks();
133 3713 : }
134 :
135 4619 : void ReadContext::incTick(const Fraction& f)
136 : {
137 4619 : _tick += f;
138 4619 : _tick.reduce();
139 4619 : _intTick += f.ticks();
140 4619 : }
141 :
142 50 : Location ReadContext::location(bool forceAbsFrac) const
143 : {
144 50 : Location l = Location::absolute();
145 50 : fillLocation(l, forceAbsFrac);
146 50 : return l;
147 : }
148 :
149 : //---------------------------------------------------------
150 : // fillLocation
151 : // fills location fields which have default values with
152 : // values relevant for the current reader's position.
153 : // When in paste mode (or forceAbsFrac is true) absolute
154 : // fraction values are used and measure number is set to
155 : // zero.
156 : //---------------------------------------------------------
157 :
158 382 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
159 : {
160 382 : constexpr Location defaults = Location::absolute();
161 382 : const bool absFrac = (pasteMode() || forceAbsFrac);
162 382 : if (l.track() == defaults.track()) {
163 50 : l.setTrack(static_cast<int>(track()));
164 : }
165 382 : if (l.frac() == defaults.frac()) {
166 382 : l.setFrac(absFrac ? tick() : rtick());
167 : }
168 382 : if (l.measure() == defaults.measure()) {
169 382 : l.setMeasure(absFrac ? 0 : currentMeasureIndex());
170 : }
171 382 : }
172 :
173 : //---------------------------------------------------------
174 : // setLocation
175 : // sets a new reading location, taking into account its
176 : // type (absolute or relative).
177 : //---------------------------------------------------------
178 :
179 100 : void ReadContext::setLocation(const Location& l)
180 : {
181 100 : if (l.isRelative()) {
182 50 : Location newLoc = l;
183 50 : newLoc.toAbsolute(location());
184 50 : int intTicks = l.frac().ticks();
185 50 : if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
186 0 : _intTick += intTicks;
187 0 : setTrack(newLoc.track() - _trackOffset);
188 0 : return;
189 : }
190 50 : setLocation(newLoc); // recursion
191 50 : return;
192 : }
193 50 : setTrack(l.track() - _trackOffset);
194 50 : setTick(l.frac() - _tickOffset);
195 50 : if (!pasteMode()) {
196 50 : assert(l.measure() == currentMeasureIndex());
197 50 : incTick(currentMeasure()->tick());
198 : }
199 : }
200 :
201 166 : void ReadContext::removeConnector(const ConnectorInfoReader* c)
202 : {
203 190 : while (c->prev()) {
204 24 : c = c->prev();
205 : }
206 498 : while (c) {
207 332 : ConnectorInfoReader* next = c->next();
208 428 : for (auto it = _connectors.begin(); it != _connectors.end(); ++it) {
209 428 : if (it->get() == c) {
210 332 : _connectors.erase(it);
211 332 : break;
212 : }
213 : }
214 332 : c = next;
215 : }
216 166 : }
217 :
218 332 : void ReadContext::addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c)
219 : {
220 332 : _pendingConnectors.push_back(c);
221 332 : }
222 :
223 1695 : void ReadContext::checkConnectors()
224 : {
225 2027 : for (std::shared_ptr<ConnectorInfoReader>& c : _pendingConnectors) {
226 332 : addConnectorInfo(c);
227 : }
228 1695 : _pendingConnectors.clear();
229 1695 : }
230 :
231 332 : void ReadContext::addConnectorInfo(std::shared_ptr<ConnectorInfoReader> c)
232 : {
233 332 : _connectors.push_back(std::move(c));
234 332 : ConnectorInfoReader* c1 = _connectors.back().get();
235 332 : c1->update();
236 570 : for (std::shared_ptr<ConnectorInfoReader>& c2 : _connectors) {
237 404 : if (c2->connect(c1)) {
238 166 : if (c2->finished()) {
239 166 : c2->addToScore(pasteMode());
240 166 : removeConnector(c2.get());
241 : }
242 166 : break;
243 : }
244 : }
245 332 : }
246 :
247 0 : static bool distanceSort(const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p1,
248 : const std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> >& p2)
249 : {
250 0 : return p1.first < p2.first;
251 : }
252 :
253 : //---------------------------------------------------------
254 : // reconnectBrokenConnectors
255 : //---------------------------------------------------------
256 :
257 123 : void ReadContext::reconnectBrokenConnectors()
258 : {
259 123 : if (_connectors.empty()) {
260 123 : return;
261 : }
262 0 : LOGD("Reconnecting broken connectors (%d nodes)", int(_connectors.size()));
263 0 : std::vector<std::pair<int, std::pair<ConnectorInfoReader*, ConnectorInfoReader*> > > brokenPairs;
264 0 : for (size_t i = 1; i < _connectors.size(); ++i) {
265 0 : for (size_t j = 0; j < i; ++j) {
266 0 : ConnectorInfoReader* c1 = _connectors[i].get();
267 0 : ConnectorInfoReader* c2 = _connectors[j].get();
268 0 : int d = c1->connectionDistance(*c2);
269 0 : if (d >= 0) {
270 0 : brokenPairs.push_back(std::make_pair(d, std::make_pair(c1, c2)));
271 : } else {
272 0 : brokenPairs.push_back(std::make_pair(-d, std::make_pair(c2, c1)));
273 : }
274 : }
275 : }
276 0 : std::sort(brokenPairs.begin(), brokenPairs.end(), distanceSort);
277 0 : std::set<ConnectorInfoReader*> processed;
278 0 : for (auto& distPair : brokenPairs) {
279 0 : if (distPair.first == INT_MAX) {
280 0 : continue;
281 : }
282 0 : auto& pair = distPair.second;
283 0 : if (processed.count(pair.first) || processed.count(pair.second)) {
284 0 : continue;
285 : }
286 0 : pair.first->forceConnect(pair.second);
287 0 : processed.insert(pair.first);
288 0 : processed.insert(pair.second);
289 : }
290 0 : std::set<ConnectorInfoReader*> reconnected;
291 0 : for (auto& conn : _connectors) {
292 0 : ConnectorInfoReader* c = conn.get();
293 0 : if (c->finished()) {
294 0 : reconnected.insert(static_cast<ConnectorInfoReader*>(c->start()));
295 : }
296 : }
297 0 : for (ConnectorInfoReader* cptr : reconnected) {
298 0 : cptr->addToScore(pasteMode());
299 0 : removeConnector(cptr);
300 : }
301 0 : LOGD() << "reconnected broken connectors: " << reconnected.size();
302 0 : }
303 :
304 117 : void ReadContext::clearOrphanedConnectors()
305 : {
306 117 : if (_connectors.empty() && _pendingConnectors.empty()) {
307 117 : return;
308 : }
309 :
310 0 : LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
311 :
312 0 : std::set<LinkedObjects*> deletedLinks;
313 :
314 0 : auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
315 0 : EngravingItem* conn = c ? c->releaseConnector() : nullptr;
316 0 : if (!conn) {
317 0 : return;
318 : }
319 :
320 0 : LinkedObjects* links = conn->links();
321 0 : bool linksWillBeDeleted = links && links->size() == 1;
322 :
323 0 : if (!conn->isTuplet()) { // tuplets are added to score even when not finished
324 0 : if (linksWillBeDeleted) {
325 0 : deletedLinks.insert(links);
326 0 : if (conn->isTrill()) {
327 0 : EngravingItem* ornament = (EngravingItem*)toTrill(conn)->ornament();
328 0 : if (ornament && ornament->links()) {
329 0 : deletedLinks.insert(ornament->links());
330 : }
331 : }
332 : }
333 :
334 0 : if (conn->eid().isValid()) {
335 0 : conn->masterScore()->eidRegister()->removeItem(conn);
336 : }
337 :
338 0 : delete conn;
339 : }
340 0 : };
341 :
342 0 : if (!_connectors.empty()) {
343 0 : for (auto& c : _connectors) {
344 0 : deleteConnectors(c);
345 : }
346 0 : _connectors.clear();
347 : }
348 :
349 0 : if (!_pendingConnectors.empty()) {
350 0 : for (auto& c : _pendingConnectors) {
351 0 : deleteConnectors(c);
352 : }
353 0 : _pendingConnectors.clear();
354 : }
355 0 : }
356 :
357 231 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
358 : {
359 231 : if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
360 153 : _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
361 : }
362 231 : }
|