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 "scoreverticalviewlayout.h"
23 :
24 : #include "containers.h"
25 :
26 : #include "dom/score.h"
27 : #include "dom/system.h"
28 : #include "dom/bracket.h"
29 : #include "dom/layoutbreak.h"
30 : #include "dom/page.h"
31 :
32 : #include "passresetlayoutdata.h"
33 : #include "passlayoutindependentitems.h"
34 :
35 : #include "measurelayout.h"
36 : #include "systemlayout.h"
37 : #include "pagelayout.h"
38 :
39 : #include "log.h"
40 :
41 : using namespace mu::engraving;
42 : using namespace mu::engraving::rendering::score;
43 :
44 0 : void ScoreVerticalViewLayout::layoutVerticalView(Score* score, LayoutContext& ctx, const Fraction& stick, const Fraction& etick)
45 : {
46 0 : ctx.mutState().setEndTick(etick);
47 :
48 : //---------------------------------------------------
49 : // initialize layout context lc
50 : //---------------------------------------------------
51 :
52 0 : MeasureBase* m = score->tick2measure(stick);
53 0 : if (m == 0) {
54 0 : m = score->first();
55 : }
56 : // start layout one measure earlier to handle clefs and cautionary elements
57 0 : if (m->prevMeasureMM()) {
58 0 : m = m->prevMeasureMM();
59 0 : } else if (m->prev()) {
60 0 : m = m->prev();
61 : }
62 0 : while (!m->isMeasure() && m->prev()) {
63 0 : m = m->prev();
64 : }
65 :
66 : // if the first measure of the score is part of a multi measure rest
67 : // m->system() will return a nullptr. We need to find the multi measure
68 : // rest which replaces the measure range
69 :
70 0 : if (!m->system() && m->isMeasure() && toMeasure(m)->hasMMRest()) {
71 0 : LOGD(" don’t start with mmrest");
72 0 : m = toMeasure(m)->mmRest();
73 : }
74 :
75 0 : if (!ctx.state().isLayoutAll() && m->system()) {
76 0 : System* system = m->system();
77 0 : system_idx_t systemIndex = muse::indexOf(score->systems(), system);
78 0 : ctx.mutState().setPage(system->page());
79 0 : ctx.mutState().setPageIdx(score->pageIdx(ctx.state().page()));
80 0 : if (ctx.state().pageIdx() == muse::nidx) {
81 0 : ctx.mutState().setPageIdx(0);
82 : }
83 0 : ctx.mutState().setCurSystem(system);
84 0 : ctx.mutState().setSystemList(muse::mid(score->systems(), systemIndex));
85 :
86 0 : if (systemIndex == 0) {
87 0 : ctx.mutState().setNextMeasure(ctx.conf().isShowVBox() ? score->first() : score->firstMeasure());
88 : } else {
89 0 : System* prevSystem = score->systems()[systemIndex - 1];
90 0 : ctx.mutState().setNextMeasure(prevSystem->measures().back()->next());
91 : }
92 :
93 0 : score->systems().erase(score->systems().begin() + systemIndex, score->systems().end());
94 0 : if (!ctx.state().nextMeasure()->prevMeasure()) {
95 0 : ctx.mutState().setMeasureNumber(0);
96 0 : ctx.mutState().setTick(Fraction(0, 1));
97 : } else {
98 0 : const MeasureBase* mb = ctx.state().nextMeasure()->prev();
99 0 : if (mb) {
100 0 : mb = mb->mbWithPrecedingSectionBreak();
101 : }
102 :
103 0 : const LayoutBreak* layoutBreak = mb->sectionBreakElement();
104 : // TODO: also use mb in else clause here?
105 : // probably not, only actual measures have meaningful numbers
106 0 : if (layoutBreak && layoutBreak->startWithMeasureOne()) {
107 0 : ctx.mutState().setMeasureNumber(0);
108 : } else {
109 0 : ctx.mutState().setMeasureNumber(ctx.state().nextMeasure()->prevMeasure()->measureNumber() // will be adjusted later with respect
110 0 : + (ctx.state().nextMeasure()->prevMeasure()->excludeFromNumbering() ? 0 : 1)); // to the user-defined offset.
111 : }
112 0 : ctx.mutState().setTick(ctx.state().nextMeasure()->tick());
113 : }
114 : } else {
115 0 : for (System* s : score->systems()) {
116 0 : for (Bracket* b : s->brackets()) {
117 0 : if (b->selected()) {
118 0 : bool selected = b->selected();
119 0 : score->selection().remove(b);
120 0 : BracketItem* item = b->bracketItem();
121 0 : item->setSelected(selected);
122 0 : score->setSelectionChanged(true);
123 : }
124 : }
125 : // no need to clear the page placement: the systems are deleted below,
126 : // and ~System unlinks itself from its page
127 : }
128 0 : for (MeasureBase* mb = score->first(); mb; mb = mb->next()) {
129 0 : mb->setSystem(nullptr);
130 0 : if (mb->isMeasure() && toMeasure(mb)->mmRest()) {
131 0 : toMeasure(mb)->mmRest()->setSystem(nullptr);
132 : }
133 : }
134 0 : muse::DeleteAll(score->systems());
135 0 : score->systems().clear();
136 :
137 0 : muse::DeleteAll(score->pages());
138 0 : score->pages().clear();
139 :
140 0 : ctx.mutState().setNextMeasure(ctx.conf().isShowVBox() ? score->first() : score->firstMeasure());
141 : }
142 :
143 0 : ctx.mutState().setPrevMeasure(nullptr);
144 :
145 0 : PassResetLayoutData resetPass;
146 0 : resetPass.run(score, ctx);
147 :
148 0 : MeasureLayout::getNextMeasure(ctx);
149 0 : ctx.mutState().setCurSystem(SystemLayout::collectSystem(ctx));
150 :
151 0 : if (ctx.state().isLayoutAll()) {
152 0 : PassLayoutIndependentItems independentPass;
153 0 : independentPass.run(score, ctx);
154 0 : }
155 :
156 0 : doLayout(ctx);
157 0 : }
158 :
159 0 : void ScoreVerticalViewLayout::doLayout(LayoutContext& ctx)
160 : {
161 0 : const MeasureBase* lmb = nullptr;
162 : do {
163 0 : PageLayout::getNextPage(ctx);
164 0 : PageLayout::collectPage(ctx);
165 :
166 0 : if (ctx.state().page() && !ctx.state().page()->systems().empty()) {
167 0 : lmb = ctx.state().page()->systems().back()->measures().back();
168 : } else {
169 0 : lmb = nullptr;
170 : }
171 :
172 : // we can stop collecting pages when:
173 : // 1) we reach the end of score (curSystem is nullptr)
174 : // or
175 : // 2) we have fully processed the range and reached a point of stability:
176 : // a) we have completed layout for the range (rangeDone is true)
177 : // b) we haven't collected a system that will need to go on the next page
178 : // c) this page ends with the same measure as the previous layout
179 : // pageOldMeasure will be last measure from previous layout if range was completed on or before this page
180 : // it will be nullptr if this page was never laid out or if we collected a system for next page
181 0 : } while (ctx.state().curSystem() && !(ctx.state().rangeDone() && lmb == ctx.state().pageOldMeasure()));
182 : // && page->system(0)->measures().back()->tick() > endTick // FIXME: perhaps the first measure was meant? Or last system?
183 :
184 0 : SystemLayout::centerPendingSystems(ctx);
185 :
186 0 : if (!ctx.state().curSystem()) {
187 : // The end of the score. The remaining systems are not needed...
188 0 : muse::DeleteAll(ctx.mutState().systemList());
189 0 : ctx.mutState().systemList().clear();
190 : // ...and the remaining pages too
191 0 : while (ctx.dom().npages() > ctx.state().pageIdx()) {
192 0 : Page* p = ctx.mutDom().pages().back();
193 0 : ctx.mutDom().pages().pop_back();
194 0 : delete p;
195 : }
196 : } else {
197 0 : Page* p = ctx.mutState().curSystem()->page();
198 0 : if (p && (p != ctx.state().page())) {
199 0 : p->invalidateBspTree();
200 : }
201 : }
202 0 : ctx.mutDom().systems().insert(ctx.mutDom().systems().end(), ctx.state().systemList().begin(), ctx.state().systemList().end());
203 0 : }
|