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 : #include "connectorinforeader.h"
23 :
24 : #include "../../infrastructure/rtti.h"
25 :
26 : #include "../../dom/factory.h"
27 : #include "../../dom/score.h"
28 : #include "../../dom/engravingitem.h"
29 : #include "../../dom/spanner.h"
30 : #include "../../dom/chordrest.h"
31 : #include "../../dom/measure.h"
32 : #include "../../dom/note.h"
33 : #include "../../dom/noteline.h"
34 : #include "../../dom/tie.h"
35 : #include "../../dom/chord.h"
36 : #include "../../dom/staff.h"
37 :
38 : #include "../types/typesconv.h"
39 :
40 : #include "../xmlreader.h"
41 :
42 : #include "tread.h"
43 :
44 : #include "log.h"
45 :
46 : using namespace mu::engraving;
47 : using namespace mu::engraving::read400;
48 :
49 : //---------------------------------------------------------
50 : // ConnectorInfoReader
51 : //---------------------------------------------------------
52 :
53 3062 : ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, ReadContext* ctx, EngravingItem* current, int track)
54 3062 : : ConnectorInfo(current, track), m_reader(&e), m_ctx(ctx), m_connector(nullptr), m_connectorReceiver(current)
55 3062 : {}
56 :
57 : //---------------------------------------------------------
58 : // readPositionInfo
59 : //---------------------------------------------------------
60 :
61 0 : static Location readPositionInfo(ReadContext* ctx, int track)
62 : {
63 0 : Location info = ctx->location();
64 0 : info.setTrack(track);
65 0 : return info;
66 : }
67 :
68 : //---------------------------------------------------------
69 : // ConnectorInfoReader
70 : //---------------------------------------------------------
71 :
72 0 : ConnectorInfoReader::ConnectorInfoReader(XmlReader& e, ReadContext* ctx, Score* current, int track)
73 0 : : ConnectorInfo(current, readPositionInfo(ctx, track)), m_reader(&e), m_ctx(ctx), m_connector(nullptr), m_connectorReceiver(current)
74 : {
75 0 : setCurrentUpdated(true);
76 0 : }
77 :
78 : //---------------------------------------------------------
79 : // ConnectorInfoReader::read
80 : //---------------------------------------------------------
81 :
82 3062 : bool ConnectorInfoReader::read()
83 : {
84 3062 : XmlReader& e = *m_reader;
85 3062 : const AsciiStringView name(e.asciiAttribute("type"));
86 3062 : m_type = TConv::fromXml(name, ElementType::INVALID);
87 :
88 3062 : m_ctx->fillLocation(m_currentLoc);
89 :
90 7655 : while (e.readNextStartElement()) {
91 4593 : const AsciiStringView tag(e.name());
92 :
93 4593 : if (tag == "prev") {
94 1531 : readEndpointLocation(m_prevLoc);
95 3062 : } else if (tag == "next") {
96 1531 : readEndpointLocation(m_nextLoc);
97 : } else {
98 1531 : if (tag == name) {
99 1531 : m_connector = Factory::createItemByName(tag, m_connectorReceiver->score()->dummy());
100 : } else {
101 0 : LOGW("ConnectorInfoReader::read: element tag (%s) does not match connector type (%s). Is the file corrupted?",
102 : tag.ascii(), name.ascii());
103 : }
104 :
105 1531 : if (!m_connector) {
106 0 : e.unknown();
107 0 : return false;
108 : }
109 1531 : m_connector->setTrack(m_currentLoc.track());
110 1531 : read400::TRead::readItem(m_connector, e, *m_ctx);
111 : }
112 : }
113 3062 : return true;
114 : }
115 :
116 : //---------------------------------------------------------
117 : // ConnectorInfoReader::readEndpointLocation
118 : //---------------------------------------------------------
119 :
120 3062 : void ConnectorInfoReader::readEndpointLocation(Location& l)
121 : {
122 3062 : XmlReader& e = *m_reader;
123 6124 : while (e.readNextStartElement()) {
124 3062 : const AsciiStringView tag(e.name());
125 3062 : if (tag == "location") {
126 3062 : l = Location::relative();
127 3062 : read400::TRead::read(&l, e, *m_ctx);
128 : } else {
129 0 : e.unknown();
130 : }
131 : }
132 3062 : }
133 :
134 : //---------------------------------------------------------
135 : // ConnectorInfoReader::update
136 : //---------------------------------------------------------
137 :
138 3062 : void ConnectorInfoReader::update()
139 : {
140 3062 : if (!currentUpdated()) {
141 3062 : updateCurrentInfo(m_ctx->pasteMode());
142 : }
143 3062 : if (hasPrevious()) {
144 1531 : m_prevLoc.toAbsolute(m_currentLoc);
145 : }
146 3062 : if (hasNext()) {
147 1531 : m_nextLoc.toAbsolute(m_currentLoc);
148 : }
149 3062 : }
150 :
151 : //---------------------------------------------------------
152 : // ConnectorInfoReader::addToScore
153 : //---------------------------------------------------------
154 :
155 : class ReadAddConnectorVisitor : public rtti::Visitor<ReadAddConnectorVisitor>
156 : {
157 : public:
158 :
159 : template<typename T>
160 7684 : static bool doVisit(EngravingObject* item, ConnectorInfoReader* info, bool pasteMode)
161 : {
162 7684 : if (T::classof(item) || (std::is_same<T, ChordRest>::value && item->isChordRest())) {
163 3062 : ConnectorInfoReader::readAddConnector(static_cast<T*>(item), info, pasteMode);
164 3062 : return true;
165 : }
166 4622 : return false;
167 : }
168 : };
169 :
170 : using ReadAddConnectorTypes = rtti::TypeList<ChordRest, Measure, Note, Score>;
171 :
172 1531 : void ConnectorInfoReader::addToScore(bool pasteMode)
173 : {
174 1531 : ConnectorInfoReader* r = this;
175 1540 : while (r->prev()) {
176 9 : r = r->prev();
177 : }
178 4593 : while (r) {
179 3062 : bool found = ReadAddConnectorVisitor::visit(ReadAddConnectorTypes {}, r->m_connectorReceiver, r, pasteMode);
180 3062 : DO_ASSERT(found);
181 3062 : r = r->next();
182 : }
183 1531 : }
184 :
185 : //---------------------------------------------------------
186 : // ConnectorInfoReader::readConnector
187 : //---------------------------------------------------------
188 :
189 3062 : void ConnectorInfoReader::readConnector(std::shared_ptr<ConnectorInfoReader> info, XmlReader& e, ReadContext& ctx)
190 : {
191 3062 : if (!info->read()) {
192 0 : e.skipCurrentElement();
193 0 : return;
194 : }
195 3062 : ctx.addConnectorInfoLater(info);
196 : }
197 :
198 : //---------------------------------------------------------
199 : // ConnectorInfoReader::connector
200 : //---------------------------------------------------------
201 :
202 3062 : EngravingItem* ConnectorInfoReader::connector()
203 : {
204 : // connector should be contained in the first node normally.
205 3062 : ConnectorInfo* i = findFirst();
206 3062 : if (i) {
207 3062 : return static_cast<ConnectorInfoReader*>(i)->m_connector;
208 : }
209 0 : return nullptr;
210 : }
211 :
212 : //---------------------------------------------------------
213 : // ConnectorInfoReader::connector
214 : //---------------------------------------------------------
215 :
216 0 : const EngravingItem* ConnectorInfoReader::connector() const
217 : {
218 0 : return const_cast<ConnectorInfoReader*>(this)->connector();
219 : }
220 :
221 : //---------------------------------------------------------
222 : // ConnectorInfoReader::releaseConnector
223 : //---------------------------------------------------------
224 :
225 0 : EngravingItem* ConnectorInfoReader::releaseConnector()
226 : {
227 0 : ConnectorInfoReader* i = static_cast<ConnectorInfoReader*>(findFirst());
228 0 : if (!i) {
229 : // circular connector?
230 0 : ConnectorInfoReader* ii = this;
231 0 : EngravingItem* c = nullptr;
232 0 : while (ii->prev()) {
233 0 : if (ii->m_connector) {
234 0 : c = ii->m_connector;
235 0 : ii->m_connector = nullptr;
236 : }
237 0 : ii = ii->prev();
238 0 : if (ii == this) {
239 0 : break;
240 : }
241 : }
242 0 : return c;
243 : }
244 0 : EngravingItem* c = i->m_connector;
245 0 : i->m_connector = nullptr;
246 0 : return c;
247 : }
248 :
249 420 : void ConnectorInfoReader::readAddConnector(ChordRest* item, ConnectorInfoReader* info, bool pasteMode)
250 : {
251 420 : const ElementType type = info->type();
252 420 : switch (type) {
253 420 : case ElementType::SLUR:
254 : {
255 420 : Spanner* spanner = toSpanner(info->connector());
256 420 : const Location& l = info->location();
257 :
258 420 : if (info->isStart()) {
259 210 : spanner->setTrack(l.track());
260 210 : spanner->setTick(item->tick());
261 210 : spanner->setStartElement(item);
262 210 : if (pasteMode) {
263 0 : item->score()->undoAddElement(spanner);
264 0 : for (EngravingObject* ee : spanner->linkList()) {
265 0 : if (ee == spanner) {
266 0 : continue;
267 : }
268 0 : Spanner* ls = toSpanner(ee);
269 0 : ls->setTick(spanner->tick());
270 0 : for (EngravingObject* eee : item->linkList()) {
271 0 : ChordRest* cr = toChordRest(eee);
272 0 : if (cr->score() == eee->score() && cr->staffIdx() == ls->staffIdx()) {
273 0 : ls->setTrack(cr->track());
274 0 : if (ls->isSlur()) {
275 0 : ls->setStartElement(cr);
276 : }
277 0 : break;
278 : }
279 0 : }
280 0 : }
281 : } else {
282 210 : item->score()->addSpanner(spanner);
283 : }
284 210 : } else if (info->isEnd()) {
285 210 : spanner->setTrack2(l.track());
286 210 : spanner->setTick2(item->tick());
287 210 : spanner->setEndElement(item);
288 210 : if (pasteMode) {
289 0 : for (EngravingObject* ee : spanner->linkList()) {
290 0 : if (ee == spanner) {
291 0 : continue;
292 : }
293 0 : Spanner* ls = toSpanner(ee);
294 0 : ls->setTick2(spanner->tick2());
295 0 : for (EngravingObject* eee : item->linkList()) {
296 0 : ChordRest* cr = toChordRest(eee);
297 0 : if (cr->score() == eee->score() && cr->staffIdx() == ls->staffIdx()) {
298 0 : ls->setTrack2(cr->track());
299 0 : if (ls->isSlur()) {
300 0 : ls->setEndElement(cr);
301 : }
302 0 : break;
303 : }
304 0 : }
305 0 : }
306 : }
307 : } else {
308 0 : LOGD("ChordRest::readAddConnector(): Slur end is neither start nor end");
309 : }
310 : }
311 420 : break;
312 0 : default:
313 0 : break;
314 : }
315 420 : }
316 :
317 662 : void ConnectorInfoReader::readAddConnector(Measure* item, ConnectorInfoReader* info, bool pasteMode)
318 : {
319 662 : const ElementType type = info->type();
320 662 : switch (type) {
321 662 : case ElementType::HAIRPIN:
322 : case ElementType::PEDAL:
323 : case ElementType::OTTAVA:
324 : case ElementType::TRILL:
325 : case ElementType::TEXTLINE:
326 : case ElementType::LET_RING:
327 : case ElementType::GRADUAL_TEMPO_CHANGE:
328 : case ElementType::VIBRATO:
329 : case ElementType::PALM_MUTE:
330 : case ElementType::WHAMMY_BAR:
331 : case ElementType::RASGUEADO:
332 : case ElementType::HARMONIC_MARK:
333 : case ElementType::PICK_SCRAPE:
334 : case ElementType::VOLTA:
335 : {
336 662 : Spanner* sp = toSpanner(info->connector());
337 662 : const Location& l = info->location();
338 662 : Fraction lTick = l.frac();
339 662 : Fraction spTick = pasteMode ? lTick : (item->tick() + lTick);
340 662 : if (info->isStart()) {
341 331 : sp->setTrack(l.track());
342 331 : sp->setTrack2(sp->track());
343 331 : sp->setTick(spTick);
344 : // Defer computing the end element: the end anchor (tick2/track2) is only known once the
345 : // matching end connector is processed (below). Computing it here would cache a wrong value
346 : // (tick2 == tick) that nothing recomputes until layout. See readAddConnector end branch.
347 331 : item->score()->addSpanner(sp, /*computeStartEnd=*/ false);
348 331 : sp->computeStartElement();
349 331 : } else if (info->isEnd()) {
350 331 : sp->setTrack2(l.track());
351 331 : sp->setTick2(spTick);
352 : // Now that the end anchor is set, compute the end element once with the final value.
353 331 : sp->computeEndElement();
354 : }
355 : }
356 662 : break;
357 0 : default:
358 0 : break;
359 : }
360 662 : }
361 :
362 1980 : void ConnectorInfoReader::readAddConnector(Note* item, ConnectorInfoReader* info, bool pasteMode)
363 : {
364 1980 : const ElementType type = info->type();
365 1980 : const Location& l = info->location();
366 1980 : switch (type) {
367 1980 : case ElementType::TIE:
368 : case ElementType::TEXTLINE:
369 : case ElementType::GLISSANDO:
370 : {
371 1980 : Spanner* sp = toSpanner(info->connector());
372 1980 : if (info->isStart()) {
373 990 : sp->setTrack(l.track());
374 990 : sp->setTick(item->tick());
375 990 : if (sp->isTie()) {
376 921 : Note* n = item;
377 921 : while (n->tieFor()) {
378 0 : n = n->tieFor()->endNote();
379 : }
380 921 : Tie* tie = toTie(sp);
381 921 : tie->setOwnershipParent(n);
382 921 : tie->setStartNote(n);
383 921 : n->setTieFor(tie);
384 : } else {
385 69 : sp->setStartElement(item);
386 69 : item->addSpannerFor(sp);
387 69 : sp->setOwnershipParent(item);
388 : }
389 990 : } else if (info->isEnd()) {
390 990 : sp->setTrack2(l.track());
391 990 : sp->setTick2(item->tick());
392 990 : sp->setEndElement(item);
393 990 : if (sp->isTie()) {
394 921 : item->setTieBack(toTie(sp));
395 : } else {
396 69 : bool isNoteAnchoredTextLine = sp->isNoteLine() && toNoteLine(sp)->enforceMinLength();
397 69 : if ((sp->isGlissando() || isNoteAnchoredTextLine) && item->ownershipParent() && item->ownershipParent()->isChord()) {
398 69 : toChord(item->ownershipParent())->setEndsNoteAnchoredLine(true);
399 : }
400 69 : item->addSpannerBack(sp);
401 : }
402 :
403 : // As spanners get added after being fully read, they
404 : // do not get cloned with the note when pasting to
405 : // linked staves. So add this spanner explicitly.
406 990 : if (pasteMode) {
407 0 : item->score()->undoAddElement(sp);
408 : }
409 : }
410 : }
411 : default:
412 1980 : break;
413 : }
414 1980 : }
415 :
416 0 : void ConnectorInfoReader::readAddConnector(Score* item, ConnectorInfoReader* info, bool pasteMode)
417 : {
418 0 : if (!pasteMode) {
419 : // How did we get there?
420 0 : LOGD("Score::readAddConnector is called not in paste mode.");
421 0 : return;
422 : }
423 0 : const ElementType type = info->type();
424 0 : switch (type) {
425 0 : case ElementType::HAIRPIN:
426 : case ElementType::PEDAL:
427 : case ElementType::OTTAVA:
428 : case ElementType::TRILL:
429 : case ElementType::TEXTLINE:
430 : case ElementType::VOLTA:
431 : case ElementType::PALM_MUTE:
432 : case ElementType::WHAMMY_BAR:
433 : case ElementType::RASGUEADO:
434 : case ElementType::HARMONIC_MARK:
435 : case ElementType::PICK_SCRAPE:
436 : case ElementType::LET_RING:
437 : case ElementType::GRADUAL_TEMPO_CHANGE:
438 : case ElementType::VIBRATO:
439 : {
440 0 : Spanner* sp = toSpanner(info->connector());
441 0 : const Location& l = info->location();
442 0 : if (info->isStart()) {
443 0 : sp->setTrack(l.track());
444 0 : sp->setTrack2(l.track());
445 0 : sp->setTick(l.frac());
446 0 : } else if (info->isEnd()) {
447 0 : sp->setTick2(l.frac());
448 0 : item->undoAddElement(sp);
449 0 : if (sp->isOttava()) {
450 0 : sp->staff()->updateOttava();
451 : }
452 : }
453 : }
454 0 : break;
455 0 : default:
456 0 : break;
457 : }
458 : }
|