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