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