LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - pagelayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 93.8 % 566 531
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) 2023 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              : #include "pagelayout.h"
      23              : 
      24              : #include "realfn.h"
      25              : 
      26              : #include "dom/barline.h"
      27              : #include "dom/beam.h"
      28              : #include "dom/box.h"
      29              : #include "dom/bracketitem.h"
      30              : #include "dom/chord.h"
      31              : #include "dom/chordrest.h"
      32              : #include "dom/durationelement.h"
      33              : #include "dom/factory.h"
      34              : #include "dom/fingering.h"
      35              : #include "dom/measure.h"
      36              : #include "dom/measurebase.h"
      37              : #include "dom/note.h"
      38              : #include "dom/page.h"
      39              : #include "dom/score.h"
      40              : #include "dom/segment.h"
      41              : #include "dom/slur.h"
      42              : #include "dom/spacer.h"
      43              : #include "dom/spannermap.h"
      44              : #include "dom/staff.h"
      45              : #include "dom/staffvisibilityindicator.h"
      46              : #include "dom/system.h"
      47              : #include "dom/tremolosinglechord.h"
      48              : #include "dom/tremolotwochord.h"
      49              : #include "dom/tuplet.h"
      50              : 
      51              : #include "arpeggiolayout.h"
      52              : #include "beamlayout.h"
      53              : #include "chordlayout.h"
      54              : #include "chordbracketlayout.h"
      55              : #include "headerfooterlayout.h"
      56              : #include "masklayout.h"
      57              : #include "measurelayout.h"
      58              : #include "slurtielayout.h"
      59              : #include "systemlayout.h"
      60              : #include "tlayout.h"
      61              : #include "tupletlayout.h"
      62              : #include "verticalgapdata.h"
      63              : #include "systemheaderlayout.h"
      64              : 
      65              : #include "log.h"
      66              : 
      67              : using namespace mu::engraving;
      68              : using namespace mu::engraving::rendering::score;
      69              : 
      70              : //---------------------------------------------------------
      71              : //   getNextPage
      72              : //---------------------------------------------------------
      73              : 
      74         4161 : void PageLayout::getNextPage(LayoutContext& ctx)
      75              : {
      76         4161 :     LayoutState& state = ctx.mutState();
      77         4161 :     DomAccessor& dom = ctx.mutDom();
      78              : 
      79         4161 :     if (!state.page() || state.pageIdx() >= dom.npages()) {
      80         3554 :         state.setPage(Factory::createPage(dom.score()));
      81         3554 :         dom.pages().push_back(state.page());
      82         3554 :         state.setPrevSystem(nullptr);
      83         3554 :         state.setPageOldMeasure(nullptr);
      84              :     } else {
      85          607 :         state.setPage(dom.pages()[state.pageIdx()]);
      86          607 :         std::vector<System*>& systems = state.page()->systems();
      87          607 :         state.setPageOldMeasure(systems.empty() || systems.back()->measures().empty() ? nullptr : systems.back()->measures().back());
      88          607 :         const system_idx_t i = muse::indexOf(systems, state.curSystem());
      89          607 :         if ((i < systems.size()) && i > 0 && systems[i - 1]->page() == state.page()) {
      90              :             // Current and previous systems are on the current page.
      91              :             // Erase only the current and the following systems
      92              :             // as the previous one will not participate in layout.
      93          115 :             systems.erase(systems.begin() + i, systems.end());
      94              :         } else { // system is not on the current page (or will be the first one)
      95          492 :             systems.clear();
      96              :         }
      97          607 :         state.setPrevSystem(systems.empty() ? nullptr : systems.back());
      98              :     }
      99         4161 :     state.page()->mutldata()->setBbox(0.0, 0.0, ctx.conf().loWidth(), ctx.conf().loHeight());
     100         4161 :     state.page()->setPageNumber(state.pageIdx());
     101         4161 :     double x = 0.0;
     102         4161 :     double y = 0.0;
     103         4161 :     if (state.pageIdx()) {
     104          161 :         const Page* prevPage = dom.pages().at(state.pageIdx() - 1);
     105          161 :         if (MScore::verticalOrientation()) {
     106            0 :             y = prevPage->pos().y() + state.page()->height() + MScore::verticalPageGap;
     107              :         } else {
     108          161 :             double gap = (state.pageIdx() + ctx.conf().pageNumberOffset())
     109          161 :                          & 1 ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven;
     110          161 :             x = prevPage->pos().x() + state.page()->width() + gap;
     111              :         }
     112              :     }
     113         4161 :     state.setPageIdx(state.pageIdx() + 1);
     114         4161 :     state.page()->setPos(x, y);
     115         4161 : }
     116              : 
     117              : //---------------------------------------------------------
     118              : //   collectPage
     119              : //---------------------------------------------------------
     120              : 
     121         4125 : void PageLayout::collectPage(LayoutContext& ctx)
     122              : {
     123         4140 :     TRACEFUNC;
     124              : 
     125         4125 :     Page* page = ctx.mutState().page();
     126         4125 :     const LayoutConfiguration& conf = ctx.conf();
     127              : 
     128              :     LAYOUT_CALL() << "pageNumber: " << page->pageNumber();
     129              : 
     130              :     /* The page count will be wrong unless this is the final page, but we need to
     131              :      * take into account the space taken up by headers/footers, when positioning
     132              :      * other elements. If there was a page count involved, all headers/footers will
     133              :      * be re-computed after all pages are laid out */
     134         4125 :     HeaderFooterLayout::layoutHeaderFooter(ctx, page);
     135              : 
     136         4125 :     const double slb = conf.styleAbsolute(Sid::staffLowerBorder);
     137         4125 :     const bool breakPages = conf.viewMode() != LayoutMode::SYSTEM;
     138         4125 :     const double footerExtension = HeaderFooterLayout::footerExtension(ctx, page);
     139         4125 :     const double headerExtension = HeaderFooterLayout::headerExtension(ctx, page);
     140         4125 :     const double headerFooterPadding = conf.styleAbsolute(Sid::staffHeaderFooterPadding);
     141         4125 :     const double endY = page->height() - page->bm();
     142         4125 :     double y = 0.0;
     143              : 
     144         4125 :     System* nextSystem = nullptr;
     145         4125 :     int systemIdx = -1;
     146              : 
     147         4125 :     auto getPageLock = [&ctx](MeasureBase* mb) -> const RangeLock* {
     148         4125 :         return mb && ctx.conf().viewMode() == LayoutMode::PAGE
     149         8250 :                ? ctx.dom().pageLocks()->lockStartingAt(mb) : nullptr;
     150         4125 :     };
     151              : 
     152         4125 :     const RangeLock* pageLock = nullptr;
     153              : 
     154              :     // re-calculate positions for systems before current
     155              :     // (they may have been filled on previous layout)
     156         4125 :     size_t pSystems = page->systems().size();
     157         4125 :     if (pSystems > 0) {
     158          115 :         SystemLayout::restoreLayout2(page->system(0), ctx);
     159          115 :         y = page->system(0)->y() + page->system(0)->height();
     160              : 
     161              :         // Get existing page lock
     162          115 :         pageLock = getPageLock(page->system(0)->first());
     163              :     } else {
     164         4010 :         y = page->tm();
     165              :     }
     166              : 
     167         4152 :     for (size_t i = 1; i < pSystems; ++i) {
     168           27 :         System* cs = page->system(i);
     169           27 :         System* ps = page->system(i - 1);
     170           27 :         double distance = SystemLayout::minDistance(ps, cs, ctx);
     171           27 :         y += distance;
     172           27 :         cs->setPos(page->lm(), y);
     173           27 :         SystemLayout::restoreLayout2(cs, ctx);
     174           27 :         y += cs->height();
     175              :     }
     176              : 
     177              :     for (;;) {
     178              :         //
     179              :         // calculate distance to previous system
     180              :         //
     181              :         double distance;
     182        10053 :         if (ctx.state().prevSystem()) {
     183         6043 :             distance = SystemLayout::minDistance(ctx.state().prevSystem(), ctx.state().curSystem(), ctx);
     184              :         } else {
     185              :             // this is the first system on page
     186              :             // get page lock here
     187         4010 :             pageLock = getPageLock(ctx.state().curSystem()->first());
     188         4010 :             if (ctx.state().curSystem()->vbox()) {
     189              :                 // if the header exists and there is a frame, move the frame downwards
     190              :                 // to avoid collisions
     191         2149 :                 distance = headerExtension ? headerExtension + headerFooterPadding : 0.0;
     192              :             } else {
     193         1861 :                 distance = ctx.conf().styleAbsolute(Sid::staffUpperBorder);
     194         1861 :                 bool fixedDistance = false;
     195         7527 :                 for (MeasureBase* mb : ctx.mutState().curSystem()->measures()) {
     196         5666 :                     if (mb->isMeasure()) {
     197         5655 :                         Measure* m = toMeasure(mb);
     198         5655 :                         Spacer* sp = m->vspacerUp(0);
     199         5655 :                         if (sp) {
     200            0 :                             if (sp->spacerType() == SpacerType::FIXED) {
     201            0 :                                 distance = sp->absoluteGap();
     202            0 :                                 fixedDistance = true;
     203            0 :                                 break;
     204              :                             } else {
     205            0 :                                 distance = std::max(distance, sp->absoluteGap());
     206              :                             }
     207              :                         }
     208              :                     }
     209              :                 }
     210         1861 :                 if (!fixedDistance) {
     211         1861 :                     double top = ctx.state().curSystem()->minTop();
     212              :                     // ensure it doesn't collide with header
     213         1861 :                     if (headerExtension > 0.0) {
     214          134 :                         top += headerExtension + headerFooterPadding;
     215              :                     }
     216         1861 :                     distance = std::max(distance, top);
     217              :                 }
     218              :             }
     219              :         }
     220              : 
     221        10053 :         y += distance;
     222        10053 :         ctx.mutState().curSystem()->setPos(ctx.state().page()->lm(), y);
     223        10053 :         SystemLayout::restoreLayout2(ctx.mutState().curSystem(), ctx);
     224        10053 :         ctx.mutState().page()->appendSystem(ctx.mutState().curSystem());
     225        10053 :         y += ctx.state().curSystem()->height();
     226              : 
     227              :         //
     228              :         //  check for page break or if next system will fit on page
     229              :         //
     230        10053 :         bool collected = false;
     231        10053 :         if (ctx.state().rangeDone()) {
     232              :             // take next system unchanged
     233          859 :             if (systemIdx > 0) {
     234            0 :                 nextSystem = muse::value(ctx.mutDom().systems(), systemIdx++);
     235            0 :                 if (!nextSystem) {
     236              :                     // TODO: handle next movement
     237              :                 }
     238              :             } else {
     239          859 :                 nextSystem = ctx.state().systemList().empty() ? 0 : muse::takeFirst(ctx.mutState().systemList());
     240          859 :                 if (nextSystem) {
     241          590 :                     ctx.mutDom().systems().push_back(nextSystem);
     242              :                 }
     243              :             }
     244              :         } else {
     245         9194 :             nextSystem = SystemLayout::collectSystem(ctx);
     246         9194 :             if (nextSystem) {
     247         5509 :                 collected = true;
     248              :             }
     249              :         }
     250              : 
     251        10053 :         if (ctx.dom().lastSegment()) {
     252        10051 :             float curPercent = static_cast<float>(ctx.state().measureNumber()) / ctx.dom().lastSegment()->measure()->index();
     253        10051 :             page->score()->layoutProgressChannel().send(curPercent);
     254              :         }
     255              : 
     256        10053 :         ctx.mutState().setPrevSystem(ctx.mutState().curSystem());
     257        10053 :         assert(ctx.state().curSystem() != nextSystem);
     258        10053 :         ctx.mutState().setCurSystem(nextSystem);
     259              : 
     260        10053 :         MeasureBase* endMB = ctx.state().prevSystem()->last();
     261        10053 :         bool endOfPageLock = endMB && endMB->isEndOfPageLock();
     262        10053 :         const MeasureBase* nextMB = endMB ? endMB->nextMM() : nullptr;
     263        10053 :         bool pageLockStart = nextMB && nextMB->isStartOfPageLock();
     264              : 
     265        10053 :         bool isPageBreak = !ctx.state().curSystem()
     266        10053 :                            || (breakPages && (ctx.state().prevSystem()->pageBreak() || endOfPageLock || pageLockStart));
     267              : 
     268        10053 :         if (!isPageBreak && !pageLock) {
     269         5996 :             double dist = SystemLayout::minDistance(ctx.state().prevSystem(), ctx.state().curSystem(), ctx)
     270         5996 :                           + ctx.state().curSystem()->height();
     271         5996 :             Box* vbox = ctx.state().curSystem()->vbox();
     272         5996 :             if (vbox) {
     273          137 :                 if (footerExtension > 0) {
     274            8 :                     dist += footerExtension;
     275              :                 }
     276         5859 :             } else if (!ctx.state().prevSystem()->hasFixedDownDistance()) {
     277              :                 // Down spacers define distance to the following system, not to the page footer.
     278              :                 // Using them here can visually pin/stretch the current system to page edge.
     279         5857 :                 double margin = ctx.state().curSystem()->minBottom();
     280              :                 // ensure it doesn't collide with footer
     281         5857 :                 if (footerExtension > 0) {
     282           54 :                     margin += footerExtension + headerFooterPadding;
     283              :                 }
     284         5857 :                 dist += std::max(margin, slb);
     285              :             }
     286         5996 :             isPageBreak = (y + dist) >= endY && breakPages;
     287              :         }
     288        10053 :         if (isPageBreak) {
     289         4125 :             double dist = ctx.state().prevSystem()->minBottom();
     290         4125 :             if (!ctx.conf().isVerticalSpreadEnabled()) {
     291         1015 :                 dist = std::max(dist, ctx.state().prevSystem()->spacerDistance(false));
     292              :             }
     293         4125 :             double footerPadding = 0.0;
     294              :             // ensure it doesn't collide with footer
     295         4125 :             if (footerExtension > 0) {
     296           41 :                 footerPadding = footerExtension + headerFooterPadding;
     297           41 :                 dist += footerPadding;
     298              :             }
     299         4125 :             dist = std::max(dist, slb);
     300         4125 :             layoutPage(ctx, page, endY - (y + dist), footerPadding, pageLock);
     301              :             // if we collected a system we cannot fit onto this page,
     302              :             // we need to collect next page in order to correctly set system positions
     303         4125 :             if (collected) {
     304          160 :                 ctx.mutState().setPageOldMeasure(nullptr);
     305              :             }
     306         4125 :             break;
     307              :         }
     308         5928 :     }
     309              : 
     310              :     // All staves have been hidden, so correct last system's header
     311         4125 :     System* lastSys = ctx.mutState().page()->systems().back();
     312         4125 :     Measure* lastSysMeasure = lastSys->firstMeasure();
     313         4125 :     if (lastSysMeasure) {
     314         4093 :         MeasureLayout::addSystemHeader(lastSysMeasure, false, ctx);
     315              :     }
     316              : 
     317         4125 :     Fraction stick2 = Fraction(-1, 1);
     318        14320 :     for (System* s : page->systems()) {
     319        46217 :         for (MeasureBase* mb : s->measures()) {
     320        36022 :             if (!mb->isMeasure()) {
     321         2519 :                 continue;
     322              :             }
     323        33503 :             Measure* m = toMeasure(mb);
     324        33503 :             if (stick2 == Fraction(-1, 1)) {
     325         4122 :                 stick2 = m->tick();
     326              :             }
     327              : 
     328       228431 :             for (size_t track = 0; track < ctx.dom().ntracks(); ++track) {
     329       920552 :                 for (Segment* segment = m->first(); segment; segment = segment->next()) {
     330       725624 :                     EngravingItem* e2 = segment->element(track);
     331       725624 :                     if (!e2) {
     332       545598 :                         continue;
     333              :                     }
     334       180026 :                     if (e2->isChordRest()) {
     335        92940 :                         if (!ctx.dom().staff(track2staff(track))->show()) {
     336          800 :                             continue;
     337              :                         }
     338        92140 :                         ChordRest* cr = toChordRest(e2);
     339        92140 :                         if (BeamLayout::isStartOfCrossBeam(cr)) {                           // layout cross staff beams
     340           10 :                             TLayout::layoutBeam(cr->beam(), ctx);
     341           10 :                             BeamLayout::checkCrossPosAndStemConsistency(cr->beam(), ctx);
     342              :                         }
     343        92140 :                         if (TupletLayout::notTopTuplet(cr)) {
     344              :                             // fix layout of tuplets
     345           41 :                             DurationElement* de = cr;
     346           82 :                             while (de->tuplet() && de->tuplet()->elements().front() == de) {
     347           41 :                                 Tuplet* t = de->tuplet();
     348           41 :                                 TLayout::layoutTuplet(t, ctx);
     349           41 :                                 de = t;
     350              :                             }
     351              :                         }
     352              : 
     353        92140 :                         if (cr->isChord()) {
     354        48512 :                             Chord* c = toChord(cr);
     355        49421 :                             for (Chord* cc : c->graceNotes()) {
     356          909 :                                 if (cc->beam() && cc->beam()->elements().front() == cc) {
     357           92 :                                     TLayout::layoutBeam(cc->beam(), ctx);
     358           92 :                                     BeamLayout::checkCrossPosAndStemConsistency(cc->beam(), ctx);
     359              :                                 }
     360          909 :                                 ChordLayout::layoutSpanners(cc, ctx);
     361          909 :                                 for (EngravingItem* element : cc->el()) {
     362            0 :                                     if (element->isSlur()) {
     363            0 :                                         TLayout::layoutItem(element, ctx);
     364              :                                     }
     365              :                                 }
     366              :                             }
     367        48512 :                             ArpeggioLayout::layoutArpeggio2(c->arpeggio(), ctx);
     368        48680 :                             for (EngravingItem* e : c->el()) {
     369          168 :                                 if (e->isChordBracket()) {
     370            0 :                                     ChordBracket* bracket = toChordBracket(e);
     371            0 :                                     if (c->onTabStaff()) {
     372            0 :                                         TLayout::layoutItem(bracket, ctx);
     373              :                                     } else {
     374            0 :                                         ChordBracketLayout::updateVerticalGeometry(bracket, ctx);
     375              :                                     }
     376              :                                 }
     377              :                             }
     378        48512 :                             ChordLayout::layoutSpanners(c, ctx);
     379        48512 :                             if (c->tremoloTwoChord()) {
     380          220 :                                 TremoloTwoChord* t = c->tremoloTwoChord();
     381          220 :                                 Chord* c1 = t->chord1();
     382          220 :                                 Chord* c2 = t->chord2();
     383          220 :                                 if (c1 && c2 && (c1->staffMove() || c2->staffMove())) {
     384              :                                     // For cross-staff tremolo, vertical justification may have changed
     385              :                                     // staff distances, and therefore also stem directions, so re-compute them
     386            0 :                                     ChordLayout::computeUp(c1, ctx);     // This will also call a tremolo layout
     387            0 :                                     ChordLayout::computeUp(c2, ctx);
     388              :                                 }
     389              :                             }
     390              :                         }
     391        87086 :                     } else if (e2->isBarLine()) {
     392        55574 :                         TLayout::layoutBarLine2(toBarLine(e2), ctx);
     393              :                     }
     394              :                 }
     395              :             }
     396        33503 :             MeasureLayout::layout2(m, ctx);
     397              :         }
     398        10195 :         SystemLayout::layoutSystemLockIndicators(s, ctx);
     399        10195 :         if (ctx.conf().isMode(LayoutMode::PAGE)) {
     400        10195 :             SystemLayout::layoutPageLockIndicators(s);
     401              :         }
     402              : 
     403        10195 :         if (StaffVisibilityIndicator* visibilityIndicator = s->staffVisibilityIndicator()) {
     404           48 :             TLayout::layoutIndicatorIcon(visibilityIndicator, visibilityIndicator->mutldata());
     405              :         }
     406              :     }
     407              : 
     408              :     // If this is the last page we layout, we must also relayout the first barlines of the
     409              :     // next page, because they may have been altered while collecting the systems.
     410         4125 :     MeasureBase* lastOfThisPage = ctx.mutState().page()->systems().back()->measures().back();
     411         4125 :     MeasureBase* firstOfNextPage = lastOfThisPage ? lastOfThisPage->next() : nullptr;
     412         4125 :     if (firstOfNextPage && firstOfNextPage->isMeasure() && firstOfNextPage->tick() > ctx.state().endTick()) {
     413          109 :         for (Segment& segment : toMeasure(firstOfNextPage)->segments()) {
     414           90 :             if (!segment.isType(SegmentType::BarLineTypes)) {
     415           57 :                 continue;
     416              :             }
     417          277 :             for (EngravingItem* item : segment.elist()) {
     418          244 :                 if (item && item->isBarLine()) {
     419           61 :                     TLayout::layoutBarLine2(toBarLine(item), ctx);
     420              :                 }
     421              :             }
     422              :         }
     423              :     }
     424              : 
     425         4125 :     if (ctx.conf().isMode(LayoutMode::SYSTEM)) {
     426            0 :         const System* s = page->systems().back();
     427            0 :         double height = s ? s->pos().y() + s->height() + s->minBottom() : ctx.state().page()->tm();
     428            0 :         page->mutldata()->setBbox(0.0, 0.0, ctx.conf().loWidth(), height + ctx.state().page()->bm());
     429              :     }
     430              : 
     431         4125 :     layoutCrossStaffElements(ctx, page);
     432              : 
     433        14320 :     for (System* system : page->systems()) {
     434        10195 :         SystemLayout::centerElementsBetweenStaves(system);
     435        10195 :         ctx.mutState().removeSystemNeedingCentering(system);
     436              :     }
     437              : 
     438         4125 :     if (ctx.conf().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>() == TimeSigPlacement::ACROSS_STAVES
     439         4125 :         && ctx.conf().styleB(Sid::timeSigCenterAcrossStaveGroup)) {
     440            0 :         for (const System* system : page->systems()) {
     441            0 :             SystemLayout::centerBigTimeSigsAcrossStaves(system);
     442              :         }
     443              :     }
     444              : 
     445         4125 :     MaskLayout::computeMasks(ctx, page);
     446              : 
     447         4125 :     layoutSystemDividers(ctx, page);
     448              : 
     449         4125 :     page->invalidateBspTree();
     450         4125 : }
     451              : 
     452         4125 : void PageLayout::layoutCrossStaffElements(LayoutContext& ctx, Page* page)
     453              : {
     454        14320 :     for (System* system : page->systems()) {
     455        10195 :         if (!system->firstMeasure()) {
     456         3010 :             continue;
     457              :         }
     458              : 
     459         7816 :         Fraction stick = system->firstMeasure()->tick();
     460         7816 :         Fraction etick = system->endTick();
     461              : 
     462         7816 :         IF_ASSERT_FAILED(stick < etick) {
     463            0 :             continue;
     464              :         }
     465              : 
     466         7816 :         if (etick <= ctx.state().startTick() || etick > ctx.state().tick()) {
     467          631 :             continue;
     468              :         }
     469              : 
     470         7185 :         layoutCrossStaffSlurs(ctx, system);
     471              : 
     472         7185 :         layoutArticAndFingeringOnCrossStaffBeams(ctx, system);
     473              :     }
     474         4125 : }
     475              : 
     476         7185 : void PageLayout::layoutCrossStaffSlurs(LayoutContext& ctx, System* system)
     477              : {
     478         7185 :     Fraction stick = system->firstMeasure()->tick();
     479         7185 :     Fraction etick = system->endTick();
     480              : 
     481         7185 :     auto spanners = ctx.dom().spannerMap().findOverlapping(stick.ticks(), etick.ticks());
     482         9574 :     for (auto interval : spanners) {
     483         2389 :         Spanner* sp = interval.value;
     484         2389 :         if (!sp->isSlur() || sp->tick() == system->endTick()) {
     485         1489 :             continue;
     486              :         }
     487          900 :         Slur* slur = toSlur(sp);
     488          900 :         if (slur->isCrossStaff() || slur->hasCrossBeams()) {
     489           24 :             SlurTieLayout::layoutSystem(slur, system, ctx);
     490              : 
     491           24 :             ChordRest* scr = toChordRest(slur->startElement());
     492           24 :             ChordRest* ecr = toChordRest(slur->endElement());
     493           24 :             if (scr && scr->isChord()) {
     494           24 :                 ChordLayout::layoutArticulations3(toChord(scr), slur, ctx);
     495              :             }
     496           24 :             if (ecr && ecr->isChord()) {
     497           24 :                 ChordLayout::layoutArticulations3(toChord(ecr), slur, ctx);
     498              :             }
     499              :         }
     500              :     }
     501         7185 : }
     502              : 
     503         7185 : void PageLayout::layoutArticAndFingeringOnCrossStaffBeams(LayoutContext& ctx, System* system)
     504              : {
     505        37697 :     for (const MeasureBase* mb : system->measures()) {
     506        30512 :         if (!mb->isMeasure()) {
     507          135 :             continue;
     508              :         }
     509       146924 :         for (const Segment& segment : toMeasure(mb)->segments()) {
     510       116547 :             if (!segment.isChordRestType()) {
     511        53337 :                 continue;
     512              :             }
     513       417238 :             for (EngravingItem* item : segment.elist()) {
     514       354028 :                 if (!item || !item->isChord()) {
     515       305774 :                     continue;
     516              :                 }
     517              : 
     518        48254 :                 Chord* c = toChord(item);
     519        48254 :                 bool hasCrossBeam = c->beam() && (c->beam()->cross() || c->staffMove() != 0);
     520        48254 :                 if (!hasCrossBeam) {
     521        48037 :                     continue;
     522              :                 }
     523              : 
     524          217 :                 ChordLayout::layoutArticulations(c, ctx);
     525          217 :                 ChordLayout::layoutArticulations2(c, ctx, true);
     526              : 
     527          434 :                 for (Note* note : c->notes()) {
     528          242 :                     for (EngravingItem* e : note->el()) {
     529           25 :                         if (!e || !e->isFingering()) {
     530            0 :                             continue;
     531              :                         }
     532           25 :                         Fingering* fingering = toFingering(e);
     533           25 :                         if (fingering->isOnCrossBeamSide()) {
     534           25 :                             TLayout::layoutFingering(fingering, fingering->mutldata());
     535           25 :                             if (fingering->addToSkyline()) {
     536           25 :                                 const Note* n = fingering->note();
     537           50 :                                 const RectF r = fingering->ldata()->bbox().translated(
     538           25 :                                     fingering->pos() + n->pos() + n->chord()->pos() + segment.pos()
     539           50 :                                     + segment.measure()->pos());
     540           25 :                                 system->staff(fingering->note()->chord()->vStaffIdx())->skyline().add(r, fingering);
     541              :                             }
     542              :                         }
     543              :                     }
     544              :                 }
     545              :             }
     546              :         }
     547              :     }
     548         7185 : }
     549              : 
     550              : //---------------------------------------------------------
     551              : //   layoutPage
     552              : //    restHeight - vertical space which has to be distributed
     553              : //                 between systems
     554              : //    The algorithm tries to produce most equally spaced
     555              : //    systems.
     556              : //---------------------------------------------------------
     557              : 
     558         4125 : void PageLayout::layoutPage(LayoutContext& ctx, Page* page, double restHeight, double footerPadding, bool squeezeToFit)
     559              : {
     560         4140 :     TRACEFUNC;
     561         4125 :     if (restHeight < 0.0 && !squeezeToFit) {
     562              :         LOGN("restHeight < 0.0: %f\n", restHeight);
     563           13 :         restHeight = 0;
     564              :     }
     565              : 
     566         4125 :     int gaps = static_cast<int>(page->systems().size()) - 1;
     567              : 
     568         4125 :     std::vector<System*> sList;
     569              : 
     570              :     // build list of systems (excluding last)
     571              :     // set initial distance for each to the unstretched minimum distance to next
     572        10195 :     for (int i = 0; i < gaps; ++i) {
     573         6070 :         System* s1 = page->systems().at(i);
     574         6070 :         System* s2 = page->systems().at(i + 1);
     575         6070 :         s1->setDistance(s2->y() - s1->y());
     576         6070 :         if (s1->vbox() || s2->vbox() || s1->hasFixedDownDistance()) {
     577         2398 :             continue;
     578              :         }
     579         3672 :         sList.push_back(s1);
     580              :     }
     581              : 
     582         5377 :     if (sList.empty() || MScore::noVerticalStretch || ctx.conf().isVerticalSpreadEnabled()
     583         5377 :         || ctx.conf().viewMode() == LayoutMode::SYSTEM) {
     584         3769 :         if (ctx.conf().viewMode() == LayoutMode::FLOAT) {
     585            0 :             for (System* system : page->systems()) {
     586            0 :                 system->move(PointF(0.0, 0.0));
     587              :             }
     588         3769 :         } else if ((ctx.conf().viewMode() != LayoutMode::SYSTEM) && ctx.conf().isVerticalSpreadEnabled()) {
     589         3110 :             distributeStaves(ctx, page, footerPadding, squeezeToFit);
     590              :         }
     591              : 
     592         3769 :         return;
     593              :     }
     594              : 
     595          356 :     const bool compactStaves = restHeight <= 0.0;
     596          413 :     auto pageFilled = [compactStaves](double space) -> bool {
     597          413 :         return compactStaves ? (space >= 0.0) : (space <= 0.0);
     598          356 :     };
     599              : 
     600          356 :     double maxDist = ctx.conf().maxSystemDistance();
     601              : 
     602              :     // allocate space as needed to normalize system distance (bottom of one system to top of next)
     603         2670 :     std::sort(sList.begin(), sList.end(), [](System* a, System* b) { return a->distance() - a->height() < b->distance() - b->height(); });
     604          356 :     System* s0 = sList[0];
     605          356 :     double dist = s0->distance() - s0->height();             // distance for shortest system
     606         1415 :     for (size_t i = 1; i < sList.size(); ++i) {
     607         1075 :         System* si = sList[i];
     608         1075 :         double ndist = si->distance() - si->height();        // next taller system
     609         1075 :         double fill  = ndist - dist;                         // amount by which this system distance exceeds next shorter
     610         1075 :         if (fill > 0.0) {
     611          413 :             double totalFill = fill * static_cast<double>(i); // space required to add this amount to all shorter systems
     612          413 :             if (totalFill > restHeight) {
     613           16 :                 totalFill = restHeight;                     // too much; adjust amount
     614           16 :                 fill = restHeight / static_cast<double>(i);
     615              :             }
     616         1527 :             for (size_t k = 0; k < i; ++k) {                   // add amount to all shorter systems
     617         1114 :                 System* s = sList[k];
     618         1114 :                 double d = s->distance() + fill;
     619         1114 :                 if ((d - s->height()) > maxDist) {          // but don't exceed max system distance
     620          190 :                     d = std::max(maxDist + s->height(), s->distance());
     621              :                 }
     622         1114 :                 s->setDistance(d);
     623              :             }
     624          413 :             restHeight -= totalFill;                        // reduce available space for next iteration
     625          413 :             if (pageFilled(restHeight)) {
     626           16 :                 break;                                      // no space left
     627              :             }
     628              :         }
     629         1059 :         dist = ndist;                                       // set up for next iteration
     630              :     }
     631              : 
     632          356 :     if (restHeight > 0.0) {                                 // space left?
     633          340 :         double fill = restHeight / static_cast<double>(sList.size());
     634         1659 :         for (System* s : sList) {                           // allocate it to systems equally
     635         1319 :             double d = s->distance() + fill;
     636         1319 :             if ((d - s->height()) > maxDist) {              // but don't exceed max system distance
     637          564 :                 d = std::max(maxDist + s->height(), s->distance());
     638              :             }
     639         1319 :             s->setDistance(d);
     640              :         }
     641              :     }
     642              : 
     643          356 :     double y = page->systems().at(0)->y();
     644         2059 :     for (int i = 0; i < gaps; ++i) {
     645         1703 :         System* s1  = page->systems().at(i);
     646         1703 :         s1->mutldata()->setPosY(y);
     647         1703 :         y += s1->distance();
     648              :     }
     649              : 
     650          356 :     page->systems().back()->mutldata()->setPosY(y);
     651         7894 : }
     652              : 
     653         3110 : void PageLayout::distributeStaves(LayoutContext& ctx, Page* page, double footerPadding, bool squeezeToFit)
     654              : {
     655         3110 :     VerticalGapDataList vgdl;
     656              : 
     657              :     // Find and classify all gaps between staves.
     658         3110 :     int ngaps { 0 };
     659         3110 :     double prevYBottom  { page->tm() };
     660         3110 :     double yBottom      { 0.0 };
     661         3110 :     double spacerOffset { 0.0 };
     662         3110 :     bool vbox          { false };
     663         3110 :     Spacer* nextSpacer { nullptr };
     664         3110 :     bool transferNormalBracket { false };
     665         3110 :     bool transferCurlyBracket  { false };
     666        10060 :     for (System* system : page->systems()) {
     667         6950 :         if (system->vbox()) {
     668         1592 :             VerticalGapData* vgd = new VerticalGapData(&ctx.conf().style(), !ngaps++, system, nullptr, nullptr, nullptr, prevYBottom);
     669         1592 :             vgd->addSpaceAroundVBox(true);
     670         1592 :             prevYBottom = system->y();
     671         1592 :             yBottom     = system->y() + system->height();
     672         1592 :             vbox        = true;
     673         1592 :             vgdl.push_back(vgd);
     674         1592 :             transferNormalBracket = false;
     675         1592 :             transferCurlyBracket  = false;
     676              :         } else {
     677         5358 :             bool newSystem       { true };
     678         5358 :             bool addSpaceAroundNormalBracket { false };
     679         5358 :             bool addSpaceAroundCurlyBracket  { false };
     680         5358 :             int endNormalBracket { -1 };
     681         5358 :             int endCurlyBracket  { -1 };
     682         5358 :             int staffNr { -1 };
     683        13766 :             for (SysStaff* sysStaff : system->staves()) {
     684         8408 :                 const Staff* staff = ctx.dom().staff(++staffNr);
     685         8408 :                 IF_ASSERT_FAILED(staff) {
     686            0 :                     break;
     687              :                 }
     688         8408 :                 addSpaceAroundNormalBracket |= endNormalBracket == staffNr;
     689         8408 :                 addSpaceAroundCurlyBracket  |= endCurlyBracket == staffNr;
     690        11185 :                 for (const BracketItem* bi : ctx.dom().brackets(staffNr)) {
     691         2777 :                     if (bi->bracketType() == BracketType::NORMAL) {
     692          448 :                         addSpaceAroundNormalBracket |= int(staff->idx()) > (endNormalBracket - 1);
     693          448 :                         endNormalBracket = std::max(endNormalBracket, int(staff->idx() + bi->bracketSpan()));
     694         2329 :                     } else if (bi->bracketType() == BracketType::BRACE) {
     695          946 :                         addSpaceAroundCurlyBracket |= int(staff->idx()) > (endCurlyBracket - 1);
     696          946 :                         endCurlyBracket = std::max(endCurlyBracket, int(staff->idx() + bi->bracketSpan()));
     697              :                     }
     698              :                 }
     699              : 
     700         8408 :                 if (!sysStaff->show()) {
     701          199 :                     continue;
     702              :                 }
     703              : 
     704              :                 VerticalGapData* vgd
     705         8209 :                     = new VerticalGapData(&ctx.conf().style(), !ngaps++, system, staff, sysStaff, nextSpacer, prevYBottom);
     706         8209 :                 nextSpacer = system->downSpacer(staff->idx());
     707              : 
     708         8209 :                 if (newSystem) {
     709         5355 :                     vgd->addSpaceBetweenSections();
     710         5355 :                     newSystem = false;
     711              :                 }
     712         8209 :                 if (addSpaceAroundNormalBracket || transferNormalBracket) {
     713          465 :                     vgd->addSpaceAroundNormalBracket();
     714          465 :                     addSpaceAroundNormalBracket = false;
     715          465 :                     transferNormalBracket = false;
     716              :                 }
     717         8209 :                 if (addSpaceAroundCurlyBracket || transferCurlyBracket) {
     718         1063 :                     vgd->addSpaceAroundCurlyBracket();
     719         1063 :                     addSpaceAroundCurlyBracket = false;
     720         1063 :                     transferCurlyBracket = false;
     721         7146 :                 } else if (staffNr < endCurlyBracket) {
     722          686 :                     vgd->insideCurlyBracket();
     723              :                 }
     724              : 
     725         8209 :                 if (vbox) {
     726         1576 :                     vgd->addSpaceAroundVBox(false);
     727         1576 :                     vbox = false;
     728              :                 }
     729              : 
     730         8209 :                 prevYBottom  = system->y() + sysStaff->bbox().bottom();
     731         8209 :                 yBottom      = system->y() + sysStaff->y() + sysStaff->skyline().south().max();
     732         8209 :                 spacerOffset = sysStaff->skyline().south().max() - sysStaff->bbox().height();
     733         8209 :                 vgdl.push_back(vgd);
     734              :             }
     735         5358 :             transferNormalBracket = endNormalBracket >= 0;
     736         5358 :             transferCurlyBracket  = endCurlyBracket >= 0;
     737              :         }
     738              :     }
     739         3110 :     --ngaps;
     740         3110 :     const double staffLowerBorder = ctx.conf().styleAbsolute(Sid::staffLowerBorder);
     741         3110 :     const double combinedBottomMargin = page->bm() + footerPadding;
     742         3110 :     const double marginToStaff = page->bm() + staffLowerBorder;
     743         3110 :     double spaceRemaining{ std::min(page->height() - combinedBottomMargin - yBottom, page->height() - marginToStaff - prevYBottom) };
     744              : 
     745         3110 :     if (nextSpacer) {
     746            0 :         spaceRemaining -= std::max(0.0, nextSpacer->absoluteGap() - spacerOffset - staffLowerBorder);
     747              :     }
     748         3110 :     if (spaceRemaining <= 0.0 && !squeezeToFit) {
     749            2 :         return;
     750              :     }
     751              : 
     752         3108 :     const bool compactStaves = spaceRemaining <= 0.0;
     753         9587 :     auto breakLoop = [compactStaves](double space) -> bool {
     754         9587 :         return compactStaves ? (space >= 0.0) : (space <= 0.0);
     755         3108 :     };
     756              : 
     757              :     // Try to make the gaps equal, taking the spread factors and maximum spacing into account.
     758              :     static const int maxPasses { 20 };                  // Saveguard to prevent endless loops.
     759         3108 :     int pass { 0 };
     760         4946 :     while (!muse::RealIsNull(spaceRemaining) && (ngaps > 0) && (++pass < maxPasses)) {
     761         3927 :         ngaps = 0;
     762         3927 :         double smallest     { vgdl.smallest() };
     763         3927 :         double nextSmallest { vgdl.smallest(smallest) };
     764         3927 :         if (muse::RealIsNull(smallest) || muse::RealIsNull(nextSmallest)) {
     765         2089 :             break;
     766              :         }
     767              : 
     768         1838 :         if ((nextSmallest - smallest) * vgdl.sumStretchFactor() > spaceRemaining) {
     769         1018 :             nextSmallest = smallest + spaceRemaining / vgdl.sumStretchFactor();
     770              :         }
     771              : 
     772         1838 :         double addedSpace { 0.0 };
     773         1838 :         VerticalGapDataList modified;
     774        19010 :         for (VerticalGapData* vgd : vgdl) {
     775        17172 :             if (!muse::RealIsNull(vgd->spacing() - smallest)) {
     776         9423 :                 continue;
     777              :             }
     778         7749 :             double step { nextSmallest - vgd->spacing() };
     779         7749 :             if (step < 0.0) {
     780            0 :                 continue;
     781              :             }
     782         7749 :             step = vgd->addSpacing(step);
     783         7749 :             if (!muse::RealIsNull(step)) {
     784         7542 :                 addedSpace += step * vgd->factor();
     785         7542 :                 modified.push_back(vgd);
     786         7542 :                 ++ngaps;
     787              :             }
     788         7749 :             if (breakLoop(spaceRemaining - addedSpace)) {
     789            0 :                 break;
     790              :             }
     791              :         }
     792         1838 :         if (breakLoop(spaceRemaining - addedSpace)) {
     793            0 :             for (VerticalGapData* vgd : modified) {
     794            0 :                 vgd->undoLastAddSpacing();
     795              :             }
     796            0 :             ngaps = 0;
     797              :         } else {
     798         1838 :             spaceRemaining -= addedSpace;
     799              :         }
     800         1838 :     }
     801              : 
     802              :     // If there is still space left, distribute the space of the staves.
     803              :     // However, there is a limit on how much space is added per gap.
     804         3108 :     const double maxPageFill = ctx.conf().styleAbsolute(Sid::maxPageFillSpread);
     805         3108 :     spaceRemaining = std::min(maxPageFill * static_cast<double>(vgdl.size()), spaceRemaining);
     806         3108 :     pass = 0;
     807         3108 :     ngaps = 1;
     808         7656 :     while (!muse::RealIsNull(spaceRemaining) && !muse::RealIsNull(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) {
     809         4548 :         ngaps = 0;
     810         4548 :         double addedSpace { 0.0 };
     811         4548 :         double step { spaceRemaining / vgdl.sumStretchFactor() };
     812        22399 :         for (VerticalGapData* vgd : vgdl) {
     813        17851 :             double res { vgd->addFillSpacing(step, maxPageFill) };
     814        17851 :             if (!muse::RealIsNull(res)) {
     815         7063 :                 addedSpace += res * vgd->factor();
     816         7063 :                 ++ngaps;
     817              :             }
     818              :         }
     819         4548 :         spaceRemaining -= addedSpace;
     820              :     }
     821              : 
     822         3108 :     std::set<System*> systems;
     823         3108 :     double systemShift { 0.0 };
     824         3108 :     double staffShift  { 0.0 };
     825         3108 :     System* prvSystem { nullptr };
     826        12877 :     for (VerticalGapData* vgd : vgdl) {
     827         9769 :         if (vgd->sysStaff) {
     828         8177 :             systems.insert(vgd->system);
     829              :         }
     830         9769 :         systemShift += vgd->actualAddedSpace();
     831         9769 :         if (prvSystem == vgd->system) {
     832         2824 :             staffShift += vgd->actualAddedSpace();
     833              :         } else {
     834         6945 :             vgd->system->mutldata()->moveY(systemShift);
     835         6945 :             if (prvSystem) {
     836         3837 :                 prvSystem->setDistance(vgd->system->y() - prvSystem->y());
     837         3837 :                 prvSystem->setHeight(prvSystem->height() + staffShift);
     838              :             }
     839         6945 :             staffShift = 0.0;
     840              :         }
     841              : 
     842         9769 :         if (vgd->sysStaff) {
     843         8177 :             vgd->sysStaff->bbox().translate(0.0, staffShift);
     844              :         }
     845              : 
     846         9769 :         prvSystem = vgd->system;
     847              :     }
     848         3108 :     if (prvSystem) {
     849         3108 :         prvSystem->setHeight(prvSystem->height() + staffShift);
     850              :     }
     851              : 
     852         8461 :     for (System* system : systems) {
     853         5353 :         SystemLayout::setMeasureHeight(system, system->height(), ctx);
     854         5353 :         SystemHeaderLayout::layoutBracketsVertical(system, ctx);
     855         5353 :         SystemHeaderLayout::setInstrumentNamesVerticalPos(system, ctx);
     856              :     }
     857         3108 :     vgdl.deleteAll();
     858         3110 : }
     859              : 
     860         4125 : void PageLayout::layoutSystemDividers(LayoutContext& ctx, Page* page)
     861              : {
     862         4125 :     const std::vector<System*>& systems = page->systems();
     863        14320 :     for (size_t i = 0; i < systems.size(); ++i) {
     864        10195 :         System* system = systems[i];
     865        10195 :         if (system->vbox()) {
     866         2371 :             continue;
     867              :         }
     868              : 
     869         7824 :         System* nextSystem = i + 1 < systems.size() ? systems[i + 1] : nullptr;
     870              : 
     871         7824 :         bool needsDivider = nextSystem && !nextSystem->vbox();
     872         7824 :         bool needsLeftDivider = needsDivider && ctx.conf().styleB(Sid::dividerLeft);
     873         7824 :         bool needsRightDivider = needsDivider && ctx.conf().styleB(Sid::dividerRight);
     874              : 
     875         7824 :         updateSystemDivider(ctx, system, nextSystem, SystemDividerType::LEFT, needsLeftDivider);
     876         7824 :         updateSystemDivider(ctx, system, nextSystem, SystemDividerType::RIGHT, needsRightDivider);
     877              :     }
     878         4125 : }
     879              : 
     880        15648 : void PageLayout::updateSystemDivider(LayoutContext& ctx, System* system, System* nextSystem, SystemDividerType type, bool needsDivider)
     881              : {
     882        15648 :     bool left = type == SystemDividerType::LEFT;
     883        15648 :     SystemDivider* divider = left ? system->systemDividerLeft() : system->systemDividerRight();
     884        15648 :     if (divider) {
     885            6 :         system->remove(divider);
     886              :     }
     887              : 
     888        15648 :     if (!needsDivider) {
     889        15620 :         return;
     890              :     }
     891              : 
     892           28 :     DO_ASSERT(system && nextSystem);
     893              : 
     894           28 :     Score* score = system->score();
     895           28 :     size_t systemIdx = muse::indexOf(score->systems(), system);
     896           28 :     IF_ASSERT_FAILED(systemIdx != muse::nidx) {
     897            0 :         return;
     898              :     }
     899              : 
     900           28 :     divider = score->systemDivider(systemIdx, type);
     901           28 :     if (!divider) {
     902           14 :         divider = new SystemDivider(system);
     903           14 :         divider->setDividerType(type);
     904           14 :         divider->setGenerated(true);
     905           14 :         score->addSystemDivider(systemIdx, divider);
     906              :     }
     907              : 
     908           28 :     system->add(divider);
     909              : 
     910           28 :     SystemDivider::LayoutData* ldata = divider->mutldata();
     911           28 :     TLayout::layoutSystemDivider(divider, ldata, ctx);
     912              : 
     913           28 :     RectF systemBBox = system->ldata()->bbox();
     914           28 :     double xDefault = 0.0;
     915           28 :     if (left) {
     916           14 :         if (Measure* firstM = system->firstMeasure(); firstM&& ctx.conf().styleB(Sid::dividerLeftAlignToSystemBarline)) {
     917              :             // Align to the outermost left system barline
     918           14 :             double leftMostSystem = firstM->x();
     919           66 :             for (const System* sys : system->page()->systems()) {
     920           52 :                 if (sys->vbox()) {
     921            4 :                     continue;
     922              :                 }
     923           48 :                 Measure* fm = sys->firstMeasure();
     924           48 :                 if (fm) {
     925           48 :                     leftMostSystem = std::min(leftMostSystem, fm->x());
     926              :                 }
     927              :             }
     928           14 :             xDefault = leftMostSystem - 0.5 * ldata->bbox().width();
     929              :         } else {
     930            0 :             xDefault = 0.0;
     931              :         }
     932              :     } else {
     933           14 :         if (Measure* lastM = system->lastMeasure(); lastM&& ctx.conf().styleB(Sid::dividerRightAlignToSystemBarline)) {
     934              :             // Align to the outermost right system barline
     935           14 :             double rightMostSystem = lastM->x() + lastM->width();
     936           66 :             for (const System* sys : system->page()->systems()) {
     937           52 :                 if (sys->vbox()) {
     938            4 :                     continue;
     939              :                 }
     940           48 :                 Measure* lm = sys->lastMeasure();
     941           48 :                 if (lm) {
     942           48 :                     rightMostSystem = std::max(rightMostSystem, lm->x() + lm->width());
     943              :                 }
     944              :             }
     945           14 :             xDefault = rightMostSystem - 0.5 * ldata->bbox().width();
     946              :         } else {
     947            0 :             xDefault = systemBBox.right() - ldata->bbox().width();
     948              :         }
     949              :     }
     950              :     double xPos = xDefault
     951           28 :                   + system->absoluteFromSpatium(left ? ctx.conf().styleS(Sid::dividerLeftX) : ctx.conf().styleS(Sid::dividerRightX));
     952              : 
     953           28 :     double yInnerPos = -ldata->bbox().top() - 0.5 * ldata->bbox().height()
     954           28 :                        + system->absoluteFromSpatium(left ? ctx.conf().styleS(Sid::dividerLeftY) : ctx.conf().styleS(Sid::dividerRightY));
     955              : 
     956           28 :     SysStaff* lastVisibleOfThis = system->staff(system->lastVisibleStaff());
     957           28 :     double bottomOfThisSystem = lastVisibleOfThis->bbox().bottom();
     958              : 
     959           28 :     SysStaff* firstVisibleOfNext = system->staff(system->firstVisibleStaff());
     960           28 :     double yNextSystem = nextSystem->pagePos().y() - system->pagePos().y();
     961           28 :     double topOfNextSystem = yNextSystem + firstVisibleOfNext->bbox().top();
     962              : 
     963           28 :     double yMid = 0.5 * (bottomOfThisSystem + topOfNextSystem);
     964              : 
     965           28 :     ldata->setPos(xPos, yInnerPos + yMid);
     966              : }
        

Generated by: LCOV version 2.0-1