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) 2026 MuseScore Limited
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/masterscore.h"
26 : #include "dom/measure.h"
27 : #include "dom/note.h"
28 : #include "dom/score.h"
29 :
30 : #include "log.h"
31 :
32 : using namespace mu::engraving;
33 : using namespace mu::engraving::read500;
34 :
35 805 : ReadContext::ReadContext(Score* score, bool pasteMode)
36 805 : : m_score(score), _pasteMode(pasteMode)
37 : {
38 805 : }
39 :
40 805 : ReadContext::~ReadContext()
41 : {
42 805 : IF_ASSERT_FAILED(m_incompleteNoteAnchoredSpanners.empty()) {
43 0 : for (auto& [spanner, startEndEIDs] : m_incompleteNoteAnchoredSpanners) {
44 0 : delete spanner;
45 : }
46 :
47 0 : m_incompleteNoteAnchoredSpanners.clear();
48 : }
49 805 : }
50 :
51 10813 : Score* ReadContext::score() const
52 : {
53 10813 : return m_score;
54 : }
55 :
56 136 : void ReadContext::setScore(Score* score)
57 : {
58 136 : m_score = score;
59 136 : }
60 :
61 19 : const MStyle& ReadContext::style() const
62 : {
63 19 : return score()->style();
64 : }
65 :
66 0 : std::shared_ptr<IEngravingFontsProvider> ReadContext::engravingFonts() const
67 : {
68 0 : return score()->engravingFonts();
69 : }
70 :
71 0 : String ReadContext::mscoreVersion() const
72 : {
73 0 : return m_score->mscoreVersion();
74 : }
75 :
76 502 : int ReadContext::mscVersion() const
77 : {
78 502 : return m_score->mscVersion();
79 : }
80 :
81 0 : int ReadContext::fileDivision() const
82 : {
83 0 : return m_score->fileDivision();
84 : }
85 :
86 0 : int ReadContext::fileDivision(int t) const
87 : {
88 0 : return m_score->fileDivision(t);
89 : }
90 :
91 1657 : double ReadContext::spatium() const
92 : {
93 1657 : return m_score->style().spatium();
94 : }
95 :
96 0 : void ReadContext::setSpatium(double v)
97 : {
98 0 : m_score->style().set(Sid::spatium, v);
99 0 : }
100 :
101 3673 : DummyParent* ReadContext::dummy() const
102 : {
103 3673 : return m_score->dummy();
104 : }
105 :
106 6519 : Staff* ReadContext::staff(staff_idx_t n)
107 : {
108 6519 : return m_score->staff(n);
109 : }
110 :
111 6506 : bool ReadContext::isSameScore(const EngravingObject* obj) const
112 : {
113 6506 : return obj->score() == m_score;
114 : }
115 :
116 2 : Fraction ReadContext::rtick() const
117 : {
118 2 : return _curMeasure ? _tick - _curMeasure->tick() : _tick;
119 : }
120 :
121 13563 : void ReadContext::setTick(const Fraction& f)
122 : {
123 13563 : _tick = f.reduced();
124 13563 : _intTick = _tick.ticks();
125 13563 : }
126 :
127 9518 : void ReadContext::incTick(const Fraction& f)
128 : {
129 9518 : _tick += f;
130 9518 : _tick.reduce();
131 9518 : _intTick += f.ticks();
132 9518 : }
133 :
134 132 : Location ReadContext::location(bool forceAbsFrac) const
135 : {
136 132 : Location l = Location::absolute();
137 132 : fillLocation(l, forceAbsFrac);
138 132 : return l;
139 : }
140 :
141 : //---------------------------------------------------------
142 : // fillLocation
143 : // fills location fields which have default values with
144 : // values relevant for the current reader's position.
145 : // When in paste mode (or forceAbsFrac is true) absolute
146 : // fraction values are used and measure number is set to
147 : // zero.
148 : //---------------------------------------------------------
149 :
150 132 : void ReadContext::fillLocation(Location& l, bool forceAbsFrac) const
151 : {
152 132 : constexpr Location defaults = Location::absolute();
153 132 : const bool absFrac = (pasteMode() || forceAbsFrac);
154 132 : if (l.track() == defaults.track()) {
155 132 : l.setTrack(static_cast<int>(track()));
156 : }
157 132 : if (l.frac() == defaults.frac()) {
158 132 : l.setFrac(absFrac ? tick() : rtick());
159 : }
160 132 : if (l.measure() == defaults.measure()) {
161 132 : l.setMeasure(absFrac ? 0 : currentMeasureIndex());
162 : }
163 132 : }
164 :
165 : //---------------------------------------------------------
166 : // setLocation
167 : // sets a new reading location, taking into account its
168 : // type (absolute or relative).
169 : //---------------------------------------------------------
170 :
171 221 : void ReadContext::setLocation(const Location& l)
172 : {
173 221 : if (l.isRelative()) {
174 132 : Location newLoc = l;
175 132 : newLoc.toAbsolute(location());
176 132 : int intTicks = l.frac().ticks();
177 132 : if (_tick == Fraction::fromTicks(_intTick + intTicks)) {
178 43 : _intTick += intTicks;
179 43 : setTrack(newLoc.track() - _trackOffset);
180 43 : return;
181 : }
182 89 : setLocation(newLoc); // recursion
183 89 : return;
184 : }
185 89 : setTrack(l.track() - _trackOffset);
186 89 : setTick(l.frac() - _tickOffset);
187 89 : if (!pasteMode()) {
188 2 : assert(l.measure() == currentMeasureIndex());
189 2 : incTick(currentMeasure()->tick());
190 : }
191 : }
192 :
193 376 : void ReadContext::addPartAudioSettingCompat(PartAudioSettingsCompat partAudioSetting)
194 : {
195 376 : if (_settingsCompat.audioSettings.count(partAudioSetting.instrumentId.partId) == 0) {
196 292 : _settingsCompat.audioSettings.insert({ partAudioSetting.instrumentId.partId, partAudioSetting });
197 : }
198 376 : }
199 :
200 10 : void ReadContext::addMMRestEndMeasureEID(Measure* mmrest, EID lastMeasureEID)
201 : {
202 10 : DO_ASSERT(lastMeasureEID.isValid());
203 10 : m_mmRestEndMeasures.emplace(mmrest, lastMeasureEID);
204 10 : }
205 :
206 238 : void ReadContext::setMMRestEndMeasures()
207 : {
208 238 : EIDRegister* eidRegister = score()->masterScore()->eidRegister();
209 248 : for (auto& [mmrest, lastMeasureEID] : m_mmRestEndMeasures) {
210 10 : EngravingObject* linkedElement = eidRegister->itemFromEID(lastMeasureEID);
211 10 : IF_ASSERT_FAILED(linkedElement && linkedElement->isMeasure()) {
212 0 : LOGE() << "No valid end measure found for MMRest at " << mmrest->tick().toString();
213 0 : continue;
214 : }
215 :
216 10 : Measure* lastMeasure = toMeasure(linkedElement);
217 10 : MeasureBase* nextMB = lastMeasure->next();
218 10 : mmrest->setNext(nextMB);
219 : }
220 238 : }
221 :
222 1909 : void ReadContext::registerPastedEID(const EID& clipboardEid, const EID& fileEid)
223 : {
224 1909 : m_pastedEIDs.emplace(clipboardEid, fileEid);
225 1909 : }
226 :
227 73 : EID ReadContext::resolvePastedEID(const EID& clipboardEid) const
228 : {
229 73 : if (!_pasteMode) {
230 0 : return clipboardEid;
231 : }
232 :
233 73 : auto it = m_pastedEIDs.find(clipboardEid);
234 73 : IF_ASSERT_FAILED(it != m_pastedEIDs.end()) {
235 0 : return clipboardEid;
236 : }
237 :
238 73 : return it->second;
239 : }
240 :
241 41 : void ReadContext::addNoteAnchoredSpannerStartEl(Spanner* spanner, EID startElementEID)
242 : {
243 41 : auto it = m_incompleteNoteAnchoredSpanners.find(spanner);
244 41 : if (it != m_incompleteNoteAnchoredSpanners.end()) {
245 0 : it->second.first = startElementEID;
246 : } else {
247 41 : m_incompleteNoteAnchoredSpanners.emplace(spanner, std::make_pair(startElementEID, EID::invalid()));
248 : }
249 41 : }
250 :
251 41 : void ReadContext::addNoteAnchoredSpannerEndEl(Spanner* spanner, EID endElementEID)
252 : {
253 41 : auto it = m_incompleteNoteAnchoredSpanners.find(spanner);
254 41 : if (it != m_incompleteNoteAnchoredSpanners.end()) {
255 41 : it->second.second = endElementEID;
256 : } else {
257 0 : m_incompleteNoteAnchoredSpanners.emplace(spanner, std::make_pair(EID::invalid(), endElementEID));
258 : }
259 41 : }
260 :
261 280 : void ReadContext::connectNoteAnchoredSpanners()
262 : {
263 280 : EIDRegister* eidRegister = score()->masterScore()->eidRegister();
264 321 : for (auto& [spanner, startEndEIDs] : m_incompleteNoteAnchoredSpanners) {
265 41 : EID startElementEID = startEndEIDs.first;
266 41 : EID endElementEID = startEndEIDs.second;
267 41 : EngravingItem* startElement = toEngravingItem(eidRegister->itemFromEID(startElementEID));
268 41 : EngravingItem* endElement = toEngravingItem(eidRegister->itemFromEID(endElementEID));
269 41 : IF_ASSERT_FAILED(startElement && startElement->isNote()) {
270 0 : LOGD() << "Could not find note element for " << spanner->typeName();
271 0 : delete spanner;
272 0 : continue;
273 : }
274 41 : IF_ASSERT_FAILED(endElement && endElement->isNote()) {
275 0 : LOGD() << "Could not find end note for " << spanner->typeName();
276 0 : delete spanner;
277 0 : continue;
278 : }
279 :
280 41 : Note* startNote = toNote(startElement);
281 41 : Note* endNote = toNote(endElement);
282 41 : spanner->setNoteSpan(startNote, endNote);
283 :
284 41 : if (spanner->isTie()) {
285 32 : Tie* tie = toTie(spanner);
286 32 : startNote->setTieFor(tie);
287 32 : endNote->setTieBack(tie);
288 : } else {
289 9 : score()->addElement(spanner);
290 : }
291 : }
292 280 : m_incompleteNoteAnchoredSpanners.clear();
293 280 : }
|