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) 2025 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 "editnote.h"
24 : #include "editchord.h"
25 : #include "noteinput.h"
26 :
27 : #include <algorithm>
28 : #include <set>
29 :
30 : #include "dom/accidental.h"
31 : #include "dom/articulation.h"
32 : #include "dom/chord.h"
33 : #include "dom/drumset.h"
34 : #include "dom/factory.h"
35 : #include "dom/linkedobjects.h"
36 : #include "dom/measure.h"
37 : #include "dom/note.h"
38 : #include "dom/ornament.h"
39 : #include "dom/part.h"
40 : #include "dom/score.h"
41 : #include "dom/staff.h"
42 : #include "dom/stringdata.h"
43 : #include "dom/utils.h"
44 :
45 : #include "editing/transaction/transaction.h"
46 :
47 : using namespace mu::engraving;
48 :
49 : //---------------------------------------------------------
50 : // ChangePitch
51 : //---------------------------------------------------------
52 :
53 : class ChangePitch : public UndoableCommand
54 : {
55 3638 : OBJECT_ALLOCATOR(engraving, ChangePitch)
56 :
57 : Note* note = nullptr;
58 : int pitch = 0;
59 : int tpc1 = 0;
60 : int tpc2 = 0;
61 :
62 1958 : void flip() override
63 : {
64 1958 : int f_pitch = note->pitch();
65 1958 : int f_tpc1 = note->tpc1();
66 1958 : int f_tpc2 = note->tpc2();
67 : // do not change unless necessary
68 1958 : if (f_pitch == pitch && f_tpc1 == tpc1 && f_tpc2 == tpc2) {
69 817 : return;
70 : }
71 :
72 1141 : note->setPitch(pitch, tpc1, tpc2);
73 1141 : pitch = f_pitch;
74 1141 : tpc1 = f_tpc1;
75 1141 : tpc2 = f_tpc2;
76 :
77 1141 : note->triggerLayout();
78 : }
79 :
80 : public:
81 1822 : ChangePitch(Note* _note, int _pitch, int _tpc1, int _tpc2)
82 1822 : {
83 1822 : note = _note;
84 1822 : pitch = _pitch;
85 1822 : tpc1 = _tpc1;
86 1822 : tpc2 = _tpc2;
87 1822 : }
88 :
89 376 : UNDO_TYPE(CommandType::ChangePitch)
90 0 : UNDO_NAME("ChangePitch")
91 1128 : UNDO_CHANGED_OBJECTS({ note })
92 : };
93 :
94 : //---------------------------------------------------------
95 : // ChangeFretting
96 : //
97 : // To use with tablatures to force a specific note fretting;
98 : // Pitch, string and fret must be changed all together; otherwise,
99 : // if they are not consistent among themselves, the refretting algorithm may re-assign
100 : // fret and string numbers for (potentially) all the notes of all the chords of a segment.
101 : //---------------------------------------------------------
102 :
103 : class ChangeFretting : public UndoableCommand
104 : {
105 0 : OBJECT_ALLOCATOR(engraving, ChangeFretting)
106 :
107 : Note* note = nullptr;
108 : int pitch = 0;
109 : int string = 0;
110 : int fret = 0;
111 : int tpc1 = 0;
112 : int tpc2 = 0;
113 :
114 0 : void flip() override
115 : {
116 0 : int f_pitch = note->pitch();
117 0 : int f_string= note->string();
118 0 : int f_fret = note->fret();
119 0 : int f_tpc1 = note->tpc1();
120 0 : int f_tpc2 = note->tpc2();
121 : // do not change unless necessary
122 0 : if (f_pitch == pitch && f_string == string && f_fret == fret && f_tpc1 == tpc1 && f_tpc2 == tpc2) {
123 0 : return;
124 : }
125 :
126 0 : note->setPitch(pitch, tpc1, tpc2);
127 0 : note->setString(string);
128 0 : note->setFret(fret);
129 0 : pitch = f_pitch;
130 0 : string= f_string;
131 0 : fret = f_fret;
132 0 : tpc1 = f_tpc1;
133 0 : tpc2 = f_tpc2;
134 0 : note->triggerLayout();
135 : }
136 :
137 : public:
138 0 : ChangeFretting(Note* _note, int _pitch, int _string, int _fret, int _tpc1, int _tpc2)
139 0 : {
140 0 : note = _note;
141 0 : pitch = _pitch;
142 0 : string= _string;
143 0 : fret = _fret;
144 0 : tpc1 = _tpc1;
145 0 : tpc2 = _tpc2;
146 0 : }
147 :
148 0 : UNDO_TYPE(CommandType::ChangeFretting)
149 0 : UNDO_NAME("ChangeFretting")
150 0 : UNDO_CHANGED_OBJECTS({ note })
151 : };
152 :
153 : //---------------------------------------------------------
154 : // toggleOrnament
155 : //---------------------------------------------------------
156 :
157 0 : void EditNote::toggleOrnament(Score* score, SymId attr)
158 : {
159 0 : std::set<Chord*> set;
160 0 : for (EngravingItem* el : score->selection().elements()) {
161 0 : if (el->isNote() || el->isChord()) {
162 0 : Chord* cr = 0;
163 : // apply articulation on a given chord only once
164 0 : if (el->isNote()) {
165 0 : cr = toNote(el)->chord();
166 0 : if (muse::contains(set, cr)) {
167 0 : continue;
168 : }
169 : }
170 0 : Ornament* na = Factory::createOrnament(score->dummy());
171 0 : na->setSymId(attr);
172 0 : if (!EditChord::toggleArticulation(score, el, na)) {
173 0 : delete na;
174 : }
175 :
176 0 : if (cr) {
177 0 : set.insert(cr);
178 : }
179 : }
180 : }
181 0 : }
182 :
183 : //---------------------------------------------------------
184 : // toggleAccidental
185 : //---------------------------------------------------------
186 :
187 0 : void EditNote::toggleAccidental(Score* score, AccidentalType at)
188 : {
189 0 : bool applyNaturalToInputNotes = false;
190 0 : if (score->inputState().accidentalType() == at && at != AccidentalType::NONE) {
191 0 : at = AccidentalType::NONE; // NONE also means "search for previous accidental and use it if found"
192 0 : applyNaturalToInputNotes = true;
193 : }
194 :
195 0 : if (score->noteEntryMode()) {
196 0 : score->inputState().setAccidentalType(at);
197 0 : score->inputState().setRest(false);
198 :
199 0 : if (!score->inputState().notes().empty()) {
200 0 : EditNote::applyAccidentalToInputNotes(score,
201 : applyNaturalToInputNotes ? AccidentalType::NATURAL : at);
202 : }
203 : } else {
204 0 : if (score->selection().isNone()) {
205 0 : score->inputState().setAccidentalType(at);
206 0 : score->inputState().setDuration(DurationType::V_QUARTER);
207 0 : score->inputState().setRest(false);
208 : } else {
209 0 : EditNote::changeAccidental(score, at);
210 : }
211 : }
212 0 : }
213 :
214 : //---------------------------------------------------------
215 : // applyAccidentalToInputNotes
216 : //---------------------------------------------------------
217 :
218 0 : void EditNote::applyAccidentalToInputNotes(Score* score, AccidentalType accidentalType)
219 : {
220 0 : NoteValList notes;
221 0 : notes.reserve(score->inputState().notes().size());
222 :
223 0 : Position pos;
224 0 : pos.segment = score->inputState().segment();
225 0 : pos.staffIdx = score->inputState().staffIdx();
226 :
227 0 : for (const NoteVal& oldVal : score->inputState().notes()) {
228 0 : pos.line = noteValToLine(oldVal, score->inputState().staff(), score->inputState().tick());
229 :
230 0 : bool error = false;
231 0 : const NoteVal newVal = NoteInput::noteValForPosition(score, pos, accidentalType, error);
232 :
233 0 : if (error) {
234 0 : notes.push_back(oldVal);
235 : } else {
236 0 : notes.push_back(newVal);
237 : }
238 : }
239 :
240 0 : score->inputState().setNotes(notes);
241 0 : }
242 :
243 : //---------------------------------------------------------
244 : // changeAccidental
245 : /// Change accidental to subtype \a idx for all selected
246 : /// notes.
247 : //---------------------------------------------------------
248 :
249 1 : void EditNote::changeAccidental(Score* score, AccidentalType idx)
250 : {
251 5 : for (EngravingItem* item : score->selection().elements()) {
252 4 : switch (item->type()) {
253 0 : case ElementType::ACCIDENTAL: {
254 0 : Accidental* accidental = toAccidental(item);
255 0 : if (accidental->accidentalType() == idx) {
256 0 : EditNote::changeAccidental(score, accidental->note(), AccidentalType::NONE);
257 : } else {
258 0 : EditNote::changeAccidental(score, accidental->note(), idx);
259 : }
260 0 : break;
261 : }
262 2 : case ElementType::NOTE:
263 2 : EditNote::changeAccidental(score, toNote(item), idx);
264 2 : break;
265 2 : default:
266 2 : break;
267 : }
268 : }
269 1 : }
270 :
271 : //---------------------------------------------------------
272 : // changeAccidental2
273 : //---------------------------------------------------------
274 :
275 241 : void EditNote::changeAccidental2(Note* n, int pitch, int tpc)
276 : {
277 241 : Score* score = n->score();
278 241 : Chord* chord = n->chord();
279 241 : Staff* st = chord->staff();
280 241 : int fret = n->fret();
281 241 : int string = n->string();
282 :
283 241 : if (st->isTabStaff(chord->tick())) {
284 0 : if (pitch != n->pitch()) {
285 : //
286 : // as pitch has changed, calculate new
287 : // string & fret
288 : //
289 0 : const StringData* stringData = n->originPart()->stringData(n->tick(), st->idx());
290 0 : if (stringData) {
291 0 : stringData->convertPitch(pitch, st, &string, &fret);
292 : }
293 : }
294 : }
295 : int tpc1;
296 241 : int tpc2 = n->transposeTpc(tpc);
297 241 : if (n->style().styleB(Sid::concertPitch)) {
298 0 : tpc1 = tpc;
299 : } else {
300 241 : tpc1 = tpc2;
301 241 : tpc2 = tpc;
302 : }
303 :
304 241 : if (!st->isTabStaff(chord->tick())) {
305 : //
306 : // handle ties
307 : //
308 241 : if (n->tieBack()) {
309 0 : if (pitch != n->pitch()) {
310 0 : score->undoRemoveElement(n->tieBack());
311 0 : if (n->tieFor()) {
312 0 : score->undoRemoveElement(n->tieFor());
313 : }
314 : }
315 : } else {
316 241 : Note* nn = n;
317 265 : while (nn && nn->tieFor()) {
318 24 : nn = nn->tieFor()->endNote();
319 24 : if (nn) {
320 24 : score->undo(new ChangePitch(nn, pitch, tpc1, tpc2));
321 : }
322 : }
323 : }
324 : }
325 241 : undoChangePitch(score, n, pitch, tpc1, tpc2);
326 241 : }
327 :
328 : //---------------------------------------------------------
329 : // changeAccidental
330 : /// Change accidental to subtype \accidental for
331 : /// note \a note.
332 : //---------------------------------------------------------
333 :
334 241 : void EditNote::changeAccidental(Score* score, Note* note, AccidentalType accidental)
335 : {
336 241 : Chord* chord = note ? note->chord() : nullptr;
337 241 : if (!chord) {
338 0 : return;
339 : }
340 241 : Segment* segment = chord->segment();
341 241 : if (!segment) {
342 0 : return;
343 : }
344 241 : Measure* measure = segment->measure();
345 241 : if (!measure) {
346 0 : return;
347 : }
348 241 : Fraction tick = segment->tick();
349 241 : Staff* estaff = score->staff(chord->vStaffIdx());
350 241 : if (!estaff) {
351 0 : return;
352 : }
353 241 : ClefType clef = estaff->clef(tick);
354 241 : if (clef == ClefType::TAB
355 241 : || clef == ClefType::TAB4
356 241 : || clef == ClefType::TAB_SERIF
357 241 : || clef == ClefType::TAB4_SERIF) {
358 0 : return;
359 : }
360 241 : int step = ClefInfo::pitchOffset(clef) - note->line();
361 241 : while (step < 0) {
362 0 : step += 7;
363 : }
364 241 : step %= 7;
365 : //
366 : // accidental change may result in pitch change
367 : //
368 241 : AccidentalVal acc2 = measure->findAccidental(note);
369 241 : AccidentalVal acc = (accidental == AccidentalType::NONE) ? acc2 : Accidental::subtype2value(accidental);
370 :
371 241 : int pitch = line2pitch(note->line(), clef, Key::C) + int(acc);
372 241 : if (!note->concertPitch()) {
373 241 : pitch += note->transposition();
374 : }
375 :
376 241 : int tpc = step2tpc(step, acc);
377 :
378 241 : bool forceRemove = false;
379 241 : bool forceAdd = false;
380 :
381 : // delete accidental
382 : // both for this note and for any linked notes
383 241 : if (accidental == AccidentalType::NONE) {
384 177 : forceRemove = true;
385 : }
386 : // precautionary or microtonal accidental
387 : // either way, we display it unconditionally
388 : // both for this note and for any linked notes
389 44 : else if (acc == acc2 || (pitch == note->pitch() && !Accidental::isMicrotonal(note->accidentalType()))
390 108 : || Accidental::isMicrotonal(accidental)) {
391 49 : forceAdd = true;
392 : }
393 :
394 482 : for (EngravingObject* se : note->linkList()) {
395 241 : Note* ln = toNote(se);
396 241 : if (ln->concertPitch() != note->concertPitch()) {
397 0 : continue;
398 : }
399 241 : Score* lns = ln->score();
400 241 : Accidental* a = ln->accidental();
401 241 : if (forceRemove) {
402 177 : if (a) {
403 2 : lns->undoRemoveElement(a);
404 : }
405 177 : if (ln->tieBack()) {
406 0 : continue;
407 : }
408 64 : } else if (forceAdd) {
409 49 : if (a) {
410 0 : score->undoRemoveElement(a);
411 : }
412 49 : Accidental* a1 = Factory::createAccidental(ln);
413 49 : a1->setAccidentalType(accidental);
414 49 : a1->setRole(AccidentalRole::USER);
415 49 : lns->undoAddElement(a1);
416 15 : } else if (a && Accidental::isMicrotonal(a->accidentalType())) {
417 0 : lns->undoRemoveElement(a);
418 : }
419 241 : changeAccidental2(ln, pitch, tpc);
420 241 : }
421 241 : score->setPlayNote(true);
422 241 : score->setSelectionChanged(true);
423 : }
424 :
425 : //---------------------------------------------------------
426 : // upDownChromatic
427 : //---------------------------------------------------------
428 :
429 2 : void EditNote::upDownChromatic(bool up, int pitch, Note* n, Key key, int tpc1, int tpc2,
430 : int& newPitch, int& newTpc1, int& newTpc2)
431 : {
432 2 : bool concertPitch = n->concertPitch();
433 2 : AccidentalVal noteAccVal = tpc2alter(concertPitch ? tpc1 : tpc2);
434 2 : AccidentalVal accState = AccidentalVal::NATURAL;
435 2 : if (Measure* m = n->findMeasure()) {
436 2 : accState = m->findAccidental(n);
437 : }
438 2 : if (up && pitch < 127) {
439 0 : newPitch = pitch + 1;
440 0 : if (concertPitch) {
441 0 : if (tpc1 > Tpc::TPC_A + int(key) && noteAccVal >= accState) {
442 0 : newTpc1 = tpc1 - 5; // up semitone diatonic
443 : } else {
444 0 : newTpc1 = tpc1 + 7; // up semitone chromatic
445 : }
446 0 : newTpc2 = n->transposeTpc(newTpc1);
447 : } else {
448 0 : if (tpc2 > Tpc::TPC_A + int(key) && noteAccVal >= accState) {
449 0 : newTpc2 = tpc2 - 5; // up semitone diatonic
450 : } else {
451 0 : newTpc2 = tpc2 + 7; // up semitone chromatic
452 : }
453 0 : newTpc1 = n->transposeTpc(newTpc2);
454 : }
455 2 : } else if (!up && pitch > 0) {
456 2 : newPitch = pitch - 1;
457 2 : if (concertPitch) {
458 0 : if (tpc1 > Tpc::TPC_C + int(key) || noteAccVal > accState) {
459 0 : newTpc1 = tpc1 - 7; // down semitone chromatic
460 : } else {
461 0 : newTpc1 = tpc1 + 5; // down semitone diatonic
462 : }
463 0 : newTpc2 = n->transposeTpc(newTpc1);
464 : } else {
465 2 : if (tpc2 > Tpc::TPC_C + int(key) || noteAccVal > accState) {
466 2 : newTpc2 = tpc2 - 7; // down semitone chromatic
467 : } else {
468 0 : newTpc2 = tpc2 + 5; // down semitone diatonic
469 : }
470 2 : newTpc1 = n->transposeTpc(newTpc2);
471 : }
472 : }
473 2 : }
474 :
475 : //---------------------------------------------------------
476 : // upDown
477 : //---------------------------------------------------------
478 :
479 1 : void EditNote::upDown(Score* score, bool up, UpDownMode mode)
480 : {
481 1 : std::vector<Note*> el = score->selection().uniqueNotes();
482 :
483 1 : std::sort(el.begin(), el.end(), [up](Note* a, Note* b) {
484 2 : if (up) {
485 0 : return a->string() < b->string();
486 : } else {
487 2 : return a->string() > b->string();
488 : }
489 : });
490 :
491 3 : for (Note* oNote : el) {
492 2 : Fraction tick = oNote->chord()->tick();
493 2 : Staff* staff = oNote->staff();
494 2 : Part* part = oNote->originPart();
495 2 : Key key = staff->key(tick);
496 2 : int tpc1 = oNote->tpc1();
497 2 : int tpc2 = oNote->tpc2();
498 2 : int pitch = oNote->pitch();
499 2 : int pitchOffset = staff->pitchOffset(tick);
500 2 : int newTpc1 = tpc1; // default to unchanged
501 2 : int newTpc2 = tpc2; // default to unchanged
502 2 : int newPitch = pitch; // default to unchanged
503 2 : int string = oNote->string();
504 2 : int fret = oNote->fret();
505 :
506 2 : StaffGroup staffGroup = staff->staffType(oNote->chord()->tick())->group();
507 : // if not tab, check for instrument instead of staffType (for pitched to unpitched instrument changes)
508 2 : if (staffGroup != StaffGroup::TAB) {
509 2 : staffGroup = part->instrument(oNote->tick())->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
510 : }
511 :
512 2 : switch (staffGroup) {
513 0 : case StaffGroup::PERCUSSION:
514 : {
515 0 : const Drumset* ds = part->instrument(tick)->drumset();
516 0 : if (ds) {
517 0 : newPitch = up ? ds->nextPitch(pitch) : ds->prevPitch(pitch);
518 0 : newTpc1 = pitch2tpc(newPitch, Key::C, Prefer::NEAREST);
519 0 : newTpc2 = newTpc1;
520 : }
521 : }
522 0 : break;
523 0 : case StaffGroup::TAB:
524 : {
525 0 : const StringData* stringData = part->stringData(tick, staff->idx());
526 0 : switch (mode) {
527 0 : case UpDownMode::OCTAVE:
528 : {
529 0 : const StaffType* stt = staff->staffType(tick);
530 0 : string = stt->physStringToVisual(string);
531 0 : string += (up ? -1 : 1);
532 0 : if (string < 0 || string >= static_cast<int>(stringData->strings())) {
533 0 : return; // no next string to move to
534 : }
535 0 : string = stt->visualStringToPhys(string);
536 0 : fret = stringData->fret(pitch, string, staff, tick);
537 0 : if (fret == -1) {
538 0 : return;
539 : }
540 : }
541 0 : break;
542 :
543 0 : case UpDownMode::DIATONIC:
544 0 : upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
545 0 : break;
546 :
547 0 : case UpDownMode::CHROMATIC:
548 : {
549 0 : if (!stringData->frets()) {
550 0 : LOGD("upDown tab chromatic: no frets?");
551 0 : return;
552 : }
553 0 : fret += (up ? 1 : -1);
554 0 : if (fret < 0 || fret > stringData->frets()) {
555 0 : LOGD("upDown tab in-string: out of fret range");
556 0 : return;
557 : }
558 0 : upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
559 0 : if (newPitch + pitchOffset != stringData->getPitch(string, fret, staff, oNote->tick()) && !oNote->bendBack()) {
560 0 : LOGD("upDown tab in-string: pitch mismatch");
561 0 : return;
562 : }
563 0 : oNote->undoChangeProperty(Pid::FRET, fret);
564 : }
565 0 : break;
566 : }
567 : }
568 0 : break;
569 2 : case StaffGroup::STANDARD:
570 2 : switch (mode) {
571 0 : case UpDownMode::OCTAVE:
572 0 : if (up) {
573 0 : if (pitch < 116) {
574 0 : newPitch = pitch + 12;
575 : }
576 : } else {
577 0 : if (pitch > 11) {
578 0 : newPitch = pitch - 12;
579 : }
580 : }
581 0 : break;
582 :
583 2 : case UpDownMode::CHROMATIC:
584 2 : upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
585 2 : break;
586 :
587 0 : case UpDownMode::DIATONIC:
588 : {
589 0 : Note* firstTiedNote = oNote->firstTiedNote();
590 0 : int newLine = firstTiedNote->line() + (up ? -1 : 1);
591 0 : Staff* vStaff = score->staff(firstTiedNote->chord()->vStaffIdx());
592 :
593 0 : bool error = false;
594 0 : AccidentalVal accOffs = firstTiedNote->chord()->measure()->findAccidental(
595 0 : firstTiedNote->chord()->segment(), firstTiedNote->chord()->vStaffIdx(), newLine, error);
596 0 : if (error) {
597 0 : accOffs = Accidental::subtype2value(AccidentalType::NONE);
598 : }
599 0 : int nStep = absStep(newLine, vStaff->clef(tick));
600 0 : int octave = nStep / 7;
601 0 : int testPitch = step2pitch(nStep) + octave * 12 + int(accOffs);
602 :
603 0 : if (testPitch <= 127 && testPitch > 0) {
604 0 : newPitch = testPitch;
605 0 : newTpc1 = newTpc2 = step2tpc(nStep % 7, accOffs);
606 0 : if (firstTiedNote->concertPitch()) {
607 0 : newTpc2 = firstTiedNote->transposeTpc(newTpc1);
608 : } else {
609 0 : newPitch += vStaff->transpose(tick).chromatic;
610 0 : newTpc1 = firstTiedNote->transposeTpc(newTpc2);
611 : }
612 : }
613 : }
614 0 : break;
615 : }
616 2 : break;
617 : }
618 :
619 2 : if ((oNote->pitch() != newPitch) || (oNote->tpc1() != newTpc1) || oNote->tpc2() != newTpc2) {
620 2 : if (mode != UpDownMode::OCTAVE) {
621 2 : auto l = oNote->linkList();
622 4 : for (EngravingObject* e : l) {
623 2 : Note* ln = toNote(e);
624 2 : if (ln->accidental()) {
625 0 : score->doUndoRemoveElement(ln->accidental());
626 : }
627 : }
628 2 : }
629 2 : EditNote::undoChangePitch(score, oNote, newPitch, newTpc1, newTpc2);
630 2 : if (mode == UpDownMode::DIATONIC) {
631 0 : part->stringData(tick, staff->idx())->convertPitch(newPitch, staff, tick, &string, &fret);
632 0 : EditNote::undoChangeFretting(score, oNote, newPitch, string, fret, newTpc1, newTpc2);
633 : }
634 0 : } else if (staff->staffType(tick)->group() == StaffGroup::TAB) {
635 0 : bool refret = false;
636 0 : if (oNote->string() != string) {
637 0 : oNote->undoChangeProperty(Pid::STRING, string);
638 0 : refret = true;
639 : }
640 0 : if (oNote->fret() != fret) {
641 0 : oNote->undoChangeProperty(Pid::FRET, fret);
642 0 : refret = true;
643 : }
644 0 : if (refret) {
645 0 : const StringData* stringData = part->stringData(tick, staff->idx());
646 0 : stringData->fretChords(oNote->chord());
647 : }
648 : }
649 :
650 2 : score->setPlayNote(true);
651 : }
652 1 : score->setSelectionChanged(true);
653 1 : }
654 :
655 : //---------------------------------------------------------
656 : // undoChangePitch
657 : //---------------------------------------------------------
658 :
659 1796 : void EditNote::undoChangePitch(Score* score, Note* note, int pitch, int tpc1, int tpc2)
660 : {
661 1796 : Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
662 :
663 3594 : for (EngravingObject* e : note->linkList()) {
664 1798 : Note* n = toNote(e);
665 1798 : tx.push(new ChangePitch(n, pitch, tpc1, tpc2));
666 1796 : }
667 1796 : }
668 :
669 : //---------------------------------------------------------
670 : // undoChangeFretting
671 : //---------------------------------------------------------
672 :
673 0 : void EditNote::undoChangeFretting(Score* score, Note* note, int pitch, int string, int fret, int tpc1, int tpc2)
674 : {
675 0 : Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
676 :
677 0 : for (EngravingObject* e : note->linkList()) {
678 0 : Note* n = toNote(e);
679 0 : tx.push(new ChangeFretting(n, pitch, string, fret, tpc1, tpc2));
680 0 : }
681 0 : }
682 :
683 : //---------------------------------------------------------
684 : // ChangeVelocity
685 : //---------------------------------------------------------
686 :
687 0 : ChangeVelocity::ChangeVelocity(Note* n, int o)
688 0 : : note(n), userVelocity(o)
689 : {
690 0 : }
691 :
692 0 : void ChangeVelocity::flip()
693 : {
694 0 : int v = note->userVelocity();
695 0 : note->setUserVelocity(userVelocity);
696 0 : userVelocity = v;
697 0 : }
698 :
699 : //---------------------------------------------------------
700 : // ChangeNoteEventList::flip
701 : //---------------------------------------------------------
702 :
703 0 : void ChangeNoteEventList::flip()
704 : {
705 : // Get copy of current list.
706 0 : NoteEventList nel = note->playEvents();
707 : // Replace current copy with new list.
708 0 : note->setPlayEvents(newEvents);
709 : // Save copy of replaced list.
710 0 : newEvents = nel;
711 : // Get a copy of the current playEventType.
712 0 : PlayEventType petval = note->chord()->playEventType();
713 : // Replace current setting with new setting.
714 0 : note->chord()->setPlayEventType(newPetype);
715 : // Save copy of old setting.
716 0 : newPetype = petval;
717 0 : }
718 :
719 : //---------------------------------------------------------
720 : // ChangeNoteEvent::flip
721 : //---------------------------------------------------------
722 :
723 0 : void ChangeNoteEvent::flip()
724 : {
725 0 : NoteEvent e = *oldEvent;
726 0 : *oldEvent = newEvent;
727 0 : newEvent = e;
728 : // Get a copy of the current playEventType.
729 0 : PlayEventType petval = note->chord()->playEventType();
730 : // Replace current setting with new setting.
731 0 : note->chord()->setPlayEventType(newPetype);
732 : // Save copy of old setting.
733 0 : newPetype = petval;
734 0 : }
735 :
736 : //---------------------------------------------------------
737 : // ChangeChordPlayEventType::flip
738 : //---------------------------------------------------------
739 :
740 0 : void ChangeChordPlayEventType::flip()
741 : {
742 : // Flips data between NoteEventList's.
743 0 : size_t n = chord->notes().size();
744 0 : for (size_t i = 0; i < n; ++i) {
745 0 : Note* note = chord->notes()[i];
746 0 : note->playEvents().swap(events[int(i)]);
747 : }
748 : // Flips PlayEventType between chord and undo.
749 0 : PlayEventType curPetype = chord->playEventType();
750 0 : chord->setPlayEventType(petype);
751 0 : petype = curPetype;
752 0 : }
|