LCOV - code coverage report
Current view: top level - src/engraving/dom - staff.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 75.6 % 824 623
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) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "containers.h"
      24              : 
      25              : #include "iengravingconfiguration.h" // IWYU pragma: keep
      26              : 
      27              : #include "types/typesconv.h"
      28              : 
      29              : #include "barline.h"
      30              : #include "clef.h"
      31              : #include "cleflist.h"
      32              : #include "excerpt.h"
      33              : #include "factory.h"
      34              : #include "instrtemplate.h"
      35              : #include "linkedobjects.h"
      36              : #include "masterscore.h"
      37              : #include "measure.h"
      38              : #include "mscore.h"
      39              : #include "note.h"
      40              : #include "ottava.h"
      41              : #include "part.h"
      42              : #include "score.h"
      43              : #include "segment.h"
      44              : #include "staff.h"
      45              : #include "stafftype.h"
      46              : #include "timesig.h"
      47              : #include "utils.h"
      48              : 
      49              : #include "../editing/editstaffbrackets.h"
      50              : #include "../editing/editcapo.h"
      51              : #include "../editing/transpose.h"
      52              : 
      53              : // #define DEBUG_CLEFS
      54              : 
      55              : #include "log.h"
      56              : 
      57              : using namespace mu;
      58              : using namespace mu::engraving;
      59              : 
      60              : #ifdef DEBUG_CLEFS
      61              : #define DUMP_CLEFS(s) dumpClefs(s)
      62              : #else
      63              : #define DUMP_CLEFS(s)
      64              : #endif
      65              : 
      66              : namespace mu::engraving {
      67              : //---------------------------------------------------------
      68              : //   Staff
      69              : //---------------------------------------------------------
      70              : 
      71         3558 : Staff::Staff(Part* parent)
      72        17790 :     : EngravingItem(ElementType::STAFF, parent)
      73              : {
      74         3558 :     m_color = configuration()->defaultColor();
      75         3558 :     initFromStaffType(0);
      76         3558 : }
      77              : 
      78              : //---------------------------------------------------------
      79              : //   copy ctor
      80              : //---------------------------------------------------------
      81              : 
      82            2 : Staff::Staff(const Staff& staff)
      83           10 :     : EngravingItem(staff)
      84              : {
      85            2 :     init(&staff);
      86            2 :     m_part = staff.m_part;
      87            2 : }
      88              : 
      89              : //---------------------------------------------------------
      90              : //   clone
      91              : //---------------------------------------------------------
      92              : 
      93            2 : Staff* Staff::clone() const
      94              : {
      95            2 :     return new Staff(*this);
      96              : }
      97              : 
      98              : //---------------------------------------------------------
      99              : //   idx
     100              : //---------------------------------------------------------
     101              : 
     102       376778 : staff_idx_t Staff::idx() const
     103              : {
     104       376778 :     return muse::indexOf(score()->staves(), (Staff*)this);
     105              : }
     106              : 
     107       263134 : Part* Staff::originPart(const Fraction& tick) const
     108              : {
     109              :     // Hides EngravingItem::originPart, whose track and tick are meaningless for a staff
     110              :     // Always returns part of voice 0
     111       263134 :     if (!m_part || !m_part->isSharedPart()) {
     112       261216 :         return m_part;
     113              :     }
     114              : 
     115         1918 :     return m_part->originPart(staff2track(idx(), 0), tick);
     116              : }
     117              : 
     118              : //---------------------------------------------------------
     119              : //   triggerLayout
     120              : //---------------------------------------------------------
     121              : 
     122          411 : void Staff::triggerLayout() const
     123              : {
     124          411 :     score()->setLayoutAll(idx(), this);
     125          411 : }
     126              : 
     127           36 : void Staff::triggerLayout(const Fraction& tick)
     128              : {
     129           36 :     score()->setLayout(tick, idx());
     130           36 : }
     131              : 
     132          105 : Staff* Staff::findLinkedInScore(const Score* score) const
     133              : {
     134          163 :     for (Staff* staff : score->staves()) {
     135          132 :         if (staff->id() == id()) {
     136           74 :             return staff;
     137              :         }
     138              :     }
     139              : 
     140           31 :     return nullptr;
     141              : }
     142              : 
     143         5979 : track_idx_t Staff::getLinkedTrackInStaff(const Staff* linkedStaff, const track_idx_t originalTrack) const
     144              : {
     145         5979 :     IF_ASSERT_FAILED(linkedStaff && originalTrack != muse::nidx) {
     146            0 :         return muse::nidx;
     147              :     }
     148              : 
     149         5979 :     Score* thisScore = score();
     150         5979 :     Score* linkedStaffScore = linkedStaff->score();
     151         5979 :     staff_idx_t linkedStaffIdx = linkedStaff->idx();
     152              : 
     153         5979 :     if (thisScore == linkedStaffScore) {
     154              :         // For staves linked within the same score, voices are always mapped 1 to 1
     155         5877 :         voice_idx_t voice = track2voice(originalTrack);
     156         5877 :         return staff2track(linkedStaffIdx, voice);
     157              :     }
     158              : 
     159              :     // NOTE 1: if linkedStaff has a different id than *this, it means that there isn't direct voice mapping between
     160              :     // the two. Example: if we have guitar+TAB in the score, with corresponding guitar+TAB in the part, *this
     161              :     // may be the notation-staff of the score and linkedStaff may be the TAB-staff of the part. In that case, the
     162              :     // correct voice mapping must be obtained by looking for the staff with same id in thisScore.
     163              : 
     164          102 :     track_idx_t refTrack = originalTrack;
     165          102 :     if (linkedStaff->id() != id()) {
     166            2 :         Staff* correspondingStaffInThisScore = linkedStaff->findLinkedInScore(thisScore);
     167            2 :         if (!correspondingStaffInThisScore) {
     168            0 :             return muse::nidx;
     169              :         }
     170            2 :         voice_idx_t originalVoice = track2voice(originalTrack);
     171            2 :         refTrack = staff2track(correspondingStaffInThisScore->idx(), originalVoice);
     172              :     }
     173              : 
     174              :     // NOTE 2: TracksMap is a map from a track in the *score* to track(s) in the *part*.
     175              :     // If we are in the master score, refTrack corresponds to one of the keys in the map, so we can retrieve
     176              :     // the linked tracks by simply querying the map by key. However, if we are in a part, we need to search for
     177              :     // refTrack among the *values* of the map, and the corresponding key gives us the linked track in the score.
     178              :     // If linkedStaff is *also* in a part, we need to do it in two steps: first find the linked track in the score,
     179              :     // then use it to find the linked track in linkeStaff's part.
     180              : 
     181          102 :     if (thisScore->isMaster()) {
     182           92 :         const TracksMap& tracksMap = linkedStaffScore->excerpt()->tracksMapping();
     183           92 :         std::vector<track_idx_t> linkedTracks = muse::values(tracksMap, refTrack);
     184           92 :         for (track_idx_t track : linkedTracks) {
     185           90 :             if (track2staff(track) == linkedStaffIdx) {
     186           90 :                 return track;
     187              :             }
     188              :         }
     189              : 
     190            2 :         return muse::nidx;
     191           92 :     }
     192              : 
     193           10 :     const TracksMap& thisTracksMap = thisScore->excerpt()->tracksMapping();
     194           10 :     track_idx_t linkedTrackInScore = muse::nidx;
     195           24 :     for (auto pair : thisTracksMap) {
     196           24 :         track_idx_t trackInScore = pair.first;
     197           24 :         std::vector<track_idx_t> tracksInPart = muse::values(thisTracksMap, trackInScore);
     198           38 :         for (track_idx_t trackInPart : tracksInPart) {
     199           24 :             if (trackInPart == refTrack) {
     200           10 :                 linkedTrackInScore = trackInScore;
     201           10 :                 break;
     202              :             }
     203              :         }
     204           24 :         if (linkedTrackInScore != muse::nidx) {
     205           10 :             break;
     206              :         }
     207           24 :     }
     208              : 
     209           10 :     if (linkedStaffScore->isMaster() || linkedTrackInScore == muse::nidx) {
     210           10 :         return linkedTrackInScore;
     211              :     }
     212              : 
     213            0 :     const TracksMap& linkedTracksMap = linkedStaffScore->excerpt()->tracksMapping();
     214            0 :     std::vector<track_idx_t> linkedTracks = muse::values(linkedTracksMap, linkedTrackInScore);
     215            0 :     for (track_idx_t track : linkedTracks) {
     216            0 :         if (track2staff(track) == linkedStaffIdx) {
     217            0 :             return track;
     218              :         }
     219              :     }
     220              : 
     221            0 :     return muse::nidx;
     222            0 : }
     223              : 
     224           17 : bool Staff::trackHasLinksInVoiceZero(track_idx_t track)
     225              : {
     226           34 :     for (Staff* linkedStaff : staffList()) {
     227           17 :         track_idx_t linkedTrack = getLinkedTrackInStaff(linkedStaff, track);
     228           17 :         if (linkedTrack != muse::nidx && track2voice(linkedTrack) == 0) {
     229            0 :             return true;
     230              :         }
     231           17 :     }
     232              : 
     233           17 :     return false;
     234              : }
     235              : 
     236            0 : void Staff::undoSetShowMeasureNumbers(bool show)
     237              : {
     238            0 :     bool isTopStave = score()->staves().front() == this;
     239            0 :     if (show) {
     240            0 :         undoChangeProperty(Pid::SHOW_MEASURE_NUMBERS, isTopStave ? AutoOnOff::AUTO : AutoOnOff::ON);
     241              :     } else {
     242            0 :         undoChangeProperty(Pid::SHOW_MEASURE_NUMBERS, isTopStave ? AutoOnOff::OFF : AutoOnOff::AUTO);
     243              :     }
     244            0 : }
     245              : 
     246        17098 : bool Staff::shouldShowMeasureNumbers() const
     247              : {
     248        17098 :     MeasureNumberPlacement placementMode = style().styleV(Sid::measureNumberPlacementMode).value<MeasureNumberPlacement>();
     249        17098 :     switch (placementMode) {
     250        17098 :     case MeasureNumberPlacement::ABOVE_SYSTEM:
     251        17098 :         return score()->staves().front() == this;
     252            0 :     case MeasureNumberPlacement::BELOW_SYSTEM:
     253            0 :         return score()->staves().back() == this;
     254            0 :     case MeasureNumberPlacement::ON_SYSTEM_OBJECT_STAVES:
     255              :     {
     256            0 :         bool isTopStave = score()->staves().front() == this;
     257            0 :         return (isTopStave && m_showMeasureNumbers != AutoOnOff::OFF) || (isSystemObjectStaff() && m_showMeasureNumbers == AutoOnOff::ON);
     258              :     }
     259            0 :     case MeasureNumberPlacement::ON_ALL_STAVES:
     260            0 :         return show();
     261              :     }
     262              : 
     263            0 :     return false;
     264              : }
     265              : 
     266         1320 : bool Staff::isLastOfScore() const
     267              : {
     268         1320 :     return score()->staves().empty() ? false : score()->staves().back() == this;
     269              : }
     270              : 
     271        49610 : bool Staff::isSystemObjectStaff() const
     272              : {
     273        49610 :     return score() && muse::contains(score()->systemObjectStaves(), const_cast<Staff*>(this));
     274              : }
     275              : 
     276        49295 : bool Staff::hasSystemObjectsBelowBottomStaff() const
     277              : {
     278        49295 :     return isSystemObjectStaff() && isLastOfScore() && style().styleB(Sid::systemObjectsBelowBottomStaff);
     279              : }
     280              : 
     281         5652 : String Staff::individualStaffNameLong(const Fraction& tick) const
     282              : {
     283         5652 :     IF_ASSERT_FAILED(m_part) {
     284            0 :         return String();
     285              :     }
     286              : 
     287         5652 :     return staffType(tick)->longName();
     288              : }
     289              : 
     290         3838 : String Staff::individualStaffNameShort(const Fraction& tick) const
     291              : {
     292         3838 :     IF_ASSERT_FAILED(m_part) {
     293            0 :         return String();
     294              :     }
     295              : 
     296         3838 :     return staffType(tick)->shortName();
     297              : }
     298              : 
     299         7876 : bool Staff::playbackVoice(int voice) const
     300              : {
     301         7876 :     return m_playbackVoice[voice];
     302              : }
     303              : 
     304            1 : void Staff::setPlaybackVoice(int voice, bool val)
     305              : {
     306            1 :     m_playbackVoice[voice] = val;
     307            1 : }
     308              : 
     309          521 : const std::array<bool, VOICES>& Staff::visibilityVoices() const
     310              : {
     311          521 :     return m_visibilityVoices;
     312              : }
     313              : 
     314           21 : bool Staff::isVoiceVisible(voice_idx_t voice) const
     315              : {
     316           21 :     if (voice >= VOICES) {
     317            0 :         return false;
     318              :     }
     319              : 
     320           21 :     return m_visibilityVoices[voice];
     321              : }
     322              : 
     323           18 : void Staff::setVoiceVisible(voice_idx_t voice, bool visible)
     324              : {
     325           18 :     if (voice >= VOICES) {
     326            0 :         return;
     327              :     }
     328              : 
     329           18 :     m_visibilityVoices[voice] = visible;
     330              : }
     331              : 
     332            0 : bool Staff::canDisableVoice() const
     333              : {
     334            0 :     auto voices = visibilityVoices();
     335            0 :     int countOfVisibleVoices = 0;
     336            0 :     for (size_t i = 0; i < voices.size(); ++i) {
     337            0 :         if (voices[i]) {
     338            0 :             countOfVisibleVoices++;
     339              :         }
     340              :     }
     341              : 
     342            0 :     return countOfVisibleVoices > 1;
     343              : }
     344              : 
     345          184 : void Staff::updateVisibilityVoices(const Staff* masterStaff, const TracksMap& tracks)
     346              : {
     347          184 :     if (tracks.empty()) {
     348            1 :         m_visibilityVoices = { true, true, true, true };
     349            1 :         return;
     350              :     }
     351              : 
     352          183 :     std::array<bool, VOICES> voices{ false, false, false, false };
     353              : 
     354          183 :     staff_idx_t masterStaffIdx = masterStaff->idx();
     355          183 :     staff_idx_t staffIdx = idx();
     356              : 
     357          183 :     voice_idx_t voiceIndex = 0;
     358          915 :     for (voice_idx_t voice = 0; voice < VOICES; voice++) {
     359          732 :         std::vector<track_idx_t> masterStaffTracks = muse::values(tracks, masterStaffIdx * VOICES + voice % VOICES);
     360          732 :         bool isVoiceVisible = muse::contains(masterStaffTracks, staffIdx * VOICES + voiceIndex % VOICES);
     361          732 :         if (isVoiceVisible) {
     362          715 :             voices[voice] = true;
     363          715 :             voiceIndex++;
     364              :         }
     365          732 :     }
     366              : 
     367          183 :     m_visibilityVoices = voices;
     368              : }
     369              : 
     370         1145 : bool Staff::reflectTranspositionInLinkedTab() const
     371              : {
     372         1145 :     return m_reflectTranspositionInLinkedTab;
     373              : }
     374              : 
     375            6 : void Staff::setReflectTranspositionInLinkedTab(bool reflect)
     376              : {
     377            6 :     m_reflectTranspositionInLinkedTab = reflect;
     378            6 : }
     379              : 
     380              : //---------------------------------------------------------
     381              : //   partName
     382              : //---------------------------------------------------------
     383              : 
     384          161 : String Staff::partName() const
     385              : {
     386          161 :     return m_part->partName();
     387              : }
     388              : 
     389              : //---------------------------------------------------------
     390              : //   Staff::clefType
     391              : //---------------------------------------------------------
     392              : 
     393        89659 : ClefTypeList Staff::clefType(const Fraction& tick) const
     394              : {
     395        89659 :     ClefTypeList ct = m_clefs.clef(tick.ticks());
     396        89659 :     if (ct.concertClef == ClefType::INVALID) {
     397              :         // Clef compatibility based on instrument (override StaffGroup)
     398        54509 :         StaffGroup staffGroup = staffType(tick)->group();
     399        54509 :         if (staffGroup != StaffGroup::TAB) {
     400        53847 :             staffGroup = originPart(tick)->instrument(tick)->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
     401              :         }
     402              : 
     403        54509 :         switch (staffGroup) {
     404          662 :         case StaffGroup::TAB:
     405              :         {
     406          662 :             ClefType sct = ClefType(style().styleI(Sid::tabClef));
     407          662 :             ct = staffType(tick)->lines() <= 4 ? ClefTypeList(sct == ClefType::TAB ? ClefType::TAB4 : ClefType::TAB4_SERIF) : ClefTypeList(
     408              :                 sct == ClefType::TAB ? ClefType::TAB : ClefType::TAB_SERIF);
     409              :         }
     410          662 :         break;
     411        52987 :         case StaffGroup::STANDARD:
     412        52987 :             ct = defaultClefType();
     413        52987 :             break;
     414          860 :         case StaffGroup::PERCUSSION:
     415          860 :             ct = ClefTypeList(ClefType::PERC);
     416          860 :             break;
     417              :         }
     418              :     }
     419        89659 :     return ct;
     420              : }
     421              : 
     422              : //---------------------------------------------------------
     423              : //   Staff::clef
     424              : //---------------------------------------------------------
     425              : 
     426        71557 : ClefType Staff::clef(const Fraction& tick) const
     427              : {
     428        71557 :     ClefTypeList c = clefType(tick);
     429       143114 :     return style().styleB(Sid::concertPitch) ? c.concertClef : c.transposingClef;
     430              : }
     431              : 
     432              : //---------------------------------------------------------
     433              : //   Staff::nextClefTick
     434              : //
     435              : //    return the tick of next clef after tick
     436              : //    return last tick of score if not found
     437              : //---------------------------------------------------------
     438              : 
     439           57 : Fraction Staff::nextClefTick(const Fraction& tick) const
     440              : {
     441           57 :     Fraction t = Fraction::fromTicks(m_clefs.nextClefTick(tick.ticks()));
     442           57 :     return t != Fraction(-1, 1) ? t : score()->endTick();
     443              : }
     444              : 
     445              : //---------------------------------------------------------
     446              : //   Staff::currentClefTick
     447              : //
     448              : //    return the tick position of the clef currently
     449              : //    in effect at tick
     450              : //    return 0, if no such clef
     451              : //---------------------------------------------------------
     452              : 
     453        13300 : Fraction Staff::currentClefTick(const Fraction& tick) const
     454              : {
     455        13300 :     return Fraction::fromTicks(m_clefs.currentClefTick(tick.ticks()));
     456              : }
     457              : 
     458            0 : String Staff::staffName() const
     459              : {
     460            0 :     return TConv::translatedUserName(clefType(Fraction()).transposingClef);
     461              : }
     462              : 
     463              : #ifndef NDEBUG
     464              : //---------------------------------------------------------
     465              : //   dumpClef
     466              : //---------------------------------------------------------
     467              : 
     468            0 : void Staff::dumpClefs(const char* title) const
     469              : {
     470            0 :     LOGD("(%zd): %s", m_clefs.size(), title);
     471            0 :     for (auto& i : m_clefs) {
     472            0 :         LOGD("  %d: %d %d", i.first, int(i.second.concertClef), int(i.second.transposingClef));
     473              :     }
     474            0 : }
     475              : 
     476              : //---------------------------------------------------------
     477              : //   dumpKeys
     478              : //---------------------------------------------------------
     479              : 
     480            0 : void Staff::dumpKeys(const char* title) const
     481              : {
     482            0 :     LOGD("(%zd): %s", m_keys.size(), title);
     483            0 :     for (auto& i : m_keys) {
     484            0 :         LOGD("  %d: %d", i.first, int(i.second.key()));
     485              :     }
     486            0 : }
     487              : 
     488              : //---------------------------------------------------------
     489              : //   dumpTimeSigs
     490              : //---------------------------------------------------------
     491              : 
     492            0 : void Staff::dumpTimeSigs(const char* title) const
     493              : {
     494            0 :     LOGD("size (%zd) staffIdx %zu: %s", m_timesigs.size(), idx(), title);
     495            0 :     for (auto& i : m_timesigs) {
     496            0 :         LOGD("  %d: %d/%d", i.first, i.second->sig().numerator(), i.second->sig().denominator());
     497              :     }
     498            0 : }
     499              : 
     500              : #endif
     501              : 
     502              : //---------------------------------------------------------
     503              : //   setClef
     504              : //---------------------------------------------------------
     505              : 
     506         1644 : void Staff::setClef(Clef* clef)
     507              : {
     508         1644 :     if (clef->generated()) {
     509            0 :         return;
     510              :     }
     511         1644 :     Fraction tick = clef->segment()->tick();
     512         3835 :     for (Segment* s = clef->segment()->next1(); s && s->tick() == tick; s = s->next1()) {
     513         4380 :         if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
     514           14 :             && s->element(clef->track())
     515         4382 :             && !s->element(clef->track())->generated()) {
     516              :             // adding this clef has no effect on the clefs list
     517            0 :             return;
     518              :         }
     519              :     }
     520         1644 :     m_clefs.setClef(clef->segment()->tick().ticks(), clef->clefTypeList());
     521              :     DUMP_CLEFS("setClef");
     522              : }
     523              : 
     524              : //---------------------------------------------------------
     525              : //   removeClef
     526              : //---------------------------------------------------------
     527              : 
     528           71 : void Staff::removeClef(const Clef* clef)
     529              : {
     530           71 :     if (clef->generated()) {
     531           52 :         return;
     532              :     }
     533           19 :     Fraction tick = clef->segment()->tick();
     534           67 :     for (Segment* s = clef->segment()->next1(); s && s->tick() == tick; s = s->next1()) {
     535           96 :         if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
     536            0 :             && s->element(clef->track())
     537           96 :             && !s->element(clef->track())->generated()) {
     538              :             // removal of this clef has no effect on the clefs list
     539            0 :             return;
     540              :         }
     541              :     }
     542           19 :     m_clefs.erase(clef->segment()->tick().ticks());
     543           30 :     for (Segment* s = clef->segment()->prev1(); s && s->tick() == tick; s = s->prev1()) {
     544           22 :         if ((s->segmentType() == SegmentType::Clef || s->segmentType() == SegmentType::HeaderClef)
     545            1 :             && s->element(clef->track())
     546           22 :             && !s->element(clef->track())->generated()) {
     547              :             // a previous clef at the same tick position gets valid
     548            0 :             m_clefs.setClef(tick.ticks(), toClef(s->element(clef->track()))->clefTypeList());
     549            0 :             break;
     550              :         }
     551              :     }
     552              :     DUMP_CLEFS("removeClef");
     553              : }
     554              : 
     555              : //---------------------------------------------------------
     556              : //   timeStretch
     557              : //---------------------------------------------------------
     558              : 
     559      1618214 : Fraction Staff::timeStretch(const Fraction& tick) const
     560              : {
     561      1618214 :     TimeSig* timesig = timeSig(tick);
     562      1618214 :     return timesig ? timesig->stretch() : Fraction(1, 1);
     563              : }
     564              : 
     565              : //---------------------------------------------------------
     566              : //   timeSig
     567              : //    lookup time signature before or at tick
     568              : //---------------------------------------------------------
     569              : 
     570      2044772 : TimeSig* Staff::timeSig(const Fraction& tick) const
     571              : {
     572      2044772 :     auto i = m_timesigs.upper_bound(tick.ticks());
     573      2044772 :     if (i != m_timesigs.begin()) {
     574      2016692 :         --i;
     575              :     }
     576      2044772 :     if (i == m_timesigs.end()) {
     577        27787 :         return 0;
     578      2016985 :     } else if (tick < Fraction::fromTicks(i->first)) {
     579          293 :         return 0;
     580              :     }
     581      2016692 :     return i->second;
     582              : }
     583              : 
     584              : //---------------------------------------------------------
     585              : //   nextTimeSig
     586              : //    lookup time signature at tick or after
     587              : //---------------------------------------------------------
     588              : 
     589            2 : TimeSig* Staff::nextTimeSig(const Fraction& tick) const
     590              : {
     591            2 :     auto i = m_timesigs.lower_bound(tick.ticks());
     592            2 :     return (i == m_timesigs.end()) ? 0 : i->second;
     593              : }
     594              : 
     595              : //---------------------------------------------------------
     596              : //   currentTimeSigTick
     597              : //
     598              : //    return the tick position of the time sig currently
     599              : //    in effect at tick
     600              : //---------------------------------------------------------
     601              : 
     602            0 : Fraction Staff::currentTimeSigTick(const Fraction& tick) const
     603              : {
     604            0 :     if (m_timesigs.empty()) {
     605            0 :         return Fraction(0, 1);
     606              :     }
     607            0 :     auto i = m_timesigs.upper_bound(tick.ticks());
     608            0 :     if (i == m_timesigs.begin()) {
     609            0 :         return Fraction(0, 1);
     610              :     }
     611            0 :     --i;
     612            0 :     return Fraction::fromTicks(i->first);
     613              : }
     614              : 
     615              : //---------------------------------------------------------
     616              : //   group
     617              : //---------------------------------------------------------
     618              : 
     619       152365 : const Groups& Staff::group(const Fraction& tick) const
     620              : {
     621       152365 :     TimeSig* ts = timeSig(tick);
     622       152365 :     if (ts) {
     623       152335 :         if (!ts->groups().empty()) {
     624            0 :             return ts->groups();
     625              :         }
     626       152335 :         return Groups::endings(ts->sig());
     627              :     }
     628           30 :     Measure* m = score()->tick2measure(tick);
     629           30 :     return Groups::endings(m ? m->timesig() : Fraction(4, 4));
     630              : }
     631              : 
     632              : //---------------------------------------------------------
     633              : //   addTimeSig
     634              : //---------------------------------------------------------
     635              : 
     636         7029 : void Staff::addTimeSig(TimeSig* timesig)
     637              : {
     638         7029 :     if (timesig->segment()->segmentType() == SegmentType::TimeSig) {
     639         6788 :         m_timesigs[timesig->segment()->tick().ticks()] = timesig;
     640              :     }
     641              : //      dumpTimeSigs("after addTimeSig");
     642         7029 : }
     643              : 
     644              : //---------------------------------------------------------
     645              : //   removeTimeSig
     646              : //---------------------------------------------------------
     647              : 
     648         3364 : void Staff::removeTimeSig(TimeSig* timesig)
     649              : {
     650         3364 :     if (timesig->segment()->segmentType() == SegmentType::TimeSig) {
     651         3170 :         if (m_timesigs[timesig->segment()->tick().ticks()] == timesig) {
     652         3134 :             m_timesigs.erase(timesig->segment()->tick().ticks());
     653              :         }
     654              :     }
     655              : //      dumpTimeSigs("after removeTimeSig");
     656         3364 : }
     657              : 
     658              : //---------------------------------------------------------
     659              : //   clearTimeSig
     660              : //---------------------------------------------------------
     661              : 
     662         3372 : void Staff::clearTimeSig()
     663              : {
     664         3372 :     m_timesigs.clear();
     665         3372 : }
     666              : 
     667              : //---------------------------------------------------------
     668              : //   Staff::transpose
     669              : //
     670              : //    actual staff transposioton at tick
     671              : //    (taking key into account)
     672              : //---------------------------------------------------------
     673              : 
     674        13876 : Interval Staff::transpose(const Fraction& tick) const
     675              : {
     676              :     // get real transposition
     677              : 
     678        13876 :     const Part* p = originPart(tick);
     679        13876 :     Interval v = p->instrument(tick)->transpose();
     680        13876 :     if (v.isZero()) {
     681        10205 :         return v;
     682              :     }
     683         3671 :     Key cKey = concertKey(tick);
     684         3671 :     v.flip();
     685         3671 :     Key tKey = Transpose::transposeKey(cKey, v, p->preferSharpFlat());
     686         3671 :     v.flip();
     687              : 
     688         3671 :     int chromatic = (7 * (static_cast<int>(cKey) - static_cast<int>(tKey))) % 12;
     689         3671 :     if (chromatic < 0) {
     690          716 :         chromatic += 12;
     691              :     }
     692         3671 :     int diatonic = (4 * (static_cast<int>(cKey) - static_cast<int>(tKey))) % 7;
     693         3671 :     if (diatonic < 0) {
     694          716 :         diatonic += 7;
     695              :     }
     696              : 
     697         3671 :     if (v.chromatic < 0 || v.diatonic < 0) {
     698         3661 :         chromatic -= 12;
     699         3661 :         diatonic -= 7;
     700              :     }
     701              : 
     702         3671 :     v.chromatic = v.chromatic - (v.chromatic % 12) + (chromatic % 12);
     703         3671 :     v.diatonic = v.diatonic - (v.diatonic % 7) + (diatonic % 7);
     704              : 
     705         3671 :     return v;
     706              : }
     707              : 
     708              : //---------------------------------------------------------
     709              : //   Staff::keySigEvent
     710              : //
     711              : //    locates the key sig currently in effect at tick
     712              : //---------------------------------------------------------
     713              : 
     714      1786807 : KeySigEvent Staff::keySigEvent(const Fraction& tick) const
     715              : {
     716      1786807 :     return m_keys.key(tick.ticks());
     717              : }
     718              : 
     719              : //---------------------------------------------------------
     720              : //   setKey
     721              : //---------------------------------------------------------
     722              : 
     723         3190 : void Staff::setKey(const Fraction& tick, KeySigEvent k)
     724              : {
     725         3190 :     m_keys.setKey(tick.ticks(), k);
     726         3190 : }
     727              : 
     728              : //---------------------------------------------------------
     729              : //   removeKey
     730              : //---------------------------------------------------------
     731              : 
     732           71 : void Staff::removeKey(const Fraction& tick)
     733              : {
     734           71 :     m_keys.erase(tick.ticks());
     735           71 : }
     736              : 
     737              : //---------------------------------------------------------
     738              : //   prevkey
     739              : //---------------------------------------------------------
     740              : 
     741            0 : KeySigEvent Staff::prevKey(const Fraction& tick) const
     742              : {
     743            0 :     return m_keys.prevKey(tick.ticks());
     744              : }
     745              : 
     746              : //---------------------------------------------------------
     747              : //   Staff::nextKeyTick
     748              : //
     749              : //    return the tick at which the key sig after tick is located
     750              : //    return 0, if no such a key sig
     751              : //---------------------------------------------------------
     752              : 
     753          363 : Fraction Staff::nextKeyTick(const Fraction& tick) const
     754              : {
     755          363 :     Fraction t = Fraction::fromTicks(m_keys.nextKeyTick(tick.ticks()));
     756          363 :     return t != Fraction(-1, 1) ? t : score()->endTick();
     757              : }
     758              : 
     759              : //---------------------------------------------------------
     760              : //   Staff::currentKeyTick
     761              : //
     762              : //    return the tick position of the key currently
     763              : //    in effect at tick
     764              : //    return 0, if no such a key sig
     765              : //---------------------------------------------------------
     766              : 
     767        57923 : Fraction Staff::currentKeyTick(const Fraction& tick) const
     768              : {
     769        57923 :     return Fraction::fromTicks(m_keys.currentKeyTick(tick.ticks()));
     770              : }
     771              : 
     772              : //---------------------------------------------------------
     773              : //   height
     774              : //---------------------------------------------------------
     775              : 
     776        32851 : double Staff::staffHeight() const
     777              : {
     778        32851 :     Fraction tick = Fraction(0, 1);
     779        32851 :     return (lines(tick) - 1) * spatium(tick) * staffType(tick)->lineDistance().val();
     780              : }
     781              : 
     782        15199 : double Staff::staffHeight(const Fraction& tick) const
     783              : {
     784        15199 :     return (lines(tick) - 1) * spatium(tick) * staffType(tick)->lineDistance().val();
     785              : }
     786              : 
     787              : //---------------------------------------------------------
     788              : //   spatium
     789              : //---------------------------------------------------------
     790              : 
     791       260954 : double Staff::spatium(const Fraction& tick) const
     792              : {
     793       260954 :     return style().spatium() * staffMag(tick);
     794              : }
     795              : 
     796      3810151 : double Staff::spatium(const EngravingItem* e) const
     797              : {
     798      3810151 :     return style().spatium() * staffMag(e);
     799              : }
     800              : 
     801              : //---------------------------------------------------------
     802              : //   mag
     803              : //---------------------------------------------------------
     804              : 
     805     11224818 : double Staff::staffMag(const StaffType* stt) const
     806              : {
     807     11224818 :     return (stt->isSmall() ? style().styleD(Sid::smallStaffMag) : 1.0) * stt->userMag();
     808              : }
     809              : 
     810       582247 : double Staff::staffMag(const Fraction& tick) const
     811              : {
     812       582247 :     return staffMag(staffType(tick));
     813              : }
     814              : 
     815     10642571 : double Staff::staffMag(const EngravingItem* element) const
     816              : {
     817     10642571 :     return staffMag(staffTypeForElement(element));
     818              : }
     819              : 
     820              : //---------------------------------------------------------
     821              : //   swing
     822              : //---------------------------------------------------------
     823              : 
     824         1391 : SwingParameters Staff::swing(const Fraction& tick) const
     825              : {
     826         1391 :     SwingParameters sp;
     827         1391 :     int swingUnit = 0;
     828         1391 :     muse::ByteArray ba = style().styleSt(Sid::swingUnit).toAscii();
     829         1391 :     DurationType unit = TConv::fromXml(ba.constChar(), DurationType::V_INVALID);
     830         1391 :     int swingRatio = style().styleI(Sid::swingRatio);
     831         1391 :     if (unit == DurationType::V_EIGHTH) {
     832            0 :         swingUnit = Constants::DIVISION / 2;
     833         1391 :     } else if (unit == DurationType::V_16TH) {
     834            0 :         swingUnit = Constants::DIVISION / 4;
     835         1391 :     } else if (unit == DurationType::V_ZERO) {
     836         1391 :         swingUnit = 0;
     837              :     }
     838         1391 :     sp.swingRatio = swingRatio;
     839         1391 :     sp.swingUnit = swingUnit;
     840         1391 :     if (m_swingMap.empty()) {
     841         1391 :         return sp;
     842              :     }
     843              : 
     844            0 :     auto it = muse::findLessOrEqual(m_swingMap, tick.ticks());
     845            0 :     return it == m_swingMap.cend() ? sp : it->second;
     846         1391 : }
     847              : 
     848        18290 : const CapoParams& Staff::capo(const Fraction& tick) const
     849              : {
     850        18290 :     static const CapoParams dummy;
     851        18290 :     if (m_capoMap.empty()) {
     852        17658 :         return dummy;
     853              :     }
     854              : 
     855          632 :     auto it = muse::findLessOrEqual(m_capoMap, tick.ticks());
     856          632 :     return it == m_capoMap.cend() ? dummy : it->second;
     857              : }
     858              : 
     859           65 : void Staff::insertCapoParams(const Fraction& tick, const CapoParams& params, bool ignoreNotationUpdate)
     860              : {
     861           65 :     if (ignoreNotationUpdate) {
     862           13 :         m_capoMap.insert_or_assign(tick.ticks(), params);
     863           13 :         return;
     864              :     }
     865           26 :     auto isNeedUpdate = [](const CapoParams& oldParams, const CapoParams& newParams) -> bool {
     866           44 :         return !(oldParams.active == newParams.active
     867           18 :                  && oldParams.transposeMode == newParams.transposeMode
     868            2 :                  && oldParams.fretPosition == newParams.fretPosition
     869           28 :                  && oldParams.ignoredStrings == newParams.ignoredStrings);
     870              :     };
     871              : 
     872           52 :     int startTick = tick.ticks();
     873           52 :     int endTick = -1;
     874              : 
     875           52 :     if (auto it = m_capoMap.find(startTick); it == m_capoMap.end()) {
     876           26 :         auto result = m_capoMap.insert({ startTick, params });
     877           26 :         if (const auto nextIt = std::next(result.first); nextIt != m_capoMap.end()) {
     878            0 :             endTick = nextIt->first;
     879              :         }
     880           26 :         if (result.first != m_capoMap.begin()) {
     881            6 :             const auto prevIt = std::prev(result.first);
     882            6 :             CapoParams oldParams = prevIt->second;
     883              :             // We don't need to apply any changes if the previous capo is inactive
     884            6 :             if (oldParams.active) {
     885            2 :                 EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
     886              :             }
     887              :             // This is an undo action
     888           26 :         } else if (CapoParams::TransposeMode::PLAYBACK_ONLY != params.transposeMode
     889           10 :                    && params.active) {
     890            8 :             CapoParams oldParams;
     891            8 :             oldParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
     892            8 :             oldParams.fretPosition = params.fretPosition;
     893            8 :             oldParams.active = params.active;
     894            8 :             oldParams.ignoredStrings = params.ignoredStrings;
     895            8 :             EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
     896            8 :         }
     897              :     } else {
     898           26 :         CapoParams oldParams = it->second;
     899           26 :         if (!isNeedUpdate(oldParams, params)) {
     900            0 :             return;
     901              :         }
     902           26 :         auto result = m_capoMap.insert_or_assign(startTick, params);
     903           26 :         if (const auto nextIt = std::next(result.first); nextIt != m_capoMap.end()) {
     904            0 :             endTick = nextIt->first;
     905              :         }
     906           26 :         EditCapo::updateNotationForCapoChange(oldParams, params, this, startTick, endTick);
     907           26 :     }
     908              : }
     909              : 
     910           10 : void Staff::removeCapoParams(const mu::engraving::Fraction& tick)
     911              : {
     912           10 :     const int startTick = tick.ticks();
     913              : 
     914           10 :     const auto it = m_capoMap.find(startTick);
     915           10 :     IF_ASSERT_FAILED(it != m_capoMap.end()) {
     916            0 :         LOGE() << "Key must exist in capo map!";
     917            0 :         return;
     918              :     }
     919              : 
     920           10 :     const auto nextCapoIt = std::next(it);
     921           10 :     const int endTick = nextCapoIt == m_capoMap.end() ? -1 : nextCapoIt->first;
     922              : 
     923           10 :     CapoParams revertParams;
     924           10 :     revertParams.fretPosition = it->second.fretPosition;
     925              : 
     926           10 :     CapoParams oldParams = it->second;
     927              :     // If this capo is inactive, it is treated as PLAYBACK_ONLY mode capo
     928           10 :     if (!oldParams.active) {
     929            2 :         oldParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
     930              :     }
     931              : 
     932           10 :     if (it != m_capoMap.begin()) {
     933            6 :         revertParams = std::prev(it)->second;
     934              :         // If not active, treat as PLAYBACK_ONLY
     935            6 :         if (!revertParams.active) {
     936            4 :             revertParams.transposeMode = CapoParams::TransposeMode::PLAYBACK_ONLY;
     937              :         }
     938              :     }
     939           10 :     EditCapo::updateNotationForCapoChange(oldParams, revertParams, this, startTick, endTick);
     940              : 
     941           10 :     m_capoMap.erase(startTick);
     942           10 : }
     943              : 
     944         2712 : bool Staff::shouldMergeMatchingRests() const
     945              : {
     946         2712 :     return mergeMatchingRests() == AutoOnOff::ON
     947         2712 :            || (mergeMatchingRests() == AutoOnOff::AUTO && style().value(Sid::mergeMatchingRests).toBool());
     948              : }
     949              : 
     950              : //---------------------------------------------------------
     951              : //   channel
     952              : //---------------------------------------------------------
     953              : 
     954        19180 : int Staff::channel(const Fraction& tick, voice_idx_t voice) const
     955              : {
     956        19180 :     const std::map<int, int>& map = m_channelList[voice];
     957        19180 :     if (map.empty()) {
     958        17312 :         return 0;
     959              :     }
     960              : 
     961         1868 :     auto it = muse::findLessOrEqual(map, tick.ticks());
     962         1868 :     return it == map.cend() ? 0 : it->second;
     963              : }
     964              : 
     965              : //---------------------------------------------------------
     966              : //   middleLine
     967              : //    returns logical line number of middle staff line
     968              : //---------------------------------------------------------
     969              : 
     970           30 : int Staff::middleLine(const Fraction& tick) const
     971              : {
     972           30 :     return staffType(tick)->middleLine();
     973              : }
     974              : 
     975              : //---------------------------------------------------------
     976              : //   bottomLine
     977              : //    returns logical line number of bottom staff line
     978              : //---------------------------------------------------------
     979              : 
     980            0 : int Staff::bottomLine(const Fraction& tick) const
     981              : {
     982            0 :     return staffType(tick)->bottomLine();
     983              : }
     984              : 
     985              : //---------------------------------------------------------
     986              : //   stemless
     987              : //---------------------------------------------------------
     988              : 
     989      2685601 : bool Staff::stemless(const Fraction& tick) const
     990              : {
     991      2685601 :     return staffType(tick)->stemless();
     992              : }
     993              : 
     994              : //---------------------------------------------------------
     995              : //   setSlashStyle
     996              : //---------------------------------------------------------
     997              : 
     998            0 : void Staff::setSlashStyle(const Fraction& tick, bool val)
     999              : {
    1000            0 :     staffType(tick)->setStemless(val);
    1001            0 : }
    1002              : 
    1003              : //---------------------------------------------------------
    1004              : //   isPrimaryStaff
    1005              : ///   if there are linked staves, the primary staff is
    1006              : ///   the one who is played back and it's not a tab staff
    1007              : ///   because we don't have enough information  to play
    1008              : ///   e.g ornaments. NOTE: it's not necessarily the top staff!
    1009              : //---------------------------------------------------------
    1010              : 
    1011         7065 : bool Staff::isPrimaryStaff() const
    1012              : {
    1013         7065 :     if (!m_links) {
    1014         4636 :         return true;
    1015              :     }
    1016              : 
    1017         2429 :     std::vector<const Staff*> linkedStavesInThisScore;
    1018         2429 :     std::vector<const Staff*> linkedNonTabStavesInThisScore;
    1019              : 
    1020         7287 :     for (const EngravingObject* linked : *m_links) {
    1021         4858 :         const Staff* staff = toStaff(linked);
    1022              : 
    1023         4858 :         if (staff->score() == score()) {
    1024         4856 :             linkedStavesInThisScore.push_back(staff);
    1025              : 
    1026         4856 :             if (!staff->isTabStaff(Fraction(0, 1))) {
    1027         2431 :                 linkedNonTabStavesInThisScore.push_back(staff);
    1028              :             }
    1029              :         }
    1030              :     }
    1031              : 
    1032         2429 :     IF_ASSERT_FAILED(!linkedStavesInThisScore.empty()) {
    1033            0 :         return true;
    1034              :     }
    1035              : 
    1036         2429 :     if (!linkedNonTabStavesInThisScore.empty()) {
    1037         2429 :         return linkedNonTabStavesInThisScore.front() == this;
    1038              :     }
    1039              : 
    1040            0 :     return linkedStavesInThisScore.front() == this;
    1041         2429 : }
    1042              : 
    1043              : //---------------------------------------------------------
    1044              : //   staffType
    1045              : //---------------------------------------------------------
    1046              : 
    1047      8656190 : const StaffType* Staff::staffType(const Fraction& tick) const
    1048              : {
    1049      8656190 :     return &m_staffTypeList.staffType(tick);
    1050              : }
    1051              : 
    1052        19057 : const StaffType* Staff::constStaffType(const Fraction& tick) const
    1053              : {
    1054        19057 :     return &m_staffTypeList.staffType(tick);
    1055              : }
    1056              : 
    1057       389873 : StaffType* Staff::staffType(const Fraction& tick)
    1058              : {
    1059       389873 :     return &m_staffTypeList.staffType(tick);
    1060              : }
    1061              : 
    1062     18018811 : const StaffType* Staff::staffTypeForElement(const EngravingItem* e) const
    1063              : {
    1064     18018811 :     if (m_staffTypeList.uniqueStaffType()) {
    1065              :         // if one staff type spans for the entire staff, optimize by omitting a call to `tick()`
    1066     17874393 :         return &m_staffTypeList.staffType({ 0, 1 });
    1067              :     }
    1068              :     // Handle items at the last tick of measures as StaffTypeList::staffType rounds up
    1069       144418 :     const Measure* measure = e->findMeasure();
    1070       144418 :     const Fraction tick = measure ? measure->tick() : e->tick();
    1071       144418 :     return &m_staffTypeList.staffType(tick);
    1072              : }
    1073              : 
    1074        55670 : bool Staff::isStaffTypeStartFrom(const Fraction& tick) const
    1075              : {
    1076        55670 :     return m_staffTypeList.isStaffTypeStartFrom(tick);
    1077              : }
    1078              : 
    1079            0 : void Staff::moveStaffType(const Fraction& from, const Fraction& to)
    1080              : {
    1081            0 :     m_staffTypeList.moveStaffType(from, to);
    1082            0 :     staffTypeListChanged(from);
    1083            0 : }
    1084              : 
    1085          538 : std::pair<int, int> Staff::staffTypeRange(const Fraction& tick) const
    1086              : {
    1087          538 :     return m_staffTypeList.staffTypeRange(tick);
    1088              : }
    1089              : 
    1090              : //---------------------------------------------------------
    1091              : //   staffTypeListChanged
    1092              : //    Signal that the staffTypeList has changed at
    1093              : //    position tick. Update layout range.
    1094              : //---------------------------------------------------------
    1095              : 
    1096           18 : void Staff::staffTypeListChanged(const Fraction& tick)
    1097              : {
    1098           18 :     std::pair<int, int> range = m_staffTypeList.staffTypeRange(tick);
    1099              : 
    1100           18 :     if (range.first < 0) {
    1101            0 :         triggerLayout(Fraction(0, 1));
    1102              :     } else {
    1103           18 :         triggerLayout(Fraction::fromTicks(range.first));
    1104              :     }
    1105              : 
    1106           18 :     if (range.second < 0) {
    1107              :         // When reading a score and there is a Staff Change on the first
    1108              :         // measure, there are no measures yet and nothing to layout.
    1109           18 :         if (score()->lastMeasure()) {
    1110           18 :             triggerLayout(score()->lastMeasure()->endTick());
    1111              :         }
    1112              :     } else {
    1113            0 :         triggerLayout(Fraction::fromTicks(range.second));
    1114              :     }
    1115           18 : }
    1116              : 
    1117              : //---------------------------------------------------------
    1118              : //   setStaffType
    1119              : //---------------------------------------------------------
    1120              : 
    1121         6203 : StaffType* Staff::setStaffType(const Fraction& tick, const StaffType& nst)
    1122              : {
    1123         6203 :     StaffType* stt = m_staffTypeList.setStaffType(tick, nst);
    1124         6203 :     stt->setScore(score());
    1125         6203 :     return stt;
    1126              : }
    1127              : 
    1128              : //---------------------------------------------------------
    1129              : //   setStaffType
    1130              : //---------------------------------------------------------
    1131              : 
    1132            0 : void Staff::removeStaffType(const Fraction& tick)
    1133              : {
    1134            0 :     double old = spatium(tick);
    1135            0 :     const bool removed = m_staffTypeList.removeStaffType(tick);
    1136            0 :     if (!removed) {
    1137            0 :         return;
    1138              :     }
    1139            0 :     setLocalSpatium(old, spatium(tick), tick);
    1140            0 :     staffTypeListChanged(tick);
    1141              : }
    1142              : 
    1143              : //---------------------------------------------------------
    1144              : //   init
    1145              : //---------------------------------------------------------
    1146              : 
    1147           18 : void Staff::init(const InstrumentTemplate* t, const StaffType* staffType, staff_idx_t templateStaffIdx)
    1148              : {
    1149              :     // Initialises a staff from an InstrumentTemplate and StaffType. The staff MUST be added to the score already.
    1150           18 :     assert(idx() != muse::nidx);
    1151              :     // set staff-type-independent parameters
    1152           18 :     const StaffType* pst = staffType ? staffType : t->staffTypePreset;
    1153           18 :     if (!pst) {
    1154            3 :         pst = StaffType::getDefaultPreset(t->staffGroup);
    1155              :     }
    1156              : 
    1157           18 :     const staff_idx_t staffIdx = idx() != muse::nidx ? idx() : score()->nstaves();
    1158              : 
    1159           18 :     StaffType* stt = setStaffType(Fraction(0, 1), *pst);
    1160           18 :     if (templateStaffIdx >= MAX_STAVES) {
    1161            0 :         stt->setSmall(false);
    1162              :     } else {
    1163           18 :         stt->setSmall(t->smallStaff[templateStaffIdx]);
    1164           18 :         EditStaffBrackets::setBracketType(score(), staffIdx, 0, t->bracket[templateStaffIdx]);
    1165           18 :         EditStaffBrackets::setBracketSpan(score(), staffIdx, 0, t->bracketSpan[templateStaffIdx]);
    1166           18 :         setBarLineSpan(t->barlineSpan[templateStaffIdx]);
    1167              :     }
    1168           18 :     setDefaultClefType(t->clefType(templateStaffIdx));
    1169           18 : }
    1170              : 
    1171              : //---------------------------------------------------------
    1172              : //   init
    1173              : //---------------------------------------------------------
    1174              : 
    1175           75 : void Staff::init(const Staff* s)
    1176              : {
    1177           75 :     m_id                = s->m_id;
    1178              : 
    1179           75 :     setStaffType(Fraction(0, 1), s->m_staffTypeList.staffType(Fraction(0, 1)));
    1180           75 :     for (const auto& stPair : s->m_staffTypeList.staffTypeChanges()) {
    1181            0 :         const StaffType& st = stPair.second;
    1182            0 :         StaffType newStaffType(st);
    1183            0 :         setStaffType(Fraction::fromTicks(stPair.first), newStaffType);
    1184            0 :     }
    1185              : 
    1186           75 :     setDefaultClefType(s->defaultClefType());
    1187           75 :     m_barLineFrom       = s->m_barLineFrom;
    1188           75 :     m_barLineTo         = s->m_barLineTo;
    1189           75 :     m_hideWhenEmpty     = s->m_hideWhenEmpty;
    1190           75 :     m_cutaway           = s->m_cutaway;
    1191           75 :     m_showIfEntireSystemEmpty = s->m_showIfEntireSystemEmpty;
    1192           75 :     m_hideSystemBarLine = s->m_hideSystemBarLine;
    1193           75 :     m_mergeMatchingRests = s->m_mergeMatchingRests;
    1194           75 :     m_color             = s->m_color;
    1195           75 :     m_userDist          = s->m_userDist;
    1196           75 :     m_visibilityVoices = s->m_visibilityVoices;
    1197           75 :     m_capoMap          = s->m_capoMap;
    1198           75 : }
    1199              : 
    1200        68889 : const ID& Staff::id() const
    1201              : {
    1202        68889 :     return m_id;
    1203              : }
    1204              : 
    1205         3732 : void Staff::setId(const ID& id)
    1206              : {
    1207         3732 :     m_id = id;
    1208         3732 : }
    1209              : 
    1210              : //---------------------------------------------------------
    1211              : //   initFromStaffType
    1212              : //---------------------------------------------------------
    1213              : 
    1214         3596 : void Staff::initFromStaffType(const StaffType* staffType)
    1215              : {
    1216              :     // get staff type if given (if none, get default preset for default staff group)
    1217         3596 :     if (!staffType) {
    1218         3592 :         staffType = StaffType::getDefaultPreset(StaffGroup::STANDARD);
    1219              :     }
    1220              : 
    1221              :     // use selected staff type
    1222         3596 :     setStaffType(Fraction(0, 1), *staffType);
    1223         3596 : }
    1224              : 
    1225              : //---------------------------------------------------------
    1226              : //   show
    1227              : //---------------------------------------------------------
    1228              : 
    1229      3304655 : bool Staff::show() const
    1230              : {
    1231      3304655 :     return m_part->show() && visible();
    1232              : }
    1233              : 
    1234              : //---------------------------------------------------------
    1235              : //   genKeySig
    1236              : //---------------------------------------------------------
    1237              : 
    1238            0 : bool Staff::genKeySig()
    1239              : {
    1240            0 :     if (constStaffType(Fraction(0, 1))->group() == StaffGroup::TAB) {
    1241            0 :         return false;
    1242              :     } else {
    1243            0 :         return constStaffType(Fraction(0, 1))->genKeysig();
    1244              :     }
    1245              : }
    1246              : 
    1247              : //---------------------------------------------------------
    1248              : //   showLedgerLines
    1249              : //---------------------------------------------------------
    1250              : 
    1251        14250 : bool Staff::showLedgerLines(const Fraction& tick) const
    1252              : {
    1253        14250 :     return staffType(tick)->showLedgerLines();
    1254              : }
    1255              : 
    1256              : //---------------------------------------------------------
    1257              : //   color
    1258              : //---------------------------------------------------------
    1259              : 
    1260        44704 : Color Staff::color(const Fraction& tick) const
    1261              : {
    1262        44704 :     return staffType(tick)->color();
    1263              : }
    1264              : 
    1265              : //---------------------------------------------------------
    1266              : //   setColor
    1267              : //---------------------------------------------------------
    1268              : 
    1269            0 : void Staff::setColor(const Fraction& tick, const Color& val)
    1270              : {
    1271            0 :     staffType(tick)->setColor(val);
    1272            0 : }
    1273              : 
    1274              : //---------------------------------------------------------
    1275              : //   updateOttava
    1276              : //---------------------------------------------------------
    1277              : 
    1278         2475 : void Staff::updateOttava()
    1279              : {
    1280         2475 :     staff_idx_t staffIdx = idx();
    1281         2475 :     m_pitchOffsetMap.clear();
    1282         5121 :     for (auto i : score()->spanner()) {
    1283         2646 :         const Spanner* s = i.second;
    1284         2646 :         if (s->isOttava() && s->staffIdx() == staffIdx && s->playSpanner()) {
    1285          223 :             const Ottava* o = toOttava(s);
    1286          223 :             m_pitchOffsetMap.insert_or_assign(o->tick().ticks(), o->pitchShift());
    1287          223 :             m_pitchOffsetMap.insert_or_assign(o->tick2().ticks(), 0);
    1288              :         }
    1289              :     }
    1290         2475 : }
    1291              : 
    1292        54630 : int Staff::pitchOffset(const Fraction& tick) const
    1293              : {
    1294        54630 :     if (m_pitchOffsetMap.empty()) {
    1295        52050 :         return 0;
    1296              :     }
    1297              : 
    1298         2580 :     const auto it = muse::findLessOrEqual(m_pitchOffsetMap, tick.ticks());
    1299         2580 :     return it == m_pitchOffsetMap.cend() ? 0 : it->second;
    1300              : }
    1301              : 
    1302              : //---------------------------------------------------------
    1303              : //   insertTime
    1304              : //---------------------------------------------------------
    1305              : 
    1306          568 : void Staff::insertTime(const Fraction& tick, const Fraction& len)
    1307              : {
    1308          568 :     if (len.isZero()) {
    1309            9 :         return;
    1310              :     }
    1311              : 
    1312              :     // move all keys and clefs >= tick
    1313              : 
    1314          559 :     if (len < Fraction(0, 1)) {
    1315              :         // remove entries between tickpos >= tick and tickpos < (tick+len)
    1316          149 :         m_keys.erase(m_keys.lower_bound(tick.ticks()), m_keys.lower_bound((tick - len).ticks()));
    1317          149 :         m_clefs.erase(m_clefs.lower_bound(tick.ticks()), m_clefs.lower_bound((tick - len).ticks()));
    1318              :     }
    1319              : 
    1320          559 :     KeyList kl2;
    1321          629 :     for (auto i = m_keys.lower_bound(tick.ticks()); i != m_keys.end();) {
    1322           70 :         KeySigEvent kse = i->second;
    1323           70 :         Fraction t = Fraction::fromTicks(i->first);
    1324           70 :         if (t + len < score()->endTick()) {
    1325           30 :             m_keys.erase(i++);
    1326           30 :             kl2[(t + len).ticks()] = kse;
    1327              :         } else {
    1328           40 :             ++i;
    1329              :         }
    1330           70 :     }
    1331          559 :     m_keys.insert(kl2.begin(), kl2.end());
    1332              : 
    1333              :     // check if there is a clef at the end of measure
    1334              :     // before tick
    1335          559 :     Clef* clef = 0;
    1336          559 :     Measure* m = score()->tick2measure(tick);
    1337          559 :     if (m && (m->tick() == tick) && (m->prevMeasure())) {
    1338          328 :         m = m->prevMeasure();
    1339          328 :         Segment* s = m->findSegment(SegmentType::Clef, tick);
    1340          328 :         if (s) {
    1341            3 :             track_idx_t track = idx() * VOICES;
    1342            3 :             clef = toClef(s->element(track));
    1343              :         }
    1344              :     }
    1345              : 
    1346          559 :     ClefList cl2;
    1347          599 :     for (auto i = m_clefs.lower_bound(tick.ticks()); i != m_clefs.end();) {
    1348           40 :         ClefTypeList ctl = i->second;
    1349           40 :         Fraction t = Fraction::fromTicks(i->first);
    1350           40 :         if (clef && tick == t) {
    1351            2 :             ++i;
    1352            2 :             continue;
    1353              :         }
    1354           38 :         m_clefs.erase(i++);
    1355           38 :         cl2.setClef((t + len).ticks(), ctl);
    1356              :     }
    1357          559 :     m_clefs.insert(cl2.begin(), cl2.end());
    1358              : 
    1359              :     // check if there is a clef at the end of measure
    1360              :     // before tick: do not remove from clefs list
    1361              : 
    1362          559 :     if (clef) {
    1363            3 :         setClef(clef);
    1364              :     }
    1365              : 
    1366          559 :     updateOttava();
    1367              :     DUMP_CLEFS("  insertTime");
    1368          559 : }
    1369              : 
    1370              : //---------------------------------------------------------
    1371              : //   staffList
    1372              : //    return list of linked staves
    1373              : //---------------------------------------------------------
    1374              : 
    1375         5963 : std::vector<Staff*> Staff::staffList() const
    1376              : {
    1377         5963 :     std::vector<Staff*> staffList;
    1378         5963 :     if (m_links) {
    1379          141 :         staffList.reserve(m_links->size());
    1380          427 :         for (EngravingObject* e : *m_links) {
    1381          286 :             staffList.push_back(toStaff(e));
    1382              :         }
    1383              :     } else {
    1384         5822 :         staffList.push_back(const_cast<Staff*>(this));
    1385              :     }
    1386         5963 :     return staffList;
    1387            0 : }
    1388              : 
    1389         1485 : Staff* Staff::primaryStaff() const
    1390              : {
    1391         1485 :     const LinkedObjects* linkedElements = links();
    1392         1485 :     if (!linkedElements) {
    1393          357 :         return nullptr;
    1394              :     }
    1395              : 
    1396         1128 :     for (auto linkedElement : *linkedElements) {
    1397         1128 :         Staff* staff = toStaff(linkedElement);
    1398         1128 :         if (staff && staff->isPrimaryStaff()) {
    1399         1128 :             return staff;
    1400              :         }
    1401              :     }
    1402              : 
    1403            0 :     return nullptr;
    1404              : }
    1405              : 
    1406              : //---------------------------------------------------------
    1407              : //   rstaff
    1408              : //---------------------------------------------------------
    1409              : 
    1410          308 : staff_idx_t Staff::rstaff() const
    1411              : {
    1412          308 :     return muse::indexOf(m_part->staves(), this);
    1413              : }
    1414              : 
    1415              : //---------------------------------------------------------
    1416              : //   isTop
    1417              : //---------------------------------------------------------
    1418              : 
    1419          294 : bool Staff::isTop() const
    1420              : {
    1421          294 :     if (m_part->staves().empty()) {
    1422            0 :         return false;
    1423              :     }
    1424              : 
    1425          294 :     return m_part->staves().front() == this;
    1426              : }
    1427              : 
    1428              : //---------------------------------------------------------
    1429              : //   getProperty
    1430              : //---------------------------------------------------------
    1431              : 
    1432        18110 : PropertyValue Staff::getProperty(Pid id) const
    1433              : {
    1434        18110 :     switch (id) {
    1435            0 :     case Pid::SMALL:
    1436            0 :         return staffType(Fraction(0, 1))->isSmall();
    1437            0 :     case Pid::MAG:
    1438            0 :         return staffType(Fraction(0, 1))->userMag();
    1439            0 :     case Pid::LINE_DISTANCE:
    1440            0 :         return staffType(Fraction(0, 1))->lineDistance();
    1441            0 :     case Pid::STAFF_INVISIBLE:
    1442            0 :         return staffType(Fraction(0, 1))->invisible();
    1443            0 :     case Pid::HIDE_WHEN_EMPTY:
    1444            0 :         return m_hideWhenEmpty;
    1445            0 :     case Pid::STAFF_COLOR:
    1446            0 :         return PropertyValue::fromValue(staffType(Fraction(0, 1))->color());
    1447         1969 :     case Pid::PLAYBACK_VOICE1:
    1448         1969 :         return playbackVoice(0);
    1449         1969 :     case Pid::PLAYBACK_VOICE2:
    1450         1969 :         return playbackVoice(1);
    1451         1969 :     case Pid::PLAYBACK_VOICE3:
    1452         1969 :         return playbackVoice(2);
    1453         1969 :     case Pid::PLAYBACK_VOICE4:
    1454         1969 :         return playbackVoice(3);
    1455         2224 :     case Pid::STAFF_BARLINE_SPAN:
    1456         2224 :         return barLineSpan();
    1457         1970 :     case Pid::STAFF_BARLINE_SPAN_FROM:
    1458         1970 :         return barLineFrom();
    1459         1970 :     case Pid::STAFF_BARLINE_SPAN_TO:
    1460         1970 :         return barLineTo();
    1461         1969 :     case Pid::STAFF_USERDIST:
    1462         1969 :         return userDist();
    1463           69 :     case Pid::GENERATED:
    1464           69 :         return false;
    1465         1969 :     case Pid::SHOW_MEASURE_NUMBERS:
    1466         1969 :         return m_showMeasureNumbers;
    1467            0 :     case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
    1468            0 :         return m_showIfEntireSystemEmpty;
    1469           27 :     case Pid::VISIBLE:
    1470           27 :         return visible();
    1471            9 :     case Pid::STAFF_CUTAWAY:
    1472            9 :         return cutaway();
    1473            9 :     case Pid::STAFF_HIDE_SYSTEM_BARLINE:
    1474            9 :         return hideSystemBarLine();
    1475            9 :     case Pid::STAFF_MERGE_MATCHING_RESTS:
    1476            9 :         return int(mergeMatchingRests());
    1477            9 :     case Pid::STAFF_REFLECT_TRANSPOSITION:
    1478            9 :         return reflectTranspositionInLinkedTab();
    1479            0 :     default:
    1480            0 :         LOGD("unhandled id <%s>", propertyName(id));
    1481            0 :         return PropertyValue();
    1482              :     }
    1483              : }
    1484              : 
    1485              : //---------------------------------------------------------
    1486              : //   setProperty
    1487              : //---------------------------------------------------------
    1488              : 
    1489          226 : bool Staff::setProperty(Pid id, const PropertyValue& v)
    1490              : {
    1491          226 :     switch (id) {
    1492            0 :     case Pid::SMALL: {
    1493            0 :         double _spatium = spatium(Fraction(0, 1));
    1494            0 :         staffType(Fraction(0, 1))->setSmall(v.toBool());
    1495            0 :         setLocalSpatium(_spatium, spatium(Fraction(0, 1)), Fraction(0, 1));
    1496            0 :         break;
    1497              :     }
    1498            9 :     case Pid::MAG: {
    1499            9 :         double _spatium = spatium(Fraction(0, 1));
    1500            9 :         staffType(Fraction(0, 1))->setUserMag(v.toReal());
    1501            9 :         setLocalSpatium(_spatium, spatium(Fraction(0, 1)), Fraction(0, 1));
    1502            9 :         break;
    1503              :     }
    1504            9 :     case Pid::LINE_DISTANCE: {
    1505            9 :         staffType(Fraction(0, 1))->setLineDistance(v.value<Spatium>());
    1506            9 :         break;
    1507              :     }
    1508            0 :     case Pid::HIDE_WHEN_EMPTY:
    1509            0 :         setHideWhenEmpty(v.value<AutoOnOff>());
    1510            0 :         break;
    1511            0 :     case Pid::STAFF_COLOR:
    1512            0 :         setColor(Fraction(0, 1), v.value<Color>());
    1513            0 :         break;
    1514            0 :     case Pid::PLAYBACK_VOICE1:
    1515            0 :         setPlaybackVoice(0, v.toBool());
    1516            0 :         break;
    1517            0 :     case Pid::PLAYBACK_VOICE2:
    1518            0 :         setPlaybackVoice(1, v.toBool());
    1519            0 :         break;
    1520            0 :     case Pid::PLAYBACK_VOICE3:
    1521            0 :         setPlaybackVoice(2, v.toBool());
    1522            0 :         break;
    1523            0 :     case Pid::PLAYBACK_VOICE4:
    1524            0 :         setPlaybackVoice(3, v.toBool());
    1525            0 :         break;
    1526          180 :     case Pid::STAFF_BARLINE_SPAN: {
    1527          180 :         setBarLineSpan(v.toBool());
    1528              :         // update non-generated barlines
    1529          180 :         track_idx_t track = idx() * VOICES;
    1530          180 :         std::vector<EngravingItem*> blList;
    1531         1837 :         for (Measure* m = score()->firstMeasure(); m; m = m->nextMeasure()) {
    1532         1657 :             Segment* s = m->getSegmentR(SegmentType::EndBarLine, m->ticks());
    1533         1657 :             if (s && s->element(track)) {
    1534          605 :                 blList.push_back(s->element(track));
    1535              :             }
    1536         1657 :             if (Measure* mm = m->mmRest()) {
    1537            0 :                 Segment* ss = mm->getSegmentR(SegmentType::EndBarLine, mm->ticks());
    1538            0 :                 if (ss && ss->element(track)) {
    1539            0 :                     blList.push_back(ss->element(track));
    1540              :                 }
    1541              :             }
    1542              :         }
    1543          785 :         for (EngravingItem* e : blList) {
    1544          605 :             if (e && e->isBarLine() && !e->generated()) {
    1545           85 :                 toBarLine(e)->setSpanStaff(barLineSpan());
    1546              :             }
    1547              :         }
    1548          180 :     }
    1549          180 :     break;
    1550            1 :     case Pid::STAFF_BARLINE_SPAN_FROM:
    1551            1 :         setBarLineFrom(v.toInt());
    1552            1 :         break;
    1553            1 :     case Pid::STAFF_BARLINE_SPAN_TO:
    1554            1 :         setBarLineTo(v.toInt());
    1555            1 :         break;
    1556            0 :     case Pid::STAFF_USERDIST:
    1557            0 :         setUserDist(v.value<Spatium>());
    1558            0 :         break;
    1559            0 :     case Pid::SHOW_MEASURE_NUMBERS:
    1560            0 :         m_showMeasureNumbers = v.value<AutoOnOff>();
    1561            0 :         break;
    1562            0 :     case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
    1563            0 :         m_showIfEntireSystemEmpty = v.toBool();
    1564            0 :         break;
    1565           10 :     case Pid::VISIBLE:
    1566           10 :         setVisible(v.toBool());
    1567           10 :         masterScore()->rebuildMidiMapping();
    1568           10 :         break;
    1569            4 :     case Pid::STAFF_CUTAWAY:
    1570            4 :         setCutaway(v.toBool());
    1571            4 :         break;
    1572            4 :     case Pid::STAFF_HIDE_SYSTEM_BARLINE:
    1573            4 :         setHideSystemBarLine(v.toBool());
    1574            4 :         break;
    1575            4 :     case Pid::STAFF_MERGE_MATCHING_RESTS:
    1576            4 :         setMergeMatchingRests(AutoOnOff(v.toInt()));
    1577            4 :         break;
    1578            4 :     case Pid::STAFF_REFLECT_TRANSPOSITION:
    1579            4 :         setReflectTranspositionInLinkedTab(v.toBool());
    1580            4 :         break;
    1581            0 :     default:
    1582            0 :         LOGD("unhandled id <%s>", propertyName(id));
    1583            0 :         break;
    1584              :     }
    1585          226 :     triggerLayout();
    1586          226 :     return true;
    1587              : }
    1588              : 
    1589              : //---------------------------------------------------------
    1590              : //   propertyDefault
    1591              : //---------------------------------------------------------
    1592              : 
    1593        17721 : PropertyValue Staff::propertyDefault(Pid id) const
    1594              : {
    1595        17721 :     switch (id) {
    1596            0 :     case Pid::SMALL:
    1597            0 :         return false;
    1598            0 :     case Pid::MAG:
    1599            0 :         return 1.0;
    1600            0 :     case Pid::HIDE_WHEN_EMPTY:
    1601            0 :         return AutoOnOff::AUTO;
    1602            0 :     case Pid::STAFF_COLOR:
    1603            0 :         return PropertyValue::fromValue(configuration()->defaultColor());
    1604         7876 :     case Pid::PLAYBACK_VOICE1:
    1605              :     case Pid::PLAYBACK_VOICE2:
    1606              :     case Pid::PLAYBACK_VOICE3:
    1607              :     case Pid::PLAYBACK_VOICE4:
    1608         7876 :         return true;
    1609         1969 :     case Pid::STAFF_BARLINE_SPAN:
    1610         1969 :         return false;
    1611         3938 :     case Pid::STAFF_BARLINE_SPAN_FROM:
    1612              :     case Pid::STAFF_BARLINE_SPAN_TO:
    1613         3938 :         return 0;
    1614         1969 :     case Pid::STAFF_USERDIST:
    1615         1969 :         return 0.0_sp;
    1616         1969 :     case Pid::SHOW_MEASURE_NUMBERS:
    1617         3938 :         return AutoOnOff::AUTO;
    1618            0 :     case Pid::SHOW_IF_ENTIRE_SYSTEM_EMPTY:
    1619            0 :         return false;
    1620            0 :     case Pid::VISIBLE:
    1621            0 :         return true;
    1622            0 :     case Pid::STAFF_CUTAWAY:
    1623            0 :         return false;
    1624            0 :     case Pid::STAFF_HIDE_SYSTEM_BARLINE:
    1625            0 :         return false;
    1626            0 :     case Pid::STAFF_MERGE_MATCHING_RESTS:
    1627            0 :         return int(AutoOnOff::AUTO);
    1628            0 :     case Pid::STAFF_REFLECT_TRANSPOSITION:
    1629            0 :         return true;
    1630            0 :     default:
    1631            0 :         LOGD("unhandled id <%s>", propertyName(id));
    1632            0 :         return PropertyValue();
    1633              :     }
    1634              : }
    1635              : 
    1636              : //---------------------------------------------------------
    1637              : //   localSpatiumChanged
    1638              : //---------------------------------------------------------
    1639              : 
    1640            9 : void Staff::setLocalSpatium(double oldVal, double newVal, Fraction tick)
    1641              : {
    1642            9 :     const int intEndTick = m_staffTypeList.staffTypeRange(tick).second;
    1643            9 :     const Fraction etick = (intEndTick == -1) ? score()->lastMeasure()->endTick() : Fraction::fromTicks(intEndTick);
    1644              : 
    1645            9 :     staff_idx_t staffIdx = idx();
    1646            9 :     track_idx_t startTrack = staffIdx * VOICES;
    1647            9 :     track_idx_t endTrack = startTrack + VOICES;
    1648           10 :     for (Segment* s = score()->tick2rightSegment(tick); s && s->tick() < etick; s = s->next1()) {
    1649            1 :         for (EngravingItem* e : s->annotations()) {
    1650            0 :             if (e->track() >= startTrack && e->track() < endTrack) {
    1651            0 :                 e->localSpatiumChanged(oldVal, newVal);
    1652              :             }
    1653              :         }
    1654            5 :         for (track_idx_t track = startTrack; track < endTrack; ++track) {
    1655            4 :             if (s->element(track)) {
    1656            0 :                 s->element(track)->localSpatiumChanged(oldVal, newVal);
    1657              :             }
    1658              :         }
    1659              :     }
    1660            9 :     auto spanners = score()->spannerMap().findContained(tick.ticks(), etick.ticks());
    1661            9 :     for (auto interval : spanners) {
    1662            0 :         Spanner* spanner = interval.value;
    1663            0 :         if (spanner->staffIdx() == staffIdx) {
    1664            0 :             for (auto k : spanner->spannerSegments()) {
    1665            0 :                 k->localSpatiumChanged(oldVal, newVal);
    1666              :             }
    1667              :         }
    1668              :     }
    1669            9 : }
    1670              : 
    1671              : //---------------------------------------------------------
    1672              : //   isPitchedStaff
    1673              : //---------------------------------------------------------
    1674              : 
    1675        64318 : bool Staff::isPitchedStaff(const Fraction& tick) const
    1676              : {
    1677              :     //return staffType(tick)->group() == StaffGroup::STANDARD;
    1678        64318 :     return staffType(tick)->group() != StaffGroup::TAB && !originPart(tick)->instrument(tick)->useDrumset();
    1679              : }
    1680              : 
    1681              : //---------------------------------------------------------
    1682              : //   isTabStaff
    1683              : //---------------------------------------------------------
    1684              : 
    1685       834514 : bool Staff::isTabStaff(const Fraction& tick) const
    1686              : {
    1687       834514 :     return staffType(tick)->group() == StaffGroup::TAB;
    1688              : }
    1689              : 
    1690              : //---------------------------------------------------------
    1691              : //   isDrumStaff
    1692              : //---------------------------------------------------------
    1693              : 
    1694        40538 : bool Staff::isDrumStaff(const Fraction& tick) const
    1695              : {
    1696              :     //check for instrument instead of staffType (for pitched to unpitched instr. changes)
    1697        40538 :     return originPart(tick)->instrument(tick)->useDrumset();
    1698              : }
    1699              : 
    1700              : //---------------------------------------------------------
    1701              : //   lines
    1702              : //---------------------------------------------------------
    1703              : 
    1704       547239 : int Staff::lines(const Fraction& tick) const
    1705              : {
    1706       547239 :     return staffType(tick)->lines();
    1707              : }
    1708              : 
    1709              : //---------------------------------------------------------
    1710              : //   setLines
    1711              : //---------------------------------------------------------
    1712              : 
    1713          761 : void Staff::setLines(const Fraction& tick, int val)
    1714              : {
    1715          761 :     staffType(tick)->setLines(val);
    1716          761 : }
    1717              : 
    1718              : //---------------------------------------------------------
    1719              : //   lineDistance
    1720              : //    distance between staff lines
    1721              : //---------------------------------------------------------
    1722              : 
    1723       339289 : double Staff::lineDistance(const Fraction& tick) const
    1724              : {
    1725       339289 :     return staffType(tick)->lineDistance().val();
    1726              : }
    1727              : 
    1728              : //---------------------------------------------------------
    1729              : //   invisible
    1730              : //---------------------------------------------------------
    1731              : 
    1732       119209 : bool Staff::isLinesInvisible(const Fraction& tick) const
    1733              : {
    1734       119209 :     return staffType(tick)->invisible();
    1735              : }
    1736              : 
    1737              : //---------------------------------------------------------
    1738              : //   setInvisible
    1739              : //---------------------------------------------------------
    1740              : 
    1741            0 : void Staff::setIsLinesInvisible(const Fraction& tick, bool val)
    1742              : {
    1743            0 :     staffType(tick)->setInvisible(val);
    1744            0 : }
    1745              : }
        

Generated by: LCOV version 2.0-1