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