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 <map>
24 : #include <set>
25 :
26 : #include "../dom/accidental.h"
27 : #include "../dom/arpeggio.h"
28 : #include "../dom/articulation.h"
29 : #include "../dom/barline.h"
30 : #include "../dom/beam.h"
31 : #include "../dom/box.h"
32 : #include "../dom/bracket.h"
33 : #include "../dom/breath.h"
34 : #include "../dom/chord.h"
35 : #include "../dom/chordbracket.h"
36 : #include "../dom/chordline.h"
37 : #include "../dom/clef.h"
38 : #include "../dom/dynamic.h"
39 : #include "../dom/excerpt.h"
40 : #include "../dom/expression.h"
41 : #include "../dom/factory.h"
42 : #include "../dom/fingering.h"
43 : #include "../dom/figuredbass.h"
44 : #include "../dom/fret.h"
45 : #include "../dom/glissando.h"
46 : #include "../dom/guitarbend.h"
47 : #include "../dom/hammeronpulloff.h"
48 : #include "../dom/harmony.h"
49 : #include "../dom/harppedaldiagram.h"
50 : #include "../dom/hook.h"
51 : #include "../dom/instrchange.h"
52 : #include "../dom/instrumentname.h"
53 : #include "../dom/key.h"
54 : #include "../dom/keylist.h"
55 : #include "../dom/keysig.h"
56 : #include "../dom/laissezvib.h"
57 : #include "../dom/layoutbreak.h"
58 : #include "../dom/linkedobjects.h"
59 : #include "../dom/lyrics.h"
60 : #include "../dom/marker.h"
61 : #include "../dom/masterscore.h"
62 : #include "../dom/measure.h"
63 : #include "../dom/measurerepeat.h"
64 : #include "../dom/note.h"
65 : #include "../dom/noteline.h"
66 : #include "../dom/ottava.h"
67 : #include "../dom/pagelockindicator.h"
68 : #include "../dom/part.h"
69 : #include "../dom/partialtie.h"
70 : #include "../dom/playcounttext.h"
71 : #include "../dom/range.h"
72 : #include "../dom/rehearsalmark.h"
73 : #include "../dom/rest.h"
74 : #include "../dom/score.h"
75 : #include "../dom/segment.h"
76 : #include "../dom/sig.h"
77 : #include "../dom/slur.h"
78 : #include "../dom/staff.h"
79 : #include "../dom/stafftext.h"
80 : #include "../dom/stem.h"
81 : #include "../dom/sticking.h"
82 : #include "../dom/stringtunings.h"
83 : #include "../dom/system.h"
84 : #include "../dom/systemlockindicator.h"
85 : #include "../dom/systemtext.h"
86 : #include "../dom/tapping.h"
87 : #include "../dom/tempotext.h"
88 : #include "../dom/text.h"
89 : #include "../dom/textline.h"
90 : #include "../dom/tie.h"
91 : #include "../dom/tiemap.h"
92 : #include "../dom/timesig.h"
93 : #include "../dom/tremolosinglechord.h"
94 : #include "../dom/tremolotwochord.h"
95 : #include "../dom/tuplet.h"
96 : #include "../dom/tupletmap.h"
97 : #include "../dom/utils.h"
98 : #include "../dom/volta.h"
99 : #include "../infrastructure/messagebox.h"
100 :
101 : #include "addremoveelement.h"
102 : #include "editstaffbrackets.h"
103 : #include "editchord.h"
104 : #include "editclef.h"
105 : #include "editkeysig.h"
106 : #include "editmeasures.h"
107 : #include "editnote.h"
108 : #include "editpagelocks.h"
109 : #include "editpart.h"
110 : #include "editproperty.h"
111 : #include "editrehearsalmark.h"
112 : #include "editscoreproperties.h"
113 : #include "editspanner.h"
114 : #include "editstaff.h"
115 : #include "editstyle.h"
116 : #include "editsystemlocks.h"
117 : #include "edittimesig.h"
118 : #include "edittremolo.h"
119 : #include "inserttime.h"
120 : #include "mscoreview.h"
121 : #include "navigation.h"
122 : #include "splitjoinmeasure.h"
123 : #include "transaction/transaction.h"
124 : #include "transaction/undostack.h"
125 : #include "transpose.h"
126 :
127 : #include "log.h"
128 :
129 : using namespace mu;
130 : using namespace mu::engraving;
131 :
132 : namespace mu::engraving {
133 0 : static ChordRest* chordOrRest(EngravingItem* el)
134 : {
135 0 : if (el) {
136 0 : if (el->isNote()) {
137 0 : return toNote(el)->chord();
138 0 : } else if (el->isRestFamily()) {
139 0 : return toRest(el);
140 0 : } else if (el->isChord()) {
141 0 : return toChord(el);
142 : }
143 : }
144 :
145 0 : return nullptr;
146 : }
147 :
148 : //---------------------------------------------------------
149 : // getSelectedNote
150 : //---------------------------------------------------------
151 :
152 0 : Note* Score::getSelectedNote()
153 : {
154 0 : EngravingItem* el = selection().element();
155 0 : if (el && el->isNote()) {
156 0 : return toNote(el);
157 : }
158 0 : MScore::setError(MsError::NO_NOTE_SELECTED);
159 0 : return 0;
160 : }
161 :
162 : //---------------------------------------------------------
163 : // getSelectedChordRest
164 : //---------------------------------------------------------
165 :
166 0 : ChordRest* Score::getSelectedChordRest() const
167 : {
168 0 : ChordRest* cr = chordOrRest(selection().element());
169 0 : if (!cr) {
170 0 : MScore::setError(MsError::NO_NOTE_REST_SELECTED);
171 : }
172 :
173 0 : return cr;
174 : }
175 :
176 : //---------------------------------------------------------
177 : // getSelectedChordRest2
178 : //---------------------------------------------------------
179 :
180 0 : void Score::getSelectedStartEndChordRests(ChordRest*& cr1, ChordRest*& cr2) const
181 : {
182 0 : cr1 = 0;
183 0 : cr2 = 0;
184 0 : for (EngravingItem* e : selection().elements()) {
185 0 : if (e->isNote()) {
186 0 : e = toNote(e)->chord();
187 : }
188 0 : if (e->isChordRest()) {
189 0 : ChordRest* cr = toChordRest(e);
190 0 : if (cr1 == 0 || (cr1)->tick() > cr->tick()) {
191 0 : cr1 = cr;
192 : }
193 0 : if (cr2 == 0 || (cr2)->tick() < cr->tick()) {
194 0 : cr2 = cr;
195 : }
196 : }
197 : }
198 0 : if (cr1 == 0) {
199 0 : MScore::setError(MsError::NO_NOTE_REST_SELECTED);
200 : }
201 0 : if (cr1 == cr2) {
202 0 : cr2 = 0;
203 : }
204 0 : }
205 :
206 : //---------------------------------------------------------
207 : // getSelectedChordRests
208 : //---------------------------------------------------------
209 :
210 101 : std::set<ChordRest*> Score::getSelectedChordRests() const
211 : {
212 101 : std::set<ChordRest*> set;
213 442 : for (EngravingItem* e : selection().elements()) {
214 341 : if (e->isNote()) {
215 140 : e = toNote(e)->chord();
216 : }
217 341 : if (e->isChordRest()) {
218 200 : set.insert(toChordRest(e));
219 : }
220 : }
221 101 : return set;
222 0 : }
223 :
224 : //---------------------------------------------------------
225 : // pos
226 : //---------------------------------------------------------
227 :
228 0 : Fraction Score::pos()
229 : {
230 0 : EngravingItem* el = selection().element();
231 0 : if (selection().activeCR()) {
232 0 : el = selection().activeCR();
233 : }
234 0 : if (el) {
235 0 : switch (el->type()) {
236 0 : case ElementType::NOTE:
237 0 : el = toNote(el)->chord();
238 : // fall through
239 0 : case ElementType::MEASURE_REPEAT:
240 : case ElementType::REST:
241 : case ElementType::MMREST:
242 : case ElementType::CHORD:
243 0 : return toChordRest(el)->tick();
244 0 : default:
245 0 : break;
246 : }
247 : }
248 0 : return Fraction(0, 1);
249 : }
250 :
251 23 : Tuplet* Score::addTuplet(ChordRest* destinationChordRest, Fraction ratio, TupletNumberType numberType, TupletBracketType bracketType)
252 : {
253 23 : if (destinationChordRest->durationType() < TDuration(DurationType::V_512TH)
254 23 : && destinationChordRest->durationType() != TDuration(DurationType::V_MEASURE)) {
255 0 : return nullptr;
256 : }
257 :
258 23 : Measure* measure = destinationChordRest->measure();
259 23 : if (measure && measure->isMMRest()) {
260 0 : return nullptr;
261 : }
262 :
263 23 : Fraction f(destinationChordRest->ticks());
264 :
265 23 : f.reduce(); //measure duration might not be reduced
266 :
267 23 : Fraction _ratio;
268 23 : _ratio.setNumerator(ratio.numerator() != -1 ? ratio.numerator() : f.numerator());
269 23 : _ratio.setDenominator(ratio.denominator() != -1 ? ratio.denominator() : f.numerator());
270 :
271 23 : Fraction fr = f * Fraction(1, _ratio.denominator());
272 23 : if (!TDuration::isValid(fr)) {
273 0 : MessageBox(iocContext()).warning(muse::mtrc("engraving", "Cannot create tuplet with ratio %1 for duration %2")
274 0 : .arg(_ratio.toString(), f.toString()).toStdString(),
275 0 : std::string(), { MessageBox::Ok });
276 0 : return nullptr;
277 : }
278 :
279 23 : return addTuplet(destinationChordRest, _ratio, TDuration(fr), numberType, bracketType);
280 : }
281 :
282 126 : static bool isValidTuplet(const Fraction& ratio, const TDuration& baseLen)
283 : {
284 126 : return baseLen.isValid() && !baseLen.isMeasure() && ratio.numerator() > 0 && ratio.denominator() > 0;
285 : }
286 :
287 162 : static Fraction tupletEnd(const Staff* staff, const Fraction& tick, Tuplet* parent, const Fraction& ratio, const TDuration& baseLen)
288 : {
289 162 : const Fraction ticks = baseLen.fraction() * ratio.denominator();
290 162 : return tick + actualTicks(ticks, parent, staff->timeStretch(tick));
291 : }
292 :
293 : // A tuplet must not cross a barline or the end of its parent tuplet
294 119 : static Fraction tupletLimit(const Tuplet* parent, const Measure* measure)
295 : {
296 119 : return parent ? parent->tick() + parent->actualTicks() : measure->endTick();
297 : }
298 :
299 : //---------------------------------------------------------
300 : // canAddTuplet
301 : // Whether addTuplet() can create a tuplet with this ratio
302 : // and base length at chordRest
303 : //---------------------------------------------------------
304 :
305 117 : bool Score::canAddTuplet(const ChordRest* chordRest, const Fraction& ratio, const TDuration& baseLen) const
306 : {
307 117 : if (!chordRest || !isValidTuplet(ratio, baseLen)) {
308 6 : return false;
309 : }
310 :
311 111 : const Measure* measure = chordRest->measure();
312 111 : if (!measure || measure->isMMRest() || chordRest->isGrace()) {
313 0 : return false;
314 : }
315 :
316 : // changeCRlen() can't change the length of a measure repeat
317 111 : if (chordRest->isMeasureRepeat() && baseLen.fraction() * ratio.denominator() != chordRest->ticks()) {
318 0 : return false;
319 : }
320 :
321 111 : return tupletEndTick(chordRest, ratio, baseLen) <= tupletLimit(chordRest->tuplet(), measure);
322 : }
323 :
324 : //---------------------------------------------------------
325 : // canAddTuplet
326 : // The same at segment in track, where no chord/rest of
327 : // the track may start: an empty voice, or inside a
328 : // longer chord/rest
329 : //---------------------------------------------------------
330 :
331 11 : bool Score::canAddTuplet(const Segment* segment, track_idx_t track, const Fraction& ratio, const TDuration& baseLen) const
332 : {
333 11 : if (!segment || track == muse::nidx) {
334 1 : return false;
335 : }
336 :
337 10 : if (const ChordRest* chordRest = segment->cr(track)) {
338 1 : return canAddTuplet(chordRest, ratio, baseLen);
339 : }
340 :
341 9 : const Measure* measure = segment->measure();
342 9 : const Staff* staff = this->staff(track2staff(track));
343 9 : if (!isValidTuplet(ratio, baseLen) || !measure || measure->isMMRest() || !staff) {
344 1 : return false;
345 : }
346 :
347 : // inside a longer chord/rest, the tuplet would be in that chord/rest's tuplet
348 8 : const ChordRest* previous = segment->nextChordRest(track, /*backwards*/ true, /*stopAtMeasureBoundary*/ true);
349 8 : Tuplet* parent = previous && previous->endTick() > segment->tick() ? previous->tuplet() : nullptr;
350 :
351 8 : return tupletEnd(staff, segment->tick(), parent, ratio, baseLen) <= tupletLimit(parent, measure);
352 : }
353 :
354 : //---------------------------------------------------------
355 : // tupletEndTick
356 : // Where a tuplet of ratio.denominator() * baseLen that
357 : // starts at chordRest ends
358 : //---------------------------------------------------------
359 :
360 154 : Fraction Score::tupletEndTick(const ChordRest* chordRest, const Fraction& ratio, const TDuration& baseLen) const
361 : {
362 154 : return tupletEnd(chordRest->staff(), chordRest->tick(), chordRest->tuplet(), ratio, baseLen);
363 : }
364 :
365 : //---------------------------------------------------------
366 : // addTuplet
367 : // Create a tuplet of ratio.denominator() * baseLen, starting
368 : // at destinationChordRest and replacing everything in that
369 : // time in its track. The tuplet can be longer or shorter than
370 : // destinationChordRest, but must not cross a barline or the end
371 : // of a parent tuplet.
372 : //---------------------------------------------------------
373 :
374 52 : Tuplet* Score::addTuplet(ChordRest* destinationChordRest, const Fraction& ratio, const TDuration& baseLen,
375 : TupletNumberType numberType, TupletBracketType bracketType)
376 : {
377 52 : if (!canAddTuplet(destinationChordRest, ratio, baseLen)) {
378 0 : const Fraction duration = baseLen.fraction() * ratio.denominator();
379 0 : MessageBox(iocContext()).warning(muse::mtrc("engraving", "Cannot create tuplet with ratio %1 for duration %2")
380 0 : .arg(ratio.toString(), duration.toString()).toStdString(),
381 0 : std::string(), { MessageBox::Ok });
382 0 : return nullptr;
383 : }
384 :
385 52 : Measure* measure = destinationChordRest->measure();
386 52 : Tuplet* ot = destinationChordRest->tuplet();
387 52 : const Fraction f = baseLen.fraction() * ratio.denominator();
388 :
389 : // Shorten a longer chord/rest first, so that the remainder is filled with rests. Shorten it to
390 : // the base length, since changeCRlen() only shortens correctly to a single note value
391 52 : if (destinationChordRest->ticks() > f) {
392 4 : changeCRlen(destinationChordRest, baseLen.fraction());
393 : }
394 :
395 : // Clear the rest of the time the tuplet will take
396 52 : if (destinationChordRest->ticks() < f) {
397 28 : makeGap(destinationChordRest->segment(), destinationChordRest->track(), f, ot, /*keepChord*/ true);
398 : }
399 :
400 52 : Tuplet* tuplet = Factory::createTuplet(measure);
401 52 : tuplet->setRatio(ratio);
402 :
403 52 : tuplet->setNumberType(numberType);
404 52 : if (tuplet->numberType() == TupletNumberType(tuplet->style().styleI(Sid::tupletNumberType))) {
405 52 : tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::STYLED);
406 : } else {
407 0 : tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::UNSTYLED);
408 : }
409 :
410 52 : tuplet->setBracketType(bracketType);
411 52 : if (tuplet->bracketType() == TupletBracketType(tuplet->style().styleI(Sid::tupletBracketType))) {
412 52 : tuplet->setPropertyFlags(Pid::BRACKET_TYPE, PropertyFlags::STYLED);
413 : } else {
414 0 : tuplet->setPropertyFlags(Pid::BRACKET_TYPE, PropertyFlags::UNSTYLED);
415 : }
416 :
417 52 : tuplet->setTicks(f);
418 52 : tuplet->setBaseLen(baseLen);
419 :
420 52 : tuplet->setTrack(destinationChordRest->track());
421 52 : tuplet->setTick(destinationChordRest->tick());
422 :
423 52 : if (ot) {
424 6 : tuplet->setTuplet(ot);
425 : }
426 :
427 52 : cmdCreateTuplet(destinationChordRest, tuplet);
428 :
429 52 : return tuplet;
430 : }
431 :
432 : //---------------------------------------------------------
433 : // selectNewTuplet
434 : // Select where input continues in a new tuplet, and use
435 : // its base length as the input duration
436 : //---------------------------------------------------------
437 :
438 0 : void Score::selectNewTuplet(Tuplet* tuplet)
439 : {
440 0 : const std::vector<DurationElement*>& elements = tuplet->elements();
441 0 : DurationElement* elementForSelect = nullptr;
442 0 : if (!elements.empty()) {
443 0 : DurationElement* firstElement = elements.front();
444 0 : if (firstElement->isRest()) {
445 0 : elementForSelect = firstElement;
446 0 : } else if (elements.size() > 1) {
447 0 : elementForSelect = elements[1];
448 : }
449 : }
450 :
451 0 : if (elementForSelect) {
452 0 : score()->select(elementForSelect, SelectType::SINGLE, 0);
453 0 : score()->inputState().setDuration(tuplet->baseLen());
454 : }
455 0 : }
456 :
457 : //---------------------------------------------------------
458 : // addRest
459 : // create one Rest at tick with duration d
460 : // create segment if necessary
461 : //---------------------------------------------------------
462 :
463 1053 : Rest* Score::addRest(const Fraction& tick, track_idx_t track, TDuration d, Tuplet* tuplet)
464 : {
465 1053 : Measure* measure = tick2measure(tick);
466 1053 : Rest* rest = Factory::createRest(this->dummy(), d);
467 1053 : if (d.type() == DurationType::V_MEASURE) {
468 674 : rest->setTicks(measure->stretchedLen(staff(track2staff(track))));
469 : } else {
470 379 : rest->setTicks(d.fraction());
471 : }
472 1053 : rest->setTrack(track);
473 1053 : rest->setTuplet(tuplet);
474 1053 : undoAddCR(rest, measure, tick);
475 1053 : return rest;
476 : }
477 :
478 : //---------------------------------------------------------
479 : // addRest
480 : //---------------------------------------------------------
481 :
482 74 : Rest* Score::addRest(Segment* s, track_idx_t track, TDuration d, Tuplet* tuplet)
483 : {
484 74 : Rest* rest = Factory::createRest(s, d);
485 74 : if (d.type() == DurationType::V_MEASURE) {
486 41 : rest->setTicks(s->measure()->stretchedLen(staff(track / VOICES)));
487 : } else {
488 33 : rest->setTicks(d.fraction());
489 : }
490 74 : rest->setTrack(track);
491 74 : rest->setTuplet(tuplet);
492 74 : undoAddCR(rest, tick2measure(s->tick()), s->tick());
493 74 : return rest;
494 : }
495 :
496 : //---------------------------------------------------------
497 : // addChord
498 : // Create one Chord at tick with duration d
499 : // - create segment if necessary.
500 : // - Use chord "oc" as prototype;
501 : // - if "genTie" then tie to chord "oc"
502 : //---------------------------------------------------------
503 :
504 1 : Chord* Score::addChord(const Fraction& tick, TDuration d, Chord* oc, bool genTie, Tuplet* tuplet)
505 : {
506 1 : Measure* measure = tick2measure(tick);
507 1 : if (measure->endTick() <= tick) {
508 0 : LOGD("Score::addChord(): end of score?");
509 0 : return 0;
510 : }
511 :
512 1 : Chord* chord = Factory::createChord(this->dummy());
513 1 : chord->setTuplet(tuplet);
514 1 : chord->setTrack(oc->track());
515 1 : chord->setDurationType(d);
516 1 : chord->setTicks(d.fraction());
517 1 : chord->setStemDirection(oc->stemDirection());
518 :
519 2 : for (Note* n : oc->notes()) {
520 1 : Note* nn = Factory::createNote(chord);
521 1 : nn->setPitch(n->pitch());
522 1 : nn->setTpc1(n->tpc1());
523 1 : nn->setTpc2(n->tpc2());
524 1 : chord->add(nn);
525 : }
526 1 : undoAddCR(chord, measure, tick);
527 :
528 : //
529 : // now as both chords are in place
530 : // (have segments as parent) we can add ties:
531 : //
532 1 : if (genTie) {
533 1 : size_t n = oc->notes().size();
534 2 : for (size_t i = 0; i < n; ++i) {
535 1 : Note* n1 = oc->notes()[i];
536 1 : Note* n2 = chord->notes()[i];
537 1 : Tie* tie = Factory::createTie(n1);
538 1 : tie->setStartNote(n1);
539 1 : tie->setEndNote(n2);
540 1 : tie->setTick(tie->startNote()->tick());
541 1 : tie->setTick2(tie->endNote()->tick());
542 1 : tie->setTrack(n1->track());
543 1 : undoAddElement(tie);
544 : }
545 : }
546 :
547 1 : return chord;
548 : }
549 :
550 : //---------------------------------------------------------
551 : // addClone
552 : //---------------------------------------------------------
553 :
554 437 : ChordRest* Score::addClone(ChordRest* cr, const Fraction& tick, const TDuration& d)
555 : {
556 : ChordRest* newcr;
557 : // change a MeasureRepeat into an Rest
558 437 : if (cr->isMeasureRepeat()) {
559 0 : newcr = Factory::copyRest(*toRest(cr));
560 0 : toRest(newcr)->hack_toRestType();
561 : } else {
562 437 : newcr = toChordRest(cr->clone());
563 : }
564 437 : newcr->mutldata()->setPosX(0.0);
565 437 : newcr->setDurationType(d);
566 437 : newcr->setTicks(d.isMeasure() ? cr->measure()->stretchedLen(cr->staff()) : d.fraction());
567 437 : newcr->setTuplet(cr->tuplet());
568 437 : newcr->setSelected(false);
569 437 : if (newcr->isChord()) {
570 3 : muse::DeleteAll(toChord(newcr)->graceNotes());
571 3 : toChord(newcr)->removeAllGraceNotes();
572 : }
573 :
574 437 : undoAddCR(newcr, cr->measure(), tick);
575 437 : return newcr;
576 : }
577 :
578 : //---------------------------------------------------------
579 : // setRest
580 : // sets rests and returns the first one
581 : // "l" is in local (stretched) time
582 : //---------------------------------------------------------
583 :
584 721 : Rest* Score::setRest(const Fraction& _tick, track_idx_t track, const Fraction& _l, bool useDots, Tuplet* tuplet, bool useFullMeasureRest)
585 : {
586 721 : std::vector<Rest*> rests = setRests(_tick, track, _l, useDots, tuplet, useFullMeasureRest);
587 1442 : return rests.empty() ? nullptr : rests.front();
588 721 : }
589 :
590 : //---------------------------------------------------------
591 : // setRests
592 : // create one or more rests to fill "l"
593 : // "l" is in local (stretched) time
594 : //---------------------------------------------------------
595 :
596 1113 : std::vector<Rest*> Score::setRests(const Fraction& _tick, track_idx_t track, const Fraction& _l, bool useDots, Tuplet* tuplet,
597 : bool useFullMeasureRest)
598 : {
599 1113 : Fraction l = _l;
600 1113 : Fraction tick = _tick;
601 1113 : Measure* measure = tick2measure(tick);
602 1113 : std::vector<Rest*> rests;
603 1113 : Staff* staff = Score::staff(track / VOICES);
604 :
605 1735 : while (!l.isZero()) {
606 : //
607 : // divide into measures
608 : //
609 975 : Fraction f;
610 975 : if (tuplet) {
611 75 : f = tuplet->baseLen().fraction() * tuplet->ratio().numerator();
612 156 : for (DurationElement* de : tuplet->elements()) {
613 119 : if (de->tick() >= tick) {
614 38 : break;
615 : }
616 81 : f -= de->ticks();
617 : }
618 : //
619 : // restrict to tuplet len
620 : //
621 75 : if (f < l) {
622 0 : l = f;
623 : }
624 : } else {
625 900 : if (measure->tick() < tick) {
626 184 : f = measure->endTick() - tick;
627 : } else {
628 716 : f = measure->ticks();
629 : }
630 900 : f *= staff->timeStretch(tick);
631 : }
632 :
633 975 : if (f > l) {
634 144 : f = l;
635 : }
636 :
637 : // Don't fill a full measure with rests on a non-zero voice, *unless* it has links in voice zero
638 975 : bool fullMeasure = tick == measure->tick() && f == measure->stretchedLen(staff);
639 975 : bool emptyNonZeroVoice = track2voice(track) != 0 && !measure->hasVoice(track);
640 975 : if (emptyNonZeroVoice && fullMeasure && !staff->trackHasLinksInVoiceZero(track)) {
641 17 : l -= f;
642 17 : measure = measure->nextMeasure();
643 17 : if (!measure) {
644 353 : break;
645 : }
646 17 : tick = measure->tick();
647 17 : continue;
648 : }
649 :
650 958 : if ((measure->timesig() == measure->ticks()) // not in pickup measure
651 945 : && fullMeasure
652 659 : && !tuplet
653 1903 : && useFullMeasureRest) {
654 659 : Rest* rest = addRest(tick, track, TDuration(DurationType::V_MEASURE), tuplet);
655 659 : tick += rest->actualTicks();
656 659 : rests.push_back(rest);
657 : } else {
658 : //
659 : // compute list of durations which will fit l
660 : //
661 299 : std::vector<TDuration> dList;
662 299 : if (tuplet || f == Fraction(0, 1)) {
663 75 : dList = toDurationList(l, useDots);
664 75 : std::reverse(dList.begin(), dList.end());
665 : } else {
666 224 : Fraction timeStretch = staff->timeStretch(tick);
667 672 : dList = toRhythmicDurationList(f, true, (tick - measure->tick()) * timeStretch, sigmap()->timesig(
668 672 : tick).nominal(), measure, useDots ? 1 : 0, timeStretch);
669 : }
670 299 : if (dList.empty()) {
671 0 : return rests;
672 : }
673 :
674 299 : Rest* rest = 0;
675 678 : for (const TDuration& d : dList) {
676 379 : rest = addRest(tick, track, d, tuplet);
677 : // If we're filling an empty non-zero voice make these gaps
678 379 : rest->setGap(emptyNonZeroVoice && !fullMeasure);
679 379 : rests.push_back(rest);
680 379 : tick += rest->actualTicks();
681 : }
682 299 : }
683 958 : l -= f;
684 :
685 958 : measure = measure->nextMeasure();
686 958 : if (!measure) {
687 353 : break;
688 : }
689 605 : tick = measure->tick();
690 : }
691 1113 : return rests;
692 0 : }
693 :
694 0 : Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, const Slur* slurTemplate)
695 : {
696 0 : if (!secondChordRest) {
697 0 : ChordRestNavigateOptions options;
698 0 : options.disableOverRepeats = true;
699 0 : secondChordRest = Navigation::nextChordRest(firstChordRest, options);
700 :
701 0 : if (!secondChordRest || !secondChordRest->isChord()) {
702 0 : if (slurTemplate && slurTemplate->isHammerOnPullOff() && firstChordRest->isChord()) {
703 0 : Note* endNote = GuitarBend::createEndNote(toChord(firstChordRest)->upNote());
704 0 : if (endNote) {
705 0 : secondChordRest = endNote->chord();
706 : }
707 : }
708 0 : if (!secondChordRest) {
709 0 : secondChordRest = firstChordRest;
710 : }
711 0 : } else if (secondChordRest->isChord()) {
712 0 : bool firstChordRestIsTiedToSecond = firstChordRest->isChord() && toChord(firstChordRest)->allNotesTiedToNext()
713 0 : && toChord(firstChordRest)->upNote()->tieFor()->endNote()->parent() == secondChordRest;
714 :
715 : // Follow chain of tied notes and slur until the last
716 0 : if (firstChordRestIsTiedToSecond || !firstChordRest->isGrace()) {
717 0 : while (toChord(secondChordRest)->allNotesTiedToNext()) {
718 0 : secondChordRest = toChord(secondChordRest)->upNote()->tieFor()->endNote()->chord();
719 : }
720 : }
721 :
722 : // If the first chord rest is also tied to this chain, slur to the next non-tied note
723 0 : if (firstChordRestIsTiedToSecond) {
724 0 : ChordRest* nextCandidate = Navigation::nextChordRest(secondChordRest, options);
725 0 : if (nextCandidate) {
726 0 : secondChordRest = nextCandidate;
727 : }
728 : }
729 : }
730 : }
731 :
732 0 : Slur* slur = slurTemplate ? slurTemplate->clone() : Factory::createSlur(score()->dummy());
733 0 : slur->setScore(firstChordRest->score());
734 0 : slur->setTick(firstChordRest->tick());
735 0 : slur->setTick2(secondChordRest->tick());
736 0 : slur->setTrack(firstChordRest->track());
737 0 : if (secondChordRest->staff()->part() == firstChordRest->staff()->part()
738 0 : && !secondChordRest->staff()->isLinked(firstChordRest->staff())) {
739 0 : slur->setTrack2(secondChordRest->track());
740 : } else {
741 0 : slur->setTrack2(firstChordRest->track());
742 : }
743 0 : slur->setStartElement(firstChordRest);
744 0 : slur->setEndElement(secondChordRest);
745 :
746 0 : firstChordRest->score()->undoAddElement(slur);
747 0 : SlurTieSegment* ss = slur->newSlurTieSegment();
748 0 : ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
749 0 : if (firstChordRest == secondChordRest && !(slur->isOutgoing() || slur->isIncoming())) {
750 0 : ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->style().spatium(), 0.0));
751 : }
752 0 : slur->add(ss);
753 :
754 0 : return slur;
755 : }
756 :
757 : /// The segment that holds the annotations of `chordRest`. For a multimeasure rest,
758 : /// that is the corresponding segment of the first underlying measure.
759 0 : static Segment* annotationSegment(const ChordRest* chordRest)
760 : {
761 0 : const Measure* measure = chordRest->measure();
762 0 : if (measure && measure->isMMRest()) {
763 0 : return measure->mmRestFirst()->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
764 : }
765 :
766 0 : return chordRest->segment();
767 : }
768 :
769 1 : TextBase* Score::addText(TextStyleType type, EngravingItem* destinationElement)
770 : {
771 1 : TextBase* textBox = nullptr;
772 :
773 1 : switch (type) {
774 0 : case TextStyleType::TITLE:
775 : case TextStyleType::SUBTITLE:
776 : case TextStyleType::COMPOSER:
777 : case TextStyleType::LYRICIST:
778 : case TextStyleType::INSTRUMENT_EXCERPT: {
779 0 : MeasureBase* frame = nullptr;
780 :
781 0 : if (destinationElement && destinationElement->isVBox()) {
782 0 : frame = toMeasureBase(destinationElement);
783 : } else {
784 0 : frame = first();
785 0 : if (!frame || !frame->isBox()) {
786 0 : frame = insertBox(ElementType::VBOX, frame);
787 : }
788 : }
789 :
790 0 : textBox = Factory::createText(frame, type);
791 0 : undoAddElement(textBox);
792 0 : break;
793 : }
794 0 : case TextStyleType::FRAME: {
795 0 : if (!destinationElement) {
796 0 : break;
797 : }
798 0 : textBox = Factory::createText(destinationElement, type);
799 0 : undoAddElement(textBox);
800 0 : break;
801 : }
802 0 : case TextStyleType::REHEARSAL_MARK: {
803 0 : ChordRest* chordRest = chordOrRest(destinationElement);
804 0 : if (!chordRest) {
805 0 : break;
806 : }
807 0 : textBox = Factory::createRehearsalMark(annotationSegment(chordRest));
808 0 : textBox->setTrack(chordRest->track());
809 0 : RehearsalMark* r = toRehearsalMark(textBox);
810 0 : textBox->setXmlText(EditRehearsalMark::createRehearsalMarkText(this, r));
811 0 : undoAddElement(textBox);
812 0 : break;
813 : }
814 0 : case TextStyleType::STAFF: {
815 0 : ChordRest* chordRest = chordOrRest(destinationElement);
816 0 : if (!chordRest) {
817 0 : break;
818 : }
819 0 : textBox = Factory::createStaffText(annotationSegment(chordRest), TextStyleType::STAFF);
820 0 : textBox->setTrack(chordRest->track());
821 0 : undoAddElement(textBox);
822 0 : break;
823 : }
824 0 : case TextStyleType::SYSTEM: {
825 0 : ChordRest* chordRest = chordOrRest(destinationElement);
826 0 : if (!chordRest) {
827 0 : break;
828 : }
829 0 : textBox = Factory::createSystemText(annotationSegment(chordRest), TextStyleType::SYSTEM);
830 0 : textBox->setTrack(chordRest->track());
831 0 : undoAddElement(textBox);
832 0 : break;
833 : }
834 0 : case TextStyleType::DYNAMICS: {
835 0 : ChordRest* chordRest = chordOrRest(destinationElement);
836 0 : if (!chordRest) {
837 0 : break;
838 : }
839 0 : textBox = Factory::createDynamic(annotationSegment(chordRest));
840 0 : textBox->setTrack(chordRest->track());
841 0 : undoAddElement(textBox);
842 0 : break;
843 : }
844 0 : case TextStyleType::EXPRESSION: {
845 0 : ChordRest* chordRest = chordOrRest(destinationElement);
846 0 : if (!chordRest) {
847 0 : break;
848 : }
849 0 : textBox = Factory::createExpression(annotationSegment(chordRest));
850 0 : textBox->setTrack(chordRest->track());
851 0 : undoAddElement(textBox);
852 0 : break;
853 : }
854 0 : case TextStyleType::INSTRUMENT_CHANGE: {
855 0 : ChordRest* chordRest = chordOrRest(destinationElement);
856 0 : if (!chordRest) {
857 0 : break;
858 : }
859 0 : textBox = Factory::createInstrumentChange(annotationSegment(chordRest));
860 0 : textBox->setTrack(chordRest->track());
861 0 : undoAddElement(textBox);
862 0 : break;
863 : }
864 0 : case TextStyleType::STICKING: {
865 0 : ChordRest* chordRest = chordOrRest(destinationElement);
866 0 : if (!chordRest) {
867 0 : break;
868 : }
869 0 : textBox = Factory::createSticking(annotationSegment(chordRest));
870 0 : textBox->setTrack(chordRest->track());
871 0 : undoAddElement(textBox);
872 0 : break;
873 : }
874 0 : case TextStyleType::FINGERING:
875 : case TextStyleType::LH_GUITAR_FINGERING:
876 : case TextStyleType::RH_GUITAR_FINGERING:
877 : case TextStyleType::STRING_NUMBER: {
878 0 : if (!destinationElement || !destinationElement->isNote()) {
879 0 : break;
880 : }
881 :
882 0 : const Staff* staff = destinationElement->staff();
883 0 : bool isTablature = staff->isTabStaff(destinationElement->tick());
884 0 : bool tabFingering = staff->staffType(destinationElement->tick())->showTabFingering();
885 0 : if (isTablature && !tabFingering) {
886 0 : break;
887 : }
888 :
889 0 : textBox = Factory::createFingering(toNote(destinationElement), type);
890 0 : textBox->setTrack(destinationElement->track());
891 0 : undoAddElement(textBox);
892 0 : break;
893 : }
894 1 : case TextStyleType::HARMONY_A:
895 : case TextStyleType::HARMONY_ROMAN:
896 : case TextStyleType::HARMONY_NASHVILLE: {
897 1 : track_idx_t track = muse::nidx;
898 1 : EngravingItem* newParent = nullptr;
899 1 : if (destinationElement && destinationElement->isFretDiagram()) {
900 1 : FretDiagram* fretDiagram = toFretDiagram(destinationElement);
901 1 : track = fretDiagram->track();
902 1 : newParent = fretDiagram->harmony() ? toEngravingItem(fretDiagram->segment()) : toEngravingItem(fretDiagram);
903 : } else {
904 0 : ChordRest* chordRest = chordOrRest(destinationElement);
905 0 : if (chordRest) {
906 0 : track = chordRest->track();
907 0 : newParent = chordRest->segment();
908 : }
909 : }
910 :
911 1 : if (track == muse::nidx || !newParent) {
912 : break;
913 : }
914 :
915 1 : Harmony* harmony = Factory::createHarmony(newParent);
916 1 : harmony->setTrack(track);
917 :
918 : static const std::map<TextStyleType, HarmonyType> harmonyTypes = {
919 : { TextStyleType::HARMONY_A, HarmonyType::STANDARD },
920 : { TextStyleType::HARMONY_ROMAN, HarmonyType::ROMAN },
921 : { TextStyleType::HARMONY_NASHVILLE, HarmonyType::NASHVILLE }
922 3 : };
923 1 : harmony->setHarmonyType(harmonyTypes.at(type));
924 :
925 1 : textBox = harmony;
926 1 : undoAddElement(textBox);
927 1 : break;
928 : }
929 0 : case TextStyleType::LYRICS_ODD: {
930 0 : if (!destinationElement || (!destinationElement->isNote() && !destinationElement->isLyrics() && !destinationElement->isRest())) {
931 0 : break;
932 : }
933 0 : ChordRest* chordRest = nullptr;
934 0 : if (destinationElement->isNote()) {
935 0 : chordRest = toNote(destinationElement)->chord();
936 0 : if (chordRest->isGrace()) {
937 0 : chordRest = toChordRest(chordRest->ownershipParent());
938 : }
939 0 : } else if (destinationElement->isLyrics()) {
940 0 : chordRest = toLyrics(destinationElement)->chordRest();
941 0 : } else if (destinationElement->isRest()) {
942 0 : chordRest = toChordRest(destinationElement);
943 : } else {
944 0 : break;
945 : }
946 :
947 0 : int no = static_cast<int>(chordRest->lyrics().size());
948 0 : const auto& spanners = spannerMap().findOverlapping(chordRest->tick().ticks(), chordRest->endTick().ticks());
949 0 : for (auto& spanner : spanners) {
950 0 : if (!spanner.value->isPartialLyricsLine() || spanner.start != chordRest->tick().ticks()) {
951 0 : continue;
952 : }
953 0 : const PartialLyricsLine* line = toPartialLyricsLine(spanner.value);
954 :
955 0 : no = std::max(no, line->verse() + 1);
956 : }
957 :
958 : // Also check how many partial lines there are
959 0 : Lyrics* lyrics = Factory::createLyrics(chordRest);
960 0 : lyrics->setTrack(chordRest->track());
961 0 : lyrics->setProperty(Pid::VERSE, no);
962 :
963 0 : textBox = lyrics;
964 0 : undoAddElement(textBox);
965 0 : break;
966 : }
967 0 : case TextStyleType::TEMPO: {
968 0 : ChordRest* chordRest = chordOrRest(destinationElement);
969 0 : if (!chordRest) {
970 0 : break;
971 : }
972 :
973 0 : SigEvent event = sigmap()->timesig(chordRest->tick());
974 0 : Fraction f = event.nominal();
975 0 : String text(u"<sym>metNoteQuarterUp</sym> = 80");
976 0 : switch (f.denominator()) {
977 0 : case 1:
978 0 : text = u"<sym>metNoteWhole</sym> = 80";
979 0 : break;
980 0 : case 2:
981 0 : text = u"<sym>metNoteHalfUp</sym> = 80";
982 0 : break;
983 0 : case 4:
984 0 : text = u"<sym>metNoteQuarterUp</sym> = 80";
985 0 : break;
986 0 : case 8:
987 0 : if (f.numerator() % 3 == 0) {
988 0 : text = u"<sym>metNoteQuarterUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
989 : } else {
990 0 : text = u"<sym>metNote8thUp</sym> = 80";
991 : }
992 0 : break;
993 0 : case 16:
994 0 : if (f.numerator() % 3 == 0) {
995 0 : text = u"<sym>metNote8thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
996 : } else {
997 0 : text = u"<sym>metNote16thUp</sym> = 80";
998 : }
999 0 : break;
1000 0 : case 32:
1001 0 : if (f.numerator() % 3 == 0) {
1002 0 : text = u"<sym>metNote16thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
1003 : } else {
1004 0 : text = u"<sym>metNote32ndUp</sym> = 80";
1005 : }
1006 0 : break;
1007 0 : case 64:
1008 0 : if (f.numerator() % 3 == 0) {
1009 0 : text = u"<sym>metNote32ndUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
1010 : } else {
1011 0 : text = u"<sym>metNote64thUp</sym> = 80";
1012 : }
1013 0 : break;
1014 0 : case 128:
1015 0 : if (f.numerator() % 3 == 0) {
1016 0 : text = u"<sym>metNote64ndUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
1017 : } else {
1018 0 : text = u"<sym>metNote128thUp</sym> = 80";
1019 : }
1020 0 : break;
1021 0 : default:
1022 0 : break;
1023 : }
1024 :
1025 0 : TempoText* tempoText = Factory::createTempoText(chordRest->segment());
1026 0 : tempoText->setTrack(0);
1027 0 : tempoText->setXmlText(text);
1028 0 : tempoText->setFollowText(true);
1029 0 : tempoText->updateTempo();
1030 :
1031 0 : textBox = tempoText;
1032 0 : undoAddElement(textBox);
1033 0 : break;
1034 0 : }
1035 0 : case TextStyleType::HARP_PEDAL_DIAGRAM:
1036 : case TextStyleType::HARP_PEDAL_TEXT_DIAGRAM: {
1037 0 : ChordRest* chordRest = getSelectedChordRest();
1038 0 : if (!chordRest) {
1039 0 : break;
1040 : }
1041 0 : textBox = Factory::createHarpPedalDiagram(annotationSegment(chordRest));
1042 0 : textBox->setTrack(chordRest->track());
1043 0 : undoAddElement(textBox);
1044 0 : break;
1045 : }
1046 0 : default:
1047 0 : break;
1048 : }
1049 :
1050 1 : return textBox;
1051 : }
1052 :
1053 : //---------------------------------------------------------
1054 : // searchNote
1055 : // search for note or rest before or at tick position tick
1056 : // in staff
1057 : //---------------------------------------------------------
1058 :
1059 0 : ChordRest* Score::searchNote(const Fraction& tick, track_idx_t track) const
1060 : {
1061 0 : ChordRest* ipe = 0;
1062 0 : SegmentType st = SegmentType::ChordRest;
1063 0 : for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st)) {
1064 0 : ChordRest* cr = segment->cr(track);
1065 0 : if (!cr) {
1066 0 : continue;
1067 : }
1068 0 : if (cr->tick() == tick) {
1069 0 : return cr;
1070 : }
1071 0 : if (cr->tick() > tick) {
1072 0 : return ipe ? ipe : cr;
1073 : }
1074 0 : ipe = cr;
1075 : }
1076 0 : return 0;
1077 : }
1078 :
1079 : //---------------------------------------------------------
1080 : // cmdAddOttava
1081 : //---------------------------------------------------------
1082 :
1083 0 : void Score::cmdAddOttava(OttavaType type)
1084 : {
1085 0 : const Selection sel = selection(); // copy selection state before the operation.
1086 : // add on each staff if possible
1087 0 : if (sel.isRange() && sel.staffStart() != sel.staffEnd() - 1) {
1088 0 : for (staff_idx_t staffIdx = sel.staffStart(); staffIdx < sel.staffEnd(); ++staffIdx) {
1089 0 : ChordRest* cr1 = sel.firstChordRest(staffIdx * VOICES);
1090 0 : ChordRest* cr2 = sel.lastChordRest(staffIdx * VOICES);
1091 0 : if (!cr1) {
1092 0 : continue;
1093 : }
1094 0 : if (!cr2) {
1095 0 : cr2 = cr1;
1096 : }
1097 0 : Ottava* ottava = Factory::createOttava(this->dummy());
1098 0 : ottava->setOttavaType(type);
1099 0 : if (type == OttavaType::OTTAVA_8VB /*|| type == OttavaType::OTTAVA_15MB || type == OttavaType::OTTAVA_22MB*/) {
1100 0 : ottava->setPlacement(PlacementV::BELOW);
1101 0 : ottava->styleChanged();
1102 : }
1103 0 : ottava->setTrack(cr1->track());
1104 0 : ottava->setTrack2(cr1->track());
1105 0 : ottava->setTick(cr1->tick());
1106 0 : ottava->setTick2(cr2->endTick());
1107 0 : undoAddElement(ottava);
1108 : }
1109 : } else {
1110 : ChordRest* cr1;
1111 : ChordRest* cr2;
1112 0 : getSelectedStartEndChordRests(cr1, cr2);
1113 0 : if (!cr1) {
1114 0 : return;
1115 : }
1116 0 : if (!cr2) {
1117 0 : cr2 = cr1;
1118 : }
1119 :
1120 0 : Ottava* ottava = Factory::createOttava(this->dummy());
1121 0 : ottava->setOttavaType(type);
1122 0 : if (type == OttavaType::OTTAVA_8VB /*|| type == OttavaType::OTTAVA_15MB || type == OttavaType::OTTAVA_22MB*/) {
1123 0 : ottava->setPlacement(PlacementV::BELOW);
1124 0 : ottava->styleChanged();
1125 : }
1126 0 : ottava->setTrack(cr1->track());
1127 0 : ottava->setTrack2(cr1->track());
1128 0 : ottava->setTick(cr1->tick());
1129 0 : ottava->setTick2(cr2->endTick());
1130 0 : undoAddElement(ottava);
1131 0 : if (!noteEntryMode()) {
1132 0 : select(ottava, SelectType::SINGLE, 0);
1133 : }
1134 : }
1135 0 : }
1136 :
1137 : //---------------------------------------------------------
1138 : // cmdAddNoteLine
1139 : //---------------------------------------------------------
1140 :
1141 0 : void Score::addNoteLine()
1142 : {
1143 0 : std::vector<Note*> selectedNotes;
1144 :
1145 0 : if (selection().isRange()) {
1146 0 : track_idx_t startTrack = selection().staffStart() * VOICES;
1147 0 : track_idx_t endTrack = selection().staffEnd() * VOICES;
1148 :
1149 0 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1150 0 : std::vector<Note*> notes = selection().noteList(track);
1151 0 : selectedNotes.insert(selectedNotes.end(), notes.begin(), notes.end());
1152 0 : }
1153 : } else {
1154 0 : std::vector<Note*> notes = selection().noteList();
1155 0 : selectedNotes.insert(selectedNotes.end(), notes.begin(), notes.end());
1156 0 : }
1157 :
1158 0 : Note* startNote = nullptr;
1159 0 : Note* endNote = nullptr;
1160 :
1161 0 : for (Note* note : selectedNotes) {
1162 0 : if (startNote == nullptr || startNote->chord()->tick() > note->chord()->tick()) {
1163 0 : startNote = note;
1164 : }
1165 0 : if (endNote == nullptr || endNote->chord()->tick() < note->chord()->tick()) {
1166 0 : endNote = note;
1167 : }
1168 : }
1169 :
1170 0 : if (!startNote) {
1171 0 : LOGD("addNoteLine: no first note %p", startNote);
1172 0 : return;
1173 : }
1174 :
1175 0 : if (startNote == endNote) {
1176 0 : endNote = SLine::guessFinalNote(startNote);
1177 : }
1178 :
1179 0 : if (!endNote) {
1180 0 : LOGD("addNoteLine: no last note note %p", endNote);
1181 0 : return;
1182 : }
1183 :
1184 0 : NoteLine* line = Factory::createNoteLine(startNote);
1185 0 : line->setStartElement(startNote);
1186 0 : line->setTick(startNote->chord()->tick());
1187 0 : line->setEndElement(endNote);
1188 :
1189 0 : undoAddElement(line);
1190 0 : }
1191 :
1192 : //---------------------------------------------------------
1193 : // deleteItem
1194 : //---------------------------------------------------------
1195 :
1196 73 : void Score::deleteItem(EngravingItem* el)
1197 : {
1198 73 : if (!el) {
1199 0 : return;
1200 : }
1201 :
1202 73 : if (el->generated()) {
1203 15 : switch (el->type()) {
1204 : // These types can be removed, even if generated
1205 9 : case ElementType::BAR_LINE:
1206 : case ElementType::BRACKET:
1207 : case ElementType::CLEF:
1208 : case ElementType::INSTRUMENT_NAME:
1209 : case ElementType::KEYSIG:
1210 : case ElementType::MEASURE_NUMBER:
1211 : case ElementType::SYSTEM_LOCK_INDICATOR:
1212 : case ElementType::PAGE_LOCK_INDICATOR:
1213 : case ElementType::HAMMER_ON_PULL_OFF_TEXT:
1214 : case ElementType::PLAY_COUNT_TEXT:
1215 : case ElementType::LYRICSLINE_SEGMENT:
1216 9 : break;
1217 : // All other types cannot be removed if generated
1218 6 : default:
1219 6 : return;
1220 : }
1221 : }
1222 : // LOGD("%s", el->typeName());
1223 :
1224 67 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
1225 :
1226 67 : switch (el->type()) {
1227 0 : case ElementType::INSTRUMENT_NAME: {
1228 0 : Part* part = el->part();
1229 0 : InstrumentName* in = toInstrumentName(el);
1230 0 : if (in->instrumentNameType() == InstrumentNameType::LONG) {
1231 0 : tx.push(new ChangeInstrumentLong(Fraction(0, 1), part, String()));
1232 0 : } else if (in->instrumentNameType() == InstrumentNameType::SHORT) {
1233 0 : tx.push(new ChangeInstrumentShort(Fraction(0, 1), part, String()));
1234 : }
1235 : }
1236 0 : break;
1237 :
1238 0 : case ElementType::TIMESIG: {
1239 : // timesig might already be removed
1240 0 : TimeSig* ts = toTimeSig(el);
1241 0 : Segment* s = ts->segment();
1242 0 : Measure* m = s->measure();
1243 0 : Segment* ns = m->findSegment(s->segmentType(), s->tick());
1244 0 : if (!ns || (ns->element(ts->track()) != ts)) {
1245 0 : LOGD("deleteItem: not found");
1246 0 : break;
1247 : }
1248 0 : EditTimeSig::removeTimeSig(tx, this, ts);
1249 : }
1250 0 : break;
1251 :
1252 2 : case ElementType::KEYSIG:
1253 : {
1254 2 : KeySig* k = toKeySig(el);
1255 2 : Segment* nextCrSeg = k->segment()->next1(SegmentType::ChordRest);
1256 2 : const TrackRange trackRange = el->part()->trackRange();
1257 4 : bool ic = nextCrSeg && nextCrSeg->findAnnotation(ElementType::INSTRUMENT_CHANGE,
1258 2 : trackRange.startTrack, trackRange.endTrack - 1);
1259 2 : undoRemoveElement(k);
1260 2 : if (ic) {
1261 0 : KeySigEvent ke = k->keySigEvent();
1262 0 : ke.setForInstrumentChange(true);
1263 0 : Key cKey = k->staff()->keySigEvent(k->tick()).concertKey();
1264 0 : Key tKey = cKey;
1265 0 : if (!style().styleB(Sid::concertPitch)) {
1266 0 : Interval v = k->part()->instrument(k->tick())->transpose();
1267 0 : v.flip();
1268 0 : tKey = Transpose::transposeKey(cKey, v, k->part()->preferSharpFlat());
1269 : }
1270 0 : ke.setConcertKey(cKey);
1271 0 : ke.setKey(tKey);
1272 0 : EditKeySig::undoChangeKeySig(tx, this, k->staff(), k->tick(), ke);
1273 0 : }
1274 4 : for (size_t i = 0; i < k->part()->nstaves(); i++) {
1275 6 : for (Staff* staff : k->part()->staff(i)->staffList()) {
1276 4 : KeySigEvent e = staff->keySigEvent(k->tick());
1277 4 : staff->score()->updateInstrumentChangeTranspositions(e, staff, k->tick());
1278 6 : }
1279 : }
1280 : }
1281 2 : break;
1282 :
1283 4 : case ElementType::NOTE:
1284 : {
1285 4 : Chord* chord = toChord(el->ownershipParent());
1286 4 : if (chord->notes().size() > 1) {
1287 0 : undoRemoveElement(el);
1288 0 : select(chord->downNote(), SelectType::SINGLE, 0);
1289 0 : break;
1290 : }
1291 : // else fall through
1292 4 : el = chord;
1293 : }
1294 : // fall through
1295 :
1296 11 : case ElementType::CHORD:
1297 : {
1298 11 : Chord* chord = toChord(el);
1299 11 : removeChordRest(chord, false);
1300 :
1301 : // replace with rest
1302 11 : if (chord->noteType() == NoteType::NORMAL) {
1303 10 : Segment* segment = chord->segment();
1304 10 : Rest* rest = Factory::createRest(segment, chord->durationType());
1305 10 : rest->setTicks(chord->ticks());
1306 10 : rest->setTrack(el->track());
1307 :
1308 10 : undoAddCR(rest, segment->measure(), segment->tick());
1309 :
1310 10 : Tuplet* tuplet = chord->tuplet();
1311 10 : if (tuplet) {
1312 0 : std::list<EngravingObject*> tl = tuplet->linkList();
1313 0 : for (EngravingObject* e : rest->linkList()) {
1314 0 : DurationElement* de = toDurationElement(e);
1315 0 : for (EngravingObject* ee : tl) {
1316 0 : Tuplet* t = toTuplet(ee);
1317 0 : if (t->score() == de->score() && t->track() == de->track()) {
1318 0 : de->setTuplet(t);
1319 0 : t->add(de);
1320 0 : break;
1321 : }
1322 : }
1323 0 : }
1324 0 : }
1325 : //select(rest, SelectType::SINGLE, 0);
1326 : } else {
1327 : // remove segment if empty
1328 1 : Segment* seg = chord->segment();
1329 1 : if (seg->empty()) {
1330 0 : undoRemoveElement(seg);
1331 : }
1332 : }
1333 : }
1334 11 : break;
1335 :
1336 3 : case ElementType::MEASURE_REPEAT:
1337 : {
1338 3 : MeasureRepeat* mr = toMeasureRepeat(el);
1339 3 : removeChordRest(mr, false);
1340 3 : Segment* segment = mr->segment();
1341 3 : Rest* rest = Factory::createRest(segment);
1342 3 : rest->setDurationType(DurationType::V_MEASURE);
1343 3 : rest->setTicks(mr->measure()->stretchedLen(mr->staff()));
1344 3 : rest->setTrack(mr->track());
1345 3 : undoAddCR(rest, segment->measure(), segment->tick());
1346 :
1347 : // tell measures they're not part of measure repeat group anymore
1348 3 : Measure* m = mr->firstMeasureOfGroup();
1349 9 : for (int i = 1; i <= mr->numMeasures(); ++i) {
1350 6 : undoChangeMeasureRepeatCount(m, 0, mr->staffIdx());
1351 : // don't remove grouping if within measure repeat group on another staff
1352 6 : bool otherStavesStillNeedGroup = false;
1353 15 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
1354 12 : if (m->isMeasureRepeatGroupWithNextM(staffIdx) && staffIdx != mr->staffIdx()) {
1355 3 : otherStavesStillNeedGroup = true;
1356 3 : break;
1357 : }
1358 : }
1359 6 : if (!otherStavesStillNeedGroup) {
1360 3 : m->undoSetNoBreak(false);
1361 : }
1362 6 : m = m->nextMeasure();
1363 : }
1364 : }
1365 : // fall through
1366 : case ElementType::MMREST:
1367 : case ElementType::REST:
1368 : //
1369 : // only allow for voices != 0
1370 : // e.g. voice 0 rests cannot be removed
1371 : //
1372 : {
1373 6 : Rest* rest = toRest(el);
1374 6 : if (rest->tuplet() && rest->tuplet()->elements().empty()) {
1375 0 : undoRemoveElement(rest->tuplet());
1376 : }
1377 6 : if ((el->voice() != 0) && !rest->tuplet()) {
1378 3 : rest->undoChangeProperty(Pid::GAP, true);
1379 3 : rest->undoChangeProperty(Pid::BEAM_MODE, BeamMode::AUTO);
1380 9 : for (EngravingObject* r : el->linkList()) {
1381 6 : Rest* rr = toRest(r);
1382 6 : if (rr->track() % VOICES) {
1383 6 : rr->undoChangeProperty(Pid::GAP, true);
1384 : }
1385 3 : }
1386 :
1387 : // delete them really when only gap rests are in the actual measure.
1388 3 : Measure* m = toRest(el)->measure();
1389 3 : track_idx_t track = el->track();
1390 3 : if (m->isOnlyGapRests(track) && !el->staff()->trackHasLinksInVoiceZero(track)) {
1391 : static const SegmentType st { SegmentType::ChordRest };
1392 0 : for (const Segment* s = m->first(st); s; s = s->next(st)) {
1393 0 : EngravingItem* del = s->element(track);
1394 0 : if (s->segmentType() != st || !del) {
1395 0 : continue;
1396 : }
1397 0 : if (toRest(del)->isGap()) {
1398 0 : undoRemoveElement(del);
1399 : }
1400 : }
1401 0 : checkSpanner(m->tick(), m->endTick());
1402 : } else {
1403 : // check if the other rest could be combined
1404 3 : Segment* s = toRest(el)->segment();
1405 :
1406 3 : std::vector<Rest*> rests;
1407 : // find previous segment with cr in this track
1408 3 : EngravingItem* pe = 0;
1409 3 : for (Segment* ps = s->prev(SegmentType::ChordRest); ps; ps = ps->prev(SegmentType::ChordRest)) {
1410 2 : EngravingItem* elm = ps->element(track);
1411 2 : if (elm && elm->isRest() && toRest(elm)->isGap()) {
1412 0 : pe = el;
1413 0 : rests.push_back(toRest(elm));
1414 2 : } else if (elm) {
1415 2 : break;
1416 : }
1417 : }
1418 : // find next segment with cr in this track
1419 : Segment* ns;
1420 3 : EngravingItem* ne = 0;
1421 4 : for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::ChordRest)) {
1422 4 : EngravingItem* elm = ns->element(track);
1423 4 : if (elm && elm->isRest() && toRest(elm)->isGap()) {
1424 1 : ne = elm;
1425 1 : rests.push_back(toRest(elm));
1426 3 : } else if (elm) {
1427 3 : break;
1428 : }
1429 : }
1430 :
1431 3 : Fraction stick = pe ? pe->tick() : s->tick();
1432 3 : Fraction ticks = { 0, 1 };
1433 :
1434 3 : if (ne) {
1435 1 : ticks = ne->tick() - stick + toRest(ne)->actualTicks();
1436 2 : } else if (ns) {
1437 2 : ticks = ns->tick() - stick;
1438 : } else {
1439 0 : ticks = m->endTick() - stick;
1440 : }
1441 :
1442 3 : if (ticks != m->ticks() && ticks != s->ticks()) {
1443 1 : undoRemoveElement(rest);
1444 2 : for (Rest* r : rests) {
1445 1 : undoRemoveElement(r);
1446 : }
1447 :
1448 1 : Fraction f = ticks * el->staff()->timeStretch(el->tick());
1449 :
1450 1 : std::vector<TDuration> dList = toDurationList(f, true);
1451 1 : if (dList.empty()) {
1452 0 : break;
1453 : }
1454 :
1455 1 : Fraction curTick = stick;
1456 2 : for (const TDuration& d : dList) {
1457 1 : Rest* rr = Factory::createRest(this->dummy());
1458 1 : rr->setTicks(d.fraction());
1459 1 : rr->setDurationType(d);
1460 1 : rr->setTrack(track);
1461 1 : rr->setGap(true);
1462 1 : undoAddCR(rr, m, curTick);
1463 1 : curTick += rr->actualTicks();
1464 : }
1465 1 : }
1466 3 : }
1467 : // Set input position
1468 : // TODO If deleted element is last of a sequence, use prev?
1469 3 : if (noteEntryMode()) {
1470 0 : Navigation::move(score(), u"prev-chord");
1471 : }
1472 : }
1473 : }
1474 6 : break;
1475 :
1476 2 : case ElementType::ACCIDENTAL:
1477 2 : if (el->ownershipParent()->isNote()) {
1478 2 : EditNote::changeAccidental(this, toNote(el->ownershipParent()), AccidentalType::NONE);
1479 : } else {
1480 0 : undoRemoveElement(el);
1481 : }
1482 2 : break;
1483 :
1484 9 : case ElementType::BAR_LINE: {
1485 9 : BarLine* bl = toBarLine(el);
1486 9 : Segment* s = bl->segment();
1487 9 : Measure* m = s->measure();
1488 9 : if (s->isBeginBarLineType() || s->isBarLineType()) {
1489 0 : undoRemoveElement(el);
1490 : } else {
1491 9 : if (bl->barLineType() == BarLineType::START_REPEAT) {
1492 0 : Measure* m2 = m->isMMRest() ? m->mmRestFirst() : m;
1493 0 : for (Score* lscore : score()->scoreList()) {
1494 0 : Measure* lmeasure = lscore->tick2measure(m2->tick());
1495 0 : if (lmeasure) {
1496 0 : lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
1497 0 : lmeasure->undoChangeProperty(Pid::REPEAT_COUNT, 2);
1498 : }
1499 0 : }
1500 0 : if (!bl->generated()) {
1501 0 : undoRemoveElement(el);
1502 : }
1503 9 : } else if (bl->barLineType() == BarLineType::END_REPEAT) {
1504 0 : Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
1505 0 : for (Score* lscore : score()->scoreList()) {
1506 0 : Measure* lmeasure = lscore->tick2measure(m2->tick());
1507 0 : if (lmeasure) {
1508 0 : lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
1509 0 : lmeasure->undoChangeProperty(Pid::REPEAT_COUNT, 2);
1510 : }
1511 0 : }
1512 0 : if (!bl->generated()) {
1513 0 : undoRemoveElement(el);
1514 : }
1515 : } else {
1516 9 : bl->undoChangeProperty(Pid::BARLINE_TYPE, PropertyValue::fromValue(BarLineType::NORMAL));
1517 : }
1518 : }
1519 : }
1520 9 : break;
1521 :
1522 2 : case ElementType::TUPLET:
1523 2 : cmdDeleteTuplet(toTuplet(el), true);
1524 2 : break;
1525 :
1526 0 : case ElementType::MEASURE: {
1527 0 : Measure* m = toMeasure(el);
1528 0 : undoRemoveMeasures(m, m);
1529 0 : undoInsertTime(m->tick(), -(m->endTick() - m->tick()));
1530 : }
1531 0 : break;
1532 :
1533 0 : case ElementType::BRACKET:
1534 0 : EditStaffBrackets::undoRemoveBracket(this, toBracket(el));
1535 0 : break;
1536 0 : case ElementType::CLEF:
1537 : {
1538 0 : Clef* clef = toClef(el);
1539 0 : Measure* m = clef->measure();
1540 0 : if (clef->generated()) {
1541 : // find the real clef if this is a cautionary one
1542 0 : if (m && m->prevMeasure()) {
1543 0 : Fraction tick = m->tick();
1544 0 : m = m->prevMeasure();
1545 0 : Segment* s = m->findSegment(SegmentType::Clef, tick);
1546 0 : if (s && s->element(clef->track())) {
1547 0 : clef = toClef(s->element(clef->track()));
1548 : }
1549 : }
1550 : }
1551 0 : undoRemoveElement(clef);
1552 : }
1553 0 : break;
1554 :
1555 0 : case ElementType::MEASURE_NUMBER:
1556 : {
1557 0 : Measure* mea = toMeasure(el->ownershipParent());
1558 0 : switch (mea->measureNumberMode()) {
1559 : // If the user tries to remove an automatically generated measure number,
1560 : // we should force the measure not to show any measure number
1561 0 : case MeasureNumberMode::AUTO:
1562 0 : mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
1563 0 : break;
1564 :
1565 : // If the user tries to remove a measure number that he added manually,
1566 : // then we should set the MeasureNumberMode to AUTO only if will not show if set to auto.
1567 : // If after setting the MeasureNumberMode to AUTO, the measure number still shows,
1568 : // We need to force the measure to hide its measure number.
1569 0 : case MeasureNumberMode::SHOW:
1570 0 : if (mea->showMeasureNumberInAutoMode()) {
1571 0 : mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
1572 : } else {
1573 0 : mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::AUTO));
1574 : }
1575 0 : break;
1576 0 : case MeasureNumberMode::HIDE:
1577 0 : break;
1578 : }
1579 : }
1580 0 : break;
1581 0 : case ElementType::LYRICSLINE_SEGMENT:
1582 : {
1583 0 : el = toLyricsLineSegment(el)->lyricsLine();
1584 0 : Lyrics* lyrics = toLyricsLine(el)->lyrics();
1585 0 : undoRemoveElement(el);
1586 0 : lyrics->undoResetProperty(Pid::LYRIC_TICKS);
1587 0 : lyrics->undoResetProperty(Pid::SYLLABIC);
1588 :
1589 0 : break;
1590 : }
1591 10 : case ElementType::OTTAVA_SEGMENT:
1592 : case ElementType::HAIRPIN_SEGMENT:
1593 : case ElementType::TRILL_SEGMENT:
1594 : case ElementType::VIBRATO_SEGMENT:
1595 : case ElementType::TEXTLINE_SEGMENT:
1596 : case ElementType::VOLTA_SEGMENT:
1597 : case ElementType::SLUR_SEGMENT:
1598 : case ElementType::TIE_SEGMENT:
1599 : case ElementType::LAISSEZ_VIB_SEGMENT:
1600 : case ElementType::PARTIAL_TIE_SEGMENT:
1601 : case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
1602 : case ElementType::PEDAL_SEGMENT:
1603 : case ElementType::GLISSANDO_SEGMENT:
1604 : case ElementType::NOTELINE_SEGMENT:
1605 : case ElementType::LET_RING_SEGMENT:
1606 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
1607 : case ElementType::PALM_MUTE_SEGMENT:
1608 : case ElementType::WHAMMY_BAR_SEGMENT:
1609 : case ElementType::RASGUEADO_SEGMENT:
1610 : case ElementType::HARMONIC_MARK_SEGMENT:
1611 : case ElementType::PICK_SCRAPE_SEGMENT:
1612 : case ElementType::GUITAR_BEND_SEGMENT:
1613 : case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
1614 : {
1615 10 : el = toSpannerSegment(el)->spanner();
1616 10 : if (el->isTie()) {
1617 10 : Tie* tie = toTie(el);
1618 10 : if (tie->tieJumpPoints()) {
1619 6 : tie->undoRemoveTiesFromJumpPoints();
1620 : }
1621 10 : if (tie->jumpPoint()) {
1622 8 : tie->updateStartTieOnRemoval();
1623 : }
1624 : }
1625 10 : undoRemoveElement(el);
1626 :
1627 10 : if (el->isGuitarBend()) {
1628 0 : GuitarBend* bend = toGuitarBend(el);
1629 0 : if (bend->bendType() == GuitarBendType::PRE_BEND || bend->bendType() == GuitarBendType::PRE_DIVE) {
1630 0 : deleteItem(bend->startNote());
1631 : }
1632 : }
1633 : }
1634 10 : break;
1635 :
1636 0 : case ElementType::TAPPING_HALF_SLUR_SEGMENT:
1637 : {
1638 0 : TappingHalfSlur* halfSlur = toTappingHalfSlur(toTappingHalfSlurSegment(el)->spanner());
1639 0 : Tapping* tapping = toTapping(halfSlur->parent());
1640 0 : undoRemoveElement(tapping);
1641 0 : break;
1642 : }
1643 :
1644 0 : case ElementType::HAMMER_ON_PULL_OFF_TEXT:
1645 0 : undoRemoveHopoText(toHammerOnPullOffText(el));
1646 0 : break;
1647 :
1648 1 : case ElementType::STEM_SLASH: // cannot delete this elements
1649 : case ElementType::HOOK:
1650 : case ElementType::GUITAR_BEND_TEXT:
1651 : case ElementType::GUITAR_BEND_HOLD_SEGMENT:
1652 1 : LOGD("cannot remove %s", el->typeName());
1653 1 : break;
1654 :
1655 0 : case ElementType::TEXT:
1656 0 : if ((el->ownershipParent() && el->ownershipParent()->isTBox()) || el->isTBox()) {
1657 0 : el->undoChangeProperty(Pid::TEXT, String());
1658 : } else {
1659 0 : undoRemoveElement(el);
1660 : }
1661 0 : break;
1662 0 : case ElementType::PLAY_COUNT_TEXT: {
1663 0 : PlayCountText* pct = toPlayCountText(el);
1664 0 : pct->barline()->undoChangeProperty(Pid::PLAY_COUNT_TEXT_SETTING, AutoCustomHide::HIDE);
1665 0 : } break;
1666 1 : case ElementType::INSTRUMENT_CHANGE:
1667 : {
1668 1 : InstrumentChange* ic = static_cast<InstrumentChange*>(el);
1669 1 : Fraction tickStart = ic->segment()->tick();
1670 1 : Part* part = ic->part();
1671 1 : Interval oldV = part->staff(0)->transpose(tickStart);
1672 1 : undoRemoveElement(el);
1673 1 : if (tickStart != Fraction(0, 1)) {
1674 1 : for (KeySig* keySig : ic->keySigs()) {
1675 0 : deleteItem(keySig);
1676 1 : }
1677 : }
1678 1 : for (Clef* clef : ic->clefs()) {
1679 0 : deleteItem(clef);
1680 1 : }
1681 1 : if (part->staff(0)->transpose(tickStart) != oldV) {
1682 1 : auto i = part->instruments().upper_bound(tickStart.ticks());
1683 1 : Fraction tickEnd;
1684 1 : if (i == part->instruments().end()) {
1685 1 : tickEnd = Fraction(-1, 1);
1686 : } else {
1687 0 : tickEnd = Fraction::fromTicks(i->first);
1688 : }
1689 1 : Transpose::transpositionChanged(tx, this, part, oldV, tickStart, tickEnd);
1690 : }
1691 : }
1692 1 : break;
1693 0 : case ElementType::SYSTEM_LOCK_INDICATOR:
1694 : {
1695 0 : const RangeLock* systemLock = toSystemLockIndicator(el)->systemLock();
1696 0 : EditSystemLocks::undoRemoveSystemLock(tx, systemLock);
1697 : }
1698 0 : break;
1699 0 : case ElementType::PAGE_LOCK_INDICATOR:
1700 : {
1701 0 : const RangeLock* pageLock = toPageLockIndicator(el)->pageLock();
1702 0 : EditPageLocks::undoRemovePageLock(tx, pageLock);
1703 : }
1704 0 : break;
1705 0 : case ElementType::PARENTHESIS: {
1706 0 : Parenthesis* paren = toParenthesis(el);
1707 : // Use EditChord::removeChordParentheses when parent is a chord
1708 0 : if (el->parent() && el->parent()->isChord()) {
1709 0 : Chord* chord = toChord(el->parent());
1710 0 : NoteParenthesisInfo* parenInfo = chord->findNoteParenthesisInfo(paren);
1711 0 : IF_ASSERT_FAILED(parenInfo) {
1712 0 : LOGD() << "deleteItem: This parenthesis does not belong to this chord";
1713 0 : return;
1714 : }
1715 0 : for (Note* note : parenInfo->notes()) {
1716 0 : note->undoChangeProperty(Pid::HIDE_GENERATED_PARENTHESES, true);
1717 0 : note->undoChangeProperty(Pid::HAS_PARENTHESES, ParenthesesMode::NONE);
1718 : }
1719 0 : EditChord::removeChordParentheses(chord, parenInfo->notes());
1720 : } else {
1721 0 : undoRemoveElement(el);
1722 : }
1723 : }
1724 0 : break;
1725 23 : default:
1726 23 : undoRemoveElement(el);
1727 23 : break;
1728 : }
1729 : }
1730 :
1731 : //---------------------------------------------------------
1732 : // deleteMeasures
1733 : //---------------------------------------------------------
1734 :
1735 20 : void Score::deleteMeasures(MeasureBase* mbStart, MeasureBase* mbEnd, bool preserveTies)
1736 : {
1737 20 : select(0, SelectType::SINGLE, 0);
1738 :
1739 20 : if (mbEnd->isMeasure()) {
1740 20 : Measure* mbEndMeasure = toMeasure(mbEnd);
1741 20 : if (mbEndMeasure->isMMRest()) {
1742 0 : mbEnd = mbEndMeasure->mmRestLast();
1743 : }
1744 : }
1745 :
1746 20 : if (!preserveTies) {
1747 : // we're not preserving slurs and ties where they are, so the endpoints need to be either
1748 : // moved or the slur has to be deleted
1749 9 : reconnectSlurs(mbStart, mbEnd);
1750 : }
1751 :
1752 : // get the last deleted timesig & keysig in order to restore after deletion
1753 20 : std::map<staff_idx_t, TimeSig*> lastDeletedTimeSigs;
1754 :
1755 31 : for (MeasureBase* mb = mbEnd;; mb = mb->prev()) {
1756 31 : if (mb->isMeasure()) {
1757 31 : Measure* m = toMeasure(mb);
1758 31 : Segment* sts = m->findSegment(SegmentType::TimeSig, m->tick());
1759 :
1760 31 : if (sts) {
1761 18 : for (staff_idx_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
1762 9 : if (!muse::contains(lastDeletedTimeSigs, staffIdx)) {
1763 9 : if (TimeSig* ts = toTimeSig(sts->element(staffIdx * VOICES))) {
1764 9 : lastDeletedTimeSigs.insert({ staffIdx, ts });
1765 : }
1766 : }
1767 : }
1768 : }
1769 :
1770 31 : if (lastDeletedTimeSigs.size() == nstaves()) {
1771 9 : break;
1772 : }
1773 : }
1774 22 : if (mb == mbStart) {
1775 11 : break;
1776 : }
1777 11 : }
1778 :
1779 20 : std::vector<KeySigEvent> lastDeletedKeySigEvents;
1780 :
1781 40 : for (Staff* staff : score()->staves()) {
1782 20 : KeySigEvent kse = staff->keySigEvent(mbEnd->tick());
1783 20 : lastDeletedKeySigEvents.push_back(kse);
1784 20 : }
1785 :
1786 20 : Fraction startTick = mbStart->tick();
1787 20 : Fraction endTick = mbEnd->tick();
1788 :
1789 40 : for (Score* score : scoreList()) {
1790 20 : Measure* startMeasure = score->tick2measure(startTick);
1791 20 : Measure* endMeasure = score->tick2measure(endTick);
1792 :
1793 20 : score->undoRemoveMeasures(startMeasure, endMeasure, preserveTies);
1794 :
1795 : // adjust views
1796 20 : Measure* focusOn = startMeasure->prevMeasure() ? startMeasure->prevMeasure() : score->firstMeasure();
1797 20 : for (MuseScoreView* v : score->m_viewer) {
1798 0 : v->adjustCanvasPosition(focusOn);
1799 : }
1800 :
1801 : // insert correct timesig after deletion
1802 20 : Measure* mBeforeSel = startMeasure->prevMeasure();
1803 20 : Measure* mAfterSel = mBeforeSel ? mBeforeSel->nextMeasure() : score->firstMeasure();
1804 20 : if (mAfterSel) {
1805 14 : Segment* s = mAfterSel->findSegment(SegmentType::TimeSig, mAfterSel->tick());
1806 :
1807 28 : for (staff_idx_t staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
1808 14 : if (s && s->element(staffIdx * VOICES)) {
1809 0 : continue;
1810 : }
1811 :
1812 14 : TimeSig* lastDeletedForThisStaff = muse::value(lastDeletedTimeSigs, staffIdx, nullptr);
1813 14 : if (!lastDeletedForThisStaff) {
1814 9 : continue;
1815 : }
1816 :
1817 5 : if (mBeforeSel) {
1818 0 : const Staff* staff = this->staff(staffIdx);
1819 0 : if (!staff) {
1820 0 : continue;
1821 : }
1822 :
1823 0 : const TimeSig* timeSig = staff->timeSig(mBeforeSel->tick());
1824 0 : if (!timeSig || timeSig->sig() == lastDeletedForThisStaff->sig()) {
1825 0 : continue;
1826 : }
1827 : }
1828 :
1829 5 : if (!s) {
1830 5 : s = mAfterSel->undoGetSegment(SegmentType::TimeSig, mAfterSel->tick());
1831 : }
1832 :
1833 5 : TimeSig* nts = Factory::createTimeSig(s);
1834 5 : nts->setTrack(staffIdx * VOICES);
1835 5 : nts->setFrom(lastDeletedForThisStaff);
1836 5 : nts->setStretch(nts->sig() / mAfterSel->timesig());
1837 5 : score->undoAddElement(nts);
1838 : }
1839 : }
1840 :
1841 : // insert correct keysig if necessary
1842 20 : if (mAfterSel && !mBeforeSel) {
1843 5 : Segment* s = mAfterSel->undoGetSegment(SegmentType::KeySig, mAfterSel->tick());
1844 :
1845 5 : bool userCustomizedKeySig = false;
1846 25 : for (EngravingItem* e : s->elist()) {
1847 20 : if (e && e->isKeySig() && !toKeySig(e)->generated()) {
1848 0 : userCustomizedKeySig = true;
1849 0 : break;
1850 : }
1851 : }
1852 5 : if (userCustomizedKeySig) {
1853 0 : continue;
1854 : }
1855 :
1856 5 : bool concertPitch = score->style().styleB(Sid::concertPitch);
1857 10 : for (Staff* staff : score->staves()) {
1858 5 : Part* part = staff->part();
1859 5 : Instrument* instrument = part->instrument(Fraction(0, 1));
1860 5 : KeySigEvent nkse;
1861 5 : staff_idx_t staffIdx = staff->idx();
1862 :
1863 5 : if (instrument->useDrumset()) {
1864 0 : nkse.setConcertKey(Key::C);
1865 0 : nkse.setCustom(true);
1866 0 : nkse.setMode(KeyMode::NONE);
1867 : } else {
1868 5 : nkse = lastDeletedKeySigEvents.at(staffIdx);
1869 5 : if (!concertPitch && !nkse.isAtonal()) {
1870 5 : Interval v = instrument->transpose();
1871 5 : v.flip();
1872 5 : nkse.setKey(Transpose::transposeKey(nkse.concertKey(), v, part->preferSharpFlat()));
1873 : }
1874 : }
1875 :
1876 5 : KeySig* nks = (KeySig*)s->element(staff2track(staffIdx));
1877 5 : if (!nks) {
1878 5 : nks = Factory::createKeySig(s);
1879 5 : nks->setTrack(staffIdx * VOICES);
1880 5 : nks->setKeySigEvent(nkse);
1881 5 : score->undoAddElement(nks);
1882 : } else {
1883 0 : nks->setGenerated(false);
1884 0 : nks->setKeySigEvent(nkse);
1885 0 : staff->setKey(mAfterSel->tick(), nkse);
1886 : }
1887 5 : }
1888 : }
1889 20 : }
1890 :
1891 20 : undoInsertTime(mbStart->tick(), -(mbEnd->endTick() - mbStart->tick()));
1892 20 : m_is.setSegment(0); // invalidate position
1893 20 : }
1894 :
1895 9 : void Score::reconnectSlurs(MeasureBase* mbStart, MeasureBase* mbEnd)
1896 : {
1897 : // Reconnect or remove slurs that intersect with these deleted measures
1898 9 : Fraction sTick = mbStart->tick();
1899 9 : Fraction eTick = mbEnd->tick();
1900 10 : for (auto overlappingSlur : spannerMap().findOverlapping(sTick.ticks(), eTick.ticks())) {
1901 1 : Spanner* sp = overlappingSlur.value;
1902 1 : if (!sp->isSlur()) {
1903 1 : continue;
1904 : }
1905 0 : ChordRest* cr1 = sp->startCR();
1906 0 : ChordRest* cr2 = sp->endCR();
1907 0 : if (!cr1 || !cr2) {
1908 : // this is an invalid slur
1909 0 : continue;
1910 : }
1911 0 : MeasureBase* m = mbStart;
1912 0 : bool adjust1 = false;
1913 0 : bool adjust2 = false;
1914 0 : Measure* measure1 = cr1->measure();
1915 0 : Measure* measure2 = cr2->measure();
1916 : do {
1917 0 : adjust1 = adjust1 ? true : measure1 == m;
1918 0 : adjust2 = adjust2 ? true : measure2 == m;
1919 0 : if (m == mbEnd) {
1920 0 : break;
1921 : } else {
1922 0 : m = m->next();
1923 : }
1924 0 : } while (m && !(adjust1 && adjust2));
1925 0 : if (adjust1 && adjust2) {
1926 : // if both endpoints of this slur are inside this deleted range, remove the slur.
1927 0 : undoRemoveElement(sp);
1928 0 : continue;
1929 0 : } else if (adjust1) {
1930 : // endpoint 1 is in deleted range. move endpoint forward to first non-deleted CR after range
1931 0 : Measure* mNext = mbEnd->next() && mbEnd->next()->isMeasure() ? toMeasure(mbEnd->next()) : nullptr;
1932 0 : if (!mNext) {
1933 0 : undoRemoveElement(sp);
1934 0 : continue;
1935 : }
1936 0 : ChordRest* firstAvailableCr = nullptr;
1937 : // search for last cr in this voice
1938 0 : Segment* seg = mNext->first(SegmentType::ChordRest);
1939 0 : track_idx_t spTrack = sp->track();
1940 0 : ChordRest* spEndCr = sp->endCR();
1941 0 : Fraction spEndCrTick = spEndCr->tick();
1942 0 : while (seg) {
1943 0 : firstAvailableCr = seg->cr(spTrack);
1944 0 : if (!firstAvailableCr || !firstAvailableCr->isChord()) {
1945 0 : seg = seg->next(SegmentType::ChordRest);
1946 0 : continue;
1947 : }
1948 0 : auto gracesBefore = toChord(firstAvailableCr)->graceNotesBefore();
1949 0 : if (firstAvailableCr->tick() > spEndCrTick) {
1950 : // we would create a negative-length slur. remove it entirely.
1951 0 : undoRemoveElement(sp);
1952 0 : break;
1953 0 : } else if (spEndCr == firstAvailableCr) {
1954 : // this would be a zero-length slur, but a possibility exists that this cr has grace after
1955 0 : if (gracesBefore.empty()) {
1956 0 : undoRemoveElement(sp);
1957 0 : break;
1958 : }
1959 : }
1960 0 : if (!gracesBefore.empty()) {
1961 0 : firstAvailableCr = gracesBefore.front();
1962 : }
1963 0 : Spanner* newSp = toSpanner(sp->clone());
1964 0 : newSp->setStartElement(firstAvailableCr);
1965 0 : newSp->setTick(firstAvailableCr->tick());
1966 0 : undoChangeElement(sp, newSp);
1967 0 : break;
1968 0 : }
1969 0 : } else if (adjust2) {
1970 : // endpoint 2 is in deleted range. move endpoint backward to last non-deleted CR before range
1971 0 : Measure* mPrev = mbStart->prev() && mbStart->prev()->isMeasure() ? toMeasure(mbStart->prev()) : nullptr;
1972 0 : if (!mPrev) {
1973 0 : undoRemoveElement(sp);
1974 0 : continue;
1975 : }
1976 0 : ChordRest* firstAvailableCr = nullptr;
1977 : // search for last cr in this voice
1978 0 : Segment* seg = mPrev->last();
1979 0 : if (seg->segmentType() != SegmentType::ChordRest) {
1980 0 : seg = seg->prev(SegmentType::ChordRest);
1981 : }
1982 0 : ChordRest* spStartCr = sp->startCR();
1983 0 : Fraction spStartCrTick = spStartCr->tick();
1984 0 : while (seg) {
1985 0 : firstAvailableCr = seg->cr(sp->track2());
1986 0 : if (!firstAvailableCr || !firstAvailableCr->isChord()) {
1987 0 : seg = seg->prev(SegmentType::ChordRest);
1988 0 : continue;
1989 : }
1990 0 : auto gracesAfter = toChord(firstAvailableCr)->graceNotesAfter();
1991 0 : if (firstAvailableCr->tick() < spStartCrTick) {
1992 : // we would create a negative-length slur. remove it entirely.
1993 0 : undoRemoveElement(sp);
1994 0 : break;
1995 0 : } else if (spStartCr == toChordRest(firstAvailableCr)) {
1996 : // this would be a zero-length slur, but a possibility exists that this cr has grace after
1997 0 : if (gracesAfter.empty()) {
1998 0 : undoRemoveElement(sp);
1999 0 : break;
2000 : }
2001 : }
2002 0 : if (!gracesAfter.empty()) {
2003 0 : firstAvailableCr = gracesAfter.back();
2004 : }
2005 0 : Spanner* newSp = toSpanner(sp->clone());
2006 0 : newSp->setEndElement(firstAvailableCr);
2007 0 : newSp->setTick2(firstAvailableCr->tick());
2008 0 : undoChangeElement(sp, newSp);
2009 0 : break;
2010 0 : }
2011 : } else {
2012 : // both of the endpoints are outside of the deleted range. there is no reason to change or delete anything
2013 : }
2014 : }
2015 9 : }
2016 :
2017 : //---------------------------------------------------------
2018 : // deleteSpannersFromRange
2019 : /// Deletes spanners in the given range that match the
2020 : /// given selection filter.
2021 : //---------------------------------------------------------
2022 :
2023 61 : void Score::deleteOrShortenOutSpannersFromRange(const Fraction& t1, const Fraction& t2, track_idx_t track1, track_idx_t track2,
2024 : const SelectionFilter& filter)
2025 : {
2026 : static const std::set<ElementType> SPANNER_TYPES_TO_SHORTEN_OUT {
2027 : ElementType::HAIRPIN,
2028 : ElementType::OTTAVA,
2029 : ElementType::TRILL,
2030 : ElementType::VIBRATO,
2031 63 : };
2032 :
2033 : // undoRemoveElement and undoChangeProperty also handle linked elements, so we need
2034 : // to make sure each linked group is handled only once if multiple elements per linked
2035 : // group are found in the range, for example in the case of linked staves.
2036 61 : std::unordered_set<EngravingObject*> handledMainElements;
2037 :
2038 61 : auto spanners = m_spanner.findOverlapping(t1.ticks(), t2.ticks() - 1);
2039 94 : for (auto i : spanners) {
2040 33 : Spanner* sp = i.value;
2041 33 : Fraction spStartTick = sp->tick();
2042 33 : Fraction spEndTick = sp->tick2();
2043 33 : const bool trackValid = sp->track() >= track1 && sp->track() < track2;
2044 33 : if (!trackValid || sp->isVolta() || sp->systemFlag()) {
2045 15 : continue;
2046 : }
2047 :
2048 31 : if (sp->links() && !handledMainElements.insert(sp->links()->mainElement()).second) {
2049 : // already handled this linked group
2050 4 : continue;
2051 : }
2052 :
2053 : // Special handling for grace notes...
2054 27 : const Chord* startChord = sp->startChord();
2055 27 : const bool startChordIsSelectableGrace = startChord && startChord->isGrace() && filter.canSelect(startChord);
2056 :
2057 27 : const Chord* endChord = sp->endChord();
2058 27 : const bool endChordIsSelectableGrace = endChord && endChord->isGrace() && filter.canSelect(endChord);
2059 :
2060 27 : if (startChordIsSelectableGrace || endChordIsSelectableGrace) {
2061 6 : undoRemoveElement(sp);
2062 6 : continue;
2063 : }
2064 :
2065 21 : if (!filter.canSelectVoice(sp->track()) || !filter.canSelect(sp)) {
2066 3 : continue;
2067 : }
2068 29 : if (spStartTick >= t1 && spStartTick < t2
2069 29 : && spEndTick > t1 && spEndTick <= t2) {
2070 7 : undoRemoveElement(sp);
2071 14 : } else if (sp->isSlur() && ((spStartTick >= t1 && spStartTick < t2)
2072 3 : || (spEndTick >= t1 && spEndTick < t2))) {
2073 3 : undoRemoveElement(sp);
2074 8 : } else if (muse::contains(SPANNER_TYPES_TO_SHORTEN_OUT, sp->type())) {
2075 7 : bool moveStart = spStartTick >= t1 && spStartTick < t2;
2076 7 : bool moveEnd = spEndTick > t1 && spEndTick <= t2;
2077 7 : if (moveStart) {
2078 3 : Fraction tickDiff = t2 - spStartTick;
2079 3 : sp->undoChangeProperty(Pid::SPANNER_TICK, t2);
2080 3 : sp->undoChangeProperty(Pid::SPANNER_TICKS, sp->ticks() - tickDiff);
2081 4 : } else if (moveEnd) {
2082 3 : Fraction tickDiff = spEndTick - t1;
2083 3 : sp->undoChangeProperty(Pid::SPANNER_TICKS, sp->ticks() - tickDiff);
2084 : }
2085 : }
2086 : }
2087 61 : }
2088 :
2089 1113 : void Score::deleteSlursFromRange(const Fraction& t1, const Fraction& t2, track_idx_t trackStart, track_idx_t trackEnd,
2090 : const SelectionFilter& filter)
2091 : {
2092 1113 : auto spanners = m_spanner.findOverlapping(t1.ticks(), t2.ticks() - 1);
2093 1113 : for (auto i : spanners) {
2094 0 : Spanner* sp = i.value;
2095 0 : Fraction spStartTick = sp->tick();
2096 0 : Fraction spEndTick = sp->tick2();
2097 0 : if (!sp->isSlur()) {
2098 0 : continue;
2099 : }
2100 0 : if (!filter.canSelectVoice(sp->track()) || !filter.canSelect(sp)) {
2101 0 : continue;
2102 : }
2103 :
2104 0 : if (sp->track() >= trackStart && sp->track() < trackEnd) {
2105 0 : if ((spStartTick >= t1 && spStartTick < t2)
2106 0 : || (spEndTick >= t1 && spEndTick < t2)) {
2107 0 : undoRemoveElement(sp);
2108 : }
2109 : }
2110 : }
2111 1113 : }
2112 :
2113 : //---------------------------------------------------------
2114 : // deleteAnnotationsFromRange
2115 : /// Deletes annotations in the given range that match the
2116 : /// given selection filter.
2117 : //---------------------------------------------------------
2118 :
2119 61 : void Score::deleteAnnotationsFromRange(Segment* s1, Segment* s2, track_idx_t track1, track_idx_t track2, const SelectionFilter& filter)
2120 : {
2121 61 : if (!s1) {
2122 0 : return;
2123 : }
2124 61 : if (s2 && (*s2) < (*s1)) {
2125 0 : return;
2126 : }
2127 :
2128 321 : for (track_idx_t track = track1; track < track2; ++track) {
2129 260 : if (!filter.canSelectVoice(track)) {
2130 2 : continue;
2131 : }
2132 2882 : for (Segment* s = s1; s && s != s2; s = s->next1()) {
2133 2624 : const auto annotations = s->annotations(); // make a copy since we alter the list
2134 2792 : for (EngravingItem* annotation : annotations) {
2135 : // skip if not included in selection (eg, filter)
2136 168 : if (!filter.canSelect(annotation)) {
2137 152 : continue;
2138 : }
2139 16 : if (!annotation->systemFlag() && annotation->track() == track) {
2140 4 : deleteItem(annotation);
2141 : }
2142 : }
2143 2624 : }
2144 : }
2145 : }
2146 :
2147 340 : void Score::deleteRangeAtTrack(std::vector<ChordRest*>& crsToSelect, const track_idx_t track, Segment* startSeg, const Fraction& endTick,
2148 : Tuplet* currentTuplet, const SelectionFilter& filter, bool selectionContainsMultiNoteChords)
2149 : {
2150 11919 : while (startSeg && !(startSeg->isChordRestType() && startSeg->cr(track))) {
2151 : // Range should always start at a ChordRest segment - find the next one for this track...
2152 11579 : startSeg = startSeg->next1(SegmentType::ChordRest);
2153 : }
2154 :
2155 340 : if (!startSeg) {
2156 186 : return;
2157 : }
2158 :
2159 154 : Fraction restStartTick = startSeg->tick();
2160 154 : Fraction restDuration;
2161 :
2162 : // When we find a deselected DurationElement - write rests up to the start of the deselected element (using the existing values for
2163 : // restStartTick and restDuration), skip the deselected element (move restStartTick forward), and reset restDuration...
2164 238 : const auto foundDeselected = [&](const DurationElement* deselectedElement) {
2165 238 : const std::vector<Rest*> rests = setRests(restStartTick, track, restDuration, /*useDots*/ !currentTuplet, currentTuplet);
2166 238 : crsToSelect.insert(crsToSelect.end(), rests.begin(), rests.end());
2167 238 : restStartTick = deselectedElement->endTick();
2168 238 : restDuration = Fraction();
2169 238 : };
2170 :
2171 1101 : for (Segment* s = startSeg; s && (s->tick() < endTick); s = s->next1()) {
2172 947 : EngravingItem* e = s->element(track);
2173 947 : if (!e) {
2174 490 : continue;
2175 : }
2176 :
2177 833 : if (s->isBreathType()) {
2178 0 : deleteItem(e);
2179 0 : continue;
2180 : }
2181 :
2182 833 : if (!s->isChordRestType()) {
2183 : // do not delete TimeSig/KeySig, it doesn't make sense to do it (except on full system)
2184 222 : if (!s->isTimeTickType() && !s->isTimeSigType() && !s->isKeySigType()
2185 222 : && !s->isType(SegmentType::BarLineTypes) /*covers all barLine types*/) {
2186 9 : undoRemoveElement(e);
2187 : }
2188 111 : continue;
2189 : }
2190 :
2191 722 : if (e->isMeasureRepeat()) {
2192 : // MeasureRepeat has its own special handling, so it is skipped here
2193 0 : foundDeselected(toDurationElement(e));
2194 0 : deleteItem(e);
2195 0 : continue;
2196 : }
2197 :
2198 722 : ChordRest* cr1 = toChordRest(e);
2199 :
2200 : // currentTuplet refers to the tuplet that we're currently performing a delete range "inside". It is possible that currentTuplet contains nested
2201 : // tuplets, in which case we should find the "top-most nested tuplet" (nextTuplet) and:
2202 : // 1. If it is selectable, delete it completely using cmdDeleteTuplet (deletes all elements contained in that tuplet)
2203 : // 2. If it is not selectable, call this method recursively "inside" that tuplet (nextTuplet becomes currentTuplet for the recursive call)
2204 :
2205 722 : Tuplet* nextTuplet = nullptr;
2206 722 : if (cr1->tuplet() && cr1->tuplet() != currentTuplet) {
2207 102 : nextTuplet = cr1->tuplet();
2208 150 : while (nextTuplet->tuplet() && nextTuplet->tuplet() != currentTuplet) {
2209 48 : nextTuplet = nextTuplet->tuplet();
2210 : }
2211 : }
2212 :
2213 722 : if (nextTuplet) {
2214 : // The range of nextTuplet will be handled by cmdDeleteTuplet or the recursive call - skip it at this level of recursion...
2215 102 : const DurationElement* lastInTuplet = nextTuplet->elements().back();
2216 123 : while (lastInTuplet->isTuplet()) {
2217 21 : lastInTuplet = toTuplet(lastInTuplet)->elements().back();
2218 : }
2219 102 : IF_ASSERT_FAILED(lastInTuplet->isChordRest()) {
2220 0 : break;
2221 : }
2222 102 : s = toChordRest(lastInTuplet)->segment();
2223 :
2224 102 : if (filter.canSelectTuplet(nextTuplet, cr1->tick(), endTick, selectionContainsMultiNoteChords)) {
2225 20 : restDuration += nextTuplet->ticks();
2226 20 : cmdDeleteTuplet(nextTuplet, /*replaceWithRest*/ false);
2227 : } else {
2228 82 : const Fraction recursionEnd = std::min(nextTuplet->endTick(), endTick);
2229 82 : deleteRangeAtTrack(crsToSelect, track, cr1->segment(), recursionEnd, nextTuplet, filter, selectionContainsMultiNoteChords);
2230 82 : foundDeselected(nextTuplet);
2231 : }
2232 :
2233 102 : if (!s) {
2234 0 : break;
2235 : }
2236 :
2237 102 : continue;
2238 102 : }
2239 :
2240 636 : for (EngravingItem* elem : collectElementsAnchoredToChordRest(cr1)) {
2241 16 : if (!elem->isChord()) {
2242 0 : if (filter.canSelect(elem)) {
2243 0 : undoRemoveElement(elem);
2244 : }
2245 0 : continue;
2246 : }
2247 : // Special handling for grace notes...
2248 16 : const Chord* chord = toChord(elem);
2249 16 : IF_ASSERT_FAILED(chord->isGrace()) {
2250 0 : continue;
2251 : }
2252 16 : std::unordered_set<EngravingItem*> anchoredToGrace = collectElementsAnchoredToChordRest(chord);
2253 32 : for (const Note* graceNote : chord->notes()) {
2254 16 : const std::unordered_set<EngravingItem*> noteAnchored = collectElementsAnchoredToNote(graceNote, true, false);
2255 16 : anchoredToGrace.insert(noteAnchored.begin(), noteAnchored.end());
2256 16 : }
2257 16 : for (EngravingItem* graceElem : anchoredToGrace) {
2258 0 : if (filter.canSelect(graceElem)) {
2259 : // Delete elements anchored to grace note...
2260 0 : undoRemoveElement(graceElem);
2261 : }
2262 : }
2263 16 : if (filter.canSelect(elem)) {
2264 : // Delete grace note itself...
2265 8 : undoRemoveElement(elem);
2266 : }
2267 636 : }
2268 :
2269 620 : if (cr1->isRestFamily()) {
2270 163 : if (cr1->selected()) {
2271 163 : restDuration += cr1->ticks();
2272 163 : removeChordRest(cr1, true);
2273 : } else {
2274 0 : foundDeselected(cr1);
2275 : }
2276 163 : continue;
2277 : }
2278 :
2279 457 : Chord* chord = toChord(cr1);
2280 :
2281 457 : const std::vector<Note*> allNotes = chord->notes();
2282 457 : std::unordered_set<Note*> notesToRemove;
2283 1213 : for (size_t noteIdx = 0; noteIdx < allNotes.size(); ++noteIdx) {
2284 756 : Note* note = allNotes.at(noteIdx);
2285 756 : if (filter.canSelectNoteIdx(noteIdx, allNotes.size(), selectionContainsMultiNoteChords)) {
2286 534 : notesToRemove.emplace(note);
2287 : //! NOTE: Don't need to remove anchored elements - they won't survive the deletion of their parent note...
2288 756 : continue;
2289 : }
2290 222 : for (EngravingItem* elem : collectElementsAnchoredToNote(note, true, false)) {
2291 0 : if (!filter.canSelect(elem)) {
2292 0 : continue;
2293 : }
2294 0 : if (!elem->isSpanner() || elem->isPartialTie() || elem->isLaissezVib()) {
2295 0 : undoRemoveElement(elem);
2296 0 : continue;
2297 : }
2298 0 : if (noteAnchoredSpannerIsInRange(toSpanner(elem), cr1->tick(), endTick)) {
2299 0 : undoRemoveElement(elem);
2300 : }
2301 222 : }
2302 222 : if (!filter.canSelectNoteIdx(noteIdx, allNotes.size(), selectionContainsMultiNoteChords)) {
2303 222 : continue;
2304 : }
2305 0 : notesToRemove.emplace(note);
2306 : }
2307 :
2308 457 : if (notesToRemove.size() == allNotes.size()) {
2309 301 : restDuration += cr1->ticks();
2310 301 : removeChordRest(cr1, true);
2311 : } else {
2312 314 : for (Note* note : notesToRemove) {
2313 158 : undoRemoveElement(note);
2314 : }
2315 156 : foundDeselected(cr1);
2316 : }
2317 457 : }
2318 :
2319 154 : const std::vector<Rest*> rests = setRests(restStartTick, track, restDuration, /*useDots*/ !currentTuplet, currentTuplet);
2320 154 : crsToSelect.insert(crsToSelect.end(), rests.begin(), rests.end());
2321 154 : }
2322 :
2323 : //---------------------------------------------------------
2324 : // deleteRange
2325 : /// Deletes elements in the given range that match the
2326 : /// given selection filter.
2327 : /// \return A chord/rest inside the selected range
2328 : /// that can be used to establish a selection after this
2329 : /// deletion operation.
2330 : //---------------------------------------------------------
2331 :
2332 61 : std::vector<ChordRest*> Score::deleteRange(Segment* s1, Segment* s2, track_idx_t track1, track_idx_t track2, const SelectionFilter& filter,
2333 : bool selectionContainsMultiNoteChords)
2334 : {
2335 61 : std::vector<ChordRest*> crsForSelection;
2336 61 : IF_ASSERT_FAILED(s1) {
2337 0 : return crsForSelection;
2338 : }
2339 :
2340 : // delete content from measures underlying mmrests
2341 61 : if (s1->measure() && s1->measure()->isMMRest()) {
2342 0 : s1 = s1->measure()->mmRestFirst()->first();
2343 : }
2344 61 : if (s2 && s2->measure() && s2->measure()->isMMRest()) {
2345 0 : s2 = s2->measure()->mmRestLast()->last();
2346 : }
2347 :
2348 61 : const Fraction startTick = s1->tick();
2349 61 : const Fraction endTick = s2 ? s2->tick() : lastMeasure()->endTick();
2350 :
2351 61 : deleteOrShortenOutSpannersFromRange(startTick, endTick, track1, track2, filter);
2352 61 : deleteAnnotationsFromRange(s1, s2, track1, track2, filter);
2353 :
2354 321 : for (track_idx_t track = track1; track < track2; ++track) {
2355 260 : if (!filter.canSelectVoice(track)) {
2356 2 : continue;
2357 : }
2358 258 : deleteRangeAtTrack(crsForSelection, track, s1, endTick, /*currentTuplet*/ nullptr, filter, selectionContainsMultiNoteChords);
2359 : }
2360 :
2361 61 : return crsForSelection;
2362 0 : }
2363 :
2364 : //---------------------------------------------------------
2365 : // cmdDeleteSelection
2366 : //---------------------------------------------------------
2367 :
2368 94 : void Score::cmdDeleteSelection()
2369 : {
2370 94 : std::vector<ChordRest*> crsSelectedAfterDeletion; // select something after deleting notes
2371 94 : EngravingItem* elSelectedAfterDeletion = nullptr;
2372 :
2373 94 : if (selection().isRange()) {
2374 183 : crsSelectedAfterDeletion = deleteRange(selection().startSegment(), selection().endSegment(),
2375 122 : staff2track(selection().staffStart()), staff2track(selection().staffEnd()),
2376 122 : selectionFilter(), selection().rangeContainsMultiNoteChords());
2377 : } else {
2378 : // deleteItem modifies selection().elements() list,
2379 : // so we need a local copy:
2380 33 : std::vector<EngravingItem*> el = selection().elements();
2381 :
2382 : // keep track of linked elements that are deleted implicitly
2383 : // so we don't try to delete them twice if they are also in selection
2384 33 : std::set<EngravingObject*> deletedElements;
2385 : // Similarly, deleting one spanner segment, will delete all of them
2386 : // so we don't try to delete them twice if they are also in selection
2387 33 : std::set<Spanner*> deletedSpanners;
2388 :
2389 45 : auto selectCRAtTickAndTrack = [this, &crsSelectedAfterDeletion](Fraction tick, track_idx_t track) {
2390 45 : ChordRest* cr = findCR(tick, track);
2391 45 : if (cr) {
2392 42 : crsSelectedAfterDeletion.push_back(cr);
2393 : }
2394 45 : };
2395 :
2396 78 : for (EngravingItem* e : el) {
2397 : // these are the linked elements we are about to delete
2398 45 : std::list<EngravingObject*> links;
2399 45 : if (e->links()) {
2400 24 : links = *e->links();
2401 : }
2402 :
2403 : // find location of element to select after deleting notes
2404 : // get tick of element itself if that is valid
2405 : // or of spanner or parent if that is more valid
2406 45 : Fraction tick = { -1, 1 };
2407 45 : track_idx_t track = muse::nidx;
2408 45 : if (e->isNote()) {
2409 4 : tick = toNote(e)->chord()->tick();
2410 41 : } else if (e->isRest() || e->isMMRest()) {
2411 3 : tick = toRest(e)->tick();
2412 38 : } else if (e->isMeasureRepeat()) { // may be attached in different measure than it appears
2413 3 : tick = toMeasureRepeat(e)->firstMeasureOfGroup()->first()->tick();
2414 35 : } else if (e->isSpannerSegment()) {
2415 0 : tick = toSpannerSegment(e)->spanner()->tick();
2416 35 : } else if (e->isBreath()) {
2417 : // we want the tick of the ChordRest that precedes the breath mark (in the same track)
2418 5 : for (Segment* s = toBreath(e)->segment()->prev(); s; s = s->prev()) {
2419 5 : if (s->isChordRestType() && s->element(e->track())) {
2420 5 : tick = s->tick();
2421 5 : break;
2422 : }
2423 : }
2424 30 : } else if (e->isBarLine() && toBarLine(e)->barLineType() != BarLineType::START_REPEAT) {
2425 : // we want the tick of the ChordRest that precedes the barline (in the same track)
2426 0 : for (Segment* s = toBarLine(e)->segment()->prev(); s; s = s->prev()) {
2427 0 : if (s->isChordRestType() && s->element(e->track())) {
2428 0 : tick = s->tick();
2429 0 : break;
2430 : }
2431 : }
2432 30 : } else if (e->isSoundFlag()) {
2433 0 : tick = e->tick();
2434 30 : } else if (e->isTuplet()) {
2435 : // the rests replacing the tuplet start at its tick
2436 2 : tick = toTuplet(e)->tick();
2437 28 : } else if (e->ownershipParent()
2438 30 : && (e->ownershipParent()->isSegment() || e->ownershipParent()->isChord() || e->ownershipParent()->isNote()
2439 2 : || e->ownershipParent()->isRest())) {
2440 25 : tick = e->ownershipParentItem()->tick();
2441 : }
2442 : //else tick < 0
2443 45 : track = e->track();
2444 :
2445 : // We should not allow deleting the very first keySig of the piece, because it is
2446 : // logically incorrect and leads to a state of undefined key/transposition.
2447 : // Also instrument change key signatures should be undeletable.
2448 : // The correct action is for the user to set an atonal/custom keySig as needed.
2449 45 : if (e->isKeySig()) {
2450 2 : if (e->tick() == Fraction(0, 1) || toKeySig(e)->forInstrumentChange()) {
2451 0 : MScore::setError(MsError::CANNOT_REMOVE_KEY_SIG);
2452 0 : selectCRAtTickAndTrack(tick, track);
2453 0 : continue;
2454 : }
2455 : }
2456 :
2457 : // Don't allow deleting the trill cue note
2458 45 : if (e->isNote() && toNote(e)->isTrillCueNote()) {
2459 0 : selectCRAtTickAndTrack(tick, track);
2460 0 : continue;
2461 : }
2462 :
2463 45 : if (e->isFretDiagram() || e->isHarmony()) {
2464 0 : FBox* fbox = toFBox(e->findAncestor(ElementType::FBOX));
2465 0 : if (fbox) {
2466 : // We can't delete elements inside fret box, instead we hide them
2467 0 : if (FretDiagram* fretDiagram = e->isFretDiagram() ? toFretDiagram(e) : toFretDiagram(e->parent())) {
2468 0 : fretDiagram->undoChangeProperty(Pid::VISIBLE, false);
2469 0 : continue;
2470 : }
2471 0 : } else if (e->isFretDiagram()) {
2472 0 : FretDiagram* fretDiagram = toFretDiagram(e);
2473 0 : Harmony* harmony = fretDiagram->harmony();
2474 0 : if (harmony) {
2475 0 : undoChangeParent(harmony, fretDiagram->segment(), track2staff(fretDiagram->track()));
2476 0 : elSelectedAfterDeletion = harmony;
2477 : }
2478 0 : } else if (e->isHarmony()) {
2479 0 : Harmony* harmony = toHarmony(e);
2480 0 : if (harmony->ownershipParentItem()->isFretDiagram()) {
2481 0 : elSelectedAfterDeletion = harmony->ownershipParentItem();
2482 : }
2483 : }
2484 : }
2485 :
2486 : // delete element if we have not done so already
2487 45 : if (deletedElements.find(e) == deletedElements.end()) {
2488 : // do not delete two spanner segments from the same spanner
2489 45 : if (e->isSpannerSegment()) {
2490 0 : Spanner* spanner = toSpannerSegment(e)->spanner();
2491 0 : if (deletedSpanners.find(spanner) != deletedSpanners.end()) {
2492 0 : continue;
2493 : } else {
2494 0 : std::list<EngravingObject*> linkedSpanners;
2495 0 : if (spanner->links()) {
2496 0 : linkedSpanners = *spanner->links();
2497 : } else {
2498 0 : linkedSpanners.push_back(spanner);
2499 : }
2500 0 : for (EngravingObject* se : linkedSpanners) {
2501 0 : deletedSpanners.insert(toSpanner(se));
2502 : }
2503 0 : }
2504 : }
2505 45 : deleteItem(e);
2506 : }
2507 :
2508 45 : if (!elSelectedAfterDeletion) {
2509 45 : selectCRAtTickAndTrack(tick, track);
2510 : }
2511 :
2512 : // add these linked elements to list of already-deleted elements
2513 93 : for (EngravingObject* se : links) {
2514 48 : deletedElements.insert(se);
2515 : }
2516 45 : }
2517 33 : }
2518 :
2519 94 : deselectAll();
2520 : // make new selection if appropriate
2521 94 : if (noteEntryMode()) {
2522 1 : if (!crsSelectedAfterDeletion.empty()) {
2523 1 : m_is.setSegment(crsSelectedAfterDeletion.front()->segment());
2524 : } else {
2525 0 : crsSelectedAfterDeletion.push_back(m_is.cr());
2526 : }
2527 : }
2528 94 : if (elSelectedAfterDeletion) {
2529 0 : select(elSelectedAfterDeletion);
2530 94 : } else if (!crsSelectedAfterDeletion.empty()) {
2531 86 : std::vector<EngravingItem*> elementsToSelect;
2532 388 : for (ChordRest* cr : crsSelectedAfterDeletion) {
2533 302 : if (cr) {
2534 302 : if (cr->isChord()) {
2535 21 : elementsToSelect.push_back(dynamic_cast<EngravingItem*>(toChord(cr)->upNote()));
2536 : }
2537 302 : if (cr->isRest()) {
2538 280 : elementsToSelect.push_back(dynamic_cast<EngravingItem*>(cr));
2539 : }
2540 : }
2541 : }
2542 86 : select(elementsToSelect, SelectType::ADD, 0);
2543 86 : }
2544 94 : }
2545 :
2546 : //---------------------------------------------------------
2547 : // cmdFullMeasureRest
2548 : //---------------------------------------------------------
2549 :
2550 0 : void Score::cmdFullMeasureRest()
2551 : {
2552 0 : Segment* s1 = nullptr;
2553 0 : Segment* s2 = nullptr;
2554 0 : Fraction stick1 = { -1, 1 };
2555 0 : Fraction stick2 = { -1, 1 };
2556 0 : track_idx_t track1 = muse::nidx;
2557 0 : track_idx_t track2 = muse::nidx;
2558 0 : Rest* r = nullptr;
2559 :
2560 0 : if (noteEntryMode()) {
2561 0 : s1 = inputState().segment();
2562 0 : if (!s1 || s1->rtick().isNotZero()) {
2563 0 : return;
2564 : }
2565 0 : Measure* m = s1->measure();
2566 0 : s2 = m->last();
2567 0 : stick1 = s1->tick();
2568 0 : stick2 = s2->tick();
2569 0 : track1 = inputState().track();
2570 0 : track2 = track1 + 1;
2571 0 : } else if (selection().isRange()) {
2572 0 : s1 = selection().startSegment();
2573 0 : s2 = selection().endSegment();
2574 0 : if (style().styleB(Sid::createMultiMeasureRests)) {
2575 : // use underlying measures
2576 0 : if (s1 && s1->measure()->isMMRest()) {
2577 0 : s1 = tick2segment(stick1);
2578 : }
2579 0 : if (s2 && s2->measure()->isMMRest()) {
2580 0 : s2 = tick2segment(stick2, true);
2581 : }
2582 : }
2583 0 : stick1 = selection().tickStart();
2584 0 : stick2 = selection().tickEnd();
2585 0 : Segment* ss1 = s1;
2586 0 : if (ss1 && ss1->segmentType() != SegmentType::ChordRest) {
2587 0 : ss1 = ss1->next1(SegmentType::ChordRest);
2588 : }
2589 0 : bool fullMeasure = ss1 && (ss1->measure()->first(SegmentType::ChordRest) == ss1)
2590 0 : && (s2 == 0 || (s2->segmentType() == SegmentType::EndBarLine)
2591 0 : || (s2->segmentType() == SegmentType::TimeSigAnnounce)
2592 0 : || (s2->segmentType() == SegmentType::KeySigAnnounce));
2593 0 : if (!fullMeasure) {
2594 0 : return;
2595 : }
2596 0 : track1 = selection().staffStart() * VOICES;
2597 0 : track2 = selection().staffEnd() * VOICES;
2598 0 : } else if (selection().cr()) {
2599 0 : ChordRest* cr = selection().cr();
2600 0 : if (!cr || cr->rtick().isNotZero()) {
2601 0 : return;
2602 : }
2603 0 : Measure* m = cr->measure();
2604 0 : s1 = m->first();
2605 0 : s2 = m->last();
2606 0 : stick1 = s1->tick();
2607 0 : stick2 = s2->tick();
2608 0 : track1 = selection().cr()->track();
2609 0 : track2 = track1 + 1;
2610 : } else {
2611 0 : return;
2612 : }
2613 :
2614 0 : for (track_idx_t track = track1; track < track2; ++track) {
2615 0 : if (selection().isRange() && !selectionFilter().canSelectVoice(track)) {
2616 0 : continue;
2617 : }
2618 : // first pass - remove non-initial rests from empty measures/voices
2619 0 : for (Segment* s = s1; s != s2; s = s->next1()) {
2620 0 : if (!(s->measure()->isOnlyRests(track))) { // Don't remove anything from measures that contain notes
2621 0 : continue;
2622 : }
2623 0 : if (s->segmentType() != SegmentType::ChordRest || !s->element(track)) {
2624 0 : continue;
2625 : }
2626 0 : ChordRest* cr = toChordRest(s->element(track));
2627 : // keep first rest of measure as placeholder (replaced in second pass)
2628 : // but delete all others
2629 0 : if (s->rtick().isNotZero()) {
2630 0 : removeChordRest(cr, true);
2631 : }
2632 : }
2633 : // second pass - replace placeholders with full measure rests
2634 0 : for (Measure* m = s1->measure(); m; m = m->nextMeasure()) {
2635 0 : if (m->isOnlyRests(track)) {
2636 0 : ChordRest* cr = m->findChordRest(m->tick(), track);
2637 0 : if (cr) {
2638 0 : removeChordRest(cr, true);
2639 0 : r = addRest(m->tick(), track, TDuration(DurationType::V_MEASURE), 0);
2640 0 : } else if (noteEntryMode()) {
2641 : // might be no cr at input position
2642 0 : r = addRest(m->tick(), track, TDuration(DurationType::V_MEASURE), 0);
2643 : }
2644 : }
2645 0 : if (s2 && (m == s2->measure())) {
2646 0 : break;
2647 : }
2648 : }
2649 : }
2650 :
2651 : // selected range is probably empty now and possibly subsumed by an mmrest
2652 : // so updating selection requires forcing mmrests to be updated first
2653 0 : s1 = tick2segmentMM(stick1);
2654 0 : s2 = tick2segmentMM(stick2, true);
2655 0 : if (selection().isRange() && s1 && s2) {
2656 0 : m_selection.setStartSegment(s1);
2657 0 : m_selection.setEndSegment(s2);
2658 0 : m_selection.updateSelectedElements();
2659 0 : } else if (r) {
2660 : // note entry mode
2661 0 : select(r, SelectType::SINGLE);
2662 : } else {
2663 0 : deselectAll();
2664 : }
2665 : }
2666 :
2667 : //---------------------------------------------------------
2668 : // cmdCreateTuplet
2669 : // replace cr with tuplet
2670 : //---------------------------------------------------------
2671 :
2672 54 : void Score::cmdCreateTuplet(ChordRest* ocr, Tuplet* tuplet)
2673 : {
2674 54 : track_idx_t track = ocr->track();
2675 54 : Measure* measure = ocr->measure();
2676 54 : Fraction tick = ocr->tick();
2677 54 : Fraction an = (tuplet->ticks() * tuplet->ratio()) / tuplet->baseLen().fraction();
2678 54 : if (!an.denominator()) {
2679 0 : return;
2680 : }
2681 :
2682 54 : if (ocr->tuplet()) {
2683 6 : tuplet->setTuplet(ocr->tuplet());
2684 : }
2685 54 : removeChordRest(ocr, false);
2686 :
2687 : ChordRest* cr;
2688 54 : if (ocr->isChord()) {
2689 47 : cr = Factory::createChord(this->dummy());
2690 47 : toChord(cr)->setStemDirection(toChord(ocr)->stemDirection());
2691 94 : for (Note* oldNote : toChord(ocr)->notes()) {
2692 47 : Note* note = Factory::createNote(toChord(cr));
2693 47 : note->setPitch(oldNote->pitch());
2694 47 : note->setTpc1(oldNote->tpc1());
2695 47 : note->setTpc2(oldNote->tpc2());
2696 47 : cr->add(note);
2697 : }
2698 : } else {
2699 7 : cr = Factory::createRest(this->dummy());
2700 : }
2701 :
2702 54 : int actualNotes = an.numerator() / an.denominator();
2703 :
2704 54 : tuplet->setTrack(track);
2705 54 : cr->setTuplet(tuplet);
2706 54 : cr->setTrack(track);
2707 54 : cr->setDurationType(tuplet->baseLen());
2708 54 : cr->setTicks(tuplet->baseLen().fraction());
2709 :
2710 54 : undoAddCR(cr, measure, tick);
2711 :
2712 54 : Fraction ticks = cr->actualTicks();
2713 :
2714 178 : for (int i = 0; i < (actualNotes - 1); ++i) {
2715 124 : tick += ticks;
2716 124 : Rest* rest = Factory::createRest(this->dummy());
2717 124 : rest->setTuplet(tuplet);
2718 124 : rest->setTrack(track);
2719 124 : rest->setDurationType(tuplet->baseLen());
2720 124 : rest->setTicks(tuplet->baseLen().fraction());
2721 124 : undoAddCR(rest, measure, tick);
2722 : }
2723 : }
2724 :
2725 : //---------------------------------------------------------
2726 : // cmdEnterRest
2727 : //---------------------------------------------------------
2728 :
2729 1 : void Score::cmdEnterRest(const TDuration& d)
2730 : {
2731 1 : if (m_is.track() == muse::nidx) {
2732 0 : LOGD("cmdEnterRest: track invalid");
2733 0 : return;
2734 : }
2735 1 : startCmd(TranslatableString("undoableAction", "Enter rest"));
2736 1 : enterRest(d);
2737 1 : endCmd();
2738 : }
2739 :
2740 : //---------------------------------------------------------
2741 : // enterRest
2742 : //---------------------------------------------------------
2743 :
2744 1 : void Score::enterRest(const TDuration& d, InputState* externalInputState)
2745 : {
2746 1 : InputState& is = externalInputState ? (*externalInputState) : m_is;
2747 :
2748 1 : if (is.beyondScore()) {
2749 0 : appendMeasures(1);
2750 0 : is.moveToNextInputPos();
2751 : }
2752 :
2753 1 : expandVoice(is.segment(), is.track());
2754 :
2755 1 : if (!is.cr()) {
2756 0 : LOGD("cannot enter rest here");
2757 0 : return;
2758 : }
2759 :
2760 1 : const track_idx_t track = is.track();
2761 1 : NoteVal nval;
2762 1 : setNoteRest(is.segment(), track, nval,
2763 : d.fraction(), DirectionV::AUTO, /* forceAccidental */ false, is.articulationIds(), /* rhythmic */ false,
2764 : externalInputState);
2765 1 : is.moveToNextInputPos();
2766 1 : if (!is.noteEntryMode() || is.usingNoteEntryMethod(NoteEntryMethod::BY_NOTE_NAME)) {
2767 1 : is.setRest(false); // continue with normal note entry
2768 : }
2769 : }
2770 :
2771 : //---------------------------------------------------------
2772 : // removeChordRest
2773 : // remove chord or rest
2774 : // remove associated segment if empty
2775 : // remove beam
2776 : // remove slurs
2777 : //---------------------------------------------------------
2778 :
2779 666 : void Score::removeChordRest(ChordRest* cr, bool clearSegment)
2780 : {
2781 666 : std::set<Segment*> segments;
2782 1361 : for (EngravingObject* e : cr->linkList()) {
2783 695 : if (cr->isChord()) {
2784 468 : std::set<Spanner*> startingSpanners = toChord(e)->startingSpanners();
2785 469 : for (Spanner* spanner : startingSpanners) {
2786 1 : if (spanner->isTrill() || spanner->isSlur()) {
2787 0 : doUndoRemoveElement(spanner);
2788 : }
2789 : }
2790 468 : }
2791 695 : doUndoRemoveElement(static_cast<EngravingItem*>(e));
2792 695 : if (clearSegment) {
2793 622 : Segment* s = toChordRest(e)->segment();
2794 622 : if (segments.find(s) == segments.end()) {
2795 618 : segments.insert(s);
2796 : }
2797 : }
2798 666 : }
2799 1284 : for (Segment* s : segments) {
2800 618 : if (s->empty()) {
2801 419 : doUndoRemoveElement(s);
2802 : }
2803 : }
2804 666 : if (cr->beam()) {
2805 0 : Beam* beam = cr->beam();
2806 0 : if (beam->generated()) {
2807 0 : removeElement(beam);
2808 0 : delete beam;
2809 : } else {
2810 0 : undoRemoveElement(beam);
2811 : }
2812 : }
2813 666 : }
2814 :
2815 : //---------------------------------------------------------
2816 : // cmdDeleteTuplet
2817 : // remove tuplet and replace with rest
2818 : //---------------------------------------------------------
2819 :
2820 48 : void Score::cmdDeleteTuplet(Tuplet* tuplet, bool replaceWithRest)
2821 : {
2822 48 : std::vector<DurationElement*> elements = tuplet->elements();
2823 199 : for (DurationElement* de : elements) {
2824 151 : if (de->isChordRest()) {
2825 134 : removeChordRest(toChordRest(de), true);
2826 : } else {
2827 17 : assert(de->isTuplet());
2828 17 : cmdDeleteTuplet(toTuplet(de), false);
2829 : }
2830 : }
2831 48 : if (replaceWithRest) {
2832 7 : setRest(tuplet->tick(), tuplet->track(), tuplet->ticks(), true, tuplet->tuplet());
2833 : }
2834 48 : }
2835 :
2836 : //---------------------------------------------------------
2837 : // insertMeasure
2838 : // if inserting Measure, act on master score
2839 : // (and add to all linked scores)
2840 : // if inserting Box, add to local score only
2841 : //---------------------------------------------------------
2842 :
2843 202 : MeasureBase* Score::insertMeasure(ElementType type, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
2844 : {
2845 202 : MeasureBase* localInsertMeasureBase = nullptr;
2846 202 : if (type == ElementType::MEASURE) {
2847 202 : if (MeasureBase* masterInsertMeasure = masterScore()->insertMeasure(beforeMeasure, options)) {
2848 202 : localInsertMeasureBase = tick2measure(masterInsertMeasure->tick());
2849 : }
2850 : } else {
2851 0 : localInsertMeasureBase = insertBox(type, beforeMeasure, options);
2852 : }
2853 :
2854 202 : if (localInsertMeasureBase && options.needDeselectAll) {
2855 202 : deselectAll();
2856 : }
2857 :
2858 202 : return localInsertMeasureBase;
2859 : }
2860 :
2861 : //---------------------------------------------------------
2862 : // insertBox
2863 : //---------------------------------------------------------
2864 :
2865 66 : MeasureBase* Score::insertBox(ElementType type, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
2866 : {
2867 66 : const bool isFrame = type == ElementType::FBOX || type == ElementType::HBOX || type == ElementType::TBOX || type == ElementType::VBOX;
2868 66 : if (!isFrame) {
2869 0 : return nullptr;
2870 : }
2871 :
2872 66 : return this->insertBox(toMeasureBase(Factory::createItem(type, dummy())), beforeMeasure, options);
2873 : }
2874 :
2875 66 : MeasureBase* Score::insertBox(MeasureBase* box, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
2876 : {
2877 66 : IF_ASSERT_FAILED(box) {
2878 0 : return nullptr;
2879 : }
2880 :
2881 66 : ElementType type = box->type();
2882 66 : const bool isFrame = type == ElementType::FBOX || type == ElementType::HBOX || type == ElementType::TBOX || type == ElementType::VBOX;
2883 66 : if (!isFrame) {
2884 0 : return nullptr;
2885 : }
2886 :
2887 66 : if (box->score() != this) {
2888 0 : box->setScore(this);
2889 : }
2890 :
2891 66 : Fraction tick;
2892 66 : bool isTitleFrame = type == ElementType::VBOX && beforeMeasure && beforeMeasure == beforeMeasure->score()->first();
2893 66 : if (beforeMeasure) {
2894 66 : if (beforeMeasure->isMeasure()) {
2895 66 : Measure* m = toMeasure(beforeMeasure);
2896 66 : if (m->isMMRest()) {
2897 0 : beforeMeasure = m->mmRestFirst();
2898 0 : deselectAll();
2899 : }
2900 145 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
2901 79 : if (m->isMeasureRepeatGroupWithPrevM(staffIdx)) {
2902 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
2903 0 : return nullptr;
2904 : }
2905 : }
2906 : } else {
2907 0 : beforeMeasure = beforeMeasure->top(); // don't try to insert in front of nested frame
2908 : }
2909 66 : tick = beforeMeasure->tick();
2910 : } else {
2911 0 : tick = last() ? last()->endTick() : Fraction(0, 1);
2912 : }
2913 :
2914 66 : box->setTick(tick);
2915 66 : box->setNext(beforeMeasure);
2916 66 : box->setPrev(beforeMeasure ? beforeMeasure->prev() : last());
2917 66 : box->setSizeIsSpatiumDependent(!isTitleFrame);
2918 :
2919 66 : if (type == ElementType::FBOX) {
2920 5 : toFBox(box)->init();
2921 : }
2922 :
2923 66 : undo(new InsertMeasures(box, box));
2924 :
2925 66 : if (options.needDeselectAll) {
2926 66 : deselectAll();
2927 : }
2928 :
2929 66 : if (options.cloneBoxToAllParts) {
2930 47 : box->manageExclusionFromParts(/*exclude =*/ false);
2931 : }
2932 :
2933 66 : return box;
2934 : }
2935 :
2936 38 : void Score::restoreInitialKeySigAndTimeSig()
2937 : {
2938 38 : bool concertPitch = style().styleB(Sid::concertPitch);
2939 : static constexpr Fraction startTick = Fraction(0, 1);
2940 :
2941 38 : Measure* firstMeas = firstMeasure();
2942 83 : for (Staff* staff : m_staves) {
2943 45 : Key concertKey = Key::C;
2944 45 : Key transposedKey = concertKey;
2945 45 : const Part* part = staff->part();
2946 45 : const Instrument* instrument = part->instrument();
2947 45 : int transpose = -instrument->transpose().chromatic;
2948 45 : if (!concertPitch) {
2949 45 : transposedKey = Transpose::transposeKey(transposedKey, transpose, part->preferSharpFlat());
2950 : }
2951 :
2952 45 : Segment* keySegment = firstMeas->undoGetSegment(SegmentType::KeySig, startTick);
2953 :
2954 45 : KeySigEvent keySigEvent;
2955 45 : keySigEvent.setConcertKey(concertKey);
2956 45 : keySigEvent.setKey(transposedKey);
2957 : // use atonal KS for drums
2958 45 : if (instrument->useDrumset()) {
2959 0 : keySigEvent.setCustom(true);
2960 0 : keySigEvent.setMode(KeyMode::NONE);
2961 : }
2962 :
2963 45 : KeySig* newKeySig = Factory::createKeySig(keySegment);
2964 45 : newKeySig->setTrack(staff->idx() * VOICES);
2965 45 : keySegment->add(newKeySig);
2966 45 : newKeySig->setKeySigEvent(keySigEvent);
2967 45 : undoAddElement(newKeySig);
2968 :
2969 45 : staff->setKey(Fraction(0, 1), keySigEvent);
2970 :
2971 45 : Segment* timeSegment = firstMeas->undoGetSegment(SegmentType::TimeSig, startTick);
2972 45 : TimeSig* newTimeSig = Factory::createTimeSig(timeSegment);
2973 45 : newTimeSig->setTrack(staff->idx() * VOICES);
2974 45 : newTimeSig->setSig(Fraction(4, 4));
2975 45 : timeSegment->add(newTimeSig);
2976 45 : undoAddElement(newTimeSig);
2977 45 : }
2978 :
2979 38 : firstMeas->setTimesig(Fraction(4, 4));
2980 38 : firstMeas->setTicks(Fraction(4, 4));
2981 38 : }
2982 :
2983 : //---------------------------------------------------------
2984 : // checkSpanner
2985 : // check if spanners are still valid as anchors may
2986 : // have changed or be removed.
2987 : // Spanners need to have a start anchor. Slurs need a
2988 : // start and end anchor.
2989 : //---------------------------------------------------------
2990 :
2991 10491 : void Score::checkSpanner(const Fraction& startTick, const Fraction& endTick, bool removeOrphans)
2992 : {
2993 10491 : std::vector<Spanner*> spannersToRemove;
2994 10491 : std::vector<Spanner*> spannersToShorten;
2995 10491 : auto spanners = m_spanner.findOverlapping(startTick.ticks(), endTick.ticks());
2996 :
2997 : // DEBUG: check all spanner
2998 : // there may be spanners outside of score bc. some measures were deleted
2999 :
3000 10491 : Fraction lastTick = lastMeasure()->endTick();
3001 :
3002 14752 : for (const auto& i : m_spanner.map()) {
3003 4261 : Spanner* s = i.second;
3004 :
3005 4261 : if (s->isSlur()) {
3006 2297 : Segment* seg = tick2segmentMM(s->tick(), false, SegmentType::ChordRest);
3007 2297 : if (!seg || !seg->element(s->track())) {
3008 0 : spannersToRemove.push_back(s);
3009 : } else {
3010 2297 : seg = tick2segmentMM(s->tick2(), false, SegmentType::ChordRest);
3011 2297 : if (!seg || !seg->element(s->track2())) {
3012 0 : spannersToRemove.push_back(s);
3013 : }
3014 : }
3015 : } else {
3016 : // remove spanner if there is no start element
3017 1964 : s->computeStartElement();
3018 1964 : if (!s->startElement()) {
3019 0 : spannersToRemove.push_back(s);
3020 0 : LOGD("checkSpanner::remove (3)");
3021 : } else {
3022 1964 : if (s->tick2() > lastTick) {
3023 0 : spannersToShorten.push_back(s); //s->undoChangeProperty(Pid::SPANNER_TICKS, lastTick - s->tick());
3024 : } else {
3025 1964 : s->computeEndElement();
3026 : }
3027 : }
3028 : }
3029 : }
3030 10491 : if (removeOrphans) {
3031 2 : for (auto s : spannersToRemove) { // actually remove scheduled spanners
3032 0 : doUndoRemoveElement(s);
3033 : }
3034 : }
3035 10491 : for (Spanner* s : spannersToShorten) { // shorten spanners that extended past end of score
3036 0 : undo(new ChangeProperty(s, Pid::SPANNER_TICKS, lastTick - s->tick()));
3037 0 : s->computeEndElement();
3038 : }
3039 10491 : }
3040 :
3041 : //---------------------------------------------------------
3042 : // checkTimeDelete
3043 : //---------------------------------------------------------
3044 :
3045 12 : bool Score::checkTimeDelete(Segment* startSegment, Segment* endSegment)
3046 : {
3047 12 : Measure* startMeasure = startSegment->measure();
3048 : Measure* endMeasure;
3049 :
3050 12 : if (endSegment) {
3051 12 : endMeasure = endSegment->prev() ? endSegment->measure() : endSegment->measure()->prevMeasure();
3052 : } else {
3053 0 : endMeasure = lastMeasure();
3054 : }
3055 :
3056 12 : Fraction endTick = endSegment ? endSegment->tick() : endMeasure->endTick();
3057 12 : Fraction tick = startSegment->tick();
3058 12 : Fraction etick = (startMeasure == endMeasure ? endTick : startMeasure->endTick());
3059 :
3060 : // check for MeasureRepeat
3061 12 : bool startsAtBeginningOfMeasure = (tick == startMeasure->tick());
3062 12 : bool endsAtEndOfMeasure = (endTick == endMeasure->endTick());
3063 27 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3064 15 : if ((startMeasure->isMeasureRepeatGroup(staffIdx) && !startsAtBeginningOfMeasure)
3065 15 : || (endMeasure->isMeasureRepeatGroup(staffIdx) && !endsAtEndOfMeasure)
3066 15 : || startMeasure->isMeasureRepeatGroupWithPrevM(staffIdx)
3067 30 : || endMeasure->isMeasureRepeatGroupWithNextM(staffIdx)) {
3068 0 : MScore::setError(MsError::CANNOT_REMOVE_TIME_MEASURE_REPEAT);
3069 0 : return false;
3070 : }
3071 : }
3072 :
3073 12 : bool canDeleteTime = true;
3074 12 : while (canDeleteTime) {
3075 72 : for (size_t track = 0; canDeleteTime && track < m_staves.size() * VOICES; ++track) {
3076 60 : if (startMeasure->hasVoice(track)) {
3077 15 : Segment* fs = startMeasure->first(SegmentType::ChordRest);
3078 51 : for (Segment* s = fs; s; s = s->next(SegmentType::ChordRest)) {
3079 42 : if (s->element(track)) {
3080 42 : ChordRest* cr = toChordRest(s->element(track));
3081 42 : Tuplet* t = cr->tuplet();
3082 42 : DurationElement* de = t ? toDurationElement(t) : toDurationElement(cr);
3083 42 : Fraction f = de->tick() + de->actualTicks();
3084 42 : Fraction cetick = f;
3085 42 : if (cetick <= tick) {
3086 0 : continue;
3087 : }
3088 42 : if (de->tick() >= etick) {
3089 6 : break;
3090 : }
3091 36 : if (t && (t->tick() < tick || cetick > etick)) {
3092 0 : canDeleteTime = false;
3093 0 : break;
3094 : }
3095 : }
3096 : }
3097 : }
3098 : }
3099 12 : if (startMeasure == endMeasure) {
3100 12 : break;
3101 : }
3102 0 : startMeasure = endMeasure;
3103 0 : tick = startMeasure->tick();
3104 0 : etick = endTick;
3105 : }
3106 12 : if (!canDeleteTime) {
3107 0 : MScore::setError(MsError::CANNOT_REMOVE_TIME_TUPLET);
3108 0 : return false;
3109 : }
3110 12 : return true;
3111 : }
3112 :
3113 : //---------------------------------------------------------
3114 : // cmdTimeDelete
3115 : /// delete time by decreasing measure length if partial measures are selected
3116 : //---------------------------------------------------------
3117 :
3118 12 : void Score::cmdTimeDelete()
3119 : {
3120 12 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
3121 :
3122 12 : EngravingItem* e = selection().element();
3123 :
3124 12 : if (e && e->isBarLine() && toBarLine(e)->segment()->isEndBarLineType()) {
3125 0 : const Measure* m = toBarLine(e)->segment()->measure();
3126 0 : const Measure* next = m->nextMeasure();
3127 0 : if (next) {
3128 0 : SplitJoinMeasure::joinMeasures(tx, m_masterScore, m->tick(), next->tick());
3129 : }
3130 0 : return;
3131 : }
3132 :
3133 12 : Segment* startSegment = nullptr;
3134 12 : Segment* endSegment = nullptr;
3135 :
3136 12 : if (selection().state() != SelState::RANGE) {
3137 3 : if (!e) {
3138 0 : return;
3139 : }
3140 :
3141 3 : ChordRest* cr = nullptr;
3142 3 : if (e->isNote()) {
3143 3 : cr = toNote(e)->chord();
3144 0 : } else if (e->isChordRest()) {
3145 0 : cr = toChordRest(e);
3146 : } else {
3147 0 : return;
3148 : }
3149 :
3150 3 : startSegment = cr->segment();
3151 3 : Fraction endTick = cr->endTick();
3152 3 : endSegment = tick2measure(endTick)->findSegment(SegmentType::ChordRest, endTick);
3153 : } else {
3154 9 : startSegment = selection().startSegment();
3155 9 : endSegment = selection().endSegment();
3156 : }
3157 :
3158 12 : if (!isMaster() && masterScore()) {
3159 0 : Fraction startTick = startSegment->tick();
3160 0 : Measure* masterStartMeas = masterScore()->tick2measure(startTick);
3161 0 : Segment* masterStartSeg = masterStartMeas->findSegment(startSegment->segmentType(), startSegment->tick());
3162 0 : Segment* masterEndSeg = nullptr;
3163 :
3164 0 : if (endSegment) {
3165 0 : Fraction endTick = endSegment->tick();
3166 0 : Measure* masterEndMeas = masterScore()->tick2measure(endTick);
3167 0 : if (endSegment->isEndBarLineType()) {
3168 0 : Measure* prevMasterEndMeasure = masterEndMeas->prevMeasure();
3169 0 : masterEndMeas = prevMasterEndMeasure ? prevMasterEndMeasure : masterEndMeas;
3170 : }
3171 0 : masterEndSeg = masterEndMeas->findSegment(endSegment->segmentType(), endSegment->tick());
3172 : }
3173 :
3174 0 : masterScore()->doTimeDelete(masterStartSeg, masterEndSeg);
3175 : } else {
3176 12 : doTimeDelete(startSegment, endSegment);
3177 : }
3178 :
3179 12 : if (noteEntryMode()) {
3180 1 : Segment* currentSegment = endSegment;
3181 1 : ChordRest* cr = nullptr;
3182 1 : if (!currentSegment && lastMeasureMM()) {
3183 : // deleted to end of score - get last cr on current track
3184 0 : currentSegment = lastMeasureMM()->last();
3185 0 : if (currentSegment) {
3186 0 : cr = currentSegment->nextChordRest(m_is.track(), true);
3187 0 : if (cr) {
3188 0 : currentSegment = cr->segment();
3189 : }
3190 : }
3191 : }
3192 1 : if (!currentSegment) {
3193 : // no cr found - append a new measure
3194 0 : appendMeasures(1);
3195 0 : currentSegment = lastMeasureMM()->first(SegmentType::ChordRest);
3196 : }
3197 1 : m_is.setSegment(currentSegment);
3198 1 : cr = m_is.cr();
3199 1 : if (cr) {
3200 1 : if (cr->isChord()) {
3201 1 : select(toChord(cr)->upNote(), SelectType::SINGLE);
3202 : } else {
3203 0 : select(cr, SelectType::SINGLE);
3204 : }
3205 : } else {
3206 : // could not find cr to select,
3207 : // may be that there is a "hole" in the current track
3208 0 : deselectAll();
3209 : }
3210 : } else {
3211 11 : deselectAll();
3212 : }
3213 : }
3214 :
3215 12 : void Score::doTimeDelete(Segment* startSegment, Segment* endSegment)
3216 : {
3217 12 : if (!checkTimeDelete(startSegment, endSegment)) {
3218 0 : return;
3219 : }
3220 :
3221 12 : MeasureBase* mbStart = startSegment->measure();
3222 12 : if (mbStart->isMeasure() && toMeasure(mbStart)->isMMRest()) {
3223 0 : mbStart = toMeasure(mbStart)->mmRestFirst();
3224 : }
3225 : MeasureBase* mbEnd;
3226 :
3227 12 : if (endSegment) {
3228 12 : mbEnd = endSegment->prev(SegmentType::ChordRest) ? endSegment->measure() : endSegment->measure()->prevMeasure();
3229 : } else {
3230 0 : mbEnd = lastMeasure();
3231 : }
3232 :
3233 12 : Fraction endTick = endSegment ? endSegment->tick() : mbEnd->endTick();
3234 :
3235 : for (;;) {
3236 12 : if (mbStart->tick() != startSegment->tick()) {
3237 0 : Fraction tick = startSegment->tick();
3238 0 : Fraction len;
3239 0 : if (mbEnd == mbStart) {
3240 0 : len = endTick - tick;
3241 : } else {
3242 0 : len = mbStart->endTick() - tick;
3243 : }
3244 0 : doTimeDeleteForMeasure(toMeasure(mbStart), startSegment, len);
3245 0 : if (mbStart == mbEnd) {
3246 0 : break;
3247 : }
3248 0 : mbStart = mbStart->next();
3249 : }
3250 12 : endTick = endSegment ? endSegment->tick() : mbEnd->endTick();
3251 12 : if (mbEnd->endTick() != endTick) {
3252 3 : Fraction len = endTick - mbEnd->tick();
3253 3 : doTimeDeleteForMeasure(toMeasure(mbEnd), toMeasure(mbEnd)->first(), len);
3254 3 : if (mbStart == mbEnd) {
3255 3 : break;
3256 : }
3257 0 : mbEnd = mbEnd->prev();
3258 : }
3259 9 : deleteMeasures(mbStart, mbEnd);
3260 9 : break;
3261 : }
3262 : }
3263 :
3264 3 : void Score::doTimeDeleteForMeasure(Measure* m, Segment* startSegment, const Fraction& f)
3265 : {
3266 3 : if (f.isZero()) {
3267 0 : return;
3268 : }
3269 :
3270 3 : const Fraction tick = startSegment->rtick();
3271 3 : const Fraction len = f;
3272 3 : const Fraction etick = tick + len;
3273 :
3274 3 : Segment* fs = m->first(SegmentType::ChordRest);
3275 :
3276 27 : for (size_t track = 0; track < m_staves.size() * VOICES; ++track) {
3277 24 : if (m->hasVoice(track)) {
3278 12 : for (Segment* s = fs; s; s = s->next(SegmentType::ChordRest)) {
3279 12 : if (s->element(track)) {
3280 12 : ChordRest* cr = toChordRest(s->element(track));
3281 12 : Fraction cetick = cr->rtick() + cr->actualTicks();
3282 :
3283 12 : if (cetick <= tick) {
3284 0 : continue;
3285 : }
3286 12 : if (s->rtick() >= etick) {
3287 6 : break;
3288 : }
3289 :
3290 6 : if (cr->isFullMeasureRest()) {
3291 0 : if (cr->rtick() >= tick) {
3292 : // Move full-measure rest from the deleted area
3293 0 : undoRemoveElement(cr);
3294 0 : ChordRest* newCR = toChordRest(cr->clone());
3295 0 : newCR->setTicks(cr->ticks() - f);
3296 0 : undoAddCR(newCR, m, m->tick() + etick);
3297 : } else {
3298 0 : cr->undoChangeProperty(Pid::DURATION, cr->ticks() - f);
3299 : }
3300 : }
3301 : // inside deleted area
3302 6 : else if (s->rtick() >= tick && cetick <= etick) {
3303 : // inside
3304 6 : undoRemoveElement(cr);
3305 0 : } else if (s->rtick() >= tick) {
3306 : // running out
3307 0 : Fraction ff = cetick - etick;
3308 0 : undoRemoveElement(cr);
3309 0 : createCRSequence(ff, cr, tick + len);
3310 0 : } else if (s->rtick() < tick && cetick <= etick) {
3311 : // running in
3312 0 : Fraction f1 = tick - s->rtick();
3313 0 : changeCRlen(cr, f1, false);
3314 : } else {
3315 : // running in/out
3316 0 : Fraction f1 = cr->ticks() - f;
3317 0 : changeCRlen(cr, f1, false);
3318 : }
3319 : }
3320 : }
3321 : }
3322 : }
3323 3 : const Fraction abstick = startSegment->tick();
3324 3 : undoInsertTime(abstick, -len);
3325 :
3326 3 : std::vector<Segment*> emptySegments;
3327 :
3328 6 : for (Score* score : masterScore()->scoreList()) {
3329 3 : Measure* localMeasure = score->tick2measure(abstick);
3330 :
3331 3 : undo(new InsertTime(score, abstick, -len));
3332 :
3333 3 : Fraction updatedTick = tick;
3334 : // tuplets store their tick: move those after the removed time back with their segments
3335 3 : std::set<Tuplet*> tuplets;
3336 24 : for (Segment* s = localMeasure->first(SegmentType::ChordRest); s; s = s->next()) {
3337 21 : if (s->rtick() < etick || s->rtick() == updatedTick) {
3338 0 : continue;
3339 : }
3340 :
3341 21 : if (s->isChordRestType()) {
3342 162 : for (EngravingItem* e : s->elist()) {
3343 161 : for (Tuplet* t = e ? toChordRest(e)->tuplet() : nullptr; t && t->tick() >= abstick + len; t = t->tuplet()) {
3344 : // shared tuplets follow their origin
3345 17 : if (t->originItems().empty()) {
3346 14 : tuplets.insert(t);
3347 : }
3348 : }
3349 : }
3350 : }
3351 21 : s->undoChangeProperty(Pid::TICK, updatedTick);
3352 21 : updatedTick += s->ticks();
3353 :
3354 21 : if (score->isMaster()) {
3355 21 : if (s->isChordRestType() && !s->hasElements()) {
3356 0 : emptySegments.push_back(s);
3357 : }
3358 : }
3359 : }
3360 :
3361 7 : for (Tuplet* t : tuplets) {
3362 4 : t->undoChangeProperty(Pid::TICK, t->tick() - len);
3363 : }
3364 :
3365 3 : undo(new ChangeMeasureLen(localMeasure, localMeasure->ticks() - f));
3366 6 : }
3367 :
3368 3 : for (Segment* s : emptySegments) {
3369 0 : if (Segment* ns = s->next(SegmentType::ChordRest)) {
3370 : // Move annotations from the empty segment.
3371 : // TODO: do we need to preserve annotations at all?
3372 : // Maybe only some types (Tempo etc.)?
3373 0 : const auto annotations = s->annotations(); // make a copy since we alter the list
3374 0 : for (EngravingItem* a : annotations) {
3375 0 : EngravingItem* a1 = a->clone();
3376 0 : a1->setOwnershipParent(ns);
3377 0 : undoRemoveElement(a);
3378 0 : undoAddElement(a1);
3379 : }
3380 0 : }
3381 : }
3382 3 : }
3383 :
3384 : //---------------------------------------------------------
3385 : // undoPropertyChanged
3386 : // return true if an property was actually changed
3387 : //---------------------------------------------------------
3388 :
3389 0 : bool Score::undoPropertyChanged(EngravingItem* item, Pid propId, const PropertyValue& propValue, PropertyFlags propFlags)
3390 : {
3391 0 : Transaction& tx = masterScore()->transactionManager()->currentOrDummyTransaction();
3392 :
3393 0 : bool changed = false;
3394 :
3395 0 : const PropertyValue currentPropValue = item->getProperty(propId);
3396 0 : const PropertyFlags currentPropFlags = item->propertyFlags(propId);
3397 :
3398 0 : if ((currentPropValue != propValue) || (currentPropFlags != propFlags)) {
3399 0 : item->setPropertyFlags(propId, propFlags);
3400 0 : tx.pushWithoutPerforming(new ChangeProperty(item, propId, propValue, propFlags));
3401 0 : changed = true;
3402 : }
3403 :
3404 0 : const std::list<EngravingObject*> linkedItems = item->linkListForPropertyPropagation();
3405 :
3406 0 : for (EngravingObject* linkedItem : linkedItems) {
3407 0 : if (linkedItem == item) {
3408 0 : continue;
3409 : }
3410 0 : PropertyPropagation propertyPropagate = item->propertyPropagation(toEngravingItem(linkedItem), propId);
3411 0 : switch (propertyPropagate) {
3412 0 : case PropertyPropagation::PROPAGATE:
3413 0 : if (linkedItem->getProperty(propId) != currentPropValue) {
3414 0 : tx.push(new ChangeProperty(linkedItem, propId, currentPropValue, propFlags));
3415 0 : changed = true;
3416 : }
3417 0 : break;
3418 0 : case PropertyPropagation::UNLINK:
3419 0 : item->unlinkPropertyFromMaster(propId);
3420 0 : break;
3421 0 : default:
3422 0 : break;
3423 : }
3424 : }
3425 :
3426 0 : return changed;
3427 0 : }
3428 :
3429 0 : void Score::undoPropertyChanged(EngravingObject* e, Pid t, const PropertyValue& st, PropertyFlags ps)
3430 : {
3431 0 : if (e->getProperty(t) != st) {
3432 0 : Transaction& tx = masterScore()->transactionManager()->currentOrDummyTransaction();
3433 0 : tx.pushWithoutPerforming(new ChangeProperty(e, t, st, ps));
3434 : }
3435 0 : }
3436 :
3437 126 : void Score::undoChangeStyleVal(Sid idx, const PropertyValue& v)
3438 : {
3439 126 : std::unordered_map<Sid, PropertyValue> map;
3440 126 : map.emplace(idx, v);
3441 :
3442 126 : undo(new ChangeStyleValues(this, std::move(map)));
3443 126 : }
3444 :
3445 1 : void Score::undoChangeStyleValues(std::unordered_map<Sid, PropertyValue> values)
3446 : {
3447 1 : undo(new ChangeStyleValues(this, std::move(values)));
3448 1 : }
3449 :
3450 0 : void Score::undoChangePageNumberOffset(int po)
3451 : {
3452 0 : undo(new ChangePageNumberOffset(this, po));
3453 0 : }
3454 :
3455 180 : void Score::undoChangeParent(EngravingItem* element, EngravingItem* parent, staff_idx_t staffIdx, bool changeLinksParents)
3456 : {
3457 180 : if (!element || !parent) {
3458 0 : return;
3459 : }
3460 :
3461 180 : if (element->ownershipParent() == parent && staffIdx == element->staffIdx()) {
3462 0 : return;
3463 : }
3464 :
3465 180 : Staff* destStaff = staff(staffIdx);
3466 180 : bool recreateItemNeeded = false;
3467 :
3468 180 : const std::list<EngravingObject*> links = element->linkList();
3469 375 : for (EngravingObject* obj : links) {
3470 195 : EngravingItem* item = toEngravingItem(obj);
3471 195 : Score* linkedScore = item->score();
3472 195 : Staff* linkedOrigin = item->staff();
3473 195 : Staff* linkedDest = linkedScore != this && destStaff != element->staff() ? destStaff->findLinkedInScore(linkedScore) : linkedOrigin; // don't allow staff-change of linked elements within the same score
3474 195 : if (!linkedDest && element->systemFlag()) {
3475 0 : linkedDest = linkedScore->staff(0);
3476 : }
3477 :
3478 195 : if (!linkedScore || (!changeLinksParents && item != element)) {
3479 15 : continue;
3480 : }
3481 180 : if (item == element) {
3482 : // Master score
3483 180 : undo(new ChangeParent(element, parent, staffIdx));
3484 0 : } else if (linkedOrigin && linkedDest) {
3485 : // Part - origin and destination staves are in this part
3486 : EngravingItem* linkedParent;
3487 0 : if (parent->isSegment()) {
3488 : // Get parent segment in linked score
3489 0 : Segment* oldSeg = toSegment(parent);
3490 0 : Measure* oldMeas = oldSeg->measure();
3491 0 : Measure* newMeas = linkedScore->tick2measure(oldMeas->tick());
3492 0 : linkedParent = newMeas->tick2segment(oldSeg->tick(), oldSeg->segmentType());
3493 0 : if (!linkedParent && oldSeg->isType(SegmentType::Duration)) {
3494 : // A ChordRest segment that exists in the score may not exist in the part.
3495 : // In that case we create a TimeTick segment as new parent for the linked item.
3496 0 : linkedParent = newMeas->getSegment(SegmentType::TimeTick, oldSeg->tick());
3497 : }
3498 : } else {
3499 0 : linkedParent = parent->findLinkedInStaff(linkedDest);
3500 : }
3501 0 : IF_ASSERT_FAILED(linkedParent) {
3502 0 : continue;
3503 : }
3504 0 : linkedScore->undo(new ChangeParent(item, linkedParent, linkedScore->staffIdx(linkedDest)));
3505 0 : } else if (linkedOrigin && !linkedDest) {
3506 : // Part - move is to a different staff
3507 : // Remove original item
3508 0 : linkedScore->undoRemoveElement(item, false);
3509 0 : recreateItemNeeded = true;
3510 : }
3511 : }
3512 :
3513 180 : if (recreateItemNeeded) {
3514 : // Need to create item in some parts
3515 0 : const std::list<EngravingObject*> destStaffLinks = destStaff->linkList();
3516 0 : for (EngravingObject* obj : destStaffLinks) {
3517 0 : Staff* linkedDest = toStaff(obj);
3518 0 : Score* linkedScore = linkedDest->score();
3519 0 : if (linkedDest != destStaff && !element->findLinkedInScore(linkedScore)) {
3520 : // Item should be in this score and isn't already
3521 0 : EngravingItem* newItem = element->linkedClone();
3522 : EngravingItem* linkedParent;
3523 :
3524 0 : if (parent->isSegment()) {
3525 : // Get parent segment in linked score
3526 0 : Segment* oldSeg = toSegment(parent);
3527 0 : Measure* m = linkedScore->tick2measure(oldSeg->tick());
3528 0 : linkedParent = m->tick2segment(oldSeg->tick(), oldSeg->segmentType());
3529 : } else {
3530 0 : linkedParent = parent->findLinkedInScore(linkedScore);
3531 : }
3532 :
3533 0 : newItem->setOwnershipParent(linkedParent);
3534 0 : newItem->setTrack(linkedScore->staffIdx(linkedDest) * VOICES);
3535 0 : newItem->setOffset(PointF());
3536 0 : linkedParent->undoAddElement(newItem, false);
3537 : }
3538 : }
3539 0 : }
3540 180 : }
3541 :
3542 0 : void Score::undoResetPlayCountTextSettings(BarLine* bl)
3543 : {
3544 0 : if (!bl || !bl->segment()) {
3545 0 : return;
3546 : }
3547 0 : Segment* seg = bl->segment();
3548 :
3549 0 : for (staff_idx_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3550 0 : BarLine* curBl = toBarLine(seg->element(staff2track(staffIdx)));
3551 :
3552 0 : if (!curBl) {
3553 0 : continue;
3554 : }
3555 :
3556 0 : curBl->undoResetProperty(Pid::PLAY_COUNT_TEXT_SETTING);
3557 0 : curBl->undoResetProperty(Pid::PLAY_COUNT_TEXT);
3558 : }
3559 : }
3560 :
3561 277 : void Score::undoUpdatePlayCountText(Measure* m)
3562 : {
3563 277 : if (!m) {
3564 0 : return;
3565 : }
3566 277 : const MStyle& _style = style();
3567 277 : const bool showText = _style.styleB(Sid::repeatPlayCountShow);
3568 277 : const bool singleRepeats = _style.styleB(Sid::repeatPlayCountShowSingleRepeats);
3569 277 : const int playCount = m->repeatCount();
3570 277 : const bool showPlayCount = showText && (playCount == 2 ? singleRepeats : true) && m->repeatEnd();
3571 :
3572 277 : Segment* endBarSeg = m->last(SegmentType::BarLineTypes);
3573 277 : BarLine* topBl = endBarSeg ? toBarLine(endBarSeg->element(0)) : nullptr;
3574 277 : if (!topBl) {
3575 76 : return;
3576 : }
3577 :
3578 201 : PlayCountText* topPlayCountText = toPlayCountText(endBarSeg->findAnnotation(ElementType::PLAY_COUNT_TEXT, 0, 0));
3579 201 : if (showPlayCount) {
3580 0 : if (!topPlayCountText) {
3581 0 : topPlayCountText = Factory::createPlayCountText(endBarSeg);
3582 0 : topPlayCountText->setTrack(0);
3583 0 : topPlayCountText->setSelected(topBl->selected());
3584 0 : undoAddElement(topPlayCountText);
3585 : }
3586 : } else {
3587 201 : undoRemoveElement(topPlayCountText);
3588 201 : return;
3589 : }
3590 : }
3591 :
3592 1378 : Measure* Score::undoGetMeasure(const Fraction& tick)
3593 : {
3594 1378 : Measure* last = lastMeasure();
3595 1378 : if (!last || last->endTick() <= tick) {
3596 157 : Fraction lastTick = last ? last->endTick() : Fraction(0, 1);
3597 314 : while (tick >= lastTick) {
3598 157 : MeasureBase* m = score()->insertMeasure(ElementType::MEASURE);
3599 157 : lastTick += m->ticks();
3600 : }
3601 : }
3602 1378 : return tick2measure(tick);
3603 : }
3604 :
3605 21 : void Score::undoChangeBarLineType(BarLine* bl, BarLineType barType, bool allStaves, bool replace)
3606 : {
3607 21 : if (barType == bl->barLineType()) {
3608 9 : return;
3609 : }
3610 :
3611 12 : Measure* m = bl->measure();
3612 12 : if (!m) {
3613 0 : return;
3614 : }
3615 :
3616 12 : bool keepStartRepeat = false;
3617 :
3618 12 : if (barType == BarLineType::START_REPEAT && bl->segment()->segmentType() != SegmentType::BeginBarLine) {
3619 0 : m = m->nextMeasure();
3620 0 : if (!m) { // we were in last measure
3621 0 : return;
3622 : }
3623 12 : } else if (bl->barLineType() == BarLineType::START_REPEAT) {
3624 3 : if (m->system() && m->isFirstInSystem()) {
3625 2 : if (barType != BarLineType::END_REPEAT) {
3626 4 : for (Score* lscore : m->score()->scoreList()) {
3627 2 : Measure* lmeasure = lscore->tick2measure(m->tick());
3628 2 : if (lmeasure) {
3629 2 : lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
3630 : }
3631 2 : }
3632 : }
3633 2 : return;
3634 : }
3635 :
3636 1 : m = m->prevMeasureMM();
3637 1 : if (!m) {
3638 0 : return;
3639 : }
3640 :
3641 1 : bl = const_cast<BarLine*>(m->endBarLine());
3642 1 : if (!bl) {
3643 0 : return;
3644 : }
3645 :
3646 1 : if (barType == BarLineType::END_REPEAT) {
3647 0 : keepStartRepeat = true;
3648 : }
3649 : }
3650 :
3651 : // when setting barline type on mmrest, set for underlying measure (and linked staves)
3652 : // createMMRest will then set for the mmrest directly
3653 10 : Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
3654 :
3655 10 : switch (barType) {
3656 9 : case BarLineType::END:
3657 : case BarLineType::NORMAL:
3658 : case BarLineType::DOUBLE:
3659 : case BarLineType::BROKEN:
3660 : case BarLineType::DOTTED:
3661 : case BarLineType::REVERSE_END:
3662 : case BarLineType::HEAVY:
3663 : case BarLineType::DOUBLE_HEAVY: {
3664 9 : if (m->nextMeasureMM() && m->nextMeasureMM()->system() && m->nextMeasureMM()->isFirstInSystem()) {
3665 2 : keepStartRepeat = true;
3666 : }
3667 :
3668 9 : Segment* segment = bl->segment();
3669 9 : SegmentType segmentType = segment->segmentType();
3670 :
3671 9 : if (segmentType == SegmentType::EndBarLine) {
3672 9 : bool generated = false;
3673 9 : if (bl->barLineType() == barType) {
3674 1 : generated = bl->generated(); // no change: keep current status
3675 8 : } else if (!bl->generated() && (barType == BarLineType::NORMAL)) {
3676 3 : generated = true; // currently non-generated, changing to normal: assume generated
3677 : }
3678 9 : if (allStaves) {
3679 : // use all staves of master score; we will take care of parts in loop through linked staves below
3680 9 : Score* mScore = bl->masterScore();
3681 9 : if (mScore->style().styleB(Sid::createMultiMeasureRests)) {
3682 0 : m2 = mScore->tick2measureMM(m2->tick());
3683 0 : if (!m2) {
3684 0 : return;
3685 : }
3686 0 : segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
3687 0 : m2 = m2->isMMRest() ? m2->mmRestLast() : m2;
3688 0 : if (!m2) {
3689 0 : return;
3690 : }
3691 : } else {
3692 9 : m2 = mScore->tick2measure(m2->tick());
3693 9 : if (!m2) {
3694 0 : return;
3695 : }
3696 9 : segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
3697 : }
3698 : }
3699 9 : const std::vector<EngravingItem*>& elist = allStaves ? segment->elist() : std::vector<EngravingItem*> { bl };
3700 45 : for (EngravingItem* e : elist) {
3701 36 : if (!e || !e->staff() || !e->isBarLine()) {
3702 27 : continue;
3703 : }
3704 :
3705 : // handle linked staves/parts:
3706 : // barlines themselves are not necessarily linked,
3707 : // so use staffList to find linked staves
3708 9 : BarLine* sbl = toBarLine(e);
3709 18 : for (Staff* lstaff : sbl->staff()->staffList()) {
3710 9 : Score* lscore = lstaff->score();
3711 9 : track_idx_t ltrack = lstaff->idx() * VOICES;
3712 :
3713 : // handle mmrests:
3714 : // set the barline on the underlying measure
3715 : // this will copied to the mmrest during layout, in createMMRest
3716 9 : Measure* lmeasure = lscore->tick2measure(m2->tick());
3717 9 : if (!lmeasure) {
3718 0 : continue;
3719 : }
3720 :
3721 9 : lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
3722 :
3723 9 : Measure* nextMeasure = lmeasure->nextMeasure();
3724 9 : if (nextMeasure && !keepStartRepeat) {
3725 6 : nextMeasure->undoChangeProperty(Pid::REPEAT_START, false);
3726 : }
3727 9 : Segment* lsegment = lmeasure->undoGetSegmentR(SegmentType::EndBarLine, lmeasure->ticks());
3728 9 : BarLine* lbl = toBarLine(lsegment->element(ltrack));
3729 9 : if (!lbl) {
3730 0 : lbl = Factory::createBarLine(lsegment);
3731 0 : lbl->setTrack(ltrack);
3732 0 : lbl->setSpanStaff(lstaff->barLineSpan());
3733 0 : lbl->setSpanFrom(lstaff->barLineFrom());
3734 0 : lbl->setSpanTo(lstaff->barLineTo());
3735 0 : lbl->setBarLineType(barType);
3736 0 : lbl->setGenerated(generated);
3737 0 : lscore->addElement(lbl);
3738 0 : if (!generated) {
3739 0 : lbl->linkTo(sbl);
3740 : }
3741 : } else {
3742 9 : lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
3743 9 : lscore->undo(new ChangeProperty(lbl, Pid::BARLINE_TYPE, PropertyValue::fromValue(barType), PropertyFlags::NOSTYLE));
3744 : // set generated flag before and after so it sticks on type change and also works on undo/redo
3745 9 : lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
3746 9 : if (lbl != sbl && !generated && !lbl->links()) {
3747 0 : lscore->undo(new Link(lbl, sbl));
3748 9 : } else if (lbl != sbl && generated && lbl->isLinked(sbl)) {
3749 0 : lscore->undo(new Unlink(lbl));
3750 0 : while (EngravingItem* linkedOnCoveringMMRest = sbl->findLinkedInScore(lscore)) {
3751 : // Edge case: an additional link of this barline exists on the covering mmRest of lbl's measure
3752 0 : lscore->undo(new Unlink(linkedOnCoveringMMRest));
3753 0 : lscore->doUndoRemoveElement(linkedOnCoveringMMRest);
3754 0 : }
3755 : }
3756 : }
3757 9 : }
3758 : }
3759 9 : } else if (segmentType == SegmentType::BeginBarLine) {
3760 0 : for (Score* lscore : m2->score()->scoreList()) {
3761 0 : Measure* lmeasure = lscore->tick2measure(m2->tick());
3762 0 : Segment* segment1 = lmeasure->undoGetSegmentR(SegmentType::BeginBarLine, Fraction(0, 1));
3763 0 : for (EngravingItem* e : segment1->elist()) {
3764 0 : if (e) {
3765 0 : lscore->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
3766 0 : lscore->undo(new ChangeProperty(e, Pid::BARLINE_TYPE, PropertyValue::fromValue(barType), PropertyFlags::NOSTYLE));
3767 : // set generated flag before and after so it sticks on type change and also works on undo/redo
3768 0 : lscore->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
3769 : }
3770 : }
3771 0 : }
3772 : }
3773 : }
3774 9 : break;
3775 0 : case BarLineType::START_REPEAT: {
3776 0 : for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
3777 0 : if (m2->isMeasureRepeatGroupWithPrevM(staffIdx)) {
3778 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
3779 0 : return;
3780 : }
3781 : }
3782 0 : for (Score* lscore : m2->score()->scoreList()) {
3783 0 : Measure* lmeasure = lscore->tick2measure(m2->tick());
3784 0 : if (lmeasure) {
3785 0 : lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
3786 0 : lmeasure = lmeasure->prevMeasure();
3787 0 : if (lmeasure && replace) {
3788 0 : lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
3789 : }
3790 : }
3791 0 : }
3792 : }
3793 0 : break;
3794 1 : case BarLineType::END_REPEAT: {
3795 3 : for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
3796 2 : if (m2->isMeasureRepeatGroupWithNextM(staffIdx)) {
3797 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
3798 0 : return;
3799 : }
3800 : }
3801 4 : for (Score* lscore : m2->score()->scoreList()) {
3802 3 : Measure* lmeasure = lscore->tick2measure(m2->tick());
3803 3 : if (lmeasure) {
3804 3 : lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
3805 3 : lmeasure = lmeasure->nextMeasure();
3806 3 : if (lmeasure && replace) {
3807 0 : lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
3808 : }
3809 : }
3810 1 : }
3811 : }
3812 1 : break;
3813 0 : case BarLineType::END_START_REPEAT: {
3814 0 : for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
3815 0 : if (m2->isMeasureRepeatGroupWithNextM(staffIdx)) {
3816 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
3817 0 : return;
3818 : }
3819 : }
3820 0 : for (Score* lscore : m2->score()->scoreList()) {
3821 0 : Measure* lmeasure = lscore->tick2measure(m2->tick());
3822 0 : if (lmeasure) {
3823 0 : lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
3824 0 : lmeasure = lmeasure->nextMeasure();
3825 0 : if (lmeasure) {
3826 0 : lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
3827 : }
3828 : }
3829 0 : }
3830 : }
3831 0 : break;
3832 : }
3833 :
3834 10 : undoUpdatePlayCountText(m);
3835 : }
3836 :
3837 : //---------------------------------------------------------
3838 : // undoChangeElement
3839 : //---------------------------------------------------------
3840 :
3841 118 : void Score::undoChangeElement(EngravingItem* oldElement, EngravingItem* newElement)
3842 : {
3843 118 : if (!oldElement) {
3844 3 : undoAddElement(newElement);
3845 : } else {
3846 115 : const std::list<EngravingObject*> links = oldElement->linkList();
3847 230 : for (EngravingObject* obj : links) {
3848 115 : EngravingItem* item = toEngravingItem(obj);
3849 115 : if (item == oldElement) {
3850 115 : undo(new ChangeElement(oldElement, newElement));
3851 : } else {
3852 0 : if (item->score()) {
3853 0 : EngravingItem* newClone = newElement->clone();
3854 0 : item->score()->undo(new ChangeElement(item, newClone));
3855 : }
3856 : }
3857 : }
3858 115 : }
3859 118 : }
3860 :
3861 35 : void Score::updateInstrumentChangeTranspositions(KeySigEvent& key, Staff* staff, const Fraction& tick)
3862 : {
3863 35 : DO_ASSERT(staff->score() == this);
3864 35 : if (!key.forInstrumentChange()) {
3865 35 : KeyList* kl = staff->keyList();
3866 35 : int nextTick = kl->nextKeyTick(tick.ticks());
3867 :
3868 43 : while (nextTick != -1) {
3869 8 : KeySigEvent e = kl->key(nextTick);
3870 8 : if (e.forInstrumentChange()) {
3871 8 : if (key.isAtonal() && !e.isAtonal()) {
3872 0 : e.setMode(KeyMode::NONE);
3873 0 : e.setConcertKey(Key::C);
3874 : } else {
3875 8 : e.setMode(key.mode());
3876 8 : Interval transposeInterval = staff->part()->instrument(Fraction::fromTicks(nextTick))->transpose();
3877 8 : transposeInterval.flip();
3878 8 : Key ckey = key.concertKey();
3879 8 : Key nkey = Transpose::transposeKey(ckey, transposeInterval, staff->part()->preferSharpFlat());
3880 8 : e.setConcertKey(ckey);
3881 8 : e.setKey(nkey);
3882 : }
3883 8 : Measure* m = tick2measure(Fraction::fromTicks(nextTick));
3884 8 : Segment* s = m ? m->tick2segment(Fraction::fromTicks(nextTick), SegmentType::KeySig) : nullptr;
3885 8 : track_idx_t track = staff->idx() * VOICES;
3886 8 : KeySig* keySig = nullptr;
3887 8 : EngravingItem* keySigElem = s ? s->element(track) : nullptr;
3888 8 : if (keySigElem && keySigElem->isKeySig()) {
3889 8 : keySig = toKeySig(keySigElem);
3890 : }
3891 8 : if (keySig) {
3892 8 : undo(new ChangeKeySig(keySig, e, keySig->showCourtesy()));
3893 : }
3894 8 : nextTick = kl->nextKeyTick(nextTick);
3895 : } else {
3896 0 : nextTick = -1;
3897 : }
3898 8 : }
3899 : }
3900 35 : }
3901 :
3902 : //---------------------------------------------------------
3903 : // findLinkedChord
3904 : //---------------------------------------------------------
3905 :
3906 537 : static Chord* findLinkedChord(Chord* c, Staff* nstaff)
3907 : {
3908 537 : Excerpt* se = c->score()->excerpt();
3909 537 : Excerpt* de = nstaff->score()->excerpt();
3910 537 : track_idx_t strack = c->track();
3911 :
3912 537 : if (se) {
3913 0 : strack = muse::key(se->tracksMapping(), strack);
3914 : }
3915 537 : track_idx_t dtrack = nstaff->idx() * VOICES + strack % VOICES;
3916 :
3917 537 : if (de) {
3918 0 : std::vector<track_idx_t> l = muse::values(de->tracksMapping(), strack);
3919 0 : if (l.empty()) {
3920 : // simply return the first linked chord whose staff is equal to nstaff
3921 0 : for (EngravingObject* ee : c->linkList()) {
3922 0 : Chord* ch = toChord(ee);
3923 0 : if (ch->staff() == nstaff) {
3924 0 : return ch;
3925 : }
3926 0 : }
3927 0 : return nullptr;
3928 : }
3929 0 : for (track_idx_t i : l) {
3930 0 : if (nstaff->idx() * VOICES <= i && (nstaff->idx() + 1) * VOICES > i) {
3931 0 : dtrack = i;
3932 0 : break;
3933 : }
3934 : }
3935 0 : }
3936 :
3937 537 : Segment* s = c->segment();
3938 537 : Measure* nm = nstaff->score()->tick2measure(s->tick());
3939 537 : Segment* ns = nm->findSegment(s->segmentType(), s->tick());
3940 537 : EngravingItem* ne = ns ? ns->element(dtrack) : nullptr;
3941 537 : if (!ne || !ne->isChord()) {
3942 0 : return nullptr;
3943 : }
3944 537 : Chord* nc = toChord(ne);
3945 537 : if (c->isGrace()) {
3946 3 : Chord* pc = toChord(c->ownershipParent());
3947 3 : size_t index = 0;
3948 3 : for (Chord* gc : pc->graceNotes()) {
3949 3 : if (c == gc) {
3950 3 : break;
3951 : }
3952 0 : index++;
3953 : }
3954 3 : if (index < nc->graceNotes().size()) {
3955 3 : nc = nc->graceNotes().at(index);
3956 : }
3957 : }
3958 537 : return nc;
3959 : }
3960 :
3961 : //---------------------------------------------------------
3962 : // undoChangeChordRestLen
3963 : //---------------------------------------------------------
3964 :
3965 69 : void Score::undoChangeChordRestLen(ChordRest* cr, const TDuration& d)
3966 : {
3967 69 : cr->undoChangeProperty(Pid::DURATION_TYPE_WITH_DOTS, d.typeWithDots());
3968 69 : cr->undoChangeProperty(Pid::DURATION, d.fraction());
3969 69 : }
3970 :
3971 : //---------------------------------------------------------
3972 : // undoRemovePart
3973 : //---------------------------------------------------------
3974 :
3975 8 : void Score::undoRemovePart(Part* part, size_t partIdx)
3976 : {
3977 8 : undo(new RemovePart(part, partIdx));
3978 8 : }
3979 :
3980 : //---------------------------------------------------------
3981 : // undoInsertPart
3982 : //---------------------------------------------------------
3983 :
3984 14 : void Score::undoInsertPart(Part* part, size_t targetPartIndex)
3985 : {
3986 14 : undo(new InsertPart(part, targetPartIndex));
3987 14 : }
3988 :
3989 : //---------------------------------------------------------
3990 : // undoRemoveStaff
3991 : // idx - index of staff in part
3992 : //---------------------------------------------------------
3993 :
3994 23 : void Score::undoRemoveStaff(Staff* staff)
3995 : {
3996 23 : const staff_idx_t staffIndex = staff->idx();
3997 23 : assert(staffIndex != muse::nidx);
3998 :
3999 23 : std::vector<Spanner*> spannersToRemove;
4000 :
4001 37 : for (auto it = m_spanner.cbegin(); it != m_spanner.cend(); ++it) {
4002 14 : Spanner* spanner = it->second;
4003 :
4004 14 : if (allowRemoveWhenRemovingStaves(spanner, staffIndex)) {
4005 11 : spannersToRemove.push_back(spanner);
4006 : }
4007 : }
4008 :
4009 27 : for (Spanner* spanner : m_unmanagedSpanner) {
4010 4 : if (allowRemoveWhenRemovingStaves(spanner, staffIndex)) {
4011 4 : spannersToRemove.push_back(spanner);
4012 : }
4013 : }
4014 :
4015 38 : for (Spanner* spanner : spannersToRemove) {
4016 15 : spanner->undoUnlink();
4017 15 : doUndoRemoveElement(spanner);
4018 : }
4019 :
4020 : //
4021 : // adjust measures
4022 : //
4023 276 : for (Measure* m = staff->score()->firstMeasure(); m; m = m->nextMeasure()) {
4024 253 : m->cmdRemoveStaves(staffIndex, staffIndex + 1);
4025 253 : if (m->hasMMRest()) {
4026 0 : m->mmRest()->cmdRemoveStaves(staffIndex, staffIndex + 1);
4027 : }
4028 : }
4029 :
4030 23 : staff->undoUnlink();
4031 :
4032 23 : undo(new RemoveStaff(staff));
4033 23 : }
4034 :
4035 : //---------------------------------------------------------
4036 : // undoInsertStaff
4037 : // idx - index of staff in part
4038 : //---------------------------------------------------------
4039 :
4040 57 : void Score::undoInsertStaff(Staff* staff, staff_idx_t ridx, bool createRests)
4041 : {
4042 57 : undo(new InsertStaff(staff, ridx));
4043 57 : staff_idx_t idx = staffIdx(staff->part()) + ridx;
4044 433 : for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
4045 376 : m->cmdAddStaves(idx, idx + 1, createRests);
4046 376 : if (m->hasMMRest()) {
4047 0 : m->mmRest()->cmdAddStaves(idx, idx + 1, false);
4048 : }
4049 : }
4050 : // when newly adding an instrument,
4051 : // this was already set when we created the staff
4052 : // we don't have any better info at this point
4053 : // and it doesn't work to adjust bracket & barlines until all staves are added
4054 : // TODO: adjust brackets only when appropriate
4055 : //adjustBracketsIns(idx, idx+1);
4056 57 : }
4057 :
4058 : //---------------------------------------------------------
4059 : // undoAddElement
4060 : //---------------------------------------------------------
4061 :
4062 8681 : void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool ctrlModifier, EngravingItem* elementToRelink)
4063 : {
4064 8681 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
4065 :
4066 8681 : Staff* ostaff = element->staff();
4067 8681 : track_idx_t strack = element->track();
4068 :
4069 8681 : ElementType et = element->type();
4070 :
4071 : //
4072 : // some elements are replicated for all parts regardless of
4073 : // linking:
4074 : //
4075 :
4076 8681 : bool isSystemLine = isSystemTextLine(element);
4077 :
4078 8681 : if ((et == ElementType::REHEARSAL_MARK)
4079 8670 : || (et == ElementType::SYSTEM_TEXT)
4080 8661 : || (et == ElementType::TRIPLET_FEEL)
4081 8661 : || (et == ElementType::JUMP)
4082 8661 : || (et == ElementType::MARKER)
4083 8659 : || (et == ElementType::TEMPO_TEXT)
4084 8564 : || (et == ElementType::PLAY_COUNT_TEXT)
4085 8564 : || isSystemLine
4086 : ) {
4087 121 : std::vector<Staff* > staffList;
4088 :
4089 121 : if (!addToLinkedStaves) {
4090 0 : staffList.push_back(element->staff());
4091 121 : } else if (ctrlModifier && isSystemLine) {
4092 0 : staffList = ostaff->staffList();
4093 0 : element->setSystemFlag(false);
4094 : } else {
4095 243 : for (Score* s : scoreList()) {
4096 122 : staffList.push_back(s->staff(0)); // system objects always appear on the top staff
4097 182 : for (Staff* staff : s->systemObjectStaves()) {
4098 60 : IF_ASSERT_FAILED(staff->idx() != muse::nidx) {
4099 0 : continue;
4100 : }
4101 :
4102 60 : staffList.push_back(staff);
4103 : }
4104 121 : }
4105 : }
4106 :
4107 121 : if (elementToRelink) {
4108 1 : muse::remove(staffList, ostaff);
4109 : }
4110 :
4111 121 : bool originalAdded = false;
4112 302 : for (Staff* staff : staffList) {
4113 181 : if (!staff) {
4114 0 : continue;
4115 : }
4116 :
4117 181 : Score* score = staff->score();
4118 181 : staff_idx_t staffIdx = staff->idx();
4119 181 : size_t ntrack = staffIdx * VOICES;
4120 : EngravingItem* ne;
4121 :
4122 181 : if (ostaff && staff->score() == ostaff->score() && staff == staffList.front() && !originalAdded) {
4123 : // add the element itself to the first system object staff in the score
4124 120 : ne = element;
4125 120 : originalAdded = true;
4126 : } else {
4127 : // add linked clones to the other staves
4128 61 : ne = element->linkedClone();
4129 61 : ne->setScore(score);
4130 61 : ne->setSelected(false);
4131 61 : ne->setTrack(staffIdx * VOICES + element->voice());
4132 : }
4133 :
4134 181 : if (isSystemLine) {
4135 5 : Spanner* nsp = toSpanner(ne);
4136 5 : Spanner* sp = toSpanner(element);
4137 5 : long long diff = sp->track2() - sp->track();
4138 5 : nsp->setTrack2(nsp->track() + diff);
4139 5 : nsp->computeStartElement();
4140 5 : nsp->computeEndElement();
4141 5 : doUndoAddElement(nsp);
4142 176 : } else if (et == ElementType::MARKER || et == ElementType::JUMP) {
4143 2 : Measure* om = toMeasure(element->ownershipParent());
4144 2 : Measure* m = score->tick2measure(om->tick());
4145 2 : ne->setTrack(ntrack);
4146 2 : ne->setOwnershipParent(m);
4147 2 : doUndoAddElement(ne);
4148 176 : } else if (et == ElementType::MEASURE_NUMBER) {
4149 0 : toMeasure(element->ownershipParent())->undoChangeProperty(Pid::MEASURE_NUMBER_MODE,
4150 : static_cast<int>(MeasureNumberMode::SHOW));
4151 174 : } else if (et == ElementType::PLAY_COUNT_TEXT) {
4152 0 : Segment* segment = toSegment(element->ownershipParent());
4153 0 : Fraction tick = segment->tick();
4154 0 : Measure* m = score->tick2measure(tick - Fraction::eps());
4155 0 : Segment* seg = m->undoGetSegment(SegmentType::EndBarLine, tick);
4156 0 : ne->setTrack(ntrack);
4157 0 : ne->setOwnershipParent(seg);
4158 0 : doUndoAddElement(ne);
4159 : } else {
4160 174 : Segment* segment = toSegment(element->ownershipParent());
4161 174 : Fraction tick = segment->tick();
4162 174 : Measure* m = score->tick2measure(tick);
4163 174 : Segment* seg = m->undoGetSegment(SegmentType::ChordRest, tick);
4164 174 : ne->setTrack(ntrack);
4165 174 : ne->setOwnershipParent(seg);
4166 174 : doUndoAddElement(ne);
4167 : }
4168 : }
4169 :
4170 121 : return;
4171 121 : }
4172 :
4173 17120 : if (et == ElementType::FINGERING
4174 8557 : || (et == ElementType::IMAGE && !element->ownershipParent()->isSegment())
4175 8554 : || (et == ElementType::SYMBOL && !element->ownershipParent()->isSegment())
4176 8551 : || et == ElementType::NOTE
4177 8471 : || et == ElementType::TEXT
4178 8471 : || et == ElementType::GLISSANDO
4179 8460 : || et == ElementType::GUITAR_BEND
4180 8168 : || et == ElementType::NOTELINE
4181 8168 : || et == ElementType::BEND
4182 8168 : || (et == ElementType::CHORD && toChord(element)->isGrace())
4183 8098 : || et == ElementType::LAISSEZ_VIB
4184 8080 : || et == ElementType::PARTIAL_TIE
4185 17117 : || et == ElementType::PARENTHESIS
4186 : ) {
4187 540 : const EngravingObject* parent = element->ownershipParent();
4188 540 : const LinkedObjects* links = parent ? parent->links() : nullptr;
4189 :
4190 : // don't link part name
4191 540 : if (et == ElementType::TEXT) {
4192 0 : Text* t = toText(element);
4193 0 : if (t->textStyleType() == TextStyleType::INSTRUMENT_EXCERPT) {
4194 0 : links = 0;
4195 : }
4196 : }
4197 540 : if (links == 0 || !addToLinkedStaves) {
4198 523 : doUndoAddElement(element);
4199 523 : return;
4200 : }
4201 51 : for (EngravingObject* ee : *links) {
4202 34 : EngravingItem* e = static_cast<EngravingItem*>(ee);
4203 : EngravingItem* ne;
4204 34 : if (e == parent) {
4205 17 : ne = element;
4206 : } else {
4207 17 : if (element->isGlissando() || element->isGuitarBend() || element->isNoteLine()) { // and other spanners with Anchor::NOTE
4208 2 : Note* newEnd = Spanner::endElementFromSpanner(toSpanner(element), e);
4209 2 : if (newEnd) {
4210 2 : ne = element->linkedClone();
4211 2 : toSpanner(ne)->setNoteSpan(toNote(e), newEnd);
4212 2 : if (element->isGuitarBend() && element->staffType()->isTabStaff()) {
4213 0 : toGuitarBend(ne)->endNote()->setVisible(true);
4214 : }
4215 : } else { //couldn't find suitable start note
4216 0 : continue;
4217 : }
4218 15 : } else if (element->isFingering()) {
4219 3 : bool tabFingering = e->staff()->staffType(e->tick())->showTabFingering();
4220 3 : if (e->staff()->isTabStaff(e->tick()) && !tabFingering) {
4221 0 : continue;
4222 : }
4223 3 : ne = element->linkedClone();
4224 : } else {
4225 12 : ne = element->linkedClone();
4226 : }
4227 : }
4228 34 : ne->setScore(e->score());
4229 34 : ne->setSelected(false);
4230 34 : ne->setOwnershipParent(e);
4231 34 : doUndoAddElement(ne);
4232 : }
4233 17 : return;
4234 : }
4235 :
4236 8020 : if (et == ElementType::LAYOUT_BREAK) {
4237 10 : LayoutBreak* lb = toLayoutBreak(element);
4238 10 : if (lb->layoutBreakType() == LayoutBreakType::SECTION) {
4239 0 : doUndoAddElement(lb);
4240 0 : MeasureBase* m = lb->measure();
4241 0 : if (m->isBox()) {
4242 : // for frames, use linked frames
4243 0 : LinkedObjects* links = m->links();
4244 0 : if (links) {
4245 0 : for (EngravingObject* lo : *links) {
4246 0 : if (lo->isBox()) {
4247 0 : Box* box = toBox(lo);
4248 0 : Score* score = box->score();
4249 0 : if (score != lb->score()) {
4250 0 : EngravingItem* e = lb->linkedClone();
4251 0 : e->setScore(score);
4252 0 : e->setOwnershipParent(box);
4253 0 : doUndoAddElement(e);
4254 : }
4255 : }
4256 : }
4257 0 : return;
4258 : } else { // if thera are not linked frames, use previous measure
4259 0 : m = m->prevMeasure();
4260 0 : if (!m) {
4261 0 : return;
4262 : }
4263 : }
4264 : }
4265 0 : for (Score* s : scoreList()) {
4266 0 : if (s != lb->score()) {
4267 0 : EngravingItem* e = lb->linkedClone();
4268 0 : e->setScore(s);
4269 0 : Measure* nm = s->tick2measure(m->tick());
4270 0 : e->setOwnershipParent(nm);
4271 0 : doUndoAddElement(e);
4272 : }
4273 0 : }
4274 0 : return;
4275 : }
4276 : }
4277 :
4278 8020 : if (ostaff == 0 || (
4279 2396 : et != ElementType::ARTICULATION
4280 2349 : && et != ElementType::ORNAMENT
4281 2346 : && et != ElementType::TAPPING
4282 2346 : && et != ElementType::CHORDLINE
4283 2343 : && et != ElementType::LYRICS
4284 2331 : && et != ElementType::SLUR
4285 2301 : && et != ElementType::HAMMER_ON_PULL_OFF
4286 2301 : && et != ElementType::TIE
4287 2042 : && et != ElementType::NOTE
4288 2042 : && et != ElementType::INSTRUMENT_CHANGE
4289 2038 : && et != ElementType::HAIRPIN
4290 2022 : && et != ElementType::OTTAVA
4291 2019 : && et != ElementType::TRILL
4292 2018 : && et != ElementType::VIBRATO
4293 2018 : && et != ElementType::TEXTLINE
4294 2018 : && et != ElementType::PEDAL
4295 2017 : && et != ElementType::LET_RING
4296 2017 : && et != ElementType::PALM_MUTE
4297 2017 : && et != ElementType::WHAMMY_BAR
4298 2017 : && et != ElementType::PARTIAL_LYRICSLINE
4299 2017 : && et != ElementType::BREATH
4300 2003 : && et != ElementType::DYNAMIC
4301 1978 : && et != ElementType::EXPRESSION
4302 1978 : && et != ElementType::STAFF_TEXT
4303 1950 : && et != ElementType::STAVE_SHARING_LABEL
4304 1948 : && et != ElementType::SYSTEM_TEXT
4305 1948 : && et != ElementType::TRIPLET_FEEL
4306 1948 : && et != ElementType::PLAYTECH_ANNOTATION
4307 1948 : && et != ElementType::CAPO
4308 1948 : && et != ElementType::STRING_TUNINGS
4309 1948 : && et != ElementType::STICKING
4310 1933 : && et != ElementType::TREMOLO_SINGLECHORD
4311 1915 : && et != ElementType::TREMOLO_TWOCHORD
4312 1915 : && et != ElementType::ARPEGGIO
4313 1915 : && et != ElementType::CHORD_BRACKET
4314 1915 : && et != ElementType::SYMBOL
4315 1915 : && et != ElementType::IMAGE
4316 1915 : && et != ElementType::TREMOLOBAR
4317 1915 : && et != ElementType::FRET_DIAGRAM
4318 1890 : && et != ElementType::FERMATA
4319 1888 : && et != ElementType::HARMONY
4320 1864 : && et != ElementType::HARP_DIAGRAM
4321 1859 : && et != ElementType::FIGURED_BASS
4322 1859 : && et != ElementType::CLEF
4323 1850 : && et != ElementType::AMBITUS
4324 1850 : && et != ElementType::PLAY_COUNT_TEXT)
4325 : ) {
4326 7474 : doUndoAddElement(element);
4327 7474 : return;
4328 : }
4329 :
4330 546 : std::vector<Staff*> staves;
4331 546 : if (addToLinkedStaves) {
4332 543 : staves = ostaff->staffList();
4333 : } else {
4334 3 : staves.push_back(ostaff);
4335 : }
4336 :
4337 546 : if (elementToRelink) {
4338 2 : muse::remove(staves, ostaff);
4339 : }
4340 :
4341 1112 : for (Staff* staff : staves) {
4342 566 : Score* score = staff->score();
4343 566 : staff_idx_t staffIdx = staff->idx();
4344 :
4345 : // Some elements in voice 1 of a staff should be copied to every track which has a linked voice in this staff
4346 : static const std::set<ElementType> VOICE1_COPY_TYPES = {
4347 : ElementType::SYMBOL,
4348 : ElementType::IMAGE,
4349 : ElementType::TREMOLOBAR,
4350 : ElementType::DYNAMIC,
4351 : ElementType::EXPRESSION,
4352 : ElementType::STAFF_TEXT,
4353 : ElementType::PLAYTECH_ANNOTATION,
4354 : ElementType::CAPO,
4355 : ElementType::STRING_TUNINGS,
4356 : ElementType::STICKING,
4357 : ElementType::FRET_DIAGRAM,
4358 : ElementType::HARMONY,
4359 : ElementType::HAIRPIN,
4360 : ElementType::OTTAVA,
4361 : ElementType::TRILL,
4362 : ElementType::SLUR,
4363 : ElementType::HAMMER_ON_PULL_OFF,
4364 : ElementType::VIBRATO,
4365 : ElementType::TEXTLINE,
4366 : ElementType::PEDAL,
4367 : ElementType::LYRICS,
4368 : ElementType::PARTIAL_LYRICSLINE,
4369 : ElementType::CLEF,
4370 : ElementType::AMBITUS
4371 570 : };
4372 :
4373 566 : track_idx_t linkedTrack = ostaff->getLinkedTrackInStaff(staff, strack);
4374 566 : if (linkedTrack == muse::nidx) {
4375 0 : if (track2voice(strack) == 0 && muse::contains(VOICE1_COPY_TYPES, et)) {
4376 0 : linkedTrack = staff2track(staffIdx);
4377 : } else {
4378 0 : continue;
4379 : }
4380 : }
4381 :
4382 : EngravingItem* ne;
4383 566 : if (staff == ostaff) {
4384 544 : ne = element;
4385 : } else {
4386 22 : if (staff->rstaff() != ostaff->rstaff()) {
4387 1 : switch (element->type()) {
4388 : // exclude certain element types except on corresponding staff in part
4389 : // this should be same list excluded in cloneStaff()
4390 1 : case ElementType::STAFF_TEXT:
4391 : case ElementType::SYSTEM_TEXT:
4392 : case ElementType::TRIPLET_FEEL:
4393 : case ElementType::PLAYTECH_ANNOTATION:
4394 : case ElementType::CAPO:
4395 : case ElementType::STRING_TUNINGS:
4396 : case ElementType::FRET_DIAGRAM:
4397 : case ElementType::HARMONY:
4398 : case ElementType::FIGURED_BASS:
4399 : case ElementType::DYNAMIC:
4400 : case ElementType::EXPRESSION:
4401 : case ElementType::LYRICS: // not normally segment-attached
4402 : case ElementType::PARTIAL_LYRICSLINE:
4403 : case ElementType::PLAY_COUNT_TEXT:
4404 : case ElementType::WHAMMY_BAR:
4405 1 : continue;
4406 0 : default:
4407 0 : break;
4408 : }
4409 : }
4410 21 : ne = element->linkedClone();
4411 21 : ne->setScore(score);
4412 21 : ne->setSelected(false);
4413 21 : ne->setTrack(linkedTrack);
4414 :
4415 21 : if (ne->isFretDiagram()) {
4416 0 : FretDiagram* fd = toFretDiagram(ne);
4417 0 : Harmony* fdHarmony = fd->harmony();
4418 0 : if (fdHarmony) {
4419 : // TODO: this stuff should be done by overriding setScore setSelected and setTrack for FretDiagram [M.S.]
4420 0 : fdHarmony->setScore(score);
4421 0 : fdHarmony->setSelected(false);
4422 0 : fdHarmony->setTrack(linkedTrack);
4423 : }
4424 : }
4425 : }
4426 :
4427 565 : if (element->isArticulationFamily()) {
4428 50 : Articulation* a = toArticulation(element);
4429 : Segment* segment;
4430 : SegmentType st;
4431 : Measure* m;
4432 50 : Fraction tick;
4433 50 : if (a->ownershipParent()->isChordRest()) {
4434 50 : ChordRest* cr = a->chordRest();
4435 50 : segment = cr->segment();
4436 50 : st = SegmentType::ChordRest;
4437 50 : tick = segment->tick();
4438 50 : m = score->tick2measure(tick);
4439 : } else {
4440 0 : segment = toSegment(a->ownershipParent()->ownershipParent());
4441 0 : st = SegmentType::EndBarLine;
4442 0 : tick = segment->tick();
4443 0 : m = score->tick2measure(tick);
4444 0 : if (m->tick() == tick) {
4445 0 : m = m->prevMeasure();
4446 : }
4447 : }
4448 50 : Segment* seg = m->findSegment(st, tick);
4449 50 : if (seg == 0) {
4450 0 : LOGW("undoAddSegment: segment not found");
4451 0 : break;
4452 : }
4453 50 : Articulation* na = toArticulation(ne);
4454 50 : na->setTrack(linkedTrack);
4455 50 : if (a->ownershipParent()->isChordRest()) {
4456 50 : ChordRest* cr = a->chordRest();
4457 : ChordRest* ncr;
4458 50 : if (cr->isGrace()) {
4459 1 : ncr = findLinkedChord(toChord(cr), score->staff(staffIdx));
4460 : } else {
4461 49 : ncr = toChordRest(seg->element(linkedTrack));
4462 : }
4463 50 : na->setOwnershipParent(ncr);
4464 : } else {
4465 0 : BarLine* bl = toBarLine(seg->element(linkedTrack));
4466 0 : na->setOwnershipParent(bl);
4467 : }
4468 50 : doUndoAddElement(na);
4469 515 : } else if (element->isChordLine() || element->isLyrics()) {
4470 18 : ChordRest* cr = toChordRest(element->ownershipParent());
4471 18 : Segment* segment = cr->segment();
4472 18 : Fraction tick = segment->tick();
4473 18 : Measure* m = score->tick2measure(tick);
4474 18 : Segment* seg = m->findSegment(SegmentType::ChordRest, tick);
4475 18 : if (seg == 0) {
4476 0 : LOGW("undoAddSegment: segment not found");
4477 0 : break;
4478 : }
4479 18 : ne->setTrack(linkedTrack);
4480 18 : ChordRest* ncr = toChordRest(seg->element(linkedTrack));
4481 18 : ne->setOwnershipParent(ncr);
4482 18 : if (element->isChordLine()) {
4483 6 : if (cr->isGrace()) {
4484 0 : ncr = findLinkedChord(toChord(cr), score->staff(staffIdx));
4485 0 : ne->setOwnershipParent(ncr);
4486 : }
4487 6 : ChordLine* oldChordLine = toChordLine(element);
4488 6 : ChordLine* newChordLine = toChordLine(ne);
4489 : // Chordline also needs to know the new note
4490 6 : Note* newNote = toChord(ncr)->findNote(oldChordLine->note()->pitch());
4491 6 : newChordLine->setNote(newNote);
4492 : }
4493 18 : doUndoAddElement(ne);
4494 497 : } else if (element->isHarmony() && element->ownershipParent()->isFretDiagram()) {
4495 1 : EngravingItem* parentFd = element->ownershipParentItem();
4496 1 : if (parentFd->score() != score) {
4497 : // Find linked fret diagram
4498 0 : EngravingItem* linkedFd = parentFd->findLinkedInScore(score);
4499 0 : ne->setOwnershipParent(linkedFd);
4500 : }
4501 1 : doUndoAddElement(ne);
4502 : }
4503 : //
4504 : // elements with Segment as parent
4505 : //
4506 496 : else if (element->isSymbol()
4507 496 : || element->isImage()
4508 496 : || element->isTremoloBar()
4509 496 : || element->isDynamic()
4510 470 : || element->isExpression()
4511 470 : || element->isStaffText()
4512 442 : || element->isStaveSharingLabel()
4513 440 : || element->isPlayTechAnnotation()
4514 440 : || element->isCapo()
4515 440 : || element->isStringTunings()
4516 440 : || element->isSticking()
4517 425 : || element->isFretDiagram()
4518 400 : || element->isFermata()
4519 397 : || element->isHarmony() // Case with parent FretDiagram handled above, so we know it has parent Segment
4520 373 : || element->isHarpPedalDiagram()
4521 368 : || element->isFiguredBass()
4522 368 : || element->isClef()
4523 992 : || element->isAmbitus()) {
4524 139 : Segment* segment = toSegment(element->ownershipParent());
4525 139 : Fraction tick = segment->tick();
4526 139 : Measure* m = score->tick2measure(tick);
4527 139 : bool addClefToPrevMeasure = segment->isType(SegmentType::Clef) && element->isClef() && !toClef(element)->isHeader();
4528 139 : bool addBlToPrevMeasure = segment->isType(SegmentType::EndBarLine);
4529 139 : if (m->tick() == tick && (addClefToPrevMeasure || addBlToPrevMeasure) && m->prevMeasure()) {
4530 1 : m = m->prevMeasure();
4531 : }
4532 139 : Segment* seg = m->undoGetSegment(segment->segmentType(), tick);
4533 139 : ne->setTrack(linkedTrack);
4534 139 : ne->setOwnershipParent(seg);
4535 :
4536 139 : if (ne->isStringTunings()) {
4537 0 : StringTunings* stringTunings = toStringTunings(ne);
4538 0 : if (stringTunings->stringData()->isNull()) {
4539 0 : const StringData* stringData = stringTunings->originPart()->stringData(tick, staff->idx());
4540 0 : int frets = stringData->frets();
4541 0 : std::vector<mu::engraving::instrString> stringList = stringData->stringList();
4542 :
4543 0 : stringTunings->setStringData(StringData(frets, stringList));
4544 0 : }
4545 : }
4546 :
4547 139 : doUndoAddElement(ne);
4548 : // transpose harmony if necessary
4549 139 : if (element->isHarmony() && ne != element) {
4550 1 : Harmony* h = toHarmony(ne);
4551 1 : if (score->style().styleB(Sid::concertPitch) != element->style().styleB(Sid::concertPitch)) {
4552 0 : Staff* staffDest = h->staff();
4553 0 : Interval interval = staffDest->transpose(tick);
4554 0 : if (!interval.isZero()) {
4555 0 : if (!score->style().styleB(Sid::concertPitch)) {
4556 0 : interval.flip();
4557 : }
4558 0 : Transpose::undoTransposeHarmony(tx, h, interval);
4559 : }
4560 : }
4561 : }
4562 :
4563 : // Match vertical align settings of exiting items
4564 139 : if (ne->isHarmony() || ne->isFretDiagram()) {
4565 49 : bool exclude = ne->excludeVerticalAlign();
4566 49 : for (EngravingItem* item : seg->annotations()) {
4567 73 : if ((!item->isFretDiagram() && !item->isHarmony())
4568 73 : || item->staffIdx() != ne->staffIdx()) {
4569 0 : continue;
4570 : }
4571 :
4572 49 : exclude = item->excludeVerticalAlign();
4573 49 : break;
4574 : }
4575 49 : if (exclude != ne->excludeVerticalAlign()) {
4576 0 : ne->undoChangeProperty(Pid::EXCLUDE_VERTICAL_ALIGN, exclude);
4577 : }
4578 : }
4579 357 : } else if (element->isSlur()
4580 326 : || element->isHairpin()
4581 309 : || element->isOttava()
4582 305 : || element->isTrill()
4583 303 : || element->isVibrato()
4584 303 : || element->isTextLine()
4585 303 : || element->isPedal()
4586 301 : || element->isLetRing()
4587 301 : || element->isPalmMute()
4588 301 : || element->isWhammyBar()
4589 683 : || element->isPartialLyricsLine()) {
4590 56 : Spanner* sp = toSpanner(element);
4591 56 : Spanner* nsp = toSpanner(ne);
4592 56 : track_idx_t tr2 = sp->effectiveTrack2();
4593 56 : int diff = static_cast<int>(tr2 - sp->track());
4594 56 : nsp->setTrack2(linkedTrack + diff);
4595 56 : nsp->setTrack(linkedTrack);
4596 :
4597 : // determine start/end element for slurs
4598 : // this is only necessary if start/end element is
4599 : // a grace note, otherwise the element can be set to zero
4600 : // and will later be calculated from tick/track values
4601 : //
4602 56 : if (element->isSlur() && sp != nsp) {
4603 1 : if (sp->startElement()) {
4604 1 : std::list<EngravingObject*> sel = sp->startElement()->linkList();
4605 2 : for (EngravingObject* ee : sel) {
4606 2 : EngravingItem* e = static_cast<EngravingItem*>(ee);
4607 2 : if (e->score() == nsp->score() && e->track() == nsp->track()) {
4608 1 : nsp->setStartElement(e);
4609 1 : break;
4610 : }
4611 : }
4612 1 : }
4613 1 : if (sp->endElement()) {
4614 1 : std::list<EngravingObject*> eel = sp->endElement()->linkList();
4615 2 : for (EngravingObject* ee : eel) {
4616 2 : EngravingItem* e = static_cast<EngravingItem*>(ee);
4617 2 : if (e->score() == nsp->score() && e->track() == nsp->track2()) {
4618 1 : nsp->setEndElement(e);
4619 1 : break;
4620 : }
4621 : }
4622 1 : }
4623 : }
4624 :
4625 56 : if (sp->isTextLine() && sp != nsp) {
4626 0 : EngravingItem* parent = sp->ownershipParentItem();
4627 0 : if (parent && parent->isNote()) {
4628 0 : nsp->setOwnershipParent(parent->findLinkedInStaff(staff));
4629 : }
4630 0 : EngravingItem* endEl = sp->endElement();
4631 0 : if (endEl && endEl->isNote()) {
4632 0 : nsp->setEndElement(endEl->findLinkedInStaff(staff));
4633 : }
4634 : }
4635 :
4636 56 : doUndoAddElement(nsp);
4637 301 : } else if (et == ElementType::GLISSANDO || et == ElementType::GUITAR_BEND || et == ElementType::NOTELINE) {
4638 0 : doUndoAddElement(toSpanner(ne));
4639 301 : } else if (element->isType(ElementType::TREMOLO_TWOCHORD)) {
4640 0 : TremoloTwoChord* tremolo = item_cast<TremoloTwoChord*>(element);
4641 0 : ChordRest* cr1 = toChordRest(tremolo->chord1());
4642 0 : ChordRest* cr2 = toChordRest(tremolo->chord2());
4643 0 : int diff = static_cast<int>(cr2->track() - cr1->track());
4644 0 : Segment* s1 = cr1->segment();
4645 0 : Segment* s2 = cr2->segment();
4646 0 : Measure* m1 = s1->measure();
4647 0 : Measure* m2 = s2->measure();
4648 0 : Measure* nm1 = score->tick2measure(m1->tick());
4649 0 : Measure* nm2 = score->tick2measure(m2->tick());
4650 0 : Segment* ns1 = nm1->findSegment(s1->segmentType(), s1->tick());
4651 0 : Segment* ns2 = nm2->findSegment(s2->segmentType(), s2->tick());
4652 0 : Chord* c1 = toChord(ns1->element(linkedTrack));
4653 0 : Chord* c2 = toChord(ns2->element(linkedTrack + diff));
4654 0 : TremoloTwoChord* ntremolo = item_cast<TremoloTwoChord*>(ne);
4655 0 : ntremolo->setChords(c1, c2);
4656 0 : ntremolo->setOwnershipParent(c1);
4657 0 : doUndoAddElement(ntremolo);
4658 301 : } else if (element->isType(ElementType::TREMOLO_SINGLECHORD)) {
4659 18 : Chord* cr = toChord(element->ownershipParent());
4660 18 : Chord* c1 = findLinkedChord(cr, score->staff(staffIdx));
4661 18 : ne->setOwnershipParent(c1);
4662 18 : doUndoAddElement(ne);
4663 283 : } else if (element->isArpeggio() || element->isChordBracket()) {
4664 0 : ChordRest* cr = toChordRest(element->ownershipParent());
4665 0 : Segment* s = cr->segment();
4666 0 : Measure* m = s->measure();
4667 0 : Measure* nm = score->tick2measure(m->tick());
4668 0 : Segment* ns = nm->findSegment(s->segmentType(), s->tick());
4669 0 : Chord* c1 = toChord(ns->element(linkedTrack));
4670 0 : ne->setOwnershipParent(c1);
4671 0 : doUndoAddElement(ne);
4672 283 : } else if (element->isTie()) {
4673 259 : Tie* tie = toTie(element);
4674 259 : Note* n1 = tie->startNote();
4675 259 : Note* n2 = tie->endNote();
4676 259 : Chord* cr1 = n1->chord();
4677 259 : Chord* cr2 = n2 ? n2->chord() : 0;
4678 :
4679 : // find corresponding notes in linked staff
4680 : // accounting for grace notes and cross-staff notation
4681 259 : int sm = 0;
4682 259 : if (cr1->staffIdx() != cr2->staffIdx()) {
4683 0 : sm = static_cast<int>(cr2->staffIdx() - cr1->staffIdx());
4684 : }
4685 259 : Chord* c1 = findLinkedChord(cr1, score->staff(staffIdx));
4686 259 : Chord* c2 = findLinkedChord(cr2, score->staff(staffIdx + sm));
4687 :
4688 259 : IF_ASSERT_FAILED(c1) {
4689 0 : return;
4690 : }
4691 :
4692 259 : Note* nn1 = c1->findNote(n1->pitch(), n1->unisonIndex());
4693 259 : Note* nn2 = c2 ? c2->findNote(n2->pitch(), n2->unisonIndex()) : 0;
4694 :
4695 259 : const track_idx_t track1 = c1->track();
4696 259 : const track_idx_t track2 = c2 ? c2->track() : track1;
4697 :
4698 : // create tie
4699 259 : Tie* ntie = toTie(ne);
4700 259 : ntie->eraseSpannerSegments();
4701 259 : ntie->setTrack(track1);
4702 259 : ntie->setTrack2(track2);
4703 259 : ntie->setStartNote(nn1);
4704 259 : ntie->setEndNote(nn2);
4705 259 : doUndoAddElement(ntie);
4706 24 : } else if (element->isInstrumentChange()) {
4707 4 : InstrumentChange* is = toInstrumentChange(element);
4708 4 : Segment* s1 = is->segment();
4709 4 : Measure* m1 = s1->measure();
4710 4 : Measure* nm1 = score->tick2measure(m1->tick());
4711 4 : Segment* ns1 = nm1->findSegment(s1->segmentType(), s1->tick());
4712 4 : InstrumentChange* nis = toInstrumentChange(ne);
4713 4 : nis->setOwnershipParent(ns1);
4714 4 : Fraction tickStart = nis->segment()->tick();
4715 4 : Part* part = nis->part();
4716 4 : Interval oldV = nis->staff()->transpose(tickStart);
4717 : // ws: instrument should not be changed here
4718 4 : if (is->instrument()->channel().empty() || is->instrument()->channel(0)->program() == -1) {
4719 1 : nis->setInstrument(*staff->part()->instrument(s1->tick()));
4720 3 : } else if (nis != is) {
4721 0 : nis->setInstrument(*is->instrument());
4722 : }
4723 4 : doUndoAddElement(nis);
4724 : // transpose root score; parts will follow
4725 4 : if (score->isMaster() && nis->staff()->transpose(tickStart) != oldV) {
4726 1 : auto i = part->instruments().upper_bound(tickStart.ticks());
4727 1 : Fraction tickEnd = i == part->instruments().end() ? Fraction(-1, 1) : Fraction::fromTicks(i->first);
4728 1 : Transpose::transpositionChanged(tx, this, part, oldV, tickStart, tickEnd);
4729 : }
4730 20 : } else if (element->isBreath()) {
4731 20 : Breath* breath = toBreath(element);
4732 20 : Fraction tick = breath->segment()->tick();
4733 20 : Measure* m = score->tick2measure(tick);
4734 : // breath appears before barline
4735 20 : if (m->tick() == tick) {
4736 6 : m = m->prevMeasure();
4737 : }
4738 20 : Segment* seg = m->undoGetSegment(SegmentType::Breath, tick);
4739 20 : Breath* nbreath = toBreath(ne);
4740 20 : nbreath->setScore(score);
4741 20 : nbreath->setTrack(linkedTrack);
4742 20 : nbreath->setOwnershipParent(seg);
4743 20 : doUndoAddElement(nbreath);
4744 : } else {
4745 0 : LOGW("undoAddElement: unhandled: <%s>", element->typeName());
4746 : }
4747 565 : ne->styleChanged();
4748 :
4749 565 : if (elementToRelink) {
4750 2 : LinkedObjects* links = ne->links();
4751 2 : if (!links) {
4752 0 : ne->linkTo(elementToRelink);
4753 : } else {
4754 2 : elementToRelink->setLinks(links);
4755 2 : links->push_back(elementToRelink);
4756 : }
4757 : }
4758 : }
4759 546 : }
4760 :
4761 75 : FBox* Score::findFretBox() const
4762 : {
4763 275 : for (MeasureBase* measure = first(); measure; measure = measure->next()) {
4764 202 : if (measure->isFBox()) {
4765 2 : return toFBox(measure);
4766 : }
4767 : }
4768 :
4769 73 : return nullptr;
4770 : }
4771 :
4772 75 : void Score::rebuildFretBox()
4773 : {
4774 75 : FBox* fretBox = findFretBox();
4775 75 : if (!fretBox) {
4776 73 : return;
4777 : }
4778 :
4779 2 : fretBox->setNeedsRebuild(true);
4780 2 : fretBox->triggerLayout();
4781 : }
4782 :
4783 : //---------------------------------------------------------
4784 : // undoAddCR
4785 : //---------------------------------------------------------
4786 :
4787 5304 : void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
4788 : {
4789 5304 : assert(!cr->isChord() || !(toChord(cr)->notes()).empty());
4790 5304 : if (!cr->lyrics().empty()) {
4791 : // Add chordrest and lyrics separately for correct
4792 : // handling of adding lyrics to linked staves.
4793 1 : std::vector<Lyrics*> lyrics;
4794 1 : std::swap(lyrics, cr->lyrics());
4795 1 : undoAddCR(cr, measure, tick);
4796 2 : for (Lyrics* l : lyrics) {
4797 1 : undoAddElement(l);
4798 : }
4799 1 : return;
4800 1 : }
4801 :
4802 5303 : Staff* ostaff = cr->staff();
4803 5303 : track_idx_t strack = cr->track();
4804 :
4805 5303 : SegmentType segmentType = SegmentType::ChordRest;
4806 :
4807 10699 : for (const Staff* staff : ostaff->staffList()) {
4808 5396 : track_idx_t linkedTrack = ostaff->getLinkedTrackInStaff(staff, strack);
4809 :
4810 5396 : if (linkedTrack == muse::nidx) {
4811 2 : continue;
4812 : }
4813 :
4814 5394 : const TrackRange trackRange = staff->part()->trackRange();
4815 5394 : if (linkedTrack < trackRange.startTrack || linkedTrack >= trackRange.endTrack) {
4816 0 : continue;
4817 : }
4818 :
4819 5394 : Score* score = staff->score();
4820 5394 : Measure* m = (score == this) ? measure : score->tick2measure(tick);
4821 5394 : if (!m) {
4822 0 : LOGD("measure not found");
4823 0 : break;
4824 : }
4825 5394 : Segment* seg = m->undoGetSegment(segmentType, tick);
4826 :
4827 5394 : assert(seg->segmentType() == segmentType);
4828 :
4829 5394 : ChordRest* newcr = (staff == ostaff) ? cr : toChordRest(cr->linkedClone());
4830 5394 : newcr->setScore(score);
4831 :
4832 5394 : newcr->setTrack(linkedTrack);
4833 5394 : newcr->setOwnershipParent(seg);
4834 :
4835 : #ifndef QT_NO_DEBUG
4836 5394 : if (newcr->isChord()) {
4837 1937 : Chord* chord = toChord(newcr);
4838 : // setTpcFromPitch needs to know the note tick position
4839 6046 : for (Note* note : chord->notes()) {
4840 : // if (note->tpc() == Tpc::TPC_INVALID)
4841 : // note->setTpcFromPitch();
4842 4109 : assert(note->tpc() != Tpc::TPC_INVALID);
4843 : }
4844 : }
4845 : #endif
4846 : // Climb up the (possibly nested) tuplets from this chordRest
4847 : // Make sure all tuplets are cloned and correctly nested
4848 5394 : DurationElement* elementBelow = cr;
4849 5394 : Tuplet* tupletAbove = elementBelow->tuplet();
4850 5403 : while (tupletAbove) {
4851 491 : DurationElement* linkedElementBelow = (DurationElement*)elementBelow->findLinkedInStaff(staff);
4852 491 : if (!linkedElementBelow) { // shouldn't happen
4853 482 : break;
4854 : }
4855 9 : Tuplet* linkedTuplet = (Tuplet*)tupletAbove->findLinkedInStaff(staff);
4856 9 : if (!linkedTuplet) {
4857 2 : linkedTuplet = toTuplet(tupletAbove->linkedClone());
4858 2 : linkedTuplet->setScore(score);
4859 2 : linkedTuplet->setTrack(newcr->track());
4860 2 : linkedTuplet->setOwnershipParent(m);
4861 : }
4862 9 : linkedElementBelow->setTuplet(linkedTuplet);
4863 :
4864 9 : elementBelow = tupletAbove;
4865 9 : tupletAbove = tupletAbove->tuplet();
4866 : }
4867 :
4868 5394 : if (newcr->isRest() && (toRest(newcr)->isGap()) && !(toRest(newcr)->track() % VOICES)) {
4869 575 : toRest(newcr)->setGap(false);
4870 : }
4871 :
4872 5394 : doUndoAddElement(newcr);
4873 5303 : }
4874 : }
4875 :
4876 : //---------------------------------------------------------
4877 : // undoRemoveElement
4878 : //---------------------------------------------------------
4879 :
4880 6769 : void Score::undoRemoveElement(EngravingItem* element, bool removeLinked)
4881 : {
4882 6769 : if (!element) {
4883 201 : return;
4884 : }
4885 6568 : std::vector<Segment*> segments;
4886 13205 : for (EngravingObject* ee : element->linkList()) {
4887 6637 : EngravingItem* e = static_cast<EngravingItem*>(ee);
4888 6637 : if (e == element || removeLinked) {
4889 6631 : doUndoRemoveElement(e);
4890 :
4891 6631 : if (e->ownershipParent() && (e->ownershipParent()->isSegment())) {
4892 3937 : Segment* s = toSegment(e->ownershipParent());
4893 3937 : if (!muse::contains(segments, s)) {
4894 3935 : segments.push_back(s);
4895 : }
4896 : }
4897 6631 : if (e->ownershipParent() && e->ownershipParent()->isSystem()) {
4898 0 : e->setOwnershipParent(0); // systems will be regenerated upon redo, so detach
4899 : }
4900 : }
4901 6568 : }
4902 :
4903 6568 : if (element->isMeasureRepeat()) {
4904 0 : const MeasureRepeat* repeat = toMeasureRepeat(element);
4905 0 : Measure* measure = repeat->firstMeasureOfGroup();
4906 0 : size_t staffIdx = repeat->staffIdx();
4907 :
4908 0 : if (measure) {
4909 0 : for (int i = 0; i < repeat->numMeasures(); ++i) {
4910 0 : undoChangeMeasureRepeatCount(measure, 0, staffIdx);
4911 0 : measure = measure->nextMeasure();
4912 : }
4913 : }
4914 : }
4915 :
4916 10503 : for (Segment* s : segments) {
4917 3935 : if (s->empty()) {
4918 1708 : if (s->header() || s->trailer()) { // probably more segment types (system header)
4919 8 : s->setEnabled(false);
4920 : } else {
4921 1700 : doUndoRemoveElement(s);
4922 : }
4923 : }
4924 : }
4925 6568 : }
4926 :
4927 0 : void Score::undoRemoveHopoText(HammerOnPullOffText* hopoText)
4928 : {
4929 0 : Chord* startChord = hopoText->startChord();
4930 0 : Chord* endChord = hopoText->endChord();
4931 0 : IF_ASSERT_FAILED(startChord && endChord) {
4932 0 : return;
4933 : }
4934 :
4935 0 : HammerOnPullOffSegment* hopoSegment = hopoText->hopoSegment();
4936 0 : HammerOnPullOff* hopo = hopoSegment ? hopoSegment->hammerOnPullOff() : nullptr;
4937 0 : IF_ASSERT_FAILED(hopo) {
4938 0 : return;
4939 : }
4940 :
4941 0 : Chord* hopoStartChord = toChord(hopo->startElement());
4942 0 : Chord* hopoEndChord = toChord(hopo->endElement());
4943 0 : IF_ASSERT_FAILED(hopoStartChord && hopoEndChord) {
4944 0 : return;
4945 : }
4946 :
4947 0 : if (startChord == hopoStartChord && endChord == hopoEndChord) {
4948 0 : undoRemoveElement(hopo);
4949 0 : return;
4950 : }
4951 :
4952 0 : Fraction hopoStartTick = hopo->tick();
4953 0 : Fraction hopoEndTick = hopo->tick2();
4954 0 : Fraction hopoTextStartTick = startChord->tick();
4955 0 : Fraction hopoTextEndTick = endChord->tick();
4956 :
4957 0 : bool shortenFromStart = (hopoTextStartTick - hopoStartTick) < (hopoEndTick - hopoTextEndTick);
4958 0 : if (shortenFromStart) {
4959 0 : Fraction newStartTick = hopoTextEndTick;
4960 0 : Fraction newTicks = hopoEndTick - newStartTick;
4961 0 : hopo->undoChangeProperty(Pid::SPANNER_TICK, newStartTick);
4962 0 : hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
4963 0 : hopo->undoChangeStartEndElements(endChord, hopoEndChord);
4964 0 : if (startChord != hopoStartChord) {
4965 0 : HammerOnPullOff* newHopo = Factory::createHammerOnPullOff(score()->dummy());
4966 0 : newHopo->setTrack(hopo->track());
4967 0 : newHopo->setTick(hopoStartTick);
4968 0 : newHopo->setTick2(hopoTextStartTick);
4969 0 : newHopo->setStartElement(hopoStartChord);
4970 0 : newHopo->setEndElement(startChord);
4971 0 : score()->undoAddElement(newHopo);
4972 : }
4973 : } else {
4974 0 : Fraction newEndTick = hopoTextStartTick;
4975 0 : Fraction newTicks = newEndTick - hopoStartTick;
4976 0 : hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
4977 0 : hopo->undoChangeStartEndElements(hopoStartChord, startChord);
4978 0 : if (endChord != hopoEndChord) {
4979 0 : HammerOnPullOff* newHopo = new HammerOnPullOff(score()->dummy());
4980 0 : newHopo->setTrack(hopo->track());
4981 0 : newHopo->setTick(hopoTextEndTick);
4982 0 : newHopo->setTick2(hopoEndTick);
4983 0 : newHopo->setStartElement(endChord);
4984 0 : newHopo->setEndElement(hopoEndChord);
4985 0 : score()->undoAddElement(newHopo);
4986 : }
4987 : }
4988 : }
4989 :
4990 : //---------------------------------------------------------
4991 : // undoChangeSpannerElements
4992 : //---------------------------------------------------------
4993 :
4994 70 : void Score::undoChangeSpannerElements(Spanner* spanner, EngravingItem* startElement, EngravingItem* endElement)
4995 : {
4996 70 : EngravingItem* oldStartElement = spanner->startElement();
4997 70 : EngravingItem* oldEndElement = spanner->endElement();
4998 70 : track_idx_t startDeltaTrack = startElement && oldStartElement ? startElement->track() - oldStartElement->track() : 0;
4999 70 : track_idx_t endDeltaTrack = endElement && oldEndElement ? endElement->track() - oldEndElement->track() : 0;
5000 : // scan all spanners linked to this one
5001 164 : for (EngravingObject* el : spanner->linkList()) {
5002 94 : Spanner* sp = toSpanner(el);
5003 94 : EngravingItem* newStartElement = nullptr;
5004 94 : EngravingItem* newEndElement = nullptr;
5005 : // if not the current spanner, but one linked to it, determine its new start and end elements
5006 : // as modifications 'parallel' to the modifications of the current spanner's start and end elements
5007 94 : if (sp != spanner) {
5008 24 : if (startElement) {
5009 : // determine the track where to expect the 'parallel' start element
5010 12 : track_idx_t newTrack = sp->startElement() ? sp->startElement()->track() + startDeltaTrack : sp->track();
5011 : // look in elements linked to new start element for an element with
5012 : // same score as linked spanner and appropriate track
5013 24 : for (EngravingObject* ee : startElement->linkList()) {
5014 24 : EngravingItem* e = toEngravingItem(ee);
5015 24 : if (e->score() == sp->score() && e->track() == newTrack) {
5016 12 : newStartElement = e;
5017 12 : break;
5018 : }
5019 12 : }
5020 : }
5021 : // similarly to determine the 'parallel' end element
5022 24 : if (endElement) {
5023 12 : track_idx_t newTrack = sp->endElement() ? sp->endElement()->track() + endDeltaTrack : sp->track2();
5024 24 : for (EngravingObject* ee : endElement->linkList()) {
5025 24 : EngravingItem* e = toEngravingItem(ee);
5026 24 : if (e->score() == sp->score() && e->track() == newTrack) {
5027 12 : newEndElement = e;
5028 12 : break;
5029 : }
5030 12 : }
5031 : }
5032 : }
5033 : // if current spanner, just use stored start and end elements
5034 : else {
5035 70 : newStartElement = startElement;
5036 70 : newEndElement = endElement;
5037 : }
5038 94 : sp->score()->undo(new ChangeSpannerElements(sp, newStartElement, newEndElement));
5039 :
5040 94 : if (sp->isTie()) {
5041 0 : toTie(sp)->updatePossibleJumpPoints();
5042 : }
5043 70 : }
5044 70 : }
5045 :
5046 : //---------------------------------------------------------
5047 : // undoInsertTime
5048 : // acts on the linked scores as well
5049 : //---------------------------------------------------------
5050 :
5051 393 : void Score::undoInsertTime(const Fraction& tick, const Fraction& len)
5052 : {
5053 393 : if (len.isZero()) {
5054 9 : return;
5055 : }
5056 :
5057 384 : std::vector<Spanner*> spannersToAdjust;
5058 497 : for (const auto& i : m_spanner.map()) {
5059 113 : Spanner* s = i.second;
5060 113 : if (s->tick2() < tick) {
5061 45 : continue;
5062 : }
5063 68 : bool append = false;
5064 68 : if (len > Fraction(0, 1)) {
5065 34 : if (tick > s->tick() && tick < s->tick2()) {
5066 5 : append = true;
5067 29 : } else if (tick <= s->tick()) {
5068 20 : append = true;
5069 : }
5070 : } else {
5071 34 : Fraction tick2 = tick - len;
5072 34 : if (s->tick() >= tick2) {
5073 15 : append = true;
5074 19 : } else if (s->tick() >= tick && s->tick2() <= tick2) {
5075 6 : append = true;
5076 13 : } else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
5077 6 : Fraction t2 = s->tick2() + len;
5078 6 : if (t2 > s->tick()) {
5079 6 : append = true;
5080 : }
5081 7 : } else if (s->tick() > tick && s->tick2() > tick2) {
5082 2 : append = true;
5083 5 : } else if (s->tick() < tick && s->tick2() < tick2) {
5084 5 : append = true;
5085 : }
5086 : }
5087 98 : for (const Spanner* ss : spannersToAdjust) {
5088 30 : if (muse::contains(ss->linkList(), static_cast<EngravingObject*>(s))) {
5089 0 : append = false;
5090 0 : break;
5091 : }
5092 : }
5093 68 : if (append) {
5094 59 : spannersToAdjust.push_back(s);
5095 : }
5096 : }
5097 443 : for (Spanner* s : spannersToAdjust) {
5098 59 : if (len > Fraction(0, 1)) {
5099 25 : if (tick == s->tick() && s->isVolta()) {
5100 0 : s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
5101 25 : } else if (tick > s->tick() && tick < s->tick2()) {
5102 : //
5103 : // case a:
5104 : // +----spanner--------+
5105 : // +---add---
5106 : //
5107 5 : s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
5108 20 : } else if (tick <= s->tick()) {
5109 : //
5110 : // case b:
5111 : // +----spanner--------
5112 : // +---add---
5113 : // and
5114 : // +----spanner--------
5115 : // +---add---+
5116 20 : EngravingItem* startElement = s->startElement();
5117 20 : EngravingItem* endElement = s->endElement();
5118 20 : undoChangeSpannerElements(s, nullptr, nullptr);
5119 20 : s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() + len);
5120 20 : undoChangeSpannerElements(s, startElement, endElement);
5121 : }
5122 : } else {
5123 34 : Fraction tick2 = tick - len;
5124 34 : if (s->tick() >= tick2) {
5125 : //
5126 : // case A:
5127 : // +----remove---+ +---spanner---+
5128 : //
5129 15 : Fraction t = s->tick() + len;
5130 15 : if (t < Fraction(0, 1)) {
5131 0 : t = Fraction(0, 1);
5132 : }
5133 15 : EngravingItem* startElement = s->startElement();
5134 15 : EngravingItem* endElement = s->endElement();
5135 15 : undoChangeSpannerElements(s, nullptr, nullptr);
5136 15 : s->undoChangeProperty(Pid::SPANNER_TICK, t);
5137 15 : undoChangeSpannerElements(s, startElement, endElement);
5138 19 : } else if (s->tick() >= tick && s->tick2() <= tick2) {
5139 : //
5140 : // case B:
5141 : // +---spanner---+
5142 : // +----remove--------+
5143 : //
5144 6 : undoRemoveElement(s);
5145 13 : } else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
5146 : //
5147 : // case C:
5148 : // +----spanner--------+
5149 : // +---remove---+
5150 : //
5151 6 : Fraction t2 = s->tick2() + len;
5152 6 : if (t2 > s->tick()) {
5153 6 : s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
5154 : }
5155 7 : } else if (s->tick() > tick && s->tick2() > tick2) {
5156 : //
5157 : // case D:
5158 : // +----spanner--------+
5159 : // +---remove---+
5160 : //
5161 2 : Fraction d1 = s->tick() - tick;
5162 2 : Fraction d2 = tick2 - s->tick();
5163 2 : Fraction le = s->ticks() - d2;
5164 2 : if (le.isZero()) {
5165 0 : undoRemoveElement(s);
5166 : } else {
5167 2 : s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() - d1);
5168 2 : s->undoChangeProperty(Pid::SPANNER_TICKS, le);
5169 : }
5170 5 : } else if (s->tick() < tick && s->tick2() < tick2) {
5171 : //
5172 : // case E:
5173 : // +----spanner--------+
5174 : // +---remove---+
5175 : //
5176 5 : Fraction d = s->tick2() - tick;
5177 5 : Fraction le = s->ticks() - d;
5178 5 : if (le.isZero()) {
5179 0 : undoRemoveElement(s);
5180 : } else {
5181 5 : s->undoChangeProperty(Pid::SPANNER_TICKS, le);
5182 : }
5183 : }
5184 : }
5185 : }
5186 :
5187 384 : undo(new InsertTimeUnmanagedSpanner(this, tick, len));
5188 384 : }
5189 :
5190 : //---------------------------------------------------------
5191 : // undoRemoveMeasures
5192 : //---------------------------------------------------------
5193 :
5194 62 : void Score::undoRemoveMeasures(Measure* m1, Measure* m2, bool preserveTies, bool moveStaffTypeChanges)
5195 : {
5196 62 : assert(m1 && m2);
5197 :
5198 62 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
5199 :
5200 62 : const Fraction startTick = m1->tick();
5201 62 : const Fraction endTick = m2->endTick();
5202 62 : std::set<Spanner*> spannersToRemove;
5203 :
5204 : //
5205 : // handle ties which start before m1 and end in (m1-m2)
5206 : //
5207 581 : for (Segment* s = m1->first(); s != m2->last(); s = s->next1()) {
5208 519 : IF_ASSERT_FAILED(s) {
5209 : // This score is corrupted; handle it without crashing,
5210 : // to help the user to fix corruptions by deleting the affected measures
5211 0 : LOGE() << "Missing segments detected while deleting measures " << m1->measureNumber() << " to " << m2->measureNumber()
5212 0 : << ". This score (" << name() << ") is corrupted. Continuing without deleting measure contents.";
5213 0 : break;
5214 : }
5215 :
5216 519 : if (!s->isChordRestType()) {
5217 178 : continue;
5218 : }
5219 :
5220 : // Make sure annotations are removed once, even if this segment contains linked copies of the same annotation
5221 : // (the linked copy would be removed by undoRemoveElement)
5222 355 : while (!s->annotations().empty()) {
5223 14 : EngravingItem* annotation = s->annotations().front();
5224 14 : IF_ASSERT_FAILED(annotation) {
5225 0 : continue;
5226 : }
5227 14 : undoRemoveElement(annotation);
5228 : }
5229 :
5230 1989 : for (track_idx_t track = 0; track < ntracks(); ++track) {
5231 1648 : EngravingItem* e = s->element(track);
5232 1648 : if (!e || !e->isChord()) {
5233 1345 : continue;
5234 : }
5235 303 : Chord* c = toChord(e);
5236 610 : for (Note* n : c->notes()) {
5237 : // Remove ties crossing measure range boundaries
5238 307 : Tie* t = n->tieBackNonPartial();
5239 307 : if (t && t->startNote() && (t->startNote()->chord()->tick() < startTick)) {
5240 0 : if (preserveTies) {
5241 0 : t->setEndNote(0);
5242 : } else {
5243 0 : undoRemoveElement(t);
5244 : }
5245 : }
5246 307 : t = n->tieForNonPartial();
5247 307 : if (t && t->endNote() && (t->endNote()->chord()->tick() >= endTick)) {
5248 6 : undoRemoveElement(t);
5249 : }
5250 :
5251 : // Do the same for other note-anchored spanners (e.g. glissandi).
5252 : // Delay actual removing to avoid modifying lists inside loops over them.
5253 307 : for (Spanner* sb : n->spannerBack()) {
5254 0 : if (sb->tick() < startTick) {
5255 0 : spannersToRemove.insert(sb);
5256 : }
5257 : }
5258 307 : for (Spanner* sf : n->spannerFor()) {
5259 0 : if (sf->tick2() >= endTick) {
5260 0 : spannersToRemove.insert(sf);
5261 : }
5262 : }
5263 : }
5264 : }
5265 : }
5266 :
5267 62 : for (Spanner* s : spannersToRemove) {
5268 0 : undoRemoveElement(s);
5269 : }
5270 :
5271 : // delete staffTypeChanges in removed measures
5272 62 : if (moveStaffTypeChanges) {
5273 124 : for (Measure* m = m1; m && m != m2->nextMeasure(); m = m->nextMeasure()) {
5274 86 : for (size_t i = m->el().size(); i > 0; --i) {
5275 0 : EngravingItem* el = m->el().at(i - 1);
5276 0 : if (el && el->isStaffTypeChange()) {
5277 0 : deleteItem(el);
5278 : }
5279 : }
5280 : }
5281 : }
5282 :
5283 62 : EditSystemLocks::removeSystemLocksOnRemoveMeasures(tx, this, m1, m2);
5284 62 : EditPageLocks::removePageLocksOnRemoveMeasures(tx, this, m1, m2);
5285 :
5286 62 : undo(new RemoveMeasures(m1, m2, moveStaffTypeChanges));
5287 62 : }
5288 :
5289 : //---------------------------------------------------------
5290 : // undoChangeMeasureRepeatCount
5291 : //---------------------------------------------------------
5292 :
5293 18 : void Score::undoChangeMeasureRepeatCount(Measure* m, int newCount, staff_idx_t staffIdx)
5294 : {
5295 54 : for (Staff* st : staff(staffIdx)->staffList()) {
5296 36 : Score* linkedScore = st->score();
5297 36 : staff_idx_t linkedStaffIdx = st->idx();
5298 36 : Measure* linkedMeasure = linkedScore->tick2measure(m->tick());
5299 :
5300 36 : int currCount = linkedMeasure->measureRepeatCount(linkedStaffIdx);
5301 36 : if (currCount != newCount) {
5302 36 : linkedScore->undo(new ChangeMeasureRepeatCount(linkedMeasure, newCount, linkedStaffIdx));
5303 : }
5304 18 : }
5305 18 : }
5306 :
5307 1118 : void Score::doUndoRemoveStaleTieJumpPoints(Tie* tie, bool undo)
5308 : {
5309 3 : auto removeTie = [&](Tie* tie) {
5310 3 : if (undo) {
5311 3 : const size_t undoIdx = undoStack()->currentIndex();
5312 3 : startCmd(TranslatableString("engraving", "Remove stale partial tie"));
5313 3 : undoRemoveElement(tie);
5314 3 : endCmd();
5315 3 : if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
5316 : // These changes should be merged with the change in repeat structure which caused the ties to become invalid
5317 : // currentIndex returns the next empty index for an undo command
5318 2 : const size_t penultimateCmdIdx = undoStack()->currentIndex() - 2;
5319 2 : undoStack()->mergeTransactions(penultimateCmdIdx);
5320 : }
5321 : } else {
5322 0 : removeElement(tie);
5323 : }
5324 3 : };
5325 :
5326 1118 : std::vector<Tie*> oldTies;
5327 1118 : if (!tie->tieJumpPoints()) {
5328 0 : return;
5329 : }
5330 1162 : for (TieJumpPoint* jumpPoint : *tie->tieJumpPoints()) {
5331 44 : if (jumpPoint->followingNote()) {
5332 9 : continue;
5333 : }
5334 35 : oldTies.push_back(jumpPoint->endTie());
5335 : }
5336 :
5337 : // Update jump points for linked ties
5338 2270 : for (EngravingObject* linkedTie : tie->linkList()) {
5339 1152 : if (!linkedTie || !linkedTie->isTie()) {
5340 0 : continue;
5341 : }
5342 1152 : toTie(linkedTie)->updatePossibleJumpPoints();
5343 1118 : }
5344 :
5345 1153 : for (Tie* oldTie : oldTies) {
5346 47 : auto findEndTie = [&oldTie](const TieJumpPoint* jumpPoint) {
5347 47 : return jumpPoint->endTie() == oldTie;
5348 35 : };
5349 35 : if (!oldTie) {
5350 35 : continue;
5351 : }
5352 :
5353 34 : if (std::find_if((*tie->tieJumpPoints()).begin(), (*tie->tieJumpPoints()).end(),
5354 68 : findEndTie) != (*tie->tieJumpPoints()).end()) {
5355 34 : continue;
5356 : }
5357 0 : removeTie(oldTie);
5358 : }
5359 :
5360 1118 : if (tie->isPartialTie() && tie->allJumpPointsInactive() && tie->startNote() && tie->startNote()->tieFor() == tie) {
5361 3 : removeTie(tie);
5362 : }
5363 1118 : }
5364 :
5365 339 : void Score::doUndoResetPartialSlur(Slur* slur, bool undo)
5366 : {
5367 339 : const size_t undoIdx = undoStack()->currentIndex();
5368 :
5369 339 : const ChordRest* startCR = slur ? slur->startCR() : nullptr;
5370 : // Slurs can only have the same start & end element when they are partial.
5371 : // If they are no longer partial, we should remove them
5372 339 : const bool shouldRemove = slur->startElement() == slur->endElement();
5373 339 : IF_ASSERT_FAILED(startCR) {
5374 0 : LOGE() << "Slur is corrupted";
5375 339 : } else if (!startCR->hasPrecedingJumpItem() && slur->isIncoming()) {
5376 3 : if (shouldRemove) {
5377 3 : if (undo) {
5378 0 : startCmd(TranslatableString("engraving", "Remove invalid incoming partial slur"));
5379 0 : undoRemoveElement(slur);
5380 0 : endCmd();
5381 : } else {
5382 3 : removeElement(slur);
5383 : }
5384 : } else {
5385 0 : if (undo) {
5386 0 : startCmd(TranslatableString("engraving", "Reset incoming partial slur"));
5387 0 : slur->undoSetIncoming(false);
5388 0 : endCmd();
5389 : } else {
5390 0 : slur->setIncoming(false);
5391 : }
5392 : }
5393 : }
5394 :
5395 339 : const ChordRest* endCR = slur ? slur->endCR() : nullptr;
5396 339 : IF_ASSERT_FAILED(endCR) {
5397 0 : LOGE() << "Slur is corrupted";
5398 339 : } else if (!endCR->hasFollowingJumpItem() && slur->isOutgoing()) {
5399 1 : if (shouldRemove) {
5400 0 : if (undo) {
5401 0 : startCmd(TranslatableString("engraving", "Remove invalid outgoing partial slur"));
5402 0 : undoRemoveElement(slur);
5403 0 : endCmd();
5404 : } else {
5405 0 : removeElement(slur);
5406 : }
5407 : } else {
5408 1 : if (undo) {
5409 0 : startCmd(TranslatableString("engraving", "Reset outgoing partial slur"));
5410 0 : slur->undoSetOutgoing(false);
5411 0 : endCmd();
5412 : } else {
5413 1 : slur->setOutgoing(false);
5414 : }
5415 : }
5416 : }
5417 :
5418 339 : if (!undo) {
5419 339 : return;
5420 : }
5421 :
5422 0 : if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
5423 : // These changes should be merged with the change in repeat structure which caused the ties to become invalid
5424 : // currentIdx returns the next empty index for an undo command
5425 0 : const size_t penultimateCmdIdx = undoStack()->currentIndex() - 2;
5426 0 : undoStack()->mergeTransactions(penultimateCmdIdx);
5427 : }
5428 : }
5429 :
5430 1159 : void Score::undoRemoveStaleTieJumpPoints(bool undo)
5431 : {
5432 1159 : size_t tracks = nstaves() * VOICES;
5433 1159 : Measure* m = firstMeasure();
5434 1159 : if (!m) {
5435 1 : return;
5436 : }
5437 :
5438 : // Copy, invalid slurs could be removed
5439 1158 : auto spannerMap = spanner();
5440 2045 : for (auto& interval : spannerMap) {
5441 887 : Spanner* sp = interval.second;
5442 887 : if (!sp || !sp->isSlur()) {
5443 548 : continue;
5444 : }
5445 339 : doUndoResetPartialSlur(toSlur(sp), undo);
5446 : }
5447 :
5448 1158 : std::set<PartialTie*> incomingPartialTies;
5449 1158 : SegmentType st = SegmentType::ChordRest;
5450 24936 : for (Segment* s = m->first(st); s; s = s->next1(st)) {
5451 156578 : for (track_idx_t i = 0; i < tracks; ++i) {
5452 132800 : EngravingItem* e = s->element(i);
5453 132800 : if (e == 0 || !e->isChordRest()) {
5454 99090 : continue;
5455 : }
5456 :
5457 33710 : if (!toChordRest(e)->hasFollowingJumpItem()) {
5458 : // Remove invalid lyrics dashes
5459 34372 : for (Lyrics* lyrics : toChordRest(e)->lyrics()) {
5460 1167 : LyricsLine* separator = lyrics->separator();
5461 2334 : if ((lyrics->syllabic() == LyricsSyllabic::BEGIN || lyrics->syllabic() == LyricsSyllabic::MIDDLE) && separator
5462 2334 : && !separator->nextLyrics() && !separator->isEndMelisma()) {
5463 0 : lyrics->setNeedRemoveInvalidSegments();
5464 : }
5465 : }
5466 : }
5467 :
5468 33710 : if (!e->isChord()) {
5469 15039 : continue;
5470 : }
5471 :
5472 18671 : Chord* c = toChord(e);
5473 41125 : for (Note* n : c->notes()) {
5474 22454 : if (n->incomingPartialTie()) {
5475 36 : incomingPartialTies.emplace(n->incomingPartialTie());
5476 : }
5477 22454 : if (!n->tieFor()) {
5478 21336 : continue;
5479 : }
5480 :
5481 1118 : doUndoRemoveStaleTieJumpPoints(n->tieFor());
5482 : }
5483 : }
5484 : }
5485 :
5486 1194 : for (PartialTie* incomingPT : incomingPartialTies) {
5487 36 : if (incomingPT->jumpPoint() || incomingPT->note()->tieBack() != incomingPT) {
5488 32 : continue;
5489 : }
5490 4 : const size_t undoIdx = undoStack()->currentIndex();
5491 4 : if (undo) {
5492 0 : startCmd(TranslatableString("engraving", "Remove stale partial tie"));
5493 0 : undoRemoveElement(incomingPT);
5494 0 : endCmd();
5495 0 : if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
5496 0 : undoStack()->mergeTransactions(undoStack()->currentIndex() - 2);
5497 : }
5498 : } else {
5499 4 : removeElement(incomingPT);
5500 : }
5501 : }
5502 1158 : }
5503 : }
|