1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2016 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "accidental.h"
14 #include "barline.h"
15 #include "beam.h"
16 #include "box.h"
17 #include "chord.h"
18 #include "clef.h"
19 #include "element.h"
20 #include "fingering.h"
21 #include "glissando.h"
22 #include "harmony.h"
23 #include "key.h"
24 #include "keysig.h"
25 #include "layoutbreak.h"
26 #include "layout.h"
27 #include "lyrics.h"
28 #include "marker.h"
29 #include "measure.h"
30 #include "mscore.h"
31 #include "notedot.h"
32 #include "note.h"
33 #include "ottava.h"
34 #include "page.h"
35 #include "part.h"
36 #include "repeat.h"
37 #include "score.h"
38 #include "segment.h"
39 #include "sig.h"
40 #include "slur.h"
41 #include "staff.h"
42 #include "stem.h"
43 #include "stemslash.h"
44 #include "sticking.h"
45 #include "style.h"
46 #include "sym.h"
47 #include "system.h"
48 #include "text.h"
49 #include "tie.h"
50 #include "timesig.h"
51 #include "tremolo.h"
52 #include "tuplet.h"
53 #include "undo.h"
54 #include "utils.h"
55 #include "volta.h"
56 #include "breath.h"
57 #include "tempotext.h"
58 #include "systemdivider.h"
59 #include "hook.h"
60 #include "ambitus.h"
61 #include "hairpin.h"
62 #include "stafflines.h"
63 #include "articulation.h"
64 #include "bracket.h"
65 #include "spacer.h"
66 #include "fermata.h"
67 #include "measurenumber.h"
68
69 namespace Ms {
70
71 // #define PAGE_DEBUG
72
73 #ifdef PAGE_DEBUG
74 #define PAGEDBG(...) qDebug(__VA_ARGS__)
75 #else
76 #define PAGEDBG(...) ;
77 #endif
78
79 //---------------------------------------------------------
80 // rebuildBspTree
81 //---------------------------------------------------------
82
rebuildBspTree()83 void Score::rebuildBspTree()
84 {
85 for (Page* page : pages())
86 page->rebuildBspTree();
87 }
88
89 //---------------------------------------------------------
90 // layoutSegmentElements
91 //---------------------------------------------------------
92
layoutSegmentElements(Segment * segment,int startTrack,int endTrack)93 static void layoutSegmentElements(Segment* segment, int startTrack, int endTrack)
94 {
95 for (int track = startTrack; track < endTrack; ++track) {
96 if (Element* e = segment->element(track))
97 e->layout();
98 }
99 }
100
101 #if 0
102 //---------------------------------------------------------
103 // vUp
104 // reurns true if chord should be treated as up
105 // for purpose of setting horizontal position
106 // for most chords, this is just chord->up()
107 // but for notes on cross-staff beams, we take care to produce more consistent results
108 // since the initial guess for up() may change during layout
109 //---------------------------------------------------------
110 static bool vUp(Chord* chord)
111 {
112 if (!chord)
113 return true;
114 else if (!chord->beam() || !chord->beam()->cross()) {
115 return chord->up();
116 }
117 else {
118 // cross-staff beam: we cannot know the actual direction of this chord until the beam layout,
119 // but that's too late - it won't work to lay out as if the chord is up on pass one but then down on pass two
120 // so just assign a logical direction based on attributes that won't change
121 // so chords can be laid out consistently on both passes
122 bool up;
123 if (chord->stemDirection() != Direction::AUTO)
124 up = chord->stemDirection() == Direction::UP;
125 else if (chord->staffMove())
126 up = chord->staffMove() > 0;
127 else if (chord->track() < chord->beam()->track())
128 up = false;
129 else if (chord->track() > chord->beam()->track())
130 up = true;
131 else if (chord->measure()->hasVoices(chord->staffIdx(), chord->tick(), chord->actualTicks()))
132 up = !(chord->track() % 2);
133 else
134 up = !chord->staff()->isTop();
135 return up;
136 }
137 }
138 #endif
139
140 //---------------------------------------------------------
141 // layoutChords1
142 // - layout upstem and downstem chords
143 // - offset as necessary to avoid conflict
144 //---------------------------------------------------------
145
layoutChords1(Segment * segment,int staffIdx)146 void Score::layoutChords1(Segment* segment, int staffIdx)
147 {
148 const Staff* staff = Score::staff(staffIdx);
149 const int startTrack = staffIdx * VOICES;
150 const int endTrack = startTrack + VOICES;
151 const Fraction tick = segment->tick();
152
153 if (staff->isTabStaff(tick)) {
154 layoutSegmentElements(segment, startTrack, endTrack);
155 return;
156 }
157
158 bool crossBeamFound = false;
159 std::vector<Note*> upStemNotes;
160 std::vector<Note*> downStemNotes;
161 int upVoices = 0;
162 int downVoices = 0;
163 qreal nominalWidth = noteHeadWidth() * staff->mag(tick);
164 qreal maxUpWidth = 0.0;
165 qreal maxDownWidth = 0.0;
166 qreal maxUpMag = 0.0;
167 qreal maxDownMag = 0.0;
168
169 // dots and hooks can affect layout of notes as well as vice versa
170 int upDots = 0;
171 int downDots = 0;
172 bool upHooks = false;
173 bool downHooks = false;
174
175 // also check for grace notes
176 bool upGrace = false;
177 bool downGrace = false;
178
179 for (int track = startTrack; track < endTrack; ++track) {
180 Element* e = segment->element(track);
181 if (e && e->isChord()) {
182 Chord* chord = toChord(e);
183 if (chord->beam() && chord->beam()->cross())
184 crossBeamFound = true;
185 bool hasGraceBefore = false;
186 for (Chord* c : chord->graceNotes()) {
187 if (c->isGraceBefore())
188 hasGraceBefore = true;
189 layoutChords2(c->notes(), c->up()); // layout grace note noteheads
190 layoutChords3(c->notes(), staff, 0); // layout grace note chords
191 }
192 if (chord->up()) {
193 ++upVoices;
194 upStemNotes.insert(upStemNotes.end(), chord->notes().begin(), chord->notes().end());
195 upDots = qMax(upDots, chord->dots());
196 maxUpMag = qMax(maxUpMag, chord->mag());
197 if (!upHooks)
198 upHooks = chord->hook();
199 if (hasGraceBefore)
200 upGrace = true;
201 }
202 else {
203 ++downVoices;
204 downStemNotes.insert(downStemNotes.end(), chord->notes().begin(), chord->notes().end());
205 downDots = qMax(downDots, chord->dots());
206 maxDownMag = qMax(maxDownMag, chord->mag());
207 if (!downHooks)
208 downHooks = chord->hook();
209 if (hasGraceBefore)
210 downGrace = true;
211 }
212 }
213 }
214
215 if (upVoices + downVoices) {
216 // TODO: use track as secondary sort criteria?
217 // otherwise there might be issues with unisons between voices
218 // in some corner cases
219
220 maxUpWidth = nominalWidth * maxUpMag;
221 maxDownWidth = nominalWidth * maxDownMag;
222
223 // layout upstem noteheads
224 if (upVoices > 1) {
225 std::sort(upStemNotes.begin(), upStemNotes.end(),
226 [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
227 }
228 if (upVoices) {
229 qreal hw = layoutChords2(upStemNotes, true);
230 maxUpWidth = qMax(maxUpWidth, hw);
231 }
232
233 // layout downstem noteheads
234 if (downVoices > 1) {
235 std::sort(downStemNotes.begin(), downStemNotes.end(),
236 [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
237 }
238 if (downVoices) {
239 qreal hw = layoutChords2(downStemNotes, false);
240 maxDownWidth = qMax(maxDownWidth, hw);
241 }
242
243 qreal sp = staff->spatium(tick);
244 qreal upOffset = 0.0; // offset to apply to upstem chords
245 qreal downOffset = 0.0; // offset to apply to downstem chords
246 qreal dotAdjust = 0.0; // additional chord offset to account for dots
247 qreal dotAdjustThreshold = 0.0; // if it exceeds this amount
248
249 // centering adjustments for whole note, breve, and small chords
250 qreal centerUp = 0.0; // offset to apply in order to center upstem chords
251 qreal oversizeUp = 0.0; // adjustment to oversized upstem chord needed if laid out to the right
252 qreal centerDown = 0.0; // offset to apply in order to center downstem chords
253 qreal centerAdjustUp = 0.0; // adjustment to upstem chord needed after centering donwstem chord
254 qreal centerAdjustDown = 0.0; // adjustment to downstem chord needed after centering upstem chord
255
256 // only center chords if they differ from nominal by at least this amount
257 // this avoids unnecessary centering on differences due only to floating point roundoff
258 // it also allows for the possibility of disabling centering
259 // for notes only "slightly" larger than nominal, like half notes
260 // but this will result in them not being aligned with each other between voices
261 // unless you change to left alignment as described in the comments below
262 qreal centerThreshold = 0.01 * sp;
263
264 // amount by which actual width exceeds nominal, adjusted for staff mag() only
265 qreal headDiff = maxUpWidth - nominalWidth;
266 // amount by which actual width exceeds nominal, adjusted for staff & chord/note mag()
267 qreal headDiff2 = maxUpWidth - nominalWidth * (maxUpMag / staff->mag(tick));
268 if (headDiff > centerThreshold) {
269 // larger than nominal
270 centerUp = headDiff * -0.5;
271 // maxUpWidth is true width, but we no longer will care about that
272 // instead, we care only about portion to right of origin
273 maxUpWidth += centerUp;
274 // to left align rather than center, delete both of the above
275 if (headDiff2 > centerThreshold) {
276 // if max notehead is wider than nominal with chord/note mag() applied
277 // then noteheads extend to left of origin
278 // because stemPosX() is based on nominal width
279 // so we need to correct for that too
280 centerUp += headDiff2;
281 oversizeUp = headDiff2;
282 }
283 }
284 else if (-headDiff > centerThreshold) {
285 // smaller than nominal
286 centerUp = -headDiff * 0.5;
287 if (headDiff2 > centerThreshold) {
288 // max notehead is wider than nominal with chord/note mag() applied
289 // perform same adjustment as above
290 centerUp += headDiff2;
291 oversizeUp = headDiff2;
292 }
293 centerAdjustDown = centerUp;
294 }
295
296 headDiff = maxDownWidth - nominalWidth;
297 if (headDiff > centerThreshold) {
298 // larger than nominal
299 centerDown = headDiff * -0.5;
300 // to left align rather than center, change the above to
301 //centerAdjustUp = headDiff;
302 maxDownWidth = nominalWidth - centerDown;
303 }
304 else if (-headDiff > centerThreshold) {
305 // smaller than nominal
306 centerDown = -headDiff * 0.5;
307 centerAdjustUp = centerDown;
308 }
309
310 // handle conflict between upstem and downstem chords
311
312 if (upVoices && downVoices) {
313 Note* bottomUpNote = upStemNotes.front();
314 Note* topDownNote = downStemNotes.back();
315 int separation;
316 // TODO: handle conflicts for cross-staff notes and notes on cross-staff beams
317 // for now we simply treat these as though there is no conflict
318 if (bottomUpNote->chord()->staffMove() == topDownNote->chord()->staffMove() && !crossBeamFound)
319 separation = topDownNote->line() - bottomUpNote->line();
320 else
321 separation = 2; // no conflict
322 QVector<Note*> overlapNotes;
323 overlapNotes.reserve(8);
324
325 if (separation == 1) {
326 // second
327 downOffset = maxUpWidth;
328 // align stems if present, leave extra room if not
329 if (topDownNote->chord()->stem() && bottomUpNote->chord()->stem())
330 downOffset -= topDownNote->chord()->stem()->lineWidth();
331 else
332 downOffset += 0.1 * sp;
333 }
334
335 else if (separation < 1) {
336
337 // overlap (possibly unison)
338
339 // build list of overlapping notes
340 for (size_t i = 0, n = upStemNotes.size(); i < n; ++i) {
341 if (upStemNotes[i]->line() >= topDownNote->line() - 1)
342 overlapNotes.append(upStemNotes[i]);
343 else
344 break;
345 }
346 for (size_t i = downStemNotes.size(); i > 0; --i) { // loop most probably needs to be in this reverse order
347 if (downStemNotes[i-1]->line() <= bottomUpNote->line() + 1)
348 overlapNotes.append(downStemNotes[i-1]);
349 else
350 break;
351 }
352 std::sort(overlapNotes.begin(), overlapNotes.end(),
353 [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
354
355 // determine nature of overlap
356 bool shareHeads = true; // can all overlapping notes share heads?
357 bool matchPending = false; // looking for a unison match
358 bool conflictUnison = false; // unison found
359 bool conflictSecondUpHigher = false; // second found
360 bool conflictSecondDownHigher = false; // second found
361 int lastLine = 1000;
362 Note* p = overlapNotes[0];
363 for (int i = 0, count = overlapNotes.size(); i < count; ++i) {
364 Note* n = overlapNotes[i];
365 NoteHead::Type nHeadType;
366 NoteHead::Type pHeadType;
367 Chord* nchord = n->chord();
368 Chord* pchord = p->chord();
369 if (n->mirror()) {
370 if (separation < 0) {
371 // don't try to share heads if there is any mirroring
372 shareHeads = false;
373 // don't worry about conflicts involving mirrored notes
374 continue;
375 }
376 }
377 int line = n->line();
378 int d = lastLine - line;
379 switch (d) {
380 case 0:
381 // unison
382 conflictUnison = true;
383 matchPending = false;
384 nHeadType = (n->headType() == NoteHead::Type::HEAD_AUTO) ? n->chord()->durationType().headType() : n->headType();
385 pHeadType = (p->headType() == NoteHead::Type::HEAD_AUTO) ? p->chord()->durationType().headType() : p->headType();
386 // the most important rules for sharing noteheads on unisons between voices are
387 // that notes must be one same line with same tpc
388 // noteheads must be unmirrored and of same group
389 // and chords must be same size (or else sharing code won't work)
390 if (n->headGroup() != p->headGroup() || n->tpc() != p->tpc() || n->mirror() || p->mirror() || nchord->small() != pchord->small()) {
391 shareHeads = false;
392 }
393 else {
394 // noteheads are potentially shareable
395 // it is more subjective at this point
396 // current default is to require *either* of the following:
397 // 1) both chords have same number of dots, both have stems, and both noteheads are same type and are full size (automatic match)
398 // or 2) one or more of the noteheads is not of type AUTO, but is explicitly set to match the other (user-forced match)
399 // or 3) exactly one of the noteheads is invisible (user-forced match)
400 // thus user can force notes to be shared despite differing number of dots or either being stemless
401 // by setting one of the notehead types to match the other or by making one notehead invisible
402 // TODO: consider adding a style option, staff properties, or note property to control sharing
403 if ((nchord->dots() != pchord->dots() || !nchord->stem() || !pchord->stem() || nHeadType != pHeadType || n->small() || p->small()) &&
404 ((n->headType() == NoteHead::Type::HEAD_AUTO && p->headType() == NoteHead::Type::HEAD_AUTO) || nHeadType != pHeadType) &&
405 (n->visible() == p->visible())) {
406 shareHeads = false;
407 }
408 }
409 break;
410 case 1:
411 // second
412 // trust that this won't be a problem for single unison
413 if (separation < 0) {
414 if (n->chord()->up())
415 conflictSecondUpHigher = true;
416 else
417 conflictSecondDownHigher = true;
418 shareHeads = false;
419 }
420 break;
421 default:
422 // no conflict
423 if (matchPending)
424 shareHeads = false;
425 matchPending = true;
426 }
427 p = n;
428 lastLine = line;
429 }
430 if (matchPending)
431 shareHeads = false;
432
433 // calculate offsets
434 if (shareHeads) {
435 for (int i = overlapNotes.size() - 1; i >= 1; i -= 2) {
436 Note* previousNote = overlapNotes[i-1];
437 Note* n = overlapNotes[i];
438 if (!(previousNote->chord()->isNudged() || n->chord()->isNudged())) {
439 if (previousNote->chord()->dots() == n->chord()->dots()) {
440 // hide one set dots
441 bool onLine = !(previousNote->line() & 1);
442 if (onLine) {
443 // hide dots for lower voice
444 if (previousNote->voice() & 1)
445 previousNote->setDotsHidden(true);
446 else
447 n->setDotsHidden(true);
448 }
449 else {
450 // hide dots for upper voice
451 if (!(previousNote->voice() & 1))
452 previousNote->setDotsHidden(true);
453 else
454 n->setDotsHidden(true);
455 }
456 }
457 // formerly we hid noteheads in an effort to fix playback
458 // but this doesn't work for cases where noteheads cannot be shared
459 // so better to solve the problem elsewhere
460 }
461 }
462 }
463 else if (conflictUnison && separation == 0 && (!downGrace || upGrace))
464 downOffset = maxUpWidth + 0.3 * sp;
465 else if (conflictUnison)
466 upOffset = maxDownWidth + 0.3 * sp;
467 else if (conflictSecondUpHigher)
468 upOffset = maxDownWidth + 0.2 * sp;
469 else if ((downHooks && !upHooks) && !(upDots && !downDots))
470 downOffset = maxUpWidth + 0.3 * sp;
471 else if (conflictSecondDownHigher) {
472 if (downDots && !upDots)
473 downOffset = maxUpWidth + 0.3 * sp;
474 else {
475 upOffset = maxDownWidth - 0.2 * sp;
476 if (downHooks)
477 upOffset += 0.3 * sp;
478 }
479 }
480 else {
481 // no direct conflict, so parts can overlap (downstem on left)
482 // just be sure that stems clear opposing noteheads
483 qreal clearLeft = 0.0, clearRight = 0.0;
484 if (topDownNote->chord()->stem())
485 clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
486 if (bottomUpNote->chord()->stem())
487 clearRight = bottomUpNote->chord()->stem()->lineWidth() + qMax(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
488 else
489 downDots = 0; // no need to adjust for dots in this case
490 upOffset = qMax(clearLeft, clearRight);
491 if (downHooks) {
492 // we will need more space to avoid collision with hook
493 // but we won't need as much dot adjustment
494 upOffset = qMax(upOffset, maxDownWidth + 0.1 * sp);
495 dotAdjustThreshold = maxUpWidth - 0.3 * sp;
496 }
497 // if downstem chord is small, don't center
498 // and we might not need as much dot adjustment either
499 if (centerDown > 0.0) {
500 centerDown = 0.0;
501 centerAdjustUp = 0.0;
502 dotAdjustThreshold = (upOffset - maxDownWidth) + maxUpWidth - 0.3 * sp;
503 }
504 }
505
506 }
507
508 // adjust for dots
509 if ((upDots && !downDots) || (downDots && !upDots)) {
510 // only one sets of dots
511 // place between chords
512 int dots;
513 qreal mag;
514 if (upDots) {
515 dots = upDots;
516 mag = maxUpMag;
517 }
518 else {
519 dots = downDots;
520 mag = maxDownMag;
521 }
522 qreal dotWidth = segment->symWidth(SymId::augmentationDot);
523 // first dot
524 dotAdjust = styleP(Sid::dotNoteDistance) + dotWidth;
525 // additional dots
526 if (dots > 1)
527 dotAdjust += styleP(Sid::dotDotDistance) * (dots - 1);
528 dotAdjust *= mag;
529 // only by amount over threshold
530 dotAdjust = qMax(dotAdjust - dotAdjustThreshold, 0.0);
531 }
532 if (separation == 1)
533 dotAdjust += 0.1 * sp;
534
535 }
536
537 // apply chord offsets
538 for (int track = startTrack; track < endTrack; ++track) {
539 Element* e = segment->element(track);
540 if (e && e->isChord()) {
541 Chord* chord = toChord(e);
542 if (chord->up()) {
543 if (upOffset != 0.0) {
544 chord->rxpos() += upOffset + centerAdjustUp + oversizeUp;
545 if (downDots && !upDots)
546 chord->rxpos() += dotAdjust;
547 }
548 else
549 chord->rxpos() += centerUp;
550 }
551 else {
552 if (downOffset != 0.0) {
553 chord->rxpos() += downOffset + centerAdjustDown;
554 if (upDots && !downDots)
555 chord->rxpos() += dotAdjust;
556 }
557 else
558 chord->rxpos() += centerDown;
559 }
560 }
561 }
562
563 // layout chords
564 std::vector<Note*> notes;
565 if (upVoices)
566 notes.insert(notes.end(), upStemNotes.begin(), upStemNotes.end());
567 if (downVoices)
568 notes.insert(notes.end(), downStemNotes.begin(), downStemNotes.end());
569 if (upVoices + downVoices > 1)
570 std::sort(notes.begin(), notes.end(),
571 [](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
572 layoutChords3(notes, staff, segment);
573 }
574
575 layoutSegmentElements(segment, startTrack, endTrack);
576 }
577
578 //---------------------------------------------------------
579 // layoutChords2
580 // - determine which notes need mirroring
581 // - this is called once for each stem direction
582 // eg, once for voices 1&3, once for 2&4
583 // with all notes combined and sorted to resemble one chord
584 // - return maximum non-mirrored notehead width
585 //---------------------------------------------------------
586
layoutChords2(std::vector<Note * > & notes,bool up)587 qreal Score::layoutChords2(std::vector<Note*>& notes, bool up)
588 {
589 int startIdx, endIdx, incIdx;
590 qreal maxWidth = 0.0;
591
592 // loop in correct direction so that first encountered notehead wins conflict
593 if (up) {
594 // loop bottom up
595 startIdx = 0;
596 endIdx = int(notes.size());
597 incIdx = 1;
598 }
599 else {
600 // loop top down
601 startIdx = int(notes.size()) - 1;
602 endIdx = -1;
603 incIdx = -1;
604 }
605
606 int ll = 1000; // line of previous notehead
607 // hack: start high so first note won't show as conflict
608 bool lvisible = false; // was last note visible?
609 bool mirror = false; // should current notehead be mirrored?
610 // value is retained and may be used on next iteration
611 // to track mirror status of previous note
612 bool isLeft = notes[startIdx]->chord()->up(); // is notehead on left?
613 int lmove = notes[startIdx]->chord()->staffMove(); // staff offset of last note (for cross-staff beaming)
614
615 for (int idx = startIdx; idx != endIdx; idx += incIdx) {
616 Note* note = notes[idx]; // current note
617 int line = note->line(); // line of current note
618 Chord* chord = note->chord();
619 int move = chord->staffMove(); // staff offset of current note
620
621 // there is a conflict
622 // if this is same or adjacent line as previous note (and chords are on same staff!)
623 // but no need to do anything about it if either note is invisible
624 bool conflict = (qAbs(ll - line) < 2) && (lmove == move) && note->visible() && lvisible;
625
626 // this note is on opposite side of stem as previous note
627 // if there is a conflict
628 // or if this the first note *after* a conflict
629 if (conflict || (chord->up() != isLeft))
630 isLeft = !isLeft;
631
632 // determine if we would need to mirror current note
633 // to get it to the correct side
634 // this would be needed to get a note to left or downstem or right of upstem
635 // whether or not we actually do this is determined later (based on user mirror property)
636 bool nmirror = (chord->up() != isLeft);
637
638 // by default, notes and dots are not hidden
639 // this may be changed later to allow unisons to share noteheads
640 note->setHidden(false);
641 note->setDotsHidden(false);
642
643 // be sure chord position is initialized
644 // chord may be moved to the right later
645 // if there are conflicts between voices
646 chord->rxpos() = 0.0;
647
648 // let user mirror property override the default we calculated
649 if (note->userMirror() == MScore::DirectionH::AUTO) {
650 mirror = nmirror;
651 }
652 else {
653 mirror = note->chord()->up();
654 if (note->userMirror() == MScore::DirectionH::LEFT)
655 mirror = !mirror;
656 }
657 note->setMirror(mirror);
658
659 // accumulate return value
660 if (!mirror)
661 maxWidth = qMax(maxWidth, note->bboxRightPos());
662
663 // prepare for next iteration
664 lvisible = note->visible();
665 lmove = move;
666 ll = line;
667 }
668
669 return maxWidth;
670 }
671
672 //---------------------------------------------------------
673 // AcEl
674 //---------------------------------------------------------
675
676 struct AcEl {
677 Note* note;
678 qreal x; // actual x position of this accidental relative to origin
679 qreal top; // top of accidental bbox relative to staff
680 qreal bottom; // bottom of accidental bbox relative to staff
681 int line; // line of note
682 int next; // index of next accidental of same pitch class (ascending list)
683 qreal width; // width of accidental
684 qreal ascent; // amount (in sp) vertical strokes extend above body
685 qreal descent; // amount (in sp) vertical strokes extend below body
686 qreal rightClear; // amount (in sp) to right of last vertical stroke above body
687 qreal leftClear; // amount (in sp) to left of last vertical stroke below body
688 };
689
690 //---------------------------------------------------------
691 // resolveAccidentals
692 // lx = calculated position of rightmost edge of left accidental relative to origin
693 //---------------------------------------------------------
694
resolveAccidentals(AcEl * left,AcEl * right,qreal & lx,qreal pd,qreal sp)695 static bool resolveAccidentals(AcEl* left, AcEl* right, qreal& lx, qreal pd, qreal sp)
696 {
697 AcEl* upper;
698 AcEl* lower;
699 if (left->line >= right->line) {
700 upper = right;
701 lower = left;
702 }
703 else {
704 upper = left;
705 lower = right;
706 }
707
708 qreal gap = lower->top - upper->bottom;
709
710 // no conflict at all if there is sufficient vertical gap between accidentals
711 // the arrangement of accidentals into columns assumes accidentals an octave apart *do* clear
712 if (gap >= pd || lower->line - upper->line >= 7)
713 return false;
714
715 qreal allowableOverlap = qMax(upper->descent, lower->ascent) - pd;
716
717 // accidentals that are "close" (small gap or even slight overlap)
718 if (qAbs(gap) <= 0.33 * sp) {
719 // acceptable with slight offset
720 // if one of the accidentals can subsume the overlap
721 // and both accidentals allow it
722 if (-gap <= allowableOverlap && qMin(upper->descent, lower->ascent) > 0.0) {
723 qreal align = qMin(left->width, right->width);
724 lx = qMin(lx, right->x + align - pd);
725 return true;
726 }
727 }
728
729 // amount by which overlapping accidentals will be separated
730 // for example, the vertical stems of two flat signs
731 // these need more space than we would need between non-overlapping accidentals
732 qreal overlapShift = pd * 1.41;
733
734 // accidentals with more significant overlap
735 // acceptable if one accidental can subsume overlap
736 if (left == lower && -gap <= allowableOverlap) {
737 qreal offset = qMax(left->rightClear, right->leftClear);
738 offset = qMin(offset, left->width) - overlapShift;
739 lx = qMin(lx, right->x + offset);
740 return true;
741 }
742
743 // accidentals with even more overlap
744 // can work if both accidentals can subsume overlap
745 if (left == lower && -gap <= upper->descent + lower->ascent - pd) {
746 qreal offset = qMin(left->rightClear, right->leftClear) - overlapShift;
747 if (offset > 0.0) {
748 lx = qMin(lx, right->x + offset);
749 return true;
750 }
751 }
752
753 // otherwise, there is real conflict
754 lx = qMin(lx, right->x - pd);
755 return true;
756 }
757
758 //---------------------------------------------------------
759 // layoutAccidental
760 //---------------------------------------------------------
761
layoutAccidental(AcEl * me,AcEl * above,AcEl * below,qreal colOffset,QVector<Note * > & leftNotes,qreal pnd,qreal pd,qreal sp)762 static QPair<qreal, qreal> layoutAccidental(AcEl* me, AcEl* above, AcEl* below, qreal colOffset, QVector<Note*>& leftNotes, qreal pnd, qreal pd, qreal sp)
763 {
764 qreal lx = colOffset;
765 Accidental* acc = me->note->accidental();
766 qreal mag = acc->mag();
767 pnd *= mag;
768 pd *= mag;
769
770 Chord* chord = me->note->chord();
771 Staff* staff = chord->staff();
772 Fraction tick = chord->tick();
773
774 // extra space for ledger lines
775 qreal ledgerAdjust = 0.0;
776 qreal ledgerVerticalClear = 0.0;
777 bool ledgerAbove = chord->upNote()->line() <= -2;
778 bool ledgerBelow = chord->downNote()->line() >= staff->lines(tick) * 2;
779 if (ledgerAbove || ledgerBelow) {
780 // ledger lines are present
781 // check for collision with lines above & below staff
782 // note that on 1-line staff, both collisions are possible at once
783 // TODO: account for cutouts in accidental
784 qreal lds = staff->lineDistance(tick) * sp;
785 if ((ledgerAbove && me->top + lds <= pnd) || (ledgerBelow && staff->lines(tick) * lds - me->bottom <= pnd)) {
786 ledgerAdjust = -acc->score()->styleS(Sid::ledgerLineLength).val() * sp;
787 ledgerVerticalClear = acc->score()->styleS(Sid::ledgerLineWidth).val() * 0.5 * sp;
788 lx = qMin(lx, ledgerAdjust);
789 }
790 }
791
792 // clear left notes
793 int lns = leftNotes.size();
794 for (int i = 0; i < lns; ++i) {
795 Note* ln = leftNotes[i];
796 int lnLine = ln->line();
797 qreal lnTop = (lnLine - 1) * 0.5 * sp;
798 qreal lnBottom = lnTop + sp;
799 if (me->top - lnBottom <= pnd && lnTop - me->bottom <= pnd) {
800 qreal lnLedgerAdjust = 0.0;
801 if (lnLine <= -2 || lnLine >= staff->lines(tick) * 2) {
802 // left note has a ledger line we probably need to clear horizontally as well
803 // except for accidentals that clear the last extended ledger line vertically
804 // in these cases, the accidental may tuck closer
805 Note* lastLnNote = lnLine < 0 ? leftNotes[0] : leftNotes[lns - 1];
806 int lastLnLine = lastLnNote->line();
807 qreal ledgerY = (lastLnLine / 2) * sp;
808 if (me->line < 0 && ledgerY - me->bottom < ledgerVerticalClear)
809 lnLedgerAdjust = ledgerAdjust;
810 else if (me->line > 0 && me->top - ledgerY < ledgerVerticalClear)
811 lnLedgerAdjust = ledgerAdjust;
812 }
813 // undercut note above if possible
814 if (lnBottom - me->top <= me->ascent - pnd)
815 lx = qMin(lx, ln->x() + ln->chord()->x() + lnLedgerAdjust + me->rightClear);
816 else
817 lx = qMin(lx, ln->x() + ln->chord()->x() + lnLedgerAdjust);
818 }
819 else if (lnTop > me->bottom)
820 break;
821 }
822
823 // clear other accidentals
824 bool conflictAbove = false;
825 bool conflictBelow = false;
826
827 if (above)
828 conflictAbove = resolveAccidentals(me, above, lx, pd, sp);
829 if (below)
830 conflictBelow = resolveAccidentals(me, below, lx, pd, sp);
831 if (conflictAbove || conflictBelow)
832 me->x = lx - acc->width() - acc->bbox().x();
833 else if (colOffset != 0.0)
834 me->x = lx - pd - acc->width() - acc->bbox().x();
835 else
836 me->x = lx - pnd - acc->width() - acc->bbox().x();
837
838 return QPair<qreal, qreal> (me->x, me->x + me->width);
839 }
840
841 //---------------------------------------------------------
842 // layoutChords3
843 // - calculate positions of notes, accidentals, dots
844 //---------------------------------------------------------
845
layoutChords3(std::vector<Note * > & notes,const Staff * staff,Segment * segment)846 void Score::layoutChords3(std::vector<Note*>& notes, const Staff* staff, Segment* segment)
847 {
848 //---------------------------------------------------
849 // layout accidentals
850 // find column for dots
851 //---------------------------------------------------
852
853 QVector<Note*> leftNotes; // notes to left of origin
854 leftNotes.reserve(8);
855 QVector<AcEl> aclist; // accidentals
856 aclist.reserve(8);
857
858 // track columns of octave-separated accidentals
859 int columnBottom[7] = { -1, -1, -1, -1, -1, -1, -1 };
860
861 Fraction tick = notes.front()->chord()->segment()->tick();
862 qreal sp = staff->spatium(tick);
863 qreal stepDistance = sp * staff->lineDistance(tick) * .5;
864 int stepOffset = staff->staffType(tick)->stepOffset();
865
866 qreal lx = 10000.0; // leftmost notehead position
867 qreal upDotPosX = 0.0;
868 qreal downDotPosX = 0.0;
869
870 int nNotes = int(notes.size());
871 int nAcc = 0;
872 for (int i = nNotes-1; i >= 0; --i) {
873 Note* note = notes[i];
874 Accidental* ac = note->accidental();
875 if (ac && !note->fixed()) {
876 ac->layout();
877 if (!ac->visible()) {
878 ac->setPos(ac->bbox().x() - ac->width(), 0.0);
879 }
880 else {
881 AcEl acel;
882 acel.note = note;
883 int line = note->line();
884 acel.line = line;
885 acel.x = 0.0;
886 acel.top = line * 0.5 * sp + ac->bbox().top();
887 acel.bottom = line * 0.5 * sp + ac->bbox().bottom();
888 acel.width = ac->width();
889 QPointF bboxNE = ac->symBbox(ac->symbol()).topRight();
890 QPointF bboxSW = ac->symBbox(ac->symbol()).bottomLeft();
891 QPointF cutOutNE = ac->symCutOutNE(ac->symbol());
892 QPointF cutOutSW = ac->symCutOutSW(ac->symbol());
893 if (!cutOutNE.isNull()) {
894 acel.ascent = cutOutNE.y() - bboxNE.y();
895 acel.rightClear = bboxNE.x() - cutOutNE.x();
896 }
897 else {
898 acel.ascent = 0.0;
899 acel.rightClear = 0.0;
900 }
901 if (!cutOutSW.isNull()) {
902 acel.descent = bboxSW.y() - cutOutSW.y();
903 acel.leftClear = cutOutSW.x() - bboxSW.x();
904 }
905 else {
906 acel.descent = 0.0;
907 acel.leftClear = 0.0;
908 }
909 int pitchClass = (line + 700) % 7;
910 acel.next = columnBottom[pitchClass];
911 columnBottom[pitchClass] = nAcc;
912 aclist.append(acel);
913 ++nAcc;
914 }
915 }
916
917 Chord* chord = note->chord();
918 bool _up = chord->up();
919
920 if (chord->stemSlash())
921 chord->stemSlash()->layout();
922
923 qreal overlapMirror;
924 Stem* stem = chord->stem();
925 if (stem)
926 overlapMirror = stem->lineWidth();
927 else if (chord->durationType().headType() == NoteHead::Type::HEAD_WHOLE)
928 overlapMirror = styleP(Sid::stemWidth) * chord->mag();
929 else
930 overlapMirror = 0.0;
931
932 qreal x = 0.0;
933 if (note->mirror())
934 if (_up)
935 x = chord->stemPosX() - overlapMirror;
936 else
937 x = -note->headBodyWidth() + overlapMirror;
938 else if (_up)
939 x = chord->stemPosX() - note->headBodyWidth();
940
941 qreal ny = (note->line() + stepOffset) * stepDistance;
942 if (note->rypos() != ny) {
943 note->rypos() = ny;
944 if (chord->stem()) {
945 chord->stem()->layout();
946 if (chord->hook())
947 chord->hook()->rypos() = chord->stem()->hookPos().y();
948 }
949 }
950 note->rxpos() = x;
951
952 // find leftmost non-mirrored note to set as X origin for accidental layout
953 // a mirrored note that extends to left of segment X origin
954 // will displace accidentals only if there is conflict
955 qreal sx = x + chord->x(); // segment-relative X position of note
956 if (note->mirror() && !chord->up() && sx < 0.0)
957 leftNotes.append(note);
958 else if (sx < lx)
959 lx = sx;
960
961 qreal xx = x + note->headBodyWidth() + chord->pos().x();
962
963 Direction dotPosition = note->userDotPosition();
964 if (chord->dots()) {
965 if (chord->up())
966 upDotPosX = qMax(upDotPosX, xx);
967 else {
968 downDotPosX = qMax(downDotPosX, xx);
969 }
970
971 if (dotPosition == Direction::AUTO && nNotes > 1 && note->visible() && !note->dotsHidden()) {
972 // resolve dot conflicts
973 int line = note->line();
974 Note* above = (i < nNotes - 1) ? notes[i+1] : 0;
975 if (above && (!above->visible() || above->dotsHidden()))
976 above = 0;
977 int intervalAbove = above ? line - above->line() : 1000;
978 Note* below = (i > 0) ? notes[i-1] : 0;
979 if (below && (!below->visible() || below->dotsHidden()))
980 below = 0;
981 int intervalBelow = below ? below->line() - line : 1000;
982 if ((line & 1) == 0) {
983 // line
984 if (intervalAbove == 1 && intervalBelow != 1)
985 dotPosition = Direction::DOWN;
986 else if (intervalBelow == 1 && intervalAbove != 1)
987 dotPosition = Direction::UP;
988 else if (intervalAbove == 0 && above->chord()->dots()) {
989 // unison
990 if (((above->voice() & 1) == (note->voice() & 1))) {
991 above->setDotY(Direction::UP);
992 dotPosition = Direction::DOWN;
993 }
994 }
995 }
996 else {
997 // space
998 if (intervalAbove == 0 && above->chord()->dots()) {
999 // unison
1000 if (!(note->voice() & 1))
1001 dotPosition = Direction::UP;
1002 else {
1003 if (!(above->voice() & 1))
1004 above->setDotY(Direction::UP);
1005 else
1006 dotPosition = Direction::DOWN;
1007 }
1008 }
1009 }
1010 }
1011 }
1012 note->setDotY(dotPosition); // also removes invalid dots
1013 }
1014
1015 // if there are no non-mirrored notes in a downstem chord,
1016 // then use the stem X position as X origin for accidental layout
1017 if (nNotes && leftNotes.size() == nNotes)
1018 lx = notes.front()->chord()->stemPosX();
1019
1020 if (segment) {
1021 // align all dots for segment/staff
1022 // it would be possible to dots for up & down chords separately
1023 // this would require space to have been allocated previously
1024 // when calculating chord offsets
1025 segment->setDotPosX(staff->idx(), qMax(upDotPosX, downDotPosX));
1026 }
1027
1028 if (nAcc == 0)
1029 return;
1030
1031 QVector<int> umi;
1032 qreal pd = styleP(Sid::accidentalDistance);
1033 qreal pnd = styleP(Sid::accidentalNoteDistance);
1034 qreal colOffset = 0.0;
1035
1036 if (nAcc >= 2 && aclist[nAcc-1].line - aclist[0].line >= 7) {
1037
1038 // accidentals spread over an octave or more
1039 // set up columns for accidentals with octave matches
1040 // these will start at right and work to the left
1041 // unmatched accidentals will use zig zag approach (see below)
1042 // starting to the left of the octave columns
1043
1044 int columnTop[7] = { -1, -1, -1, -1, -1, -1, -1 };
1045
1046 // find columns of octaves
1047 for (int pc = 0; pc < 7; ++pc) {
1048 if (columnBottom[pc] == -1)
1049 continue;
1050 // calculate column height
1051 for (int j = columnBottom[pc]; j != -1; j = aclist[j].next)
1052 columnTop[pc] = j;
1053 }
1054
1055 // compute reasonable column order
1056 // use zig zag
1057 QVector<int> column;
1058 QVector<int> unmatched;
1059 int n = nAcc - 1;
1060 for (int i = 0; i <= n; ++i, --n) {
1061 int pc = (aclist[i].line + 700) % 7;
1062 if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
1063 if (!column.contains(pc))
1064 column.append(pc);
1065 }
1066 else
1067 unmatched.append(i);
1068 if (i == n)
1069 break;
1070 pc = (aclist[n].line + 700) % 7;
1071 if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
1072 if (!column.contains(pc))
1073 column.append(pc);
1074 }
1075 else
1076 unmatched.append(n);
1077 }
1078 int nColumns = column.size();
1079 int nUnmatched = unmatched.size();
1080
1081 // handle unmatched accidentals
1082 for (int i = 0; i < nUnmatched; ++i) {
1083 // first try to slot it into an existing column
1084 AcEl* me = &aclist[unmatched[i]];
1085 // find column
1086 bool found = false;
1087 for (int j = 0; j < nColumns; ++j) {
1088 int pc = column[j];
1089 int above = -1;
1090 int below = -1;
1091 // find slot within column
1092 for (int k = columnBottom[pc]; k != -1; k = aclist[k].next) {
1093 if (aclist[k].line < me->line) {
1094 above = k;
1095 break;
1096 }
1097 below = k;
1098 }
1099 // check to see if accidental can fit in slot
1100 qreal myPd = pd * me->note->accidental()->mag();
1101 bool conflict = false;
1102 if (above != -1 && me->top - aclist[above].bottom < myPd)
1103 conflict = true;
1104 else if (below != -1 && aclist[below].top - me->bottom < myPd)
1105 conflict = true;
1106 if (!conflict) {
1107 // insert into column
1108 found = true;
1109 me->next = above;
1110 if (above == -1)
1111 columnTop[pc] = unmatched[i];
1112 if (below != -1)
1113 aclist[below].next = unmatched[i];
1114 else
1115 columnBottom[pc] = unmatched[i];
1116 break;
1117 }
1118 }
1119 // if no slot found, then add to list of unmatched accidental indices
1120 if (!found)
1121 umi.push_back(unmatched[i]);
1122 }
1123 nAcc = umi.size();
1124 if (nAcc > 1)
1125 std::sort(umi.begin(), umi.end());
1126
1127 bool alignLeft = score()->styleB(Sid::alignAccidentalsLeft);
1128
1129 // through columns
1130 for (int i = 0; i < nColumns; ++i) {
1131 // column index
1132 const int pc = column[i];
1133
1134 qreal minX = 0.0;
1135 qreal maxX = 0.0;
1136
1137 AcEl* below = 0;
1138 // through accidentals in this column
1139 for (int j = columnBottom[pc]; j != -1; j = aclist[j].next) {
1140 QPair<qreal, qreal> x = layoutAccidental(&aclist[j], 0, below, colOffset, leftNotes, pnd, pd, sp);
1141 minX = qMin(minX, x.first);
1142 maxX = qMin(maxX, x.second);
1143 below = &aclist[j];
1144 }
1145
1146 // align
1147 int next = -1;
1148 for (int j = columnBottom[pc]; j != -1; j = next) {
1149 AcEl* current = &aclist[j];
1150 next = current->next;
1151 if (next != -1 && current->line == aclist[next].line)
1152 continue;
1153
1154 if (alignLeft)
1155 current->x = minX;
1156 else
1157 current->x = maxX - current->width;
1158 }
1159 colOffset = minX;
1160 }
1161 }
1162
1163 else {
1164 for (int i = 0; i < nAcc; ++i)
1165 umi.push_back(i);
1166 }
1167
1168 if (nAcc) {
1169 // for accidentals with no octave matches, use zig zag approach
1170 // layout right to left in pairs, (next) highest then lowest
1171
1172 AcEl* me = &aclist[umi[0]];
1173 AcEl* above = 0;
1174 AcEl* below = 0;
1175
1176 // layout top accidental
1177 layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
1178
1179 // layout bottom accidental
1180 int n = nAcc - 1;
1181 if (n > 0) {
1182 above = me;
1183 me = &aclist[umi[n]];
1184 layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
1185 }
1186
1187 // layout middle accidentals
1188 if (n > 1) {
1189 for (int i = 1; i < n; ++i, --n) {
1190 // next highest
1191 below = me;
1192 me = &aclist[umi[i]];
1193 layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
1194 if (i == n - 1)
1195 break;
1196 // next lowest
1197 above = me;
1198 me = &aclist[umi[n-1]];
1199 layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
1200 }
1201 }
1202 }
1203
1204 for (const AcEl& e : qAsConst(aclist)) {
1205 // even though we initially calculate accidental position relative to segment
1206 // we must record pos for accidental relative to note,
1207 // since pos is always interpreted relative to parent
1208 Note* note = e.note;
1209 qreal x = e.x + lx - (note->x() + note->chord()->x());
1210 note->accidental()->setPos(x, 0);
1211 }
1212 }
1213
1214 #define beamModeMid(a) (a == Beam::Mode::MID || a == Beam::Mode::BEGIN32 || a == Beam::Mode::BEGIN64)
1215
beamNoContinue(Beam::Mode mode)1216 bool beamNoContinue(Beam::Mode mode)
1217 {
1218 return mode == Beam::Mode::END || mode == Beam::Mode::NONE || mode == Beam::Mode::INVALID;
1219 }
1220
1221 //---------------------------------------------------------
1222 // beamGraceNotes
1223 //---------------------------------------------------------
1224
beamGraceNotes(Chord * mainNote,bool after)1225 void Score::beamGraceNotes(Chord* mainNote, bool after)
1226 {
1227 ChordRest* a1 = 0; // start of (potential) beam
1228 Beam* beam = 0; // current beam
1229 Beam::Mode bm = Beam::Mode::AUTO;
1230 QVector<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();
1231
1232 for (ChordRest* cr : qAsConst(graceNotes)) {
1233 bm = Groups::endBeam(cr);
1234 if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
1235 if (beam) {
1236 beam->layoutGraceNotes();
1237 beam = 0;
1238 }
1239 if (a1) {
1240 a1->removeDeleteBeam(false);
1241 a1 = 0;
1242 }
1243 cr->removeDeleteBeam(false);
1244 continue;
1245 }
1246 if (beam) {
1247 bool beamEnd = bm == Beam::Mode::BEGIN;
1248 if (!beamEnd) {
1249 cr->replaceBeam(beam);
1250 cr = 0;
1251 beamEnd = (bm == Beam::Mode::END);
1252 }
1253 if (beamEnd) {
1254 beam->layoutGraceNotes();
1255 beam = 0;
1256 }
1257 }
1258 if (!cr)
1259 continue;
1260 if (a1 == 0)
1261 a1 = cr;
1262 else {
1263 if (!beamModeMid(bm) && (bm == Beam::Mode::BEGIN)) {
1264 a1->removeDeleteBeam(false);
1265 a1 = cr;
1266 }
1267 else {
1268 beam = a1->beam();
1269 if (beam == 0 || beam->elements().front() != a1) {
1270 beam = new Beam(this);
1271 beam->setGenerated(true);
1272 beam->setTrack(mainNote->track());
1273 a1->replaceBeam(beam);
1274 }
1275 cr->replaceBeam(beam);
1276 a1 = 0;
1277 }
1278 }
1279 }
1280 if (beam)
1281 beam->layoutGraceNotes();
1282 else if (a1)
1283 a1->removeDeleteBeam(false);
1284 }
1285
1286 #if 0 // unused
1287 //---------------------------------------------------------
1288 // layoutSpanner
1289 // called after dragging a staff
1290 //---------------------------------------------------------
1291
1292 void Score::layoutSpanner()
1293 {
1294 int tracks = ntracks();
1295 for (int track = 0; track < tracks; ++track) {
1296 for (Segment* segment = firstSegment(SegmentType::All); segment; segment = segment->next1()) {
1297 if (track == tracks-1) {
1298 size_t n = segment->annotations().size();
1299 for (size_t i = 0; i < n; ++i)
1300 segment->annotations().at(i)->layout();
1301 }
1302 Element* e = segment->element(track);
1303 if (e && e->isChord()) {
1304 Chord* c = toChord(segment->element(track));
1305 c->layoutStem();
1306 for (Note* n : c->notes()) {
1307 Tie* tie = n->tieFor();
1308 if (tie)
1309 tie->layout();
1310 for (Spanner* sp : n->spannerFor())
1311 sp->layout();
1312 }
1313 }
1314 }
1315 }
1316 rebuildBspTree();
1317 }
1318 #endif
1319
1320 //---------------------------------------------------------
1321 // hideEmptyStaves
1322 //---------------------------------------------------------
1323
hideEmptyStaves(System * system,bool isFirstSystem)1324 void Score::hideEmptyStaves(System* system, bool isFirstSystem)
1325 {
1326 int staves = _staves.size();
1327 int staffIdx = 0;
1328 bool systemIsEmpty = true;
1329
1330 for (Staff* staff : qAsConst(_staves)) {
1331 SysStaff* ss = system->staff(staffIdx);
1332
1333 Staff::HideMode hideMode = staff->hideWhenEmpty();
1334
1335 if (hideMode == Staff::HideMode::ALWAYS
1336 || (styleB(Sid::hideEmptyStaves)
1337 && (staves > 1)
1338 && !(isFirstSystem && styleB(Sid::dontHideStavesInFirstSystem))
1339 && hideMode != Staff::HideMode::NEVER)) {
1340 bool hideStaff = true;
1341 for (MeasureBase* m : system->measures()) {
1342 if (!m->isMeasure())
1343 continue;
1344 Measure* measure = toMeasure(m);
1345 if (!measure->isEmpty(staffIdx)) {
1346 hideStaff = false;
1347 break;
1348 }
1349 }
1350 // check if notes moved into this staff
1351 Part* part = staff->part();
1352 int n = part->nstaves();
1353 if (hideStaff && (n > 1)) {
1354 int idx = part->staves()->front()->idx();
1355 for (int i = 0; i < part->nstaves(); ++i) {
1356 int st = idx + i;
1357
1358 for (MeasureBase* mb : system->measures()) {
1359 if (!mb->isMeasure())
1360 continue;
1361 Measure* m = toMeasure(mb);
1362 if (staff->hideWhenEmpty() == Staff::HideMode::INSTRUMENT && !m->isEmpty(st)) {
1363 hideStaff = false;
1364 break;
1365 }
1366 for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
1367 for (int voice = 0; voice < VOICES; ++voice) {
1368 ChordRest* cr = s->cr(st * VOICES + voice);
1369 if (cr == 0 || cr->isRest())
1370 continue;
1371 int staffMove = cr->staffMove();
1372 if (staffIdx == st + staffMove) {
1373 hideStaff = false;
1374 break;
1375 }
1376 }
1377 }
1378 if (!hideStaff)
1379 break;
1380 }
1381 if (!hideStaff)
1382 break;
1383 }
1384 }
1385 ss->setShow(hideStaff ? false : staff->show());
1386 if (ss->show())
1387 systemIsEmpty = false;
1388 }
1389 else if (!staff->show()) {
1390 // TODO: OK to check this first and not bother with checking if empty?
1391 ss->setShow(false);
1392 }
1393 else {
1394 systemIsEmpty = false;
1395 ss->setShow(true);
1396 }
1397
1398 ++staffIdx;
1399 }
1400 Staff* firstVisible = nullptr;
1401 if (systemIsEmpty) {
1402 for (Staff* staff : qAsConst(_staves)) {
1403 SysStaff* ss = system->staff(staff->idx());
1404 if (staff->showIfEmpty() && !ss->show()) {
1405 ss->setShow(true);
1406 systemIsEmpty = false;
1407 }
1408 else if (!firstVisible && staff->show()) {
1409 firstVisible = staff;
1410 }
1411 }
1412 }
1413 // don’t allow a complete empty system
1414 if (systemIsEmpty) {
1415 Staff* staff = firstVisible ? firstVisible : _staves.front();
1416 SysStaff* ss = system->staff(staff->idx());
1417 ss->setShow(true);
1418 }
1419 }
1420
1421 //---------------------------------------------------------
1422 // connectTies
1423 /// Rebuild tie connections.
1424 //---------------------------------------------------------
1425
connectTies(bool silent)1426 void Score::connectTies(bool silent)
1427 {
1428 int tracks = nstaves() * VOICES;
1429 Measure* m = firstMeasure();
1430 if (!m)
1431 return;
1432
1433 SegmentType st = SegmentType::ChordRest;
1434 for (Segment* s = m->first(st); s; s = s->next1(st)) {
1435 for (int i = 0; i < tracks; ++i) {
1436 Element* e = s->element(i);
1437 if (e == 0 || !e->isChord())
1438 continue;
1439 Chord* c = toChord(e);
1440 for (Note* n : c->notes()) {
1441 // connect a tie without end note
1442 Tie* tie = n->tieFor();
1443 if (tie && !tie->endNote()) {
1444 Note* nnote;
1445 if (_mscVersion <= 114)
1446 nnote = searchTieNote114(n);
1447 else
1448 nnote = searchTieNote(n);
1449 if (nnote == 0) {
1450 if (!silent) {
1451 qDebug("next note at %d track %d for tie not found (version %d)", s->tick().ticks(), i, _mscVersion);
1452 delete tie;
1453 n->setTieFor(0);
1454 }
1455 }
1456 else {
1457 tie->setEndNote(nnote);
1458 nnote->setTieBack(tie);
1459 }
1460 }
1461 // connect a glissando without initial note (old glissando format)
1462 for (Spanner* spanner : n->spannerBack()) {
1463 if (spanner->isGlissando() && !spanner->startElement()) {
1464 Note* initialNote = Glissando::guessInitialNote(n->chord());
1465 n->removeSpannerBack(spanner);
1466 if (initialNote) {
1467 spanner->setStartElement(initialNote);
1468 spanner->setEndElement(n);
1469 spanner->setTick(initialNote->chord()->tick());
1470 spanner->setTick2(n->chord()->tick());
1471 spanner->setTrack(n->track());
1472 spanner->setTrack2(n->track());
1473 spanner->setParent(initialNote);
1474 initialNote->add(spanner);
1475 }
1476 else {
1477 delete spanner;
1478 }
1479 }
1480 }
1481 // spanner with no end element can happen during copy/paste
1482 for (Spanner* spanner : n->spannerFor()) {
1483 if (spanner->endElement() == nullptr) {
1484 n->removeSpannerFor(spanner);
1485 delete spanner;
1486 }
1487 }
1488 }
1489 #if 0 // chords are set in tremolo->layout()
1490 // connect two note tremolos
1491 Tremolo* tremolo = c->tremolo();
1492 if (tremolo && tremolo->twoNotes() && !tremolo->chord2()) {
1493 for (Segment* ls = s->next1(st); ls; ls = ls->next1(st)) {
1494 Element* element = ls->element(i);
1495 if (!element)
1496 continue;
1497 if (!element->isChord())
1498 qDebug("cannot connect tremolo");
1499 else {
1500 Chord* nc = toChord(element);
1501 nc->setTremolo(tremolo);
1502 tremolo->setChords(c, nc);
1503 // cross-measure tremolos are not supported
1504 // but can accidentally result from copy & paste
1505 // remove them now
1506 if (c->measure() != nc->measure())
1507 c->remove(tremolo);
1508 }
1509 break;
1510 }
1511 }
1512 #endif
1513 }
1514 }
1515 }
1516
1517 //---------------------------------------------------------
1518 // checkDivider
1519 //---------------------------------------------------------
1520
checkDivider(bool left,System * s,qreal yOffset,bool remove=false)1521 static void checkDivider(bool left, System* s, qreal yOffset, bool remove = false)
1522 {
1523 SystemDivider* divider = left ? s->systemDividerLeft() : s->systemDividerRight();
1524 if ((s->score()->styleB(left ? Sid::dividerLeft : Sid::dividerRight)) && !remove) {
1525 if (!divider) {
1526 divider = new SystemDivider(s->score());
1527 divider->setDividerType(left ? SystemDivider::Type::LEFT : SystemDivider::Type::RIGHT);
1528 divider->setGenerated(true);
1529 s->add(divider);
1530 }
1531 divider->layout();
1532 divider->rypos() = divider->height() * .5 + yOffset;
1533 if (left) {
1534 divider->rypos() += s->score()->styleD(Sid::dividerLeftY) * SPATIUM20;
1535 divider->rxpos() = s->score()->styleD(Sid::dividerLeftX) * SPATIUM20;
1536 }
1537 else {
1538 divider->rypos() += s->score()->styleD(Sid::dividerRightY) * SPATIUM20;
1539 divider->rxpos() = s->score()->styleD(Sid::pagePrintableWidth) * DPI - divider->width();
1540 divider->rxpos() += s->score()->styleD(Sid::dividerRightX) * SPATIUM20;
1541 }
1542 }
1543 else if (divider) {
1544 if (divider->generated()) {
1545 s->remove(divider);
1546 delete divider;
1547 }
1548 else
1549 s->score()->undoRemoveElement(divider);
1550 }
1551 }
1552
1553 //---------------------------------------------------------
1554 // almostZero
1555 //---------------------------------------------------------
1556
almostZero(qreal value)1557 bool inline almostZero(qreal value)
1558 {
1559 // 1e-3 is close enough to zero to see it as zero.
1560 return value > -1e-3 && value < 1e-3;
1561 }
1562
1563 //---------------------------------------------------------
1564 // distributeStaves
1565 //---------------------------------------------------------
1566
distributeStaves(Page * page)1567 static void distributeStaves(Page* page)
1568 {
1569 Score* score { page->score() };
1570 VerticalGapDataList vgdl;
1571
1572 // Find and classify all gaps between staves.
1573 int ngaps { 0 };
1574 qreal prevYBottom { page->tm() };
1575 qreal yBottom { 0.0 };
1576 bool vbox { false };
1577 Spacer* fixedSpacer { nullptr };
1578 bool transferNormalBracket { false };
1579 bool transferCurlyBracket { false };
1580 for (System* system : page->systems()) {
1581 if (system->vbox()) {
1582 VerticalGapData* vgd = new VerticalGapData(!ngaps++, system, nullptr, nullptr, nullptr, prevYBottom);
1583 vgd->addSpaceAroundVBox(true);
1584 prevYBottom = system->y();
1585 yBottom = system->y() + system->height();
1586 vbox = true;
1587 vgdl.append(vgd);
1588 transferNormalBracket = false;
1589 transferCurlyBracket = false;
1590 }
1591 else {
1592 bool newSystem { true };
1593 bool addSpaceAroundNormalBracket { false };
1594 bool addSpaceAroundCurlyBracket { false };
1595 int endNormalBracket { -1 };
1596 int endCurlyBracket { -1 };
1597 int staffNr { -1 };
1598 for (SysStaff* sysStaff : *system->staves()) {
1599 Staff* staff { score->staff(++staffNr)};
1600 addSpaceAroundNormalBracket |= endNormalBracket == staffNr;
1601 addSpaceAroundCurlyBracket |= endCurlyBracket == staffNr;
1602 for (const BracketItem* bi : staff->brackets()) {
1603 if (bi->bracketType() == BracketType::NORMAL) {
1604 addSpaceAroundNormalBracket |= staff->idx() > (endNormalBracket - 1);
1605 endNormalBracket = qMax(endNormalBracket, staff->idx() + bi->bracketSpan());
1606 }
1607 else if(bi->bracketType() == BracketType::BRACE) {
1608 addSpaceAroundCurlyBracket |= staff->idx() > (endCurlyBracket - 1);
1609 endCurlyBracket = qMax(endCurlyBracket, staff->idx() + bi->bracketSpan());
1610 }
1611 }
1612
1613 if (!sysStaff->show())
1614 continue;
1615
1616 VerticalGapData* vgd = new VerticalGapData(!ngaps++, system, staff, sysStaff, fixedSpacer, prevYBottom);
1617 fixedSpacer = nullptr;
1618
1619 if (newSystem) {
1620 vgd->addSpaceBetweenSections();
1621 newSystem = false;
1622 }
1623 if (addSpaceAroundNormalBracket || transferNormalBracket) {
1624 vgd->addSpaceAroundNormalBracket();
1625 addSpaceAroundNormalBracket = false;
1626 transferNormalBracket = false;
1627 }
1628 if (addSpaceAroundCurlyBracket || transferCurlyBracket) {
1629 vgd->addSpaceAroundCurlyBracket();
1630 addSpaceAroundCurlyBracket = false;
1631 transferCurlyBracket = false;
1632 }
1633 else if (staffNr < endCurlyBracket) {
1634 vgd->insideCurlyBracket();
1635 }
1636
1637 if (vbox) {
1638 vgd->addSpaceAroundVBox(false);
1639 vbox = false;
1640 }
1641
1642 prevYBottom = system->y() + sysStaff->y() + sysStaff->bbox().height();
1643 yBottom = system->y() + sysStaff->y() + sysStaff->skyline().south().max();
1644 vgdl.append(vgd);
1645 }
1646 transferNormalBracket = endNormalBracket >= 0;
1647 transferCurlyBracket = endCurlyBracket >= 0;
1648 }
1649 fixedSpacer = system->getFixedSpacer();
1650 }
1651 --ngaps;
1652
1653 qreal spaceLeft { page->height() - page->bm() - score->styleP(Sid::staffLowerBorder) - yBottom };
1654 if (spaceLeft <= 0.0)
1655 return;
1656
1657 // Try to make the gaps equal, taking the spread factors and maximum spacing into account.
1658 static const int maxPasses { 20 }; // Saveguard to prevent endless loops.
1659 int pass { 0 };
1660 while (!almostZero(spaceLeft) && (ngaps > 0) && (++pass < maxPasses)) {
1661 ngaps = 0;
1662 qreal smallest { vgdl.smallest() };
1663 qreal nextSmallest { vgdl.smallest(smallest) };
1664 if (almostZero(smallest) || almostZero(nextSmallest))
1665 break;
1666
1667 if ((nextSmallest - smallest) * vgdl.sumStretchFactor() > spaceLeft)
1668 nextSmallest = smallest + spaceLeft/vgdl.sumStretchFactor();
1669
1670 qreal addedSpace { 0.0 };
1671 VerticalGapDataList modified;
1672 for (VerticalGapData* vgd : vgdl) {
1673 if (!almostZero(vgd->spacing() - smallest))
1674 continue;
1675 qreal step { nextSmallest - vgd->spacing() };
1676 if (step < 0.0)
1677 continue;
1678 step = vgd->addSpacing(step);
1679 if (!almostZero(step)) {
1680 addedSpace += step * vgd->factor();
1681 modified.append(vgd);
1682 ++ngaps;
1683 }
1684 if ((spaceLeft - addedSpace) <= 0.0)
1685 break;
1686 }
1687 if ((spaceLeft - addedSpace) <= 0.0)
1688 {
1689 for (VerticalGapData* vgd : modified)
1690 vgd->undoLastAddSpacing();
1691 ngaps = 0;
1692 }
1693 else {
1694 spaceLeft -= addedSpace;
1695 }
1696 }
1697
1698 // If there is still space left, distribute the space of the staves.
1699 const qreal maxPageFill { score->styleP(Sid::maxPageFillSpread) };
1700 pass = 0;
1701 ngaps = 1;
1702 while (!almostZero(spaceLeft) && !almostZero(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) {
1703 ngaps = 0;
1704 qreal addedSpace { 0.0 };
1705 for (VerticalGapData* vgd : vgdl) {
1706 qreal step = spaceLeft / vgdl.sumStretchFactor();
1707 step = vgd->addFillSpacing(step, maxPageFill);
1708 if (!almostZero(step)) {
1709 addedSpace += step * vgd->factor();
1710 ++ngaps;
1711 }
1712 }
1713 spaceLeft -= addedSpace;
1714 }
1715
1716 QSet<System*> systems;
1717 qreal systemShift { 0.0 };
1718 qreal staffShift { 0.0 };
1719 System* prvSystem { nullptr };
1720 for (VerticalGapData* vgd : vgdl) {
1721 if (vgd->sysStaff)
1722 systems.insert(vgd->system);
1723 systemShift += vgd->actualAddedSpace();
1724 if (prvSystem == vgd->system) {
1725 staffShift += vgd->actualAddedSpace();
1726 }
1727 else {
1728 vgd->system->rypos() += systemShift;
1729 if (prvSystem) {
1730 prvSystem->setDistance(vgd->system->y() - prvSystem->y());
1731 prvSystem->setHeight(prvSystem->height() + staffShift);
1732 }
1733 staffShift = 0.0;
1734 }
1735
1736 if (vgd->sysStaff)
1737 vgd->sysStaff->bbox().translate(0.0, staffShift);
1738
1739 prvSystem = vgd->system;
1740 }
1741 if (prvSystem) {
1742 prvSystem->setHeight(prvSystem->height() + staffShift);
1743 }
1744
1745 for (System* system : systems) {
1746 system->setMeasureHeight(system->height());
1747 system->layoutBracketsVertical();
1748 system->layoutInstrumentNames();
1749 }
1750 vgdl.deleteAll();
1751 }
1752
1753 //---------------------------------------------------------
1754 // layoutPage
1755 // restHeight - vertical space which has to be distributed
1756 // between systems
1757 // The algorithm tries to produce most equally spaced
1758 // systems.
1759 //---------------------------------------------------------
1760
layoutPage(Page * page,qreal restHeight)1761 static void layoutPage(Page* page, qreal restHeight)
1762 {
1763 if (restHeight < 0.0) {
1764 qDebug("restHeight < 0.0: %f\n", restHeight);
1765 restHeight = 0;
1766 }
1767
1768 Score* score = page->score();
1769 int gaps = page->systems().size() - 1;
1770
1771 QList<System*> sList;
1772
1773 // build list of systems (excluding last)
1774 // set initial distance for each to the unstretched minimum distance to next
1775 for (int i = 0; i < gaps; ++i) {
1776 System* s1 = page->systems().at(i);
1777 System* s2 = page->systems().at(i+1);
1778 s1->setDistance(s2->y() - s1->y());
1779 if (s1->vbox() || s2->vbox() || s1->getFixedSpacer()) {
1780 if (s2->vbox()) {
1781 checkDivider(true, s1, 0.0, true); // remove
1782 checkDivider(false, s1, 0.0, true); // remove
1783 checkDivider(true, s2, 0.0, true); // remove
1784 checkDivider(false, s2, 0.0, true); // remove
1785 }
1786 continue;
1787 }
1788 sList.push_back(s1);
1789 }
1790
1791 // last system needs no divider
1792 System* lastSystem = page->systems().back();
1793 checkDivider(true, lastSystem, 0.0, true); // remove
1794 checkDivider(false, lastSystem, 0.0, true); // remove
1795
1796 if (sList.empty() || MScore::noVerticalStretch || score->enableVerticalSpread() || score->layoutMode() == LayoutMode::SYSTEM) {
1797 if (score->layoutMode() == LayoutMode::FLOAT) {
1798 qreal y = restHeight * .5;
1799 for (System* system : page->systems())
1800 system->move(QPointF(0.0, y));
1801 }
1802 else if ((score->layoutMode() != LayoutMode::SYSTEM) && score->enableVerticalSpread())
1803 distributeStaves(page);
1804
1805 // system dividers
1806 for (int i = 0; i < gaps; ++i) {
1807 System* s1 = page->systems().at(i);
1808 System* s2 = page->systems().at(i+1);
1809 if (!(s1->vbox() || s2->vbox())) {
1810 qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
1811 checkDivider(true, s1, yOffset);
1812 checkDivider(false, s1, yOffset);
1813 }
1814 }
1815 return;
1816 }
1817
1818 qreal maxDist = score->maxSystemDistance();
1819
1820 // allocate space as needed to normalize system distance (bottom of one system to top of next)
1821 std::sort(sList.begin(), sList.end(), [](System* a, System* b) { return a->distance() - a->height() < b->distance() - b->height(); });
1822 System* s0 = sList[0];
1823 qreal dist = s0->distance() - s0->height(); // distance for shortest system
1824 for (int i = 1; i < sList.size(); ++i) {
1825 System* si = sList[i];
1826 qreal ndist = si->distance() - si->height(); // next taller system
1827 qreal fill = ndist - dist; // amount by which this system distance exceeds next shorter
1828 if (fill > 0.0) {
1829 qreal totalFill = fill * i; // space required to add this amount to all shorter systems
1830 if (totalFill > restHeight) {
1831 totalFill = restHeight; // too much; adjust amount
1832 fill = restHeight / i;
1833 }
1834 for (int k = 0; k < i; ++k) { // add amount to all shorter systems
1835 System* s = sList[k];
1836 qreal d = s->distance() + fill;
1837 if ((d - s->height()) > maxDist) // but don't exceed max system distance
1838 d = qMax(maxDist + s->height(), s->distance());
1839 s->setDistance(d);
1840 }
1841 restHeight -= totalFill; // reduce available space for next iteration
1842 if (restHeight <= 0)
1843 break; // no space left
1844 }
1845 dist = ndist; // set up for next iteration
1846 }
1847
1848 if (restHeight > 0.0) { // space left?
1849 qreal fill = restHeight / sList.size();
1850 for (System* s : qAsConst(sList)) { // allocate it to systems equally
1851 qreal d = s->distance() + fill;
1852 if ((d - s->height()) > maxDist) // but don't exceed max system distance
1853 d = qMax(maxDist + s->height(), s->distance());
1854 s->setDistance(d);
1855 }
1856 }
1857
1858 qreal y = page->systems().at(0)->y();
1859 for (int i = 0; i < gaps; ++i) {
1860 System* s1 = page->systems().at(i);
1861 System* s2 = page->systems().at(i+1);
1862 s1->rypos() = y;
1863 y += s1->distance();
1864
1865 if (!(s1->vbox() || s2->vbox())) {
1866 qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
1867 checkDivider(true, s1, yOffset);
1868 checkDivider(false, s1, yOffset);
1869 }
1870 }
1871 page->systems().back()->rypos() = y;
1872 }
1873
1874 //---------------------------------------------------------
1875 // Spring
1876 //---------------------------------------------------------
1877
1878 struct Spring {
1879 int seg;
1880 qreal stretch;
1881 qreal fix;
SpringMs::Spring1882 Spring(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
1883 };
1884
1885 typedef std::multimap<qreal, Spring, std::less<qreal> > SpringMap;
1886
1887 //---------------------------------------------------------
1888 // sff2
1889 // compute 1/Force for a given Extend
1890 //---------------------------------------------------------
1891
sff2(qreal width,qreal xMin,const SpringMap & springs)1892 static qreal sff2(qreal width, qreal xMin, const SpringMap& springs)
1893 {
1894 if (width <= xMin)
1895 return 0.0;
1896 auto i = springs.begin();
1897 qreal c = i->second.stretch;
1898 if (c == 0.0) //DEBUG
1899 c = 1.1;
1900 qreal f = 0.0;
1901 for (; i != springs.end();) {
1902 xMin -= i->second.fix;
1903 f = (width - xMin) / c;
1904 ++i;
1905 if (i == springs.end() || f <= i->first)
1906 break;
1907 c += i->second.stretch;
1908 }
1909 return f;
1910 }
1911
1912 //---------------------------------------------------------
1913 // respace
1914 //---------------------------------------------------------
1915
respace(std::vector<ChordRest * > * elements)1916 void Score::respace(std::vector<ChordRest*>* elements)
1917 {
1918 ChordRest* cr1 = elements->front();
1919 ChordRest* cr2 = elements->back();
1920 int n = int(elements->size());
1921 qreal x1 = cr1->segment()->pos().x();
1922 qreal x2 = cr2->segment()->pos().x();
1923
1924 #if (!defined (_MSCVER) && !defined (_MSC_VER))
1925 qreal width[n-1];
1926 int ticksList[n-1];
1927 #else
1928 // MSVC does not support VLA. Replace with std::vector. If profiling determines that the
1929 // heap allocation is slow, an optimization might be used.
1930 std::vector<qreal> width(n-1);
1931 std::vector<int> ticksList(n-1);
1932 #endif
1933 int minTick = 100000;
1934
1935 for (int i = 0; i < n-1; ++i) {
1936 ChordRest* cr = (*elements)[i];
1937 ChordRest* ncr = (*elements)[i+1];
1938 width[i] = cr->shape().minHorizontalDistance(ncr->shape());
1939 ticksList[i] = cr->ticks().ticks();
1940 minTick = qMin(ticksList[i], minTick);
1941 }
1942
1943 //---------------------------------------------------
1944 // compute stretches
1945 //---------------------------------------------------
1946
1947 SpringMap springs;
1948 qreal minimum = 0.0;
1949 for (int i = 0; i < n-1; ++i) {
1950 qreal w = width[i];
1951 int t = ticksList[i];
1952 qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
1953 qreal d = w / str;
1954
1955 springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
1956 minimum += w;
1957 }
1958
1959 //---------------------------------------------------
1960 // distribute stretch to elements
1961 //---------------------------------------------------
1962
1963 qreal force = sff2(x2 - x1, minimum, springs);
1964 for (auto i = springs.begin(); i != springs.end(); ++i) {
1965 qreal stretch = force * i->second.stretch;
1966 if (stretch < i->second.fix)
1967 stretch = i->second.fix;
1968 width[i->second.seg] = stretch;
1969 }
1970 qreal x = x1;
1971 for (int i = 1; i < n-1; ++i) {
1972 x += width[i-1];
1973 ChordRest* cr = (*elements)[i];
1974 qreal dx = x - cr->segment()->pos().x();
1975 cr->rxpos() += dx;
1976 }
1977 }
1978
1979 //---------------------------------------------------------
1980 // getNextPage
1981 //---------------------------------------------------------
1982
getNextPage()1983 void LayoutContext::getNextPage()
1984 {
1985 if (!page || curPage >= score->npages()) {
1986 page = new Page(score);
1987 score->pages().push_back(page);
1988 prevSystem = nullptr;
1989 pageOldMeasure = nullptr;
1990 }
1991 else {
1992 page = score->pages()[curPage];
1993 QList<System*>& systems = page->systems();
1994 pageOldMeasure = systems.isEmpty() ? nullptr : systems.back()->measures().back();
1995 const int i = systems.indexOf(curSystem);
1996 if (i > 0 && systems[i-1]->page() == page) {
1997 // Current and previous systems are on the current page.
1998 // Erase only the current and the following systems
1999 // as the previous one will not participate in layout.
2000 systems.erase(systems.begin() + i, systems.end());
2001 }
2002 else // system is not on the current page (or will be the first one)
2003 systems.clear();
2004 prevSystem = systems.empty() ? nullptr : systems.back();
2005 }
2006 page->bbox().setRect(0.0, 0.0, score->loWidth(), score->loHeight());
2007 page->setNo(curPage);
2008 qreal x = 0.0;
2009 qreal y = 0.0;
2010 if (curPage) {
2011 Page* prevPage = score->pages()[curPage - 1];
2012 if (MScore::verticalOrientation())
2013 y = prevPage->pos().y() + page->height() + MScore::verticalPageGap;
2014 else {
2015 qreal gap = (curPage + score->pageNumberOffset()) & 1 ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven;
2016 x = prevPage->pos().x() + page->width() + gap;
2017 }
2018 }
2019 ++curPage;
2020 page->setPos(x, y);
2021 }
2022
2023 //---------------------------------------------------------
2024 // getNextSystem
2025 //---------------------------------------------------------
2026
getNextSystem(LayoutContext & lc)2027 System* Score::getNextSystem(LayoutContext& lc)
2028 {
2029 bool isVBox = lc.curMeasure->isVBox();
2030 System* system;
2031 if (lc.systemList.empty()) {
2032 system = new System(this);
2033 lc.systemOldMeasure = 0;
2034 }
2035 else {
2036 system = lc.systemList.takeFirst();
2037 lc.systemOldMeasure = system->measures().empty() ? 0 : system->measures().back();
2038 system->clear(); // remove measures from system
2039 }
2040 _systems.append(system);
2041 if (!isVBox) {
2042 int nstaves = Score::nstaves();
2043 system->adjustStavesNumber(nstaves);
2044 }
2045 return system;
2046 }
2047
2048 //---------------------------------------------------------
2049 // createMMRest
2050 // create a multi measure rest from m to lm (inclusive)
2051 //---------------------------------------------------------
2052
createMMRest(Measure * m,Measure * lm,const Fraction & len)2053 void Score::createMMRest(Measure* m, Measure* lm, const Fraction& len)
2054 {
2055 int n = 1;
2056 if (m != lm) {
2057 for (Measure* mm = m->nextMeasure(); mm; mm = mm->nextMeasure()) {
2058 ++n;
2059 mm->setMMRestCount(-1);
2060 if (mm->mmRest())
2061 undo(new ChangeMMRest(mm, 0));
2062 if (mm == lm)
2063 break;
2064 }
2065 }
2066
2067 Measure* mmr = m->mmRest();
2068 if (mmr) {
2069 // reuse existing mmrest
2070 if (mmr->ticks() != len) {
2071 Segment* s = mmr->findSegmentR(SegmentType::EndBarLine, mmr->ticks());
2072 // adjust length
2073 mmr->setTicks(len);
2074 // move existing end barline
2075 if (s)
2076 s->setRtick(len);
2077 }
2078 mmr->removeSystemTrailer();
2079 }
2080 else {
2081 mmr = new Measure(this);
2082 mmr->setTicks(len);
2083 mmr->setTick(m->tick());
2084 undo(new ChangeMMRest(m, mmr));
2085 }
2086 mmr->setTimesig(m->timesig());
2087 mmr->setPageBreak(lm->pageBreak());
2088 mmr->setLineBreak(lm->lineBreak());
2089 mmr->setMMRestCount(n);
2090 mmr->setNo(m->no());
2091
2092 Segment* ss = lm->findSegmentR(SegmentType::EndBarLine, lm->ticks());
2093 if (ss) {
2094 Segment* ds = mmr->undoGetSegmentR(SegmentType::EndBarLine, mmr->ticks());
2095 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
2096 Element* e = ss->element(staffIdx * VOICES);
2097 if (e) {
2098 bool generated = e->generated();
2099 if (!ds->element(staffIdx * VOICES)) {
2100 Element* ee = generated ? e->clone() : e->linkedClone();
2101 ee->setGenerated(generated);
2102 ee->setParent(ds);
2103 undoAddElement(ee);
2104 }
2105 else {
2106 BarLine* bd = toBarLine(ds->element(staffIdx * VOICES));
2107 BarLine* bs = toBarLine(e);
2108 if (!generated && !bd->links())
2109 undo(new Link(bd, bs));
2110 if (bd->barLineType() != bs->barLineType()) {
2111 // change directly when generating mmrests, do not change underlying measures or follow links
2112 undo(new ChangeProperty(bd, Pid::BARLINE_TYPE, QVariant::fromValue(bs->barLineType()), PropertyFlags::NOSTYLE));
2113 undo(new ChangeProperty(bd, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
2114 }
2115 }
2116 }
2117 }
2118 }
2119
2120 Segment* clefSeg = lm->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef, lm->ticks());
2121 if (clefSeg) {
2122 Segment* mmrClefSeg = mmr->undoGetSegment(clefSeg->segmentType(), lm->endTick());
2123 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
2124 const int track = staff2track(staffIdx);
2125 Element* e = clefSeg->element(track);
2126 if (e && e->isClef()) {
2127 Clef* clef = toClef(e);
2128 if (!mmrClefSeg->element(track)) {
2129 Clef* mmrClef = clef->generated() ? clef->clone() : toClef(clef->linkedClone());
2130 mmrClef->setParent(mmrClefSeg);
2131 undoAddElement(mmrClef);
2132 }
2133 else {
2134 Clef* mmrClef = toClef(mmrClefSeg->element(track));
2135 mmrClef->setClefType(clef->clefType());
2136 mmrClef->setShowCourtesy(clef->showCourtesy());
2137 }
2138 }
2139 }
2140 }
2141
2142 mmr->setRepeatStart(m->repeatStart() || lm->repeatStart());
2143 mmr->setRepeatEnd(m->repeatEnd() || lm->repeatEnd());
2144 mmr->setSectionBreak(lm->sectionBreak());
2145
2146 ElementList oldList = mmr->takeElements();
2147 ElementList newList = lm->el();
2148
2149 for (Element* e : m->el()) {
2150 if (e->isMarker())
2151 newList.push_back(e);
2152 }
2153 for (Element* e : newList) {
2154 bool found = false;
2155 for (Element* ee : oldList) {
2156 if (ee->type() == e->type() && ee->subtype() == e->subtype()) {
2157 mmr->add(ee);
2158 auto i = std::find(oldList.begin(), oldList.end(), ee);
2159 if (i != oldList.end())
2160 oldList.erase(i);
2161 found = true;
2162 break;
2163 }
2164 }
2165 if (!found)
2166 mmr->add(e->clone());
2167 }
2168 for (Element* e : oldList)
2169 delete e;
2170 Segment* s = mmr->undoGetSegmentR(SegmentType::ChordRest, Fraction(0,1));
2171 for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2172 int track = staffIdx * VOICES;
2173 if (s->element(track) == 0) {
2174 Rest* r = new Rest(this);
2175 r->setDurationType(TDuration::DurationType::V_MEASURE);
2176 r->setTicks(mmr->ticks());
2177 r->setTrack(track);
2178 r->setParent(s);
2179 undo(new AddElement(r));
2180 }
2181 }
2182
2183 //
2184 // check for clefs
2185 //
2186 Segment* cs = lm->findSegmentR(SegmentType::Clef, lm->ticks());
2187 Segment* ns = mmr->findSegment(SegmentType::Clef, lm->endTick());
2188 if (cs) {
2189 if (ns == 0)
2190 ns = mmr->undoGetSegmentR(SegmentType::Clef, lm->ticks());
2191 ns->setEnabled(cs->enabled());
2192 ns->setTrailer(cs->trailer());
2193 for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2194 int track = staffIdx * VOICES;
2195 Clef* clef = toClef(cs->element(track));
2196 if (clef) {
2197 if (ns->element(track) == 0)
2198 ns->add(clef->clone());
2199 else {
2200 //TODO: check if same clef
2201 }
2202 }
2203 }
2204 }
2205 else if (ns) {
2206 // TODO: remove elements from ns?
2207 undo(new RemoveElement(ns));
2208 }
2209
2210 //
2211 // check for time signature
2212 //
2213 cs = m->findSegmentR(SegmentType::TimeSig, Fraction(0,1));
2214 ns = mmr->findSegment(SegmentType::TimeSig, m->tick());
2215 if (cs) {
2216 if (ns == 0)
2217 ns = mmr->undoGetSegmentR(SegmentType::TimeSig, Fraction(0,1));
2218 ns->setEnabled(cs->enabled());
2219 ns->setHeader(cs->header());
2220 for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2221 int track = staffIdx * VOICES;
2222 TimeSig* ts = toTimeSig(cs->element(track));
2223 if (ts) {
2224 TimeSig* nts = toTimeSig(ns->element(track));
2225 if (!nts) {
2226 nts = ts->generated() ? ts->clone() : toTimeSig(ts->linkedClone());
2227 nts->setParent(ns);
2228 undo(new AddElement(nts));
2229 }
2230 else {
2231 nts->setSig(ts->sig(), ts->timeSigType());
2232 nts->layout();
2233 }
2234 }
2235 }
2236 }
2237 else if (ns) {
2238 // TODO: remove elements from ns?
2239 undo(new RemoveElement(ns));
2240 }
2241
2242 //
2243 // check for ambitus
2244 //
2245 cs = m->findSegmentR(SegmentType::Ambitus, Fraction(0,1));
2246 ns = mmr->findSegment(SegmentType::Ambitus, m->tick());
2247 if (cs) {
2248 if (ns == 0)
2249 ns = mmr->undoGetSegmentR(SegmentType::Ambitus, Fraction(0,1));
2250 for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2251 int track = staffIdx * VOICES;
2252 Ambitus* a = toAmbitus(cs->element(track));
2253 if (a) {
2254 Ambitus* na = toAmbitus(ns->element(track));
2255 if (!na) {
2256 na = a->clone();
2257 na->setParent(ns);
2258 undo(new AddElement(na));
2259 }
2260 else {
2261 na->initFrom(a);
2262 na->layout();
2263 }
2264 }
2265 }
2266 }
2267 else if (ns) {
2268 // TODO: remove elements from ns?
2269 undo(new RemoveElement(ns));
2270 }
2271
2272 //
2273 // check for key signature
2274 //
2275 cs = m->findSegmentR(SegmentType::KeySig, Fraction(0,1));
2276 ns = mmr->findSegmentR(SegmentType::KeySig, Fraction(0,1));
2277 if (cs) {
2278 if (ns == 0)
2279 ns = mmr->undoGetSegmentR(SegmentType::KeySig, Fraction(0,1));
2280 ns->setEnabled(cs->enabled());
2281 ns->setHeader(cs->header());
2282 for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2283 int track = staffIdx * VOICES;
2284 KeySig* ks = toKeySig(cs->element(track));
2285 if (ks) {
2286 KeySig* nks = toKeySig(ns->element(track));
2287 if (!nks) {
2288 nks = ks->generated() ? ks->clone() : toKeySig(ks->linkedClone());
2289 nks->setParent(ns);
2290 nks->setGenerated(true);
2291 undo(new AddElement(nks));
2292 }
2293 else {
2294 if (!(nks->keySigEvent() == ks->keySigEvent())) {
2295 bool addKey = ks->isChange();
2296 undo(new ChangeKeySig(nks, ks->keySigEvent(), nks->showCourtesy(), addKey));
2297 }
2298 }
2299 }
2300 }
2301 }
2302 else if (ns) {
2303 ns->setEnabled(false);
2304 // TODO: remove elements from ns, then delete ns
2305 // previously we removed the segment if not empty,
2306 // but this resulted in "stale" keysig in mmrest after removed from underlying measure
2307 //undo(new RemoveElement(ns));
2308 }
2309
2310 mmr->checkHeader();
2311 mmr->checkTrailer();
2312
2313 //
2314 // check for rehearsal mark etc.
2315 //
2316 cs = m->findSegmentR(SegmentType::ChordRest, Fraction(0,1));
2317 if (cs) {
2318 // clone elements from underlying measure to mmr
2319 for (Element* e : cs->annotations()) {
2320 // look at elements in underlying measure
2321 if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()))
2322 continue;
2323 // try to find a match in mmr
2324 bool found = false;
2325 for (Element* ee : s->annotations()) {
2326 if (e->linkList().contains(ee)) {
2327 found = true;
2328 break;
2329 }
2330 }
2331 // add to mmr if no match found
2332 if (!found) {
2333 Element* ne = e->linkedClone();
2334 ne->setParent(s);
2335 undo(new AddElement(ne));
2336 }
2337 }
2338
2339 // remove stray elements (possibly leftover from a previous layout of this mmr)
2340 // this should not happen since the elements are linked?
2341 for (Element* e : s->annotations()) {
2342 // look at elements in mmr
2343 if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()))
2344 continue;
2345 // try to find a match in underlying measure
2346 bool found = false;
2347 for (Element* ee : cs->annotations()) {
2348 if (e->linkList().contains(ee)) {
2349 found = true;
2350 break;
2351 }
2352 }
2353 // remove from mmr if no match found
2354 if (!found)
2355 undo(new RemoveElement(e));
2356 }
2357 }
2358 MeasureBase* nm = _showVBox ? lm->next() : lm->nextMeasure();
2359 mmr->setNext(nm);
2360 mmr->setPrev(m->prev());
2361 }
2362
2363 //---------------------------------------------------------
2364 // validMMRestMeasure
2365 // return true if this might be a measure in a
2366 // multi measure rest
2367 //---------------------------------------------------------
2368
validMMRestMeasure(Measure * m)2369 static bool validMMRestMeasure(Measure* m)
2370 {
2371 if (m->irregular())
2372 return false;
2373
2374 int n = 0;
2375 for (Segment* s = m->first(); s; s = s->next()) {
2376 for (Element* e : s->annotations()) {
2377 if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()))
2378 return false;
2379 }
2380 if (s->isChordRestType()) {
2381 bool restFound = false;
2382 int tracks = m->score()->ntracks();
2383 for (int track = 0; track < tracks; ++track) {
2384 if ((track % VOICES) == 0 && !m->score()->staff(track/VOICES)->show()) {
2385 track += VOICES-1;
2386 continue;
2387 }
2388 if (s->element(track)) {
2389 if (!s->element(track)->isRest())
2390 return false;
2391 restFound = true;
2392 }
2393 }
2394 for (Element* e : s->annotations()) {
2395 if (e->isFermata())
2396 return false;
2397 }
2398 if (restFound)
2399 ++n;
2400 // measure is not empty if there is more than one rest
2401 if (n > 1)
2402 return false;
2403 }
2404 }
2405 return true;
2406 }
2407
2408 //---------------------------------------------------------
2409 // breakMultiMeasureRest
2410 // return true if this measure should start a new
2411 // multi measure rest
2412 //---------------------------------------------------------
2413
breakMultiMeasureRest(Measure * m)2414 static bool breakMultiMeasureRest(Measure* m)
2415 {
2416 if (m->breakMultiMeasureRest())
2417 return true;
2418
2419 if (m->repeatStart()
2420 || (m->prevMeasure() && m->prevMeasure()->repeatEnd())
2421 || (m->isIrregular())
2422 || (m->prevMeasure() && m->prevMeasure()->isIrregular())
2423 || (m->prevMeasure() && (m->prevMeasure()->sectionBreak())))
2424 return true;
2425
2426 auto sl = m->score()->spannerMap().findOverlapping(m->tick().ticks(), m->endTick().ticks());
2427 for (auto i : sl) {
2428 Spanner* s = i.value;
2429 // break for first measure of volta or textline and first measure *after* volta
2430 if ((s->isVolta() || s->isTextLine()) && (s->tick() == m->tick() || s->tick2() == m->tick()))
2431 return true;
2432 }
2433
2434 // break for marker in this measure
2435 for (Element* e : m->el()) {
2436 if (e->isMarker()) {
2437 Marker* mark = toMarker(e);
2438 if (!(mark->align() & Align::RIGHT))
2439 return true;
2440 }
2441 }
2442
2443 // break for marker & jump in previous measure
2444 Measure* pm = m->prevMeasure();
2445 if (pm) {
2446 for (Element* e : pm->el()) {
2447 if (e->isJump())
2448 return true;
2449 else if (e->isMarker()) {
2450 Marker* mark = toMarker(e);
2451 if (mark->align() & Align::RIGHT)
2452 return true;
2453 }
2454 }
2455 }
2456
2457 for (Segment* s = m->first(); s; s = s->next()) {
2458 for (Element* e : s->annotations()) {
2459 if (!e->visible())
2460 continue;
2461 if (e->isRehearsalMark() ||
2462 e->isTempoText() ||
2463 ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) && (e->systemFlag() || m->score()->staff(e->staffIdx())->show())))
2464 return true;
2465 }
2466 for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
2467 if (!m->score()->staff(staffIdx)->show())
2468 continue;
2469 Element* e = s->element(staffIdx * VOICES);
2470 if (!e || e->generated())
2471 continue;
2472 if (s->isStartRepeatBarLineType())
2473 return true;
2474 if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && m->tick().isNotZero())
2475 return true;
2476 if (s->isClefType()) {
2477 if (s->tick() != m->endTick() && m->tick().isNotZero())
2478 return true;
2479 }
2480 }
2481 }
2482 if (pm) {
2483 Segment* s = pm->findSegmentR(SegmentType::EndBarLine, pm->ticks());
2484 if (s) {
2485 for (int staffIdx = 0; staffIdx < s->score()->nstaves(); ++staffIdx) {
2486 BarLine* bl = toBarLine(s->element(staffIdx * VOICES));
2487 if (bl) {
2488 BarLineType t = bl->barLineType();
2489 if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated())
2490 return true;
2491 else
2492 break;
2493 }
2494 }
2495 }
2496 if (pm->findSegment(SegmentType::Clef, m->tick()))
2497 return true;
2498 }
2499 return false;
2500 }
2501
2502 //---------------------------------------------------------
2503 // adjustMeasureNo
2504 //---------------------------------------------------------
2505
adjustMeasureNo(MeasureBase * m)2506 int LayoutContext::adjustMeasureNo(MeasureBase* m)
2507 {
2508 measureNo += m->noOffset();
2509 m->setNo(measureNo);
2510 if (!m->irregular()) // don’t count measure
2511 ++measureNo;
2512 if (m->sectionBreakElement() && m->sectionBreakElement()->startWithMeasureOne())
2513 measureNo = 0;
2514 return measureNo;
2515 }
2516
2517 //---------------------------------------------------------
2518 // createBeams
2519 // helper function
2520 //---------------------------------------------------------
2521
createBeams(LayoutContext & lc,Measure * measure)2522 void Score::createBeams(LayoutContext& lc, Measure* measure)
2523 {
2524 bool crossMeasure = styleB(Sid::crossMeasureValues);
2525
2526 for (int track = 0; track < ntracks(); ++track) {
2527 Staff* stf = staff(track2staff(track));
2528
2529 // don’t compute beams for invisible staves and tablature without stems
2530 if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->stemless()))
2531 continue;
2532
2533 ChordRest* a1 = 0; // start of (potential) beam
2534 bool firstCR = true;
2535 Beam* beam = 0; // current beam
2536 Beam::Mode bm = Beam::Mode::AUTO;
2537 ChordRest* prev = 0;
2538 bool checkBeats = false;
2539 Fraction stretch = Fraction(1,1);
2540 QHash<int, TDuration> beatSubdivision;
2541
2542 // if this measure is simple meter (actually X/4),
2543 // then perform a prepass to determine the subdivision of each beat
2544
2545 beatSubdivision.clear();
2546 TimeSig* ts = stf->timeSig(measure->tick());
2547 checkBeats = false;
2548 stretch = ts ? ts->stretch() : Fraction(1,1);
2549
2550 const SegmentType st = SegmentType::ChordRest;
2551 if (ts && ts->denominator() == 4) {
2552 checkBeats = true;
2553 for (Segment* s = measure->first(st); s; s = s->next(st)) {
2554 ChordRest* mcr = toChordRest(s->element(track));
2555 if (mcr == 0)
2556 continue;
2557 int beat = (mcr->rtick() * stretch).ticks() / MScore::division;
2558 if (beatSubdivision.contains(beat))
2559 beatSubdivision[beat] = qMin(beatSubdivision[beat], mcr->durationType());
2560 else
2561 beatSubdivision[beat] = mcr->durationType();
2562 }
2563 }
2564
2565 for (Segment* segment = measure->first(st); segment; segment = segment->next(st)) {
2566 ChordRest* cr = segment->cr(track);
2567 if (cr == 0)
2568 continue;
2569
2570 if (firstCR) {
2571 firstCR = false;
2572 // Handle cross-measure beams
2573 Beam::Mode mode = cr->beamMode();
2574 if (mode == Beam::Mode::MID || mode == Beam::Mode::END) {
2575 ChordRest* prevCR = findCR(measure->tick() - Fraction::fromTicks(1), track);
2576 if (prevCR) {
2577 const Measure* pm = prevCR->measure();
2578 if (!beamNoContinue(prevCR->beamMode())
2579 && !pm->lineBreak() && !pm->pageBreak() && !pm->sectionBreak()
2580 && lc.prevMeasure
2581 && prevCR->durationType().type() >= TDuration::DurationType::V_EIGHTH
2582 && prevCR->durationType().type() <= TDuration::DurationType::V_1024TH) {
2583 beam = prevCR->beam();
2584 //a1 = beam ? beam->elements().front() : prevCR;
2585 a1 = beam ? nullptr : prevCR; // when beam is found, a1 is no longer required.
2586 }
2587 }
2588 }
2589 }
2590 #if 0
2591 for (Lyrics* l : cr->lyrics()) {
2592 if (l)
2593 l->layout();
2594 }
2595 #endif
2596 // handle grace notes and cross-measure beaming
2597 // (tied chords?)
2598 if (cr->isChord()) {
2599 Chord* chord = toChord(cr);
2600 beamGraceNotes(chord, false); // grace before
2601 beamGraceNotes(chord, true); // grace after
2602 // set up for cross-measure values as soon as possible
2603 // to have all computations (stems, hooks, ...) consistent with it
2604 if (!chord->isGrace())
2605 chord->crossMeasureSetup(crossMeasure);
2606 }
2607
2608 if (cr->isRest() && cr->beamMode() == Beam::Mode::AUTO)
2609 bm = Beam::Mode::NONE; // do not beam rests set to Beam::Mode::AUTO
2610 else
2611 bm = Groups::endBeam(cr, prev); // get defaults from time signature properties
2612
2613 // perform additional context-dependent checks
2614 if (bm == Beam::Mode::AUTO) {
2615 // check if we need to break beams according to minimum duration in current / previous beat
2616 if (checkBeats && cr->rtick().isNotZero()) {
2617 Fraction tick = cr->rtick() * stretch;
2618 // check if on the beat
2619 if ((tick.ticks() % MScore::division) == 0) {
2620 int beat = tick.ticks() / MScore::division;
2621 // get minimum duration for this & previous beat
2622 TDuration minDuration = qMin(beatSubdivision[beat], beatSubdivision[beat - 1]);
2623 // re-calculate beam as if this were the duration of current chordrest
2624 TDuration saveDuration = cr->actualDurationType();
2625 TDuration saveCMDuration = cr->crossMeasureDurationType();
2626 CrossMeasure saveCrossMeasVal = cr->crossMeasure();
2627 cr->setDurationType(minDuration);
2628 bm = Groups::endBeam(cr, prev);
2629 cr->setDurationType(saveDuration);
2630 cr->setCrossMeasure(saveCrossMeasVal);
2631 cr->setCrossMeasureDurationType(saveCMDuration);
2632 }
2633 }
2634 }
2635
2636 prev = cr;
2637
2638 // if chord has hooks and is 2nd element of a cross-measure value
2639 // set beam mode to NONE (do not combine with following chord beam/hook, if any)
2640
2641 if (cr->durationType().hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND)
2642 bm = Beam::Mode::NONE;
2643
2644 if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
2645 bool removeBeam = true;
2646 if (beam) {
2647 beam->layout1();
2648 removeBeam = (beam->elements().size() <= 1);
2649 beam = 0;
2650 }
2651 if (a1) {
2652 if (removeBeam)
2653 a1->removeDeleteBeam(false);
2654 a1 = 0;
2655 }
2656 cr->removeDeleteBeam(false);
2657 continue;
2658 }
2659
2660 if (beam) {
2661 bool beamEnd = (bm == Beam::Mode::BEGIN);
2662 if (!beamEnd) {
2663 cr->replaceBeam(beam);
2664 cr = 0;
2665 beamEnd = (bm == Beam::Mode::END);
2666 }
2667 if (beamEnd) {
2668 beam->layout1();
2669 beam = 0;
2670 }
2671 }
2672 if (!cr)
2673 continue;
2674
2675 if (a1 == 0)
2676 a1 = cr;
2677 else {
2678 if (!beamModeMid(bm)
2679 &&
2680 (bm == Beam::Mode::BEGIN
2681 || (a1->segment()->segmentType() != cr->segment()->segmentType())
2682 || (a1->tick() + a1->actualTicks() < cr->tick())
2683 )
2684 )
2685 {
2686 a1->removeDeleteBeam(false);
2687 a1 = cr;
2688 }
2689 else {
2690 beam = a1->beam();
2691 if (beam == 0 || beam->elements().front() != a1) {
2692 beam = new Beam(this);
2693 beam->setGenerated(true);
2694 beam->setTrack(track);
2695 a1->replaceBeam(beam);
2696 }
2697 cr->replaceBeam(beam);
2698 a1 = 0;
2699 }
2700 }
2701 }
2702 if (beam)
2703 beam->layout1();
2704 else if (a1) {
2705 Fraction nextTick = a1->tick() + a1->actualTicks();
2706 Measure* m = (nextTick >= measure->endTick() ? measure->nextMeasure() : measure);
2707 ChordRest* nextCR = (m ? m->findChordRest(nextTick, track) : nullptr);
2708 Beam* b = a1->beam();
2709 if (!(b && b->elements().startsWith(a1) && nextCR && beamModeMid(nextCR->beamMode())))
2710 a1->removeDeleteBeam(false);
2711 }
2712 }
2713 }
2714
2715 //---------------------------------------------------------
2716 // breakCrossMeasureBeams
2717 //---------------------------------------------------------
2718
breakCrossMeasureBeams(Measure * measure)2719 static void breakCrossMeasureBeams(Measure* measure)
2720 {
2721 MeasureBase* mbNext = measure->next();
2722 if (!mbNext || !mbNext->isMeasure())
2723 return;
2724
2725 Measure* next = toMeasure(mbNext);
2726 Score* score = measure->score();
2727 const int ntracks = score->ntracks();
2728 Segment* fstSeg = next->first(SegmentType::ChordRest);
2729 if (!fstSeg)
2730 return;
2731
2732 for (int track = 0; track < ntracks; ++track) {
2733 Staff* stf = score->staff(track2staff(track));
2734
2735 // don’t compute beams for invisible staves and tablature without stems
2736 if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->stemless()))
2737 continue;
2738
2739 Element* e = fstSeg->element(track);
2740 if (!e || !e->isChordRest())
2741 continue;
2742
2743 ChordRest* cr = toChordRest(e);
2744 Beam* beam = cr->beam();
2745 if (!beam || beam->elements().front()->measure() == next) // no beam or not cross-measure beam
2746 continue;
2747
2748 std::vector<ChordRest*> mElements;
2749 std::vector<ChordRest*> nextElements;
2750
2751 for (ChordRest* beamCR : beam->elements()) {
2752 if (beamCR->measure() == measure)
2753 mElements.push_back(beamCR);
2754 else
2755 nextElements.push_back(beamCR);
2756 }
2757
2758 if (mElements.size() == 1)
2759 mElements[0]->removeDeleteBeam(false);
2760
2761 Beam* newBeam = nullptr;
2762 if (nextElements.size() > 1) {
2763 newBeam = new Beam(score);
2764 newBeam->setGenerated(true);
2765 newBeam->setTrack(track);
2766 }
2767
2768 const bool nextBeamed = bool(newBeam);
2769 for (ChordRest* nextCR : nextElements) {
2770 nextCR->removeDeleteBeam(nextBeamed);
2771 if (newBeam)
2772 newBeam->add(nextCR);
2773 }
2774
2775 if (newBeam)
2776 newBeam->layout1();
2777 }
2778 }
2779
2780 //---------------------------------------------------------
2781 // layoutDrumsetChord
2782 //---------------------------------------------------------
2783
layoutDrumsetChord(Chord * c,const Drumset * drumset,const StaffType * st,qreal spatium)2784 void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium)
2785 {
2786 for (Note* note : c->notes()) {
2787 int pitch = note->pitch();
2788 if (!drumset->isValid(pitch)) {
2789 // qDebug("unmapped drum note %d", pitch);
2790 }
2791 else if (!note->fixed()) {
2792 note->undoChangeProperty(Pid::HEAD_GROUP, int(drumset->noteHead(pitch)));
2793 int line = drumset->line(pitch);
2794 note->setLine(line);
2795
2796 int off = st->stepOffset();
2797 qreal ld = st->lineDistance().val();
2798 note->rypos() = (line + off * 2.0) * spatium * .5 * ld;
2799 }
2800 }
2801 }
2802
2803 //---------------------------------------------------------
2804 // extendedStemLenWithTwoNotesTremolo
2805 // Goal: To extend stem of one of the chords to make the tremolo less steep
2806 // Returns a modified pair of stem lengths of two chords
2807 //---------------------------------------------------------
2808
extendedStemLenWithTwoNoteTremolo(Tremolo * tremolo,qreal stemLen1,qreal stemLen2)2809 std::pair<qreal, qreal> extendedStemLenWithTwoNoteTremolo(Tremolo* tremolo, qreal stemLen1, qreal stemLen2)
2810 {
2811 const qreal spatium = tremolo->spatium();
2812 Chord* c1 = tremolo->chord1();
2813 Chord* c2 = tremolo->chord2();
2814 Stem* s1 = c1->stem();
2815 Stem* s2 = c2->stem();
2816 const qreal sgn1 = c1->up() ? -1.0 : 1.0;
2817 const qreal sgn2 = c2->up() ? -1.0 : 1.0;
2818 const qreal stemTipDistance = (s1 && s2) ? (s2->pagePos().y() + stemLen2) - (s1->pagePos().y() + stemLen1)
2819 : (c2->stemPos().y() + stemLen2) - (c1->stemPos().y() + stemLen1);
2820
2821 // same staff & same direction: extend one of the stems
2822 if (c1->staffMove() == c2->staffMove() && c1->up() == c2->up()) {
2823 const bool stem1Higher = stemTipDistance > 0.0;
2824 if (std::abs(stemTipDistance) > 1.0 * spatium) {
2825 if ((c1->up() && !stem1Higher) || (!c1->up() && stem1Higher))
2826 return { stemLen1 + sgn1 * (std::abs(stemTipDistance) - 1.0 * spatium), stemLen2 };
2827 else /* if ((c1->up() && stem1Higher) || (!c1->up() && !stem1Higher)) */
2828 return { stemLen1, stemLen2 + sgn2 * (std::abs(stemTipDistance) - 1.0 * spatium) };
2829 }
2830 }
2831
2832 // TODO: cross-staff two-note tremolo. Currently doesn't generate the right result in some cases.
2833 #if 0
2834 // cross-staff & beam between staves: extend both stems by the same length
2835 else if (tremolo->crossStaffBeamBetween()) {
2836 const qreal sw = tremolo->score()->styleS(Sid::tremoloStrokeWidth).val();
2837 const qreal td = tremolo->score()->styleS(Sid::tremoloDistance).val();
2838 const qreal tremoloMinHeight = ((tremolo->lines() - 1) * td + sw) * spatium;
2839 const qreal dy = c1->up() ? tremoloMinHeight - stemTipDistance : tremoloMinHeight + stemTipDistance;
2840 const bool tooShort = dy > 1.0 * spatium;
2841 const bool tooLong = dy < -1.0 * spatium;
2842 const qreal idealDistance = 1.0 * spatium - tremoloMinHeight;
2843
2844 if (tooShort)
2845 return { stemLen1 + sgn1 * (std::abs(stemTipDistance) - idealDistance) / 2.0,
2846 stemLen2 + sgn2 * (std::abs(stemTipDistance) - idealDistance) / 2.0 };
2847 else if (tooLong)
2848 return { stemLen1 - sgn1 * (std::abs(stemTipDistance) + idealDistance) / 2.0,
2849 stemLen2 - sgn2 * (std::abs(stemTipDistance) + idealDistance) / 2.0 };
2850 }
2851 #endif
2852
2853 return { stemLen1, stemLen2 };
2854 }
2855
2856 //---------------------------------------------------------
2857 // getNextMeasure
2858 //---------------------------------------------------------
2859
getNextMeasure(LayoutContext & lc)2860 void Score::getNextMeasure(LayoutContext& lc)
2861 {
2862 lc.prevMeasure = lc.curMeasure;
2863 lc.curMeasure = lc.nextMeasure;
2864 if (!lc.curMeasure)
2865 lc.nextMeasure = _showVBox ? first() : firstMeasure();
2866 else
2867 lc.nextMeasure = _showVBox ? lc.curMeasure->next() : lc.curMeasure->nextMeasure();
2868 if (!lc.curMeasure)
2869 return;
2870
2871 int mno = lc.adjustMeasureNo(lc.curMeasure);
2872
2873 if (lc.curMeasure->isMeasure()) {
2874 if (score()->styleB(Sid::createMultiMeasureRests)) {
2875 Measure* m = toMeasure(lc.curMeasure);
2876 Measure* nm = m;
2877 Measure* lm = nm;
2878 int n = 0;
2879 Fraction len;
2880
2881 while (validMMRestMeasure(nm)) {
2882 MeasureBase* mb = _showVBox ? nm->next() : nm->nextMeasure();
2883 if (breakMultiMeasureRest(nm) && n)
2884 break;
2885 if (nm != m)
2886 lc.adjustMeasureNo(nm);
2887 ++n;
2888 len += nm->ticks();
2889 lm = nm;
2890 if (!(mb && mb->isMeasure()))
2891 break;
2892 nm = toMeasure(mb);
2893 }
2894 if (n >= styleI(Sid::minEmptyMeasures)) {
2895 createMMRest(m, lm, len);
2896 lc.curMeasure = m->mmRest();
2897 lc.nextMeasure = _showVBox ? lm->next() : lm->nextMeasure();
2898 }
2899 else {
2900 if (m->mmRest())
2901 undo(new ChangeMMRest(m, 0));
2902 m->setMMRestCount(0);
2903 lc.measureNo = mno;
2904 }
2905 }
2906 else if (toMeasure(lc.curMeasure)->isMMRest()) {
2907 qDebug("mmrest: no %d += %d", lc.measureNo, toMeasure(lc.curMeasure)->mmRestCount());
2908 lc.measureNo += toMeasure(lc.curMeasure)->mmRestCount() - 1;
2909 }
2910 }
2911 if (!lc.curMeasure->isMeasure()) {
2912 lc.curMeasure->setTick(lc.tick);
2913 return;
2914 }
2915
2916 //-----------------------------------------
2917 // process one measure
2918 //-----------------------------------------
2919
2920 Measure* measure = toMeasure(lc.curMeasure);
2921 measure->moveTicks(lc.tick - measure->tick());
2922
2923 if (lineMode() && (measure->tick() < lc.startTick || measure->tick() > lc.endTick)) {
2924 // needed to reset segment widths if they can change after measure width is computed
2925 //for (Segment& s : measure->segments())
2926 // s.createShapes();
2927 lc.tick += measure->ticks();
2928 return;
2929 }
2930
2931 measure->connectTremolo();
2932
2933 //
2934 // calculate accidentals and note lines,
2935 // create stem and set stem direction
2936 //
2937 for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
2938 const Staff* staff = Score::staff(staffIdx);
2939 const Drumset* drumset = staff->part()->instrument(measure->tick())->useDrumset() ? staff->part()->instrument(measure->tick())->drumset() : 0;
2940 AccidentalState as; // list of already set accidentals for this measure
2941 as.init(staff->keySigEvent(measure->tick()), staff->clef(measure->tick()));
2942
2943 for (Segment& segment : measure->segments()) {
2944 // TODO? maybe we do need to process it here to make it possible to enable later
2945 //if (!segment.enabled())
2946 // continue;
2947 if (segment.isKeySigType()) {
2948 KeySig* ks = toKeySig(segment.element(staffIdx * VOICES));
2949 if (!ks)
2950 continue;
2951 Fraction tick = segment.tick();
2952 as.init(staff->keySigEvent(tick), staff->clef(tick));
2953 ks->layout();
2954 }
2955 else if (segment.isChordRestType()) {
2956 const StaffType* st = staff->staffTypeForElement(&segment);
2957 int track = staffIdx * VOICES;
2958 int endTrack = track + VOICES;
2959
2960 for (int t = track; t < endTrack; ++t) {
2961 ChordRest* cr = segment.cr(t);
2962 if (!cr)
2963 continue;
2964 qreal m = staff->mag(&segment);
2965 if (cr->small())
2966 m *= score()->styleD(Sid::smallNoteMag);
2967
2968 if (cr->isChord()) {
2969 Chord* chord = toChord(cr);
2970 chord->cmdUpdateNotes(&as);
2971 for (Chord* c : chord->graceNotes()) {
2972 c->setMag(m * score()->styleD(Sid::graceNoteMag));
2973 c->computeUp();
2974 if (c->stemDirection() != Direction::AUTO)
2975 c->setUp(c->stemDirection() == Direction::UP);
2976 else
2977 c->setUp(!(t % 2));
2978 if (drumset)
2979 layoutDrumsetChord(c, drumset, st, spatium());
2980 c->layoutStem1();
2981 }
2982 if (drumset)
2983 layoutDrumsetChord(chord, drumset, st, spatium());
2984 chord->computeUp();
2985 chord->layoutStem1(); // create stems needed to calculate spacing
2986 // stem direction can change later during beam processing
2987
2988 // if there is a two-note tremolo attached, and it is too steep,
2989 // extend stem of one of the chords (if not cross-staff)
2990 // or extend both stems (if cross-staff)
2991 // this should be done after the stem lengths of two notes are both calculated
2992 if (chord->tremolo() && chord == chord->tremolo()->chord2()) {
2993 Stem* stem1 = chord->tremolo()->chord1()->stem();
2994 Stem* stem2 = chord->tremolo()->chord2()->stem();
2995 if (stem1 && stem2) {
2996 std::pair<qreal, qreal> extendedLen = extendedStemLenWithTwoNoteTremolo(chord->tremolo(),
2997 stem1->p2().y(), stem2->p2().y());
2998 stem1->setLen(extendedLen.first);
2999 stem2->setLen(extendedLen.second);
3000 }
3001 }
3002 }
3003 cr->setMag(m);
3004 }
3005 }
3006 else if (segment.isClefType()) {
3007 Element* e = segment.element(staffIdx * VOICES);
3008 if (e) {
3009 toClef(e)->setSmall(true);
3010 e->layout();
3011 }
3012 }
3013 else if (segment.isType(SegmentType::TimeSig | SegmentType::Ambitus | SegmentType::HeaderClef)) {
3014 Element* e = segment.element(staffIdx * VOICES);
3015 if (e)
3016 e->layout();
3017 }
3018 }
3019 }
3020
3021 createBeams(lc, measure);
3022
3023 for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
3024 for (Segment& segment : measure->segments()) {
3025 if (segment.isChordRestType()) {
3026 layoutChords1(&segment, staffIdx);
3027 for (int voice = 0; voice < VOICES; ++voice) {
3028 ChordRest* cr = segment.cr(staffIdx * VOICES + voice);
3029 if (cr) {
3030 for (Lyrics* l : cr->lyrics()) {
3031 if (l)
3032 l->layout();
3033 }
3034 }
3035 }
3036 }
3037 }
3038 }
3039
3040 measure->computeTicks();
3041
3042 for (Segment& segment : measure->segments()) {
3043 if (segment.isBreathType()) {
3044 for (Element* e : segment.elist()) {
3045 if (e && e->isBreath())
3046 e->layout();
3047 }
3048 }
3049 else if (segment.isChordRestType()) {
3050 for (Element* e : segment.annotations()) {
3051 if (e->isSymbol())
3052 e->layout();
3053 }
3054 }
3055 }
3056
3057 rebuildTempoAndTimeSigMaps(measure);
3058
3059 Segment* seg = measure->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
3060 if (measure->repeatStart()) {
3061 if (!seg)
3062 seg = measure->getSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
3063 measure->barLinesSetSpan(seg); // this also creates necessary barlines
3064 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3065 BarLine* b = toBarLine(seg->element(staffIdx * VOICES));
3066 if (b) {
3067 b->setBarLineType(BarLineType::START_REPEAT);
3068 b->layout();
3069 }
3070 }
3071 }
3072 else if (seg)
3073 score()->undoRemoveElement(seg);
3074
3075 for (Segment& s : measure->segments()) {
3076 // TODO? maybe we do need to process it here to make it possible to enable later
3077 //if (!s.enabled())
3078 // continue;
3079 // DEBUG: relayout grace notes as beaming/flags may have changed
3080 if (s.isChordRestType()) {
3081 for (Element* e : s.elist()) {
3082 if (e && e->isChord()) {
3083 Chord* chord = toChord(e);
3084 chord->layout();
3085 // if (chord->tremolo()) // debug
3086 // chord->tremolo()->layout();
3087 }
3088 }
3089 }
3090 else if (s.isEndBarLineType())
3091 continue;
3092 s.createShapes();
3093 }
3094
3095 lc.tick += measure->ticks();
3096 }
3097
3098 //---------------------------------------------------------
3099 // isTopBeam
3100 // returns true for the first CR of a beam that is not cross-staff
3101 //---------------------------------------------------------
3102
isTopBeam(ChordRest * cr)3103 bool isTopBeam(ChordRest* cr)
3104 {
3105 Beam* b = cr->beam();
3106 if (b && b->elements().front() == cr) {
3107 // beam already considered cross?
3108 if (b->cross())
3109 return false;
3110
3111 // for beams not already considered cross,
3112 // consider them so here if any elements were moved up
3113 for (ChordRest* cr1 : b->elements()) {
3114 // some element moved up?
3115 if (cr1->staffMove() < 0)
3116 return false;
3117 }
3118
3119 // not cross
3120 return true;
3121 }
3122
3123 // no beam or not first element
3124 return false;
3125 }
3126
3127 //---------------------------------------------------------
3128 // notTopBeam
3129 // returns true for the first CR of a beam that is cross-staff
3130 //---------------------------------------------------------
3131
notTopBeam(ChordRest * cr)3132 bool notTopBeam(ChordRest* cr)
3133 {
3134 Beam* b = cr->beam();
3135 if (b && b->elements().front() == cr) {
3136 // beam already considered cross?
3137 if (b->cross())
3138 return true;
3139
3140 // for beams not already considered cross,
3141 // consider them so here if any elements were moved up
3142 for (ChordRest* cr1 : b->elements()) {
3143 // some element moved up?
3144 if (cr1->staffMove() < 0)
3145 return true;
3146 }
3147
3148 // not cross
3149 return false;
3150 }
3151
3152 // no beam or not first element
3153 return false;
3154 }
3155
3156 //---------------------------------------------------------
3157 // isTopTuplet
3158 // returns true for the first CR of a tuplet that is not cross-staff
3159 //---------------------------------------------------------
3160
isTopTuplet(ChordRest * cr)3161 bool isTopTuplet(ChordRest* cr)
3162 {
3163 Tuplet* t = cr->tuplet();
3164 if (t && t->elements().front() == cr) {
3165 // find top level tuplet
3166 while (t->tuplet())
3167 t = t->tuplet();
3168 // consider tuplet cross if anything moved within it
3169 if (t->cross())
3170 return false;
3171 else
3172 return true;
3173 }
3174
3175 // no tuplet or not first element
3176 return false;
3177 }
3178
3179 //---------------------------------------------------------
3180 // notTopTuplet
3181 // returns true for the first CR of a tuplet that is cross-staff
3182 //---------------------------------------------------------
3183
notTopTuplet(ChordRest * cr)3184 bool notTopTuplet(ChordRest* cr)
3185 {
3186 Tuplet* t = cr->tuplet();
3187 if (t && t->elements().front() == cr) {
3188 // find top level tuplet
3189 while (t->tuplet())
3190 t = t->tuplet();
3191 // consider tuplet cross if anything moved within it
3192 if (t->cross())
3193 return true;
3194 else
3195 return false;
3196 }
3197
3198 // no tuplet or not first element
3199 return false;
3200 }
3201
3202
3203 //---------------------------------------------------------
3204 // findLyricsMaxY
3205 //---------------------------------------------------------
3206
findLyricsMaxY(Segment & s,int staffIdx)3207 static qreal findLyricsMaxY(Segment& s, int staffIdx)
3208 {
3209 qreal yMax = 0.0;
3210 if (!s.isChordRestType())
3211 return yMax;
3212
3213 qreal lyricsMinTopDistance = s.score()->styleP(Sid::lyricsMinTopDistance);
3214
3215 for (int voice = 0; voice < VOICES; ++voice) {
3216 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3217 if (cr && !cr->lyrics().empty()) {
3218 SkylineLine sk(true);
3219
3220 for (Lyrics* l : cr->lyrics()) {
3221 if (l->autoplace() && l->placeBelow()) {
3222 qreal yOff = l->offset().y();
3223 QPointF offset = l->pos() + cr->pos() + s.pos() + s.measure()->pos();
3224 QRectF r = l->bbox().translated(offset);
3225 r.translate(0.0, -yOff);
3226 sk.add(r.x(), r.top(), r.width());
3227 }
3228 }
3229 SysStaff* ss = s.measure()->system()->staff(staffIdx);
3230 for (Lyrics* l : cr->lyrics()) {
3231 if (l->autoplace() && l->placeBelow()) {
3232 qreal y = ss->skyline().south().minDistance(sk);
3233 if (y > -lyricsMinTopDistance)
3234 yMax = qMax(yMax, y + lyricsMinTopDistance);
3235 }
3236 }
3237 }
3238 }
3239 return yMax;
3240 }
3241
3242 //---------------------------------------------------------
3243 // findLyricsMinY
3244 //---------------------------------------------------------
3245
findLyricsMinY(Segment & s,int staffIdx)3246 static qreal findLyricsMinY(Segment& s, int staffIdx)
3247 {
3248 qreal yMin = 0.0;
3249 if (!s.isChordRestType())
3250 return yMin;
3251 qreal lyricsMinTopDistance = s.score()->styleP(Sid::lyricsMinTopDistance);
3252 for (int voice = 0; voice < VOICES; ++voice) {
3253 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3254 if (cr && !cr->lyrics().empty()) {
3255 SkylineLine sk(false);
3256
3257 for (Lyrics* l : cr->lyrics()) {
3258 if (l->autoplace() && l->placeAbove()) {
3259 qreal yOff = l->offset().y();
3260 QRectF r = l->bbox().translated(l->pos() + cr->pos() + s.pos() + s.measure()->pos());
3261 r.translate(0.0, -yOff);
3262 sk.add(r.x(), r.bottom(), r.width());
3263 }
3264 }
3265 SysStaff* ss = s.measure()->system()->staff(staffIdx);
3266 for (Lyrics* l : cr->lyrics()) {
3267 if (l->autoplace() && l->placeAbove()) {
3268 qreal y = sk.minDistance(ss->skyline().north());
3269 if (y > -lyricsMinTopDistance)
3270 yMin = qMin(yMin, -y - lyricsMinTopDistance);
3271 }
3272 }
3273 }
3274 }
3275 return yMin;
3276 }
3277
findLyricsMaxY(Measure * m,int staffIdx)3278 static qreal findLyricsMaxY(Measure* m, int staffIdx)
3279 {
3280 qreal yMax = 0.0;
3281 for (Segment& s : m->segments())
3282 yMax = qMax(yMax, findLyricsMaxY(s, staffIdx));
3283 return yMax;
3284 }
3285
findLyricsMinY(Measure * m,int staffIdx)3286 static qreal findLyricsMinY(Measure* m, int staffIdx)
3287 {
3288 qreal yMin = 0.0;
3289 for (Segment& s : m->segments())
3290 yMin = qMin(yMin, findLyricsMinY(s, staffIdx));
3291 return yMin;
3292 }
3293
3294 //---------------------------------------------------------
3295 // applyLyricsMax
3296 //---------------------------------------------------------
3297
applyLyricsMax(Segment & s,int staffIdx,qreal yMax)3298 static void applyLyricsMax(Segment& s, int staffIdx, qreal yMax)
3299 {
3300 if (!s.isChordRestType())
3301 return;
3302 Skyline& sk = s.measure()->system()->staff(staffIdx)->skyline();
3303 for (int voice = 0; voice < VOICES; ++voice) {
3304 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3305 if (cr && !cr->lyrics().empty()) {
3306 qreal lyricsMinBottomDistance = s.score()->styleP(Sid::lyricsMinBottomDistance);
3307 for (Lyrics* l : cr->lyrics()) {
3308 if (l->autoplace() && l->placeBelow()) {
3309 l->rypos() += yMax - l->propertyDefault(Pid::OFFSET).toPointF().y();
3310 if (l->addToSkyline()) {
3311 QPointF offset = l->pos() + cr->pos() + s.pos() + s.measure()->pos();
3312 sk.add(l->bbox().translated(offset).adjusted(0.0, 0.0, 0.0, lyricsMinBottomDistance));
3313 }
3314 }
3315 }
3316 }
3317 }
3318 }
3319
applyLyricsMax(Measure * m,int staffIdx,qreal yMax)3320 static void applyLyricsMax(Measure* m, int staffIdx, qreal yMax)
3321 {
3322 for (Segment& s : m->segments())
3323 applyLyricsMax(s, staffIdx, yMax);
3324 }
3325
3326 //---------------------------------------------------------
3327 // applyLyricsMin
3328 //---------------------------------------------------------
3329
applyLyricsMin(ChordRest * cr,int staffIdx,qreal yMin)3330 static void applyLyricsMin(ChordRest* cr, int staffIdx, qreal yMin)
3331 {
3332 Skyline& sk = cr->measure()->system()->staff(staffIdx)->skyline();
3333 for (Lyrics* l : cr->lyrics()) {
3334 if (l->autoplace() && l->placeAbove()) {
3335 l->rypos() += yMin - l->propertyDefault(Pid::OFFSET).toPointF().y();
3336 if (l->addToSkyline()) {
3337 QPointF offset = l->pos() + cr->pos() + cr->segment()->pos() + cr->segment()->measure()->pos();
3338 sk.add(l->bbox().translated(offset));
3339 }
3340 }
3341 }
3342 }
3343
applyLyricsMin(Measure * m,int staffIdx,qreal yMin)3344 static void applyLyricsMin(Measure* m, int staffIdx, qreal yMin)
3345 {
3346 for (Segment& s : m->segments()) {
3347 if (s.isChordRestType()) {
3348 for (int voice = 0; voice < VOICES; ++voice) {
3349 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3350 if (cr)
3351 applyLyricsMin(cr, staffIdx, yMin);
3352 }
3353 }
3354 }
3355 }
3356
3357 //---------------------------------------------------------
3358 // restoreBeams
3359 //---------------------------------------------------------
3360
restoreBeams(Measure * m)3361 static void restoreBeams(Measure* m)
3362 {
3363 for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
3364 for (Element* e : s->elist()) {
3365 if (e && e->isChordRest()) {
3366 ChordRest* cr = toChordRest(e);
3367 if (isTopBeam(cr)) {
3368 Beam* b = cr->beam();
3369 b->layout();
3370 b->addSkyline(m->system()->staff(b->staffIdx())->skyline());
3371 }
3372 }
3373 }
3374 }
3375 }
3376
3377 //---------------------------------------------------------
3378 // layoutLyrics
3379 //
3380 // vertical align lyrics
3381 //
3382 //---------------------------------------------------------
3383
layoutLyrics(System * system)3384 void Score::layoutLyrics(System* system)
3385 {
3386 std::vector<int> visibleStaves;
3387 for (int staffIdx = system->firstVisibleStaff(); staffIdx < nstaves(); staffIdx = system->nextVisibleStaff(staffIdx))
3388 visibleStaves.push_back(staffIdx);
3389
3390 //int nAbove[nstaves()];
3391 std::vector<int> VnAbove(nstaves());
3392
3393 for (int staffIdx : visibleStaves) {
3394 VnAbove[staffIdx] = 0;
3395 for (MeasureBase* mb : system->measures()) {
3396 if (!mb->isMeasure())
3397 continue;
3398 Measure* m = toMeasure(mb);
3399 for (Segment& s : m->segments()) {
3400 if (s.isChordRestType()) {
3401 for (int voice = 0; voice < VOICES; ++voice) {
3402 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3403 if (cr) {
3404 int nA = 0;
3405 for (Lyrics* l : cr->lyrics()) {
3406 // user adjusted offset can possibly change placement
3407 if (l->offsetChanged() != OffsetChange::NONE) {
3408 Placement p = l->placement();
3409 l->rebaseOffset();
3410 if (l->placement() != p) {
3411 l->undoResetProperty(Pid::AUTOPLACE);
3412 //l->undoResetProperty(Pid::OFFSET);
3413 //l->layout();
3414 }
3415 }
3416 l->setOffsetChanged(false);
3417 if (l->placeAbove())
3418 ++nA;
3419 }
3420 VnAbove[staffIdx] = qMax(VnAbove[staffIdx], nA);
3421 }
3422 }
3423 }
3424 }
3425 }
3426 }
3427
3428 for (int staffIdx : visibleStaves) {
3429 for (MeasureBase* mb : system->measures()) {
3430 if (!mb->isMeasure())
3431 continue;
3432 Measure* m = toMeasure(mb);
3433 for (Segment& s : m->segments()) {
3434 if (s.isChordRestType()) {
3435 for (int voice = 0; voice < VOICES; ++voice) {
3436 ChordRest* cr = s.cr(staffIdx * VOICES + voice);
3437 if (cr) {
3438 for (Lyrics* l : cr->lyrics())
3439 l->layout2(VnAbove[staffIdx]);
3440 }
3441 }
3442 }
3443 }
3444 }
3445 }
3446
3447 VerticalAlignRange ar = VerticalAlignRange(styleI(Sid::autoplaceVerticalAlignRange));
3448
3449 switch (ar) {
3450 case VerticalAlignRange::MEASURE:
3451 for (MeasureBase* mb : system->measures()) {
3452 if (!mb->isMeasure())
3453 continue;
3454 Measure* m = toMeasure(mb);
3455 for (int staffIdx : visibleStaves) {
3456 qreal yMax = findLyricsMaxY(m, staffIdx);
3457 applyLyricsMax(m, staffIdx, yMax);
3458 }
3459 }
3460 break;
3461 case VerticalAlignRange::SYSTEM:
3462 for (int staffIdx : visibleStaves) {
3463 qreal yMax = 0.0;
3464 qreal yMin = 0.0;
3465 for (MeasureBase* mb : system->measures()) {
3466 if (!mb->isMeasure())
3467 continue;
3468 yMax = qMax<qreal>(yMax, findLyricsMaxY(toMeasure(mb), staffIdx));
3469 yMin = qMin(yMin, findLyricsMinY(toMeasure(mb), staffIdx));
3470 }
3471 for (MeasureBase* mb : system->measures()) {
3472 if (!mb->isMeasure())
3473 continue;
3474 applyLyricsMax(toMeasure(mb), staffIdx, yMax);
3475 applyLyricsMin(toMeasure(mb), staffIdx, yMin);
3476 }
3477 }
3478 break;
3479 case VerticalAlignRange::SEGMENT:
3480 for (MeasureBase* mb : system->measures()) {
3481 if (!mb->isMeasure())
3482 continue;
3483 Measure* m = toMeasure(mb);
3484 for (int staffIdx : visibleStaves) {
3485 for (Segment& s : m->segments()) {
3486 qreal yMax = findLyricsMaxY(s, staffIdx);
3487 applyLyricsMax(s, staffIdx, yMax);
3488 }
3489 }
3490 }
3491 break;
3492 }
3493 }
3494
3495 //---------------------------------------------------------
3496 // layoutTies
3497 //---------------------------------------------------------
3498
layoutTies(Chord * ch,System * system,const Fraction & stick)3499 void layoutTies(Chord* ch, System* system, const Fraction& stick)
3500 {
3501 SysStaff* staff = system->staff(ch->staffIdx());
3502 if (!staff->show())
3503 return;
3504 for (Note* note : ch->notes()) {
3505 Tie* t = note->tieFor();
3506 if (t) {
3507 TieSegment* ts = t->layoutFor(system);
3508 if (ts && ts->addToSkyline())
3509 staff->skyline().add(ts->shape().translated(ts->pos()));
3510 }
3511 t = note->tieBack();
3512 if (t) {
3513 if (t->startNote()->tick() < stick) {
3514 TieSegment* ts = t->layoutBack(system);
3515 if (ts && ts->addToSkyline())
3516 staff->skyline().add(ts->shape().translated(ts->pos()));
3517 }
3518 }
3519 }
3520 }
3521
3522 //---------------------------------------------------------
3523 // layoutHarmonies
3524 //---------------------------------------------------------
3525
layoutHarmonies(const std::vector<Segment * > & sl)3526 void layoutHarmonies(const std::vector<Segment*>& sl)
3527 {
3528 for (const Segment* s : sl) {
3529 for (Element* e : s->annotations()) {
3530 if (e->isHarmony()) {
3531 Harmony* h = toHarmony(e);
3532 // For chord symbols that coincide with a chord or rest,
3533 // a partial layout can also happen (if needed) during ChordRest layout
3534 // in order to calculate a bbox and allocate its shape to the ChordRest.
3535 // But that layout (if it happens at all) does not do autoplace,
3536 // so we need the full layout here.
3537 h->layout();
3538 h->autoplaceSegmentElement();
3539 }
3540 }
3541 }
3542 }
3543
3544 //---------------------------------------------------------
3545 // alignHarmonies
3546 //---------------------------------------------------------
3547
alignHarmonies(const System * system,const std::vector<Segment * > & sl,bool harmony,const qreal maxShiftAbove,const qreal maxShiftBelow)3548 void alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool harmony, const qreal maxShiftAbove, const qreal maxShiftBelow)
3549 {
3550
3551 // Help class.
3552 // Contains harmonies/fretboard per segment.
3553 class HarmonyList : public QList<Element*> {
3554 QMap<const Segment*, QList<Element*>> elements;
3555 QList<Element*> modified;
3556
3557 Element* getReferenceElement(const Segment* s, bool above, bool visible) const
3558 {
3559 // Returns the reference element for aligning.
3560 // When a segments contains multiple harmonies/fretboard, the lowest placed
3561 // element (for placement above, otherwise the highest placed element) is
3562 // used for alignment.
3563 Element* element { nullptr };
3564 for (Element* e : elements[s]) {
3565 // Only chord symbols have styled offset, fretboards don't.
3566 if (!e->autoplace() || (e->isHarmony() && !e->isStyled(Pid::OFFSET)) || (visible && !e->visible()))
3567 continue;
3568 if (!element) {
3569 element = e;
3570 }
3571 else {
3572 if ((e->placeAbove() && above && (element->y() < e->y())) ||
3573 (e->placeBelow() && !above && (element->y() > e->y())))
3574 element = e;
3575 }
3576 }
3577 return element;
3578 }
3579
3580 public:
3581 HarmonyList()
3582 {
3583 elements.clear();
3584 modified.clear();
3585 }
3586
3587 void append(const Segment* s, Element* e)
3588 {
3589 elements[s].append(e);
3590 }
3591
3592 qreal getReferenceHeight(bool above) const
3593 {
3594 // The reference height is the height of
3595 // the lowest element if placed above
3596 // or
3597 // the highest element if placed below.
3598 bool first { true };
3599 qreal ref { 0.0 };
3600 for (auto s : elements.keys()) {
3601 Element* e { getReferenceElement(s, above, true) };
3602 if (!e)
3603 continue;
3604 if (e->placeAbove() && above) {
3605 ref = first ? e->y() : qMin(ref, e->y());
3606 first = false;
3607 }
3608 else if (e->placeBelow() && !above) {
3609 ref = first ? e->y() : qMax(ref, e->y());
3610 first = false;
3611 }
3612 }
3613 return ref;
3614 }
3615
3616 bool align(bool above, qreal reference, qreal maxShift)
3617 {
3618 // Align the elements. If a segment contains multiple elements,
3619 // only the reference elements is used in the algorithm. All other
3620 // elements will remain their original placement with respect to
3621 // the reference element.
3622 bool moved { false };
3623 if (almostZero(reference))
3624 return moved;
3625
3626 for (auto s : elements.keys()) {
3627 QList<Element*> handled;
3628 Element* be = getReferenceElement(s, above, false);
3629 if (!be)
3630 // If there are only invisible elements, we have to use an invisible
3631 // element for alignment reference.
3632 be = getReferenceElement(s, above, true);
3633 if (be && ((above && (be->y() < (reference + maxShift))) || ((!above && (be->y() > (reference - maxShift)))))) {
3634 qreal shift = be->rypos();
3635 be->rypos() = reference - be->ryoffset();
3636 shift -= be->rypos();
3637 for (Element* e : elements[s]) {
3638 if ((above && e->placeBelow()) || (!above && e->placeAbove()))
3639 continue;
3640 modified.append(e);
3641 handled.append(e);
3642 moved = true;
3643 if (e != be)
3644 e->rypos() -= shift;
3645 }
3646 for (auto e : handled)
3647 elements[s].removeOne(e);
3648 }
3649 }
3650 return moved;
3651 }
3652
3653 void addToSkyline(const System* system)
3654 {
3655 for (Element* e : qAsConst(modified)) {
3656 const Segment* s = toSegment(e->parent());
3657 const MeasureBase* m = toMeasureBase(s->parent());
3658 system->staff(e->staffIdx())->skyline().add(e->shape().translated(e->pos() + s->pos() + m->pos()));
3659 if (e->isFretDiagram()) {
3660 FretDiagram* fd = toFretDiagram(e);
3661 Harmony* h = fd->harmony();
3662 if (h)
3663 system->staff(e->staffIdx())->skyline().add(h->shape().translated(h->pos() + fd->pos() + s->pos() + m->pos()));
3664 else
3665 system->staff(e->staffIdx())->skyline().add(fd->shape().translated(fd->pos() + s->pos() + m->pos()));
3666 }
3667 }
3668 }
3669 };
3670
3671 if (almostZero(maxShiftAbove) && almostZero(maxShiftBelow))
3672 return;
3673
3674 // Collect all fret diagrams and chord symbol and store them per staff.
3675 // In the same pass, the maximum height is collected.
3676 QMap<int, HarmonyList> staves;
3677 for (const Segment* s : sl) {
3678 for (Element* e : s->annotations()) {
3679 if ((harmony && e->isHarmony()) || (!harmony && e->isFretDiagram()))
3680 staves[e->staffIdx()].append(s, e);
3681 }
3682 }
3683
3684 for (int idx: staves.keys()) {
3685 // Align the objects.
3686 // Algorithm:
3687 // - Find highest placed harmony/fretdiagram.
3688 // - Align all harmony/fretdiagram objects placed between height and height-maxShiftAbove.
3689 // - Repeat for all harmony/fretdiagram objects below heigt-maxShiftAbove.
3690 bool moved { true };
3691 int pass { 0 };
3692 while (moved && (pass++ < 10)) {
3693 moved = false;
3694 moved |= staves[idx].align(true, staves[idx].getReferenceHeight(true), maxShiftAbove);
3695 moved |= staves[idx].align(false, staves[idx].getReferenceHeight(false), maxShiftBelow);
3696 }
3697
3698 // Add all aligned objects to the sky line.
3699 staves[idx].addToSkyline(system);
3700 }
3701 }
3702
3703 //---------------------------------------------------------
3704 // processLines
3705 //---------------------------------------------------------
3706
processLines(System * system,std::vector<Spanner * > lines,bool align)3707 static void processLines(System* system, std::vector<Spanner*> lines, bool align)
3708 {
3709 std::vector<SpannerSegment*> segments;
3710 for (Spanner* sp : lines) {
3711 SpannerSegment* ss = sp->layoutSystem(system); // create/layout spanner segment for this system
3712 if (ss->autoplace())
3713 segments.push_back(ss);
3714 }
3715
3716 if (align && segments.size() > 1) {
3717 const int nstaves = system->staves()->size();
3718 constexpr qreal minY = -1000000.0;
3719 const qreal defaultY = segments[0]->rypos();
3720 std::vector<qreal> y(nstaves, minY);
3721
3722 for (SpannerSegment* ss : segments) {
3723 if (ss->visible()) {
3724 qreal& staffY = y[ss->staffIdx()];
3725 staffY = qMax(staffY, ss->rypos());
3726 }
3727 }
3728 for (SpannerSegment* ss : segments) {
3729 if (!ss->isStyled(Pid::OFFSET))
3730 continue;
3731 const qreal staffY = y[ss->staffIdx()];
3732 if (staffY > minY)
3733 ss->rypos() = staffY;
3734 else
3735 ss->rypos() = defaultY;
3736 }
3737 }
3738
3739 //
3740 // add shapes to skyline
3741 //
3742 for (SpannerSegment* ss : segments) {
3743 if (ss->addToSkyline())
3744 system->staff(ss->staffIdx())->skyline().add(ss->shape().translated(ss->pos()));
3745 }
3746 }
3747
3748 //---------------------------------------------------------
3749 // collectSystem
3750 //---------------------------------------------------------
3751
collectSystem(LayoutContext & lc)3752 System* Score::collectSystem(LayoutContext& lc)
3753 {
3754 if (!lc.curMeasure)
3755 return 0;
3756 const MeasureBase* measure = _systems.empty() ? 0 : _systems.back()->measures().back();
3757 if (measure)
3758 measure = measure->findPotentialSectionBreak();
3759 if (measure) {
3760 lc.firstSystem = measure->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
3761 lc.firstSystemIndent = lc.firstSystem && measure->sectionBreakElement()->firstSystemIdentation() && styleB(Sid::enableIndentationOnFirstSystem);
3762 lc.startWithLongNames = lc.firstSystem && measure->sectionBreakElement()->startWithLongNames();
3763 }
3764 System* system = getNextSystem(lc);
3765 Fraction lcmTick = lc.curMeasure->tick();
3766 system->setInstrumentNames(lc.startWithLongNames, lcmTick);
3767
3768 qreal minWidth = 0;
3769 qreal layoutSystemMinWidth = 0;
3770 bool firstMeasure = true;
3771 bool createHeader = false;
3772 qreal systemWidth = styleD(Sid::pagePrintableWidth) * DPI;
3773 system->setWidth(systemWidth);
3774
3775 // save state of measure
3776 qreal curWidth = lc.curMeasure->width();
3777 bool curHeader = lc.curMeasure->header();
3778 bool curTrailer = lc.curMeasure->trailer();
3779
3780 while (lc.curMeasure) { // collect measure for system
3781 System* oldSystem = lc.curMeasure->system();
3782 system->appendMeasure(lc.curMeasure);
3783
3784 qreal ww = 0; // width of current measure
3785
3786 if (lc.curMeasure->isMeasure()) {
3787 Measure* m = toMeasure(lc.curMeasure);
3788 if (firstMeasure) {
3789 layoutSystemMinWidth = minWidth;
3790 system->layoutSystem(minWidth, lc.firstSystem, lc.firstSystemIndent);
3791 minWidth += system->leftMargin();
3792 if (m->repeatStart()) {
3793 Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
3794 if (!s->enabled())
3795 s->setEnabled(true);
3796 }
3797 m->addSystemHeader(lc.firstSystem);
3798 firstMeasure = false;
3799 createHeader = false;
3800 }
3801 else {
3802 if (createHeader) {
3803 m->addSystemHeader(false);
3804 createHeader = false;
3805 }
3806 else if (m->header())
3807 m->removeSystemHeader();
3808 }
3809
3810 m->createEndBarLines(true);
3811 m->addSystemTrailer(m->nextMeasure());
3812 m->computeMinWidth();
3813 ww = m->width();
3814 }
3815 else if (lc.curMeasure->isHBox()) {
3816 lc.curMeasure->computeMinWidth();
3817 ww = lc.curMeasure->width();
3818 createHeader = toHBox(lc.curMeasure)->createSystemHeader();
3819 }
3820 else {
3821 // vbox:
3822 getNextMeasure(lc);
3823 system->layout2(); // compute staff distances
3824 return system;
3825 }
3826 // check if lc.curMeasure fits, remove if not
3827 // collect at least one measure and the break
3828
3829 bool doBreak = (system->measures().size() > 1) && ((minWidth + ww) > systemWidth);
3830 if (doBreak) {
3831 if (lc.prevMeasure->noBreak() && system->measures().size() > 2) {
3832 // remove last two measures
3833 // TODO: check more measures for noBreak()
3834 system->removeLastMeasure();
3835 system->removeLastMeasure();
3836 lc.curMeasure->setSystem(oldSystem);
3837 lc.prevMeasure->setSystem(oldSystem);
3838 lc.nextMeasure = lc.curMeasure;
3839 lc.curMeasure = lc.prevMeasure;
3840 lc.prevMeasure = lc.curMeasure->prevMeasure();
3841 break;
3842 }
3843 else if (!lc.prevMeasure->noBreak()) {
3844 // remove last measure
3845 system->removeLastMeasure();
3846 lc.curMeasure->setSystem(oldSystem);
3847 break;
3848 }
3849 }
3850
3851 if (lc.prevMeasure && lc.prevMeasure->isMeasure() && lc.prevMeasure->system() == system) {
3852 //
3853 // now we know that the previous measure is not the last
3854 // measure in the system and we finally can create the end barline for it
3855
3856 Measure* m = toMeasure(lc.prevMeasure);
3857 // TODO: if lc.curMeasure is a frame, removing the trailer may be premature
3858 // but merely skipping this code isn't good enough,
3859 // we need to find the right time to re-enable the trailer,
3860 // since it seems to be disabled somewhere else
3861 if (m->trailer()) {
3862 qreal ow = m->width();
3863 m->removeSystemTrailer();
3864 minWidth += m->width() - ow;
3865 }
3866 // if the prev measure is an end repeat and the cur measure
3867 // is an repeat, the createEndBarLines() created an start-end repeat barline
3868 // and we can remove the start repeat barline of the current barline
3869
3870 if (lc.curMeasure->isMeasure()) {
3871 Measure* m1 = toMeasure(lc.curMeasure);
3872 if (m1->repeatStart()) {
3873 Segment* s = m1->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
3874 if (!s->enabled()) {
3875 s->setEnabled(true);
3876 m1->computeMinWidth();
3877 ww = m1->width();
3878 }
3879 }
3880 }
3881 // TODO: we actually still don't know for sure
3882 // if this will be the last true measure of the system or not
3883 // since the lc.curMeasure may be a frame
3884 // but at this point we have no choice but to assume it isn't
3885 // since we don't know yet if another true measure will fit
3886 // worst that happens is we don't get the automatic double bar before a courtesy key signature
3887 minWidth += m->createEndBarLines(false); // create final barLine
3888 }
3889
3890 MeasureBase* mb = lc.curMeasure;
3891 bool lineBreak = false;
3892 switch (_layoutMode) {
3893 case LayoutMode::PAGE:
3894 case LayoutMode::SYSTEM:
3895 lineBreak = mb->pageBreak() || mb->lineBreak() || mb->sectionBreak();
3896 break;
3897 case LayoutMode::FLOAT:
3898 case LayoutMode::LINE:
3899 lineBreak = false;
3900 break;
3901 }
3902
3903 // preserve state of next measure (which is about to become current measure)
3904 if (lc.nextMeasure) {
3905 MeasureBase* nmb = lc.nextMeasure;
3906 if (nmb->isMeasure() && styleB(Sid::createMultiMeasureRests)) {
3907 Measure* nm = toMeasure(nmb);
3908 if (nm->hasMMRest())
3909 nmb = nm->mmRest();
3910 }
3911 curWidth = nmb->width();
3912 curHeader = nmb->header();
3913 curTrailer = nmb->trailer();
3914 }
3915
3916 getNextMeasure(lc);
3917
3918 minWidth += ww;
3919
3920 // ElementType nt = lc.curMeasure ? lc.curMeasure->type() : ElementType::INVALID;
3921 mb = lc.curMeasure;
3922 bool tooWide = false; // minWidth + minMeasureWidth > systemWidth; // TODO: noBreak
3923 if (lineBreak || !mb || mb->isVBox() || mb->isTBox() || mb->isFBox() || tooWide)
3924 break;
3925 }
3926
3927 if (lc.endTick < lc.prevMeasure->tick()) {
3928 // we've processed the entire range
3929 // but we need to continue layout until we reach a system whose last measure is the same as previous layout
3930 if (lc.prevMeasure == lc.systemOldMeasure) {
3931 // this system ends in the same place as the previous layout
3932 // ok to stop
3933 if (lc.curMeasure && lc.curMeasure->isMeasure()) {
3934 // we may have previously processed first measure of next system
3935 // so now we must restore it to its original state
3936 Measure* m = toMeasure(lc.curMeasure);
3937 if (m->repeatStart()) {
3938 Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
3939 if (!s->enabled())
3940 s->setEnabled(true);
3941 }
3942 // TODO: use findPotentialSectionBreak here to handle breaks on frames correctly?
3943 bool firstSystem = lc.prevMeasure->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
3944 if (curHeader)
3945 m->addSystemHeader(firstSystem);
3946 else
3947 m->removeSystemHeader();
3948 if (curTrailer)
3949 m->addSystemTrailer(m->nextMeasure());
3950 else
3951 m->removeSystemTrailer();
3952 m->computeMinWidth();
3953 m->stretchMeasure(curWidth);
3954 restoreBeams(m);
3955 }
3956 lc.rangeDone = true;
3957 }
3958 }
3959
3960 //
3961 // now we have a complete set of measures for this system
3962 //
3963 // prevMeasure is the last measure in the system
3964 if (lc.prevMeasure && lc.prevMeasure->isMeasure()) {
3965 breakCrossMeasureBeams(toMeasure(lc.prevMeasure));
3966 qreal w = toMeasure(lc.prevMeasure)->createEndBarLines(true);
3967 minWidth += w;
3968 }
3969
3970 hideEmptyStaves(system, lc.firstSystem);
3971 // Relayout system decorations to reuse space properly for
3972 // hidden staves' instrument names or other hidden elements.
3973 minWidth -= system->leftMargin();
3974 system->layoutSystem(layoutSystemMinWidth, lc.firstSystem, lc.firstSystemIndent);
3975 minWidth += system->leftMargin();
3976
3977 //-------------------------------------------------------
3978 // add system trailer if needed
3979 // (cautionary time/key signatures etc)
3980 //-------------------------------------------------------
3981
3982 Measure* lm = system->lastMeasure();
3983 if (lm) {
3984 Measure* nm = lm->nextMeasure();
3985 if (nm) {
3986 qreal w = lm->width();
3987 lm->addSystemTrailer(nm);
3988 if (lm->trailer())
3989 lm->computeMinWidth();
3990 minWidth += lm->width() - w;
3991 }
3992 }
3993
3994 //
3995 // stretch incomplete row
3996 //
3997 qreal rest;
3998 if (MScore::noHorizontalStretch)
3999 rest = 0;
4000 else {
4001 qreal mw = system->leftMargin(); // DEBUG
4002 qreal totalWeight = 0.0;
4003
4004 for (MeasureBase* mb : system->measures()) {
4005 if (mb->isHBox()) {
4006 mw += mb->width();
4007 }
4008 else if (mb->isMeasure()) {
4009 Measure* m = toMeasure(mb);
4010 mw += m->width(); // measures are stretched already with basicStretch()
4011 int weight = m->layoutWeight();
4012 totalWeight += weight * m->basicStretch();
4013 }
4014 }
4015
4016 #ifndef NDEBUG
4017 if (!qFuzzyCompare(mw, minWidth))
4018 qDebug("==layoutSystem %6d old %.1f new %.1f", system->measures().front()->tick().ticks(), minWidth, mw);
4019 #endif
4020 rest = systemWidth - minWidth;
4021 //
4022 // don’t stretch last system row, if accumulated minWidth is <= lastSystemFillLimit
4023 //
4024 if (lc.curMeasure == 0 && ((minWidth / systemWidth) <= styleD(Sid::lastSystemFillLimit))) {
4025 if (minWidth > rest)
4026 rest = rest * .5;
4027 else
4028 rest = minWidth;
4029 }
4030 rest /= totalWeight;
4031 }
4032
4033 QPointF pos;
4034 firstMeasure = true;
4035 bool createBrackets = false;
4036 for (MeasureBase* mb : system->measures()) {
4037 qreal ww = mb->width();
4038 if (mb->isMeasure()) {
4039 if (firstMeasure) {
4040 pos.rx() += system->leftMargin();
4041 firstMeasure = false;
4042 }
4043 mb->setPos(pos);
4044 Measure* m = toMeasure(mb);
4045 qreal stretch = m->basicStretch();
4046 int weight = m->layoutWeight();
4047 ww += rest * weight * stretch;
4048 m->stretchMeasure(ww);
4049 m->layoutStaffLines();
4050 if (createBrackets) {
4051 system->addBrackets(toMeasure(mb));
4052 createBrackets = false;
4053 }
4054 }
4055 else if (mb->isHBox()) {
4056 mb->setPos(pos + QPointF(toHBox(mb)->topGap(), 0.0));
4057 mb->layout();
4058 createBrackets = toHBox(mb)->createSystemHeader();
4059 }
4060 else if (mb->isVBox())
4061 mb->setPos(pos);
4062 pos.rx() += ww;
4063 }
4064 system->setWidth(pos.x());
4065
4066 layoutSystemElements(system, lc);
4067 system->layout2(); // compute staff distances
4068 // TODO: now that the code at the top of this function does this same backwards search,
4069 // we might be able to eliminate this block
4070 // but, lc might be used elsewhere so we need to be careful
4071 #if 1
4072 measure = system->measures().back();
4073 if (measure)
4074 measure = measure->findPotentialSectionBreak();
4075 if (measure) {
4076 lc.firstSystem = measure->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
4077 lc.firstSystemIndent = lc.firstSystem && measure->sectionBreakElement()->firstSystemIdentation() && styleB(Sid::enableIndentationOnFirstSystem);
4078 lc.startWithLongNames = lc.firstSystem && measure->sectionBreakElement()->startWithLongNames();
4079 }
4080 #endif
4081 return system;
4082 }
4083
4084 //---------------------------------------------------------
4085 // layoutSystemElements
4086 //---------------------------------------------------------
4087
layoutSystemElements(System * system,LayoutContext & lc)4088 void Score::layoutSystemElements(System* system, LayoutContext& lc)
4089 {
4090 //-------------------------------------------------------------
4091 // create cr segment list to speed up computations
4092 //-------------------------------------------------------------
4093
4094 std::vector<Segment*> sl;
4095 for (MeasureBase* mb : system->measures()) {
4096 if (!mb->isMeasure())
4097 continue;
4098 Measure* m = toMeasure(mb);
4099 m->layoutMeasureNumber();
4100 m->layoutMMRestRange();
4101
4102 // in continuous view, entire score is one system
4103 // but we only need to process the range
4104 if (lineMode() && (m->tick() < lc.startTick || m->tick() > lc.endTick))
4105 continue;
4106 for (Segment* s = m->first(); s; s = s->next()) {
4107 if (s->isChordRestType() || !s->annotations().empty())
4108 sl.push_back(s);
4109 }
4110 }
4111
4112 //-------------------------------------------------------------
4113 // layout beams
4114 // Needs to be done before creating skylines as stem lengths
4115 // may change.
4116 //-------------------------------------------------------------
4117
4118 for (Segment* s : sl) {
4119 for (Element* e : s->elist()) {
4120 if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show()) {
4121 // the beam and its system may still be referenced when selecting all,
4122 // even if the staff is invisible. The old system is invalid and does cause problems in #284012
4123 if (e && e->isChordRest() && !score()->staff(e->staffIdx())->show() && toChordRest(e)->beam())
4124 toChordRest(e)->beam()->setParent(nullptr);
4125 continue;
4126 }
4127 ChordRest* cr = toChordRest(e);
4128
4129 // layout beam
4130 if (isTopBeam(cr)) {
4131 Beam* b = cr->beam();
4132 b->layout();
4133 }
4134 }
4135 }
4136
4137 //-------------------------------------------------------------
4138 // create skylines
4139 //-------------------------------------------------------------
4140
4141 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
4142 SysStaff* ss = system->staff(staffIdx);
4143 Skyline& skyline = ss->skyline();
4144 skyline.clear();
4145 for (MeasureBase* mb : system->measures()) {
4146 if (!mb->isMeasure())
4147 continue;
4148 Measure* m = toMeasure(mb);
4149 MeasureNumber* mno = m->noText(staffIdx);
4150 // no need to build skyline outside of range in continuous view
4151 if (lineMode() && (m->tick() < lc.startTick || m->tick() > lc.endTick))
4152 continue;
4153 if (mno && mno->addToSkyline())
4154 ss->skyline().add(mno->bbox().translated(m->pos() + mno->pos()));
4155 if (m->staffLines(staffIdx)->addToSkyline())
4156 ss->skyline().add(m->staffLines(staffIdx)->bbox().translated(m->pos()));
4157 for (Segment& s : m->segments()) {
4158 if (!s.enabled() || s.isTimeSigType()) // hack: ignore time signatures
4159 continue;
4160 QPointF p(s.pos() + m->pos());
4161 if (s.segmentType() & (SegmentType::BarLine | SegmentType::EndBarLine | SegmentType::StartRepeatBarLine | SegmentType::BeginBarLine)) {
4162 BarLine* bl = toBarLine(s.element(staffIdx * VOICES));
4163 if (bl && bl->addToSkyline()) {
4164 QRectF r = bl->layoutRect();
4165 skyline.add(r.translated(bl->pos() + p));
4166 }
4167 }
4168 else {
4169 int strack = staffIdx * VOICES;
4170 int etrack = strack + VOICES;
4171 for (Element* e : s.elist()) {
4172 if (!e)
4173 continue;
4174 int effectiveTrack = e->vStaffIdx() * VOICES + e->voice();
4175 if (effectiveTrack < strack || effectiveTrack >= etrack)
4176 continue;
4177
4178 // clear layout for chord-based fingerings
4179 // do this before adding chord to skyline
4180 if (e->isChord()) {
4181 Chord* c = toChord(e);
4182 std::list<Note*> notes;
4183 for (auto gc : c->graceNotes()) {
4184 for (auto n : gc->notes())
4185 notes.push_back(n);
4186 }
4187 for (auto n : c->notes())
4188 notes.push_back(n);
4189 for (Note* note : notes) {
4190 for (Element* en : note->el()) {
4191 if (en->isFingering()) {
4192 Fingering* f = toFingering(en);
4193 if (f->layoutType() == ElementType::CHORD) {
4194 f->setPos(QPointF());
4195 f->setbbox(QRectF());
4196 }
4197 }
4198 }
4199 }
4200 }
4201
4202 // add element to skyline
4203 if (e->addToSkyline())
4204 skyline.add(e->shape().translated(e->pos() + p));
4205
4206 // add tremolo to skyline
4207 if (e->isChord() && toChord(e)->tremolo()) {
4208 Tremolo* t = toChord(e)->tremolo();
4209 Chord* c1 = t->chord1();
4210 Chord* c2 = t->chord2();
4211 if (!t->twoNotes() || (c1 && !c1->staffMove() && c2 && !c2->staffMove())) {
4212 if (t->chord() == e && t->addToSkyline())
4213 skyline.add(t->shape().translated(t->pos() + e->pos() + p));
4214 }
4215 }
4216 }
4217 }
4218 }
4219 }
4220 }
4221
4222 //-------------------------------------------------------------
4223 // layout fingerings, add beams to skylines
4224 //-------------------------------------------------------------
4225
4226 for (Segment* s : sl) {
4227 std::set<int> recreateShapes;
4228 for (Element* e : s->elist()) {
4229 if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show())
4230 continue;
4231 ChordRest* cr = toChordRest(e);
4232
4233 // add beam to skyline
4234 if (isTopBeam(cr)) {
4235 Beam* b = cr->beam();
4236 b->addSkyline(system->staff(b->staffIdx())->skyline());
4237 }
4238
4239 // layout chord-based fingerings
4240 if (e->isChord()) {
4241 Chord* c = toChord(e);
4242 std::list<Note*> notes;
4243 for (auto gc : c->graceNotes()) {
4244 for (auto n : gc->notes())
4245 notes.push_back(n);
4246 }
4247 for (auto n : c->notes())
4248 notes.push_back(n);
4249 std::list<Fingering*> fingerings;
4250 for (Note* note : notes) {
4251 for (Element* el : note->el()) {
4252 if (el->isFingering()) {
4253 Fingering* f = toFingering(el);
4254 if (f->layoutType() == ElementType::CHORD) {
4255 if (f->placeAbove())
4256 fingerings.push_back(f);
4257 else
4258 fingerings.push_front(f);
4259 }
4260 }
4261 }
4262 }
4263 for (Fingering* f : fingerings) {
4264 f->layout();
4265 if (f->addToSkyline()) {
4266 Note* n = f->note();
4267 QRectF r = f->bbox().translated(f->pos() + n->pos() + n->chord()->pos() + s->pos() + s->measure()->pos());
4268 system->staff(f->note()->chord()->vStaffIdx())->skyline().add(r);
4269 }
4270 recreateShapes.insert(f->staffIdx());
4271 }
4272 }
4273 }
4274 for (auto staffIdx : recreateShapes)
4275 s->createShape(staffIdx);
4276 }
4277
4278 //-------------------------------------------------------------
4279 // layout articulations
4280 //-------------------------------------------------------------
4281
4282 for (Segment* s : sl) {
4283 for (Element* e : s->elist()) {
4284 if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show())
4285 continue;
4286 ChordRest* cr = toChordRest(e);
4287 // articulations
4288 if (cr->isChord()) {
4289 Chord* c = toChord(cr);
4290 c->layoutArticulations();
4291 c->layoutArticulations2();
4292 }
4293 }
4294 }
4295
4296 //-------------------------------------------------------------
4297 // layout tuplets
4298 //-------------------------------------------------------------
4299
4300 for (Segment* s : sl) {
4301 for (Element* e : s->elist()) {
4302 if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show())
4303 continue;
4304 ChordRest* cr = toChordRest(e);
4305 if (!isTopTuplet(cr))
4306 continue;
4307 DurationElement* de = cr;
4308 while (de->tuplet() && de->tuplet()->elements().front() == de) {
4309 Tuplet* t = de->tuplet();
4310 t->layout();
4311 de = t;
4312 }
4313 }
4314 }
4315
4316 //-------------------------------------------------------------
4317 // Drumline sticking
4318 //-------------------------------------------------------------
4319
4320 for (const Segment* s : sl) {
4321 for (Element* e : s->annotations()) {
4322 if (e->isSticking())
4323 e->layout();
4324 }
4325 }
4326
4327 //-------------------------------------------------------------
4328 // layout slurs
4329 //-------------------------------------------------------------
4330
4331 bool useRange = false; // TODO: lineMode();
4332 Fraction stick = useRange ? lc.startTick : system->measures().front()->tick();
4333 Fraction etick = useRange ? lc.endTick : system->measures().back()->endTick();
4334 auto spanners = score()->spannerMap().findOverlapping(stick.ticks(), etick.ticks());
4335
4336 std::vector<Spanner*> spanner;
4337 for (auto interval : spanners) {
4338 Spanner* sp = interval.value;
4339 sp->computeStartElement();
4340 sp->computeEndElement();
4341 lc.processedSpanners.insert(sp);
4342 if (sp->tick() < etick && sp->tick2() >= stick) {
4343 if (sp->isSlur()) {
4344 // skip cross-staff slurs
4345 ChordRest* scr = sp->startCR();
4346 ChordRest* ecr = sp->endCR();
4347 int idx = sp->vStaffIdx();
4348 if (scr && ecr && (scr->vStaffIdx() != idx || ecr->vStaffIdx() != idx))
4349 continue;
4350 spanner.push_back(sp);
4351 }
4352 }
4353 }
4354 processLines(system, spanner, false);
4355 for (auto s : spanner) {
4356 Slur* slur = toSlur(s);
4357 ChordRest* scr = s->startCR();
4358 ChordRest* ecr = s->endCR();
4359 if (scr && scr->isChord())
4360 toChord(scr)->layoutArticulations3(slur);
4361 if (ecr && ecr->isChord())
4362 toChord(ecr)->layoutArticulations3(slur);
4363 }
4364
4365 std::vector<Dynamic*> dynamics;
4366 for (Segment* s : sl) {
4367 for (Element* e : s->elist()) {
4368 if (!e)
4369 continue;
4370 if (e->isChord()) {
4371 Chord* c = toChord(e);
4372 for (Chord* ch : c->graceNotes())
4373 layoutTies(ch, system, stick);
4374 layoutTies(c, system, stick);
4375 }
4376 }
4377 for (Element* e : s->annotations()) {
4378 if (e->isDynamic()) {
4379 Dynamic* d = toDynamic(e);
4380 d->layout();
4381
4382 if (d->autoplace()) {
4383 d->autoplaceSegmentElement(false);
4384 dynamics.push_back(d);
4385 }
4386 }
4387 else if (e->isFiguredBass()) {
4388 e->layout();
4389 e->autoplaceSegmentElement();
4390 }
4391 }
4392 }
4393
4394 // add dynamics shape to skyline
4395
4396 for (Dynamic* d : dynamics) {
4397 if (!d->addToSkyline())
4398 continue;
4399 int si = d->staffIdx();
4400 Segment* s = d->segment();
4401 Measure* m = s->measure();
4402 system->staff(si)->skyline().add(d->shape().translated(d->pos() + s->pos() + m->pos()));
4403 }
4404
4405 //-------------------------------------------------------------
4406 // layout SpannerSegments for current system
4407 // ottavas, pedals, voltas are collected here, but layouted later
4408 //-------------------------------------------------------------
4409
4410 spanner.clear();
4411 std::vector<Spanner*> hairpins;
4412 std::vector<Spanner*> ottavas;
4413 std::vector<Spanner*> pedal;
4414 std::vector<Spanner*> voltas;
4415
4416 for (auto interval : spanners) {
4417 Spanner* sp = interval.value;
4418 if (sp->tick() < etick && sp->tick2() > stick) {
4419 if (sp->isOttava())
4420 ottavas.push_back(sp);
4421 else if (sp->isPedal())
4422 pedal.push_back(sp);
4423 else if (sp->isVolta())
4424 voltas.push_back(sp);
4425 else if (sp->isHairpin())
4426 hairpins.push_back(sp);
4427 else if (!sp->isSlur() && !sp->isVolta()) // slurs are already
4428 spanner.push_back(sp);
4429 }
4430 }
4431 processLines(system, hairpins, false);
4432 processLines(system, spanner, false);
4433
4434 //-------------------------------------------------------------
4435 // Fermata, TremoloBar
4436 //-------------------------------------------------------------
4437
4438 for (const Segment* s : sl) {
4439 for (Element* e : s->annotations()) {
4440 if (e->isFermata() || e->isTremoloBar())
4441 e->layout();
4442 }
4443 }
4444
4445 //-------------------------------------------------------------
4446 // Ottava, Pedal
4447 //-------------------------------------------------------------
4448
4449 processLines(system, ottavas, false);
4450 processLines(system, pedal, true);
4451
4452 //-------------------------------------------------------------
4453 // Lyric
4454 //-------------------------------------------------------------
4455
4456 layoutLyrics(system);
4457
4458 // here are lyrics dashes and melisma
4459 for (Spanner* sp : _unmanagedSpanner) {
4460 if (sp->tick() >= etick || sp->tick2() <= stick)
4461 continue;
4462 sp->layoutSystem(system);
4463 }
4464
4465 //
4466 // We need to known if we have FretDiagrams in the system to decide when to layout the Harmonies
4467 //
4468
4469 bool hasFretDiagram = false;
4470 for (const Segment* s : sl) {
4471 for (Element* e : s->annotations()) {
4472 if (e->isFretDiagram()) {
4473 hasFretDiagram = true;
4474 break;
4475 }
4476 }
4477
4478 if (hasFretDiagram)
4479 break;
4480 }
4481
4482 //-------------------------------------------------------------
4483 // Harmony, 1st place
4484 // If we have FretDiagrams, we want the Harmony above this and
4485 // above the volta, therefore we delay the layout.
4486 //-------------------------------------------------------------
4487
4488 if (!hasFretDiagram) {
4489 layoutHarmonies(sl);
4490 alignHarmonies(system, sl, true, styleP(Sid::maxChordShiftAbove), styleP(Sid::maxChordShiftBelow));
4491 }
4492
4493 //-------------------------------------------------------------
4494 // StaffText, InstrumentChange
4495 //-------------------------------------------------------------
4496
4497 for (const Segment* s : sl) {
4498 for (Element* e : s->annotations()) {
4499 if (e->isStaffText() || e->isSystemText() || e->isInstrumentChange())
4500 e->layout();
4501 }
4502 }
4503
4504 //-------------------------------------------------------------
4505 // Jump, Marker
4506 //-------------------------------------------------------------
4507
4508 for (MeasureBase* mb : system->measures()) {
4509 if (!mb->isMeasure())
4510 continue;
4511 Measure* m = toMeasure(mb);
4512 for (Element* e : m->el()) {
4513 if (e->isJump() || e->isMarker())
4514 e->layout();
4515 }
4516 }
4517
4518 //-------------------------------------------------------------
4519 // TempoText
4520 //-------------------------------------------------------------
4521
4522 for (const Segment* s : sl) {
4523 for (Element* e : s->annotations()) {
4524 if (e->isTempoText())
4525 e->layout();
4526 }
4527 }
4528
4529 //-------------------------------------------------------------
4530 // layout Voltas for current system
4531 //-------------------------------------------------------------
4532
4533 processLines(system, voltas, false);
4534
4535 //
4536 // vertical align volta segments
4537 //
4538 for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
4539 std::vector<SpannerSegment*> voltaSegments;
4540 for (SpannerSegment* ss : system->spannerSegments()) {
4541 if (ss->isVoltaSegment() && ss->staffIdx() == staffIdx)
4542 voltaSegments.push_back(ss);
4543 }
4544 while (!voltaSegments.empty()) {
4545 // we assume voltas are sorted left to right (by tick values)
4546 qreal y = 0;
4547 int idx = 0;
4548 Volta* prevVolta = 0;
4549 for (SpannerSegment* ss : voltaSegments) {
4550 Volta* volta = toVolta(ss->spanner());
4551 if (prevVolta && prevVolta != volta) {
4552 // check if volta is adjacent to prevVolta
4553 if (prevVolta->tick2() != volta->tick())
4554 break;
4555 }
4556 y = qMin(y, ss->rypos());
4557 ++idx;
4558 prevVolta = volta;
4559 }
4560
4561 for (int i = 0; i < idx; ++i) {
4562 SpannerSegment* ss = voltaSegments[i];
4563 if (ss->autoplace() && ss->isStyled(Pid::OFFSET))
4564 ss->rypos() = y;
4565 if (ss->addToSkyline())
4566 system->staff(staffIdx)->skyline().add(ss->shape().translated(ss->pos()));
4567 }
4568
4569 voltaSegments.erase(voltaSegments.begin(), voltaSegments.begin() + idx);
4570 }
4571 }
4572
4573 //-------------------------------------------------------------
4574 // FretDiagram
4575 //-------------------------------------------------------------
4576
4577 if (hasFretDiagram) {
4578 for (const Segment* s : sl) {
4579 for (Element* e : s->annotations()) {
4580 if (e->isFretDiagram())
4581 e->layout();
4582 }
4583 }
4584
4585 //-------------------------------------------------------------
4586 // Harmony, 2nd place
4587 // We have FretDiagrams, we want the Harmony above this and
4588 // above the volta.
4589 //-------------------------------------------------------------
4590
4591 layoutHarmonies(sl);
4592 alignHarmonies(system, sl, false, styleP(Sid::maxFretShiftAbove), styleP(Sid::maxFretShiftBelow));
4593 }
4594
4595 //-------------------------------------------------------------
4596 // RehearsalMark
4597 //-------------------------------------------------------------
4598
4599 for (const Segment* s : sl) {
4600 for (Element* e : s->annotations()) {
4601 if (e->isRehearsalMark())
4602 e->layout();
4603 }
4604 }
4605
4606 //-------------------------------------------------------------
4607 // Image
4608 //-------------------------------------------------------------
4609
4610 for (const Segment* s : sl) {
4611 for (Element* e : s->annotations()) {
4612 if (e->isImage())
4613 e->layout();
4614 }
4615 }
4616
4617 }
4618
4619 //---------------------------------------------------------
4620 // collectPage
4621 //---------------------------------------------------------
4622
collectPage()4623 void LayoutContext::collectPage()
4624 {
4625 const qreal slb = score->styleP(Sid::staffLowerBorder);
4626 bool breakPages = score->layoutMode() != LayoutMode::SYSTEM;
4627 qreal ey = page->height() - page->bm();
4628 qreal y = 0.0;
4629
4630 System* nextSystem = 0;
4631 int systemIdx = -1;
4632
4633 // re-calculate positions for systems before current
4634 // (they may have been filled on previous layout)
4635 int pSystems = page->systems().size();
4636 if (pSystems > 0) {
4637 page->system(0)->restoreLayout2();
4638 y = page->system(0)->y() + page->system(0)->height();
4639 }
4640 else {
4641 y = page->tm();
4642 }
4643 for (int i = 1; i < pSystems; ++i) {
4644 System* cs = page->system(i);
4645 System* ps = page->system(i - 1);
4646 qreal distance = ps->minDistance(cs);
4647 y += distance;
4648 cs->setPos(page->lm(), y);
4649 cs->restoreLayout2();
4650 y += cs->height();
4651 }
4652
4653 for (int k = 0;;++k) {
4654 //
4655 // calculate distance to previous system
4656 //
4657 qreal distance;
4658 if (prevSystem)
4659 distance = prevSystem->minDistance(curSystem);
4660 else {
4661 // this is the first system on page
4662 if (curSystem->vbox())
4663 distance = 0.0;
4664 else {
4665 distance = score->styleP(Sid::staffUpperBorder);
4666 bool fixedDistance = false;
4667 // TODO: curSystem->spacerDistance(true)
4668 for (MeasureBase* mb : curSystem->measures()) {
4669 if (mb->isMeasure()) {
4670 Measure* m = toMeasure(mb);
4671 Spacer* sp = m->vspacerUp(0); // TODO: first visible?
4672 if (sp) {
4673 if (sp->spacerType() == SpacerType::FIXED) {
4674 distance = sp->gap();
4675 fixedDistance = true;
4676 break;
4677 }
4678 else
4679 distance = qMax(distance, sp->gap());
4680 }
4681 //TODO::ws distance = qMax(distance, -m->staffShape(0).top());
4682 }
4683 }
4684 if (!fixedDistance)
4685 distance = qMax(distance, curSystem->minTop());
4686 }
4687 }
4688 //TODO-ws ??
4689 // distance += score->staves().front()->userDist();
4690
4691 y += distance;
4692 curSystem->setPos(page->lm(), y);
4693 curSystem->restoreLayout2();
4694 page->appendSystem(curSystem);
4695 y += curSystem->height();
4696
4697 //
4698 // check for page break or if next system will fit on page
4699 //
4700 bool collected = false;
4701 if (rangeDone) {
4702 // take next system unchanged
4703 if (systemIdx > 0) {
4704 nextSystem = score->systems().value(systemIdx++);
4705 if (!nextSystem) {
4706 // TODO: handle next movement
4707 }
4708 }
4709 else {
4710 nextSystem = systemList.empty() ? 0 : systemList.takeFirst();
4711 if (nextSystem)
4712 score->systems().append(nextSystem);
4713 else if (score->isMaster()) {
4714 MasterScore* ms = static_cast<MasterScore*>(score)->next();
4715 if (ms) {
4716 score = ms;
4717 systemIdx = 0;
4718 nextSystem = score->systems().value(systemIdx++);
4719 }
4720 }
4721 }
4722 }
4723 else {
4724 nextSystem = score->collectSystem(*this);
4725 if (nextSystem)
4726 collected = true;
4727 if (!nextSystem && score->isMaster()) {
4728 MasterScore* ms = static_cast<MasterScore*>(score)->next();
4729 if (ms) {
4730 score = ms;
4731 QList<System*>& systems = ms->systems();
4732 if (systems.empty() || systems.front()->measures().empty()) {
4733 systemList = systems;
4734 systems.clear();
4735 measureNo = 0;
4736 startWithLongNames = true;
4737 firstSystem = true;
4738 tick = Fraction(0,1);
4739 prevMeasure = 0;
4740 curMeasure = 0;
4741 nextMeasure = ms->measures()->first();
4742 ms->getNextMeasure(*this);
4743 nextSystem = ms->collectSystem(*this);
4744 ms->setScoreFont(ScoreFont::fontFactory(ms->styleSt(Sid::MusicalSymbolFont)));
4745 ms->setNoteHeadWidth(ms->scoreFont()->width(SymId::noteheadBlack, ms->spatium() / SPATIUM20));
4746 }
4747 else {
4748 rangeDone = true;
4749 systemIdx = 0;
4750 nextSystem = score->systems().value(systemIdx++);
4751 }
4752 }
4753 }
4754 }
4755 prevSystem = curSystem;
4756 Q_ASSERT(curSystem != nextSystem);
4757 curSystem = nextSystem;
4758
4759 bool breakPage = !curSystem || (breakPages && prevSystem->pageBreak());
4760
4761 if (!breakPage) {
4762 qreal dist = prevSystem->minDistance(curSystem) + curSystem->height();
4763 Box* vbox = curSystem->vbox();
4764 if (vbox) {
4765 dist += vbox->bottomGap();
4766 }
4767 else if (!prevSystem->getFixedSpacer()) {
4768 qreal margin = qMax(curSystem->minBottom(), curSystem->spacerDistance(false));
4769 dist += qMax(margin, slb);
4770 }
4771 breakPage = (y + dist) >= ey && breakPages;
4772 }
4773 if (breakPage) {
4774 qreal dist = qMax(prevSystem->minBottom(), prevSystem->spacerDistance(false));
4775 dist = qMax(dist, slb);
4776 layoutPage(page, ey - (y + dist));
4777 // if we collected a system we cannot fit onto this page,
4778 // we need to collect next page in order to correctly set system positions
4779 if (collected)
4780 pageOldMeasure = nullptr;
4781 break;
4782 }
4783 }
4784
4785 Fraction stick = Fraction(-1,1);
4786 for (System* s : page->systems()) {
4787 Score* currentScore = s->score();
4788 for (MeasureBase* mb : s->measures()) {
4789 if (!mb->isMeasure())
4790 continue;
4791 Measure* m = toMeasure(mb);
4792 if (stick == Fraction(-1,1))
4793 stick = m->tick();
4794
4795 for (int track = 0; track < currentScore->ntracks(); ++track) {
4796 for (Segment* segment = m->first(); segment; segment = segment->next()) {
4797 Element* e = segment->element(track);
4798 if (!e)
4799 continue;
4800 if (e->isChordRest()) {
4801 if (!currentScore->staff(track2staff(track))->show())
4802 continue;
4803 ChordRest* cr = toChordRest(e);
4804 if (notTopBeam(cr)) // layout cross staff beams
4805 cr->beam()->layout();
4806 if (notTopTuplet(cr)) {
4807 // fix layout of tuplets
4808 DurationElement* de = cr;
4809 while (de->tuplet() && de->tuplet()->elements().front() == de) {
4810 Tuplet* t = de->tuplet();
4811 t->layout();
4812 de = t;
4813 }
4814 }
4815
4816 if (cr->isChord()) {
4817 Chord* c = toChord(cr);
4818 for (Chord* cc : c->graceNotes()) {
4819 if (cc->beam() && cc->beam()->elements().front() == cc)
4820 cc->beam()->layout();
4821 cc->layoutSpanners();
4822 for (Element* element : cc->el()) {
4823 if (element->isSlur())
4824 element->layout();
4825 }
4826 }
4827 c->layoutArpeggio2();
4828 c->layoutSpanners();
4829 if (c->tremolo()) {
4830 Tremolo* t = c->tremolo();
4831 Chord* c1 = t->chord1();
4832 Chord* c2 = t->chord2();
4833 if (t->twoNotes() && c1 && c2 && (c1->staffMove() || c2->staffMove()))
4834 t->layout();
4835 }
4836 }
4837 }
4838 else if (e->isBarLine())
4839 toBarLine(e)->layout2();
4840 }
4841 }
4842 m->layout2();
4843 }
4844 }
4845
4846 if (score->systemMode()) {
4847 System* s = page->systems().last();
4848 qreal height = s ? s->pos().y() + s->height() + s->minBottom() : page->tm();
4849 page->bbox().setRect(0.0, 0.0, score->loWidth(), height + page->bm());
4850 }
4851
4852 page->rebuildBspTree();
4853 }
4854
4855 //---------------------------------------------------------
4856 // doLayout
4857 // do a complete (re-) layout
4858 //---------------------------------------------------------
4859
doLayout()4860 void Score::doLayout()
4861 {
4862 doLayoutRange(Fraction(0,1), Fraction(-1,1));
4863 }
4864
4865 //---------------------------------------------------------
4866 // CmdStateLocker
4867 //---------------------------------------------------------
4868
4869 class CmdStateLocker {
4870 Score* score;
4871 public:
CmdStateLocker(Score * s)4872 CmdStateLocker(Score* s) : score(s) { score->cmdState().lock(); }
~CmdStateLocker()4873 ~CmdStateLocker() { score->cmdState().unlock(); }
4874 };
4875
4876 //---------------------------------------------------------
4877 // doLayoutRange
4878 //---------------------------------------------------------
4879
doLayoutRange(const Fraction & st,const Fraction & et)4880 void Score::doLayoutRange(const Fraction& st, const Fraction& et)
4881 {
4882 CmdStateLocker cmdStateLocker(this);
4883 LayoutContext lc(this);
4884
4885 Fraction stick(st);
4886 Fraction etick(et);
4887 Q_ASSERT(!(stick == Fraction(-1,1) && etick == Fraction(-1,1)));
4888
4889 if (!last() || (lineMode() && !firstMeasure())) {
4890 qDebug("empty score");
4891 qDeleteAll(_systems);
4892 _systems.clear();
4893 qDeleteAll(pages());
4894 pages().clear();
4895 lc.getNextPage();
4896 return;
4897 }
4898 // if (!_systems.isEmpty())
4899 // return;
4900 bool layoutAll = stick <= Fraction(0,1) && (etick < Fraction(0,1) || etick >= masterScore()->last()->endTick());
4901 if (stick < Fraction(0,1))
4902 stick = Fraction(0,1);
4903 if (etick < Fraction(0,1))
4904 etick = last()->endTick();
4905
4906 lc.endTick = etick;
4907 _scoreFont = ScoreFont::fontFactory(style().value(Sid::MusicalSymbolFont).toString());
4908 _noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, spatium() / SPATIUM20);
4909
4910 if (cmdState().layoutFlags & LayoutFlag::REBUILD_MIDI_MAPPING) {
4911 if (isMaster())
4912 masterScore()->rebuildMidiMapping();
4913 }
4914 if (cmdState().layoutFlags & LayoutFlag::FIX_PITCH_VELO)
4915 updateVelo();
4916 #if 0 // TODO: needed? It was introduced in ab9774ec4098512068b8ef708167d9aa6e702c50
4917 if (cmdState().layoutFlags & LayoutFlag::PLAY_EVENTS)
4918 createPlayEvents();
4919 #endif
4920
4921 //---------------------------------------------------
4922 // initialize layout context lc
4923 //---------------------------------------------------
4924
4925 MeasureBase* m = tick2measure(stick);
4926 if (m == 0)
4927 m = first();
4928 // start layout one measure earlier to handle clefs and cautionary elements
4929 if (m->prevMeasureMM())
4930 m = m->prevMeasureMM();
4931 else if (m->prev())
4932 m = m->prev();
4933 while (!m->isMeasure() && m->prev())
4934 m = m->prev();
4935
4936 // if the first measure of the score is part of a multi measure rest
4937 // m->system() will return a nullptr. We need to find the multi measure
4938 // rest which replaces the measure range
4939
4940 if (!m->system() && m->isMeasure() && toMeasure(m)->hasMMRest()) {
4941 qDebug(" don’t start with mmrest");
4942 m = toMeasure(m)->mmRest();
4943 }
4944
4945 // qDebug("start <%s> tick %d, system %p", m->name(), m->tick(), m->system());
4946
4947 if (lineMode()) {
4948 lc.prevMeasure = 0;
4949 lc.nextMeasure = m; //_showVBox ? first() : firstMeasure();
4950 lc.startTick = m->tick();
4951 layoutLinear(layoutAll, lc);
4952 return;
4953 }
4954 if (!layoutAll && m->system()) {
4955 System* system = m->system();
4956 int systemIndex = _systems.indexOf(system);
4957 lc.page = system->page();
4958 lc.curPage = pageIdx(lc.page);
4959 if (lc.curPage == -1)
4960 lc.curPage = 0;
4961 lc.curSystem = system;
4962 lc.systemList = _systems.mid(systemIndex);
4963
4964 if (systemIndex == 0)
4965 lc.nextMeasure = _showVBox ? first() : firstMeasure();
4966 else {
4967 System* prevSystem = _systems[systemIndex-1];
4968 lc.nextMeasure = prevSystem->measures().back()->next();
4969 }
4970
4971 _systems.erase(_systems.begin() + systemIndex, _systems.end());
4972 if (!lc.nextMeasure->prevMeasure()) {
4973 lc.measureNo = 0;
4974 lc.tick = Fraction(0,1);
4975 }
4976 else {
4977 const MeasureBase* mb = lc.nextMeasure->prev();
4978 if (mb)
4979 mb->findPotentialSectionBreak();
4980 LayoutBreak* sectionBreak = mb->sectionBreakElement();
4981 // TODO: also use mb in else clause here?
4982 // probably not, only actual measures have meaningful numbers
4983 if (sectionBreak && sectionBreak->startWithMeasureOne())
4984 lc.measureNo = 0;
4985 else
4986 lc.measureNo = lc.nextMeasure->prevMeasure()->no() // will be adjusted later with respect
4987 + (lc.nextMeasure->prevMeasure()->irregular() ? 0 : 1); // to the user-defined offset.
4988 lc.tick = lc.nextMeasure->tick();
4989 }
4990 }
4991 else {
4992 // qDebug("layoutAll, systems %p %d", &_systems, int(_systems.size()));
4993 //lc.measureNo = 0;
4994 //lc.tick = 0;
4995 // qDeleteAll(_systems);
4996 // _systems.clear();
4997 // lc.systemList = _systems;
4998 // _systems.clear();
4999
5000 for (System* s : qAsConst(_systems)) {
5001 for (Bracket* b : s->brackets()) {
5002 if (b->selected()) {
5003 _selection.remove(b);
5004 setSelectionChanged(true);
5005 }
5006 }
5007 // for (SpannerSegment* ss : s->spannerSegments())
5008 // ss->setParent(0);
5009 s->setParent(nullptr);
5010 }
5011 for (MeasureBase* mb = first(); mb; mb = mb->next()) {
5012 mb->setSystem(0);
5013 if (mb->isMeasure() && toMeasure(mb)->mmRest())
5014 toMeasure(mb)->mmRest()->setSystem(0);
5015 }
5016 qDeleteAll(_systems);
5017 _systems.clear();
5018
5019 qDeleteAll(pages());
5020 pages().clear();
5021
5022 lc.nextMeasure = _showVBox ? first() : firstMeasure();
5023 }
5024
5025 lc.prevMeasure = 0;
5026
5027 getNextMeasure(lc);
5028 lc.curSystem = collectSystem(lc);
5029
5030 lc.layout();
5031 }
5032
5033 //---------------------------------------------------------
5034 // layout
5035 //---------------------------------------------------------
5036
layout()5037 void LayoutContext::layout()
5038 {
5039 MeasureBase* lmb;
5040 do {
5041 getNextPage();
5042 collectPage();
5043
5044 if (page && !page->systems().isEmpty())
5045 lmb = page->systems().back()->measures().back();
5046 else
5047 lmb = nullptr;
5048
5049 // we can stop collecting pages when:
5050 // 1) we reach the end of score (curSystem is nullptr)
5051 // or
5052 // 2) we have fully processed the range and reached a point of stability:
5053 // a) we have completed layout for the range (rangeDone is true)
5054 // b) we haven't collected a system that will need to go on the next page
5055 // c) this page ends with the same measure as the previous layout
5056 // pageOldMeasure will be last measure from previous layout if range was completed on or before this page
5057 // it will be nullptr if this page was never laid out or if we collected a system for next page
5058 } while (curSystem && !(rangeDone && lmb == pageOldMeasure));
5059 // && page->system(0)->measures().back()->tick() > endTick // FIXME: perhaps the first measure was meant? Or last system?
5060
5061 if (!curSystem) {
5062 // The end of the score. The remaining systems are not needed...
5063 qDeleteAll(systemList);
5064 systemList.clear();
5065 // ...and the remaining pages too
5066 while (score->npages() > curPage)
5067 delete score->pages().takeLast();
5068 }
5069 else {
5070 Page* p = curSystem->page();
5071 if (p && (p != page))
5072 p->rebuildBspTree();
5073 }
5074 score->systems().append(systemList); // TODO
5075 }
5076
5077 //---------------------------------------------------------
5078 // LayoutContext::LayoutContext
5079 //---------------------------------------------------------
5080
LayoutContext(Score * s)5081 LayoutContext::LayoutContext(Score* s)
5082 : score(s)
5083 {
5084 firstSystemIndent = score && score->styleB(Sid::enableIndentationOnFirstSystem);
5085 }
5086
5087 //---------------------------------------------------------
5088 // LayoutContext::~LayoutContext
5089 //---------------------------------------------------------
5090
~LayoutContext()5091 LayoutContext::~LayoutContext()
5092 {
5093 for (Spanner* s : processedSpanners)
5094 s->layoutSystemsDone();
5095
5096 for (MuseScoreView* v : score->getViewer())
5097 v->layoutChanged();
5098 }
5099
5100 //---------------------------------------------------------
5101 // VerticalStretchData
5102 //---------------------------------------------------------
5103
VerticalGapData(bool first,System * sys,Staff * st,SysStaff * sst,const Spacer * spacer,qreal y)5104 VerticalGapData::VerticalGapData(bool first, System *sys, Staff *st, SysStaff *sst, const Spacer* spacer, qreal y)
5105 : _fixedHeight(first), system(sys), sysStaff(sst), staff(st)
5106 {
5107 if (_fixedHeight) {
5108 _normalisedSpacing = system->score()->styleP(Sid::staffUpperBorder);
5109 _maxActualSpacing = _normalisedSpacing;
5110 }
5111 else {
5112 if (spacer) {
5113 _fixedHeight = true;
5114 _normalisedSpacing = spacer->gap();
5115 _maxActualSpacing = _normalisedSpacing;
5116 }
5117 else {
5118 _normalisedSpacing = system->y() + (sysStaff ? sysStaff->y() : 0.0) - y;
5119 _maxActualSpacing = system->score()->styleP(Sid::maxStaffSpread);
5120 }
5121 }
5122 }
5123
5124 //---------------------------------------------------------
5125 // updateFactor
5126 //---------------------------------------------------------
5127
updateFactor(qreal factor)5128 void VerticalGapData::updateFactor(qreal factor)
5129 {
5130 if (_fixedHeight)
5131 return;
5132 qreal f = qMax(factor, _factor);
5133 _normalisedSpacing *= _factor / f;
5134 _factor = f;
5135 }
5136
5137 //---------------------------------------------------------
5138 // addSpaceBetweenSections
5139 //---------------------------------------------------------
5140
addSpaceBetweenSections()5141 void VerticalGapData::addSpaceBetweenSections()
5142 {
5143 updateFactor(system->score()->styleD(Sid::spreadSystem));
5144 if (!_fixedHeight)
5145 _maxActualSpacing = qMax(_maxActualSpacing, system->score()->styleP(Sid::maxSystemSpread));
5146 }
5147
5148 //---------------------------------------------------------
5149 // addSpaceAroundVBox
5150 //---------------------------------------------------------
5151
addSpaceAroundVBox(bool above)5152 void VerticalGapData::addSpaceAroundVBox(bool above)
5153 {
5154 _fixedHeight = true;
5155 _factor = 1.0;
5156 const Score* score { system->score() };
5157 _normalisedSpacing = above ? score->styleP(Sid::frameSystemDistance) : score->styleP(Sid::systemFrameDistance);
5158 _maxActualSpacing = _normalisedSpacing;
5159 }
5160
5161 //---------------------------------------------------------
5162 // addSpaceAroundNormalBracket
5163 //---------------------------------------------------------
5164
addSpaceAroundNormalBracket()5165 void VerticalGapData::addSpaceAroundNormalBracket()
5166 {
5167 updateFactor(system->score()->styleD(Sid::spreadSquareBracket));
5168 }
5169
5170 //---------------------------------------------------------
5171 // addSpaceAroundCurlyBracket
5172 //---------------------------------------------------------
5173
addSpaceAroundCurlyBracket()5174 void VerticalGapData::addSpaceAroundCurlyBracket()
5175 {
5176 updateFactor(system->score()->styleD(Sid::spreadCurlyBracket));
5177 }
5178
5179 //---------------------------------------------------------
5180 // insideCurlyBracket
5181 //---------------------------------------------------------
5182
insideCurlyBracket()5183 void VerticalGapData::insideCurlyBracket()
5184 {
5185 _maxActualSpacing = system->score()->styleP(Sid::maxAkkoladeDistance);
5186 }
5187
5188 //---------------------------------------------------------
5189 // factor
5190 //---------------------------------------------------------
5191
factor() const5192 qreal VerticalGapData::factor() const
5193 {
5194 return _factor;
5195 }
5196
5197 //---------------------------------------------------------
5198 // spacing
5199 // return normalised spacing
5200 //---------------------------------------------------------
5201
spacing() const5202 qreal VerticalGapData::spacing() const
5203 {
5204 return _normalisedSpacing + _addedNormalisedSpace;
5205 }
5206
5207 //---------------------------------------------------------
5208 // addedSpace
5209 //---------------------------------------------------------
5210
actualAddedSpace() const5211 qreal VerticalGapData::actualAddedSpace() const
5212 {
5213 return _addedNormalisedSpace * factor();
5214 }
5215
5216 //---------------------------------------------------------
5217 // addSpacing
5218 //---------------------------------------------------------
5219
addSpacing(qreal step)5220 qreal VerticalGapData::addSpacing(qreal step)
5221 {
5222 if (_fixedHeight)
5223 return 0.0;
5224 if ((_normalisedSpacing >= _maxActualSpacing)) {
5225 _normalisedSpacing = _maxActualSpacing;
5226 step = 0.0;
5227 }
5228 else {
5229 qreal newSpacing { _normalisedSpacing + _addedNormalisedSpace + step };
5230 if ((newSpacing >= _maxActualSpacing))
5231 step = _maxActualSpacing - _normalisedSpacing - _addedNormalisedSpace;
5232 }
5233 _addedNormalisedSpace += step;
5234 _lastStep = step;
5235 return step;
5236 }
5237
5238 //---------------------------------------------------------
5239 // isFixedHeight
5240 //---------------------------------------------------------
5241
isFixedHeight() const5242 bool VerticalGapData::isFixedHeight() const
5243 {
5244 return _fixedHeight;
5245 }
5246
5247 //---------------------------------------------------------
5248 // undoLastAddSpacing
5249 //---------------------------------------------------------
5250
undoLastAddSpacing()5251 void VerticalGapData::undoLastAddSpacing()
5252 {
5253 _addedNormalisedSpace -= _lastStep;
5254 _lastStep = 0.0;
5255 }
5256
5257 //---------------------------------------------------------
5258 // addFillSpacing
5259 //---------------------------------------------------------
5260
addFillSpacing(qreal step,qreal maxFill)5261 qreal VerticalGapData::addFillSpacing(qreal step, qreal maxFill)
5262 {
5263 qreal res = addSpacing(qMin(maxFill - _fillSpacing, step));
5264 _fillSpacing += res;
5265 return res;
5266 }
5267
5268 //---------------------------------------------------------
5269 // deleteAll
5270 //---------------------------------------------------------
5271
deleteAll()5272 void VerticalGapDataList::deleteAll()
5273 {
5274 for (auto vsd : *this)
5275 delete vsd;
5276 }
5277
5278 //---------------------------------------------------------
5279 // sumStretchFactor
5280 //---------------------------------------------------------
5281
sumStretchFactor() const5282 qreal VerticalGapDataList::sumStretchFactor() const
5283 {
5284 qreal sum { 0.0 };
5285 for (VerticalGapData* vsd : *this)
5286 sum += vsd->factor();
5287 return sum;
5288 }
5289
5290 //---------------------------------------------------------
5291 // smallest
5292 //---------------------------------------------------------
5293
smallest(qreal limit) const5294 qreal VerticalGapDataList::smallest(qreal limit) const
5295 {
5296 VerticalGapData* vdp { nullptr };
5297 for (VerticalGapData* vgd : *this) {
5298 if (vgd->isFixedHeight())
5299 continue;
5300 if ((qCeil(limit) == qCeil(vgd->spacing())))
5301 continue;
5302 if (!vdp || (vgd->spacing() < vdp->spacing()))
5303 vdp = vgd;
5304 }
5305 return vdp ? vdp->spacing() : 0.0;
5306 }
5307
5308 }
5309