LCOV - code coverage report
Current view: top level - src/engraving/dom - system.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 69.8 % 559 390
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              : /**
      24              :  \file
      25              :  Implementation of classes SysStaff and System.
      26              : */
      27              : 
      28              : #include "system.h"
      29              : 
      30              : #include "iengravingconfiguration.h" // IWYU pragma: keep
      31              : 
      32              : #include "style/style.h"
      33              : 
      34              : #include "../editing/navigation.h"
      35              : 
      36              : #include "beam.h"
      37              : #include "box.h"
      38              : #include "bracket.h"
      39              : #include "bracketitem.h"
      40              : #include "chordrest.h"
      41              : #include "factory.h"
      42              : #include "measure.h"
      43              : #include "mscore.h"
      44              : #include "page.h"
      45              : #include "pagelockindicator.h"
      46              : #include "part.h"
      47              : #include "score.h"
      48              : #include "segment.h"
      49              : #include "spacer.h"
      50              : #include "spanner.h"
      51              : #include "staff.h"
      52              : #include "staffvisibilityindicator.h"
      53              : #include "system.h"
      54              : #include "systemdivider.h"
      55              : #include "systemlockindicator.h"
      56              : 
      57              : #ifndef ENGRAVING_NO_ACCESSIBILITY
      58              : #include "accessibility/accessibleitem.h"
      59              : #endif
      60              : 
      61              : #include "log.h"
      62              : 
      63              : using namespace mu;
      64              : using namespace mu::engraving;
      65              : 
      66              : namespace mu::engraving {
      67              : //---------------------------------------------------------
      68              : //   ~SysStaff
      69              : //---------------------------------------------------------
      70              : 
      71         9384 : SysStaff::~SysStaff()
      72              : {
      73        13096 :     for (auto& pair : m_instrumentNames) {
      74         3712 :         if (InstrumentName* n = pair.second) {
      75         3712 :             delete n;
      76              :         }
      77              :     }
      78         9384 : }
      79              : 
      80              : //---------------------------------------------------------
      81              : //   yBottom
      82              : //---------------------------------------------------------
      83              : 
      84            0 : double SysStaff::yBottom() const
      85              : {
      86            0 :     return skyline().south().valid() ? skyline().south().max() : m_height;
      87              : }
      88              : 
      89              : //---------------------------------------------------------
      90              : //   saveLayout
      91              : //---------------------------------------------------------
      92              : 
      93        10686 : void SysStaff::saveLayout()
      94              : {
      95        10686 :     m_height =  bbox().height();
      96        10686 :     m_yPos = bbox().y();
      97        10686 : }
      98              : 
      99              : //---------------------------------------------------------
     100              : //   saveLayout
     101              : //---------------------------------------------------------
     102              : 
     103        11702 : void SysStaff::restoreLayout()
     104              : {
     105        11702 :     bbox().setTop(m_yPos);
     106        11702 :     bbox().setHeight(m_height);
     107        11702 : }
     108              : 
     109       426608 : InstrumentName* SysStaff::name(InstrumentNameRole role) const
     110              : {
     111       426608 :     if (muse::contains(m_instrumentNames, role)) {
     112        40159 :         return m_instrumentNames.at(role);
     113              :     }
     114              : 
     115       386449 :     return nullptr;
     116              : }
     117              : 
     118         3771 : void SysStaff::addInstrumentName(InstrumentName* n)
     119              : {
     120         3771 :     InstrumentNameRole role = n->instrumentNameRole();
     121         3771 :     if (InstrumentName* curName = name(role)) {
     122            0 :         delete curName;
     123              :     }
     124              : 
     125         3771 :     m_instrumentNames[role] = n;
     126         3771 : }
     127              : 
     128            0 : void SysStaff::removeInstrumentName(InstrumentNameRole role)
     129              : {
     130            0 :     if (InstrumentName* n = name(role)) {
     131            0 :         delete n;
     132              :     }
     133              : 
     134            0 :     m_instrumentNames[role] = nullptr;
     135            0 : }
     136              : 
     137              : //---------------------------------------------------------
     138              : //   System
     139              : //---------------------------------------------------------
     140              : 
     141         8361 : System::System(Score* parent)
     142         8361 :     : EngravingItem(ElementType::SYSTEM, parent)
     143              : {
     144         8361 : }
     145              : 
     146            0 : void System::setOwnershipParent(Score* score)
     147              : {
     148            0 :     EngravingItem::setOwnershipParent(score);
     149            0 : }
     150              : 
     151       854260 : EngravingItem* System::layoutParent() const
     152              : {
     153       854260 :     return m_page;
     154              : }
     155              : 
     156              : //---------------------------------------------------------
     157              : //   ~System
     158              : //---------------------------------------------------------
     159              : 
     160        15792 : System::~System()
     161              : {
     162         7896 :     if (m_page) {
     163         7870 :         muse::remove(m_page->systems(), this);
     164              :     }
     165        11540 :     for (SpannerSegment* ss : spannerSegments()) {
     166         3644 :         if (ss->system() == this) {
     167         3644 :             ss->setSystem(nullptr);
     168              :         }
     169              :     }
     170        35600 :     for (MeasureBase* mb : measures()) {
     171        27704 :         if (mb->system() == this) {
     172        16882 :             mb->setSystem(nullptr);
     173              :         }
     174              :     }
     175         7896 :     muse::DeleteAll(m_staves);
     176         7896 :     muse::DeleteAll(m_brackets);
     177         7896 :     muse::DeleteAll(m_systemLockIndicators);
     178         7896 :     if (m_staffVisibilityIndicator) {
     179           47 :         delete m_staffVisibilityIndicator;
     180              :     }
     181         7896 :     if (m_pageLockIndicator) {
     182           32 :         delete m_pageLockIndicator;
     183              :     }
     184        15792 : }
     185              : 
     186              : #ifndef ENGRAVING_NO_ACCESSIBILITY
     187            0 : AccessibleItemPtr System::createAccessible()
     188              : {
     189            0 :     return std::make_shared<AccessibleItem>(this, AccessibleItem::Group);
     190              : }
     191              : 
     192              : #endif
     193              : 
     194              : //---------------------------------------------------------
     195              : ///   clear
     196              : ///   Clear layout of System
     197              : //---------------------------------------------------------
     198              : 
     199         1158 : void System::clear()
     200              : {
     201         4919 :     for (MeasureBase* mb : measures()) {
     202         3761 :         if (mb->system() == this) {
     203         3715 :             mb->setSystem(nullptr);
     204              :         }
     205              :     }
     206         1158 :     m_ml.clear();
     207         1800 :     for (SpannerSegment* ss : m_spannerSegments) {
     208          642 :         if (ss->system() == this) {
     209          642 :             ss->setSystem(nullptr);
     210              :         }
     211              :     }
     212         1158 :     m_spannerSegments.clear();
     213              :     // _systemDividers are reused
     214         1158 : }
     215              : 
     216              : //---------------------------------------------------------
     217              : //   appendMeasure
     218              : //---------------------------------------------------------
     219              : 
     220        34456 : void System::appendMeasure(MeasureBase* mb)
     221              : {
     222        34456 :     assert(!mb->isMeasure() || !(style().styleB(Sid::createMultiMeasureRests) && toMeasure(mb)->hasMMRest()));
     223        34456 :     mb->setSystem(this);
     224        34456 :     m_ml.push_back(mb);
     225        34456 : }
     226              : 
     227              : //---------------------------------------------------------
     228              : //   removeMeasure
     229              : //---------------------------------------------------------
     230              : 
     231          131 : void System::removeMeasure(MeasureBase* mb)
     232              : {
     233          131 :     m_ml.erase(std::remove(m_ml.begin(), m_ml.end(), mb), m_ml.end());
     234          131 :     if (mb->system() == this) {
     235          131 :         mb->setSystem(nullptr);
     236              :     }
     237          131 : }
     238              : 
     239              : //---------------------------------------------------------
     240              : //   removeLastMeasure
     241              : //---------------------------------------------------------
     242              : 
     243         1652 : void System::removeLastMeasure()
     244              : {
     245         1652 :     if (m_ml.empty()) {
     246            0 :         return;
     247              :     }
     248         1652 :     MeasureBase* mb = m_ml.back();
     249         1652 :     m_ml.pop_back();
     250         1652 :     if (mb->system() == this) {
     251         1652 :         mb->setSystem(nullptr);
     252              :     }
     253              : }
     254              : 
     255           29 : EngravingItemList System::accessibleChildren() const
     256              : {
     257              :     // The measures are owned by the score and the spanner segments by their spanner,
     258              :     // but it is the system that places them, so the system is where the accessibility
     259              :     // tree finds them.
     260           29 :     EngravingItemList children = EngravingItem::accessibleChildren();
     261           29 :     children.insert(children.end(), m_ml.begin(), m_ml.end());
     262           29 :     children.insert(children.end(), m_spannerSegments.begin(), m_spannerSegments.end());
     263              : 
     264           29 :     return children;
     265            0 : }
     266              : 
     267              : //---------------------------------------------------------
     268              : //   vbox
     269              : //    a system can only contain one vertical frame
     270              : //---------------------------------------------------------
     271              : 
     272       130683 : Box* System::vbox() const
     273              : {
     274       130683 :     if (!m_ml.empty() && m_ml.front()->isVBoxBase()) {
     275        18377 :         return toBox(m_ml.front());
     276              :     }
     277       112306 :     return nullptr;
     278              : }
     279              : 
     280              : //---------------------------------------------------------
     281              : //   insertStaff
     282              : //---------------------------------------------------------
     283              : 
     284         9951 : SysStaff* System::insertStaff(int idx)
     285              : {
     286         9951 :     SysStaff* staff = new SysStaff;
     287         9951 :     if (idx) {
     288              :         // HACK: guess position
     289         3479 :         staff->bbox().setTop(m_staves[idx - 1]->y() + 6 * spatium());
     290              :     }
     291         9951 :     m_staves.insert(m_staves.begin() + idx, staff);
     292         9951 :     return staff;
     293              : }
     294              : 
     295              : //---------------------------------------------------------
     296              : //   removeStaff
     297              : //---------------------------------------------------------
     298              : 
     299           59 : void System::removeStaff(int idx)
     300              : {
     301           59 :     m_staves.erase(m_staves.begin() + idx);
     302           59 : }
     303              : 
     304              : //---------------------------------------------------------
     305              : //   adjustStavesNumber
     306              : //---------------------------------------------------------
     307              : 
     308         7485 : void System::adjustStavesNumber(size_t nstaves)
     309              : {
     310        17436 :     for (size_t i = m_staves.size(); i < nstaves; ++i) {
     311         9951 :         insertStaff(static_cast<int>(i));
     312              :     }
     313         7485 :     const size_t dn = m_staves.size() - nstaves;
     314         7544 :     for (size_t i = 0; i < dn; ++i) {
     315           59 :         removeStaff(static_cast<int>(m_staves.size()) - 1);
     316              :     }
     317         7485 : }
     318              : 
     319        14470 : size_t System::getBracketsColumnsCount() const
     320              : {
     321        14470 :     size_t columns = 0;
     322        36404 :     for (const Staff* staff : score()->staves()) {
     323        27894 :         for (auto bi : score()->brackets(staff->idx())) {
     324         5960 :             columns = std::max(columns, bi->column() + 1);
     325              :         }
     326              :     }
     327        14470 :     return columns;
     328              : }
     329              : 
     330         7182 : void System::setHasStaffVisibilityIndicator(bool has)
     331              : {
     332         7182 :     if (has && !m_staffVisibilityIndicator) {
     333           48 :         m_staffVisibilityIndicator = Factory::createStaffVisibilityIndicator(this);
     334         7134 :     } else if (!has && m_staffVisibilityIndicator) {
     335            0 :         delete m_staffVisibilityIndicator;
     336            0 :         m_staffVisibilityIndicator = nullptr;
     337              :     }
     338         7182 : }
     339              : 
     340           38 : bool System::isLocked() const
     341              : {
     342           38 :     return m_ml.front()->isStartOfSystemLock();
     343              : }
     344              : 
     345        10195 : const RangeLock* System::systemLock() const
     346              : {
     347        10195 :     return m_ml.front()->systemLock();
     348              : }
     349              : 
     350           99 : void System::addSystemLockIndicator(SystemLockIndicator* sli)
     351              : {
     352           99 :     assert(sli);
     353           99 :     m_systemLockIndicators.push_back(sli);
     354           99 : }
     355              : 
     356        10195 : void System::deleteSystemLockIndicators()
     357              : {
     358        10195 :     muse::DeleteAll(m_systemLockIndicators);
     359        10195 :     m_systemLockIndicators.clear();
     360        10195 : }
     361              : 
     362           56 : void System::setPageLockIndicator(PageLockIndicator* pli)
     363              : {
     364           56 :     m_pageLockIndicator = pli;
     365           56 : }
     366              : 
     367        10195 : void System::deletePageLockIndicator()
     368              : {
     369        10195 :     delete m_pageLockIndicator;
     370        10195 :     m_pageLockIndicator = nullptr;
     371        10195 : }
     372              : 
     373              : //---------------------------------------------------------
     374              : //   nextVisibleStaff
     375              : //---------------------------------------------------------
     376              : 
     377       321522 : staff_idx_t System::firstVisibleStaffFrom(staff_idx_t startStaffIdx) const
     378              : {
     379       326748 :     for (staff_idx_t i = startStaffIdx; i < m_staves.size(); ++i) {
     380       108208 :         Staff* s  = score()->staff(i);
     381       108208 :         SysStaff* ss = m_staves[i];
     382              : 
     383       108208 :         if (s->show() && ss->show()) {
     384       102982 :             return i;
     385              :         }
     386              :     }
     387              : 
     388       218540 :     return muse::nidx;
     389              : }
     390              : 
     391       319134 : staff_idx_t System::nextVisibleStaff(staff_idx_t staffIdx) const
     392              : {
     393       319134 :     return firstVisibleStaffFrom(staffIdx + 1);
     394              : }
     395              : 
     396         2604 : staff_idx_t System::prevVisibleStaff(staff_idx_t startStaffIdx) const
     397              : {
     398         2604 :     if (startStaffIdx == 0 || m_staves.empty()) {
     399         2431 :         return muse::nidx;
     400              :     }
     401          173 :     startStaffIdx = std::min(startStaffIdx, static_cast<staff_idx_t>(m_staves.size()));
     402              : 
     403          173 :     for (staff_idx_t i = startStaffIdx - 1;; --i) {
     404          177 :         Staff* s  = score()->staff(i);
     405          177 :         SysStaff* ss = m_staves[i];
     406              : 
     407          177 :         if (s->show() && ss->show()) {
     408          171 :             return i;
     409              :         }
     410              : 
     411            6 :         if (i == 0) {
     412            2 :             break;
     413              :         }
     414            4 :     }
     415              : 
     416            2 :     return muse::nidx;
     417              : }
     418              : 
     419           32 : staff_idx_t System::lastVisibleStaff() const
     420              : {
     421           32 :     const size_t nstaves = std::min(score()->nstaves(), m_staves.size());
     422           32 :     if (nstaves < 1 || m_staves.empty()) {
     423            0 :         return muse::nidx;
     424              :     }
     425              : 
     426           32 :     for (staff_idx_t i = nstaves - 1; ; --i) {
     427           32 :         Staff* staff = score()->staff(i);
     428           32 :         SysStaff* sysStaff = m_staves[i];
     429           32 :         if (staff->show() && sysStaff->show()) {
     430           32 :             return i;
     431              :         }
     432            0 :         if (i == 0) {
     433            0 :             break;
     434              :         }
     435            0 :     }
     436              : 
     437            0 :     return muse::nidx;
     438              : }
     439              : 
     440              : //---------------------------------------------------------
     441              : //   firstVisibleStaff
     442              : //---------------------------------------------------------
     443              : 
     444         2388 : staff_idx_t System::firstVisibleStaff() const
     445              : {
     446         2388 :     return firstVisibleStaffFrom(0);
     447              : }
     448              : 
     449              : //---------------------------------------------------------
     450              : //   searchStaff
     451              : ///   Finds a staff which y position is most close to the
     452              : ///   given \p y.
     453              : ///   \param y The y coordinate in system coordinates.
     454              : ///   \param preferredStaff If not -1, will give more space
     455              : ///   to a staff with the given number when searching it by
     456              : ///   coordinate.
     457              : ///   \returns Number of the found staff.
     458              : //---------------------------------------------------------
     459              : 
     460            0 : staff_idx_t System::searchStaff(double y, staff_idx_t preferredStaff /* = invalid */, double spacingFactor) const
     461              : {
     462            0 :     staff_idx_t i = 0;
     463            0 :     const size_t nstaves = score()->nstaves();
     464            0 :     for (; i < nstaves;) {
     465            0 :         SysStaff* stff = staff(i);
     466            0 :         if (!stff->show() || !score()->staff(i)->show()) {
     467            0 :             ++i;
     468            0 :             continue;
     469              :         }
     470            0 :         staff_idx_t ni = i;
     471              :         for (;;) {
     472            0 :             ++ni;
     473            0 :             if (ni == nstaves || (staff(ni)->show() && score()->staff(ni)->show())) {
     474            0 :                 break;
     475              :             }
     476              :         }
     477              : 
     478              :         double sy2;
     479            0 :         if (ni != nstaves) {
     480            0 :             SysStaff* nstaff = staff(ni);
     481            0 :             double s1y2       = stff->bbox().y() + stff->bbox().height();
     482            0 :             if (i == preferredStaff) {
     483            0 :                 sy2 = s1y2 + (nstaff->bbox().y() - s1y2);
     484            0 :             } else if (ni == preferredStaff) {
     485            0 :                 sy2 = s1y2;
     486              :             } else {
     487            0 :                 sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * spacingFactor;
     488              :             }
     489              :         } else {
     490            0 :             sy2 = page()->height() - pos().y();
     491              :         }
     492            0 :         if (y > sy2) {
     493            0 :             i   = ni;
     494            0 :             continue;
     495              :         }
     496            0 :         break;
     497              :     }
     498            0 :     return i;
     499              : }
     500              : 
     501              : //---------------------------------------------------------
     502              : //   add
     503              : //---------------------------------------------------------
     504              : 
     505        12825 : void System::add(EngravingItem* el)
     506              : {
     507        12825 :     if (!el) {
     508            0 :         return;
     509              :     }
     510              : // LOGD("%p System::add: %p %s", this, el, el->typeName());
     511              : 
     512        12825 :     if (el->isMeasureBase()) {
     513            0 :         toMeasureBase(el)->setSystem(this);
     514        12825 :     } else if (el->isSpannerSegment()) {
     515         6210 :         toSpannerSegment(el)->setSystem(this);
     516         6615 :     } else if (!el->isBeam()) {   // a beam's placement is derived from its elements
     517         6615 :         el->setOwnershipParent(this);
     518              :     }
     519              : 
     520        12825 :     switch (el->type()) {
     521         3771 :     case ElementType::INSTRUMENT_NAME:
     522              :     {
     523         3771 :         InstrumentName* n = toInstrumentName(el);
     524         3771 :         SysStaff* sysStaff = m_staves[n->staffIdx()];
     525         3771 :         sysStaff->addInstrumentName(n);
     526         3771 :         n->setSysStaff(sysStaff);
     527         3771 :         break;
     528              :     }
     529            0 :     case ElementType::BEAM:
     530            0 :         score()->addElement(el);
     531            0 :         break;
     532              : 
     533         2816 :     case ElementType::BRACKET: {
     534         2816 :         Bracket* b   = toBracket(el);
     535         2816 :         m_brackets.push_back(b);
     536              :     }
     537         2816 :     break;
     538              : 
     539            0 :     case ElementType::MEASURE:
     540              :     case ElementType::HBOX:
     541              :     case ElementType::VBOX:
     542              :     case ElementType::TBOX:
     543              :     case ElementType::FBOX:
     544            0 :         score()->addElement(el);
     545            0 :         break;
     546         6210 :     case ElementType::TEXTLINE_SEGMENT:
     547              :     case ElementType::HAIRPIN_SEGMENT:
     548              :     case ElementType::OTTAVA_SEGMENT:
     549              :     case ElementType::TRILL_SEGMENT:
     550              :     case ElementType::VIBRATO_SEGMENT:
     551              :     case ElementType::VOLTA_SEGMENT:
     552              :     case ElementType::SLUR_SEGMENT:
     553              :     case ElementType::TIE_SEGMENT:
     554              :     case ElementType::LAISSEZ_VIB_SEGMENT:
     555              :     case ElementType::PARTIAL_TIE_SEGMENT:
     556              :     case ElementType::PEDAL_SEGMENT:
     557              :     case ElementType::LYRICSLINE_SEGMENT:
     558              :     case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
     559              :     case ElementType::GLISSANDO_SEGMENT:
     560              :     case ElementType::NOTELINE_SEGMENT:
     561              :     case ElementType::LET_RING_SEGMENT:
     562              :     case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
     563              :     case ElementType::PALM_MUTE_SEGMENT:
     564              :     case ElementType::WHAMMY_BAR_SEGMENT:
     565              :     case ElementType::RASGUEADO_SEGMENT:
     566              :     case ElementType::HARMONIC_MARK_SEGMENT:
     567              :     case ElementType::PICK_SCRAPE_SEGMENT:
     568              :     case ElementType::GUITAR_BEND_SEGMENT:
     569              :     case ElementType::GUITAR_BEND_HOLD_SEGMENT:
     570              :     case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
     571              :     case ElementType::TAPPING_HALF_SLUR_SEGMENT:
     572              :     {
     573         6210 :         SpannerSegment* ss = toSpannerSegment(el);
     574              : #ifndef NDEBUG
     575         6210 :         if (muse::contains(m_spannerSegments, ss)) {
     576           16 :             LOGD("System::add() %s %p already there", ss->typeName(), ss);
     577              :         } else
     578              : #endif
     579         6194 :         m_spannerSegments.push_back(ss);
     580              :     }
     581         6210 :     break;
     582              : 
     583           28 :     case ElementType::SYSTEM_DIVIDER:
     584              :     {
     585           28 :         SystemDivider* sd = toSystemDivider(el);
     586           28 :         if (sd->dividerType() == SystemDividerType::LEFT) {
     587           14 :             m_systemDividerLeft = sd;
     588              :         } else {
     589           14 :             m_systemDividerRight = sd;
     590              :         }
     591              :     }
     592           28 :     break;
     593              : 
     594            0 :     default:
     595            0 :         LOGD("System::add(%s) not implemented", el->typeName());
     596            0 :         return;
     597              :     }
     598              : 
     599        12825 :     el->added();
     600              : }
     601              : 
     602              : //---------------------------------------------------------
     603              : //   remove
     604              : //---------------------------------------------------------
     605              : 
     606         1509 : void System::remove(EngravingItem* el)
     607              : {
     608         1509 :     switch (el->type()) {
     609            0 :     case ElementType::INSTRUMENT_NAME:
     610              :     {
     611              :         // NOTE: el gets deleted here
     612            0 :         InstrumentName* n = toInstrumentName(el);
     613            0 :         SysStaff* sysStaff = m_staves[n->staffIdx()];
     614            0 :         sysStaff->removeInstrumentName(n->instrumentNameRole());
     615            0 :         return;
     616              :     }
     617            0 :     case ElementType::BEAM:
     618            0 :         score()->removeElement(el);
     619            0 :         break;
     620            0 :     case ElementType::BRACKET:
     621              :     {
     622            0 :         Bracket* b = toBracket(el);
     623            0 :         if (!muse::remove(m_brackets, b)) {
     624            0 :             LOGD("System::remove: bracket not found");
     625              :         }
     626              :     }
     627            0 :     break;
     628            0 :     case ElementType::MEASURE:
     629              :     case ElementType::HBOX:
     630              :     case ElementType::VBOX:
     631              :     case ElementType::TBOX:
     632              :     case ElementType::FBOX:
     633            0 :         score()->removeElement(el);
     634            0 :         break;
     635         1487 :     case ElementType::TEXTLINE_SEGMENT:
     636              :     case ElementType::HAIRPIN_SEGMENT:
     637              :     case ElementType::OTTAVA_SEGMENT:
     638              :     case ElementType::TRILL_SEGMENT:
     639              :     case ElementType::VIBRATO_SEGMENT:
     640              :     case ElementType::VOLTA_SEGMENT:
     641              :     case ElementType::SLUR_SEGMENT:
     642              :     case ElementType::TIE_SEGMENT:
     643              :     case ElementType::LAISSEZ_VIB_SEGMENT:
     644              :     case ElementType::PARTIAL_TIE_SEGMENT:
     645              :     case ElementType::PEDAL_SEGMENT:
     646              :     case ElementType::LYRICSLINE_SEGMENT:
     647              :     case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
     648              :     case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
     649              :     case ElementType::GLISSANDO_SEGMENT:
     650              :     case ElementType::NOTELINE_SEGMENT:
     651              :     case ElementType::GUITAR_BEND_SEGMENT:
     652              :     case ElementType::GUITAR_BEND_HOLD_SEGMENT:
     653              :     {
     654         1487 :         SpannerSegment* ss = toSpannerSegment(el);
     655         1487 :         if (!muse::remove(m_spannerSegments, ss)) {
     656            0 :             LOGD("System::remove: %p(%s) not found, score %p", el, el->typeName(), score());
     657            0 :             assert(score() == el->score());
     658              :         }
     659         1487 :         if (ss->system() == this) {
     660         1487 :             ss->setSystem(nullptr);
     661              :         }
     662              :     }
     663         1487 :     break;
     664            6 :     case ElementType::SYSTEM_DIVIDER:
     665            6 :         if (el == m_systemDividerLeft) {
     666            3 :             m_systemDividerLeft = 0;
     667              :         } else {
     668            3 :             assert(m_systemDividerRight == el);
     669            3 :             m_systemDividerRight = 0;
     670              :         }
     671            6 :         break;
     672              : 
     673           16 :     default:
     674           16 :         LOGD("System::remove(%s) not implemented", el->typeName());
     675           16 :         return;
     676              :     }
     677              : 
     678         1493 :     el->removed();
     679              : }
     680              : 
     681              : //---------------------------------------------------------
     682              : //   change
     683              : //---------------------------------------------------------
     684              : 
     685            0 : void System::change(EngravingItem* o, EngravingItem* n)
     686              : {
     687            0 :     remove(o);
     688            0 :     add(n);
     689            0 : }
     690              : 
     691              : //---------------------------------------------------------
     692              : //   snap
     693              : //---------------------------------------------------------
     694              : 
     695            0 : Fraction System::snap(const Fraction& tick, const PointF p) const
     696              : {
     697            0 :     for (const MeasureBase* m : m_ml) {
     698            0 :         if (p.x() < m->x() + m->width()) {
     699            0 :             return toMeasure(m)->snap(tick, p - m->pos());       //TODO: MeasureBase
     700              :         }
     701              :     }
     702            0 :     return toMeasure(m_ml.back())->snap(tick, p - pos());          //TODO: MeasureBase
     703              : }
     704              : 
     705              : //---------------------------------------------------------
     706              : //   snap
     707              : //---------------------------------------------------------
     708              : 
     709            0 : Fraction System::snapNote(const Fraction& tick, const PointF p, int staff) const
     710              : {
     711            0 :     for (const MeasureBase* m : m_ml) {
     712            0 :         if (p.x() < m->x() + m->width()) {
     713            0 :             return toMeasure(m)->snapNote(tick, p - m->pos(), staff);        //TODO: MeasureBase
     714              :         }
     715              :     }
     716            0 :     return toMeasure(m_ml.back())->snap(tick, p - pos());          // TODO: MeasureBase
     717              : }
     718              : 
     719              : //---------------------------------------------------------
     720              : //   firstMeasure
     721              : //---------------------------------------------------------
     722              : 
     723       639309 : Measure* System::firstMeasure() const
     724              : {
     725      1284796 :     auto i = std::find_if(m_ml.begin(), m_ml.end(), [](MeasureBase* mb) { return mb->isMeasure(); });
     726       639309 :     return i != m_ml.end() ? toMeasure(*i) : 0;
     727              : }
     728              : 
     729              : //---------------------------------------------------------
     730              : //   lastMeasure
     731              : //---------------------------------------------------------
     732              : 
     733       180049 : Measure* System::lastMeasure() const
     734              : {
     735       360414 :     auto i = std::find_if(m_ml.rbegin(), m_ml.rend(), [](MeasureBase* mb) { return mb->isMeasure(); });
     736       180049 :     return i != m_ml.rend() ? toMeasure(*i) : 0;
     737              : }
     738              : 
     739              : //---------------------------------------------------------
     740              : //   nextMeasure
     741              : //---------------------------------------------------------
     742              : 
     743            0 : MeasureBase* System::nextMeasure(const MeasureBase* m) const
     744              : {
     745            0 :     if (m == m_ml.back()) {
     746            0 :         return 0;
     747              :     }
     748            0 :     MeasureBase* nm = m->next();
     749            0 :     if (nm->isMeasure() && style().styleB(Sid::createMultiMeasureRests) && toMeasure(nm)->hasMMRest()) {
     750            0 :         nm = toMeasure(nm)->mmRest();
     751              :     }
     752            0 :     return nm;
     753              : }
     754              : 
     755              : //---------------------------------------------------------
     756              : //   scanElements
     757              : //---------------------------------------------------------
     758              : 
     759          655 : void System::scanElements(std::function<void(EngravingItem*)> func)
     760              : {
     761          655 :     if (m_pageLockIndicator) {
     762            5 :         func(m_pageLockIndicator);
     763              :     }
     764              : 
     765          655 :     if (vbox()) {
     766          122 :         return;
     767              :     }
     768          655 :     for (Bracket* b : m_brackets) {
     769          122 :         b->scanElements(func);
     770              :     }
     771              : 
     772          533 :     if (m_systemDividerLeft) {
     773            0 :         func(m_systemDividerLeft);
     774              :     }
     775          533 :     if (m_systemDividerRight) {
     776            0 :         func(m_systemDividerRight);
     777              :     }
     778              : 
     779          533 :     if (m_staffVisibilityIndicator) {
     780           14 :         func(m_staffVisibilityIndicator);
     781              :     }
     782              : 
     783          533 :     for (auto i : m_systemLockIndicators) {
     784            0 :         func(i);
     785              :     }
     786              : 
     787         1220 :     for (const SysStaff* st : m_staves) {
     788          687 :         if (st->show()) {
     789          837 :             for (auto& pair : st->instrumentNames()) {
     790          176 :                 if (InstrumentName* n = pair.second) {
     791          176 :                     func(n);
     792              :                 }
     793              :             }
     794              :         } else {
     795           26 :             if (InstrumentName* n = st->name(InstrumentNameRole::GROUP); n && n->effectiveStaffIdx() != muse::nidx) {
     796            0 :                 func(n);
     797              :             }
     798           26 :             if (InstrumentName* n = st->name(InstrumentNameRole::PART); n && n->effectiveStaffIdx() != muse::nidx) {
     799            0 :                 func(n);
     800              :             }
     801              :         }
     802              :     }
     803              : 
     804          800 :     for (SpannerSegment* ss : m_spannerSegments) {
     805          267 :         staff_idx_t staffIdx = ss->spanner()->staffIdx();
     806          267 :         if (staffIdx == muse::nidx) {
     807            0 :             LOGD("System::scanElements: staffIDx == -1: %s %p", ss->spanner()->typeName(), ss->spanner());
     808            0 :             staffIdx = 0;
     809              :         }
     810          267 :         bool v = true;
     811          267 :         Spanner* spanner = ss->spanner();
     812          267 :         if (spanner->anchor() == Spanner::Anchor::SEGMENT || spanner->anchor() == Spanner::Anchor::CHORDREST) {
     813          178 :             EngravingItem* se = spanner->startElement();
     814          178 :             EngravingItem* ee = spanner->endElement();
     815          178 :             bool v1 = true;
     816          178 :             if (se && se->isChordRest()) {
     817          173 :                 ChordRest* cr = toChordRest(se);
     818          173 :                 Measure* m    = cr->measure();
     819          173 :                 v1            = m->visible(cr->staffIdx());
     820              :             }
     821          178 :             bool v2 = true;
     822          178 :             if (!v1 && ee && ee->isChordRest()) {
     823            2 :                 ChordRest* cr = toChordRest(ee);
     824            2 :                 Measure* m    = cr->measure();
     825            2 :                 v2            = m->visible(cr->staffIdx());
     826              :             }
     827          178 :             v = v1 || v2;       // hide spanner if both chords are hidden
     828              :         }
     829          534 :         if ((score()->staff(staffIdx)->show() && m_staves[staffIdx]->show() && v) || spanner->isVolta()
     830          534 :             || spanner->systemFlag()) {
     831          263 :             ss->scanElements(func);
     832              :         }
     833              :     }
     834              : }
     835              : 
     836              : //---------------------------------------------------------
     837              : //   staffYpage
     838              : //    return page coordinates
     839              : //---------------------------------------------------------
     840              : 
     841       563560 : double System::staffYpage(staff_idx_t staffIdx) const
     842              : {
     843       563560 :     if (staffIdx >= m_staves.size()) {
     844            0 :         return pagePos().y();
     845              :     }
     846              : 
     847       563560 :     return m_staves[staffIdx]->y() + y();
     848              : }
     849              : 
     850              : //---------------------------------------------------------
     851              : //   staffCanvasYpage
     852              : //    return canvas coordinates
     853              : //---------------------------------------------------------
     854              : 
     855           22 : double System::staffCanvasYpage(staff_idx_t staffIdx) const
     856              : {
     857           22 :     return m_staves[staffIdx]->y() + y() + page()->canvasPos().y();
     858              : }
     859              : 
     860      1784563 : SysStaff* System::staff(size_t staffIdx) const
     861              : {
     862      1784563 :     if (staffIdx < m_staves.size()) {
     863      1784471 :         return m_staves[staffIdx];
     864              :     }
     865              : 
     866           92 :     return nullptr;
     867              : }
     868              : 
     869              : //---------------------------------------------------------
     870              : //   nextSegmentElement
     871              : //---------------------------------------------------------
     872              : 
     873            0 : EngravingItem* System::nextSegmentElement()
     874              : {
     875            0 :     Measure* m = firstMeasure();
     876            0 :     if (m) {
     877            0 :         Segment* firstSeg = m->segments().first();
     878            0 :         if (firstSeg) {
     879            0 :             return firstSeg->element(0);
     880              :         }
     881              :     }
     882            0 :     return Navigation::lastElement(score());
     883              : }
     884              : 
     885              : //---------------------------------------------------------
     886              : //   prevSegmentElement
     887              : //---------------------------------------------------------
     888              : 
     889            0 : EngravingItem* System::prevSegmentElement()
     890              : {
     891            0 :     EngravingItem* re = 0;
     892            0 :     Measure* m = firstMeasure();
     893            0 :     if (m) {
     894            0 :         Segment* seg = m->first();
     895            0 :         while (!re) {
     896            0 :             seg = seg->prev1MM();
     897            0 :             if (!seg) {
     898            0 :                 return Navigation::firstElement(score());
     899              :             }
     900              : 
     901            0 :             if (seg->segmentType() == SegmentType::EndBarLine) {
     902            0 :                 score()->inputState().setTrack((score()->staves().size() - 1) * VOICES);       //correction
     903              :             }
     904            0 :             re = seg->lastElementForNavigation(score()->staves().size() - 1);
     905              :         }
     906              :     }
     907            0 :     return re;
     908              : }
     909              : 
     910              : //---------------------------------------------------------
     911              : //   topDistance
     912              : //    return minimum distance to the above south skyline
     913              : //---------------------------------------------------------
     914              : 
     915            0 : double System::topDistance(staff_idx_t staffIdx, const SkylineLine& s) const
     916              : {
     917            0 :     assert(!vbox());
     918            0 :     assert(!s.isNorth());
     919              :     // in continuous view, we only build a partial skyline for performance reasons
     920              :     // this means we cannot expect the minDistance calculation to produce meaningful results
     921              :     // so just give up on autoplace for spanners in continuous view
     922              :     // (or any other calculations that rely on this value)
     923            0 :     if (score()->lineMode() && !configuration()->minDistanceForPartialSkylineCalculated()) {
     924            0 :         return 0.0;
     925              :     }
     926            0 :     return s.minDistance(staff(staffIdx)->skyline().north());
     927              : }
     928              : 
     929              : //---------------------------------------------------------
     930              : //   bottomDistance
     931              : //---------------------------------------------------------
     932              : 
     933            0 : double System::bottomDistance(staff_idx_t staffIdx, const SkylineLine& s) const
     934              : {
     935            0 :     assert(!vbox());
     936            0 :     assert(s.isNorth());
     937              :     // see note on topDistance() above
     938            0 :     if (score()->lineMode() && !configuration()->minDistanceForPartialSkylineCalculated()) {
     939            0 :         return 0.0;
     940              :     }
     941            0 :     return staff(staffIdx)->skyline().south().minDistance(s);
     942              : }
     943              : 
     944              : //---------------------------------------------------------
     945              : //   firstVisibleSysStaff
     946              : //---------------------------------------------------------
     947              : 
     948         6276 : staff_idx_t System::firstVisibleSysStaff() const
     949              : {
     950         6276 :     size_t nstaves = m_staves.size();
     951         6290 :     for (staff_idx_t i = 0; i < nstaves; ++i) {
     952         6284 :         if (m_staves[i]->show()) {
     953         6270 :             return i;
     954              :         }
     955              :     }
     956            6 :     return muse::nidx;
     957              : }
     958              : 
     959              : //---------------------------------------------------------
     960              : //   lastVisibleSysStaff
     961              : //---------------------------------------------------------
     962              : 
     963        11076 : staff_idx_t System::lastVisibleSysStaff() const
     964              : {
     965        11076 :     int nstaves = static_cast<int>(m_staves.size());
     966        11321 :     for (int i = nstaves - 1; i >= 0; --i) {
     967        11312 :         if (m_staves[i]->show()) {
     968        11067 :             return static_cast<staff_idx_t>(i);
     969              :         }
     970              :     }
     971            9 :     return muse::nidx;
     972              : }
     973              : 
     974              : //---------------------------------------------------------
     975              : //   minTop
     976              : //    Return the minimum top margin.
     977              : //---------------------------------------------------------
     978              : 
     979         6276 : double System::minTop() const
     980              : {
     981         6276 :     staff_idx_t si = firstVisibleSysStaff();
     982         6276 :     SysStaff* s = si == muse::nidx ? nullptr : staff(si);
     983         6276 :     if (s) {
     984         6270 :         return -s->skyline().north().max();
     985              :     }
     986            6 :     return 0.0;
     987              : }
     988              : 
     989              : //---------------------------------------------------------
     990              : //   minBottom
     991              : //    Return the minimum bottom margin.
     992              : //---------------------------------------------------------
     993              : 
     994        10091 : double System::minBottom() const
     995              : {
     996        10091 :     if (const Box* vb = vbox()) {
     997           30 :         return vb->absoluteFromSpatium(vb->bottomGap());
     998              :     }
     999        10061 :     staff_idx_t si = lastVisibleSysStaff();
    1000        10061 :     SysStaff* s = si == muse::nidx ? nullptr : staff(si);
    1001        10061 :     if (s) {
    1002        10055 :         return s->skyline().south().max() - s->bbox().height();
    1003              :     }
    1004            6 :     return 0.0;
    1005              : }
    1006              : 
    1007              : //---------------------------------------------------------
    1008              : //   spacerDistance
    1009              : //    Return the distance needed due to spacers
    1010              : //---------------------------------------------------------
    1011              : 
    1012         1015 : double System::spacerDistance(bool up) const
    1013              : {
    1014         1015 :     staff_idx_t staff = up ? firstVisibleSysStaff() : lastVisibleSysStaff();
    1015         1015 :     if (staff == muse::nidx) {
    1016            3 :         return 0.0;
    1017              :     }
    1018         1012 :     double dist = 0.0;
    1019         4469 :     for (MeasureBase* mb : measures()) {
    1020         3457 :         if (mb->isMeasure()) {
    1021         3413 :             Measure* m = toMeasure(mb);
    1022         3413 :             Spacer* sp = up ? m->vspacerUp(staff) : m->vspacerDown(staff);
    1023         3413 :             if (sp) {
    1024            0 :                 if (sp->spacerType() == SpacerType::FIXED) {
    1025            0 :                     dist = sp->absoluteGap();
    1026            0 :                     break;
    1027              :                 } else {
    1028            0 :                     dist = std::max(dist, sp->absoluteGap());
    1029              :                 }
    1030              :             }
    1031              :         }
    1032              :     }
    1033         1012 :     return dist;
    1034              : }
    1035              : 
    1036              : //---------------------------------------------------------
    1037              : //   upSpacer
    1038              : //    Return largest upSpacer for this system. This can
    1039              : //    be a downSpacer of the previous system.
    1040              : //---------------------------------------------------------
    1041              : 
    1042         6673 : Spacer* System::upSpacer(staff_idx_t staffIdx, Spacer* prevDownSpacer) const
    1043              : {
    1044         6673 :     if (staffIdx == muse::nidx) {
    1045            0 :         return nullptr;
    1046              :     }
    1047              : 
    1048         6673 :     if (prevDownSpacer && (prevDownSpacer->spacerType() == SpacerType::FIXED)) {
    1049            2 :         return prevDownSpacer;
    1050              :     }
    1051              : 
    1052         6671 :     Spacer* spacer { prevDownSpacer };
    1053        38094 :     for (MeasureBase* mb : measures()) {
    1054        31423 :         if (!(mb && mb->isMeasure())) {
    1055           92 :             continue;
    1056              :         }
    1057        31331 :         Spacer* sp { toMeasure(mb)->vspacerUp(staffIdx) };
    1058        31331 :         if (sp) {
    1059            0 :             if (!spacer || ((spacer->spacerType() == SpacerType::UP) && (sp->gap() > spacer->gap()))) {
    1060            0 :                 spacer = sp;
    1061              :             }
    1062            0 :             continue;
    1063              :         }
    1064              :     }
    1065         6671 :     return spacer;
    1066              : }
    1067              : 
    1068              : //---------------------------------------------------------
    1069              : //   downSpacer
    1070              : //    Return the largest downSpacer for this system.
    1071              : //---------------------------------------------------------
    1072              : 
    1073         8209 : Spacer* System::downSpacer(staff_idx_t staffIdx) const
    1074              : {
    1075         8209 :     if (staffIdx == muse::nidx) {
    1076            0 :         return nullptr;
    1077              :     }
    1078              : 
    1079         8209 :     Spacer* spacer { nullptr };
    1080        44232 :     for (MeasureBase* mb : measures()) {
    1081        36025 :         if (!(mb && mb->isMeasure())) {
    1082           97 :             continue;
    1083              :         }
    1084        35928 :         Spacer* sp { toMeasure(mb)->vspacerDown(staffIdx) };
    1085        35928 :         if (sp) {
    1086            2 :             if (sp->spacerType() == SpacerType::FIXED) {
    1087            2 :                 return sp;
    1088              :             } else {
    1089            0 :                 if (!spacer || (sp->gap() > spacer->gap())) {
    1090            0 :                     spacer = sp;
    1091              :                 }
    1092              :             }
    1093              :         }
    1094              :     }
    1095         8207 :     return spacer;
    1096              : }
    1097              : 
    1098              : //---------------------------------------------------------
    1099              : //   firstNoteRestSegmentX
    1100              : //    in System() coordinates
    1101              : //    returns the position of the first note or rest,
    1102              : //    or the position just after the last non-chordrest segment
    1103              : //---------------------------------------------------------
    1104              : 
    1105          744 : double System::firstNoteRestSegmentX(bool leading) const
    1106              : {
    1107          744 :     double margin = style().styleAbsolute(Sid::headerToLineStartDistance);
    1108          767 :     for (const MeasureBase* mb : measures()) {
    1109          767 :         if (mb->isMeasure()) {
    1110          744 :             const Measure* measure = toMeasure(mb);
    1111          744 :             margin = measure->firstNoteRestSegmentX(leading);
    1112          744 :             break;
    1113              :         }
    1114              :     }
    1115              : 
    1116          744 :     return margin;
    1117              : }
    1118              : 
    1119              : //---------------------------------------------------------
    1120              : //   endingXForOpenEndedLines
    1121              : //    in System() coordinates returns the end point for
    1122              : //    open ended (i.e. cross-system) lines
    1123              : //---------------------------------------------------------
    1124              : 
    1125           39 : double System::endingXForOpenEndedLines() const
    1126              : {
    1127           39 :     double margin = style().styleAbsolute(Sid::lineEndToBarlineDistance);
    1128           39 :     double systemEndX = ldata()->bbox().width();
    1129              : 
    1130           39 :     Measure* lastMeas = lastMeasure();
    1131           39 :     if (!lastMeas) {
    1132            0 :         return systemEndX - margin;
    1133              :     }
    1134              : 
    1135           39 :     return lastMeas->endingXForOpenEndedLines();
    1136              : }
    1137              : 
    1138              : //---------------------------------------------------------
    1139              : //   lastChordRest
    1140              : //    returns the last chordrest of a system for a particular track
    1141              : //---------------------------------------------------------
    1142              : 
    1143           36 : ChordRest* System::lastChordRest(track_idx_t track) const
    1144              : {
    1145           36 :     for (auto measureBaseIter = measures().rbegin(); measureBaseIter != measures().rend(); measureBaseIter++) {
    1146           36 :         if ((*measureBaseIter)->isMeasure()) {
    1147           36 :             const Measure* measure = toMeasure(*measureBaseIter);
    1148           36 :             return measure->lastChordRest(track);
    1149              :         }
    1150              :     }
    1151            0 :     return nullptr;
    1152              : }
    1153              : 
    1154              : //---------------------------------------------------------
    1155              : //   firstChordRest
    1156              : //    returns the last chordrest of a system for a particular track
    1157              : //---------------------------------------------------------
    1158              : 
    1159           36 : ChordRest* System::firstChordRest(track_idx_t track) const
    1160              : {
    1161           36 :     for (const MeasureBase* mb : measures()) {
    1162           36 :         if (!mb->isMeasure()) {
    1163            0 :             continue;
    1164              :         }
    1165           36 :         const Measure* measure = toMeasure(mb);
    1166           36 :         return measure->firstChordRest(track);
    1167              :     }
    1168            0 :     return nullptr;
    1169              : }
    1170              : 
    1171              : //---------------------------------------------------------
    1172              : //   pageBreak
    1173              : //---------------------------------------------------------
    1174              : 
    1175         6099 : bool System::pageBreak() const
    1176              : {
    1177         6099 :     return m_ml.empty() ? false : m_ml.back()->pageBreak();
    1178              : }
    1179              : 
    1180              : //---------------------------------------------------------
    1181              : //   endTick
    1182              : //---------------------------------------------------------
    1183              : 
    1184        16955 : Fraction System::endTick() const
    1185              : {
    1186        16955 :     return measures().back()->endTick();
    1187              : }
    1188              : 
    1189              : //---------------------------------------------------------
    1190              : //   firstSysStaffOfPart
    1191              : //---------------------------------------------------------
    1192              : 
    1193        30153 : staff_idx_t System::firstSysStaffOfPart(const Part* part) const
    1194              : {
    1195        30153 :     staff_idx_t staffIdx = 0;
    1196       242714 :     for (const Part* p : score()->parts()) {
    1197       242714 :         if (p == part) {
    1198        30153 :             return staffIdx;
    1199              :         }
    1200       212561 :         staffIdx += p->nstaves();
    1201              :     }
    1202            0 :     return muse::nidx;   // Part not found.
    1203              : }
    1204              : 
    1205              : //---------------------------------------------------------
    1206              : //   firstVisibleSysStaffOfPart
    1207              : //---------------------------------------------------------
    1208              : 
    1209          328 : staff_idx_t System::firstVisibleSysStaffOfPart(const Part* part) const
    1210              : {
    1211          328 :     staff_idx_t firstIdx = firstSysStaffOfPart(part);
    1212          328 :     if (firstIdx == muse::nidx) {
    1213            0 :         return muse::nidx;
    1214              :     }
    1215              : 
    1216          524 :     for (staff_idx_t idx = firstIdx; idx < firstIdx + part->nstaves(); ++idx) {
    1217          425 :         const SysStaff* s = staff(idx);
    1218          425 :         if (s && s->show()) {
    1219          229 :             return idx;
    1220              :         }
    1221              :     }
    1222              : 
    1223           99 :     return muse::nidx; // No visible staves on this part.
    1224              : }
    1225              : 
    1226            0 : staff_idx_t System::firstVisibleSysStaffWithInstrument(const String& instrumentId, staff_idx_t startFrom)
    1227              : {
    1228            0 :     Fraction tick = first()->tick();
    1229            0 :     for (staff_idx_t idx = startFrom; idx < m_staves.size(); ++idx) {
    1230            0 :         Part* part = score()->staff(idx)->part();
    1231            0 :         if (part->instrument(tick)->id() == instrumentId) {
    1232            0 :             staff_idx_t firstVisOfPart = firstVisibleSysStaffOfPart(part);
    1233            0 :             if (firstVisOfPart != muse::nidx) {
    1234            0 :                 return firstVisOfPart;
    1235              :             }
    1236              :         }
    1237              :     }
    1238              : 
    1239            0 :     return muse::nidx;
    1240              : }
    1241              : 
    1242              : //---------------------------------------------------------
    1243              : //   lastSysStaffOfPart
    1244              : //---------------------------------------------------------
    1245              : 
    1246          186 : staff_idx_t System::lastSysStaffOfPart(const Part* part) const
    1247              : {
    1248          186 :     staff_idx_t firstIdx = firstSysStaffOfPart(part);
    1249          186 :     if (firstIdx == muse::nidx) {
    1250            0 :         return muse::nidx;     // Part not found.
    1251              :     }
    1252          186 :     return firstIdx + part->nstaves() - 1;
    1253              : }
    1254              : 
    1255              : //---------------------------------------------------------
    1256              : //   lastVisibleSysStaffOfPart
    1257              : //---------------------------------------------------------
    1258              : 
    1259          186 : staff_idx_t System::lastVisibleSysStaffOfPart(const Part* part) const
    1260              : {
    1261          186 :     staff_idx_t firstStaffIdx = firstSysStaffOfPart(part);
    1262          186 :     if (firstStaffIdx == muse::nidx) {
    1263            0 :         return muse::nidx;
    1264              :     }
    1265          186 :     for (int idx = static_cast<int>(lastSysStaffOfPart(part)); idx >= static_cast<int>(firstStaffIdx); --idx) {
    1266          186 :         if (staff(idx)->show()) {
    1267          186 :             return idx;
    1268              :         }
    1269              :     }
    1270            0 :     return muse::nidx;    // No visible staves on this part.
    1271              : }
    1272              : 
    1273        29453 : std::vector<staff_idx_t> System::visibleStavesOfPart(const Part* part) const
    1274              : {
    1275        29453 :     std::vector<staff_idx_t> result;
    1276        29453 :     result.reserve(part->nstaves());
    1277              : 
    1278        29453 :     staff_idx_t startIdx = firstSysStaffOfPart(part);
    1279        29453 :     staff_idx_t endIdx = startIdx + part->nstaves();
    1280        62286 :     for (staff_idx_t staffIdx = startIdx; staffIdx < endIdx; ++staffIdx) {
    1281        32833 :         if (staff(staffIdx)->show()) {
    1282        32588 :             result.push_back(staffIdx);
    1283              :         }
    1284              :     }
    1285              : 
    1286        29453 :     return result;
    1287            0 : }
    1288              : 
    1289          417 : std::vector<Part*> System::visiblePartsOfGroup(staff_idx_t start, staff_idx_t end) const
    1290              : {
    1291          417 :     std::vector<Part*> result;
    1292              : 
    1293         1328 :     for (staff_idx_t idx = start; idx < end;) {
    1294          911 :         Part* part = score()->staff(idx)->part();
    1295          911 :         if (visibleStavesOfPart(part).size() > 0) {
    1296          911 :             result.push_back(part);
    1297              :         }
    1298          911 :         idx += part->nstaves();
    1299              :     }
    1300              : 
    1301          417 :     return result;
    1302            0 : }
    1303              : } // namespace mu::engraving
        

Generated by: LCOV version 2.0-1