LCOV - code coverage report
Current view: top level - src/engraving/editing - editclef.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 63.8 % 141 90
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 "editclef.h"
      24              : 
      25              : #include "../dom/chordrest.h"
      26              : #include "../dom/clef.h"
      27              : #include "../dom/factory.h"
      28              : #include "../dom/instrument.h"
      29              : #include "../dom/linkedobjects.h"
      30              : #include "../dom/measure.h"
      31              : #include "../dom/note.h"
      32              : #include "../dom/part.h"
      33              : #include "../dom/score.h"
      34              : #include "../dom/segment.h"
      35              : #include "../dom/staff.h"
      36              : 
      37              : #include "log.h"
      38              : 
      39              : using namespace mu::engraving;
      40              : 
      41              : // compute line position of noteheads after clef change
      42            2 : static void updateNoteLines(Segment* segment, track_idx_t track)
      43              : {
      44            2 :     Staff* staff = segment->score()->staff(track / VOICES);
      45            2 :     if (staff->isDrumStaff(segment->tick()) || staff->isTabStaff(segment->tick())) {
      46            0 :         return;
      47              :     }
      48           20 :     for (Segment* s = segment->next1(); s; s = s->next1()) {
      49           19 :         if ((s->segmentType() & (SegmentType::Clef | SegmentType::HeaderClef)) && s->element(track) && !s->element(track)->generated()) {
      50            1 :             break;
      51              :         }
      52           18 :         if (!s->isChordRestType()) {
      53           11 :             continue;
      54              :         }
      55           35 :         for (track_idx_t t = track; t < track + VOICES; ++t) {
      56           28 :             EngravingItem* e = s->element(t);
      57           28 :             if (e && e->isChord()) {
      58            3 :                 Chord* chord = toChord(e);
      59            6 :                 for (Note* n : chord->notes()) {
      60            3 :                     n->updateLine();
      61              :                 }
      62            3 :                 chord->sortNotes();
      63            3 :                 for (Chord* gc : chord->graceNotes()) {
      64            0 :                     for (Note* gn : gc->notes()) {
      65            0 :                         gn->updateLine();
      66              :                     }
      67            0 :                     gc->sortNotes();
      68              :                 }
      69              :             }
      70              :         }
      71              :     }
      72              : }
      73              : 
      74              : //---------------------------------------------------------
      75              : //   ChangeClefType
      76              : //---------------------------------------------------------
      77              : 
      78            2 : ChangeClefType::ChangeClefType(Clef* c, ClefType cl, ClefType tc)
      79              : {
      80            2 :     clef            = c;
      81            2 :     concertClef     = cl;
      82            2 :     transposingClef = tc;
      83            2 : }
      84              : 
      85            2 : void ChangeClefType::flip()
      86              : {
      87            2 :     ClefType ocl = clef->concertClef();
      88            2 :     ClefType otc = clef->transposingClef();
      89              : 
      90            2 :     clef->setConcertClef(concertClef);
      91            2 :     clef->setTransposingClef(transposingClef);
      92              : 
      93            2 :     clef->staff()->setClef(clef);
      94            2 :     Segment* segment = clef->segment();
      95            2 :     updateNoteLines(segment, clef->track());
      96            2 :     clef->triggerLayout();
      97            2 :     clef->score()->setLayout(clef->staff()->nextClefTick(segment->tick()), clef->staffIdx());
      98              : 
      99            2 :     concertClef     = ocl;
     100            2 :     transposingClef = otc;
     101            2 : }
     102              : 
     103              : //---------------------------------------------------------
     104              : //   EditClef
     105              : //---------------------------------------------------------
     106              : 
     107            0 : bool EditClef::canInsertClef(const Score* score, ClefType type)
     108              : {
     109            0 :     if (type == ClefType::INVALID) {
     110            0 :         return false;
     111              :     }
     112              : 
     113            0 :     const Staff* staff = score->staff(score->inputState().track() / VOICES);
     114            0 :     const ChordRest* cr = score->inputState().cr();
     115              : 
     116            0 :     return staff && cr;
     117              : }
     118              : 
     119            0 : void EditClef::insertClef(Transaction& tx, Score* score, ClefType type)
     120              : {
     121            0 :     undoChangeClef(tx, score, score->staff(score->inputState().track() / VOICES), score->inputState().cr(), type);
     122            0 : }
     123              : 
     124              : //---------------------------------------------------------
     125              : //   insertClef
     126              : //    insert clef before cr
     127              : //---------------------------------------------------------
     128              : 
     129            0 : void EditClef::insertClef(Transaction& tx, Score* score, Clef* clef, ChordRest* cr)
     130              : {
     131            0 :     undoChangeClef(tx, score, cr->staff(), cr, clef->clefType());
     132            0 :     delete clef;
     133            0 : }
     134              : 
     135              : //---------------------------------------------------------
     136              : //   undoChangeClef
     137              : //    change clef if e is a clef
     138              : //    else
     139              : //    create a clef before element e
     140              : //---------------------------------------------------------
     141              : 
     142           14 : void EditClef::undoChangeClef(Transaction&, Score* score, Staff* ostaff, EngravingItem* e, ClefType ct, bool forInstrumentChange,
     143              :                               Clef* clefToRelink)
     144              : {
     145           14 :     IF_ASSERT_FAILED(ostaff && e) {
     146            0 :         return;
     147              :     }
     148              : 
     149           14 :     bool moveClef = false;
     150           14 :     SegmentType st = SegmentType::Clef;
     151           14 :     if (e->isMeasure()) {
     152           13 :         if (toMeasure(e)->prevMeasure()) {
     153           12 :             moveClef = true;
     154              :         } else {
     155            1 :             st = SegmentType::HeaderClef;
     156              :         }
     157            1 :     } else if (e->isClef()) {
     158            0 :         Clef* clef = toClef(e);
     159            0 :         if (clef->segment()->isHeaderClefType()) {
     160            0 :             if (clef->measure()->prevMeasure()) {
     161            0 :                 moveClef = true;
     162              :             } else {
     163            0 :                 st = SegmentType::HeaderClef;
     164              :             }
     165            0 :         } else if (clef->rtick() == clef->measure()->ticks()) {
     166            0 :             moveClef = true;
     167              :         }
     168            1 :     } else if (e->rtick() == Fraction(0, 1)) {
     169            0 :         Measure* curMeasure = e->findMeasure();
     170            0 :         Measure* prevMeasure = curMeasure ? curMeasure->prevMeasure() : nullptr;
     171            0 :         if (prevMeasure && !prevMeasure->sectionBreak()) {
     172            0 :             moveClef = true;
     173              :         }
     174              :     }
     175              : 
     176           14 :     bool concertPitch = score->style().styleB(Sid::concertPitch);
     177           14 :     Clef* gclef = 0;
     178           14 :     Fraction tick = e->tick();
     179           14 :     Fraction rtick = e->rtick();
     180           14 :     bool isSmall = (st == SegmentType::Clef);
     181           29 :     for (Staff* staff : ostaff->staffList()) {
     182           15 :         if (clefToRelink && ostaff == staff) {
     183            1 :             continue;
     184              :         }
     185              : 
     186           14 :         Score* staffScore = staff->score();
     187           14 :         Measure* measure = staffScore->tick2measure(tick);
     188              : 
     189           14 :         if (!measure) {
     190            0 :             LOGW("measure for tick %d not found!", tick.ticks());
     191            0 :             continue;
     192              :         }
     193              : 
     194              :         Segment* destSeg;
     195           14 :         Fraction rt;
     196           14 :         if (moveClef) {                // if at start of measure and there is a previous measure
     197           12 :             measure = measure->prevMeasure();
     198           12 :             rt      = measure->ticks();
     199              :         } else {
     200            2 :             rt = rtick;
     201              :         }
     202           14 :         destSeg = measure->undoGetSegmentR(st, rt);
     203              : 
     204           14 :         staff_idx_t staffIdx = staff->idx();
     205           14 :         track_idx_t track    = staffIdx * VOICES;
     206           14 :         Clef* clef   = toClef(destSeg->element(track));
     207              : 
     208           14 :         StaffType* staffType = staff->staffType(e->tick());
     209           14 :         StaffGroup staffGroup = staffType->group();
     210           14 :         if (ClefInfo::staffGroup(ct) != staffGroup && !forInstrumentChange) {
     211            0 :             continue;
     212              :         }
     213              : 
     214           14 :         if (clef) {
     215              :             //
     216              :             // for transposing instruments, differentiate
     217              :             // clef type for concertPitch
     218              :             //
     219            2 :             Instrument* i = staff->part()->instrument(tick);
     220              :             ClefType cp, tp;
     221            2 :             if (i->transpose().isZero()) {
     222            2 :                 cp = ct;
     223            2 :                 tp = ct;
     224              :             } else {
     225            0 :                 if (concertPitch) {
     226            0 :                     cp = ct;
     227            0 :                     tp = clef->transposingClef();
     228              :                 } else {
     229            0 :                     cp = clef->concertClef();
     230            0 :                     tp = ct;
     231              :                 }
     232              :             }
     233            2 :             clef->setGenerated(false);
     234            2 :             staffScore->undo(new ChangeClefType(clef, cp, tp));
     235            2 :             Clef* oClef = clef->otherClef();
     236            2 :             if (oClef && !(oClef->generated())) {
     237            0 :                 staffScore->undo(new ChangeClefType(oClef, cp, tp));
     238              :             }
     239              :             // change the clef in the mmRest if any
     240            2 :             if (measure->hasMMRest()) {
     241            0 :                 Measure* mmMeasure = measure->mmRest();
     242            0 :                 Segment* mmDestSeg = mmMeasure->findSegment(SegmentType::Clef, tick);
     243            0 :                 if (mmDestSeg) {
     244            0 :                     Clef* mmClef = toClef(mmDestSeg->element(clef->track()));
     245            0 :                     if (mmClef) {
     246            0 :                         staffScore->undo(new ChangeClefType(mmClef, cp, tp));
     247              :                     }
     248              :                 }
     249              :             }
     250              :         } else {
     251           12 :             if (gclef) {
     252            0 :                 clef = toClef(gclef->linkedClone());
     253            0 :                 clef->setScore(staffScore);
     254            0 :                 clef->setOwnershipParent(destSeg);
     255              :             } else {
     256           12 :                 clef = Factory::createClef(destSeg);
     257           12 :                 clef->setClefType(ct);
     258           12 :                 gclef = clef;
     259              :             }
     260           12 :             clef->setTrack(track);
     261           12 :             staffScore->doUndoAddElement(clef);
     262              :         }
     263           14 :         if (forInstrumentChange) {
     264            0 :             clef->setForInstrumentChange(true);
     265              :         }
     266           14 :         clef->setSmall(isSmall);
     267              : 
     268           14 :         if (clefToRelink) {
     269            1 :             LinkedObjects* links = clef->links();
     270            1 :             if (!links) {
     271            1 :                 clef->linkTo(clefToRelink);
     272            0 :             } else if (!clef->isLinked(clefToRelink)) {
     273            0 :                 clefToRelink->setLinks(links);
     274            0 :                 links->push_back(clefToRelink);
     275              :             }
     276              :         }
     277           14 :     }
     278              : }
        

Generated by: LCOV version 2.0-1