LCOV - code coverage report
Current view: top level - src/engraving/editing - editslashnotation.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 85.1 % 101 86
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) 2026 MuseScore Limited
       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 "editslashnotation.h"
      24              : 
      25              : #include "noteinput.h"
      26              : 
      27              : #include "containers.h"
      28              : 
      29              : #include "../dom/chord.h"
      30              : #include "../dom/durationtype.h"
      31              : #include "../dom/input.h"
      32              : #include "../dom/linkedobjects.h"
      33              : #include "../dom/measure.h"
      34              : #include "../dom/note.h"
      35              : #include "../dom/rest.h"
      36              : #include "../dom/score.h"
      37              : #include "../dom/segment.h"
      38              : #include "../dom/select.h"
      39              : #include "../dom/staff.h"
      40              : #include "../dom/stafftype.h"
      41              : 
      42              : using namespace mu::engraving;
      43              : 
      44              : //---------------------------------------------------------
      45              : //   slashFill
      46              : ///   fills selected region with slashes
      47              : //---------------------------------------------------------
      48              : 
      49            1 : void EditSlashNotation::slashFill(Transaction&, Score* score)
      50              : {
      51            1 :     staff_idx_t startStaff = score->selection().staffStart();
      52            1 :     staff_idx_t endStaff = score->selection().staffEnd();
      53            1 :     Segment* startSegment = score->selection().startSegment();
      54            1 :     if (!startSegment) { // empty score?
      55            0 :         return;
      56              :     }
      57              : 
      58            1 :     Segment* endSegment = score->selection().endSegment();
      59              : 
      60              :     // operate on measures underlying mmrests
      61            1 :     if (startSegment && startSegment->measure() && startSegment->measure()->isMMRest()) {
      62            0 :         startSegment = startSegment->measure()->mmRestFirst()->first();
      63              :     }
      64            1 :     if (endSegment && endSegment->measure() && endSegment->measure()->isMMRest()) {
      65            0 :         endSegment = endSegment->measure()->mmRestLast()->last();
      66              :     }
      67              : 
      68            1 :     Fraction endTick = endSegment ? endSegment->tick() : score->lastSegment()->tick() + Fraction::eps();
      69            1 :     Chord* firstSlash = 0;
      70            1 :     Chord* lastSlash = 0;
      71              : 
      72              :     // loop through staves in selection
      73            3 :     for (staff_idx_t staffIdx = startStaff; staffIdx < endStaff; ++staffIdx) {
      74            2 :         track_idx_t track = staffIdx * VOICES;
      75            2 :         voice_idx_t voice = muse::nidx;
      76              :         // loop through segments adding slashes on each beat
      77           36 :         for (Segment* s = startSegment; s && s->tick() < endTick; s = s->next1()) {
      78           34 :             if (s->segmentType() != SegmentType::ChordRest) {
      79            6 :                 continue;
      80              :             }
      81              :             // determine beat type based on time signature
      82           28 :             int d = s->measure()->timesig().denominator();
      83           28 :             int n = (d > 4 && s->measure()->timesig().numerator() % 3 == 0) ? 3 : 1;
      84           28 :             Fraction f(n, d);
      85              :             // skip over any leading segments before next (first) beat
      86           28 :             if (s->rtick().ticks() % f.ticks()) {
      87            0 :                 continue;
      88              :             }
      89              :             // determine voice to use - first available voice for this measure / staff
      90           28 :             if (voice == muse::nidx || s->rtick().isZero()) {
      91              :                 bool needGap[VOICES];
      92           11 :                 for (voice = 0; voice < VOICES; ++voice) {
      93           11 :                     needGap[voice] = false;
      94           11 :                     ChordRest* cr = toChordRest(s->element(track + voice));
      95              :                     // no chordrest == treat as ordinary rest for purpose of determining availability of voice
      96              :                     // but also, we will need to make a gap for this voice if we do end up choosing it
      97           11 :                     if (!cr) {
      98            3 :                         needGap[voice] = true;
      99              :                     }
     100              :                     // chord == keep looking for an available voice
     101            8 :                     else if (cr->isChord()) {
     102            2 :                         continue;
     103              :                     }
     104              :                     // full measure rest == OK to use voice
     105            6 :                     else if (cr->durationType() == DurationType::V_MEASURE) {
     106            4 :                         break;
     107              :                     }
     108              :                     // no chordrest or ordinary rest == OK to use voice
     109              :                     // if there are nothing but rests for duration of measure / selection
     110            5 :                     bool ok = true;
     111            9 :                     for (Segment* ns = s->next(SegmentType::ChordRest); ns && ns != endSegment; ns = ns->next(SegmentType::ChordRest)) {
     112            5 :                         ChordRest* ncr = toChordRest(ns->element(track + voice));
     113            5 :                         if (ncr && ncr->isChord()) {
     114            1 :                             ok = false;
     115            1 :                             break;
     116              :                         }
     117              :                     }
     118            5 :                     if (ok) {
     119            4 :                         break;
     120              :                     }
     121              :                 }
     122              :                 // no available voices, just use voice 0
     123            8 :                 if (voice == VOICES) {
     124            0 :                     voice = 0;
     125              :                 }
     126              :                 // no cr was found in segment for this voice, so make gap
     127            8 :                 if (needGap[voice]) {
     128            3 :                     score->makeGapVoice(s, track + voice, f, s->tick());
     129              :                 }
     130              :             }
     131              :             // construct note
     132           28 :             int line = 0;
     133           28 :             bool error = false;
     134           28 :             NoteVal nv;
     135           28 :             if (score->staff(staffIdx)->staffType(s->tick())->group() == StaffGroup::TAB) {
     136            0 :                 line = score->staff(staffIdx)->lines(s->tick()) / 2;
     137              :             } else {
     138           28 :                 line = score->staff(staffIdx)->middleLine(s->tick());             // staff(staffIdx)->lines() - 1;
     139              :             }
     140           28 :             if (score->staff(staffIdx)->staffType(s->tick())->group() == StaffGroup::PERCUSSION) {
     141           14 :                 nv.pitch = 0;
     142           14 :                 nv.headGroup = NoteHeadGroup::HEAD_SLASH;
     143              :             } else {
     144           14 :                 Position p;
     145           14 :                 p.segment = s;
     146           14 :                 p.staffIdx = staffIdx;
     147           14 :                 p.line = line;
     148           14 :                 p.fret = INVALID_FRET_INDEX;
     149           14 :                 score->inputState().setRest(false);             // needed for tab
     150           14 :                 nv = NoteInput::noteValForPosition(score, p, AccidentalType::NONE, error);
     151              :             }
     152           28 :             if (error) {
     153            0 :                 continue;
     154              :             }
     155              :             // insert & turn into slash
     156           28 :             s = score->setNoteRest(s, track + voice, nv, f);
     157           28 :             Chord* c = toChord(s->element(track + voice));
     158           28 :             if (c) {
     159           28 :                 if (c->links()) {
     160            0 :                     for (EngravingObject* e : *c->links()) {
     161            0 :                         Chord* lc = toChord(e);
     162            0 :                         lc->setSlash(true, true);
     163              :                     }
     164              :                 } else {
     165           28 :                     c->setSlash(true, true);
     166              :                 }
     167              :             }
     168           28 :             lastSlash = c;
     169           28 :             if (!firstSlash) {
     170            1 :                 firstSlash = c;
     171              :             }
     172              :         }
     173              :     }
     174              : 
     175              :     // re-select the slashes
     176            1 :     score->deselectAll();
     177            1 :     if (firstSlash && lastSlash) {
     178            1 :         score->select(firstSlash, SelectType::RANGE);
     179            1 :         score->select(lastSlash, SelectType::RANGE);
     180              :     }
     181              : }
     182              : 
     183              : //---------------------------------------------------------
     184              : //   slashRhythm
     185              : ///   converts rhythms in selected region to slashes
     186              : //---------------------------------------------------------
     187              : 
     188            1 : void EditSlashNotation::slashRhythm(Transaction&, Score* score)
     189              : {
     190            1 :     std::set<Chord*> chords;
     191              :     // loop through all notes in selection
     192          130 :     for (EngravingItem* e : score->selection().elements()) {
     193          129 :         if (e->voice() >= 2 && e->isRest()) {
     194            2 :             Rest* r = toRest(e);
     195            2 :             if (r->links()) {
     196            0 :                 for (EngravingObject* se : *r->links()) {
     197            0 :                     Rest* lr = toRest(se);
     198            0 :                     lr->setAccent(!lr->accent());
     199              :                 }
     200              :             } else {
     201            2 :                 r->setAccent(!r->accent());
     202              :             }
     203            2 :             continue;
     204          129 :         } else if (e->isNote()) {
     205           46 :             Note* n = toNote(e);
     206           46 :             if (n->noteType() != NoteType::NORMAL) {
     207            0 :                 continue;
     208              :             }
     209           46 :             Chord* c = n->chord();
     210              :             // check for duplicates (chords with multiple notes)
     211           46 :             if (muse::contains(chords, c)) {
     212            0 :                 continue;
     213              :             }
     214           46 :             chords.insert(c);
     215              :             // toggle slash setting
     216           46 :             c->setSlash(!c->slash(), false);
     217              :         }
     218              :     }
     219            1 : }
        

Generated by: LCOV version 2.0-1