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 : /**
24 : \file
25 : Handling of several GUI commands.
26 : */
27 :
28 : #include <assert.h>
29 : #include <set>
30 :
31 : #include "translation.h"
32 :
33 : #include "infrastructure/messagebox.h"
34 : #include "style/style.h"
35 :
36 : #include "rw/xmlreader.h"
37 :
38 : #include "../dom/accidental.h"
39 : #include "../dom/articulation.h"
40 : #include "../dom/barline.h"
41 : #include "../dom/box.h"
42 : #include "../dom/chord.h"
43 : #include "../dom/chordrest.h"
44 : #include "../dom/clef.h"
45 : #include "../dom/drumset.h"
46 : #include "../dom/durationtype.h"
47 : #include "../dom/dynamic.h"
48 : #include "../dom/factory.h"
49 : #include "../dom/glissando.h"
50 : #include "../dom/guitarbend.h"
51 : #include "../dom/hairpin.h"
52 : #include "../dom/harmony.h"
53 : #include "../dom/key.h"
54 : #include "../dom/laissezvib.h"
55 : #include "../dom/layoutbreak.h"
56 : #include "../dom/linkedobjects.h"
57 : #include "../dom/lyrics.h"
58 : #include "../dom/masterscore.h"
59 : #include "../dom/measure.h"
60 : #include "../dom/measurerepeat.h"
61 : #include "../dom/mscore.h"
62 : #include "../dom/note.h"
63 : #include "../dom/ornament.h"
64 : #include "../dom/page.h"
65 : #include "../dom/part.h"
66 : #include "../dom/pitchspelling.h"
67 : #include "../dom/rehearsalmark.h"
68 : #include "../dom/rest.h"
69 : #include "../dom/score.h"
70 : #include "../dom/segment.h"
71 : #include "../dom/sig.h"
72 : #include "../dom/slur.h"
73 : #include "../dom/spacer.h"
74 : #include "../dom/staff.h"
75 : #include "../dom/stafftype.h"
76 : #include "../dom/stafftypechange.h"
77 : #include "../dom/stem.h"
78 : #include "../dom/stringdata.h"
79 : #include "../dom/system.h"
80 : #include "../dom/tapping.h"
81 : #include "../dom/tie.h"
82 : #include "../dom/timesig.h"
83 : #include "../dom/tremolotwochord.h"
84 : #include "../dom/tuplet.h"
85 : #include "../dom/utils.h"
86 :
87 : #include "editchord.h"
88 : #include "editnote.h"
89 : #include "noteinput.h"
90 : #include "editpagelocks.h"
91 : #include "editproperty.h"
92 : #include "editspanner.h"
93 : #include "editstaff.h"
94 : #include "editsystemlocks.h"
95 : #include "mscoreview.h"
96 : #include "navigation.h"
97 : #include "transaction/transaction.h"
98 : #include "transaction/undostack.h"
99 : #include "transpose.h"
100 :
101 : #include "log.h"
102 :
103 : using namespace muse::io;
104 : using namespace mu::engraving;
105 :
106 : namespace mu::engraving {
107 : //---------------------------------------------------------
108 : // reset
109 : //---------------------------------------------------------
110 :
111 2819 : void CmdState::reset()
112 : {
113 2819 : layoutFlags = LayoutFlag::NO_FLAGS;
114 2819 : m_updateMode = UpdateMode::DoNothing;
115 2819 : m_startTick = Fraction(-1, 1);
116 2819 : m_endTick = Fraction(-1, 1);
117 :
118 2819 : m_startStaff = muse::nidx;
119 2819 : m_endStaff = muse::nidx;
120 2819 : m_el = nullptr;
121 2819 : m_oneElement = true;
122 2819 : m_mb = nullptr;
123 2819 : m_oneMeasureBase = true;
124 2819 : m_locked = false;
125 2819 : }
126 :
127 : //---------------------------------------------------------
128 : // setTick
129 : //---------------------------------------------------------
130 :
131 968330 : void CmdState::setTick(const Fraction& t)
132 : {
133 968330 : if (m_locked) {
134 595599 : return;
135 : }
136 :
137 372731 : if (m_startTick == Fraction(-1, 1) || t < m_startTick) {
138 3283 : m_startTick = t;
139 : }
140 372731 : if (m_endTick == Fraction(-1, 1) || t > m_endTick) {
141 10847 : m_endTick = t;
142 : }
143 372731 : setUpdateMode(UpdateMode::Layout);
144 : }
145 :
146 : //---------------------------------------------------------
147 : // setStaff
148 : //---------------------------------------------------------
149 :
150 901543 : void CmdState::setStaff(staff_idx_t st)
151 : {
152 901543 : if (m_locked || st == muse::nidx) {
153 818041 : return;
154 : }
155 :
156 83502 : if (m_startStaff == muse::nidx || st < m_startStaff) {
157 2385 : m_startStaff = st;
158 : }
159 83502 : if (m_endStaff == muse::nidx || st > m_endStaff) {
160 2883 : m_endStaff = st;
161 : }
162 : }
163 :
164 : //---------------------------------------------------------
165 : // setMeasureBase
166 : //---------------------------------------------------------
167 :
168 14723 : void CmdState::setMeasureBase(const MeasureBase* mb)
169 : {
170 14723 : if (!mb || m_mb == mb || m_locked) {
171 10572 : return;
172 : }
173 :
174 4151 : m_oneMeasureBase = !m_mb;
175 4151 : m_mb = mb;
176 : }
177 :
178 : //---------------------------------------------------------
179 : // setElement
180 : //---------------------------------------------------------
181 :
182 885109 : void CmdState::setElement(const EngravingItem* e)
183 : {
184 885109 : if (!e || m_el == e || m_locked) {
185 831338 : return;
186 : }
187 :
188 53771 : m_oneElement = !m_el;
189 53771 : m_el = e;
190 :
191 53771 : if (m_oneMeasureBase) {
192 14723 : setMeasureBase(e->findMeasureBase());
193 : }
194 : }
195 :
196 : //---------------------------------------------------------
197 : // unsetElement
198 : //---------------------------------------------------------
199 :
200 231241 : void CmdState::unsetElement(const EngravingItem* e)
201 : {
202 231241 : if (m_el == e) {
203 66 : m_el = nullptr;
204 : }
205 231241 : if (m_mb == e) {
206 0 : m_mb = nullptr;
207 : }
208 231241 : }
209 :
210 : //---------------------------------------------------------
211 : // element
212 : //---------------------------------------------------------
213 :
214 0 : const EngravingItem* CmdState::element() const
215 : {
216 0 : if (m_oneElement) {
217 0 : return m_el;
218 : }
219 0 : if (m_oneMeasureBase) {
220 0 : return m_mb;
221 : }
222 0 : return nullptr;
223 : }
224 :
225 : //---------------------------------------------------------
226 : // setUpdateMode
227 : //---------------------------------------------------------
228 :
229 3142 : void CmdState::_setUpdateMode(UpdateMode m)
230 : {
231 3142 : m_updateMode = m;
232 3142 : }
233 :
234 389993 : void CmdState::setUpdateMode(UpdateMode m)
235 : {
236 389993 : if (int(m) > int(m_updateMode)) {
237 3142 : _setUpdateMode(m);
238 : }
239 389993 : }
240 :
241 : //---------------------------------------------------------
242 : // deleteLater
243 : //---------------------------------------------------------
244 :
245 133 : void CmdState::deleteLater(EngravingObject* e)
246 : {
247 133 : m_postponedDeletions.push_back(e);
248 133 : }
249 :
250 1555 : std::vector<EngravingObject*> CmdState::takePostponedDeletions()
251 : {
252 1555 : std::vector<EngravingObject*> result;
253 1555 : result.swap(m_postponedDeletions);
254 1555 : return result;
255 : }
256 :
257 1555 : static void deletePostponed(CmdState& cmdState)
258 : {
259 1624 : for (EngravingObject* e : cmdState.takePostponedDeletions()) {
260 69 : if (e->isSystem()) {
261 23 : System* s = toSystem(e);
262 23 : std::list<SpannerSegment*> spanners = s->spannerSegments();
263 25 : for (SpannerSegment* ss : spanners) {
264 2 : if (ss->system() == s) {
265 2 : ss->moveToSystem(nullptr);
266 : }
267 : }
268 23 : }
269 69 : delete e;
270 1555 : }
271 1555 : }
272 :
273 : //---------------------------------------------------------
274 : // startCmd
275 : /// Start a GUI command by clearing the redraw area
276 : /// and starting a user-visible undo.
277 : //---------------------------------------------------------
278 :
279 523 : void Score::startCmd(const TranslatableString& actionName)
280 : {
281 523 : masterScore()->transactionManager()->beginTransaction(actionName);
282 523 : }
283 :
284 : //---------------------------------------------------------
285 : // undoRedo
286 : //---------------------------------------------------------
287 :
288 190 : void Score::undoRedo(bool undo, EditData* ed)
289 : {
290 190 : masterScore()->transactionManager()->undoRedo(undo, ed);
291 190 : }
292 :
293 : //---------------------------------------------------------
294 : // endCmd
295 : /// End a GUI command by (if \a undo) ending a user-visible undo
296 : /// and (always) updating the redraw area.
297 : //---------------------------------------------------------
298 :
299 526 : void Score::endCmd(bool rollback, bool layoutAllParts, bool keepRolledBackElements)
300 : {
301 526 : masterScore()->transactionManager()->endTransaction(rollback, layoutAllParts, keepRolledBackElements);
302 526 : }
303 :
304 : //---------------------------------------------------------
305 : // undo
306 : // Deprecated: use Transaction::push instead.
307 : //---------------------------------------------------------
308 :
309 37575 : void Score::undo(UndoableCommand* cmd) const
310 : {
311 37575 : Transaction* tx = masterScore()->transactionManager()->currentTransaction();
312 37575 : if (!tx) {
313 : // this can happen for layout() outside of a transaction (load)
314 17169 : if (!ScoreLoad::loading()) {
315 7840 : LOGW() << "called outside of transaction";
316 : }
317 :
318 17169 : cmd->redo();
319 17169 : delete cmd;
320 17169 : return;
321 : }
322 :
323 20406 : tx->push(cmd);
324 : }
325 :
326 : #ifndef NDEBUG
327 : //---------------------------------------------------------
328 : // CmdState::dump
329 : //---------------------------------------------------------
330 :
331 0 : void CmdState::dump()
332 : {
333 0 : LOGD("CmdState: mode %d %d-%d", int(m_updateMode), m_startTick.ticks(), m_endTick.ticks());
334 : // bool _excerptsChanged { false };
335 : // bool _instrumentsChanged { false };
336 0 : }
337 :
338 : #endif
339 :
340 : //---------------------------------------------------------
341 : // update
342 : // layout & update
343 : //---------------------------------------------------------
344 :
345 77 : void Score::update()
346 : {
347 77 : masterScore()->update();
348 77 : }
349 :
350 1555 : void MasterScore::update(bool resetCmdState, bool layoutAllParts)
351 : {
352 1555 : if (m_updatesLocked) {
353 0 : return;
354 : }
355 :
356 1558 : TRACEFUNC;
357 :
358 1555 : deletePostponed(m_cmdState);
359 :
360 1555 : bool updateAll = false;
361 1555 : if (m_cmdState.layoutRange()) {
362 2349 : for (Score* s : scoreList()) {
363 1289 : if (s != this && !s->isOpen() && scoreList().size() > 1 && !layoutAllParts) {
364 199 : continue;
365 : }
366 1090 : s->doLayoutRange(m_cmdState.startTick(), m_cmdState.endTick());
367 1060 : }
368 1060 : updateAll = true;
369 : }
370 :
371 1555 : if (needUpdateTicksAndTimeSigMap()) {
372 275 : updateTicksAndTimeSigMap();
373 : }
374 :
375 1555 : if (updateAll || m_cmdState.updateAll()) {
376 2457 : for (Score* s : scoreList()) {
377 1344 : for (MuseScoreView* v : s->getViewer()) {
378 0 : v->updateAll();
379 : }
380 1113 : }
381 442 : } else if (m_cmdState.updateRange()) {
382 : // Any score that accumulated a refresh rect via addRefresh() calls dataChanged() on its viewers.
383 60 : for (Score* s : scoreList()) {
384 30 : if (s->refreshRect().isNull()) {
385 0 : continue;
386 : }
387 30 : const std::list<MuseScoreView*>& viewers = s->getViewer();
388 30 : if (!viewers.empty()) {
389 0 : double d = s->style().spatium() * .5;
390 0 : RectF rect = s->refreshRect().adjusted(-d, -d, 2 * d, 2 * d);
391 0 : for (MuseScoreView* v : viewers) {
392 0 : v->dataChanged(rect);
393 : }
394 : }
395 30 : s->clearRefreshRect();
396 30 : }
397 : }
398 :
399 1555 : if (resetCmdState) {
400 87 : m_cmdState.reset();
401 : }
402 :
403 3344 : for (Score* score : scoreList()) {
404 1789 : Selection& sel = score->selection();
405 1789 : if (sel.isRange() && !sel.isLocked()) {
406 231 : sel.updateSelectedElements();
407 : }
408 1555 : }
409 1555 : }
410 :
411 0 : void MasterScore::lockUpdates(bool locked)
412 : {
413 0 : m_updatesLocked = locked;
414 0 : }
415 :
416 : //---------------------------------------------------------
417 : // expandVoice
418 : // fills gaps in voice with rests,
419 : // from previous cr (or beginning of measure) to next cr (or end of measure)
420 : //---------------------------------------------------------
421 :
422 170 : void Score::expandVoice(Segment* s, track_idx_t track)
423 : {
424 170 : if (!s || track == muse::nidx) {
425 0 : LOGD("expand voice: no segment or invalid track");
426 128 : return;
427 : }
428 170 : if (s->element(track)) {
429 128 : return;
430 : }
431 :
432 : // find previous segment with cr in this track
433 : Segment* ps;
434 91 : for (ps = s; ps; ps = ps->prev(SegmentType::ChordRest)) {
435 49 : if (ps->element(track)) {
436 0 : break;
437 : }
438 : }
439 42 : if (ps) {
440 0 : ChordRest* cr = toChordRest(ps->element(track));
441 0 : Fraction tick = cr->endTick();
442 0 : if (tick > s->tick()) {
443 : // previous cr extends past current segment
444 0 : LOGD("expandVoice: cannot insert element here");
445 0 : return;
446 : }
447 0 : if (cr->isChord()) {
448 : // previous cr ends on or before current segment
449 : // for chords, move ps to just after cr ends
450 : // so we can fill any gap that might exist
451 : // but don't move ps if previous cr is a rest
452 : // this will be combined with any new rests needed to fill up to s->tick() below
453 0 : ps = ps->measure()->undoGetSegment(SegmentType::ChordRest, tick);
454 : }
455 : }
456 : //
457 : // fill up to s->tick() with rests
458 : //
459 42 : Measure* m = s->measure();
460 42 : Fraction stick = ps ? ps->tick() : m->tick();
461 :
462 42 : const Staff* currStaff = staff(track2staff(track));
463 42 : IF_ASSERT_FAILED(currStaff) {
464 0 : return;
465 : }
466 42 : const Fraction stretch = currStaff->timeStretch(stick);
467 :
468 42 : Fraction ticks = s->tick() - stick;
469 42 : if (ticks.isNotZero()) {
470 5 : setRest(stick, track, ticks * stretch, false, 0);
471 : }
472 :
473 : //
474 : // fill from s->tick() until next chord/rest in measure
475 : //
476 : Segment* ns;
477 180 : for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::ChordRest)) {
478 140 : if (ns->element(track)) {
479 2 : break;
480 : }
481 : }
482 42 : ticks = ns ? (ns->tick() - s->tick()) : (m->ticks() - s->rtick());
483 42 : if (ticks == m->ticks()) {
484 35 : addRest(s, track, TDuration(DurationType::V_MEASURE), 0);
485 : } else {
486 7 : setRest(s->tick(), track, ticks * stretch, false, 0);
487 : }
488 : }
489 :
490 25 : void Score::expandVoice()
491 : {
492 25 : Segment* s = m_is.segment();
493 25 : track_idx_t track = m_is.track();
494 25 : expandVoice(s, track);
495 25 : }
496 :
497 : //---------------------------------------------------------
498 : // addInterval
499 : //---------------------------------------------------------
500 :
501 40 : void Score::addInterval(int val, const std::vector<Note*>& nl)
502 : {
503 : // Prepare note selection in case there are not selected tied notes and sort them
504 40 : std::vector<Note*> tmpnl;
505 40 : std::vector<Note*> _nl = nl;
506 40 : bool selIsList = selection().isList();
507 40 : bool selIsSingle = selIsList && _nl.size() == 1;
508 40 : bool shouldSelectFirstNote = selIsSingle && _nl.front()->tieFor();
509 :
510 40 : std::sort(_nl.begin(), _nl.end(), [](const Note* a, const Note* b) -> bool {
511 0 : return a->tick() < b->tick();
512 : });
513 :
514 80 : for (Note* currNote : _nl) {
515 80 : for (Note* n = currNote; n && !muse::contains(tmpnl, n); n = n->tieFor() ? n->tieFor()->endNote() : nullptr) {
516 40 : if (selIsList && n->selected()) {
517 40 : deselect(n);
518 : }
519 40 : tmpnl.emplace_back(n);
520 : }
521 : }
522 :
523 40 : Note* prevTied = nullptr;
524 40 : std::vector<EngravingItem*> notesToSelect;
525 40 : int deltaLine = val < 0 ? val + 1 : val - 1;
526 40 : bool accidental = m_is.noteEntryMode() && m_is.accidentalType() != AccidentalType::NONE;
527 40 : bool useOctaveRule = (deltaLine % STEP_DELTA_OCTAVE == 0) && !accidental; // Both octaves and unison
528 :
529 80 : for (Note* on : tmpnl) {
530 40 : Chord* chord = on->chord();
531 40 : Fraction tick = chord->tick();
532 40 : NoteVal nval;
533 40 : bool forceAccidental = false;
534 40 : bool noteValid = false;
535 40 : if (on->staff() && on->staff()->isDrumStaff(tick)) {
536 : // For drum staves: Don't calculate the new note based on pitch, but choose one using lines
537 0 : const Drumset* ds = on->staff()->part()->instrument(tick)->drumset();
538 0 : nval.pitch = ds->defaultPitchForLine(on->line() - deltaLine);
539 0 : nval.headGroup = ds->noteHead(nval.pitch);
540 0 : noteValid = ds->isValid(nval.pitch) && nval.headGroup != NoteHeadGroup::HEAD_INVALID;
541 : } else {
542 40 : if (useOctaveRule) {
543 40 : int octaves = deltaLine / STEP_DELTA_OCTAVE;
544 40 : nval.pitch = on->pitch() + octaves * PITCH_DELTA_OCTAVE;
545 40 : nval.tpc1 = on->tpc1();
546 40 : nval.tpc2 = on->tpc2();
547 : } else {
548 0 : int line = on->line() - deltaLine;
549 0 : Staff* estaff = staff(chord->vStaffIdx());
550 0 : ClefType clef = estaff->clef(tick);
551 0 : Key key = estaff->key(tick);
552 : int ntpc;
553 0 : if (accidental) {
554 0 : AccidentalVal acci = Accidental::subtype2value(m_is.accidentalType());
555 0 : int step = absStep(line, clef);
556 0 : int octave = step / 7;
557 0 : nval.pitch = step2pitch(step) + octave * PITCH_DELTA_OCTAVE + int(acci);
558 0 : forceAccidental = (nval.pitch == line2pitch(line, clef, key));
559 0 : ntpc = step2tpc(step % STEP_DELTA_OCTAVE, acci);
560 : } else {
561 0 : nval.pitch = line2pitch(line, clef, key);
562 0 : ntpc = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
563 : }
564 :
565 0 : Interval v = estaff->transpose(tick);
566 0 : if (v.isZero()) {
567 0 : nval.tpc1 = nval.tpc2 = ntpc;
568 : } else {
569 0 : if (style().styleB(Sid::concertPitch)) {
570 0 : v.flip();
571 0 : nval.tpc1 = ntpc;
572 0 : nval.tpc2 = Transpose::transposeTpc(ntpc, v, true);
573 : } else {
574 0 : nval.pitch += v.chromatic;
575 0 : nval.tpc2 = ntpc;
576 0 : nval.tpc1 = Transpose::transposeTpc(ntpc, v, true);
577 : }
578 : }
579 : }
580 40 : noteValid = pitchIsValid(nval.pitch);
581 : }
582 :
583 40 : if (!noteValid) {
584 30 : notesToSelect.push_back(toEngravingItem(on));
585 30 : continue;
586 : }
587 :
588 10 : Note* note = Factory::createNote(chord);
589 10 : note->setTrack(chord->track());
590 10 : note->setNval(nval, tick);
591 10 : undoAddElement(note);
592 :
593 10 : if (forceAccidental) {
594 0 : Accidental* a = Factory::createAccidental(note);
595 0 : a->setAccidentalType(m_is.accidentalType());
596 0 : a->setRole(AccidentalRole::USER);
597 0 : undoAddElement(a);
598 : }
599 10 : if (on->tieBack() && prevTied) {
600 0 : Tie* tie = prevTied->tieFor();
601 0 : tie->setEndNote(note);
602 0 : tie->setTick2(note->tick());
603 0 : note->setTieBack(tie);
604 0 : undoAddElement(tie);
605 0 : prevTied = nullptr;
606 : }
607 :
608 10 : Tie* tieFor = on->tieFor();
609 10 : if (tieFor) {
610 0 : Tie* tie = tieFor->isLaissezVib() ? Factory::createLaissezVib(note) : Factory::createTie(note);
611 0 : tie->setStartNote(note);
612 0 : tie->setTick(note->tick());
613 0 : tie->setTrack(note->track());
614 0 : note->setTieFor(tie);
615 0 : prevTied = note;
616 : }
617 :
618 10 : setPlayNote(true);
619 10 : if (selIsList && note) {
620 10 : notesToSelect.push_back(toEngravingItem(note));
621 : }
622 : }
623 40 : if (m_is.noteEntryMode()) {
624 40 : m_is.setAccidentalType(AccidentalType::NONE);
625 : }
626 40 : if (notesToSelect.empty()) {
627 0 : return;
628 : }
629 40 : if (shouldSelectFirstNote) {
630 0 : select(notesToSelect.front(), SelectType::SINGLE, 0);
631 : } else {
632 40 : select(notesToSelect, SelectType::ADD, 0);
633 : }
634 40 : if (m_is.cr() == toChordRest(_nl.front()->chord()) && selIsSingle) {
635 0 : m_is.moveToNextInputPos();
636 : }
637 40 : }
638 :
639 : //---------------------------------------------------------
640 : // setGraceNote
641 : /// Create a grace note in front of a normal note.
642 : /// \arg ch is the chord of the normal note
643 : /// \arg pitch is the pitch of the grace note
644 : /// \arg is the grace note type
645 : /// \len is the visual duration of the grace note (1/16 or 1/32)
646 : //---------------------------------------------------------
647 :
648 70 : Note* Score::setGraceNote(Chord* ch, int pitch, NoteType type, int len)
649 : {
650 70 : Chord* chord = Factory::createChord(this->dummy());
651 70 : Note* note = Factory::createNote(chord);
652 :
653 : // allow grace notes to be added to other grace notes
654 : // by really adding to parent chord
655 70 : if (ch->noteType() != NoteType::NORMAL) {
656 0 : ch = toChord(ch->ownershipParent());
657 : }
658 :
659 70 : chord->setTrack(ch->track());
660 70 : chord->setOwnershipParent(ch);
661 70 : chord->add(note);
662 :
663 : // find corresponding note within chord and use its tpc information
664 : // if no note with same pitch found, derive tpc from pitch / key
665 70 : if (Note* n = ch->findNote(pitch)) {
666 65 : note->setNval(n->noteVal(), ch->tick());
667 : } else {
668 5 : note->setPitch(pitch);
669 5 : note->setTpcFromPitch();
670 : }
671 :
672 70 : TDuration d;
673 70 : d.setVal(len);
674 70 : chord->setDurationType(d);
675 70 : chord->setTicks(d.fraction());
676 70 : chord->setNoteType(type);
677 70 : chord->setShowStemSlashInAdvance();
678 70 : chord->mutldata()->setMag(ch->staff()->staffMag(chord->tick()) * style().styleD(Sid::graceNoteMag));
679 :
680 70 : undoAddElement(chord);
681 70 : return note;
682 : }
683 :
684 292 : GuitarBend* Score::addGuitarBend(GuitarBendType type, Note* note, Note* endNote)
685 : {
686 292 : if (note->isPreBendOrDiveStart()) {
687 0 : if (type == GuitarBendType::BEND && note->staffType()->isTabStaff()) {
688 0 : GuitarBend* preBend = note->bendFor();
689 0 : Note* mainNote = preBend ? preBend->endNote() : nullptr;
690 0 : if (mainNote) {
691 0 : return addGuitarBend(type, mainNote, nullptr);
692 : }
693 : }
694 0 : return nullptr;
695 : }
696 :
697 292 : if (note->isGraceBendStart() && type != GuitarBendType::PRE_BEND) {
698 0 : return nullptr;
699 : }
700 :
701 398 : if (GuitarBend* bendBack = note->bendBack(); bendBack && !bendBack->isDive()
702 398 : && (type == GuitarBendType::PRE_BEND || type == GuitarBendType::GRACE_NOTE_BEND)) {
703 0 : return nullptr;
704 : }
705 :
706 292 : if (GuitarBend* bendFor = note->bendFor(); bendFor && !bendFor->isDive()
707 292 : && (type == GuitarBendType::BEND || type == GuitarBendType::SLIGHT_BEND)) {
708 0 : return nullptr;
709 : }
710 :
711 292 : if (type == GuitarBendType::DIP) {
712 0 : for (Note* n : note->chord()->notes()) {
713 0 : if (GuitarBend* bendFor = n->bendFor(); bendFor && bendFor->bendType() == GuitarBendType::DIP) {
714 0 : return nullptr; // Only one dip per chord
715 : }
716 : }
717 : }
718 :
719 292 : Chord* chord = note->chord();
720 292 : bool isDive = static_cast<int>(type) >= static_cast<int>(GuitarBendType::DIVE)
721 292 : && static_cast<int>(type) <= static_cast<int>(GuitarBendType::SCOOP);
722 :
723 292 : if (type == GuitarBendType::BEND || type == GuitarBendType::DIVE) {
724 218 : for (Spanner* sp : note->spannerFor()) {
725 0 : if ((sp->isGuitarBend() && toGuitarBend(sp)->isDive() == isDive) || sp->isGlissando()) {
726 0 : return nullptr;
727 : }
728 : }
729 :
730 218 : if (!endNote) {
731 0 : endNote = SLine::guessFinalNote(note);
732 : }
733 :
734 218 : bool suitableEndNote = endNote;
735 218 : if (endNote) {
736 218 : for (Spanner* sp : endNote->spannerBack()) {
737 0 : if (sp->isTie() || sp->isGlissando() || (sp->isGuitarBend() && toGuitarBend(sp)->isDive() == isDive)) {
738 0 : suitableEndNote = false;
739 0 : break;
740 : }
741 : }
742 : }
743 :
744 218 : if (!suitableEndNote) {
745 0 : endNote = GuitarBend::createEndNote(note, type);
746 : }
747 :
748 218 : if (!endNote) {
749 0 : return nullptr;
750 : }
751 : }
752 :
753 292 : GuitarBend* bend = new GuitarBend(score()->dummy());
754 292 : bend->setTick(chord->tick());
755 292 : bend->setTrack(chord->track());
756 :
757 292 : if (type == GuitarBendType::BEND || type == GuitarBendType::DIVE) {
758 218 : bend->setBendType(chord->isGrace() && type == GuitarBendType::BEND ? GuitarBendType::GRACE_NOTE_BEND : type);
759 218 : bend->setStartElement(note);
760 218 : bend->setTick2(endNote->tick());
761 218 : bend->setTrack2(endNote->track());
762 218 : bend->setEndElement(endNote);
763 218 : bend->setOwnershipParent(note);
764 218 : if (type == GuitarBendType::BEND) {
765 118 : GuitarBend::fixNotesFrettingForStandardBend(note, endNote);
766 : }
767 : } else {
768 74 : bend->setBendType(type);
769 74 : bend->setTick2(chord->tick());
770 74 : bend->setTrack2(chord->track());
771 :
772 74 : if (type == GuitarBendType::PRE_BEND || type == GuitarBendType::GRACE_NOTE_BEND || type == GuitarBendType::PRE_DIVE) {
773 66 : const GraceNotesGroup& gracesBefore = chord->graceNotesBefore();
774 :
775 : // Add to existing grace chord only if it already has a bend/dive ghost.
776 66 : const bool hasGhostGrace = !gracesBefore.empty()
777 71 : && std::any_of(gracesBefore.back()->notes().begin(),
778 5 : gracesBefore.back()->notes().end(),
779 5 : [](const Note* n) {
780 5 : return n->isPreBendOrDiveStart() || n->isGraceBendStart();
781 66 : });
782 : Note* graceNote = hasGhostGrace
783 72 : ? NoteInput::addNote(transactionManager()->currentOrDummyTransaction(), this, gracesBefore.back(),
784 3 : note->noteVal())
785 66 : : setGraceNote(chord, note->pitch(), NoteType::APPOGGIATURA, Constants::DIVISION / 2);
786 66 : graceNote->transposeDiatonic(type == GuitarBendType::PRE_DIVE ? 1 : -1, true, false);
787 66 : GuitarBend::fixNotesFrettingForGraceBend(graceNote, note);
788 :
789 66 : Chord* graceChord = graceNote->chord();
790 132 : for (EngravingObject* item : graceChord->linkList()) {
791 66 : Chord* linkedGrace = toChord(item);
792 66 : linkedGrace->setNoStem(true);
793 66 : linkedGrace->setBeamMode(BeamMode::NONE);
794 66 : }
795 :
796 : // Add bend
797 66 : bend->setOwnershipParent(graceNote);
798 66 : bend->setStartElement(graceNote);
799 66 : bend->setEndElement(note);
800 74 : } else if (type == GuitarBendType::SLIGHT_BEND || type == GuitarBendType::DIP || type == GuitarBendType::SCOOP) {
801 8 : bend->setOwnershipParent(note);
802 8 : bend->setStartElement(note);
803 : // Slight bends don't end on another note
804 8 : bend->setEndElement(note);
805 : }
806 : }
807 :
808 292 : Chord* startChord = bend->startNote()->chord();
809 292 : if (startChord->isGrace()) {
810 202 : for (EngravingObject* item : startChord->linkList()) {
811 101 : Chord* linkedGrace = toChord(item);
812 101 : if (linkedGrace->staffType()->isTabStaff()) {
813 0 : linkedGrace->setNoStem(true);
814 0 : linkedGrace->setBeamMode(BeamMode::NONE);
815 : }
816 101 : }
817 : }
818 :
819 292 : score()->undoAddElement(bend);
820 :
821 292 : if (GuitarBend* overlapping = bend->overlappingBendOrDive()) {
822 0 : int halfBendAmount = std::floor(overlapping->bendAmountInQuarterTones() / 2);
823 0 : if (bend->isDive()) {
824 0 : bend->undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, halfBendAmount);
825 : } else {
826 0 : overlapping->undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, halfBendAmount);
827 : }
828 : }
829 :
830 292 : return bend;
831 : }
832 :
833 : //---------------------------------------------------------
834 : // createCRSequence
835 : // Create a rest or chord of len f.
836 : // If f is not a basic len, create several rests or
837 : // tied chords.
838 : //
839 : // f total len of ChordRest
840 : // cr prototype CR
841 : // tick start position in measure
842 : //---------------------------------------------------------
843 :
844 0 : void Score::createCRSequence(const Fraction& f, ChordRest* cr, const Fraction& t)
845 : {
846 0 : Fraction tick(t);
847 0 : Measure* measure = cr->measure();
848 0 : ChordRest* ocr = 0;
849 0 : for (TDuration d : toDurationList(f, true)) {
850 0 : ChordRest* ncr = toChordRest(cr->clone());
851 0 : ncr->setDurationType(d);
852 0 : ncr->setTicks(d.fraction());
853 0 : undoAddCR(ncr, measure, measure->tick() + tick);
854 0 : if (cr->isChord() && ocr) {
855 0 : Chord* nc = toChord(ncr);
856 0 : Chord* oc = toChord(ocr);
857 0 : for (unsigned int i = 0; i < oc->notes().size(); ++i) {
858 0 : Note* on = oc->notes()[i];
859 0 : Note* nn = nc->notes()[i];
860 0 : Tie* tie = Factory::createTie(on);
861 0 : tie->setStartNote(on);
862 0 : tie->setEndNote(nn);
863 0 : tie->setTick(tie->startNote()->tick());
864 0 : tie->setTick2(tie->endNote()->tick());
865 0 : tie->setTrack(cr->track());
866 0 : on->setTieFor(tie);
867 0 : nn->setTieBack(tie);
868 0 : undoAddElement(tie);
869 : }
870 : }
871 :
872 0 : tick += ncr->actualTicks();
873 0 : ocr = ncr;
874 0 : }
875 0 : }
876 :
877 : //---------------------------------------------------------
878 : // setNoteRest
879 : // pitch == -1 -> set rest
880 : // "sd" is in local (stretched) time
881 : // return segment of last created note/rest
882 : //---------------------------------------------------------
883 :
884 566 : Segment* Score::setNoteRest(Segment* segment, track_idx_t track, NoteVal nval, Fraction sd, DirectionV stemDirection,
885 : bool forceAccidental, const std::set<SymId>& articulationIds, bool rhythmic, InputState* externalInputState)
886 : {
887 566 : assert(segment->segmentType() == SegmentType::ChordRest);
888 566 : InputState& is = externalInputState ? (*externalInputState) : m_is;
889 :
890 566 : bool isRest = nval.isRest();
891 566 : Fraction tick = segment->tick();
892 566 : EngravingItem* nr = nullptr;
893 566 : Tie* tie = nullptr;
894 566 : ChordRest* cr = toChordRest(segment->element(track));
895 566 : Tuplet* tuplet = cr ? cr->tuplet() : nullptr;
896 566 : Measure* measure = nullptr;
897 566 : bool targetIsRest = cr && cr->isRest();
898 :
899 : // preserve lyrics before the ChordRest is removed or replaced
900 566 : std::vector<Lyrics*> lyricsToPreserve;
901 566 : bool shouldPreserveLyrics = false;
902 566 : if (!isRest && cr) {
903 172 : lyricsToPreserve = cr->lyrics();
904 172 : shouldPreserveLyrics = !lyricsToPreserve.empty();
905 : }
906 566 : bool lyricsPreserved = false;
907 :
908 : for (;;) {
909 596 : if (track % VOICES) {
910 25 : expandVoice(segment, track);
911 : }
912 596 : if (targetIsRest && !cr->isRest()) {
913 0 : undoRemoveElement(cr);
914 0 : segment = addRest(segment, track, cr->ticks(), cr->tuplet())->segment();
915 : }
916 : // the returned gap ends at the measure boundary or at tuplet end
917 596 : Fraction dd = makeGap(segment, track, sd, tuplet);
918 :
919 596 : if (dd.isZero()) {
920 0 : LOGD("cannot get gap at %d type: %d/%d", tick.ticks(), sd.numerator(),
921 : sd.denominator());
922 0 : break;
923 : }
924 :
925 596 : measure = segment->measure();
926 596 : Fraction timeStretch = staff(track2staff(track))->timeStretch(tick);
927 596 : std::vector<TDuration> dl;
928 596 : if (rhythmic) {
929 1146 : dl = toRhythmicDurationList(dd, isRest, segment->rtick() * timeStretch, sigmap()->timesig(
930 1146 : tick).nominal(), measure, 1, timeStretch);
931 : } else {
932 214 : dl = toDurationList(dd, true);
933 : }
934 596 : size_t n = dl.size();
935 1404 : for (size_t i = 0; i < n; ++i) {
936 808 : const TDuration& d = dl[i];
937 808 : ChordRest* ncr = nullptr;
938 808 : Note* note = nullptr;
939 808 : Tie* addTie = nullptr;
940 808 : if (isRest) {
941 590 : nr = ncr = Factory::createRest(this->dummy());
942 590 : nr->setTrack(track);
943 590 : ncr->setDurationType(d);
944 590 : ncr->setTicks(d.isMeasure() ? measure->ticks() * timeStretch : d.fraction());
945 : } else {
946 218 : nr = note = Factory::createNote(this->dummy());
947 :
948 218 : if (tie) {
949 39 : tie->setEndNote(note);
950 39 : note->setTieBack(tie);
951 39 : addTie = tie;
952 : }
953 218 : Chord* chord = Factory::createChord(this->dummy());
954 218 : chord->setTrack(track);
955 218 : chord->setDurationType(d);
956 218 : chord->setTicks(d.fraction());
957 218 : chord->setStemDirection(stemDirection);
958 218 : chord->add(note);
959 218 : if (cr && cr->isChord()) {
960 21 : std::vector<Chord*> graceNotes = toChord(cr)->graceNotes();
961 21 : for (Chord* grace : graceNotes) {
962 0 : undoChangeParent(grace, chord, chord->staffIdx());
963 : }
964 21 : }
965 218 : note->setNval(nval, tick);
966 218 : if (forceAccidental) {
967 0 : int tpc = style().styleB(Sid::concertPitch) ? nval.tpc1 : nval.tpc2;
968 0 : AccidentalVal alter = tpc2alter(tpc);
969 0 : AccidentalType at = Accidental::value2subtype(alter);
970 0 : Accidental* a = Factory::createAccidental(note);
971 0 : a->setAccidentalType(at);
972 0 : a->setRole(AccidentalRole::USER);
973 0 : note->add(a);
974 : }
975 :
976 218 : ncr = chord;
977 218 : if (i + 1 < n) {
978 13 : tie = Factory::createTie(note);
979 13 : tie->setStartNote(note);
980 13 : tie->setTick(tie->startNote()->tick());
981 13 : tie->setTrack(track);
982 13 : note->setTieFor(tie);
983 : }
984 : }
985 808 : if (tuplet) {
986 48 : ncr->setTuplet(tuplet);
987 : }
988 808 : undoAddCR(ncr, measure, tick);
989 :
990 808 : if (shouldPreserveLyrics && !lyricsPreserved) {
991 : // reattach the preserved lyrics to the first replacement ChordRest
992 : // to ensure they are not duplicated during subsequent operations
993 10 : lyricsPreserved = true;
994 26 : for (Lyrics* lyric : lyricsToPreserve) {
995 16 : undoChangeParent(lyric, ncr, ncr->staffIdx());
996 : }
997 : }
998 :
999 808 : if (addTie) {
1000 39 : undoAddElement(addTie);
1001 : }
1002 808 : setPlayNote(true);
1003 808 : segment = ncr->segment();
1004 808 : tick += ncr->actualTicks();
1005 :
1006 808 : if (!articulationIds.empty()) {
1007 0 : if (ncr->isChord()) {
1008 0 : toChord(ncr)->updateArticulations(articulationIds);
1009 : }
1010 : }
1011 : }
1012 :
1013 596 : sd -= dd;
1014 596 : if (sd.isZero()) {
1015 566 : break;
1016 : }
1017 :
1018 30 : if (tick >= score()->endTick()) {
1019 1 : appendMeasures(1);
1020 : }
1021 30 : Segment* nseg = tick2segment(tick, false, SegmentType::ChordRest);
1022 30 : if (!nseg) {
1023 0 : break;
1024 : }
1025 30 : segment = nseg;
1026 :
1027 30 : cr = toChordRest(segment->element(track));
1028 : // if the cr was too big for the tuplet, get the next tuplet so we can spill into that one
1029 30 : tuplet = cr && cr->tuplet() ? cr->tuplet() : nullptr;
1030 30 : if (!cr) {
1031 0 : if (track % VOICES) {
1032 0 : cr = addRest(segment, track, TDuration(DurationType::V_MEASURE), 0);
1033 : } else {
1034 0 : LOGD("no rest in voice 0");
1035 0 : break;
1036 : }
1037 : }
1038 : //
1039 : // Note does not fit on current measure, create Tie to
1040 : // next part of note
1041 30 : if (!isRest) {
1042 26 : tie = Factory::createTie((Note*)nr);
1043 26 : tie->setStartNote((Note*)nr);
1044 26 : tie->setTick(tie->startNote()->tick());
1045 26 : tie->setTrack(nr->track());
1046 26 : ((Note*)nr)->setTieFor(tie);
1047 : }
1048 626 : }
1049 566 : if (tie) {
1050 18 : connectTies();
1051 : }
1052 :
1053 566 : if (nr) {
1054 566 : if (is.slur() && nr->isNote()) {
1055 : // If the start element was the same as the end element when the slur was created,
1056 : // the end grip of the front slur segment was given an x-offset of 3.0 * spatium().
1057 : // Now that the slur is about to be given a new end element, this should be reset.
1058 0 : if (is.slur()->endElement() == is.slur()->startElement()) {
1059 0 : is.slur()->frontSegment()->reset();
1060 : }
1061 : //
1062 : // extend slur
1063 : //
1064 0 : Chord* chord = toNote(nr)->chord();
1065 0 : is.slur()->undoChangeProperty(Pid::SPANNER_TICKS, chord->tick() - is.slur()->tick());
1066 0 : for (EngravingObject* se : is.slur()->linkList()) {
1067 0 : Slur* slur = toSlur(se);
1068 0 : for (EngravingObject* ee : chord->linkList()) {
1069 0 : EngravingItem* e = static_cast<EngravingItem*>(ee);
1070 0 : if (e->score() == slur->score() && e->track() == slur->track2()) {
1071 0 : slur->score()->undo(new ChangeSpannerElements(slur, slur->startElement(), e));
1072 0 : break;
1073 : }
1074 0 : }
1075 0 : }
1076 : }
1077 566 : if (externalInputState) {
1078 50 : is.setTrack(nr->track());
1079 50 : cr = nr->isRest() ? toChordRest(nr) : toNote(nr)->chord();
1080 50 : is.setLastSegment(is.segment());
1081 50 : is.setSegment(cr->segment());
1082 : } else {
1083 516 : select(nr, SelectType::SINGLE, 0);
1084 : }
1085 : }
1086 566 : return segment;
1087 566 : }
1088 :
1089 : //---------------------------------------------------------
1090 : // makeGap
1091 : // make time gap at tick by removing/shortening
1092 : // chord/rest
1093 : //
1094 : // if keepChord, the chord at tick is not removed
1095 : //
1096 : // "sd" is in local (stretched) time
1097 : //
1098 : // gap does not exceed measure or scope of tuplet
1099 : //
1100 : // return size of actual gap in local time
1101 : //---------------------------------------------------------
1102 :
1103 2157 : Fraction Score::makeGap(Segment* segment, track_idx_t track, const Fraction& _sd, Tuplet* tuplet, bool keepChord, bool deleteAnnotations)
1104 : {
1105 2157 : assert(_sd.numerator());
1106 :
1107 2157 : Staff* stf = staff(track2staff(track));
1108 2157 : IF_ASSERT_FAILED(stf) {
1109 0 : return Fraction();
1110 : }
1111 :
1112 2157 : Measure* measure = segment->measure();
1113 2157 : Fraction accumulated;
1114 2157 : Fraction sd = _sd;
1115 :
1116 : //
1117 : // remember first segment which should
1118 : // not be deleted (it may contain other elements we want to preserve)
1119 : //
1120 2157 : Segment* firstSegment = segment;
1121 2157 : const Fraction firstSegmentEnd = firstSegment->tick() + firstSegment->ticks();
1122 2157 : Fraction nextTick = segment->tick();
1123 :
1124 3853 : for (Segment* seg = firstSegment; seg; seg = seg->next(SegmentType::ChordRest)) {
1125 : //
1126 : // voices != 0 may have gaps:
1127 : //
1128 3840 : ChordRest* cr = toChordRest(seg->element(track));
1129 3840 : Fraction timeStretch = stf->timeStretch(seg->tick());
1130 3840 : if (!cr) {
1131 25 : if (seg->tick() < nextTick) {
1132 23 : continue;
1133 : }
1134 2 : Segment* seg1 = seg->next(SegmentType::ChordRest);
1135 2 : Fraction tick2 = seg1 ? seg1->tick() : seg->measure()->endTick();
1136 2 : Fraction td(tick2 - seg->tick());
1137 2 : td /= actualTicks(Fraction(1, 1), tuplet, timeStretch);
1138 2 : if (td > sd) {
1139 1 : td = sd;
1140 : }
1141 2 : accumulated += td;
1142 2 : sd -= td;
1143 2 : if (sd.isZero()) {
1144 2 : break;
1145 : }
1146 0 : nextTick = tick2;
1147 0 : continue;
1148 0 : }
1149 3815 : if (seg->tick() > nextTick) {
1150 : // there was a gap
1151 0 : Fraction td(seg->tick() - nextTick);
1152 0 : td /= actualTicks(Fraction(1, 1), tuplet, timeStretch);
1153 0 : if (td > sd) {
1154 0 : td = sd;
1155 : }
1156 0 : accumulated += td;
1157 0 : sd -= td;
1158 0 : if (sd.isZero()) {
1159 0 : break;
1160 : }
1161 : }
1162 : //
1163 : // limit to tuplet level
1164 : //
1165 3815 : if (tuplet) {
1166 151 : bool tupletEnd = true;
1167 151 : Tuplet* t = cr->tuplet();
1168 165 : while (t) {
1169 141 : if (t == tuplet) {
1170 127 : tupletEnd = false;
1171 127 : break;
1172 : }
1173 14 : t = t->tuplet();
1174 : }
1175 151 : if (tupletEnd) {
1176 24 : break;
1177 : }
1178 : }
1179 3791 : Fraction td(cr->ticks());
1180 :
1181 : // remove tremolo between 2 notes, if present
1182 3791 : if (cr->isChord()) {
1183 1547 : Chord* c = toChord(cr);
1184 1547 : if (c->tremoloTwoChord()) {
1185 0 : undoRemoveElement(c->tremoloTwoChord());
1186 : }
1187 : }
1188 3791 : Tuplet* ltuplet = cr->tuplet();
1189 3791 : if (ltuplet != tuplet) {
1190 : //
1191 : // Current location points to the start of a (nested)tuplet.
1192 : // We have to remove the complete tuplet.
1193 :
1194 : // get the outermost tuplet inside the current one
1195 4 : while (ltuplet->tuplet() != tuplet) {
1196 0 : ltuplet = ltuplet->tuplet();
1197 : }
1198 :
1199 : // get last segment of tuplet, drilling down to leaf nodes as necessary
1200 4 : Tuplet* t = ltuplet;
1201 4 : while (t->elements().back()->isTuplet()) {
1202 0 : t = toTuplet(t->elements().back());
1203 : }
1204 4 : seg = toChordRest(t->elements().back())->segment();
1205 :
1206 : // now delete the full tuplet
1207 4 : td = ltuplet->ticks();
1208 4 : cmdDeleteTuplet(ltuplet, false);
1209 : } else {
1210 3787 : if (seg != firstSegment || !keepChord) {
1211 3731 : undoRemoveElement(cr);
1212 : }
1213 : // even if there was a tuplet, we didn't remove it
1214 3787 : ltuplet = 0;
1215 : }
1216 3791 : nextTick += actualTicks(td, tuplet, timeStretch);
1217 3791 : if (sd < td) {
1218 : //
1219 : // we removed too much
1220 : //
1221 284 : accumulated = _sd;
1222 284 : Fraction rd = td - sd;
1223 284 : Fraction tick = cr->tick() + actualTicks(sd, tuplet, timeStretch);
1224 :
1225 284 : std::vector<TDuration> dList;
1226 284 : if (tuplet) {
1227 5 : dList = toDurationList(rd, false);
1228 5 : std::reverse(dList.begin(), dList.end());
1229 : } else {
1230 279 : TimeSig* timeSig = stf->timeSig(tick);
1231 279 : TimeSigFrac refTimeSig = timeSig ? timeSig->sig() : sigmap()->timesig(tick).nominal();
1232 279 : Fraction rTickStart = (tick - measure->tick()) * stf->timeStretch(tick);
1233 279 : dList = toRhythmicDurationList(rd, true, rTickStart, refTimeSig, measure, 0, stf->timeStretch(tick));
1234 : }
1235 284 : if (dList.empty()) {
1236 0 : break;
1237 : }
1238 :
1239 725 : for (TDuration d : dList) {
1240 441 : if (ltuplet) {
1241 : // take care not to recreate tuplet we just deleted
1242 4 : Rest* r = setRest(tick, track, d.fraction(), false, tuplet, false);
1243 4 : tick += r->actualTicks();
1244 : } else {
1245 437 : tick += addClone(cr, tick, d)->actualTicks();
1246 : }
1247 : }
1248 284 : break;
1249 284 : }
1250 3507 : accumulated += td;
1251 3507 : sd -= td;
1252 3507 : if (sd.isZero()) {
1253 1834 : break;
1254 : }
1255 : }
1256 :
1257 2157 : const Fraction t1 = firstSegmentEnd;
1258 2157 : const Fraction t2 = firstSegment->tick() + actualTicks(accumulated, tuplet, stf->timeStretch(firstSegment->tick()));
1259 2157 : if (t1 < t2) {
1260 : // Delete annotations that require an anchor to the previous segment
1261 1113 : Segment* s1 = tick2rightSegment(t1);
1262 1113 : Segment* s2 = tick2rightSegment(t2);
1263 1113 : const bool segsValid = s1 && s2 && (*s2) > (*s1);
1264 1113 : if (segsValid && deleteAnnotations) {
1265 1279 : for (Segment* s = s1; s && s != s2; s = s->next1()) {
1266 917 : const auto annotations = s->annotations(); // make a copy since we alter the list
1267 920 : for (EngravingItem* annotation : annotations) {
1268 3 : if (!annotation->systemFlag() && !annotation->allowTimeAnchor() && annotation->track() == track) {
1269 0 : deleteItem(annotation);
1270 : }
1271 : }
1272 917 : }
1273 : }
1274 :
1275 1113 : SelectionFilter filter;
1276 1113 : deleteSlursFromRange(t1, t2, track, track + 1, filter);
1277 : }
1278 :
1279 2157 : return accumulated;
1280 : }
1281 :
1282 401 : bool Score::makeGapVoice(Segment* seg, track_idx_t track, Fraction len, const Fraction& tick, bool deleteAnnotations)
1283 : {
1284 401 : ChordRest* cr = 0;
1285 401 : cr = toChordRest(seg->element(track));
1286 401 : if (!cr) {
1287 : // check if we are in the middle of a chord/rest
1288 44 : Segment* seg1 = seg->prev(SegmentType::ChordRest);
1289 : for (;;) {
1290 50 : if (seg1 == 0) {
1291 30 : if (!(track % VOICES)) {
1292 0 : LOGD("no segment before tick %d", tick.ticks());
1293 : }
1294 : // this happens only for voices other than voice 1
1295 30 : expandVoice(seg, track);
1296 30 : return makeGapVoice(seg, track, len, tick, deleteAnnotations);
1297 : }
1298 20 : if (seg1->element(track)) {
1299 14 : break;
1300 : }
1301 6 : seg1 = seg1->prev(SegmentType::ChordRest);
1302 : }
1303 14 : ChordRest* cr1 = toChordRest(seg1->element(track));
1304 14 : Fraction srcF = cr1->ticks();
1305 14 : Fraction dstF = (tick - cr1->tick()) * cr1->staff()->timeStretch(cr1->tick());
1306 14 : std::vector<TDuration> dList = toDurationList(dstF, true);
1307 14 : if (dList.empty()) {
1308 0 : LOGD("Could not make durations for: %d/%d", dstF.numerator(), dstF.denominator());
1309 0 : return false;
1310 : }
1311 14 : size_t n = dList.size();
1312 14 : undoChangeChordRestLen(cr1, TDuration(dList[0]));
1313 14 : if (n > 1) {
1314 1 : Fraction crtick = cr1->endTick();
1315 1 : Measure* measure = tick2measure(crtick);
1316 1 : if (cr1->isChord()) {
1317 : // split Chord
1318 1 : Chord* c = toChord(cr1);
1319 2 : for (size_t i = 1; i < n; ++i) {
1320 1 : TDuration d = dList[i];
1321 1 : Chord* c2 = addChord(crtick, d, c, true, c->tuplet());
1322 1 : c = c2;
1323 1 : seg1 = c->segment();
1324 1 : crtick += c->actualTicks();
1325 : }
1326 : } else {
1327 : // split Rest
1328 0 : Rest* r = toRest(cr1);
1329 0 : for (size_t i = 1; i < n; ++i) {
1330 0 : TDuration d = dList[i];
1331 0 : Rest* r2 = toRest(r->clone());
1332 0 : r2->setTicks(d.fraction());
1333 0 : r2->setDurationType(d);
1334 0 : undoAddCR(r2, measure, crtick);
1335 0 : seg1 = r2->segment();
1336 0 : crtick += r2->actualTicks();
1337 : }
1338 : }
1339 : }
1340 14 : setRest(tick, track, srcF - dstF, true, 0);
1341 : for (;;) {
1342 14 : seg1 = seg1->next1(SegmentType::ChordRest);
1343 14 : if (seg1 == 0) {
1344 0 : LOGD("no segment");
1345 0 : return false;
1346 : }
1347 14 : if (seg1->element(track)) {
1348 14 : cr = toChordRest(seg1->element(track));
1349 14 : break;
1350 : }
1351 : }
1352 14 : }
1353 :
1354 : for (;;) {
1355 375 : if (!cr) {
1356 0 : LOGD("cannot make gap");
1357 0 : return false;
1358 : }
1359 375 : Fraction l = makeGap(cr->segment(), cr->track(), len, nullptr, /*keepChord*/ false, deleteAnnotations);
1360 375 : if (l.isZero()) {
1361 0 : LOGD("returns zero gap");
1362 0 : return false;
1363 : }
1364 375 : len -= l;
1365 375 : if (len.isZero()) {
1366 371 : break;
1367 : }
1368 : // go to next cr
1369 4 : Measure* m = cr->measure()->nextMeasure();
1370 4 : if (!m) {
1371 0 : LOGD("EOS reached");
1372 0 : InsertMeasureOptions options;
1373 0 : options.createMeasureRests = false;
1374 0 : insertMeasure(ElementType::MEASURE, nullptr, options);
1375 0 : m = cr->measure()->nextMeasure();
1376 0 : if (!m) {
1377 0 : LOGD("===EOS reached");
1378 0 : return true;
1379 : }
1380 : }
1381 : // first segment in measure was removed, have to recreate it
1382 4 : Segment* s = m->undoGetSegment(SegmentType::ChordRest, m->tick());
1383 4 : track_idx_t t = cr->track();
1384 4 : cr = toChordRest(s->element(t));
1385 4 : if (!cr) {
1386 2 : addRest(s, t, TDuration(DurationType::V_MEASURE), 0);
1387 2 : cr = toChordRest(s->element(t));
1388 : }
1389 4 : }
1390 371 : return true;
1391 : }
1392 :
1393 : //---------------------------------------------------------
1394 : // splitGapToMeasureBoundaries
1395 : // cr - start of gap
1396 : // gap - gap len in local (stretched) time
1397 : //---------------------------------------------------------
1398 :
1399 28 : std::vector<Fraction> Score::splitGapToMeasureBoundaries(ChordRest* cr, Fraction gap)
1400 : {
1401 28 : std::vector<Fraction> flist;
1402 :
1403 28 : Tuplet* tuplet = cr->tuplet();
1404 28 : if (tuplet) {
1405 1 : Fraction rest = tuplet->elementsDuration();
1406 3 : for (DurationElement* de : tuplet->elements()) {
1407 3 : if (de == cr) {
1408 1 : break;
1409 : }
1410 2 : rest -= de->ticks();
1411 : }
1412 1 : if (rest < gap) {
1413 0 : LOGD("does not fit in tuplet");
1414 : } else {
1415 1 : flist.push_back(gap);
1416 : }
1417 1 : return flist;
1418 : }
1419 :
1420 27 : Segment* s = cr->segment();
1421 30 : while (gap > Fraction(0, 1)) {
1422 30 : Measure* m = s->measure();
1423 30 : Fraction timeStretch = cr->staff()->timeStretch(s->tick());
1424 30 : Fraction rest = (m->ticks() - s->rtick()) * timeStretch;
1425 30 : if (rest >= gap) {
1426 27 : flist.push_back(gap);
1427 27 : return flist;
1428 : }
1429 3 : flist.push_back(rest);
1430 3 : gap -= rest;
1431 3 : if (m->nextMeasure() != 0) {
1432 3 : m = m->nextMeasure();
1433 : }
1434 3 : s = m->first(SegmentType::ChordRest);
1435 : }
1436 0 : return flist;
1437 0 : }
1438 :
1439 : //---------------------------------------------------------
1440 : // changeCRlen
1441 : // - dstF is in local (stretched) time
1442 : //---------------------------------------------------------
1443 :
1444 50 : void Score::changeCRlen(ChordRest* cr, const TDuration& d)
1445 : {
1446 50 : Fraction dstF;
1447 50 : if (d.type() == DurationType::V_MEASURE) {
1448 0 : dstF = cr->measure()->stretchedLen(cr->staff());
1449 : } else {
1450 50 : dstF = d.fraction();
1451 : }
1452 50 : changeCRlen(cr, dstF);
1453 50 : }
1454 :
1455 55 : void Score::changeCRlen(ChordRest* cr, const Fraction& dstF, bool fillWithRest)
1456 : {
1457 55 : if (cr->isMeasureRepeat()) {
1458 : // it is not clear what should this
1459 : // operation mean for measure repeats.
1460 27 : return;
1461 : }
1462 55 : Fraction srcF(cr->ticks());
1463 55 : if (srcF == dstF) {
1464 0 : if (cr->isFullMeasureRest()) {
1465 0 : undoChangeChordRestLen(cr, dstF);
1466 : }
1467 0 : return;
1468 : }
1469 :
1470 : //keep selected element if any
1471 55 : EngravingItem* selElement = selection().isSingle() ? getSelectedElement() : 0;
1472 :
1473 55 : track_idx_t track = cr->track();
1474 55 : Tuplet* tuplet = cr->tuplet();
1475 :
1476 55 : if (srcF > dstF) {
1477 : //
1478 : // make shorter and fill with rest
1479 : //
1480 27 : deselectAll();
1481 27 : if (cr->isChord()) {
1482 : //
1483 : // remove ties and tremolo between 2 notes
1484 : //
1485 24 : Chord* c = toChord(cr);
1486 24 : if (c->tremoloTwoChord()) {
1487 0 : undoRemoveElement(c->tremoloTwoChord());
1488 : }
1489 48 : for (Note* n : c->notes()) {
1490 24 : if (Tie* tie = n->tieFor()) {
1491 0 : if (tie->tieJumpPoints()) {
1492 0 : tie->undoRemoveTiesFromJumpPoints();
1493 : }
1494 0 : undoRemoveElement(tie);
1495 : }
1496 24 : for (Spanner* sp : n->spannerFor()) {
1497 0 : if (sp->isGlissando() || sp->isGuitarBend()) {
1498 0 : undoRemoveElement(sp);
1499 : }
1500 : }
1501 : }
1502 : }
1503 27 : Fraction timeStretch = cr->staff()->timeStretch(cr->tick());
1504 27 : std::vector<TDuration> dList = toDurationList(dstF, true);
1505 27 : if (dList.empty()) {
1506 0 : LOGD("Could not make durations for: %d/%d", dstF.numerator(), dstF.denominator());
1507 0 : return;
1508 : }
1509 27 : undoChangeChordRestLen(cr, dList[0]);
1510 27 : Fraction tick2 = cr->tick();
1511 27 : for (unsigned i = 1; i < dList.size(); ++i) {
1512 0 : tick2 += actualTicks(dList[i - 1].ticks(), tuplet, timeStretch);
1513 0 : TDuration d = dList[i];
1514 0 : setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
1515 : }
1516 27 : if (fillWithRest) {
1517 27 : setRest(cr->endTick(), track, srcF - dstF, false, tuplet);
1518 : }
1519 :
1520 27 : if (selElement) {
1521 21 : select(selElement, SelectType::SINGLE, 0);
1522 : }
1523 27 : return;
1524 27 : }
1525 :
1526 : //
1527 : // make longer
1528 : //
1529 : // split required len into Measures
1530 28 : std::vector<Fraction> flist = splitGapToMeasureBoundaries(cr, dstF);
1531 28 : if (flist.empty()) {
1532 0 : return;
1533 : }
1534 :
1535 28 : deselectAll();
1536 28 : EngravingItem* elementToSelect = nullptr;
1537 :
1538 28 : Fraction tick = cr->tick();
1539 28 : Fraction f = dstF;
1540 28 : ChordRest* cr1 = cr;
1541 28 : Chord* oc = 0;
1542 28 : Segment* s = cr->segment();
1543 :
1544 28 : bool first = true;
1545 57 : for (const Fraction& f2 : flist) {
1546 31 : if (!cr1) {
1547 0 : expandVoice(s, track);
1548 0 : cr1 = toChordRest(s->element(track));
1549 : }
1550 :
1551 31 : f -= f2;
1552 31 : makeGap(cr1->segment(), cr1->track(), f2, tuplet, first);
1553 :
1554 31 : if (cr->isRest()) {
1555 4 : Fraction timeStretch = cr1->staff()->timeStretch(cr1->tick());
1556 4 : Rest* r = toRest(cr);
1557 4 : if (first) {
1558 1 : std::vector<TDuration> dList = toDurationList(f2, true);
1559 1 : undoChangeChordRestLen(cr, dList[0]);
1560 1 : Fraction tick2 = cr->tick();
1561 1 : for (unsigned i = 1; i < dList.size(); ++i) {
1562 0 : tick2 += actualTicks(dList[i - 1].ticks(), tuplet, timeStretch);
1563 0 : TDuration d = dList[i];
1564 0 : setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
1565 : }
1566 1 : } else {
1567 3 : r = setRest(tick, track, f2, false, tuplet);
1568 : }
1569 4 : if (first) {
1570 1 : elementToSelect = r;
1571 1 : first = false;
1572 : }
1573 4 : tick += actualTicks(f2, tuplet, timeStretch);
1574 : } else {
1575 27 : std::vector<TDuration> dList = toDurationList(f2, true);
1576 27 : Measure* measure = tick2measure(tick);
1577 27 : Fraction etick = measure->tick();
1578 :
1579 27 : if (((tick - etick).ticks() % dList[0].ticks().ticks()) == 0) {
1580 52 : for (TDuration du : dList) {
1581 26 : Chord* cc = nullptr;
1582 26 : if (oc) {
1583 0 : cc = oc;
1584 0 : oc = addChord(tick, du, cc, true, tuplet);
1585 : } else {
1586 26 : cc = toChord(cr);
1587 26 : undoChangeChordRestLen(cr, du);
1588 26 : oc = cc;
1589 : }
1590 26 : if (oc && first) {
1591 26 : elementToSelect = selElement ? selElement : oc;
1592 26 : first = false;
1593 : }
1594 26 : if (oc) {
1595 26 : tick += oc->actualTicks();
1596 : }
1597 : }
1598 : } else {
1599 2 : for (size_t i = dList.size(); i > 0; --i) { // loop probably needs to be in this reverse order
1600 : Chord* cc;
1601 1 : if (oc) {
1602 0 : cc = oc;
1603 0 : oc = addChord(tick, dList[i - 1], cc, true, tuplet);
1604 : } else {
1605 1 : cc = toChord(cr);
1606 1 : undoChangeChordRestLen(cr, dList[i - 1]);
1607 1 : oc = cc;
1608 : }
1609 1 : if (first) {
1610 1 : elementToSelect = selElement;
1611 1 : first = false;
1612 : }
1613 1 : tick += oc->actualTicks();
1614 : }
1615 : }
1616 27 : }
1617 31 : const Measure* m = cr1->measure();
1618 31 : if (m->nextMeasure() == 0) {
1619 2 : if (f.isNotZero()) {
1620 0 : appendMeasures(1);
1621 : }
1622 : }
1623 31 : const Measure* m1 = m->nextMeasure();
1624 31 : if (m1 == 0) {
1625 2 : break;
1626 : }
1627 29 : s = m1->first(SegmentType::ChordRest);
1628 29 : cr1 = toChordRest(s->element(track));
1629 : }
1630 28 : connectTies();
1631 :
1632 28 : if (elementToSelect) {
1633 27 : if (canReselectItem(elementToSelect)) {
1634 22 : select(elementToSelect, SelectType::SINGLE, 0);
1635 : }
1636 : }
1637 28 : }
1638 :
1639 : //---------------------------------------------------------
1640 : // cmdMoveRest
1641 : //---------------------------------------------------------
1642 :
1643 0 : void Score::cmdMoveRest(Rest* rest, DirectionV dir)
1644 : {
1645 0 : PointF pos(rest->offset());
1646 0 : if (dir == DirectionV::UP) {
1647 0 : pos.ry() -= rest->spatium() * rest->staffType()->lineDistance().val();
1648 0 : } else if (dir == DirectionV::DOWN) {
1649 0 : pos.ry() += rest->spatium() * rest->staffType()->lineDistance().val();
1650 : }
1651 0 : rest->undoChangeProperty(Pid::OFFSET, pos);
1652 0 : }
1653 :
1654 : //---------------------------------------------------------
1655 : // cmdMoveLyrics
1656 : //---------------------------------------------------------
1657 :
1658 0 : void Score::cmdMoveLyrics(Lyrics* lyrics, DirectionV dir)
1659 : {
1660 0 : int verse = lyrics->verse() + (dir == DirectionV::UP ? -1 : 1);
1661 0 : if (verse < 0) {
1662 0 : return;
1663 : }
1664 0 : lyrics->undoChangeProperty(Pid::VERSE, verse);
1665 : }
1666 :
1667 : //---------------------------------------------------------
1668 : // cmdAddGrace
1669 : /// adds grace note of specified type to selected notes
1670 : //---------------------------------------------------------
1671 :
1672 0 : void Score::cmdAddGrace(NoteType graceType, int duration)
1673 : {
1674 0 : const std::vector<EngravingItem*> copyOfElements = selection().elements();
1675 0 : for (EngravingItem* e : copyOfElements) {
1676 0 : if (e->isNote()) {
1677 0 : Note* n = toNote(e);
1678 0 : Note* graceNote = setGraceNote(n->chord(), n->pitch(), graceType, duration);
1679 0 : select(graceNote, SelectType::SINGLE, 0);
1680 : }
1681 : }
1682 0 : }
1683 :
1684 : //---------------------------------------------------------
1685 : // cmdRemoveEmptyTrailingMeasures
1686 : //---------------------------------------------------------
1687 :
1688 0 : void Score::cmdRemoveEmptyTrailingMeasures()
1689 : {
1690 0 : Score* mScore = masterScore();
1691 0 : auto beginMeasure = mScore->firstTrailingMeasure();
1692 0 : if (beginMeasure) {
1693 0 : mScore->deleteMeasures(beginMeasure, mScore->lastMeasure());
1694 : }
1695 0 : }
1696 :
1697 : //---------------------------------------------------------
1698 : // cmdToggleLayoutBreak
1699 : //---------------------------------------------------------
1700 :
1701 0 : void Score::cmdToggleLayoutBreak(LayoutBreakType type)
1702 : {
1703 : // find measure(s)
1704 0 : std::vector<MeasureBase*> mbl;
1705 0 : bool allNoBreaks = true; // NOBREAK is not removed unless every measure in selection already has one
1706 0 : if (selection().isRange()) {
1707 0 : MeasureBase* startMeasureBase = nullptr;
1708 0 : MeasureBase* endMeasureBase = nullptr;
1709 0 : if (!selection().measureBaseRange(&startMeasureBase, &endMeasureBase)) {
1710 0 : return;
1711 : }
1712 0 : if (!startMeasureBase || !endMeasureBase) {
1713 0 : return;
1714 : }
1715 0 : if (type == LayoutBreakType::NOBREAK) {
1716 : // add throughout the selection
1717 : // or remove if already on every measure
1718 0 : if (startMeasureBase == endMeasureBase) {
1719 0 : mbl.push_back(startMeasureBase);
1720 0 : allNoBreaks = startMeasureBase->noBreak();
1721 : } else {
1722 0 : for (MeasureBase* m = startMeasureBase; m; m = m->nextMM()) {
1723 0 : if (m == endMeasureBase) {
1724 0 : break;
1725 : }
1726 0 : mbl.push_back(m);
1727 0 : if (!toMeasureBase(m)->noBreak()) {
1728 0 : allNoBreaks = false;
1729 : }
1730 : }
1731 : }
1732 : } else {
1733 : // toggle break on the last measure of the range
1734 0 : mbl.push_back(endMeasureBase);
1735 : // if more than one measure selected,
1736 : // also toggle break *before* the range (to try to fit selection on a single line)
1737 0 : if (startMeasureBase != endMeasureBase && startMeasureBase->prev()) {
1738 0 : mbl.push_back(startMeasureBase->prev());
1739 : }
1740 : }
1741 : } else {
1742 0 : MeasureBase* mb = nullptr;
1743 0 : for (EngravingItem* el : selection().elements()) {
1744 0 : switch (el->type()) {
1745 0 : case ElementType::HBOX:
1746 : case ElementType::VBOX:
1747 : case ElementType::TBOX:
1748 : case ElementType::FBOX:
1749 0 : mb = toMeasureBase(el);
1750 0 : break;
1751 0 : default: {
1752 : // find measure
1753 0 : mb = toMeasure(el->findMeasure());
1754 : // for start repeat, attach break to previous measure
1755 0 : if (mb && el->isBarLine()) {
1756 0 : BarLine* bl = toBarLine(el);
1757 0 : if (bl->barLineType() == BarLineType::START_REPEAT) {
1758 0 : mb = mb->prevMeasure();
1759 : }
1760 : }
1761 :
1762 0 : if (mb) {
1763 0 : allNoBreaks = mb->noBreak();
1764 : }
1765 : }
1766 : }
1767 : }
1768 0 : if (mb) {
1769 0 : mbl.push_back(mb);
1770 : }
1771 : }
1772 :
1773 0 : if (type == LayoutBreakType::NOBREAK && !allNoBreaks) {
1774 0 : for (MeasureBase* mb : mbl) {
1775 0 : if (mb->systemLock()) {
1776 0 : return;
1777 : }
1778 : }
1779 : }
1780 :
1781 : // toggle the breaks
1782 0 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
1783 0 : for (MeasureBase* mb: mbl) {
1784 0 : bool val = false;
1785 0 : switch (type) {
1786 0 : case LayoutBreakType::LINE:
1787 0 : if (mb->isVBoxBase()) {
1788 0 : break;
1789 : }
1790 0 : val = !mb->lineBreak();
1791 0 : if (val) {
1792 0 : EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
1793 0 : EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
1794 : }
1795 0 : mb->undoSetBreak(val, type);
1796 : // remove page break if appropriate
1797 0 : if (val && mb->pageBreak()) {
1798 0 : mb->undoSetBreak(false, LayoutBreakType::PAGE);
1799 : }
1800 0 : break;
1801 0 : case LayoutBreakType::PAGE:
1802 0 : val = !mb->pageBreak();
1803 0 : if (val) {
1804 0 : EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
1805 0 : EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
1806 : }
1807 0 : mb->undoSetBreak(val, type);
1808 : // remove line break if appropriate
1809 0 : if (val && mb->lineBreak()) {
1810 0 : mb->undoSetBreak(false, LayoutBreakType::LINE);
1811 : }
1812 0 : break;
1813 0 : case LayoutBreakType::SECTION:
1814 0 : val = !mb->sectionBreak();
1815 0 : if (val) {
1816 0 : EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
1817 0 : EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
1818 : }
1819 0 : mb->undoSetBreak(val, type);
1820 0 : break;
1821 0 : case LayoutBreakType::NOBREAK:
1822 0 : mb->undoSetBreak(!allNoBreaks, type);
1823 : // remove other breaks if appropriate
1824 0 : if (!mb->noBreak()) {
1825 0 : if (mb->pageBreak()) {
1826 0 : mb->undoSetBreak(false, LayoutBreakType::PAGE);
1827 0 : } else if (mb->lineBreak()) {
1828 0 : mb->undoSetBreak(false, LayoutBreakType::LINE);
1829 0 : } else if (mb->sectionBreak()) {
1830 0 : mb->undoSetBreak(false, LayoutBreakType::SECTION);
1831 : }
1832 : }
1833 0 : break;
1834 0 : default:
1835 0 : break;
1836 : }
1837 : }
1838 0 : }
1839 :
1840 : //---------------------------------------------------------
1841 : // cmdToggleMmrest
1842 : //---------------------------------------------------------
1843 :
1844 0 : void Score::cmdToggleMmrest()
1845 : {
1846 0 : bool val = !style().styleB(Sid::createMultiMeasureRests);
1847 0 : deselectAll();
1848 0 : undoChangeStyleVal(Sid::createMultiMeasureRests, val);
1849 0 : }
1850 :
1851 : //---------------------------------------------------------
1852 : // cmdToggleHideEmpty
1853 : //---------------------------------------------------------
1854 :
1855 0 : void Score::cmdToggleHideEmpty()
1856 : {
1857 0 : bool val = !style().styleB(Sid::hideEmptyStaves);
1858 0 : deselectAll();
1859 0 : undoChangeStyleVal(Sid::hideEmptyStaves, val);
1860 0 : }
1861 :
1862 0 : void Score::cmdSetHideStaffIfEmptyOverride(staff_idx_t staffIdx, System* system, engraving::AutoOnOff value)
1863 : {
1864 0 : for (MeasureBase* mb : system->measures()) {
1865 0 : if (!mb->isMeasure()) {
1866 0 : continue;
1867 : }
1868 0 : undo(new ChangeMStaffHideIfEmpty(engraving::toMeasure(mb), staffIdx, value));
1869 : }
1870 0 : }
1871 :
1872 : //---------------------------------------------------------
1873 : // cmdToggleAutoplace
1874 : //---------------------------------------------------------
1875 :
1876 0 : void Score::cmdToggleAutoplace(bool all)
1877 : {
1878 0 : if (all) {
1879 0 : bool val = !style().styleB(Sid::autoplaceEnabled);
1880 0 : undoChangeStyleVal(Sid::autoplaceEnabled, val);
1881 0 : setLayoutAll();
1882 : } else {
1883 0 : std::set<EngravingItem*> spanners;
1884 0 : for (EngravingItem* e : selection().elements()) {
1885 0 : if (e->isSpannerSegment()) {
1886 0 : if (EngravingObject* ee = e->propertyDelegate(Pid::AUTOPLACE)) {
1887 0 : e = toEngravingItem(ee);
1888 : }
1889 : // spanner segments may each have their own autoplace setting
1890 : // but if they delegate to spanner, only toggle once
1891 0 : if (e->isSpanner()) {
1892 0 : if (muse::contains(spanners, e)) {
1893 0 : continue;
1894 : }
1895 0 : spanners.insert(e);
1896 : }
1897 : }
1898 0 : PropertyFlags pf = e->propertyFlags(Pid::AUTOPLACE);
1899 0 : if (pf == PropertyFlags::STYLED) {
1900 0 : pf = PropertyFlags::UNSTYLED;
1901 : }
1902 0 : e->undoChangeProperty(Pid::AUTOPLACE, !e->getProperty(Pid::AUTOPLACE).toBool(), pf);
1903 : }
1904 0 : }
1905 0 : }
1906 : }
|