1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2011 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 /**
14 \file
15 Implementation of classes SysStaff and System.
16 */
17
18 #include "system.h"
19 #include "measure.h"
20 #include "segment.h"
21 #include "score.h"
22 #include "sig.h"
23 #include "key.h"
24 #include "xml.h"
25 #include "clef.h"
26 #include "text.h"
27 #include "navigate.h"
28 #include "select.h"
29 #include "staff.h"
30 #include "part.h"
31 #include "page.h"
32 #include "style.h"
33 #include "bracket.h"
34 #include "mscore.h"
35 #include "barline.h"
36 #include "system.h"
37 #include "box.h"
38 #include "chordrest.h"
39 #include "iname.h"
40 #include "spanner.h"
41 #include "sym.h"
42 #include "spacer.h"
43 #include "systemdivider.h"
44 #include "textframe.h"
45 #include "stafflines.h"
46 #include "bracketItem.h"
47 #include "global/log.h"
48
49 namespace Ms {
50
51 //---------------------------------------------------------
52 // ~SysStaff
53 //---------------------------------------------------------
54
~SysStaff()55 SysStaff::~SysStaff()
56 {
57 qDeleteAll(instrumentNames);
58 }
59
60 //---------------------------------------------------------
61 // saveLayout
62 //---------------------------------------------------------
63
saveLayout()64 void SysStaff::saveLayout()
65 {
66 _height = bbox().height();
67 _yPos = bbox().y();
68 }
69
70 //---------------------------------------------------------
71 // saveLayout
72 //---------------------------------------------------------
73
restoreLayout()74 void SysStaff::restoreLayout()
75 {
76 bbox().setY(_yPos);
77 bbox().setHeight(_height);
78 }
79
80 //---------------------------------------------------------
81 // System
82 //---------------------------------------------------------
83
System(Score * s)84 System::System(Score* s)
85 : Element(s)
86 {
87 }
88
89 //---------------------------------------------------------
90 // ~System
91 //---------------------------------------------------------
92
~System()93 System::~System()
94 {
95 for (SpannerSegment* ss : spannerSegments()) {
96 if (ss->system() == this)
97 ss->setParent(nullptr);
98 }
99 for (MeasureBase* mb : measures()) {
100 if (mb->system() == this)
101 mb->setSystem(nullptr);
102 }
103 qDeleteAll(_staves);
104 qDeleteAll(_brackets);
105 delete _systemDividerLeft;
106 delete _systemDividerRight;
107 }
108
109 //---------------------------------------------------------
110 /// clear
111 /// Clear layout of System
112 //---------------------------------------------------------
113
clear()114 void System::clear()
115 {
116 for (MeasureBase* mb : measures()) {
117 if (mb->system() == this)
118 mb->setSystem(nullptr);
119 }
120 ml.clear();
121 for (SpannerSegment* ss : qAsConst(_spannerSegments)) {
122 if (ss->system() == this)
123 ss->setParent(0); // assume parent() is System
124 }
125 _spannerSegments.clear();
126 // _systemDividers are reused
127 }
128
129 //---------------------------------------------------------
130 // appendMeasure
131 //---------------------------------------------------------
132
appendMeasure(MeasureBase * mb)133 void System::appendMeasure(MeasureBase* mb)
134 {
135 Q_ASSERT(!mb->isMeasure() || !(score()->styleB(Sid::createMultiMeasureRests) && toMeasure(mb)->hasMMRest()));
136 mb->setSystem(this);
137 ml.push_back(mb);
138 }
139
140 //---------------------------------------------------------
141 // removeMeasure
142 //---------------------------------------------------------
143
removeMeasure(MeasureBase * mb)144 void System::removeMeasure(MeasureBase* mb)
145 {
146 ml.erase(std::remove(ml.begin(), ml.end(), mb), ml.end());
147 if (mb->system() == this)
148 mb->setSystem(nullptr);
149 }
150
151 //---------------------------------------------------------
152 // removeLastMeasure
153 //---------------------------------------------------------
154
removeLastMeasure()155 void System::removeLastMeasure()
156 {
157 if (ml.empty())
158 return;
159 MeasureBase* mb = ml.back();
160 ml.pop_back();
161 if (mb->system() == this)
162 mb->setSystem(nullptr);
163 }
164
165 //---------------------------------------------------------
166 // vbox
167 // a system can only contain one vertical frame
168 //---------------------------------------------------------
169
vbox() const170 Box* System::vbox() const
171 {
172 if (!ml.empty()) {
173 if (ml[0]->isVBox() || ml[0]->isTBox())
174 return toBox(ml[0]);
175 }
176 return 0;
177 }
178
179 //---------------------------------------------------------
180 // insertStaff
181 //---------------------------------------------------------
182
insertStaff(int idx)183 SysStaff* System::insertStaff(int idx)
184 {
185 SysStaff* staff = new SysStaff;
186 if (idx) {
187 // HACK: guess position
188 staff->bbox().setY(_staves[idx-1]->y() + 6 * spatium());
189 }
190 _staves.insert(idx, staff);
191 return staff;
192 }
193
194 //---------------------------------------------------------
195 // removeStaff
196 //---------------------------------------------------------
197
removeStaff(int idx)198 void System::removeStaff(int idx)
199 {
200 _staves.takeAt(idx);
201 }
202
203 //---------------------------------------------------------
204 // adjustStavesNumber
205 //---------------------------------------------------------
206
adjustStavesNumber(int nstaves)207 void System::adjustStavesNumber(int nstaves)
208 {
209 for (int i = _staves.size(); i < nstaves; ++i)
210 insertStaff(i);
211 const int dn = _staves.size() - nstaves;
212 for (int i = 0; i < dn; ++i)
213 removeStaff(_staves.size() - 1);
214 }
215
216 //---------------------------------------------------------
217 // layoutSystem
218 /// Layout the System
219 //---------------------------------------------------------
220
layoutSystem(qreal xo1,const bool isFirstSystem,bool firstSystemIndent)221 void System::layoutSystem(qreal xo1, const bool isFirstSystem, bool firstSystemIndent)
222 {
223 if (_staves.empty()) // ignore vbox
224 return;
225
226 static const Spatium instrumentNameOffset(1.0); // TODO: make style value
227
228 int nstaves = _staves.size();
229
230 //---------------------------------------------------
231 // find x position of staves
232 //---------------------------------------------------
233 qreal xoff2 = 0.0; // x offset for instrument name
234
235 for (const Part* p : score()->parts()) {
236 if (firstVisibleSysStaffOfPart(p) < 0)
237 continue;
238 for (int staffIdx = firstSysStaffOfPart(p); staffIdx <= lastSysStaffOfPart(p); ++staffIdx) {
239 for (InstrumentName* t : qAsConst(_staves[staffIdx]->instrumentNames)) {
240 t->layout();
241 qreal w = t->width() + point(instrumentNameOffset);
242 if (w > xoff2)
243 xoff2 = w;
244 }
245 }
246 }
247
248 if (isFirstSystem && firstSystemIndent)
249 xoff2 = qMax(xoff2, styleP(Sid::firstSystemIndentationValue) * mag());
250
251 //---------------------------------------------------
252 // create brackets
253 //---------------------------------------------------
254
255 int columns = getBracketsColumnsCount();
256
257 #if (!defined (_MSCVER) && !defined (_MSC_VER))
258 qreal bracketWidth[columns];
259 #else
260 // MSVC does not support VLA. Replace with std::vector. If profiling determines that the
261 // heap allocation is slow, an optimization might be used.
262 std::vector<qreal> bracketWidth(columns);
263 #endif
264 for (int i = 0; i < columns; ++i)
265 bracketWidth[i] = 0.0;
266
267 QList<Bracket*> bl;
268 bl.swap(_brackets);
269
270 for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
271 Staff* s = score()->staff(staffIdx);
272 for (int i = 0; i < columns; ++i) {
273 for (auto bi : s->brackets()) {
274 if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET)
275 continue;
276 Bracket* b = createBracket(bi, i, staffIdx, bl, this->firstMeasure());
277 if (b != nullptr) bracketWidth[i] = qMax(bracketWidth[i], b->width());
278 }
279 }
280 }
281
282 for (Bracket* b : qAsConst(bl))
283 delete b;
284
285 //---------------------------------------------------
286 // layout SysStaff and StaffLines
287 //---------------------------------------------------
288
289 _leftMargin = xoff2;
290
291 qreal bd = score()->styleP(Sid::bracketDistance);
292 if (!_brackets.empty()) {
293 for (int w : bracketWidth)
294 _leftMargin += w + bd;
295 }
296
297 int nVisible = 0;
298 for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
299 SysStaff* s = _staves[staffIdx];
300 Staff* staff = score()->staff(staffIdx);
301 if (!staff->show() || !s->show()) {
302 s->setbbox(QRectF());
303 continue;
304 }
305 ++nVisible;
306 qreal staffMag = staff->mag(Fraction(0,1)); // ??? TODO
307 int staffLines = staff->lines(Fraction(0,1));
308 if (staffLines <= 1) {
309 qreal h = staff->lineDistance(Fraction(0,1)) * staffMag * spatium();
310 s->bbox().setRect(_leftMargin + xo1, -h, 0.0, 2 * h);
311 }
312 else {
313 qreal h = (staffLines - 1) * staff->lineDistance(Fraction(0,1));
314 h = h * staffMag * spatium();
315 s->bbox().setRect(_leftMargin + xo1, 0.0, 0.0, h);
316 }
317 }
318
319 //---------------------------------------------------
320 // layout brackets
321 //---------------------------------------------------
322
323 setBracketsXPosition(xo1 + _leftMargin);
324
325 //---------------------------------------------------
326 // layout instrument names x position
327 // at this point it is not clear which staves will
328 // be hidden, so layout all instrument names
329 //---------------------------------------------------
330
331 for (SysStaff* s : qAsConst(_staves)) {
332 for (InstrumentName* t : qAsConst(s->instrumentNames)) {
333 switch (int(t->align()) & int(Align::HMASK)) {
334 case int(Align::LEFT):
335 t->rxpos() = 0;
336 break;
337 case int(Align::HCENTER):
338 t->rxpos() = (xoff2 - point(instrumentNameOffset) + xo1) * .5;
339 break;
340 case int(Align::RIGHT):
341 default:
342 t->rxpos() = xoff2 - point(instrumentNameOffset) + xo1;
343 break;
344 }
345 }
346 }
347 }
348
349 //---------------------------------------------------------
350 // setMeasureHeight
351 //---------------------------------------------------------
352
setMeasureHeight(qreal height)353 void System::setMeasureHeight(qreal height)
354 {
355 qreal _spatium { spatium() };
356 for (MeasureBase* m : ml) {
357 if (m->isMeasure()) {
358 // note that the factor 2 * _spatium must be corrected for when exporting
359 // system distance in MusicXML (issue #24733)
360 m->bbox().setRect(0.0, -_spatium, m->width(), height + 2.0 * _spatium);
361 }
362 else if (m->isHBox()) {
363 m->bbox().setRect(0.0, 0.0, m->width(), height);
364 toHBox(m)->layout2();
365 }
366 else if (m->isTBox()) {
367 toTBox(m)->layout();
368 }
369 else
370 qDebug("unhandled measure type %s", m->name());
371 }
372 }
373
374 //---------------------------------------------------------
375 // layoutBracketsVertical
376 //---------------------------------------------------------
377
layoutBracketsVertical()378 void System::layoutBracketsVertical()
379 {
380 for (Bracket* b : qAsConst(_brackets)) {
381 int staffIdx1 = b->firstStaff();
382 int staffIdx2 = b->lastStaff();
383 qreal sy = 0; // assume bracket not visible
384 qreal ey = 0;
385 // if start staff not visible, try next staff
386 while (staffIdx1 <= staffIdx2 && !_staves[staffIdx1]->show())
387 ++staffIdx1;
388 // if end staff not visible, try prev staff
389 while (staffIdx1 <= staffIdx2 && !_staves[staffIdx2]->show())
390 --staffIdx2;
391 // if the score doesn't have "alwaysShowBracketsWhenEmptyStavesAreHidden" as true,
392 // the bracket will be shown IF:
393 // it spans at least 2 visible staves (staffIdx1 < staffIdx2) OR
394 // it spans just one visible staff (staffIdx1 == staffIdx2) but it is required to do so
395 // (the second case happens at least when the bracket is initially dropped)
396 bool notHidden = score()->styleB(Sid::alwaysShowBracketsWhenEmptyStavesAreHidden)
397 ? (staffIdx1 <= staffIdx2) : (staffIdx1 < staffIdx2) || (b->span() == 1 && staffIdx1 == staffIdx2);
398 if (notHidden) { // set vert. pos. and height to visible spanned staves
399 sy = _staves[staffIdx1]->bbox().top();
400 ey = _staves[staffIdx2]->bbox().bottom();
401 }
402 b->rypos() = sy;
403 b->setHeight(ey - sy);
404 b->layout();
405 }
406 }
407
408 //---------------------------------------------------------
409 // layoutInstrumentNames
410 //---------------------------------------------------------
411
layoutInstrumentNames()412 void System::layoutInstrumentNames()
413 {
414 int staffIdx = 0;
415
416 for (Part* p : score()->parts()) {
417 SysStaff* s = staff(staffIdx);
418 SysStaff* s2;
419 int nstaves = p->nstaves();
420
421 int visible = firstVisibleSysStaffOfPart(p);
422 if (visible >= 0) {
423 // The top staff might be invisible but this top staff contains the instrument names.
424 // To make sure these instrument name are drawn, even when the top staff is invisible,
425 // move the InstrumentName elements to the the first visible staff of the part.
426 if (visible != staffIdx) {
427 SysStaff* vs = staff(visible);
428 for (InstrumentName* t : qAsConst(s->instrumentNames)) {
429 t->setTrack(visible * VOICES);
430 vs->instrumentNames.append(t);
431 }
432 s->instrumentNames.clear();
433 s = vs;
434 }
435
436 for (InstrumentName* t : qAsConst(s->instrumentNames)) {
437 //
438 // override Text->layout()
439 //
440 qreal y1, y2;
441 switch (t->layoutPos()) {
442 default:
443 case 0: // center at part
444 y1 = s->bbox().top();
445 s2 = staff(staffIdx);
446 for (int i = staffIdx + nstaves - 1; i > 0; --i) {
447 SysStaff* s3 = staff(i);
448 if (s3->show()) {
449 s2 = s3;
450 break;
451 }
452 }
453 y2 = s2->bbox().bottom();
454 break;
455 case 1: // center at first staff
456 y1 = s->bbox().top();
457 y2 = s->bbox().bottom();
458 break;
459 case 2: // center between first and second staff
460 y1 = s->bbox().top();
461 y2 = staff(staffIdx + 1)->bbox().bottom();
462 break;
463 case 3: // center at second staff
464 y1 = staff(staffIdx + 1)->bbox().top();
465 y2 = staff(staffIdx + 1)->bbox().bottom();
466 break;
467 case 4: // center between first and second staff
468 y1 = staff(staffIdx + 1)->bbox().top();
469 y2 = staff(staffIdx + 2)->bbox().bottom();
470 break;
471 case 5: // center at third staff
472 y1 = staff(staffIdx + 2)->bbox().top();
473 y2 = staff(staffIdx + 2)->bbox().bottom();
474 break;
475 }
476 t->rypos() = y1 + (y2 - y1) * .5 + t->offset().y();
477 }
478 }
479 staffIdx += nstaves;
480 }
481 }
482
483 //---------------------------------------------------------
484 // addBrackets
485 // Add brackets in front of this measure, typically behind a HBox
486 //---------------------------------------------------------
487
addBrackets(Measure * measure)488 void System::addBrackets(Measure* measure)
489 {
490 if (_staves.empty()) // ignore vbox
491 return;
492
493 int nstaves = _staves.size();
494
495 //---------------------------------------------------
496 // find x position of staves
497 // create brackets
498 //---------------------------------------------------
499
500 int columns = getBracketsColumnsCount();
501
502 QList<Bracket*> bl;
503 bl.swap(_brackets);
504
505 for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
506 Staff* s = score()->staff(staffIdx);
507 for (int i = 0; i < columns; ++i) {
508 for (auto bi : s->brackets()) {
509 if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET)
510 continue;
511 createBracket(bi, i, staffIdx, bl, measure);
512 }
513 }
514 if (!staff(staffIdx)->show())
515 continue;
516 }
517
518 //---------------------------------------------------
519 // layout brackets
520 //---------------------------------------------------
521
522 setBracketsXPosition(measure->x());
523
524 _brackets.append(bl);
525 }
526
527 //---------------------------------------------------------
528 // createBracket
529 // Create a bracket if it spans more then one visible system
530 // If measure is NULL adds the bracket in front of the system, else in front of the measure.
531 // Returns the bracket if it got created, else NULL
532 //---------------------------------------------------------
533
createBracket(Ms::BracketItem * bi,int column,int staffIdx,QList<Ms::Bracket * > & bl,Measure * measure)534 Bracket* System::createBracket(Ms::BracketItem* bi, int column, int staffIdx, QList<Ms::Bracket *>& bl, Measure* measure)
535 {
536 int nstaves = _staves.size();
537 int firstStaff = staffIdx;
538 int lastStaff = staffIdx + bi->bracketSpan() - 1;
539 if (lastStaff >= nstaves)
540 lastStaff = nstaves - 1;
541
542 for (; firstStaff <= lastStaff; ++firstStaff) {
543 if (score()->staff(firstStaff)->show())
544 break;
545 }
546 for (; lastStaff >= firstStaff; --lastStaff) {
547 if (score()->staff(lastStaff)->show())
548 break;
549 }
550 int span = lastStaff - firstStaff + 1;
551 //
552 // do not show bracket if it only spans one
553 // system due to some invisible staves
554 //
555 if ((span > 1) || (bi->bracketSpan() == span)) {
556 //
557 // this bracket is visible
558 //
559 Bracket* b = 0;
560 int track = staffIdx * VOICES;
561 for (int k = 0; k < bl.size(); ++k) {
562 if (bl[k]->track() == track && bl[k]->column() == column && bl[k]->bracketType() == bi->bracketType() && bl[k]->measure() == measure) {
563 b = bl.takeAt(k);
564 break;
565 }
566 }
567 if (b == 0) {
568 b = new Bracket(score());
569 b->setBracketItem(bi);
570 b->setGenerated(true);
571 b->setTrack(track);
572 b->setMeasure(measure);
573 }
574 add(b);
575 b->setStaffSpan(firstStaff, lastStaff);
576 return b;
577 }
578
579 return nullptr;
580 }
581
getBracketsColumnsCount()582 int System::getBracketsColumnsCount()
583 {
584 int columns = 0;
585 int nstaves = _staves.size();
586 for (int idx = 0; idx < nstaves; ++idx) {
587 for (auto bi : score()->staff(idx)->brackets())
588 columns = qMax(columns, bi->column() + 1);
589 }
590 return columns;
591 }
592
setBracketsXPosition(const qreal xPosition)593 void System::setBracketsXPosition(const qreal xPosition)
594 {
595 qreal bracketDistance = score()->styleP(Sid::bracketDistance);
596 for (Bracket* b1 : qAsConst(_brackets)) {
597 qreal xOffset = 0;
598 for (const Bracket* b2 : qAsConst(_brackets)) {
599 bool b1FirstStaffInB2 = (b1->firstStaff() >= b2->firstStaff() && b1->firstStaff() <= b2->lastStaff());
600 bool b1LastStaffInB2 = (b1->lastStaff() >= b2->firstStaff() && b1->lastStaff() <= b2->lastStaff());
601 if (b1->column() > b2->column() &&
602 (b1FirstStaffInB2 || b1LastStaffInB2))
603 xOffset += b2->width() + bracketDistance;
604 }
605 b1->rxpos() = xPosition - xOffset - b1->width();
606 }
607 }
608
609 //---------------------------------------------------------
610 // nextVisibleStaff
611 //---------------------------------------------------------
612
nextVisibleStaff(int staffIdx) const613 int System::nextVisibleStaff(int staffIdx) const
614 {
615 int i;
616 for (i = staffIdx + 1; i < _staves.size(); ++i) {
617 Staff* s = score()->staff(i);
618 SysStaff* ss = _staves[i];
619 if (s->show() && ss->show())
620 break;
621 }
622 return i;
623 }
624
625 //---------------------------------------------------------
626 // firstVisibleStaff
627 //---------------------------------------------------------
628
firstVisibleStaff() const629 int System::firstVisibleStaff() const
630 {
631 return nextVisibleStaff(-1);
632 }
633
634 //---------------------------------------------------------
635 // layout2
636 // called after measure layout
637 // adjusts staff distance
638 //---------------------------------------------------------
639
layout2()640 void System::layout2()
641 {
642 Box* vb = vbox();
643 if (vb) {
644 vb->layout();
645 setbbox(vb->bbox());
646 return;
647 }
648
649 setPos(0.0, 0.0);
650 QList<std::pair<int,SysStaff*>> visibleStaves;
651
652 for (int i = 0; i < _staves.size(); ++i) {
653 Staff* s = score()->staff(i);
654 SysStaff* ss = _staves[i];
655 if (s->show() && ss->show())
656 visibleStaves.append(std::pair<int,SysStaff*>(i, ss));
657 else
658 ss->setbbox(QRectF()); // already done in layout() ?
659 }
660
661 qreal _spatium = spatium();
662 qreal y = 0.0;
663 qreal minVerticalDistance = score()->styleP(Sid::minVerticalDistance);
664 qreal staffDistance = score()->styleP(Sid::staffDistance);
665 qreal akkoladeDistance = score()->styleP(Sid::akkoladeDistance);
666 if (score()->enableVerticalSpread()) {
667 staffDistance = score()->styleP(Sid::minStaffSpread);
668 akkoladeDistance = score()->styleP(Sid::minStaffSpread);
669 }
670
671 if (visibleStaves.empty()) {
672 qDebug("====no visible staves, staves %d, score staves %d", _staves.size(), score()->nstaves());
673 return;
674 }
675
676 for (auto i = visibleStaves.begin();; ++i) {
677 SysStaff* ss = i->second;
678 int si1 = i->first;
679 Staff* staff = score()->staff(si1);
680 auto ni = i + 1;
681
682 qreal dist = staff->height();
683 qreal yOffset;
684 qreal h;
685 if (staff->lines(Fraction(0, 1)) == 1) {
686 yOffset = _spatium * BARLINE_SPAN_1LINESTAFF_TO * 0.5;
687 h = _spatium * (BARLINE_SPAN_1LINESTAFF_TO - BARLINE_SPAN_1LINESTAFF_FROM) * 0.5;
688 }
689 else {
690 yOffset = 0.0;
691 h = staff->height();
692 }
693 if (ni == visibleStaves.end()) {
694 // ss->setYOff(staff->lines(0) == 1 ? _spatium * staff->mag(0) : 0.0);
695 ss->setYOff(yOffset);
696 ss->bbox().setRect(_leftMargin, y - yOffset, width() - _leftMargin, h);
697 ss->saveLayout();
698 break;
699 }
700
701 int si2 = ni->first;
702 Staff* staff2 = score()->staff(si2);
703
704 #if 1
705 if (staff->part() == staff2->part()) {
706 Measure* m = firstMeasure();
707 qreal mag = m ? staff->mag(m->tick()) : 1.0;
708 dist += akkoladeDistance * mag;
709 }
710 else {
711 dist += staffDistance;
712 }
713 #else
714 // TODO: provide style setting or brace property to allow braces to also define a grand staff
715 switch (staff2->innerBracket()) {
716 case BracketType::BRACE:
717 dist += akkoladeDistance;
718 break;
719 case BracketType::NORMAL:
720 case BracketType::SQUARE:
721 case BracketType::LINE:
722 case BracketType::NO_BRACKET:
723 dist += staffDistance;
724 break;
725 }
726 #endif
727 dist += staff2->userDist();
728 #if 0
729 for (MeasureBase* mb : ml) {
730 if (!mb->isMeasure())
731 continue;
732 Measure* m = toMeasure(mb);
733 Shape& s1 = m->staffShape(si1);
734 Shape& s2 = m->staffShape(si2);
735
736 qreal d = score()->lineMode() ? 0.0 : s1.minVerticalDistance(s2);
737 dist = qMax(dist, d + minVerticalDistance);
738
739 Spacer* sp = m->vspacerDown(si1);
740 if (sp) {
741 if (sp->spacerType() == SpacerType::FIXED) {
742 dist = staff->height() + sp->gap();
743 break;
744 }
745 else
746 dist = qMax(dist, staff->height() + sp->gap());
747 }
748 sp = m->vspacerUp(si2);
749 if (sp)
750 dist = qMax(dist, sp->gap());
751 }
752 #else
753 bool fixedSpace = false;
754 for (MeasureBase* mb : ml) {
755 if (!mb->isMeasure())
756 continue;
757 Measure* m = toMeasure(mb);
758 Spacer* sp = m->vspacerDown(si1);
759 if (sp) {
760 if (sp->spacerType() == SpacerType::FIXED) {
761 dist = staff->height() + sp->gap();
762 fixedSpace = true;
763 break;
764 }
765 else
766 dist = qMax(dist, staff->height() + sp->gap());
767 }
768 sp = m->vspacerUp(si2);
769 if (sp)
770 dist = qMax(dist, sp->gap() + staff->height());
771 }
772 if (!fixedSpace) {
773 #if 1
774 // check minimum distance to next staff
775 // note that in continuous view, we normally only have a partial skyline for the system
776 // a full one is only built when triggering a full layout
777 // therefore, we don't know the value we get from minDistance will actually be enough
778 // so we remember the value between layouts and increase it when necessary
779 // (the first layout on switching to continuous view gives us good initial values)
780 // the result is space is good to start and grows as needed
781 // it does not, however, shrink when possible - only by trigger a full layout
782 // (such as by toggling to page view and back)
783 qreal d = ss->skyline().minDistance(System::staff(si2)->skyline());
784 if (score()->lineMode()) {
785 qreal previousDist = ss->continuousDist();
786 if (d > previousDist)
787 ss->setContinuousDist(d);
788 else
789 d = previousDist;
790 }
791 #else
792 // the code above does do a partial skyline comparison in continuous view
793 // we hope this does not come at too high a performance penalty for large scores
794 // if necessary, we can replace the code above with this
795 // the principle is the same, but we skip the skyline calculation on all but full layout
796 // the result is space between staves is correct to start but does not grow as needed
797 qreal d;
798 if (score()->lineMode()) {
799 d = ss->continuousDist();
800 if (d < 0.0) {
801 d = ss->skyline().minDistance(System::staff(si2)->skyline());
802 ss->setContinuousDist(d);
803 }
804 }
805 else
806 d = ss->skyline().minDistance(System::staff(si2)->skyline());
807 #endif
808 dist = qMax(dist, d + minVerticalDistance);
809 }
810 #endif
811
812 // ss->setYOff(staff->lines(0) == 1 ? _spatium * staff->mag(0) : 0.0);
813 ss->setYOff(yOffset);
814 ss->bbox().setRect(_leftMargin, y - yOffset, width() - _leftMargin, h);
815 ss->saveLayout();
816 y += dist;
817 }
818
819 _systemHeight = staff(visibleStaves.back().first)->bbox().bottom();
820 setHeight(_systemHeight);
821
822 setMeasureHeight(_systemHeight);
823
824 //---------------------------------------------------
825 // layout brackets vertical position
826 //---------------------------------------------------
827
828 layoutBracketsVertical();
829
830 //---------------------------------------------------
831 // layout instrument names
832 //---------------------------------------------------
833
834 layoutInstrumentNames();
835
836 //---------------------------------------------------
837 // layout cross-staff slurs and ties
838 //---------------------------------------------------
839
840 Fraction stick = measures().front()->tick();
841 Fraction etick = measures().back()->endTick();
842 auto spanners = score()->spannerMap().findOverlapping(stick.ticks(), etick.ticks());
843
844 std::vector<Spanner*> spanner;
845 for (auto interval : spanners) {
846 Spanner* sp = interval.value;
847 if (sp->tick() < etick && sp->tick2() >= stick) {
848 if (sp->isSlur()) {
849 ChordRest* scr = sp->startCR();
850 ChordRest* ecr = sp->endCR();
851 int idx = sp->vStaffIdx();
852 if (scr && ecr && (scr->vStaffIdx() != idx || ecr->vStaffIdx() != idx))
853 sp->layoutSystem(this);
854 }
855 }
856 }
857
858 }
859
860 //---------------------------------------------------------
861 // restoreLayout2
862 //---------------------------------------------------------
863
restoreLayout2()864 void System::restoreLayout2()
865 {
866 if (vbox())
867 return;
868
869 for (SysStaff* s : _staves)
870 s->restoreLayout();
871
872 setHeight(_systemHeight);
873 setMeasureHeight(_systemHeight);
874 }
875
876 //---------------------------------------------------------
877 // setInstrumentNames
878 //---------------------------------------------------------
879
setInstrumentNames(bool longName,Fraction tick)880 void System::setInstrumentNames(bool longName, Fraction tick)
881 {
882 //
883 // remark: add/remove instrument names is not undo/redoable
884 // as add/remove of systems is not undoable
885 //
886 if (vbox()) // ignore vbox
887 return;
888 if (!score()->showInstrumentNames()
889 || (score()->styleB(Sid::hideInstrumentNameIfOneInstrument) && score()->parts().size() == 1)) {
890 for (SysStaff* staff : qAsConst(_staves)) {
891 foreach (InstrumentName* t, staff->instrumentNames)
892 score()->removeElement(t);
893 }
894 return;
895 }
896
897 int staffIdx = 0;
898 for (SysStaff* staff : qAsConst(_staves)) {
899 Staff* s = score()->staff(staffIdx);
900 if (!s->isTop() || !s->show()) {
901 for (InstrumentName* t : qAsConst(staff->instrumentNames))
902 score()->removeElement(t);
903 ++staffIdx;
904 continue;
905 }
906
907 Part* part = s->part();
908 const QList<StaffName>& names = longName? part->longNames(tick) : part->shortNames(tick);
909
910 int idx = 0;
911 for (const StaffName& sn : names) {
912 InstrumentName* iname = staff->instrumentNames.value(idx);
913 if (iname == 0) {
914 iname = new InstrumentName(score());
915 // iname->setGenerated(true);
916 iname->setParent(this);
917 iname->setTrack(staffIdx * VOICES);
918 iname->setInstrumentNameType(longName ? InstrumentNameType::LONG : InstrumentNameType::SHORT);
919 iname->setLayoutPos(sn.pos());
920 score()->addElement(iname);
921 }
922 iname->setXmlText(sn.name());
923 ++idx;
924 }
925 for (; idx < staff->instrumentNames.size(); ++idx)
926 score()->removeElement(staff->instrumentNames[idx]);
927 ++staffIdx;
928 }
929 }
930
931 //---------------------------------------------------------
932 // y2staff
933 //---------------------------------------------------------
934
935 /**
936 Return staff number for canvas relative y position \a y
937 or -1 if not found.
938
939 To allow drag and drop above and below the staff, the actual y range
940 considered "inside" the staff is increased by "margin".
941 */
942
y2staff(qreal y) const943 int System::y2staff(qreal y) const
944 {
945 y -= pos().y();
946 int idx = 0;
947 qreal margin = spatium() * 2;
948 foreach (SysStaff* s, _staves) {
949 qreal y1 = s->bbox().top() - margin;
950 qreal y2 = s->bbox().bottom() + margin;
951 if (y >= y1 && y < y2)
952 return idx;
953 ++idx;
954 }
955 return -1;
956 }
957
958 //---------------------------------------------------------
959 // searchStaff
960 /// Finds a staff which y position is most close to the
961 /// given \p y.
962 /// \param y The y coordinate in system coordinates.
963 /// \param preferredStaff If not -1, will give more space
964 /// to a staff with the given number when searching it by
965 /// coordinate.
966 /// \returns Number of the found staff.
967 //---------------------------------------------------------
968
searchStaff(qreal y,int preferredStaff,qreal spacingFactor) const969 int System::searchStaff(qreal y, int preferredStaff /* = -1 */, qreal spacingFactor) const
970 {
971 int i = 0;
972 const int nstaves = score()->nstaves();
973 for (; i < nstaves;) {
974 SysStaff* stff = staff(i);
975 if (!stff->show() || !score()->staff(i)->show()) {
976 ++i;
977 continue;
978 }
979 int ni = i;
980 for (;;) {
981 ++ni;
982 if (ni == nstaves || (staff(ni)->show() && score()->staff(ni)->show()))
983 break;
984 }
985
986 qreal sy2;
987 if (ni != nstaves) {
988 SysStaff* nstaff = staff(ni);
989 qreal s1y2 = stff->bbox().y() + stff->bbox().height();
990 if (i == preferredStaff)
991 sy2 = s1y2 + (nstaff->bbox().y() - s1y2);
992 else if (ni == preferredStaff)
993 sy2 = s1y2;
994 else
995 sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * spacingFactor;
996 }
997 else
998 sy2 = page()->height() - pos().y();
999 if (y > sy2) {
1000 i = ni;
1001 continue;
1002 }
1003 break;
1004 }
1005 return i;
1006 }
1007
1008 //---------------------------------------------------------
1009 // add
1010 //---------------------------------------------------------
1011
add(Element * el)1012 void System::add(Element* el)
1013 {
1014 if (!el)
1015 return;
1016 // qDebug("%p System::add: %p %s", this, el, el->name());
1017
1018 el->setParent(this);
1019 switch(el->type()) {
1020 case ElementType::INSTRUMENT_NAME:
1021 // qDebug(" staffIdx %d, staves %d", el->staffIdx(), _staves.size());
1022 _staves[el->staffIdx()]->instrumentNames.append(toInstrumentName(el));
1023 break;
1024
1025 case ElementType::BEAM:
1026 score()->addElement(el);
1027 break;
1028
1029 case ElementType::BRACKET: {
1030 Bracket* b = toBracket(el);
1031 #if 0
1032 int staffIdx = b->staffIdx();
1033 int column = b->column();
1034 if (column == -1) {
1035 column = 0;
1036 for (const Bracket* bb : _brackets) {
1037 if (staffIdx >= bb->firstStaff() && staffIdx <= bb->lastStaff())
1038 ++column;
1039 }
1040 // b->setLevel(column);
1041 // b->setSpan(1);
1042 }
1043 // b->staff()->setBracket(column, b->bracketType());
1044 // b->staff()->setBracketSpan(column, b->span());
1045 #endif
1046 _brackets.append(b);
1047 }
1048 break;
1049
1050 case ElementType::MEASURE:
1051 case ElementType::HBOX:
1052 case ElementType::VBOX:
1053 case ElementType::TBOX:
1054 case ElementType::FBOX:
1055 score()->addElement(el);
1056 break;
1057 case ElementType::TEXTLINE_SEGMENT:
1058 case ElementType::HAIRPIN_SEGMENT:
1059 case ElementType::OTTAVA_SEGMENT:
1060 case ElementType::TRILL_SEGMENT:
1061 case ElementType::VIBRATO_SEGMENT:
1062 case ElementType::VOLTA_SEGMENT:
1063 case ElementType::SLUR_SEGMENT:
1064 case ElementType::TIE_SEGMENT:
1065 case ElementType::PEDAL_SEGMENT:
1066 case ElementType::LYRICSLINE_SEGMENT:
1067 case ElementType::GLISSANDO_SEGMENT:
1068 case ElementType::LET_RING_SEGMENT:
1069 case ElementType::PALM_MUTE_SEGMENT:
1070 {
1071 SpannerSegment* ss = toSpannerSegment(el);
1072 #ifndef NDEBUG
1073 if (_spannerSegments.contains(ss))
1074 qDebug("System::add() %s %p already there", ss->name(), ss);
1075 else
1076 #endif
1077 _spannerSegments.append(ss);
1078 }
1079 break;
1080
1081 case ElementType::SYSTEM_DIVIDER:
1082 {
1083 SystemDivider* sd = toSystemDivider(el);
1084 if (sd->dividerType() == SystemDivider::Type::LEFT)
1085 _systemDividerLeft = sd;
1086 else
1087 _systemDividerRight = sd;
1088 }
1089 break;
1090
1091 default:
1092 qDebug("System::add(%s) not implemented", el->name());
1093 break;
1094 }
1095 }
1096
1097 //---------------------------------------------------------
1098 // remove
1099 //---------------------------------------------------------
1100
remove(Element * el)1101 void System::remove(Element* el)
1102 {
1103 switch (el->type()) {
1104 case ElementType::INSTRUMENT_NAME:
1105 _staves[el->staffIdx()]->instrumentNames.removeOne(toInstrumentName(el));
1106 break;
1107 case ElementType::BEAM:
1108 score()->removeElement(el);
1109 break;
1110 case ElementType::BRACKET:
1111 {
1112 Bracket* b = toBracket(el);
1113 if (!_brackets.removeOne(b))
1114 qDebug("System::remove: bracket not found");
1115 }
1116 break;
1117 case ElementType::MEASURE:
1118 case ElementType::HBOX:
1119 case ElementType::VBOX:
1120 case ElementType::TBOX:
1121 case ElementType::FBOX:
1122 score()->removeElement(el);
1123 break;
1124 case ElementType::TEXTLINE_SEGMENT:
1125 case ElementType::HAIRPIN_SEGMENT:
1126 case ElementType::OTTAVA_SEGMENT:
1127 case ElementType::TRILL_SEGMENT:
1128 case ElementType::VIBRATO_SEGMENT:
1129 case ElementType::VOLTA_SEGMENT:
1130 case ElementType::SLUR_SEGMENT:
1131 case ElementType::TIE_SEGMENT:
1132 case ElementType::PEDAL_SEGMENT:
1133 case ElementType::LYRICSLINE_SEGMENT:
1134 case ElementType::GLISSANDO_SEGMENT:
1135 if (!_spannerSegments.removeOne(toSpannerSegment(el))) {
1136 qDebug("System::remove: %p(%s) not found, score %p", el, el->name(), score());
1137 Q_ASSERT(score() == el->score());
1138 }
1139 break;
1140 case ElementType::SYSTEM_DIVIDER:
1141 if (el == _systemDividerLeft)
1142 _systemDividerLeft = 0;
1143 else {
1144 Q_ASSERT(_systemDividerRight == el);
1145 _systemDividerRight = 0;
1146 }
1147 break;
1148
1149 default:
1150 qDebug("System::remove(%s) not implemented", el->name());
1151 break;
1152 }
1153 }
1154
1155 //---------------------------------------------------------
1156 // change
1157 //---------------------------------------------------------
1158
change(Element * o,Element * n)1159 void System::change(Element* o, Element* n)
1160 {
1161 remove(o);
1162 add(n);
1163 }
1164
1165 //---------------------------------------------------------
1166 // snap
1167 //---------------------------------------------------------
1168
snap(const Fraction & tick,const QPointF p) const1169 Fraction System::snap(const Fraction& tick, const QPointF p) const
1170 {
1171 for (const MeasureBase* m : ml) {
1172 if (p.x() < m->x() + m->width())
1173 return toMeasure(m)->snap(tick, p - m->pos()); //TODO: MeasureBase
1174 }
1175 return toMeasure(ml.back())->snap(tick, p-pos()); //TODO: MeasureBase
1176 }
1177
1178 //---------------------------------------------------------
1179 // snap
1180 //---------------------------------------------------------
1181
snapNote(const Fraction & tick,const QPointF p,int staff) const1182 Fraction System::snapNote(const Fraction& tick, const QPointF p, int staff) const
1183 {
1184 for (const MeasureBase* m : ml) {
1185 if (p.x() < m->x() + m->width())
1186 return toMeasure(m)->snapNote(tick, p - m->pos(), staff); //TODO: MeasureBase
1187 }
1188 return toMeasure(ml.back())->snap(tick, p-pos()); // TODO: MeasureBase
1189 }
1190
1191 //---------------------------------------------------------
1192 // firstMeasure
1193 //---------------------------------------------------------
1194
firstMeasure() const1195 Measure* System::firstMeasure() const
1196 {
1197 auto i = std::find_if(ml.begin(), ml.end(), [](MeasureBase* mb){ return mb->isMeasure(); });
1198 return i != ml.end() ? toMeasure(*i) : 0;
1199 }
1200
1201 //---------------------------------------------------------
1202 // lastMeasure
1203 //---------------------------------------------------------
1204
lastMeasure() const1205 Measure* System::lastMeasure() const
1206 {
1207 auto i = std::find_if(ml.rbegin(), ml.rend(), [](MeasureBase* mb){return mb->isMeasure();});
1208 return i != ml.rend() ? toMeasure(*i) : 0;
1209 }
1210
1211 //---------------------------------------------------------
1212 // nextMeasure
1213 //---------------------------------------------------------
1214
nextMeasure(const MeasureBase * m) const1215 MeasureBase* System::nextMeasure(const MeasureBase* m) const
1216 {
1217 if (m == ml.back())
1218 return 0;
1219 MeasureBase* nm = m->next();
1220 if (nm->isMeasure() && score()->styleB(Sid::createMultiMeasureRests) && toMeasure(nm)->hasMMRest())
1221 nm = toMeasure(nm)->mmRest();
1222 return nm;
1223 }
1224
1225 //---------------------------------------------------------
1226 // scanElements
1227 // collect all visible elements
1228 //---------------------------------------------------------
1229
scanElements(void * data,void (* func)(void *,Element *),bool all)1230 void System::scanElements(void* data, void (*func)(void*, Element*), bool all)
1231 {
1232 if (vbox())
1233 return;
1234 for (Bracket* b : qAsConst(_brackets))
1235 func(data, b);
1236
1237 if (_systemDividerLeft)
1238 func(data, _systemDividerLeft);
1239 if (_systemDividerRight)
1240 func(data, _systemDividerRight);
1241
1242 int idx = 0;
1243 for (const SysStaff* st : qAsConst(_staves)) {
1244 if (all || st->show()) {
1245 for (InstrumentName* t : st->instrumentNames)
1246 func(data, t);
1247 }
1248 ++idx;
1249 }
1250 for (SpannerSegment* ss : qAsConst(_spannerSegments)) {
1251 int staffIdx = ss->spanner()->staffIdx();
1252 if (staffIdx == -1) {
1253 qDebug("System::scanElements: staffIDx == -1: %s %p", ss->spanner()->name(), ss->spanner());
1254 staffIdx = 0;
1255 }
1256 bool v = true;
1257 Spanner* spanner = ss->spanner();
1258 if (spanner->anchor() == Spanner::Anchor::SEGMENT || spanner->anchor() == Spanner::Anchor::CHORD) {
1259 Element* se = spanner->startElement();
1260 Element* ee = spanner->endElement();
1261 bool v1 = true;
1262 if (se && se->isChordRest()) {
1263 ChordRest* cr = toChordRest(se);
1264 Measure* m = cr->measure();
1265 v1 = m->visible(cr->staffIdx());
1266 }
1267 bool v2 = true;
1268 if (!v1 && ee && ee->isChordRest()) {
1269 ChordRest* cr = toChordRest(ee);
1270 Measure* m = cr->measure();
1271 v2 = m->visible(cr->staffIdx());
1272 }
1273 v = v1 || v2; // hide spanner if both chords are hidden
1274 }
1275 if (all || (score()->staff(staffIdx)->show() && _staves[staffIdx]->show() && v)
1276 || spanner->isVolta()
1277 || (spanner->isTextLine() && spanner->systemFlag()))
1278 ss->scanElements(data, func, all);
1279 }
1280 }
1281
1282 //---------------------------------------------------------
1283 // staffYpage
1284 // return page coordinates
1285 //---------------------------------------------------------
1286
staffYpage(int staffIdx) const1287 qreal System::staffYpage(int staffIdx) const
1288 {
1289 IF_ASSERT_FAILED(!(_staves.size() <= staffIdx || staffIdx < 0)) {
1290 return pagePos().y();
1291 }
1292 return _staves[staffIdx]->y() + y();
1293 }
1294
1295 //---------------------------------------------------------
1296 // staffCanvasYpage
1297 // return canvas coordinates
1298 //---------------------------------------------------------
1299
staffCanvasYpage(int staffIdx) const1300 qreal System::staffCanvasYpage(int staffIdx) const
1301 {
1302 return _staves[staffIdx]->y() + y() + page()->canvasPos().y();
1303 }
1304
1305 //---------------------------------------------------------
1306 // write
1307 //---------------------------------------------------------
1308
write(XmlWriter & xml) const1309 void System::write(XmlWriter& xml) const
1310 {
1311 xml.stag(this);
1312 if (_systemDividerLeft && _systemDividerLeft->isUserModified())
1313 _systemDividerLeft->write(xml);
1314 if (_systemDividerRight && _systemDividerRight->isUserModified())
1315 _systemDividerRight->write(xml);
1316 xml.etag();
1317 }
1318
1319 //---------------------------------------------------------
1320 // read
1321 //---------------------------------------------------------
1322
read(XmlReader & e)1323 void System::read(XmlReader& e)
1324 {
1325 while (e.readNextStartElement()) {
1326 const QStringRef& tag(e.name());
1327 if (tag == "SystemDivider") {
1328 SystemDivider* sd = new SystemDivider(score());
1329 sd->read(e);
1330 add(sd);
1331 }
1332 else
1333 e.unknown();
1334 }
1335 }
1336
1337 //---------------------------------------------------------
1338 // nextSegmentElement
1339 //---------------------------------------------------------
1340
nextSegmentElement()1341 Element* System::nextSegmentElement()
1342 {
1343 Measure* m = firstMeasure();
1344 if (m) {
1345 Segment* firstSeg = m->segments().first();
1346 if (firstSeg)
1347 return firstSeg->element(0);
1348 }
1349 return score()->lastElement();
1350 }
1351
1352 //---------------------------------------------------------
1353 // prevSegmentElement
1354 //---------------------------------------------------------
1355
prevSegmentElement()1356 Element* System::prevSegmentElement()
1357 {
1358 Segment* seg = firstMeasure()->first();
1359 Element* re = 0;
1360 while (!re) {
1361 seg = seg->prev1MM();
1362 if (!seg)
1363 return score()->firstElement();
1364
1365 if (seg->segmentType() == SegmentType::EndBarLine)
1366 score()->inputState().setTrack((score()->staves().size() - 1) * VOICES); //correction
1367
1368 re = seg->lastElement(score()->staves().size() - 1);
1369 }
1370 return re;
1371 }
1372
1373 //---------------------------------------------------------
1374 // minDistance
1375 // Return the minimum distance between this system and s2
1376 // without any element collisions.
1377 //
1378 // this - top system
1379 // s2 - bottom system
1380 //---------------------------------------------------------
1381
minDistance(System * s2) const1382 qreal System::minDistance(System* s2) const
1383 {
1384 if (vbox() && !s2->vbox())
1385 return qMax(vbox()->bottomGap(), s2->minTop());
1386 else if (!vbox() && s2->vbox())
1387 return qMax(s2->vbox()->topGap(), minBottom());
1388 else if (vbox() && s2->vbox())
1389 return s2->vbox()->topGap() + vbox()->bottomGap();
1390
1391 qreal minVerticalDistance = score()->styleP(Sid::minVerticalDistance);
1392 qreal dist = score()->enableVerticalSpread() ? styleP(Sid::minSystemSpread) : styleP(Sid::minSystemDistance);
1393 int firstStaff;
1394 int lastStaff;
1395
1396 for (firstStaff = 0; firstStaff < _staves.size()-1; ++firstStaff) {
1397 if (score()->staff(firstStaff)->show() && s2->staff(firstStaff)->show())
1398 break;
1399 }
1400 for (lastStaff = _staves.size() -1; lastStaff > 0; --lastStaff) {
1401 if (score()->staff(lastStaff)->show() && staff(lastStaff)->show())
1402 break;
1403 }
1404
1405 dist = qMax(dist, score()->staff(firstStaff)->userDist());
1406 fixedSpacer = nullptr;
1407
1408 for (MeasureBase* mb1 : ml) {
1409 if (mb1->isMeasure()) {
1410 Measure* m = toMeasure(mb1);
1411 Spacer* sp = m->vspacerDown(lastStaff);
1412 if (sp) {
1413 if (sp->spacerType() == SpacerType::FIXED) {
1414 fixedSpacer = sp;
1415 dist = sp->gap();
1416 break;
1417 }
1418 else
1419 dist = qMax(dist, sp->gap());
1420 }
1421 }
1422 }
1423 if (!getFixedSpacer()) {
1424 for (MeasureBase* mb2 : s2->ml) {
1425 if (mb2->isMeasure()) {
1426 Measure* m = toMeasure(mb2);
1427 Spacer* sp = m->vspacerUp(firstStaff);
1428 if (sp)
1429 dist = qMax(dist, sp->gap());
1430 }
1431 }
1432 qreal sld = staff(lastStaff)->skyline().minDistance(s2->staff(firstStaff)->skyline());
1433 sld -= staff(lastStaff)->bbox().height() - minVerticalDistance;
1434 dist = qMax(dist, sld);
1435 // dist = dist - staff(lastStaff)->bbox().height() + minVerticalDistance;
1436 }
1437 return dist;
1438 }
1439
1440 //---------------------------------------------------------
1441 // topDistance
1442 // return minimum distance to the above south skyline
1443 //---------------------------------------------------------
1444
topDistance(int staffIdx,const SkylineLine & s) const1445 qreal System::topDistance(int staffIdx, const SkylineLine& s) const
1446 {
1447 Q_ASSERT(!vbox());
1448 Q_ASSERT(!s.isNorth());
1449 // in continuous view, we only build a partial skyline for performance reasons
1450 // this means we cannot expect the minDistance calculation to produce meaningful results
1451 // so just give up on autoplace for spanners in continuous view
1452 // (or any other calculations that rely on this value)
1453 if (score()->lineMode())
1454 return 0.0;
1455 return s.minDistance(staff(staffIdx)->skyline().north());
1456 }
1457
1458 //---------------------------------------------------------
1459 // bottomDistance
1460 //---------------------------------------------------------
1461
bottomDistance(int staffIdx,const SkylineLine & s) const1462 qreal System::bottomDistance(int staffIdx, const SkylineLine& s) const
1463 {
1464 Q_ASSERT(!vbox());
1465 Q_ASSERT(s.isNorth());
1466 // see note on topDistance() above
1467 if (score()->lineMode())
1468 return 0.0;
1469 return staff(staffIdx)->skyline().south().minDistance(s);
1470 }
1471
1472 //---------------------------------------------------------
1473 // firstVisibleSysStaff
1474 //---------------------------------------------------------
1475
firstVisibleSysStaff() const1476 int System::firstVisibleSysStaff() const
1477 {
1478 int nstaves = _staves.size();
1479 for (int i = 0; i < nstaves; ++i) {
1480 if (_staves[i]->show())
1481 return i;
1482 }
1483 qDebug("no sys staff");
1484 return -1;
1485 }
1486
1487 //---------------------------------------------------------
1488 // lastVisibleSysStaff
1489 //---------------------------------------------------------
1490
lastVisibleSysStaff() const1491 int System::lastVisibleSysStaff() const
1492 {
1493 int nstaves = _staves.size();
1494 for (int i = nstaves - 1; i >= 0; --i) {
1495 if (_staves[i]->show())
1496 return i;
1497 }
1498 qDebug("no sys staff");
1499 return -1;
1500 }
1501
1502 //---------------------------------------------------------
1503 // minTop
1504 // Return the minimum top margin.
1505 //---------------------------------------------------------
1506
minTop() const1507 qreal System::minTop() const
1508 {
1509 int si = firstVisibleSysStaff();
1510 SysStaff* s = si < 0 ? nullptr : staff(si);
1511 if (s)
1512 return -s->skyline().north().max();
1513 return 0.0;
1514 }
1515
1516 //---------------------------------------------------------
1517 // minBottom
1518 // Return the minimum bottom margin.
1519 //---------------------------------------------------------
1520
minBottom() const1521 qreal System::minBottom() const
1522 {
1523 if (vbox())
1524 return vbox()->bottomGap();
1525 int si = lastVisibleSysStaff();
1526 SysStaff* s = si < 0 ? nullptr : staff(si);
1527 if (s)
1528 return s->skyline().south().max() - s->bbox().height();
1529 return 0.0;
1530 }
1531
1532 //---------------------------------------------------------
1533 // spacerDistance
1534 // Return the distance needed due to spacers
1535 //---------------------------------------------------------
1536
spacerDistance(bool up) const1537 qreal System::spacerDistance(bool up) const
1538 {
1539 int staff = up ? firstVisibleSysStaff() : lastVisibleSysStaff();
1540 if (staff < 0)
1541 return 0.0;
1542 qreal dist = 0.0;
1543 for (MeasureBase* mb : measures()) {
1544 if (mb->isMeasure()) {
1545 Measure* m = toMeasure(mb);
1546 Spacer* sp = up ? m->vspacerUp(staff) : m->vspacerDown(staff);
1547 if (sp) {
1548 if (sp->spacerType() == SpacerType::FIXED) {
1549 dist = sp->gap();
1550 break;
1551 }
1552 else
1553 dist = qMax(dist, sp->gap());
1554 }
1555 }
1556 }
1557 return dist;
1558 }
1559
1560 //---------------------------------------------------------
1561 // firstNoteRestSegmentX
1562 // in System() coordinates
1563 // returns the position of the first note or rest,
1564 // or the position just after the last non-chordrest segment
1565 //---------------------------------------------------------
1566
firstNoteRestSegmentX(bool leading)1567 qreal System::firstNoteRestSegmentX(bool leading)
1568 {
1569 qreal margin = score()->spatium();
1570 for (const MeasureBase* mb : measures()) {
1571 if (mb->isMeasure()) {
1572 const Measure* measure = static_cast<const Measure*>(mb);
1573 for (const Segment* seg = measure->first(); seg; seg = seg->next()) {
1574 if (seg->isChordRestType()) {
1575 qreal noteRestPos = seg->measure()->pos().x() + seg->pos().x();
1576 if (!leading)
1577 return noteRestPos;
1578
1579 // first CR found; back up to previous segment
1580 seg = seg->prevActive();
1581 while (seg && seg->allElementsInvisible())
1582 seg = seg->prevActive();
1583 if (seg) {
1584 // find maximum width
1585 qreal width = 0.0;
1586 int n = score()->nstaves();
1587 for (int i = 0; i < n; ++i) {
1588 if (!staff(i)->show())
1589 continue;
1590 Element* e = seg->element(i * VOICES);
1591 if (e && e->addToSkyline())
1592 width = qMax(width, e->pos().x() + e->bbox().right());
1593 }
1594 return qMin(seg->measure()->pos().x() + seg->pos().x() + width + margin, noteRestPos);
1595 }
1596 else
1597 return margin;
1598 }
1599 }
1600 }
1601 }
1602 qDebug("firstNoteRestSegmentX: did not find segment");
1603 return margin;
1604 }
1605
1606 //---------------------------------------------------------
1607 // moveBracket
1608 //---------------------------------------------------------
1609
moveBracket(int,int,int)1610 void System::moveBracket(int /*staffIdx*/, int /*srcCol*/, int /*dstCol*/)
1611 {
1612 #if 0
1613 printf("System::moveBracket\n");
1614 if (vbox())
1615 return;
1616 for (Bracket* b : _brackets) {
1617 if (b->staffIdx() == staffIdx && b->column() == srcCol)
1618 b->setLevel(dstCol);
1619 }
1620 #endif
1621 }
1622
1623 //---------------------------------------------------------
1624 // pageBreak
1625 //---------------------------------------------------------
1626
pageBreak() const1627 bool System::pageBreak() const
1628 {
1629 return ml.empty() ? false : ml.back()->pageBreak();
1630 }
1631
1632 //---------------------------------------------------------
1633 // endTick
1634 //---------------------------------------------------------
1635
endTick() const1636 Fraction System::endTick() const
1637 {
1638 return measures().back()->endTick();
1639 }
1640
1641 //---------------------------------------------------------
1642 // firstSysStaffOfPart
1643 //---------------------------------------------------------
1644
firstSysStaffOfPart(const Part * part) const1645 int System::firstSysStaffOfPart(const Part* part) const
1646 {
1647 int staffIdx { 0 };
1648 for (const Part* p : score()->parts()) {
1649 if (p == part)
1650 return staffIdx;
1651 staffIdx += p->nstaves();
1652 }
1653 return -1; // Part not found.
1654 }
1655
1656 //---------------------------------------------------------
1657 // firstVisibleSysStaffOfPart
1658 //---------------------------------------------------------
1659
firstVisibleSysStaffOfPart(const Part * part) const1660 int System::firstVisibleSysStaffOfPart(const Part* part) const
1661 {
1662 int firstIdx = firstSysStaffOfPart(part);
1663 for (int idx = firstIdx ; idx < firstIdx + part->nstaves(); ++idx) {
1664 if (staff(idx)->show())
1665 return idx;
1666 }
1667 return -1; // No visible staves on this part.
1668 }
1669
1670 //---------------------------------------------------------
1671 // lastSysStaffOfPart
1672 //---------------------------------------------------------
1673
lastSysStaffOfPart(const Part * part) const1674 int System::lastSysStaffOfPart(const Part* part) const
1675 {
1676 int firstIdx = firstSysStaffOfPart(part);
1677 if (firstIdx < 0)
1678 return -1; // Part not found.
1679 return firstIdx + part->nstaves() - 1;
1680 }
1681
1682 //---------------------------------------------------------
1683 // lastVisibleSysStaffOfPart
1684 //---------------------------------------------------------
1685
lastVisibleSysStaffOfPart(const Part * part) const1686 int System::lastVisibleSysStaffOfPart(const Part* part) const
1687 {
1688 for (int idx = lastSysStaffOfPart(part); idx >= firstSysStaffOfPart(part); --idx) {
1689 if (staff(idx)->show())
1690 return idx;
1691 }
1692 return -1; // No visible staves on this part.
1693 }
1694 }
1695