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
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "systemheaderlayout.h"
24 :
25 : #include "systemlayout.h"
26 : #include "tlayout.h"
27 :
28 : #include "dom/bracket.h"
29 : #include "dom/factory.h"
30 : #include "dom/part.h"
31 : #include "dom/staff.h"
32 : #include "dom/system.h"
33 : #include "style/defaultstyle.h"
34 :
35 : using namespace mu::engraving;
36 : using namespace mu::engraving::rendering::score;
37 :
38 14371 : double SystemHeaderLayout::layoutBrackets(System* system, LayoutContext& ctx)
39 : {
40 14371 : size_t nstaves = system->staves().size();
41 14371 : size_t columns = system->getBracketsColumnsCount();
42 :
43 14371 : std::vector<double> bracketWidth(columns, 0.0);
44 :
45 14371 : std::vector<Bracket*> bl;
46 14371 : bl.swap(system->brackets());
47 :
48 36159 : for (size_t staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
49 33046 : for (size_t i = 0; i < columns; ++i) {
50 22916 : for (BracketItem* bi : ctx.dom().brackets(staffIdx)) {
51 11658 : if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET) {
52 8156 : continue;
53 : }
54 3502 : Bracket* b = createBracket(system, ctx, bi, i, static_cast<int>(staffIdx), bl, system->firstMeasure());
55 3502 : if (b != nullptr) {
56 2781 : b->mutldata()->bracketHeight.set_value(3.5 * b->spatium() * 2); // dummy
57 2781 : TLayout::layoutBracket(b, b->mutldata(), ctx.conf());
58 2781 : if (bi->bracketType() != BracketType::GROUP) { // Because Group brackets always stay outside
59 2781 : bracketWidth[i] = std::max(bracketWidth[i], b->ldata()->bracketWidth());
60 : }
61 : }
62 : }
63 : }
64 : }
65 :
66 14495 : for (Bracket* b : bl) {
67 124 : delete b;
68 : }
69 :
70 14371 : double totalBracketWidth = 0.0;
71 :
72 14371 : if (!system->brackets().empty()) {
73 5673 : for (double w : bracketWidth) {
74 3384 : totalBracketWidth += w;
75 : }
76 : }
77 :
78 14371 : return totalBracketWidth;
79 14371 : }
80 :
81 3537 : Bracket* SystemHeaderLayout::createBracket(System* system, LayoutContext& ctx, BracketItem* bi, size_t column, staff_idx_t staffIdx,
82 : std::vector<Bracket*>& bl, Measure* measure)
83 : {
84 3537 : if (!measure) {
85 0 : return nullptr;
86 : }
87 :
88 3537 : size_t nstaves = system->staves().size();
89 3537 : staff_idx_t firstStaff = staffIdx;
90 3537 : staff_idx_t lastStaff = staffIdx + bi->bracketSpan() - 1;
91 3537 : if (lastStaff >= nstaves) {
92 665 : lastStaff = nstaves - 1;
93 : }
94 :
95 3811 : for (; firstStaff <= lastStaff; ++firstStaff) {
96 3682 : if (system->staff(firstStaff)->show()) {
97 3408 : break;
98 : }
99 : }
100 3846 : for (; lastStaff >= firstStaff; --lastStaff) {
101 3717 : if (system->staff(lastStaff)->show()) {
102 3408 : break;
103 : }
104 : }
105 3537 : size_t span = lastStaff - firstStaff + 1;
106 : //
107 : // do not show bracket if it only spans one
108 : // system due to some invisible staves
109 : //
110 3537 : if (span > 1
111 746 : || (bi->bracketSpan() == span)
112 721 : || (span == 1 && ctx.conf().styleB(Sid::alwaysShowBracketsWhenEmptyStavesAreHidden)
113 0 : && bi->bracketType() != BracketType::SQUARE)
114 4283 : || (span == 1 && ctx.conf().styleB(Sid::alwaysShowSquareBracketsWhenEmptyStavesAreHidden)
115 0 : && bi->bracketType() == BracketType::SQUARE)) {
116 : //
117 : // this bracket is visible
118 : //
119 2816 : Bracket* b = 0;
120 2816 : track_idx_t track = staffIdx * VOICES;
121 2872 : for (size_t k = 0; k < bl.size(); ++k) {
122 3051 : if (bl[k]->track() == track && bl[k]->column() == column && bl[k]->bracketType() == bi->bracketType()
123 3051 : && bl[k]->measure() == measure) {
124 1481 : b = muse::takeAt(bl, k);
125 1481 : break;
126 : }
127 : }
128 2816 : if (b == 0) {
129 1335 : b = Factory::createBracket(ctx.mutDom().dummyParent());
130 1335 : b->setBracketItem(bi);
131 1335 : b->setGenerated(true);
132 1335 : b->setTrack(track);
133 1335 : b->setMeasure(measure);
134 : }
135 2816 : system->add(b);
136 :
137 2816 : if (bi->selected()) {
138 0 : bool needSelect = true;
139 :
140 0 : std::vector<EngravingItem*> brackets = ctx.selection().elements(ElementType::BRACKET);
141 0 : for (const EngravingItem* element : brackets) {
142 0 : if (toBracket(element)->bracketItem() == bi) {
143 0 : needSelect = false;
144 0 : break;
145 : }
146 : }
147 :
148 0 : if (needSelect) {
149 0 : ctx.select(b, SelectType::ADD);
150 : }
151 0 : }
152 :
153 2816 : b->setStaffSpan(firstStaff, lastStaff);
154 :
155 : InstrumentLabelVisibility visibility
156 2816 : = resolveInstrumentLabelVisibility(ctx.dom().staff(firstStaff), measure->tick(), ctx, ctx.state().firstSystem());
157 2816 : b->setLabelType(resolveInstrumentNameType(visibility));
158 :
159 2816 : return b;
160 : }
161 :
162 721 : return nullptr;
163 : }
164 :
165 99 : void SystemHeaderLayout::addBrackets(System* system, Measure* measure, LayoutContext& ctx)
166 : {
167 99 : if (system->staves().empty()) { // ignore vbox
168 0 : return;
169 : }
170 :
171 99 : size_t nstaves = system->staves().size();
172 :
173 : //---------------------------------------------------
174 : // find x position of staves
175 : // create brackets
176 : //---------------------------------------------------
177 :
178 99 : size_t columns = system->getBracketsColumnsCount();
179 :
180 99 : std::vector<Bracket*> bl;
181 99 : bl.swap(system->brackets());
182 :
183 245 : for (staff_idx_t staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
184 245 : for (size_t i = 0; i < columns; ++i) {
185 177 : for (BracketItem* bi : ctx.dom().brackets(staffIdx)) {
186 78 : if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET) {
187 43 : continue;
188 : }
189 35 : createBracket(system, ctx, bi, i, staffIdx, bl, measure);
190 : }
191 : }
192 146 : if (!system->staff(staffIdx)->show()) {
193 0 : continue;
194 : }
195 : }
196 :
197 : //---------------------------------------------------
198 : // layout brackets
199 : //---------------------------------------------------
200 99 : layoutBracketsVertical(system, ctx);
201 :
202 99 : setBracketsXPosition(system, measure->x());
203 :
204 99 : muse::join(system->brackets(), bl);
205 99 : }
206 :
207 14371 : double SystemHeaderLayout::totalBracketOffset(LayoutContext& ctx)
208 : {
209 14371 : if (ctx.state().totalBracketsWidth() >= 0) {
210 10411 : return ctx.state().totalBracketsWidth();
211 : }
212 :
213 3960 : size_t columns = 0;
214 10222 : for (const Staff* staff : ctx.dom().staves()) {
215 7676 : for (const BracketItem* bi : ctx.dom().brackets(staff->idx())) {
216 1414 : columns = std::max(columns, bi->column() + 1);
217 : }
218 : }
219 :
220 3960 : size_t nstaves = ctx.dom().nstaves();
221 3960 : std::vector < double > bracketWidth(nstaves, 0.0);
222 10222 : for (staff_idx_t staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
223 7676 : for (BracketItem* bi : ctx.dom().brackets(staffIdx)) {
224 1414 : if (bi->bracketType() == BracketType::NO_BRACKET || !bi->visible() || bi->bracketType() == BracketType::GROUP) {
225 540 : continue;
226 : }
227 :
228 : //! This logic is partially copied from System::createBracket.
229 : //! Of course, we don't need to worry about invisible staves,
230 : //! but we do need to worry about brackets that span past the
231 : //! last staff.
232 874 : staff_idx_t firstStaff = staffIdx;
233 874 : staff_idx_t lastStaff = staffIdx + bi->bracketSpan() - 1;
234 874 : if (lastStaff >= nstaves) {
235 124 : lastStaff = nstaves - 1;
236 : }
237 :
238 933 : for (; firstStaff <= lastStaff; ++firstStaff) {
239 907 : if (ctx.dom().staff(firstStaff)->show()) {
240 848 : break;
241 : }
242 : }
243 923 : for (; lastStaff >= firstStaff; --lastStaff) {
244 897 : if (ctx.dom().staff(lastStaff)->show()) {
245 848 : break;
246 : }
247 : }
248 :
249 874 : size_t span = lastStaff - firstStaff + 1;
250 874 : if (span > 1
251 149 : || (bi->bracketSpan() == span)
252 1023 : || (span == 1 && ctx.conf().styleB(Sid::alwaysShowBracketsWhenEmptyStavesAreHidden))) {
253 734 : Bracket* dummyBr = Factory::createBracket(ctx.mutDom().dummyParent(), /*isAccessibleEnabled=*/ false);
254 734 : dummyBr->setBracketItem(bi);
255 734 : dummyBr->setStaffSpan(firstStaff, lastStaff);
256 734 : dummyBr->mutldata()->bracketHeight.set_value(3.5 * dummyBr->spatium() * 2); // default
257 734 : TLayout::layoutBracket(dummyBr, dummyBr->mutldata(), ctx.conf());
258 2393 : for (staff_idx_t stfIdx = firstStaff; stfIdx <= lastStaff; ++stfIdx) {
259 1659 : bracketWidth[stfIdx] += dummyBr->ldata()->bracketWidth();
260 : }
261 734 : delete dummyBr;
262 : }
263 : }
264 : }
265 :
266 3960 : double totalBracketsWidth = 0.0;
267 10222 : for (double w : bracketWidth) {
268 6262 : totalBracketsWidth = std::max(totalBracketsWidth, w);
269 : }
270 3960 : ctx.mutState().setTotalBracketsWidth(totalBracketsWidth);
271 :
272 3960 : return totalBracketsWidth;
273 3960 : }
274 :
275 14458 : void SystemHeaderLayout::setBracketsXPosition(System* system, double xPosition)
276 : {
277 14458 : std::vector<Bracket*> brackets = system->brackets();
278 17247 : for (Bracket* b1 : brackets) {
279 2789 : BracketType bracketType = b1->bracketType();
280 : // For brackets that are drawn, we must correct for half line width
281 2789 : double lineWidthCorrection = 0.0;
282 2789 : if (bracketType == BracketType::NORMAL || bracketType == BracketType::LINE) {
283 865 : lineWidthCorrection = system->style().styleAbsolute(Sid::bracketWidth) / 2;
284 : }
285 : // Compute offset cause by other stacked brackets
286 2789 : double xOffset = 0;
287 7868 : for (const Bracket* b2 : brackets) {
288 5079 : if (!b2->bracketItem()->visible() || b2->bracketType() == BracketType::GROUP) {
289 0 : continue;
290 : }
291 5079 : if (b1->column() > b2->column() && b1->intersects(b2)) {
292 259 : xOffset += b2->ldata()->bracketWidth();
293 : }
294 : }
295 : // Set position
296 2789 : double x = xPosition - xOffset - b1->ldata()->bracketWidth() + lineWidthCorrection;
297 2789 : b1->mutldata()->setPosX(x);
298 : }
299 14458 : }
300 :
301 20468 : void SystemHeaderLayout::layoutBracketsVertical(System* system, LayoutContext& ctx)
302 : {
303 24389 : for (Bracket* b : system->brackets()) {
304 3921 : int staffIdx1 = static_cast<int>(b->firstStaff());
305 3921 : int staffIdx2 = static_cast<int>(b->lastStaff());
306 3921 : double sy = 0; // assume bracket not visible
307 3921 : double ey = 0;
308 : // if start staff not visible, try next staff
309 3921 : while (staffIdx1 <= staffIdx2 && !system->staves().at(staffIdx1)->show()) {
310 0 : ++staffIdx1;
311 : }
312 : // if end staff not visible, try prev staff
313 3921 : while (staffIdx1 <= staffIdx2 && !system->staves().at(staffIdx2)->show()) {
314 0 : --staffIdx2;
315 : }
316 : // if the score doesn't have "alwaysShowBracketsWhenEmptyStavesAreHidden" as true,
317 : // the bracket will be shown IF:
318 : // it spans at least 2 visible staves (staffIdx1 < staffIdx2) OR
319 : // it spans just one visible staff (staffIdx1 == staffIdx2) but it is required to do so
320 : // (the second case happens at least when the bracket is initially dropped)
321 3921 : bool notHidden = ctx.conf().styleB(Sid::alwaysShowBracketsWhenEmptyStavesAreHidden)
322 3921 : ? (staffIdx1 <= staffIdx2) : (staffIdx1 < staffIdx2) || (b->span() == 1 && staffIdx1 == staffIdx2);
323 3921 : if (notHidden) { // set vert. pos. and height to visible spanned staves
324 3921 : sy = system->staves().at(staffIdx1)->bbox().top();
325 3921 : ey = system->staves().at(staffIdx2)->bbox().bottom();
326 : }
327 :
328 3921 : Bracket::LayoutData* bldata = b->mutldata();
329 3921 : bldata->setPosY(sy);
330 3921 : bldata->bracketHeight = ey - sy;
331 3921 : TLayout::layoutBracket(b, bldata, ctx.conf());
332 : }
333 20468 : }
334 :
335 49099 : bool SystemHeaderLayout::stackLabelsVertically(System* system)
336 : {
337 49099 : const MStyle& style = system->style();
338 : // Ignore long/short overrides
339 49099 : bool longName = system->ldata()->useLongNames();
340 : InstrumentNamesAlign align
341 49099 : = style.styleV(longName ? Sid::instrumentNamesAlignLong : Sid::instrumentNamesAlignShort).value<InstrumentNamesAlign>();
342 : return align == InstrumentNamesAlign::CENTER_CENTER
343 49099 : || (align == InstrumentNamesAlign::RIGHT_RIGHT && style.styleB(Sid::instrumentNamesStackVertically));
344 : }
345 :
346 14371 : void SystemHeaderLayout::computeInstrumentNameOffset(System* system, LayoutContext& ctx)
347 : {
348 : // Get standard instrument name distance
349 14371 : double instrumentNameOffset = ctx.conf().styleAbsolute(Sid::instrumentNameOffset);
350 : // Now scale it depending on the text size (which also may not follow staff scaling)
351 14371 : double textSizeScaling = 1.0;
352 14371 : double actualSize = 0.0;
353 14371 : double defaultSize = 0.0;
354 14371 : bool followStaffSize = true;
355 14371 : if (ctx.state().startWithLongNames()) {
356 14371 : actualSize = ctx.conf().styleD(Sid::longInstrumentFontSize);
357 14371 : defaultSize = DefaultStyle::defaultStyle().value(Sid::longInstrumentFontSize).toDouble();
358 14371 : followStaffSize = ctx.conf().styleB(Sid::longInstrumentFontSpatiumDependent);
359 : } else {
360 0 : actualSize = ctx.conf().styleD(Sid::shortInstrumentFontSize);
361 0 : defaultSize = DefaultStyle::defaultStyle().value(Sid::shortInstrumentFontSize).toDouble();
362 0 : followStaffSize = ctx.conf().styleB(Sid::shortInstrumentFontSpatiumDependent);
363 : }
364 14371 : textSizeScaling = actualSize / defaultSize;
365 14371 : if (!followStaffSize) {
366 0 : textSizeScaling *= DefaultStyle::defaultStyle().value(Sid::spatium).toDouble() / ctx.conf().styleD(Sid::spatium);
367 : }
368 14371 : textSizeScaling = std::max(textSizeScaling, 1.0);
369 14371 : instrumentNameOffset *= textSizeScaling;
370 :
371 14371 : system->mutldata()->setInstrumentNameOffset(instrumentNameOffset);
372 14371 : }
373 :
374 14371 : void SystemHeaderLayout::computeGroupBracketsWidths(System* system, LayoutContext& ctx)
375 : {
376 14371 : System::LayoutData* ldata = system->mutldata();
377 14371 : ldata->clearGroupBracketsWidth();
378 :
379 14371 : std::vector<Bracket*> groupBrackets;
380 17152 : for (Bracket* bracket : system->brackets()) {
381 2781 : if (bracket->bracketType() == BracketType::GROUP) {
382 0 : groupBrackets.push_back(bracket);
383 : }
384 : }
385 :
386 14371 : if (groupBrackets.empty()) {
387 14371 : return;
388 : }
389 :
390 0 : std::sort(groupBrackets.begin(), groupBrackets.end(), [](Bracket* b1, Bracket* b2) { return b1->column() < b2->column(); });
391 :
392 0 : bool hangIntoMargin = ctx.conf().styleB(Sid::groupBracketHangTextIntoMargin)
393 0 : && ctx.conf().styleV(Sid::groupBracketTextOrientation).value<Orientation>() == Orientation::VERTICAL;
394 0 : double bracketDistToNames = ctx.conf().styleAbsolute(Sid::groupBracketDistanceToNames);
395 :
396 0 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
397 0 : Bracket* firstBracketOnThisStaff = nullptr;
398 0 : for (Bracket* b : groupBrackets) {
399 0 : if (b->contains(staffIdx)) {
400 0 : firstBracketOnThisStaff = b;
401 0 : break;
402 : }
403 : }
404 :
405 0 : if (!firstBracketOnThisStaff) {
406 0 : continue;
407 : }
408 :
409 0 : std::vector<Bracket*> stack;
410 0 : double bracketWidth = 0.0;
411 0 : computeStackedBracketsWidth(firstBracketOnThisStaff, groupBrackets, bracketWidth, stack);
412 0 : if (hangIntoMargin) {
413 0 : bracketWidth += stack.back()->ldata()->bbox().left();
414 : }
415 :
416 0 : ldata->setGroupBracketsWidthAtStaffIdx(staffIdx, bracketWidth + bracketDistToNames);
417 0 : }
418 :
419 0 : if (!ctx.conf().styleB(Sid::instrumentNamesAlignIncludeGroupBrackets)) {
420 0 : double maxGroupBracketWidth = 0.0;
421 0 : for (const auto& pair : ldata->groupBracketsWidth()) {
422 0 : maxGroupBracketWidth = std::max(maxGroupBracketWidth, pair.second);
423 : }
424 0 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
425 0 : ldata->setGroupBracketsWidthAtStaffIdx(staffIdx, maxGroupBracketWidth);
426 : }
427 : }
428 14371 : }
429 :
430 0 : void SystemHeaderLayout::computeStackedBracketsWidth(Bracket* first, const std::vector<Bracket*>& allGroupBracketsOrderedByColumn,
431 : double& width, std::vector<Bracket*>& stack)
432 : {
433 0 : stack.push_back(first);
434 :
435 0 : width += first->ldata()->bbox().width();
436 0 : double bracketDist = first->style().styleAbsolute(Sid::groupBracketDistanceToGroupBracket);
437 :
438 0 : for (Bracket* b2 : allGroupBracketsOrderedByColumn) {
439 0 : if (muse::contains(stack, b2)) {
440 0 : continue;
441 : }
442 0 : if (b2->column() > first->column() && b2->intersects(first)) {
443 0 : stack.push_back(b2);
444 0 : computeStackedBracketsWidth(b2, allGroupBracketsOrderedByColumn, width, stack);
445 0 : width += bracketDist;
446 : }
447 : }
448 0 : }
449 :
450 15791 : double SystemHeaderLayout::nameWidthIncludingGroupBrackets(InstrumentName* name, System* system)
451 : {
452 15791 : const System::LayoutData* ldata = system->ldata();
453 15791 : double groupBracketsWidth = ldata->groupBracketsWidthAtStaffIdx(name->effectiveStaffIdx());
454 15791 : double result = name->width();
455 15791 : if (!muse::RealIsNull(groupBracketsWidth)) {
456 0 : result += groupBracketsWidth;
457 : }
458 15791 : return result;
459 : }
460 :
461 14371 : void SystemHeaderLayout::computeInstrumentNamesWidth(System* system, LayoutContext& ctx)
462 : {
463 14371 : computeGroupBracketsWidths(system, ctx);
464 :
465 14371 : System::LayoutData* ldata = system->mutldata();
466 14371 : ldata->setFirstColumnWidth(0.0);
467 14371 : ldata->setSecondColumnWidth(0.0);
468 14371 : ldata->setTotalNamesWidth(0.0);
469 :
470 14371 : std::unordered_set<Part*> partsWithIndividualStaffNames;
471 14371 : std::unordered_set<Part*> partsWithGroupNames;
472 :
473 14371 : std::vector<InstrumentName*> groupNames;
474 14371 : std::vector<InstrumentName*> instrumentNames;
475 14371 : std::vector<InstrumentName*> sharedStaveNames;
476 :
477 36159 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
478 21788 : const SysStaff* sysStaff = system->staff(staffIdx);
479 21788 : const Staff* staff = ctx.dom().staff(staffIdx);
480 :
481 21788 : if (InstrumentName* groupName = sysStaff->name(InstrumentNameRole::GROUP)) {
482 134 : if (groupName->instrumentNameType() == InstrumentNameType::LONG && groupName->effectiveStaffIdx() == muse::nidx) {
483 : // NOTE: We can only do this for long-name systems because they don't need to have
484 : // the left barline aligned with the other systems across the page.
485 0 : groupName->mutldata()->setIsSkipDraw(true);
486 0 : continue;
487 : }
488 134 : groupName->mutldata()->setIsSkipDraw(false);
489 :
490 134 : groupNames.push_back(groupName);
491 134 : groupName->mutldata()->setColumn(1);
492 424 : for (staff_idx_t groupIdx = groupName->staffIdx(); groupIdx < groupName->ldata()->endIdxOfGroup(); ++groupIdx) {
493 290 : partsWithGroupNames.insert(ctx.dom().staff(groupIdx)->part());
494 : }
495 134 : ldata->setSecondColumnWidth(std::max(ldata->secondColumnWidth(), nameWidthIncludingGroupBrackets(groupName, system)));
496 134 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), nameWidthIncludingGroupBrackets(groupName, system)));
497 : }
498 :
499 21788 : if (!staff->show()) {
500 : // We know that the staff is hidden in the entire score so safe to skip
501 287 : continue;
502 : }
503 :
504 21501 : if (InstrumentName* name = sysStaff->name(InstrumentNameRole::STAFF)) {
505 140 : if (name->instrumentNameType() == InstrumentNameType::LONG && !sysStaff->show()) {
506 : // NOTE: We can only do this for long-name systems because they don't need to have
507 : // the left barline aligned with the other systems across the page.
508 0 : name->mutldata()->setIsSkipDraw(true);
509 0 : continue;
510 : }
511 140 : name->mutldata()->setIsSkipDraw(false);
512 :
513 140 : name->mutldata()->setColumn(0);
514 140 : partsWithIndividualStaffNames.insert(ctx.dom().staff(staffIdx)->part());
515 140 : ldata->setFirstColumnWidth(std::max(ldata->firstColumnWidth(), name->width()));
516 140 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), nameWidthIncludingGroupBrackets(name, system)));
517 : }
518 : }
519 :
520 36159 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
521 21788 : const SysStaff* sysStaff = system->staff(staffIdx);
522 21788 : const Staff* staff = ctx.dom().staff(staffIdx);
523 21788 : Part* part = staff->part();
524 :
525 21788 : if (!part->show() || part->visibleStavesCount() == 0) {
526 : // We know that the part is hidden in the entire score so safe to skip
527 14092 : continue;
528 : }
529 :
530 21509 : InstrumentName* instrName = sysStaff->name(InstrumentNameRole::PART);
531 21509 : if (!instrName) {
532 13808 : instrName = sysStaff->name(InstrumentNameRole::SHARED_STAFF);
533 : }
534 21509 : if (!instrName) {
535 13808 : continue;
536 : }
537 :
538 7701 : if (instrName->instrumentNameType() == InstrumentNameType::LONG && instrName->effectiveStaffIdx() == muse::nidx) {
539 : // NOTE: We can only do this for long-name systems because they don't need to have
540 : // the left barline aligned with the other systems across the page.
541 5 : instrName->mutldata()->setIsSkipDraw(true);
542 5 : continue;
543 : }
544 7696 : instrName->mutldata()->setIsSkipDraw(false);
545 :
546 7696 : if (instrName->instrumentNameRole() == InstrumentNameRole::PART) {
547 7696 : instrumentNames.push_back(instrName);
548 : } else {
549 0 : sharedStaveNames.push_back(instrName);
550 : }
551 :
552 7696 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), nameWidthIncludingGroupBrackets(instrName, system)));
553 :
554 7696 : if (partsWithGroupNames.count(part)) {
555 156 : instrName->mutldata()->setColumn(0);
556 156 : ldata->setFirstColumnWidth(std::max(ldata->firstColumnWidth(), instrName->width()));
557 : } else {
558 7540 : instrName->mutldata()->setColumn(1);
559 7540 : ldata->setSecondColumnWidth(std::max(ldata->secondColumnWidth(), nameWidthIncludingGroupBrackets(instrName, system)));
560 : }
561 : }
562 :
563 147 : auto sumWidth = [&](InstrumentName* outerName, InstrumentName* innerName) {
564 147 : AlignH staffNameAlign = innerName->position();
565 147 : if (staffNameAlign == AlignH::LEFT || staffNameAlign == AlignH::JUSTIFY) {
566 0 : return ldata->firstColumnWidth() + nameWidthIncludingGroupBrackets(outerName, system) + ldata->instrumentNameOffset();
567 147 : } else if (staffNameAlign == AlignH::HCENTER) {
568 0 : double sumWidth = innerName->width() + nameWidthIncludingGroupBrackets(outerName, system) + ldata->instrumentNameOffset();
569 0 : double move = 0.5 * (ldata->firstColumnWidth() - innerName->width());
570 0 : return sumWidth + move;
571 : } else {
572 147 : return innerName->width() + ldata->instrumentNameOffset() + nameWidthIncludingGroupBrackets(outerName, system);
573 : }
574 14371 : };
575 :
576 14371 : for (InstrumentName* sharedStaffName : sharedStaveNames) {
577 0 : if (sharedStaffName->ldata()->column() == 0) {
578 0 : continue;
579 : }
580 0 : Part* part = ctx.dom().staff(sharedStaffName->staffIdx())->part();
581 0 : for (staff_idx_t idx : part->staveIdxList()) {
582 0 : if (InstrumentName* n = system->staff(idx)->name(InstrumentNameRole::STAFF); n && !n->ldata()->isSkipDraw()) {
583 0 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(sharedStaffName, n)));
584 : }
585 0 : }
586 : }
587 :
588 14371 : if (stackLabelsVertically(system)) {
589 0 : return;
590 : }
591 :
592 14505 : for (InstrumentName* groupName : groupNames) {
593 134 : staff_idx_t startStaff = groupName->staffIdx();
594 134 : staff_idx_t endStaff = groupName->ldata()->endIdxOfGroup();
595 340 : if (groupName->instrumentNameType() == InstrumentNameType::LONG
596 134 : && system->visiblePartsOfGroup(startStaff, endStaff).size() % 2 == 0) {
597 72 : continue;
598 : }
599 196 : for (staff_idx_t idx = startStaff; idx < endStaff; ++idx) {
600 134 : if (InstrumentName* n = system->staff(idx)->name(InstrumentNameRole::STAFF); n && !n->ldata()->isSkipDraw()) {
601 0 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(groupName, n)));
602 : }
603 134 : if (InstrumentName* n = system->staff(idx)->name(InstrumentNameRole::PART); n && !n->ldata()->isSkipDraw()) {
604 72 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(groupName, n)));
605 : }
606 : }
607 : }
608 :
609 22067 : for (InstrumentName* instrName : instrumentNames) {
610 7696 : if (instrName->ldata()->column() == 0) {
611 156 : continue;
612 : }
613 7540 : Part* part = ctx.dom().staff(instrName->staffIdx())->part();
614 7540 : if (instrName->instrumentNameType() == InstrumentNameType::LONG && system->visibleStavesOfPart(part).size() % 2 == 0) {
615 558 : continue;
616 : }
617 14257 : for (staff_idx_t idx : part->staveIdxList()) {
618 7275 : if (InstrumentName* n = system->staff(idx)->name(InstrumentNameRole::STAFF); n && !n->ldata()->isSkipDraw()) {
619 75 : ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(instrName, n)));
620 : }
621 6982 : }
622 : }
623 14371 : }
624 :
625 20369 : void SystemHeaderLayout::setInstrumentNamesVerticalPos(System* system, LayoutContext& ctx)
626 : {
627 20369 : std::set<const Part*> partsWithIndividualStaffNames;
628 :
629 51336 : for (staff_idx_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
630 30967 : SysStaff* s = system->staff(staffIdx);
631 92901 : for (InstrumentNameRole role : { InstrumentNameRole::STAFF, InstrumentNameRole::SHARED_STAFF }) {
632 61934 : if (InstrumentName* individualName = s->name(role)) {
633 186 : const RectF& staffBBox = s->bbox();
634 186 : const RectF& nameBBox = individualName->ldata()->bbox();
635 186 : individualName->mutldata()->setPosY(0.5 * (staffBBox.top() + staffBBox.bottom() - nameBBox.bottom() - nameBBox.top()));
636 186 : partsWithIndividualStaffNames.insert(ctx.dom().staff(staffIdx)->part());
637 : }
638 : }
639 : }
640 :
641 20369 : bool stackVertically = stackLabelsVertically(system);
642 :
643 20369 : staff_idx_t staffIdx = 0;
644 47727 : for (const Part* p : ctx.dom().parts()) {
645 27358 : size_t nstaves = p->nstaves();
646 :
647 27358 : SysStaff* s = system->staff(staffIdx);
648 :
649 27358 : InstrumentName* t = s->name(InstrumentNameRole::PART);
650 27358 : if (!t || t->effectiveStaffIdx() == muse::nidx) {
651 15910 : staffIdx += nstaves;
652 27220 : continue;
653 : }
654 :
655 11448 : const RectF& bbox = t->ldata()->bbox();
656 11448 : double yCenter = 0.5 * (bbox.bottom() + bbox.top());
657 :
658 11448 : std::vector<staff_idx_t> visibleStavesOfPart = system->visibleStavesOfPart(p);
659 11448 : size_t visibleStavesCount = visibleStavesOfPart.size();
660 11448 : DO_ASSERT(visibleStavesCount > 0);
661 :
662 11448 : double y1 = 0;
663 11448 : double y2 = 0;
664 :
665 11448 : if (!muse::contains(partsWithIndividualStaffNames, p)) {
666 11286 : SysStaff* topSt = system->staff(visibleStavesOfPart.front());
667 11286 : SysStaff* bottomSt = system->staff(visibleStavesOfPart.back());
668 11286 : y1 = topSt->bbox().top();
669 11286 : y2 = bottomSt->bbox().bottom();
670 11286 : t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
671 :
672 11286 : staffIdx += nstaves;
673 11286 : continue;
674 11286 : }
675 :
676 162 : if (visibleStavesCount % 2 == 0) {
677 24 : SysStaff* staffAboveMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2 - 1]);
678 24 : SysStaff* staffBelowMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
679 24 : y1 = staffAboveMid->bbox().top();
680 24 : y2 = staffBelowMid->bbox().bottom();
681 24 : t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
682 :
683 24 : staffIdx += nstaves;
684 24 : continue;
685 24 : }
686 :
687 138 : SysStaff* midStaff = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
688 138 : y1 = midStaff->bbox().top();
689 138 : y2 = midStaff->bbox().bottom();
690 138 : if (InstrumentName* staffName = midStaff->name(InstrumentNameRole::STAFF)) {
691 138 : if (stackVertically || t->ldata()->column() == 0) {
692 0 : double lineSpacing = t->lineSpacing();
693 0 : double instrNameBottom = t->ldata()->blocks.back().y();
694 0 : double centerY = 0.5 * (midStaff->bbox().top() + midStaff->bbox().bottom());
695 0 : t->mutldata()->setPosY(centerY - instrNameBottom - 0.25 * lineSpacing);
696 0 : double staffNameTop = staffName->ldata()->blocks.front().y();
697 0 : staffName->mutldata()->setPosY(centerY - staffNameTop + 0.75 * lineSpacing);
698 138 : } else if (staffName->ldata()->rows() == 1 && t->ldata()->rows() == 1) {
699 138 : t->mutldata()->setPosY(staffName->y());
700 : } else {
701 0 : t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
702 : }
703 : }
704 :
705 138 : staffIdx += nstaves;
706 11448 : }
707 :
708 51336 : for (staff_idx_t idx = 0; idx < system->staves().size(); ++idx) {
709 30967 : InstrumentName* groupName = system->staff(idx)->name(InstrumentNameRole::GROUP);
710 30967 : if (!groupName || groupName->effectiveStaffIdx() == muse::nidx) {
711 30967 : continue;
712 : }
713 :
714 201 : const RectF& bbox = groupName->ldata()->bbox();
715 201 : double yCenter = 0.5 * (bbox.bottom() + bbox.top());
716 :
717 201 : std::vector<Part*> visibleParts = system->visiblePartsOfGroup(idx, groupName->ldata()->endIdxOfGroup());
718 201 : size_t visiblePartsCount = visibleParts.size();
719 201 : DO_ASSERT(visiblePartsCount > 0);
720 :
721 201 : double y1 = 0;
722 201 : double y2 = 0;
723 :
724 201 : if (visiblePartsCount % 2 == 0) {
725 186 : Part* partAboveMid = visibleParts[visiblePartsCount / 2 - 1];
726 186 : staff_idx_t staffIdxAboveMid = system->lastVisibleSysStaffOfPart(partAboveMid);
727 186 : DO_ASSERT(staffIdxAboveMid != muse::nidx);
728 186 : SysStaff* staffAboveMid = system->staff(staffIdxAboveMid);
729 :
730 186 : Part* partBelowMid = visibleParts[visiblePartsCount / 2];
731 186 : staff_idx_t staffIdxBelowMid = system->firstVisibleSysStaffOfPart(partBelowMid);
732 186 : DO_ASSERT(staffIdxBelowMid != muse::nidx);
733 186 : SysStaff* staffBelowMid = system->staff(staffIdxBelowMid);
734 :
735 186 : y1 = staffAboveMid->bbox().top();
736 186 : y2 = staffBelowMid->bbox().bottom();
737 186 : groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
738 :
739 186 : continue;
740 186 : }
741 :
742 15 : Part* midPart = visibleParts[visiblePartsCount / 2];
743 15 : InstrumentName* instrName = system->staff(*midPart->staveIdxList().begin())->name(InstrumentNameRole::PART);
744 15 : std::vector<staff_idx_t> visibleStaves = system->visibleStavesOfPart(midPart);
745 15 : size_t visibleStavesCount = visibleStaves.size();
746 :
747 15 : if (visibleStavesCount % 2) {
748 15 : SysStaff* midStaff = system->staff(visibleStaves[visibleStavesCount / 2]);
749 15 : y1 = midStaff->bbox().top();
750 15 : y2 = midStaff->bbox().bottom();
751 : } else {
752 0 : SysStaff* staffAboveMid = system->staff(visibleStaves[visibleStavesCount / 2 - 1]);
753 0 : SysStaff* staffBelowMid = system->staff(visibleStaves[visibleStavesCount / 2]);
754 0 : y1 = staffAboveMid->bbox().top();
755 0 : y2 = staffBelowMid->bbox().bottom();
756 : }
757 :
758 15 : if (instrName) {
759 0 : if (stackVertically) {
760 0 : double lineSpacing = groupName->lineSpacing();
761 0 : double groupNameBottom = groupName->ldata()->blocks.back().y();
762 0 : if (visibleStavesCount % 2 && system->staff(visibleStaves[visibleStavesCount / 2])->name(InstrumentNameRole::STAFF)) {
763 0 : groupName->mutldata()->setPosY(instrName->y() - groupNameBottom - lineSpacing);
764 : } else {
765 0 : double centerY = 0.5 * (y1 + y2);
766 0 : groupName->mutldata()->setPosY(centerY - groupNameBottom - 0.25 * lineSpacing);
767 0 : double instrNameTop = instrName->ldata()->blocks.front().y();
768 0 : instrName->mutldata()->setPosY(centerY - instrNameTop + 0.75 * lineSpacing);
769 : }
770 0 : } else if (instrName->ldata()->rows() == 1 && groupName->ldata()->rows() == 1) {
771 0 : groupName->mutldata()->setPosY(instrName->y());
772 : } else {
773 0 : groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
774 : }
775 :
776 0 : continue;
777 0 : }
778 :
779 15 : if (visibleStavesCount % 2 == 0) {
780 0 : groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
781 0 : continue;
782 : }
783 :
784 15 : SysStaff* midStaff = system->staff(visibleStaves[visibleStavesCount / 2]);
785 15 : InstrumentName* staffName = midStaff->name(InstrumentNameRole::STAFF);
786 15 : if (!staffName) {
787 15 : groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
788 15 : continue;
789 : }
790 :
791 0 : if (stackVertically) {
792 0 : double lineSpacing = groupName->lineSpacing();
793 0 : double groupNameBottom = groupName->ldata()->blocks.back().y();
794 0 : double centerY = 0.5 * (y1 + y2);
795 0 : groupName->mutldata()->setPosY(centerY - groupNameBottom - 0.25 * lineSpacing);
796 0 : double staffNameTop = staffName->ldata()->blocks.front().y();
797 0 : staffName->mutldata()->setPosY(centerY - staffNameTop + 0.75 * lineSpacing);
798 0 : } else if (staffName->ldata()->rows() == 1 && groupName->ldata()->rows() == 1) {
799 0 : groupName->mutldata()->setPosY(staffName->y());
800 : } else {
801 0 : groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
802 : }
803 216 : }
804 20369 : }
805 :
806 14359 : void SystemHeaderLayout::setInstrumentNamesHorizontalPos(System* system)
807 : {
808 : //---------------------------------------------------
809 : // layout instrument names x position
810 : // at this point it is not clear which staves will
811 : // be hidden, so layout all instrument names
812 : //---------------------------------------------------
813 :
814 14359 : System::LayoutData* ldata = system->mutldata();
815 14359 : double totalNamesWidth = ldata->totalNamesWidth();
816 14359 : double firstColumnWidth = ldata->firstColumnWidth();
817 : // Ignore long/short overrides
818 28718 : InstrumentNamesAlign align = system->style().styleV(
819 28718 : ldata->useLongNames() ? Sid::instrumentNamesAlignLong : Sid::instrumentNamesAlignShort).value<InstrumentNamesAlign>();
820 :
821 278 : auto placeFirstColumnName = [&](InstrumentName* name) {
822 278 : const RectF& bbox = name->ldata()->bbox();
823 278 : if (align == InstrumentNamesAlign::CENTER_CENTER) {
824 0 : double groupBracketsWidth = ldata->groupBracketsWidthAtStaffIdx(name->effectiveStaffIdx());
825 0 : name->mutldata()->setPosX(0.5 * totalNamesWidth - 0.5 * (bbox.right() + bbox.left()) + groupBracketsWidth);
826 : } else {
827 278 : switch (name->position()) {
828 0 : case AlignH::JUSTIFY:
829 : case AlignH::LEFT:
830 0 : name->mutldata()->setPosX(totalNamesWidth - firstColumnWidth - bbox.left());
831 0 : break;
832 0 : case AlignH::HCENTER:
833 0 : name->mutldata()->setPosX(totalNamesWidth - 0.5 * firstColumnWidth - 0.5 * (bbox.right() + bbox.left()));
834 0 : break;
835 278 : case AlignH::RIGHT:
836 278 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
837 : }
838 : }
839 14637 : };
840 :
841 36089 : for (const SysStaff* s : system->staves()) {
842 86920 : for (InstrumentNameRole role : { InstrumentNameRole::STAFF,
843 : InstrumentNameRole::SHARED_STAFF,
844 108650 : InstrumentNameRole::PART }) {
845 65190 : InstrumentName* n = s->name(role);
846 65190 : if (n && n->ldata()->column() == 0 && n->effectiveStaffIdx() != muse::nidx) {
847 278 : placeFirstColumnName(n);
848 : }
849 : }
850 : }
851 :
852 14359 : bool stackVertically = stackLabelsVertically(system);
853 :
854 7647 : auto placeSecondColumnName = [&](InstrumentName* name, staff_idx_t staffIdx) {
855 7647 : bool doStackVertically = stackVertically && name->instrumentNameRole() != InstrumentNameRole::SHARED_STAFF;
856 7647 : const RectF& bbox = name->ldata()->bbox();
857 7647 : double groupBracketsWidth = ldata->groupBracketsWidthAtStaffIdx(name->effectiveStaffIdx());
858 :
859 7647 : if (align == InstrumentNamesAlign::LEFT_RIGHT) {
860 0 : name->mutldata()->setPosX(groupBracketsWidth - bbox.left());
861 7637 : return;
862 : }
863 :
864 7647 : if (align == InstrumentNamesAlign::CENTER_CENTER) {
865 0 : name->mutldata()->setPosX(0.5 * totalNamesWidth - 0.5 * (bbox.right() + bbox.left()) + 0.5 * groupBracketsWidth);
866 0 : return;
867 : }
868 :
869 7647 : if (align == InstrumentNamesAlign::CENTER_RIGHT) {
870 0 : name->mutldata()->setPosX(0.5 * ldata->secondColumnWidth() - 0.5 * (bbox.right() + bbox.left()) + 0.5 * groupBracketsWidth);
871 0 : return;
872 : }
873 :
874 7647 : if (doStackVertically) {
875 0 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
876 0 : return;
877 : }
878 :
879 7647 : if (name->instrumentNameRole() == InstrumentNameRole::SHARED_STAFF) {
880 0 : if (InstrumentName* staffName = system->staff(name->staffIdx())->name(InstrumentNameRole::STAFF)) {
881 0 : name->mutldata()->setPosX(
882 0 : staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
883 : } else {
884 0 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
885 : }
886 :
887 0 : return;
888 : }
889 :
890 7647 : if (name->instrumentNameRole() == InstrumentNameRole::PART) {
891 7513 : const Part* p = system->score()->staff(staffIdx)->part();
892 7513 : std::vector<staff_idx_t> visibleStavesForPart = system->visibleStavesOfPart(p);
893 7513 : size_t visibleStaveCount = visibleStavesForPart.size();
894 7513 : if (visibleStaveCount % 2 == 0) {
895 773 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
896 773 : return;
897 : }
898 :
899 6740 : staff_idx_t centerStaff = visibleStavesForPart[visibleStaveCount / 2];
900 6740 : if (InstrumentName* staffName = system->staff(centerStaff)->name(InstrumentNameRole::STAFF)) {
901 100 : name->mutldata()->setPosX(
902 50 : staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
903 : } else {
904 6690 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
905 : }
906 :
907 6740 : return;
908 7513 : }
909 :
910 134 : std::vector<Part*> visiblePartsOfGroup = system->visiblePartsOfGroup(staffIdx, name->ldata()->endIdxOfGroup());
911 134 : size_t visiblePartsCount = visiblePartsOfGroup.size();
912 134 : if (visiblePartsCount % 2 == 0) {
913 124 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
914 124 : return;
915 : }
916 :
917 10 : Part* centerPart = visiblePartsOfGroup[visiblePartsCount / 2];
918 10 : if (InstrumentName* instrName = system->staff(*centerPart->staveIdxList().begin())->name(InstrumentNameRole::PART)) {
919 0 : name->mutldata()->setPosX(
920 0 : instrName->x() + instrName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
921 0 : return;
922 : }
923 :
924 10 : std::vector<staff_idx_t> visibleStavesOfPart = system->visibleStavesOfPart(centerPart);
925 10 : size_t visibleStavesCount = visibleStavesOfPart.size();
926 10 : if (visibleStavesCount % 2 == 0) {
927 0 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
928 0 : return;
929 : }
930 :
931 10 : staff_idx_t centerStaff = visibleStavesOfPart[visibleStavesCount / 2];
932 10 : if (InstrumentName* staffName = system->staff(centerStaff)->name(InstrumentNameRole::STAFF)) {
933 0 : name->mutldata()->setPosX(
934 0 : staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
935 : } else {
936 10 : name->mutldata()->setPosX(totalNamesWidth - bbox.right());
937 : }
938 134 : };
939 :
940 36089 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
941 21730 : const SysStaff* s = system->staff(staffIdx);
942 21730 : InstrumentName* instrName = s->name(InstrumentNameRole::PART);
943 21730 : if (!instrName) {
944 14044 : instrName = s->name(InstrumentNameRole::SHARED_STAFF);
945 : }
946 21730 : if (!instrName) {
947 14044 : continue;
948 : }
949 7686 : if (instrName && instrName->effectiveStaffIdx() != muse::nidx && instrName->ldata()->column() > 0) {
950 7513 : placeSecondColumnName(instrName, staffIdx);
951 : }
952 : }
953 :
954 36089 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
955 21730 : const SysStaff* s = system->staff(staffIdx);
956 21730 : InstrumentName* groupName = s->name(InstrumentNameRole::GROUP);
957 21730 : if (groupName && groupName->effectiveStaffIdx() != muse::nidx) {
958 134 : placeSecondColumnName(groupName, staffIdx);
959 : }
960 : }
961 14359 : }
962 :
963 14359 : void SystemHeaderLayout::setGroupBracketsHorizontalPos(System* system)
964 : {
965 14359 : std::vector<Bracket*> groupBrackets;
966 17113 : for (Bracket* b : system->brackets()) {
967 2754 : if (b->bracketType() == BracketType::GROUP) {
968 0 : groupBrackets.push_back(b);
969 : }
970 : }
971 14359 : std::sort(groupBrackets.begin(), groupBrackets.end(), [](Bracket* b1, Bracket* b2) { return b1->column() > b2->column(); });
972 :
973 14359 : const MStyle& style = system->style();
974 14359 : bool intoMargin = style.styleB(Sid::groupBracketHangTextIntoMargin)
975 14359 : && style.styleV(Sid::groupBracketTextOrientation).value<Orientation>() == Orientation::VERTICAL;
976 14359 : double bracketsDist = style.styleAbsolute(Sid::groupBracketDistanceToGroupBracket);
977 :
978 14359 : for (Bracket* b : groupBrackets) {
979 0 : b->mutldata()->setPosX(intoMargin ? 0.0 : -b->mutldata()->bbox().left());
980 0 : for (Bracket* bb : groupBrackets) {
981 0 : if (bb == b) {
982 0 : break;
983 : }
984 0 : if (bb->column() > b->column() && bb->intersects(b)) {
985 0 : b->mutldata()->setPosX(bb->x() + bb->ldata()->bbox().right() - b->ldata()->bbox().left() + bracketsDist);
986 : }
987 : }
988 : }
989 14359 : }
990 :
991 64975 : InstrumentName* SystemHeaderLayout::updateName(System* system, staff_idx_t staffIdx, LayoutContext& ctx, const String& name,
992 : InstrumentNameType type, InstrumentNameRole role)
993 : {
994 64975 : SysStaff* sysStaff = system->staff(staffIdx);
995 64975 : InstrumentName* iname = sysStaff->name(role);
996 :
997 64975 : if (name.empty()) {
998 57000 : if (iname) {
999 0 : ctx.mutDom().removeElement(iname);
1000 : }
1001 57000 : return nullptr;
1002 : }
1003 :
1004 7975 : if (!iname) {
1005 3771 : iname = new InstrumentName(system);
1006 3771 : iname->setGenerated(true);
1007 3771 : iname->setSysStaff(sysStaff);
1008 3771 : iname->setTrack(staffIdx * VOICES);
1009 3771 : iname->setInstrumentNameRole(role);
1010 3771 : ctx.mutDom().addElement(iname);
1011 : }
1012 :
1013 7975 : iname->setInstrumentNameType(type);
1014 7975 : iname->setAlign(Align(iname->align().horizontal, AlignV::BASELINE));
1015 7975 : iname->setXmlText(name);
1016 :
1017 7975 : iname->mutldata()->setColumn(0); // Reset here, will be computed later
1018 7975 : TLayout::layoutInstrumentName(iname, iname->mutldata());
1019 :
1020 7975 : return iname;
1021 : }
1022 :
1023 8614 : String SystemHeaderLayout::formattedInstrumentName(System* system, Part* part, const Fraction& tick, bool longNames)
1024 : {
1025 8614 : Instrument* instr = part->instrument(tick);
1026 8614 : const InstrumentLabel& label = instr->instrumentLabel();
1027 :
1028 8614 : const MStyle& style = system->style();
1029 8614 : bool trailingDot = style.styleB(Sid::instrumentNumeralsTrailingDotSingle);
1030 :
1031 8614 : if (muse::contains(system->ldata()->partsWithGroupName(), part)) {
1032 290 : if (label.useCustomIndividualName()) {
1033 0 : return longNames ? label.customNameLongIndividual() : label.customNameShortIndividual();
1034 : }
1035 :
1036 290 : int number = part->number(tick);
1037 290 : String result = number > 0 ? String::number(number) : String();
1038 290 : if (!result.isEmpty() && trailingDot) {
1039 0 : result += '.';
1040 : }
1041 :
1042 290 : return result;
1043 290 : }
1044 :
1045 8324 : if (label.useCustomName()) {
1046 0 : return longNames ? label.customNameLong() : label.customNameShort();
1047 : }
1048 :
1049 8324 : String instrName = longNames ? instr->longName() : instr->shortName();
1050 :
1051 8324 : bool showTranspo = longNames ? label.showTranspositionLong() : label.showTranspositionShort();
1052 8324 : showTranspo &= style.styleB(longNames ? Sid::instrumentNamesShowTranspositionLong : Sid::instrumentNamesShowTranspositionShort);
1053 8324 : String transposition = showTranspo ? instr->transposition() : String();
1054 :
1055 8324 : bool showNumber = longNames ? label.showNumberLong() : label.showNumberShort();
1056 8324 : String number = instr->number() > 0 && showNumber ? String::number(instr->number()) : String();
1057 :
1058 : InstrumentNamesFormat nameFormat
1059 8324 : = style.styleV(longNames ? Sid::instrumentNamesFormatLong : Sid::instrumentNamesFormatShort).value<InstrumentNamesFormat>();
1060 :
1061 8324 : bool addTrailingDot = trailingDot && (transposition.empty() || nameFormat == InstrumentNamesFormat::NAME_IN_TRANSP_NUM
1062 0 : || nameFormat == InstrumentNamesFormat::TRANSP_NAME_NUM);
1063 8324 : if (addTrailingDot) {
1064 0 : number += '.';
1065 : }
1066 :
1067 8324 : if (transposition.empty()) {
1068 8324 : String result = instrName;
1069 8324 : if (!number.empty()) {
1070 0 : result += u" " + number;
1071 : }
1072 8324 : return result;
1073 8324 : }
1074 :
1075 : //: For instrument transposition, e.g. Horn in F
1076 0 : String in = TranslatableString("notation", "in").translated();
1077 :
1078 0 : switch (nameFormat) {
1079 0 : case InstrumentNamesFormat::NAME_IN_TRANSP_NUM:
1080 0 : return instrName + u" " + in + u" " + transposition + (number.empty() ? String() : u" " + number);
1081 0 : case InstrumentNamesFormat::NAME_NUM_IN_TRANSP:
1082 0 : return instrName + (number.empty() ? String() : u" " + number) + u" " + in + u" " + transposition;
1083 0 : case InstrumentNamesFormat::TRANSP_NAME_NUM:
1084 0 : return transposition + u" " + instrName + (number.empty() ? String() : u" " + number);
1085 0 : default:
1086 : {
1087 0 : String result = style.styleSt(longNames ? Sid::instrumentNamesCustomFormatLong : Sid::instrumentNamesCustomFormatShort);
1088 0 : return resolveTokens(result, instrName, transposition, number);
1089 0 : }
1090 : }
1091 8324 : }
1092 :
1093 375 : String SystemHeaderLayout::formattedGroupName(System* system, Part* part, const Fraction& tick, bool longNames)
1094 : {
1095 375 : const MStyle& style = system->style();
1096 :
1097 375 : Instrument* instr = part->instrument(tick);
1098 375 : const InstrumentLabel& label = instr->instrumentLabel();
1099 :
1100 375 : if (label.useCustomGroupName()) {
1101 0 : return longNames ? label.customNameLongGroup() : label.customNameShortGroup();
1102 : }
1103 :
1104 375 : String instrName = longNames ? instr->longName() : instr->shortName();
1105 :
1106 375 : bool showTranspo = longNames ? label.showTranspositionLong() : label.showTranspositionShort();
1107 375 : showTranspo &= style.styleB(longNames ? Sid::instrumentNamesShowTranspositionLong : Sid::instrumentNamesShowTranspositionShort);
1108 :
1109 375 : String transposition = showTranspo ? instr->transposition() : String();
1110 :
1111 375 : if (transposition.empty()) {
1112 375 : return instrName;
1113 : }
1114 :
1115 : InstrumentNamesFormat nameFormat
1116 0 : = style.styleV(longNames ? Sid::instrumentNamesFormatLong : Sid::instrumentNamesFormatShort).value<InstrumentNamesFormat>();
1117 :
1118 : //: For instrument transposition, e.g. Horn in F
1119 0 : String in = TranslatableString("notation", "in").translated();
1120 :
1121 0 : switch (nameFormat) {
1122 0 : case InstrumentNamesFormat::NAME_IN_TRANSP_NUM:
1123 0 : return instrName + u" " + in + u" " + transposition;
1124 0 : case InstrumentNamesFormat::NAME_NUM_IN_TRANSP:
1125 0 : return instrName + u" " + in + u" " + transposition;
1126 0 : case InstrumentNamesFormat::TRANSP_NAME_NUM:
1127 0 : return transposition + u" " + instrName;
1128 0 : default:
1129 : {
1130 0 : String result = style.styleSt(longNames ? Sid::instrumentNamesCustomFormatLong : Sid::instrumentNamesCustomFormatShort);
1131 0 : return resolveTokens(result, instrName, transposition, /*number*/ String());
1132 0 : }
1133 : }
1134 375 : }
1135 :
1136 237 : String SystemHeaderLayout::formattedSharedStaffLabel(staff_idx_t staffIdx, const SharedTrackMap& trackMap,
1137 : const std::vector<Part*>& originParts)
1138 : {
1139 237 : if (originParts.empty()) {
1140 0 : return String();
1141 : }
1142 :
1143 237 : Score* score = originParts.front()->score();
1144 237 : const MStyle& style = score->style();
1145 237 : bool trailingDotSingle = style.styleB(Sid::instrumentNumeralsTrailingDotSingle);
1146 237 : bool trailingDotMultiple = style.styleB(Sid::instrumentNumeralsTrailingDotMultiple);
1147 237 : SharedLabelOrientation orientation = style.styleV(Sid::instrumentNumeralsOrientation).value<SharedLabelOrientation>();
1148 237 : int horizontalLimit = style.styleI(Sid::instrumentNumeralsHorizontalThreshold);
1149 237 : int verticalLimit = style.styleI(Sid::instrumentNumeralsVerticalThreshold);
1150 237 : int hyphenLimit = style.styleB(Sid::instrumentNumeralsHyphenEnable) ? style.styleI(Sid::instrumentNumeralsHyphenThreshold) : INT_MAX;
1151 :
1152 237 : std::vector<const Instrument*> instrumentsMappedToFirstVoice;
1153 237 : std::vector<const Instrument*> instrumentsMappedToSecondVoice;
1154 702 : for (auto [originTrack, sharedTrack] : trackMap) {
1155 465 : if (track2staff(sharedTrack) == staffIdx) {
1156 237 : if (track2voice(sharedTrack) == 0) {
1157 232 : instrumentsMappedToFirstVoice.push_back(score->staff(track2staff(originTrack))->part()->instrument());
1158 : } else {
1159 5 : instrumentsMappedToSecondVoice.push_back(score->staff(track2staff(originTrack))->part()->instrument());
1160 : }
1161 : }
1162 : }
1163 :
1164 237 : size_t totInstrumentCount = instrumentsMappedToFirstVoice.size() + instrumentsMappedToSecondVoice.size();
1165 :
1166 : SharedLabelOrientation actualOrientation;
1167 237 : switch (orientation) {
1168 0 : case SharedLabelOrientation::VOICE:
1169 0 : actualOrientation = orientation;
1170 0 : break;
1171 237 : case SharedLabelOrientation::HORIZONTAL:
1172 237 : actualOrientation = totInstrumentCount > size_t(horizontalLimit) ? SharedLabelOrientation::VOICE : orientation;
1173 237 : break;
1174 0 : case SharedLabelOrientation::VERTICAL:
1175 0 : actualOrientation = totInstrumentCount > size_t(verticalLimit) ? SharedLabelOrientation::VOICE : orientation;
1176 0 : break;
1177 0 : default: // should not happen, but don't leave actualOrientation uninitialized
1178 0 : ASSERT_X("Unexpected SharedLabelOrientation value: " << static_cast<int>(orientation));
1179 : actualOrientation = SharedLabelOrientation::VOICE;
1180 : break;
1181 : }
1182 :
1183 237 : if (actualOrientation == SharedLabelOrientation::HORIZONTAL || actualOrientation == SharedLabelOrientation::VERTICAL) {
1184 : // Put all instruments in single vector
1185 237 : instrumentsMappedToFirstVoice.insert(instrumentsMappedToFirstVoice.end(),
1186 : instrumentsMappedToSecondVoice.begin(), instrumentsMappedToSecondVoice.end());
1187 237 : std::sort(instrumentsMappedToFirstVoice.begin(), instrumentsMappedToFirstVoice.end(),
1188 194 : [](const Instrument* a, const Instrument* b) { return a->number() < b->number(); });
1189 :
1190 237 : String result;
1191 :
1192 237 : if (actualOrientation == SharedLabelOrientation::HORIZONTAL) {
1193 237 : result = formatSharedVoiceLabel(instrumentsMappedToFirstVoice, trailingDotSingle, trailingDotMultiple, hyphenLimit);
1194 : } else {
1195 0 : result = formatVerticalSharedLabel(instrumentsMappedToFirstVoice, trailingDotSingle);
1196 : }
1197 :
1198 237 : return result;
1199 237 : }
1200 :
1201 0 : String result = formatSharedVoiceLabel(instrumentsMappedToFirstVoice, trailingDotSingle, trailingDotMultiple, hyphenLimit);
1202 :
1203 0 : if (instrumentsMappedToSecondVoice.empty()) {
1204 0 : return result;
1205 : }
1206 :
1207 0 : result += '\n' + formatSharedVoiceLabel(instrumentsMappedToSecondVoice, trailingDotSingle, trailingDotMultiple, hyphenLimit);
1208 :
1209 0 : return result;
1210 237 : }
1211 :
1212 0 : String SystemHeaderLayout::formatVerticalSharedLabel(const std::vector<const Instrument*>& instruments, bool trailingDotSingle)
1213 : {
1214 0 : String result;
1215 :
1216 0 : for (const Instrument* instr : instruments) {
1217 0 : if (!result.empty()) {
1218 0 : result += '\n';
1219 : }
1220 :
1221 0 : result += String::number(instr->number());
1222 :
1223 0 : if (trailingDotSingle) {
1224 0 : result += '.';
1225 : }
1226 : }
1227 :
1228 0 : return result;
1229 0 : }
1230 :
1231 239 : String SystemHeaderLayout::formatSharedVoiceLabel(const std::vector<const Instrument*>& instruments, bool trailingDotSingle,
1232 : bool trailingDotMultiple, int hyphenLimit)
1233 : {
1234 239 : String result;
1235 :
1236 239 : size_t voiceCount = instruments.size();
1237 239 : bool putTrailingDot = (voiceCount <= 1 && trailingDotSingle) || (voiceCount > 1 && trailingDotMultiple);
1238 :
1239 390 : for (size_t i = 0; i < instruments.size(); ++i) {
1240 151 : if (!result.empty() && result.back() != '\n') {
1241 9 : result += '.';
1242 : }
1243 :
1244 151 : const Instrument* instr = instruments[i];
1245 151 : int startNumber = instr->number();
1246 151 : int curNumber = startNumber;
1247 239 : for (size_t j = i + 1; j < instruments.size(); ++j) {
1248 97 : const Instrument* nextInstr = instruments[j];
1249 97 : int nextNumber = nextInstr->number();
1250 97 : if (nextNumber == curNumber + 1) {
1251 88 : curNumber = nextNumber;
1252 88 : if (nextNumber - startNumber >= hyphenLimit) {
1253 88 : i = j;
1254 : } else {
1255 0 : continue;
1256 : }
1257 : } else {
1258 9 : break;
1259 : }
1260 : }
1261 :
1262 151 : if (curNumber - startNumber >= hyphenLimit) {
1263 88 : result += String::number(startNumber);
1264 :
1265 88 : if (putTrailingDot) {
1266 0 : result += '.';
1267 : }
1268 :
1269 88 : result += u'–' + String::number(curNumber);
1270 : } else {
1271 63 : result += String::number(startNumber);
1272 : }
1273 : }
1274 :
1275 239 : if (!result.isEmpty() && putTrailingDot) {
1276 0 : result += u".";
1277 : }
1278 :
1279 239 : return result;
1280 0 : }
1281 :
1282 0 : String& SystemHeaderLayout::resolveTokens(String& str, const String& name, const String& transposition, const String& number)
1283 : {
1284 0 : static const String NAME = u"$name";
1285 0 : static const String TRANSP = u"$transposition";
1286 0 : static const String NUMBER = u"$number";
1287 :
1288 0 : str.replace(NAME, name);
1289 0 : str.replace(TRANSP, transposition);
1290 :
1291 0 : if (!number.empty()) {
1292 0 : str.replace(NUMBER, number);
1293 0 : } else if (str.contains(u" $number")) {
1294 0 : str.remove(u" $number");
1295 0 : } else if (str.contains(u"$number ")) {
1296 0 : str.remove(u"$number ");
1297 0 : } else if (str.contains(u"$number")) {
1298 0 : str.remove(u"$number");
1299 : }
1300 :
1301 0 : return str;
1302 : }
1303 :
1304 41883 : InstrumentLabelVisibility SystemHeaderLayout::resolveInstrumentLabelVisibility(const Staff* staff, const Fraction& tick,
1305 : LayoutContext& ctx, bool firstSystem)
1306 : {
1307 41883 : const InstrumentLabelVisibility local = staff->staffType(tick)->instrumentLabelVisibility();
1308 41883 : if (local != InstrumentLabelVisibility::AUTO) {
1309 0 : return local;
1310 : }
1311 :
1312 41883 : if (ctx.conf().styleB(Sid::hideInstrumentNameIfOneInstrument) && ctx.dom().visiblePartCount() <= 1) {
1313 23794 : std::vector<Part*> visibleParts = ctx.dom().visibleParts();
1314 23794 : if (visibleParts.size() == 0) {
1315 0 : return InstrumentLabelVisibility::HIDE;
1316 : }
1317 23794 : if (visibleParts.size() == 1 && !visibleParts.front()->isSharedPart()) {
1318 23054 : return InstrumentLabelVisibility::HIDE;
1319 : }
1320 23794 : }
1321 :
1322 18829 : const Sid sid = firstSystem ? Sid::firstSystemInstNameVisibility : Sid::subsSystemInstNameVisibility;
1323 18829 : const InstrumentLabelVisibility styleVisibility = ctx.conf().styleV(sid).value<InstrumentLabelVisibility>();
1324 18829 : if (firstSystem && styleVisibility == InstrumentLabelVisibility::LONG && !ctx.state().startWithLongNames()) {
1325 0 : return InstrumentLabelVisibility::SHORT;
1326 : }
1327 18829 : return styleVisibility;
1328 : }
1329 :
1330 41883 : InstrumentNameType SystemHeaderLayout::resolveInstrumentNameType(InstrumentLabelVisibility visibility)
1331 : {
1332 41883 : return visibility == InstrumentLabelVisibility::LONG ? InstrumentNameType::LONG : InstrumentNameType::SHORT;
1333 : }
1334 :
1335 14371 : void SystemHeaderLayout::setInstrumentNames(System* system, LayoutContext& ctx)
1336 : {
1337 14371 : if (system->vbox()) {
1338 8 : return;
1339 : }
1340 :
1341 14371 : const Measure* firstMeasure = system->firstMeasure();
1342 14371 : if (!firstMeasure) {
1343 8 : return;
1344 : }
1345 :
1346 14363 : Fraction tick = firstMeasure->tick();
1347 :
1348 14363 : bool subsSysLongName = ctx.conf().styleV(Sid::subsSystemInstNameVisibility).value<InstrumentLabelVisibility>()
1349 14363 : == InstrumentLabelVisibility::LONG;
1350 14363 : bool longName = ctx.mutState().firstSystem() ? ctx.mutState().startWithLongNames() : subsSysLongName;
1351 :
1352 14363 : System::LayoutData* ldata = system->mutldata();
1353 14363 : ldata->setUseLongNames(longName);
1354 :
1355 14363 : InstrumentNameType type = longName ? InstrumentNameType::LONG : InstrumentNameType::SHORT;
1356 :
1357 : // We do not show instrument names for video export
1358 14363 : if (!ctx.conf().isShowInstrumentNames()) {
1359 0 : for (staff_idx_t idx = 0; idx < system->staves().size(); ++idx) {
1360 0 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::STAFF);
1361 0 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::SHARED_STAFF);
1362 0 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::PART);
1363 0 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
1364 : }
1365 0 : return;
1366 : }
1367 :
1368 14363 : updateGroupNames(system, ctx, tick);
1369 :
1370 14363 : bool firstSystem = ctx.state().firstSystem();
1371 :
1372 33605 : for (size_t staffIdx = 0; staffIdx < system->staves().size(); /*empty*/) {
1373 19242 : Part* part = ctx.dom().staff(staffIdx)->part();
1374 19242 : size_t partNstaves = part->nstaves();
1375 19242 : size_t visibleStavesCount = part->visibleStavesCount();
1376 :
1377 19242 : if (part->isSharedPart() && part->show()) {
1378 123 : setSharedPartNames(toSharedPart(part), staffIdx, system, ctx, tick);
1379 123 : staffIdx += partNstaves;
1380 123 : continue;
1381 : }
1382 :
1383 40662 : for (size_t idxInPart = 0; idxInPart < partNstaves; ++idxInPart) {
1384 21543 : staff_idx_t globalIdx = staffIdx + idxInPart;
1385 21543 : const Staff* staff = ctx.dom().staff(globalIdx);
1386 :
1387 21543 : InstrumentLabelVisibility visibility = resolveInstrumentLabelVisibility(staff, tick, ctx, firstSystem);
1388 21543 : InstrumentNameType staffNameType = resolveInstrumentNameType(visibility);
1389 21543 : bool hide = visibility == InstrumentLabelVisibility::HIDE;
1390 21543 : bool staffLongName = staffNameType == InstrumentNameType::LONG;
1391 :
1392 21543 : String instrumentName;
1393 21543 : if (idxInPart == 0 && part->show() && visibleStavesCount > 0 && !hide) {
1394 8614 : instrumentName = formattedInstrumentName(system, part, tick, staffLongName);
1395 : }
1396 21543 : updateName(system, globalIdx, ctx, instrumentName, staffNameType, InstrumentNameRole::PART);
1397 :
1398 21543 : String staffName;
1399 21543 : if (staff->show() && !hide) {
1400 9490 : staffName = staffLongName ? staff->individualStaffNameLong(tick) : staff->individualStaffNameShort(tick);
1401 : }
1402 21543 : updateName(system, globalIdx, ctx, staffName, staffNameType, InstrumentNameRole::STAFF);
1403 21543 : }
1404 :
1405 19119 : staffIdx += partNstaves;
1406 : }
1407 : }
1408 :
1409 123 : void SystemHeaderLayout::setSharedPartNames(SharedPart* sharedPart, staff_idx_t startStaffIdx, System* system, LayoutContext& ctx,
1410 : const Fraction& tick)
1411 : {
1412 : InstrumentLabelVisibility visibility
1413 123 : = resolveInstrumentLabelVisibility(ctx.dom().staff(startStaffIdx), tick, ctx, ctx.state().firstSystem());
1414 123 : InstrumentNameType type = resolveInstrumentNameType(visibility);
1415 123 : bool hideStartStaff = visibility == InstrumentLabelVisibility::HIDE;
1416 :
1417 123 : if (!sharedPart->show() || sharedPart->visibleStavesCount() == 0) {
1418 0 : for (size_t relStaffIdx = 0; relStaffIdx < sharedPart->nstaves(); ++relStaffIdx) {
1419 0 : size_t globalStaffIdx = startStaffIdx + relStaffIdx;
1420 0 : updateName(system, globalStaffIdx, ctx, String(), type, InstrumentNameRole::GROUP);
1421 0 : updateName(system, globalStaffIdx, ctx, String(), type, InstrumentNameRole::PART);
1422 0 : updateName(system, globalStaffIdx, ctx, String(), type, InstrumentNameRole::SHARED_STAFF);
1423 0 : updateName(system, globalStaffIdx, ctx, String(), type, InstrumentNameRole::STAFF);
1424 : }
1425 :
1426 0 : return;
1427 : }
1428 :
1429 123 : if (!sharedPart->isSameInstruments()) {
1430 : // TODO
1431 0 : return;
1432 : }
1433 :
1434 123 : const Instrument* instr = sharedPart->instrument();
1435 123 : bool useGroup = useGroupNames(instr->group(), ctx) && sharedPart->isSameInstrumentsAtTick(tick);
1436 :
1437 123 : const SharedTrackMap& trackMap = sharedPart->trackMapAtTick(tick);
1438 123 : const std::vector<Part*> originParts = sharedPart->originParts();
1439 :
1440 : // If using group names, assign normal PART name (one name covers all shared staves). Obtains the style
1441 : //
1442 : // 1.2
1443 : // Horn
1444 : // 3.4
1445 : //
1446 : // If not using group names, assign SHARED_STAFF name to each stave. Obtains the style
1447 : //
1448 : // Horn 1.2
1449 : //
1450 : // Horn 3.4
1451 :
1452 360 : for (size_t relStaffIdx = 0; relStaffIdx < sharedPart->nstaves(); ++relStaffIdx) {
1453 237 : size_t globalStaffIdx = startStaffIdx + relStaffIdx;
1454 237 : Part* originPart = sharedPart->originPartForStaff(globalStaffIdx, tick);
1455 :
1456 : InstrumentLabelVisibility partVisibility
1457 237 : = resolveInstrumentLabelVisibility(ctx.dom().staff(globalStaffIdx), tick, ctx, ctx.state().firstSystem());
1458 237 : InstrumentNameType partNameType = resolveInstrumentNameType(partVisibility);
1459 237 : bool partLongName = partNameType == InstrumentNameType::LONG;
1460 237 : bool hidePartName = partVisibility == InstrumentLabelVisibility::HIDE;
1461 :
1462 237 : String staffGroupName;
1463 237 : if (!hidePartName) {
1464 237 : staffGroupName = formattedGroupName(system, originPart ? originPart : sharedPart, tick, partLongName);
1465 : }
1466 :
1467 237 : if (useGroup && !hideStartStaff) {
1468 237 : if (relStaffIdx == 0) {
1469 123 : updateName(system, globalStaffIdx, ctx, staffGroupName, partNameType, InstrumentNameRole::PART);
1470 : }
1471 :
1472 237 : updateName(system, globalStaffIdx, ctx, String(), partNameType, InstrumentNameRole::SHARED_STAFF);
1473 : } else {
1474 0 : if (relStaffIdx == 0) {
1475 0 : updateName(system, globalStaffIdx, ctx, String(), type, InstrumentNameRole::PART);
1476 : }
1477 0 : updateName(system, globalStaffIdx, ctx, staffGroupName, partNameType, InstrumentNameRole::SHARED_STAFF);
1478 : }
1479 :
1480 237 : String staffLabel;
1481 237 : if (!hidePartName) {
1482 237 : staffLabel = formattedSharedStaffLabel(globalStaffIdx, trackMap, originParts);
1483 : }
1484 :
1485 237 : updateName(system, globalStaffIdx, ctx, staffLabel, partNameType, InstrumentNameRole::STAFF);
1486 237 : }
1487 123 : }
1488 :
1489 14363 : void SystemHeaderLayout::updateGroupNames(System* system, LayoutContext& ctx, const Fraction& tick)
1490 : {
1491 14363 : bool firstSystem = ctx.state().firstSystem();
1492 :
1493 14363 : System::LayoutData* ldata = system->mutldata();
1494 14363 : ldata->clearPartsWithGroupNames();
1495 :
1496 32015 : for (staff_idx_t startOfGroup = 0; startOfGroup < system->staves().size();) {
1497 17652 : Part* curPart = ctx.dom().staff(startOfGroup)->part();
1498 17652 : const Instrument* curInstrument = curPart->instrument(tick);
1499 17652 : const InstrumentLabel& curLabel = curInstrument->instrumentLabel();
1500 17652 : if (!curInstrument->instrumentLabel().allowGroupName() || !curPart->show() || curPart->isSharedPart()) {
1501 488 : ++startOfGroup;
1502 488 : continue;
1503 : }
1504 :
1505 : InstrumentLabelVisibility visibility
1506 17164 : = resolveInstrumentLabelVisibility(ctx.dom().staff(startOfGroup), tick, ctx, firstSystem);
1507 17164 : InstrumentNameType type = resolveInstrumentNameType(visibility);
1508 17164 : bool hide = visibility == InstrumentLabelVisibility::HIDE;
1509 :
1510 17164 : std::vector<Part*> partsInThisGroup;
1511 :
1512 17164 : staff_idx_t endOfGroup = startOfGroup;
1513 36038 : while (endOfGroup < system->staves().size()) {
1514 21800 : Part* nextPart = ctx.dom().staff(endOfGroup)->part();
1515 21800 : Instrument* nextInstrument = nextPart->instrument(tick);
1516 21800 : InstrumentLabel& nextLabel = nextInstrument->instrumentLabel();
1517 43600 : if (nextPart != curPart
1518 21800 : && (nextInstrument->id() != curInstrument->id() || !nextLabel.allowGroupName() || nextPart->isSharedPart())) {
1519 2926 : break;
1520 : }
1521 37748 : if (nextLabel.useCustomGroupName() && (nextLabel.customNameLongGroup() != curLabel.customNameLongGroup()
1522 18874 : || nextLabel.customNameShortGroup() != curLabel.customNameShortGroup())) {
1523 0 : break;
1524 : }
1525 :
1526 18874 : if (type == InstrumentNameType::LONG && system->visibleStavesOfPart(nextPart).size() == 0) {
1527 : // NOTE: We can only do this for long-name systems because they don't need to have
1528 : // the left barline aligned with the other systems across the page.
1529 17 : endOfGroup += nextPart->nstaves();
1530 17 : continue;
1531 : }
1532 :
1533 18857 : if (nextPart->show() && nextPart->visibleStavesCount() > 0) {
1534 18841 : partsInThisGroup.push_back(nextPart);
1535 18841 : nextLabel.setUseCustomGroupName(curLabel.useCustomGroupName());
1536 18841 : nextLabel.setCustomNameLongGroup(curLabel.customNameLongGroup());
1537 18841 : nextLabel.setCustomNameShortGroup(curLabel.customNameShortGroup());
1538 : }
1539 :
1540 18857 : endOfGroup += nextPart->nstaves();
1541 : }
1542 :
1543 17164 : if (!hide && partsInThisGroup.size() > 1 && useGroupNames(curInstrument->group(), ctx)) {
1544 138 : String name = formattedGroupName(system, curPart, tick, type == InstrumentNameType::LONG);
1545 138 : InstrumentName* groupName = updateName(system, startOfGroup, ctx, name, type, InstrumentNameRole::GROUP);
1546 :
1547 138 : if (groupName) {
1548 134 : groupName->mutldata()->setEndIdxOfGroup(endOfGroup);
1549 :
1550 424 : for (Part* p : partsInThisGroup) {
1551 290 : ldata->addPartWithGroupNames(p, groupName);
1552 : }
1553 : }
1554 :
1555 302 : for (staff_idx_t idx = startOfGroup + 1; idx < endOfGroup; ++idx) {
1556 164 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
1557 : }
1558 138 : } else {
1559 38016 : for (staff_idx_t idx = startOfGroup; idx < endOfGroup; ++idx) {
1560 20990 : updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
1561 : }
1562 : }
1563 :
1564 17164 : startOfGroup = endOfGroup;
1565 17164 : }
1566 :
1567 36143 : for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
1568 21780 : if (system->staff(staffIdx)->name(InstrumentNameRole::GROUP)) {
1569 134 : Part* part = ctx.dom().staff(staffIdx)->part();
1570 134 : if (!muse::contains(ldata->partsWithGroupName(), part)) {
1571 : // name is empty here, so the InstrumentNameType is unused (see updateName)
1572 0 : updateName(system, staffIdx, ctx, String(), InstrumentNameType::SHORT, InstrumentNameRole::GROUP);
1573 : }
1574 : }
1575 : }
1576 14363 : }
1577 :
1578 859 : bool SystemHeaderLayout::useGroupNames(const String& instrumentGroup, LayoutContext& ctx)
1579 : {
1580 859 : const MStyle& style = ctx.conf().style();
1581 :
1582 859 : if (instrumentGroup == "woodwinds" || instrumentGroup == "brass") {
1583 351 : return style.styleB(Sid::windsNameByGroup);
1584 : }
1585 508 : if (instrumentGroup == "vocals") {
1586 52 : return style.styleB(Sid::vocalsNameByGroup);
1587 : }
1588 456 : if (instrumentGroup == "strings") {
1589 84 : return style.styleB(Sid::stringsNameByGroup);
1590 : }
1591 :
1592 372 : return style.styleB(Sid::othersNameByGroup);
1593 : }
1594 :
1595 14374 : void SystemHeaderLayout::updateSystemHeaderWidth(System* system, LayoutContext& ctx)
1596 : {
1597 14374 : if (system->staves().empty()) { // ignore vbox
1598 3 : return;
1599 : }
1600 :
1601 14371 : SystemHeaderLayout::computeInstrumentNameOffset(system, ctx);
1602 14371 : double instrumentNameOffset = system->ldata()->instrumentNameOffset();
1603 :
1604 : //---------------------------------------------------
1605 : // find x position of staves
1606 : //---------------------------------------------------
1607 14371 : SystemHeaderLayout::layoutBrackets(system, ctx);
1608 14371 : double maxBracketsWidth = SystemHeaderLayout::totalBracketOffset(ctx);
1609 :
1610 14371 : SystemHeaderLayout::setInstrumentNames(system, ctx);
1611 14371 : SystemHeaderLayout::computeInstrumentNamesWidth(system, ctx);
1612 14371 : double maxNamesWidth = system->ldata()->totalNamesWidth();
1613 14371 : double indent = maxNamesWidth > 0 ? maxNamesWidth + instrumentNameOffset : 0.0;
1614 14371 : if (ctx.state().firstSystem() && ctx.state().firstSystemIndent()) {
1615 5973 : indent = std::max(indent, system->styleP(Sid::firstSystemIndentationValue) * system->mag() - maxBracketsWidth);
1616 5973 : maxNamesWidth = indent - instrumentNameOffset;
1617 : }
1618 :
1619 14371 : if (muse::RealIsNull(indent)) {
1620 6628 : if (ctx.conf().styleB(Sid::alignSystemToMargin)) {
1621 6628 : system->setLeftMargin(0.0);
1622 : } else {
1623 0 : system->setLeftMargin(maxBracketsWidth);
1624 : }
1625 : } else {
1626 7743 : system->setLeftMargin(indent + maxBracketsWidth);
1627 : }
1628 : }
|