Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : /**
24 : \file
25 : Implementation of classes SysStaff and System.
26 : */
27 :
28 : #include "system.h"
29 :
30 : #include "iengravingconfiguration.h" // IWYU pragma: keep
31 :
32 : #include "style/style.h"
33 :
34 : #include "../editing/navigation.h"
35 :
36 : #include "beam.h"
37 : #include "box.h"
38 : #include "bracket.h"
39 : #include "bracketitem.h"
40 : #include "chordrest.h"
41 : #include "factory.h"
42 : #include "measure.h"
43 : #include "mscore.h"
44 : #include "page.h"
45 : #include "pagelockindicator.h"
46 : #include "part.h"
47 : #include "score.h"
48 : #include "segment.h"
49 : #include "spacer.h"
50 : #include "spanner.h"
51 : #include "staff.h"
52 : #include "staffvisibilityindicator.h"
53 : #include "system.h"
54 : #include "systemdivider.h"
55 : #include "systemlockindicator.h"
56 :
57 : #ifndef ENGRAVING_NO_ACCESSIBILITY
58 : #include "accessibility/accessibleitem.h"
59 : #endif
60 :
61 : #include "log.h"
62 :
63 : using namespace mu;
64 : using namespace mu::engraving;
65 :
66 : namespace mu::engraving {
67 : //---------------------------------------------------------
68 : // ~SysStaff
69 : //---------------------------------------------------------
70 :
71 9384 : SysStaff::~SysStaff()
72 : {
73 13096 : for (auto& pair : m_instrumentNames) {
74 3712 : if (InstrumentName* n = pair.second) {
75 3712 : delete n;
76 : }
77 : }
78 9384 : }
79 :
80 : //---------------------------------------------------------
81 : // yBottom
82 : //---------------------------------------------------------
83 :
84 0 : double SysStaff::yBottom() const
85 : {
86 0 : return skyline().south().valid() ? skyline().south().max() : m_height;
87 : }
88 :
89 : //---------------------------------------------------------
90 : // saveLayout
91 : //---------------------------------------------------------
92 :
93 10686 : void SysStaff::saveLayout()
94 : {
95 10686 : m_height = bbox().height();
96 10686 : m_yPos = bbox().y();
97 10686 : }
98 :
99 : //---------------------------------------------------------
100 : // saveLayout
101 : //---------------------------------------------------------
102 :
103 11702 : void SysStaff::restoreLayout()
104 : {
105 11702 : bbox().setTop(m_yPos);
106 11702 : bbox().setHeight(m_height);
107 11702 : }
108 :
109 426608 : InstrumentName* SysStaff::name(InstrumentNameRole role) const
110 : {
111 426608 : if (muse::contains(m_instrumentNames, role)) {
112 40159 : return m_instrumentNames.at(role);
113 : }
114 :
115 386449 : return nullptr;
116 : }
117 :
118 3771 : void SysStaff::addInstrumentName(InstrumentName* n)
119 : {
120 3771 : InstrumentNameRole role = n->instrumentNameRole();
121 3771 : if (InstrumentName* curName = name(role)) {
122 0 : delete curName;
123 : }
124 :
125 3771 : m_instrumentNames[role] = n;
126 3771 : }
127 :
128 0 : void SysStaff::removeInstrumentName(InstrumentNameRole role)
129 : {
130 0 : if (InstrumentName* n = name(role)) {
131 0 : delete n;
132 : }
133 :
134 0 : m_instrumentNames[role] = nullptr;
135 0 : }
136 :
137 : //---------------------------------------------------------
138 : // System
139 : //---------------------------------------------------------
140 :
141 8361 : System::System(Score* parent)
142 8361 : : EngravingItem(ElementType::SYSTEM, parent)
143 : {
144 8361 : }
145 :
146 0 : void System::setOwnershipParent(Score* score)
147 : {
148 0 : EngravingItem::setOwnershipParent(score);
149 0 : }
150 :
151 854260 : EngravingItem* System::layoutParent() const
152 : {
153 854260 : return m_page;
154 : }
155 :
156 : //---------------------------------------------------------
157 : // ~System
158 : //---------------------------------------------------------
159 :
160 15792 : System::~System()
161 : {
162 7896 : if (m_page) {
163 7870 : muse::remove(m_page->systems(), this);
164 : }
165 11540 : for (SpannerSegment* ss : spannerSegments()) {
166 3644 : if (ss->system() == this) {
167 3644 : ss->setSystem(nullptr);
168 : }
169 : }
170 35600 : for (MeasureBase* mb : measures()) {
171 27704 : if (mb->system() == this) {
172 16882 : mb->setSystem(nullptr);
173 : }
174 : }
175 7896 : muse::DeleteAll(m_staves);
176 7896 : muse::DeleteAll(m_brackets);
177 7896 : muse::DeleteAll(m_systemLockIndicators);
178 7896 : if (m_staffVisibilityIndicator) {
179 47 : delete m_staffVisibilityIndicator;
180 : }
181 7896 : if (m_pageLockIndicator) {
182 32 : delete m_pageLockIndicator;
183 : }
184 15792 : }
185 :
186 : #ifndef ENGRAVING_NO_ACCESSIBILITY
187 0 : AccessibleItemPtr System::createAccessible()
188 : {
189 0 : return std::make_shared<AccessibleItem>(this, AccessibleItem::Group);
190 : }
191 :
192 : #endif
193 :
194 : //---------------------------------------------------------
195 : /// clear
196 : /// Clear layout of System
197 : //---------------------------------------------------------
198 :
199 1158 : void System::clear()
200 : {
201 4919 : for (MeasureBase* mb : measures()) {
202 3761 : if (mb->system() == this) {
203 3715 : mb->setSystem(nullptr);
204 : }
205 : }
206 1158 : m_ml.clear();
207 1800 : for (SpannerSegment* ss : m_spannerSegments) {
208 642 : if (ss->system() == this) {
209 642 : ss->setSystem(nullptr);
210 : }
211 : }
212 1158 : m_spannerSegments.clear();
213 : // _systemDividers are reused
214 1158 : }
215 :
216 : //---------------------------------------------------------
217 : // appendMeasure
218 : //---------------------------------------------------------
219 :
220 34456 : void System::appendMeasure(MeasureBase* mb)
221 : {
222 34456 : assert(!mb->isMeasure() || !(style().styleB(Sid::createMultiMeasureRests) && toMeasure(mb)->hasMMRest()));
223 34456 : mb->setSystem(this);
224 34456 : m_ml.push_back(mb);
225 34456 : }
226 :
227 : //---------------------------------------------------------
228 : // removeMeasure
229 : //---------------------------------------------------------
230 :
231 131 : void System::removeMeasure(MeasureBase* mb)
232 : {
233 131 : m_ml.erase(std::remove(m_ml.begin(), m_ml.end(), mb), m_ml.end());
234 131 : if (mb->system() == this) {
235 131 : mb->setSystem(nullptr);
236 : }
237 131 : }
238 :
239 : //---------------------------------------------------------
240 : // removeLastMeasure
241 : //---------------------------------------------------------
242 :
243 1652 : void System::removeLastMeasure()
244 : {
245 1652 : if (m_ml.empty()) {
246 0 : return;
247 : }
248 1652 : MeasureBase* mb = m_ml.back();
249 1652 : m_ml.pop_back();
250 1652 : if (mb->system() == this) {
251 1652 : mb->setSystem(nullptr);
252 : }
253 : }
254 :
255 29 : EngravingItemList System::accessibleChildren() const
256 : {
257 : // The measures are owned by the score and the spanner segments by their spanner,
258 : // but it is the system that places them, so the system is where the accessibility
259 : // tree finds them.
260 29 : EngravingItemList children = EngravingItem::accessibleChildren();
261 29 : children.insert(children.end(), m_ml.begin(), m_ml.end());
262 29 : children.insert(children.end(), m_spannerSegments.begin(), m_spannerSegments.end());
263 :
264 29 : return children;
265 0 : }
266 :
267 : //---------------------------------------------------------
268 : // vbox
269 : // a system can only contain one vertical frame
270 : //---------------------------------------------------------
271 :
272 130683 : Box* System::vbox() const
273 : {
274 130683 : if (!m_ml.empty() && m_ml.front()->isVBoxBase()) {
275 18377 : return toBox(m_ml.front());
276 : }
277 112306 : return nullptr;
278 : }
279 :
280 : //---------------------------------------------------------
281 : // insertStaff
282 : //---------------------------------------------------------
283 :
284 9951 : SysStaff* System::insertStaff(int idx)
285 : {
286 9951 : SysStaff* staff = new SysStaff;
287 9951 : if (idx) {
288 : // HACK: guess position
289 3479 : staff->bbox().setTop(m_staves[idx - 1]->y() + 6 * spatium());
290 : }
291 9951 : m_staves.insert(m_staves.begin() + idx, staff);
292 9951 : return staff;
293 : }
294 :
295 : //---------------------------------------------------------
296 : // removeStaff
297 : //---------------------------------------------------------
298 :
299 59 : void System::removeStaff(int idx)
300 : {
301 59 : m_staves.erase(m_staves.begin() + idx);
302 59 : }
303 :
304 : //---------------------------------------------------------
305 : // adjustStavesNumber
306 : //---------------------------------------------------------
307 :
308 7485 : void System::adjustStavesNumber(size_t nstaves)
309 : {
310 17436 : for (size_t i = m_staves.size(); i < nstaves; ++i) {
311 9951 : insertStaff(static_cast<int>(i));
312 : }
313 7485 : const size_t dn = m_staves.size() - nstaves;
314 7544 : for (size_t i = 0; i < dn; ++i) {
315 59 : removeStaff(static_cast<int>(m_staves.size()) - 1);
316 : }
317 7485 : }
318 :
319 14470 : size_t System::getBracketsColumnsCount() const
320 : {
321 14470 : size_t columns = 0;
322 36404 : for (const Staff* staff : score()->staves()) {
323 27894 : for (auto bi : score()->brackets(staff->idx())) {
324 5960 : columns = std::max(columns, bi->column() + 1);
325 : }
326 : }
327 14470 : return columns;
328 : }
329 :
330 7182 : void System::setHasStaffVisibilityIndicator(bool has)
331 : {
332 7182 : if (has && !m_staffVisibilityIndicator) {
333 48 : m_staffVisibilityIndicator = Factory::createStaffVisibilityIndicator(this);
334 7134 : } else if (!has && m_staffVisibilityIndicator) {
335 0 : delete m_staffVisibilityIndicator;
336 0 : m_staffVisibilityIndicator = nullptr;
337 : }
338 7182 : }
339 :
340 38 : bool System::isLocked() const
341 : {
342 38 : return m_ml.front()->isStartOfSystemLock();
343 : }
344 :
345 10195 : const RangeLock* System::systemLock() const
346 : {
347 10195 : return m_ml.front()->systemLock();
348 : }
349 :
350 99 : void System::addSystemLockIndicator(SystemLockIndicator* sli)
351 : {
352 99 : assert(sli);
353 99 : m_systemLockIndicators.push_back(sli);
354 99 : }
355 :
356 10195 : void System::deleteSystemLockIndicators()
357 : {
358 10195 : muse::DeleteAll(m_systemLockIndicators);
359 10195 : m_systemLockIndicators.clear();
360 10195 : }
361 :
362 56 : void System::setPageLockIndicator(PageLockIndicator* pli)
363 : {
364 56 : m_pageLockIndicator = pli;
365 56 : }
366 :
367 10195 : void System::deletePageLockIndicator()
368 : {
369 10195 : delete m_pageLockIndicator;
370 10195 : m_pageLockIndicator = nullptr;
371 10195 : }
372 :
373 : //---------------------------------------------------------
374 : // nextVisibleStaff
375 : //---------------------------------------------------------
376 :
377 321522 : staff_idx_t System::firstVisibleStaffFrom(staff_idx_t startStaffIdx) const
378 : {
379 326748 : for (staff_idx_t i = startStaffIdx; i < m_staves.size(); ++i) {
380 108208 : Staff* s = score()->staff(i);
381 108208 : SysStaff* ss = m_staves[i];
382 :
383 108208 : if (s->show() && ss->show()) {
384 102982 : return i;
385 : }
386 : }
387 :
388 218540 : return muse::nidx;
389 : }
390 :
391 319134 : staff_idx_t System::nextVisibleStaff(staff_idx_t staffIdx) const
392 : {
393 319134 : return firstVisibleStaffFrom(staffIdx + 1);
394 : }
395 :
396 2604 : staff_idx_t System::prevVisibleStaff(staff_idx_t startStaffIdx) const
397 : {
398 2604 : if (startStaffIdx == 0 || m_staves.empty()) {
399 2431 : return muse::nidx;
400 : }
401 173 : startStaffIdx = std::min(startStaffIdx, static_cast<staff_idx_t>(m_staves.size()));
402 :
403 173 : for (staff_idx_t i = startStaffIdx - 1;; --i) {
404 177 : Staff* s = score()->staff(i);
405 177 : SysStaff* ss = m_staves[i];
406 :
407 177 : if (s->show() && ss->show()) {
408 171 : return i;
409 : }
410 :
411 6 : if (i == 0) {
412 2 : break;
413 : }
414 4 : }
415 :
416 2 : return muse::nidx;
417 : }
418 :
419 32 : staff_idx_t System::lastVisibleStaff() const
420 : {
421 32 : const size_t nstaves = std::min(score()->nstaves(), m_staves.size());
422 32 : if (nstaves < 1 || m_staves.empty()) {
423 0 : return muse::nidx;
424 : }
425 :
426 32 : for (staff_idx_t i = nstaves - 1; ; --i) {
427 32 : Staff* staff = score()->staff(i);
428 32 : SysStaff* sysStaff = m_staves[i];
429 32 : if (staff->show() && sysStaff->show()) {
430 32 : return i;
431 : }
432 0 : if (i == 0) {
433 0 : break;
434 : }
435 0 : }
436 :
437 0 : return muse::nidx;
438 : }
439 :
440 : //---------------------------------------------------------
441 : // firstVisibleStaff
442 : //---------------------------------------------------------
443 :
444 2388 : staff_idx_t System::firstVisibleStaff() const
445 : {
446 2388 : return firstVisibleStaffFrom(0);
447 : }
448 :
449 : //---------------------------------------------------------
450 : // searchStaff
451 : /// Finds a staff which y position is most close to the
452 : /// given \p y.
453 : /// \param y The y coordinate in system coordinates.
454 : /// \param preferredStaff If not -1, will give more space
455 : /// to a staff with the given number when searching it by
456 : /// coordinate.
457 : /// \returns Number of the found staff.
458 : //---------------------------------------------------------
459 :
460 0 : staff_idx_t System::searchStaff(double y, staff_idx_t preferredStaff /* = invalid */, double spacingFactor) const
461 : {
462 0 : staff_idx_t i = 0;
463 0 : const size_t nstaves = score()->nstaves();
464 0 : for (; i < nstaves;) {
465 0 : SysStaff* stff = staff(i);
466 0 : if (!stff->show() || !score()->staff(i)->show()) {
467 0 : ++i;
468 0 : continue;
469 : }
470 0 : staff_idx_t ni = i;
471 : for (;;) {
472 0 : ++ni;
473 0 : if (ni == nstaves || (staff(ni)->show() && score()->staff(ni)->show())) {
474 0 : break;
475 : }
476 : }
477 :
478 : double sy2;
479 0 : if (ni != nstaves) {
480 0 : SysStaff* nstaff = staff(ni);
481 0 : double s1y2 = stff->bbox().y() + stff->bbox().height();
482 0 : if (i == preferredStaff) {
483 0 : sy2 = s1y2 + (nstaff->bbox().y() - s1y2);
484 0 : } else if (ni == preferredStaff) {
485 0 : sy2 = s1y2;
486 : } else {
487 0 : sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * spacingFactor;
488 : }
489 : } else {
490 0 : sy2 = page()->height() - pos().y();
491 : }
492 0 : if (y > sy2) {
493 0 : i = ni;
494 0 : continue;
495 : }
496 0 : break;
497 : }
498 0 : return i;
499 : }
500 :
501 : //---------------------------------------------------------
502 : // add
503 : //---------------------------------------------------------
504 :
505 12825 : void System::add(EngravingItem* el)
506 : {
507 12825 : if (!el) {
508 0 : return;
509 : }
510 : // LOGD("%p System::add: %p %s", this, el, el->typeName());
511 :
512 12825 : if (el->isMeasureBase()) {
513 0 : toMeasureBase(el)->setSystem(this);
514 12825 : } else if (el->isSpannerSegment()) {
515 6210 : toSpannerSegment(el)->setSystem(this);
516 6615 : } else if (!el->isBeam()) { // a beam's placement is derived from its elements
517 6615 : el->setOwnershipParent(this);
518 : }
519 :
520 12825 : switch (el->type()) {
521 3771 : case ElementType::INSTRUMENT_NAME:
522 : {
523 3771 : InstrumentName* n = toInstrumentName(el);
524 3771 : SysStaff* sysStaff = m_staves[n->staffIdx()];
525 3771 : sysStaff->addInstrumentName(n);
526 3771 : n->setSysStaff(sysStaff);
527 3771 : break;
528 : }
529 0 : case ElementType::BEAM:
530 0 : score()->addElement(el);
531 0 : break;
532 :
533 2816 : case ElementType::BRACKET: {
534 2816 : Bracket* b = toBracket(el);
535 2816 : m_brackets.push_back(b);
536 : }
537 2816 : break;
538 :
539 0 : case ElementType::MEASURE:
540 : case ElementType::HBOX:
541 : case ElementType::VBOX:
542 : case ElementType::TBOX:
543 : case ElementType::FBOX:
544 0 : score()->addElement(el);
545 0 : break;
546 6210 : case ElementType::TEXTLINE_SEGMENT:
547 : case ElementType::HAIRPIN_SEGMENT:
548 : case ElementType::OTTAVA_SEGMENT:
549 : case ElementType::TRILL_SEGMENT:
550 : case ElementType::VIBRATO_SEGMENT:
551 : case ElementType::VOLTA_SEGMENT:
552 : case ElementType::SLUR_SEGMENT:
553 : case ElementType::TIE_SEGMENT:
554 : case ElementType::LAISSEZ_VIB_SEGMENT:
555 : case ElementType::PARTIAL_TIE_SEGMENT:
556 : case ElementType::PEDAL_SEGMENT:
557 : case ElementType::LYRICSLINE_SEGMENT:
558 : case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
559 : case ElementType::GLISSANDO_SEGMENT:
560 : case ElementType::NOTELINE_SEGMENT:
561 : case ElementType::LET_RING_SEGMENT:
562 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
563 : case ElementType::PALM_MUTE_SEGMENT:
564 : case ElementType::WHAMMY_BAR_SEGMENT:
565 : case ElementType::RASGUEADO_SEGMENT:
566 : case ElementType::HARMONIC_MARK_SEGMENT:
567 : case ElementType::PICK_SCRAPE_SEGMENT:
568 : case ElementType::GUITAR_BEND_SEGMENT:
569 : case ElementType::GUITAR_BEND_HOLD_SEGMENT:
570 : case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
571 : case ElementType::TAPPING_HALF_SLUR_SEGMENT:
572 : {
573 6210 : SpannerSegment* ss = toSpannerSegment(el);
574 : #ifndef NDEBUG
575 6210 : if (muse::contains(m_spannerSegments, ss)) {
576 16 : LOGD("System::add() %s %p already there", ss->typeName(), ss);
577 : } else
578 : #endif
579 6194 : m_spannerSegments.push_back(ss);
580 : }
581 6210 : break;
582 :
583 28 : case ElementType::SYSTEM_DIVIDER:
584 : {
585 28 : SystemDivider* sd = toSystemDivider(el);
586 28 : if (sd->dividerType() == SystemDividerType::LEFT) {
587 14 : m_systemDividerLeft = sd;
588 : } else {
589 14 : m_systemDividerRight = sd;
590 : }
591 : }
592 28 : break;
593 :
594 0 : default:
595 0 : LOGD("System::add(%s) not implemented", el->typeName());
596 0 : return;
597 : }
598 :
599 12825 : el->added();
600 : }
601 :
602 : //---------------------------------------------------------
603 : // remove
604 : //---------------------------------------------------------
605 :
606 1509 : void System::remove(EngravingItem* el)
607 : {
608 1509 : switch (el->type()) {
609 0 : case ElementType::INSTRUMENT_NAME:
610 : {
611 : // NOTE: el gets deleted here
612 0 : InstrumentName* n = toInstrumentName(el);
613 0 : SysStaff* sysStaff = m_staves[n->staffIdx()];
614 0 : sysStaff->removeInstrumentName(n->instrumentNameRole());
615 0 : return;
616 : }
617 0 : case ElementType::BEAM:
618 0 : score()->removeElement(el);
619 0 : break;
620 0 : case ElementType::BRACKET:
621 : {
622 0 : Bracket* b = toBracket(el);
623 0 : if (!muse::remove(m_brackets, b)) {
624 0 : LOGD("System::remove: bracket not found");
625 : }
626 : }
627 0 : break;
628 0 : case ElementType::MEASURE:
629 : case ElementType::HBOX:
630 : case ElementType::VBOX:
631 : case ElementType::TBOX:
632 : case ElementType::FBOX:
633 0 : score()->removeElement(el);
634 0 : break;
635 1487 : case ElementType::TEXTLINE_SEGMENT:
636 : case ElementType::HAIRPIN_SEGMENT:
637 : case ElementType::OTTAVA_SEGMENT:
638 : case ElementType::TRILL_SEGMENT:
639 : case ElementType::VIBRATO_SEGMENT:
640 : case ElementType::VOLTA_SEGMENT:
641 : case ElementType::SLUR_SEGMENT:
642 : case ElementType::TIE_SEGMENT:
643 : case ElementType::LAISSEZ_VIB_SEGMENT:
644 : case ElementType::PARTIAL_TIE_SEGMENT:
645 : case ElementType::PEDAL_SEGMENT:
646 : case ElementType::LYRICSLINE_SEGMENT:
647 : case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
648 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
649 : case ElementType::GLISSANDO_SEGMENT:
650 : case ElementType::NOTELINE_SEGMENT:
651 : case ElementType::GUITAR_BEND_SEGMENT:
652 : case ElementType::GUITAR_BEND_HOLD_SEGMENT:
653 : {
654 1487 : SpannerSegment* ss = toSpannerSegment(el);
655 1487 : if (!muse::remove(m_spannerSegments, ss)) {
656 0 : LOGD("System::remove: %p(%s) not found, score %p", el, el->typeName(), score());
657 0 : assert(score() == el->score());
658 : }
659 1487 : if (ss->system() == this) {
660 1487 : ss->setSystem(nullptr);
661 : }
662 : }
663 1487 : break;
664 6 : case ElementType::SYSTEM_DIVIDER:
665 6 : if (el == m_systemDividerLeft) {
666 3 : m_systemDividerLeft = 0;
667 : } else {
668 3 : assert(m_systemDividerRight == el);
669 3 : m_systemDividerRight = 0;
670 : }
671 6 : break;
672 :
673 16 : default:
674 16 : LOGD("System::remove(%s) not implemented", el->typeName());
675 16 : return;
676 : }
677 :
678 1493 : el->removed();
679 : }
680 :
681 : //---------------------------------------------------------
682 : // change
683 : //---------------------------------------------------------
684 :
685 0 : void System::change(EngravingItem* o, EngravingItem* n)
686 : {
687 0 : remove(o);
688 0 : add(n);
689 0 : }
690 :
691 : //---------------------------------------------------------
692 : // snap
693 : //---------------------------------------------------------
694 :
695 0 : Fraction System::snap(const Fraction& tick, const PointF p) const
696 : {
697 0 : for (const MeasureBase* m : m_ml) {
698 0 : if (p.x() < m->x() + m->width()) {
699 0 : return toMeasure(m)->snap(tick, p - m->pos()); //TODO: MeasureBase
700 : }
701 : }
702 0 : return toMeasure(m_ml.back())->snap(tick, p - pos()); //TODO: MeasureBase
703 : }
704 :
705 : //---------------------------------------------------------
706 : // snap
707 : //---------------------------------------------------------
708 :
709 0 : Fraction System::snapNote(const Fraction& tick, const PointF p, int staff) const
710 : {
711 0 : for (const MeasureBase* m : m_ml) {
712 0 : if (p.x() < m->x() + m->width()) {
713 0 : return toMeasure(m)->snapNote(tick, p - m->pos(), staff); //TODO: MeasureBase
714 : }
715 : }
716 0 : return toMeasure(m_ml.back())->snap(tick, p - pos()); // TODO: MeasureBase
717 : }
718 :
719 : //---------------------------------------------------------
720 : // firstMeasure
721 : //---------------------------------------------------------
722 :
723 639309 : Measure* System::firstMeasure() const
724 : {
725 1284796 : auto i = std::find_if(m_ml.begin(), m_ml.end(), [](MeasureBase* mb) { return mb->isMeasure(); });
726 639309 : return i != m_ml.end() ? toMeasure(*i) : 0;
727 : }
728 :
729 : //---------------------------------------------------------
730 : // lastMeasure
731 : //---------------------------------------------------------
732 :
733 180049 : Measure* System::lastMeasure() const
734 : {
735 360414 : auto i = std::find_if(m_ml.rbegin(), m_ml.rend(), [](MeasureBase* mb) { return mb->isMeasure(); });
736 180049 : return i != m_ml.rend() ? toMeasure(*i) : 0;
737 : }
738 :
739 : //---------------------------------------------------------
740 : // nextMeasure
741 : //---------------------------------------------------------
742 :
743 0 : MeasureBase* System::nextMeasure(const MeasureBase* m) const
744 : {
745 0 : if (m == m_ml.back()) {
746 0 : return 0;
747 : }
748 0 : MeasureBase* nm = m->next();
749 0 : if (nm->isMeasure() && style().styleB(Sid::createMultiMeasureRests) && toMeasure(nm)->hasMMRest()) {
750 0 : nm = toMeasure(nm)->mmRest();
751 : }
752 0 : return nm;
753 : }
754 :
755 : //---------------------------------------------------------
756 : // scanElements
757 : //---------------------------------------------------------
758 :
759 655 : void System::scanElements(std::function<void(EngravingItem*)> func)
760 : {
761 655 : if (m_pageLockIndicator) {
762 5 : func(m_pageLockIndicator);
763 : }
764 :
765 655 : if (vbox()) {
766 122 : return;
767 : }
768 655 : for (Bracket* b : m_brackets) {
769 122 : b->scanElements(func);
770 : }
771 :
772 533 : if (m_systemDividerLeft) {
773 0 : func(m_systemDividerLeft);
774 : }
775 533 : if (m_systemDividerRight) {
776 0 : func(m_systemDividerRight);
777 : }
778 :
779 533 : if (m_staffVisibilityIndicator) {
780 14 : func(m_staffVisibilityIndicator);
781 : }
782 :
783 533 : for (auto i : m_systemLockIndicators) {
784 0 : func(i);
785 : }
786 :
787 1220 : for (const SysStaff* st : m_staves) {
788 687 : if (st->show()) {
789 837 : for (auto& pair : st->instrumentNames()) {
790 176 : if (InstrumentName* n = pair.second) {
791 176 : func(n);
792 : }
793 : }
794 : } else {
795 26 : if (InstrumentName* n = st->name(InstrumentNameRole::GROUP); n && n->effectiveStaffIdx() != muse::nidx) {
796 0 : func(n);
797 : }
798 26 : if (InstrumentName* n = st->name(InstrumentNameRole::PART); n && n->effectiveStaffIdx() != muse::nidx) {
799 0 : func(n);
800 : }
801 : }
802 : }
803 :
804 800 : for (SpannerSegment* ss : m_spannerSegments) {
805 267 : staff_idx_t staffIdx = ss->spanner()->staffIdx();
806 267 : if (staffIdx == muse::nidx) {
807 0 : LOGD("System::scanElements: staffIDx == -1: %s %p", ss->spanner()->typeName(), ss->spanner());
808 0 : staffIdx = 0;
809 : }
810 267 : bool v = true;
811 267 : Spanner* spanner = ss->spanner();
812 267 : if (spanner->anchor() == Spanner::Anchor::SEGMENT || spanner->anchor() == Spanner::Anchor::CHORDREST) {
813 178 : EngravingItem* se = spanner->startElement();
814 178 : EngravingItem* ee = spanner->endElement();
815 178 : bool v1 = true;
816 178 : if (se && se->isChordRest()) {
817 173 : ChordRest* cr = toChordRest(se);
818 173 : Measure* m = cr->measure();
819 173 : v1 = m->visible(cr->staffIdx());
820 : }
821 178 : bool v2 = true;
822 178 : if (!v1 && ee && ee->isChordRest()) {
823 2 : ChordRest* cr = toChordRest(ee);
824 2 : Measure* m = cr->measure();
825 2 : v2 = m->visible(cr->staffIdx());
826 : }
827 178 : v = v1 || v2; // hide spanner if both chords are hidden
828 : }
829 534 : if ((score()->staff(staffIdx)->show() && m_staves[staffIdx]->show() && v) || spanner->isVolta()
830 534 : || spanner->systemFlag()) {
831 263 : ss->scanElements(func);
832 : }
833 : }
834 : }
835 :
836 : //---------------------------------------------------------
837 : // staffYpage
838 : // return page coordinates
839 : //---------------------------------------------------------
840 :
841 563560 : double System::staffYpage(staff_idx_t staffIdx) const
842 : {
843 563560 : if (staffIdx >= m_staves.size()) {
844 0 : return pagePos().y();
845 : }
846 :
847 563560 : return m_staves[staffIdx]->y() + y();
848 : }
849 :
850 : //---------------------------------------------------------
851 : // staffCanvasYpage
852 : // return canvas coordinates
853 : //---------------------------------------------------------
854 :
855 22 : double System::staffCanvasYpage(staff_idx_t staffIdx) const
856 : {
857 22 : return m_staves[staffIdx]->y() + y() + page()->canvasPos().y();
858 : }
859 :
860 1784563 : SysStaff* System::staff(size_t staffIdx) const
861 : {
862 1784563 : if (staffIdx < m_staves.size()) {
863 1784471 : return m_staves[staffIdx];
864 : }
865 :
866 92 : return nullptr;
867 : }
868 :
869 : //---------------------------------------------------------
870 : // nextSegmentElement
871 : //---------------------------------------------------------
872 :
873 0 : EngravingItem* System::nextSegmentElement()
874 : {
875 0 : Measure* m = firstMeasure();
876 0 : if (m) {
877 0 : Segment* firstSeg = m->segments().first();
878 0 : if (firstSeg) {
879 0 : return firstSeg->element(0);
880 : }
881 : }
882 0 : return Navigation::lastElement(score());
883 : }
884 :
885 : //---------------------------------------------------------
886 : // prevSegmentElement
887 : //---------------------------------------------------------
888 :
889 0 : EngravingItem* System::prevSegmentElement()
890 : {
891 0 : EngravingItem* re = 0;
892 0 : Measure* m = firstMeasure();
893 0 : if (m) {
894 0 : Segment* seg = m->first();
895 0 : while (!re) {
896 0 : seg = seg->prev1MM();
897 0 : if (!seg) {
898 0 : return Navigation::firstElement(score());
899 : }
900 :
901 0 : if (seg->segmentType() == SegmentType::EndBarLine) {
902 0 : score()->inputState().setTrack((score()->staves().size() - 1) * VOICES); //correction
903 : }
904 0 : re = seg->lastElementForNavigation(score()->staves().size() - 1);
905 : }
906 : }
907 0 : return re;
908 : }
909 :
910 : //---------------------------------------------------------
911 : // topDistance
912 : // return minimum distance to the above south skyline
913 : //---------------------------------------------------------
914 :
915 0 : double System::topDistance(staff_idx_t staffIdx, const SkylineLine& s) const
916 : {
917 0 : assert(!vbox());
918 0 : assert(!s.isNorth());
919 : // in continuous view, we only build a partial skyline for performance reasons
920 : // this means we cannot expect the minDistance calculation to produce meaningful results
921 : // so just give up on autoplace for spanners in continuous view
922 : // (or any other calculations that rely on this value)
923 0 : if (score()->lineMode() && !configuration()->minDistanceForPartialSkylineCalculated()) {
924 0 : return 0.0;
925 : }
926 0 : return s.minDistance(staff(staffIdx)->skyline().north());
927 : }
928 :
929 : //---------------------------------------------------------
930 : // bottomDistance
931 : //---------------------------------------------------------
932 :
933 0 : double System::bottomDistance(staff_idx_t staffIdx, const SkylineLine& s) const
934 : {
935 0 : assert(!vbox());
936 0 : assert(s.isNorth());
937 : // see note on topDistance() above
938 0 : if (score()->lineMode() && !configuration()->minDistanceForPartialSkylineCalculated()) {
939 0 : return 0.0;
940 : }
941 0 : return staff(staffIdx)->skyline().south().minDistance(s);
942 : }
943 :
944 : //---------------------------------------------------------
945 : // firstVisibleSysStaff
946 : //---------------------------------------------------------
947 :
948 6276 : staff_idx_t System::firstVisibleSysStaff() const
949 : {
950 6276 : size_t nstaves = m_staves.size();
951 6290 : for (staff_idx_t i = 0; i < nstaves; ++i) {
952 6284 : if (m_staves[i]->show()) {
953 6270 : return i;
954 : }
955 : }
956 6 : return muse::nidx;
957 : }
958 :
959 : //---------------------------------------------------------
960 : // lastVisibleSysStaff
961 : //---------------------------------------------------------
962 :
963 11076 : staff_idx_t System::lastVisibleSysStaff() const
964 : {
965 11076 : int nstaves = static_cast<int>(m_staves.size());
966 11321 : for (int i = nstaves - 1; i >= 0; --i) {
967 11312 : if (m_staves[i]->show()) {
968 11067 : return static_cast<staff_idx_t>(i);
969 : }
970 : }
971 9 : return muse::nidx;
972 : }
973 :
974 : //---------------------------------------------------------
975 : // minTop
976 : // Return the minimum top margin.
977 : //---------------------------------------------------------
978 :
979 6276 : double System::minTop() const
980 : {
981 6276 : staff_idx_t si = firstVisibleSysStaff();
982 6276 : SysStaff* s = si == muse::nidx ? nullptr : staff(si);
983 6276 : if (s) {
984 6270 : return -s->skyline().north().max();
985 : }
986 6 : return 0.0;
987 : }
988 :
989 : //---------------------------------------------------------
990 : // minBottom
991 : // Return the minimum bottom margin.
992 : //---------------------------------------------------------
993 :
994 10091 : double System::minBottom() const
995 : {
996 10091 : if (const Box* vb = vbox()) {
997 30 : return vb->absoluteFromSpatium(vb->bottomGap());
998 : }
999 10061 : staff_idx_t si = lastVisibleSysStaff();
1000 10061 : SysStaff* s = si == muse::nidx ? nullptr : staff(si);
1001 10061 : if (s) {
1002 10055 : return s->skyline().south().max() - s->bbox().height();
1003 : }
1004 6 : return 0.0;
1005 : }
1006 :
1007 : //---------------------------------------------------------
1008 : // spacerDistance
1009 : // Return the distance needed due to spacers
1010 : //---------------------------------------------------------
1011 :
1012 1015 : double System::spacerDistance(bool up) const
1013 : {
1014 1015 : staff_idx_t staff = up ? firstVisibleSysStaff() : lastVisibleSysStaff();
1015 1015 : if (staff == muse::nidx) {
1016 3 : return 0.0;
1017 : }
1018 1012 : double dist = 0.0;
1019 4469 : for (MeasureBase* mb : measures()) {
1020 3457 : if (mb->isMeasure()) {
1021 3413 : Measure* m = toMeasure(mb);
1022 3413 : Spacer* sp = up ? m->vspacerUp(staff) : m->vspacerDown(staff);
1023 3413 : if (sp) {
1024 0 : if (sp->spacerType() == SpacerType::FIXED) {
1025 0 : dist = sp->absoluteGap();
1026 0 : break;
1027 : } else {
1028 0 : dist = std::max(dist, sp->absoluteGap());
1029 : }
1030 : }
1031 : }
1032 : }
1033 1012 : return dist;
1034 : }
1035 :
1036 : //---------------------------------------------------------
1037 : // upSpacer
1038 : // Return largest upSpacer for this system. This can
1039 : // be a downSpacer of the previous system.
1040 : //---------------------------------------------------------
1041 :
1042 6673 : Spacer* System::upSpacer(staff_idx_t staffIdx, Spacer* prevDownSpacer) const
1043 : {
1044 6673 : if (staffIdx == muse::nidx) {
1045 0 : return nullptr;
1046 : }
1047 :
1048 6673 : if (prevDownSpacer && (prevDownSpacer->spacerType() == SpacerType::FIXED)) {
1049 2 : return prevDownSpacer;
1050 : }
1051 :
1052 6671 : Spacer* spacer { prevDownSpacer };
1053 38094 : for (MeasureBase* mb : measures()) {
1054 31423 : if (!(mb && mb->isMeasure())) {
1055 92 : continue;
1056 : }
1057 31331 : Spacer* sp { toMeasure(mb)->vspacerUp(staffIdx) };
1058 31331 : if (sp) {
1059 0 : if (!spacer || ((spacer->spacerType() == SpacerType::UP) && (sp->gap() > spacer->gap()))) {
1060 0 : spacer = sp;
1061 : }
1062 0 : continue;
1063 : }
1064 : }
1065 6671 : return spacer;
1066 : }
1067 :
1068 : //---------------------------------------------------------
1069 : // downSpacer
1070 : // Return the largest downSpacer for this system.
1071 : //---------------------------------------------------------
1072 :
1073 8209 : Spacer* System::downSpacer(staff_idx_t staffIdx) const
1074 : {
1075 8209 : if (staffIdx == muse::nidx) {
1076 0 : return nullptr;
1077 : }
1078 :
1079 8209 : Spacer* spacer { nullptr };
1080 44232 : for (MeasureBase* mb : measures()) {
1081 36025 : if (!(mb && mb->isMeasure())) {
1082 97 : continue;
1083 : }
1084 35928 : Spacer* sp { toMeasure(mb)->vspacerDown(staffIdx) };
1085 35928 : if (sp) {
1086 2 : if (sp->spacerType() == SpacerType::FIXED) {
1087 2 : return sp;
1088 : } else {
1089 0 : if (!spacer || (sp->gap() > spacer->gap())) {
1090 0 : spacer = sp;
1091 : }
1092 : }
1093 : }
1094 : }
1095 8207 : return spacer;
1096 : }
1097 :
1098 : //---------------------------------------------------------
1099 : // firstNoteRestSegmentX
1100 : // in System() coordinates
1101 : // returns the position of the first note or rest,
1102 : // or the position just after the last non-chordrest segment
1103 : //---------------------------------------------------------
1104 :
1105 744 : double System::firstNoteRestSegmentX(bool leading) const
1106 : {
1107 744 : double margin = style().styleAbsolute(Sid::headerToLineStartDistance);
1108 767 : for (const MeasureBase* mb : measures()) {
1109 767 : if (mb->isMeasure()) {
1110 744 : const Measure* measure = toMeasure(mb);
1111 744 : margin = measure->firstNoteRestSegmentX(leading);
1112 744 : break;
1113 : }
1114 : }
1115 :
1116 744 : return margin;
1117 : }
1118 :
1119 : //---------------------------------------------------------
1120 : // endingXForOpenEndedLines
1121 : // in System() coordinates returns the end point for
1122 : // open ended (i.e. cross-system) lines
1123 : //---------------------------------------------------------
1124 :
1125 39 : double System::endingXForOpenEndedLines() const
1126 : {
1127 39 : double margin = style().styleAbsolute(Sid::lineEndToBarlineDistance);
1128 39 : double systemEndX = ldata()->bbox().width();
1129 :
1130 39 : Measure* lastMeas = lastMeasure();
1131 39 : if (!lastMeas) {
1132 0 : return systemEndX - margin;
1133 : }
1134 :
1135 39 : return lastMeas->endingXForOpenEndedLines();
1136 : }
1137 :
1138 : //---------------------------------------------------------
1139 : // lastChordRest
1140 : // returns the last chordrest of a system for a particular track
1141 : //---------------------------------------------------------
1142 :
1143 36 : ChordRest* System::lastChordRest(track_idx_t track) const
1144 : {
1145 36 : for (auto measureBaseIter = measures().rbegin(); measureBaseIter != measures().rend(); measureBaseIter++) {
1146 36 : if ((*measureBaseIter)->isMeasure()) {
1147 36 : const Measure* measure = toMeasure(*measureBaseIter);
1148 36 : return measure->lastChordRest(track);
1149 : }
1150 : }
1151 0 : return nullptr;
1152 : }
1153 :
1154 : //---------------------------------------------------------
1155 : // firstChordRest
1156 : // returns the last chordrest of a system for a particular track
1157 : //---------------------------------------------------------
1158 :
1159 36 : ChordRest* System::firstChordRest(track_idx_t track) const
1160 : {
1161 36 : for (const MeasureBase* mb : measures()) {
1162 36 : if (!mb->isMeasure()) {
1163 0 : continue;
1164 : }
1165 36 : const Measure* measure = toMeasure(mb);
1166 36 : return measure->firstChordRest(track);
1167 : }
1168 0 : return nullptr;
1169 : }
1170 :
1171 : //---------------------------------------------------------
1172 : // pageBreak
1173 : //---------------------------------------------------------
1174 :
1175 6099 : bool System::pageBreak() const
1176 : {
1177 6099 : return m_ml.empty() ? false : m_ml.back()->pageBreak();
1178 : }
1179 :
1180 : //---------------------------------------------------------
1181 : // endTick
1182 : //---------------------------------------------------------
1183 :
1184 16955 : Fraction System::endTick() const
1185 : {
1186 16955 : return measures().back()->endTick();
1187 : }
1188 :
1189 : //---------------------------------------------------------
1190 : // firstSysStaffOfPart
1191 : //---------------------------------------------------------
1192 :
1193 30153 : staff_idx_t System::firstSysStaffOfPart(const Part* part) const
1194 : {
1195 30153 : staff_idx_t staffIdx = 0;
1196 242714 : for (const Part* p : score()->parts()) {
1197 242714 : if (p == part) {
1198 30153 : return staffIdx;
1199 : }
1200 212561 : staffIdx += p->nstaves();
1201 : }
1202 0 : return muse::nidx; // Part not found.
1203 : }
1204 :
1205 : //---------------------------------------------------------
1206 : // firstVisibleSysStaffOfPart
1207 : //---------------------------------------------------------
1208 :
1209 328 : staff_idx_t System::firstVisibleSysStaffOfPart(const Part* part) const
1210 : {
1211 328 : staff_idx_t firstIdx = firstSysStaffOfPart(part);
1212 328 : if (firstIdx == muse::nidx) {
1213 0 : return muse::nidx;
1214 : }
1215 :
1216 524 : for (staff_idx_t idx = firstIdx; idx < firstIdx + part->nstaves(); ++idx) {
1217 425 : const SysStaff* s = staff(idx);
1218 425 : if (s && s->show()) {
1219 229 : return idx;
1220 : }
1221 : }
1222 :
1223 99 : return muse::nidx; // No visible staves on this part.
1224 : }
1225 :
1226 0 : staff_idx_t System::firstVisibleSysStaffWithInstrument(const String& instrumentId, staff_idx_t startFrom)
1227 : {
1228 0 : Fraction tick = first()->tick();
1229 0 : for (staff_idx_t idx = startFrom; idx < m_staves.size(); ++idx) {
1230 0 : Part* part = score()->staff(idx)->part();
1231 0 : if (part->instrument(tick)->id() == instrumentId) {
1232 0 : staff_idx_t firstVisOfPart = firstVisibleSysStaffOfPart(part);
1233 0 : if (firstVisOfPart != muse::nidx) {
1234 0 : return firstVisOfPart;
1235 : }
1236 : }
1237 : }
1238 :
1239 0 : return muse::nidx;
1240 : }
1241 :
1242 : //---------------------------------------------------------
1243 : // lastSysStaffOfPart
1244 : //---------------------------------------------------------
1245 :
1246 186 : staff_idx_t System::lastSysStaffOfPart(const Part* part) const
1247 : {
1248 186 : staff_idx_t firstIdx = firstSysStaffOfPart(part);
1249 186 : if (firstIdx == muse::nidx) {
1250 0 : return muse::nidx; // Part not found.
1251 : }
1252 186 : return firstIdx + part->nstaves() - 1;
1253 : }
1254 :
1255 : //---------------------------------------------------------
1256 : // lastVisibleSysStaffOfPart
1257 : //---------------------------------------------------------
1258 :
1259 186 : staff_idx_t System::lastVisibleSysStaffOfPart(const Part* part) const
1260 : {
1261 186 : staff_idx_t firstStaffIdx = firstSysStaffOfPart(part);
1262 186 : if (firstStaffIdx == muse::nidx) {
1263 0 : return muse::nidx;
1264 : }
1265 186 : for (int idx = static_cast<int>(lastSysStaffOfPart(part)); idx >= static_cast<int>(firstStaffIdx); --idx) {
1266 186 : if (staff(idx)->show()) {
1267 186 : return idx;
1268 : }
1269 : }
1270 0 : return muse::nidx; // No visible staves on this part.
1271 : }
1272 :
1273 29453 : std::vector<staff_idx_t> System::visibleStavesOfPart(const Part* part) const
1274 : {
1275 29453 : std::vector<staff_idx_t> result;
1276 29453 : result.reserve(part->nstaves());
1277 :
1278 29453 : staff_idx_t startIdx = firstSysStaffOfPart(part);
1279 29453 : staff_idx_t endIdx = startIdx + part->nstaves();
1280 62286 : for (staff_idx_t staffIdx = startIdx; staffIdx < endIdx; ++staffIdx) {
1281 32833 : if (staff(staffIdx)->show()) {
1282 32588 : result.push_back(staffIdx);
1283 : }
1284 : }
1285 :
1286 29453 : return result;
1287 0 : }
1288 :
1289 417 : std::vector<Part*> System::visiblePartsOfGroup(staff_idx_t start, staff_idx_t end) const
1290 : {
1291 417 : std::vector<Part*> result;
1292 :
1293 1328 : for (staff_idx_t idx = start; idx < end;) {
1294 911 : Part* part = score()->staff(idx)->part();
1295 911 : if (visibleStavesOfPart(part).size() > 0) {
1296 911 : result.push_back(part);
1297 : }
1298 911 : idx += part->nstaves();
1299 : }
1300 :
1301 417 : return result;
1302 0 : }
1303 : } // namespace mu::engraving
|