LCOV - code coverage report
Current view: top level - src/engraving/editing - editnote.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 40.5 % 402 163
Test Date: 2026-11-05 04:07:02

            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 : }
        

Generated by: LCOV version 2.0-1