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 : #include "utils.h"
24 :
25 : #include <cmath>
26 : #include <map>
27 :
28 : #include "containers.h"
29 :
30 : #include "actionicon.h"
31 : #include "accidental.h"
32 : #include "arpeggio.h"
33 : #include "chord.h"
34 : #include "chordrest.h"
35 : #include "clef.h"
36 : #include "fret.h"
37 : #include "harmony.h"
38 : #include "laissezvib.h"
39 : #include "lyrics.h"
40 : #include "marker.h"
41 : #include "masterscore.h"
42 : #include "repeatlist.h"
43 : #include "keysig.h"
44 : #include "measure.h"
45 : #include "measurenumber.h"
46 : #include "measurerepeat.h"
47 : #include "note.h"
48 : #include "page.h"
49 : #include "part.h"
50 : #include "playcounttext.h"
51 : #include "partialtie.h"
52 : #include "pitchspelling.h"
53 : #include "rest.h"
54 : #include "score.h"
55 : #include "segment.h"
56 : #include "select.h"
57 : #include "sig.h"
58 : #include "staff.h"
59 : #include "system.h"
60 : #include "spanner.h"
61 : #include "tremolosinglechord.h"
62 : #include "tremolotwochord.h"
63 : #include "tuplet.h"
64 : #include "drumset.h"
65 : #include "barline.h"
66 :
67 : #include "log.h"
68 :
69 : using namespace mu;
70 :
71 : namespace mu::engraving {
72 : //---------------------------------------------------------
73 : // handleRect
74 : //---------------------------------------------------------
75 :
76 0 : RectF handleRect(const PointF& pos)
77 : {
78 0 : return RectF(pos.x() - 4, pos.y() - 4, 8, 8);
79 : }
80 :
81 : //---------------------------------------------------------
82 : // tick2measure
83 : //---------------------------------------------------------
84 :
85 59232 : Measure* Score::tick2measure(const Fraction& tick) const
86 : {
87 59232 : if (tick == Fraction(-1, 1)) { // special number
88 13 : return lastMeasure();
89 : }
90 59219 : if (tick <= Fraction(0, 1)) {
91 20285 : return firstMeasure();
92 : }
93 :
94 38934 : return m_measures.measureByTick(tick.ticks());
95 : }
96 :
97 : //---------------------------------------------------------
98 : // tick2measureMM
99 : //---------------------------------------------------------
100 :
101 91080 : Measure* Score::tick2measureMM(const Fraction& t) const
102 : {
103 91080 : Fraction tick(t);
104 91080 : if (tick == Fraction(-1, 1)) {
105 35 : return lastMeasureMM();
106 : }
107 91045 : if (tick < Fraction(0, 1)) {
108 1 : tick = Fraction(0, 1);
109 : }
110 :
111 91045 : Measure* measure = m_measures.measureByTick(t.ticks());
112 91045 : if (!measure) {
113 171 : LOGD("tick2measureMM %d not found", tick.ticks());
114 171 : return nullptr;
115 : }
116 :
117 90874 : return measure->coveringMMRestOrThis();
118 : }
119 :
120 19 : MeasureBase* Score::tick2measureBase(const Fraction& tick) const
121 : {
122 19 : if (tick == Fraction(-1, 1)) { // special number
123 0 : return m_measures.last();
124 : }
125 19 : if (tick <= Fraction(0, 1)) {
126 9 : return m_measures.first();
127 : }
128 :
129 10 : return m_measures.firstMeasureBaseAtTick(tick.ticks());
130 : }
131 :
132 : //---------------------------------------------------------
133 : // tick2segment
134 : //---------------------------------------------------------
135 :
136 4975 : Segment* Score::tick2segmentMM(const Fraction& tick, bool first, SegmentType st) const
137 : {
138 4975 : return tick2segment(tick, first, st, true);
139 : }
140 :
141 164 : Segment* Score::tick2segmentMM(const Fraction& tick) const
142 : {
143 164 : return tick2segment(tick, false, SegmentType::All, true);
144 : }
145 :
146 80 : Segment* Score::tick2segmentMM(const Fraction& tick, bool first) const
147 : {
148 80 : return tick2segment(tick, first, SegmentType::All, true);
149 : }
150 :
151 24259 : Segment* Score::tick2segment(const Fraction& t, bool first, SegmentType st, bool useMMrest) const
152 : {
153 24259 : Fraction tick(t);
154 : Measure* m;
155 24259 : if (useMMrest) {
156 5353 : m = tick2measureMM(tick);
157 : // When mmRest force tick to the first segment of mmRest.
158 5353 : if (m && m->isMMRest() && tick != m->endTick()) {
159 10 : tick = m->tick();
160 : }
161 : } else {
162 18906 : m = tick2measure(tick);
163 : }
164 :
165 24259 : if (m == 0) {
166 592 : LOGD() << "no measure for tick " << tick.ticks();
167 592 : return 0;
168 : }
169 78313 : for (Segment* segment = m->first(st); segment;) {
170 77599 : Fraction t1 = segment->tick();
171 77599 : Segment* nsegment = segment->next(st);
172 77599 : if (tick == t1) {
173 23166 : if (first) {
174 22953 : return segment;
175 : } else {
176 5469 : if (!nsegment || tick < nsegment->tick()) {
177 5256 : return segment;
178 : }
179 : }
180 : }
181 54646 : segment = nsegment;
182 : }
183 714 : return 0;
184 : }
185 :
186 41 : Segment* Score::tick2segment(const Fraction& tick) const
187 : {
188 41 : return tick2segment(tick, false, SegmentType::All, false);
189 : }
190 :
191 0 : Segment* Score::tick2segment(const Fraction& tick, bool first) const
192 : {
193 0 : return tick2segment(tick, first, SegmentType::All, false);
194 : }
195 :
196 : //---------------------------------------------------------
197 : // tick2leftSegment
198 : /// return the segment at this tick position if any or
199 : /// the first segment *before* this tick position
200 : //---------------------------------------------------------
201 :
202 1397 : Segment* Score::tick2leftSegment(const Fraction& tick, bool useMMrest, SegmentType segType) const
203 : {
204 1397 : Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
205 1397 : if (m == 0) {
206 124 : LOGD("tick2leftSegment(): not found tick %d", tick.ticks());
207 124 : return 0;
208 : }
209 :
210 : // loop over all segments
211 1273 : Segment* ps = 0;
212 2427 : for (Segment* s = m->first(segType); s; s = s->next(segType)) {
213 2261 : if (tick < s->tick()) {
214 148 : return ps;
215 2113 : } else if (tick == s->tick()) {
216 959 : return s;
217 : }
218 1154 : ps = s;
219 : }
220 166 : return ps;
221 : }
222 :
223 : //---------------------------------------------------------
224 : // tick2rightSegment
225 : /// return the segment at this tick position if any or
226 : /// the first segment *after* this tick position
227 : //---------------------------------------------------------
228 :
229 2383 : Segment* Score::tick2rightSegment(const Fraction& tick, bool useMMrest, SegmentType segType) const
230 : {
231 2383 : Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
232 2383 : if (m == 0) {
233 : //LOGD("tick2nearestSegment(): not found tick %d", tick.ticks());
234 102 : return 0;
235 : }
236 : // loop over all segments
237 7903 : for (Segment* s = m->first(segType); s; s = s->next1(segType)) {
238 7724 : if (tick <= s->tick()) {
239 2102 : return s;
240 : }
241 : }
242 179 : return 0;
243 : }
244 :
245 : //---------------------------------------------------------
246 : // tick2beatType
247 : //---------------------------------------------------------
248 :
249 0 : BeatType Score::tick2beatType(const Fraction& tick) const
250 : {
251 0 : Measure* m = tick2measure(tick);
252 0 : Fraction msrTick = m->tick();
253 0 : TimeSigFrac timeSig = sigmap()->timesig(msrTick).nominal();
254 :
255 0 : int rtick = (tick - msrTick).ticks();
256 :
257 0 : if (m->isAnacrusis()) { // measure is incomplete (anacrusis)
258 0 : rtick += timeSig.ticksPerMeasure() - m->ticks().ticks();
259 : }
260 :
261 0 : return timeSig.rtick2beatType(rtick);
262 : }
263 :
264 1001 : void Score::checkChordList()
265 : {
266 1001 : m_chordList.checkChordList(style());
267 1001 : }
268 :
269 : //---------------------------------------------------------
270 : // next/prevChordNote
271 : //
272 : // returns the top note of the next/previous chord. If a
273 : // chord exists in the same track as note,
274 : // it is used. If not, the topmost existing chord is used.
275 : // May return nullptr if there is no next/prev note
276 : //---------------------------------------------------------
277 :
278 0 : Note* nextChordNote(Note* note)
279 : {
280 0 : track_idx_t track = note->track();
281 0 : track_idx_t fromTrack = (track / VOICES) * VOICES;
282 0 : track_idx_t toTrack = fromTrack + VOICES;
283 : // TODO : limit to same instrument, not simply to same staff!
284 0 : Segment* seg = note->chord()->segment()->nextCR(track, true);
285 0 : while (seg) {
286 0 : EngravingItem* targetElement = seg->element(track);
287 : // if a chord exists in the same track, return its top note
288 0 : if (targetElement && targetElement->isChord()) {
289 0 : return toChord(targetElement)->upNote();
290 : }
291 : // if not, return topmost chord in track range
292 0 : for (track_idx_t i = fromTrack; i < toTrack; i++) {
293 0 : targetElement = seg->element(i);
294 0 : if (targetElement && targetElement->isChord()) {
295 0 : return toChord(targetElement)->upNote();
296 : }
297 : }
298 0 : seg = seg->nextCR(track, true);
299 : }
300 0 : return nullptr;
301 : }
302 :
303 0 : Note* prevChordNote(Note* note)
304 : {
305 0 : track_idx_t track = note->track();
306 0 : track_idx_t fromTrack = (track / VOICES) * VOICES;
307 0 : track_idx_t toTrack = fromTrack + VOICES;
308 : // TODO : limit to same instrument, not simply to same staff!
309 0 : Segment* seg = note->chord()->segment()->prev1();
310 0 : while (seg) {
311 0 : if (seg->segmentType() == SegmentType::ChordRest) {
312 0 : EngravingItem* targetElement = seg->element(track);
313 : // if a chord exists in the same track, return its top note
314 0 : if (targetElement && targetElement->isChord()) {
315 0 : return toChord(targetElement)->upNote();
316 : }
317 : // if not, return topmost chord in track range
318 0 : for (track_idx_t i = fromTrack; i < toTrack; i++) {
319 0 : targetElement = seg->element(i);
320 0 : if (targetElement && targetElement->isChord()) {
321 0 : return toChord(targetElement)->upNote();
322 : }
323 : }
324 : }
325 0 : seg = seg->prev1();
326 : }
327 0 : return nullptr;
328 : }
329 :
330 : //---------------------------------------------------------
331 : // pitchKeyAdjust
332 : // change entered note to sounding pitch dependent
333 : // on key.
334 : // Example: if F is entered in G-major, a Fis is played
335 : // key -7 ... +7
336 : //---------------------------------------------------------
337 :
338 901 : int pitchKeyAdjust(int step, Key key)
339 : {
340 : static int ptab[15][7] = {
341 : // c d e f g a b
342 : { -1, 1, 3, 4, 6, 8, 10 }, // Bes
343 : { -1, 1, 3, 5, 6, 8, 10 }, // Ges
344 : { 0, 1, 3, 5, 6, 8, 10 }, // Des
345 : { 0, 1, 3, 5, 7, 8, 10 }, // As
346 : { 0, 2, 3, 5, 7, 8, 10 }, // Es
347 : { 0, 2, 3, 5, 7, 9, 10 }, // B
348 : { 0, 2, 4, 5, 7, 9, 10 }, // F
349 : { 0, 2, 4, 5, 7, 9, 11 }, // C
350 : { 0, 2, 4, 6, 7, 9, 11 }, // G
351 : { 1, 2, 4, 6, 7, 9, 11 }, // D
352 : { 1, 2, 4, 6, 8, 9, 11 }, // A
353 : { 1, 3, 4, 6, 8, 9, 11 }, // E
354 : { 1, 3, 4, 6, 8, 10, 11 }, // H
355 : { 1, 3, 5, 6, 8, 10, 11 }, // Fis
356 : { 1, 3, 5, 6, 8, 10, 12 }, // Cis
357 : };
358 901 : return ptab[int(key) + 7][step];
359 : }
360 :
361 : //---------------------------------------------------------
362 : // y2pitch
363 : //---------------------------------------------------------
364 :
365 0 : int y2pitch(double y, ClefType clef, double _spatium)
366 : {
367 0 : int l = lrint(y / _spatium * 2.0);
368 0 : return line2pitch(l, clef, Key::C);
369 : }
370 :
371 : //---------------------------------------------------------
372 : // line2pitch
373 : // key -7 ... +7
374 : //---------------------------------------------------------
375 :
376 329 : int line2pitch(int line, ClefType clef, Key key)
377 : {
378 329 : int l = std::max(ClefInfo::pitchOffset(clef) - line, 0);
379 329 : int octave = l / STEP_DELTA_OCTAVE;
380 329 : l %= STEP_DELTA_OCTAVE;
381 :
382 329 : int pitch = pitchKeyAdjust(l, key) + octave * PITCH_DELTA_OCTAVE;
383 329 : return clampPitch(pitch);
384 : }
385 :
386 : static const char16_t* valSharp[] = {
387 : u"c",
388 : u"c♯",
389 : u"d",
390 : u"d♯",
391 : u"e",
392 : u"f",
393 : u"f♯",
394 : u"g",
395 : u"g♯",
396 : u"a",
397 : u"a♯",
398 : u"b"
399 : };
400 : static const char16_t* valFlat[] = {
401 : u"c",
402 : u"d♭",
403 : u"d",
404 : u"e♭",
405 : u"e",
406 : u"f",
407 : u"g♭",
408 : u"g",
409 : u"a♭",
410 : u"a",
411 : u"b♭",
412 : u"b"
413 : };
414 :
415 : /*!
416 : * Returns the string representation of the given pitch.
417 : *
418 : * Returns the latin letter name, accidental, and octave numeral.
419 : * Uses upper case only for pitches 0-24.
420 : *
421 : * @param v
422 : * The pitch number of the note.
423 : *
424 : * @return
425 : * The string representation of the note.
426 : */
427 0 : String pitch2string(int v, bool useFlats)
428 : {
429 0 : if (!pitchIsValid(v)) {
430 0 : return String(u"----");
431 : }
432 0 : int octave = (v / PITCH_DELTA_OCTAVE) - 1;
433 0 : String o;
434 0 : o = String::number(octave);
435 0 : int i = v % PITCH_DELTA_OCTAVE;
436 :
437 0 : String pitchStr = useFlats ? valFlat[i] : valSharp[i];
438 0 : if (octave < 0) {
439 0 : pitchStr = pitchStr.toUpper();
440 : }
441 :
442 0 : return pitchStr + o;
443 0 : }
444 :
445 : /*!
446 : * Returns the pitch number of the given string.
447 : *
448 : * @param s
449 : * The string representation of the note.
450 : *
451 : * @return
452 : * The pitch number of the note.
453 : */
454 0 : int string2pitch(const String& s)
455 : {
456 0 : if (s == String(u"----")) {
457 0 : return INVALID_PITCH;
458 : }
459 :
460 0 : String value = s;
461 :
462 0 : bool negative = s.contains(u'-');
463 0 : int octave = String(s[s.size() - 1]).toInt() * (negative ? -1 : 1);
464 0 : if (octave < -1 || octave > 9) {
465 0 : return -1;
466 : }
467 :
468 0 : value = value.mid(0, value.size() - (negative ? 2 : 1));
469 0 : value = value.toLower();
470 :
471 0 : int pitchIndex = -1;
472 0 : for (int i = 0; i < PITCH_DELTA_OCTAVE; ++i) {
473 0 : if (value == valFlat[i] || value == valSharp[i]) {
474 0 : pitchIndex = i;
475 0 : break;
476 : }
477 : }
478 :
479 0 : if (pitchIndex == -1) {
480 0 : return -1;
481 : }
482 :
483 0 : return (octave + 1) * PITCH_DELTA_OCTAVE + pitchIndex;
484 0 : }
485 :
486 29 : String convertPitchStringFlatsAndSharpsToUnicode(const String& str)
487 : {
488 29 : if (str.isEmpty()) {
489 0 : return String();
490 : }
491 :
492 29 : String value = String(str[0]);
493 50 : for (size_t i = 1; i < str.size(); ++i) {
494 21 : Char symbol = str.at(i).toLower();
495 21 : if (symbol == u'b') {
496 21 : value.append(u'♭');
497 0 : } else if (symbol == u'#') {
498 0 : value.append(u'♯');
499 : } else {
500 0 : value.append(symbol);
501 : }
502 : }
503 :
504 29 : return value;
505 29 : }
506 :
507 : //---------------------------------------------------------
508 : // searchTieNote
509 : // search Note to tie to "note"
510 : //---------------------------------------------------------
511 :
512 382 : Note* searchTieNote(const Note* note, const Segment* nextSegment, const bool disableOverRepeats)
513 : {
514 382 : if (!note) {
515 0 : return nullptr;
516 : }
517 :
518 382 : Note* note2 = nullptr;
519 382 : Chord* chord = note->chord();
520 382 : Segment* seg = chord->segment();
521 :
522 382 : if (!nextSegment) {
523 253 : nextSegment = seg->next1(SegmentType::ChordRest);
524 276 : while (nextSegment && nextSegment->tick() < chord->endTick()) {
525 23 : nextSegment = nextSegment->next1(SegmentType::ChordRest);
526 : }
527 : }
528 :
529 382 : if (!nextSegment) {
530 28 : return nullptr;
531 : }
532 :
533 354 : if (disableOverRepeats && !segmentsAreAdjacent(seg, nextSegment)) {
534 4 : return nullptr;
535 : }
536 :
537 350 : if (chord->isGraceBefore()) {
538 1 : chord = toChord(chord->ownershipParent());
539 :
540 : // try to tie to next grace note
541 :
542 1 : size_t index = note->chord()->graceIndex();
543 2 : for (Chord* c : chord->graceNotes()) {
544 1 : if (c->graceIndex() == index + 1) {
545 0 : note2 = c->findNote(note->pitch());
546 0 : if (note2) {
547 0 : return note2;
548 : }
549 : }
550 : }
551 :
552 : // try to tie to note in parent chord
553 1 : note2 = chord->findNote(note->pitch());
554 1 : if (note2) {
555 1 : return note2;
556 : }
557 349 : } else if (chord->isGraceAfter()) {
558 : // grace after
559 : // we will try to tie to note in next normal chord, below
560 : // meanwhile, set chord to parent chord so the endTick calculation will make sense
561 0 : chord = toChord(chord->ownershipParent());
562 : } else {
563 : // normal chord
564 : // try to tie to grace note after if present
565 349 : std::vector<Chord*> gna = chord->graceNotesAfter();
566 349 : if (!gna.empty()) {
567 0 : Chord* gc = gna.front();
568 0 : note2 = gc->findNote(note->pitch());
569 0 : if (note2) {
570 0 : return note2;
571 : }
572 : }
573 349 : }
574 : // at this point, chord is a regular chord, not a grace chord
575 : // and we are looking for a note in the *next* chord (grace or regular)
576 :
577 349 : int idx1 = note->unisonIndex();
578 349 : Part* part = chord->part();
579 349 : const TrackRange trackRange = part->trackRange();
580 1757 : for (track_idx_t track = trackRange.startTrack; track < trackRange.endTrack; ++track) {
581 1409 : EngravingItem* e = nextSegment->element(track);
582 1409 : if (!e || !e->isChord()) {
583 1088 : continue;
584 : }
585 321 : Chord* c = toChord(e);
586 321 : if (c->vStaffIdx() != chord->vStaffIdx()) {
587 : // this check is needed as we are iterating over all staves to capture cross-staff chords
588 0 : continue;
589 : }
590 : // if there are grace notes before, try to tie to first one
591 321 : std::vector<Chord*> gnb = c->graceNotesBefore();
592 321 : if (!gnb.empty()) {
593 1 : Chord* gc = gnb.front();
594 1 : Note* gn2 = gc->findNote(note->pitch());
595 1 : if (gn2) {
596 1 : return gn2;
597 : }
598 : }
599 320 : int idx2 = 0;
600 524 : for (Note* n : c->notes()) {
601 501 : if (n->pitch() == note->pitch()) {
602 297 : if (idx1 == idx2) {
603 297 : if (!note2 || c->track() == chord->track()) {
604 297 : note2 = n;
605 297 : break;
606 : }
607 : } else {
608 0 : ++idx2;
609 : }
610 : }
611 : }
612 321 : }
613 348 : return note2;
614 : }
615 :
616 : //---------------------------------------------------------
617 : // absStep
618 : /// Compute absolute step.
619 : /// C D E F G A B ....
620 : //---------------------------------------------------------
621 :
622 204899 : int absStep(int tpc, int pitch)
623 : {
624 204899 : int line = tpc2step(tpc) + (pitch / PITCH_DELTA_OCTAVE) * STEP_DELTA_OCTAVE;
625 204899 : int tpcPitch = tpc2pitch(tpc);
626 :
627 204899 : if (tpcPitch < MIN_PITCH) {
628 191 : line += STEP_DELTA_OCTAVE;
629 : } else {
630 204708 : line -= (tpcPitch / PITCH_DELTA_OCTAVE) * STEP_DELTA_OCTAVE;
631 : }
632 204899 : return line;
633 : }
634 :
635 282 : int absStep(int pitch)
636 : {
637 : // TODO - does this need to be key-aware?
638 282 : int tpc = pitch2tpc(pitch, Key::C, Prefer::NEAREST);
639 282 : return absStep(tpc, pitch);
640 : }
641 :
642 43 : int absStep(int line, ClefType clef)
643 : {
644 43 : return ClefInfo::pitchOffset(clef) - line;
645 : }
646 :
647 : //---------------------------------------------------------
648 : // relStep
649 : /// Compute relative step from absolute step
650 : /// which depends on actual clef. Step 0 starts on the
651 : /// first (top) staff line.
652 : //---------------------------------------------------------
653 :
654 56962 : int relStep(int line, ClefType clef)
655 : {
656 56962 : return ClefInfo::pitchOffset(clef) - line;
657 : }
658 :
659 0 : int relStep(int pitch, int tpc, ClefType clef)
660 : {
661 0 : return relStep(absStep(tpc, pitch), clef);
662 : }
663 :
664 : //---------------------------------------------------------
665 : // pitch2step
666 : // returns one of { 0, 1, 2, 3, 4, 5, 6 }
667 : //---------------------------------------------------------
668 :
669 0 : int pitch2step(int pitch)
670 : {
671 : // C C# D D# E F F# G G# A A# B
672 : static const char tab[12] = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 };
673 0 : return tab[pitch % 12];
674 : }
675 :
676 : //---------------------------------------------------------
677 : // step2pitch
678 : // returns one of { 0, 2, 4, 5, 7, 9, 11 }
679 : //---------------------------------------------------------
680 :
681 3233 : int step2pitch(int step)
682 : {
683 : static const char tab[7] = { 0, 2, 4, 5, 7, 9, 11 };
684 3233 : return tab[step % 7];
685 : }
686 :
687 : //---------------------------------------------------------
688 : // convertLine
689 : // find the line in clefF corresponding to lineL2 in clefR
690 : //---------------------------------------------------------
691 :
692 22 : int convertLine(int lineL2, ClefType clefL, ClefType clefR)
693 : {
694 22 : int lineR2 = lineL2;
695 22 : int goalpitch = line2pitch(lineL2, clefL, Key::C);
696 : int p;
697 22 : while ((p = line2pitch(lineR2, clefR, Key::C)) > goalpitch && p < MAX_PITCH) {
698 0 : lineR2++;
699 : }
700 22 : while ((p = line2pitch(lineR2, clefR, Key::C)) < goalpitch && p > MIN_PITCH) {
701 0 : lineR2--;
702 : }
703 22 : return lineR2;
704 : }
705 :
706 : //---------------------------------------------------------
707 : // convertLine
708 : // find the line in clef for NoteL corresponding to lineL2 in clef for noteR
709 : // for example middle C is line 10 in Treble clef, but is line -2 in Bass clef.
710 : //---------------------------------------------------------
711 :
712 22 : int convertLine(int lineL2, const Note* noteL, const Note* noteR)
713 : {
714 44 : return convertLine(lineL2,
715 22 : noteL->chord()->staff()->clef(noteL->chord()->tick()),
716 44 : noteR->chord()->staff()->clef(noteR->chord()->tick()));
717 : }
718 :
719 : //---------------------------------------------------------
720 : // chromaticPitchSteps -- an articulation such as a trill, or mordant consists of several notes
721 : // played in succession. The pitch offsets of each such note in the sequence can be represented either
722 : // as a number of steps in the diatonic scale, or in half steps as on a piano keyboard.
723 : // this function, articulationExcursion, takes deltastep indicating the number of steps in the
724 : // diatonic scale, and calculates (and returns) the number of half steps, taking several things into account.
725 : // E.g., the key signature, a trill from e to f, is to be understood as a trill between E and F# if we are
726 : // in the key of G.
727 : // E.g., if previously (looking backward in time) in the same measure there is another note on the same
728 : // staff line/space, and that note has an accidental (sharp,flat,natural,etc), then we want to match that
729 : // tone exactly.
730 : // E.g., If there are multiple notes on the same line/space, then we only consider the most
731 : // recent one, but avoid looking forward in time after the current note.
732 : // E.g., Also if there is an accidental // on a note one (or more) octaves above or below we
733 : // observe its accidental as well.
734 : // E.g., Still another case is that if two staves are involved (such as a glissando between two
735 : // notes on different staves) then we have to search both staves for the most recent accidental.
736 : //
737 : // noteL is the note to measure the deltastep from, i.e., ornaments are w.r.t. this note
738 : // noteR is the note to search backward from to find accidentals.
739 : // for ornament calculation noteL and noteR are the same, but for glissando they are
740 : // the start and end note of glissando.
741 : // nominalDiatonicSteps is the desired number of diatonic steps between the base note and this articulation step.
742 : //---------------------------------------------------------
743 :
744 26 : int chromaticPitchSteps(const Note* noteL, const Note* noteR, const int nominalDiatonicSteps)
745 : {
746 26 : if (0 == nominalDiatonicSteps) {
747 4 : return 0;
748 : }
749 22 : Chord* chordL = noteL->chord();
750 22 : Chord* chordR = noteR->chord();
751 22 : int epitchL = noteL->epitch();
752 22 : Fraction tickL = chordL->tick();
753 : // we cannot use staffL = chord->staff() because that won't correspond to the noteL->line()
754 : // in the case the user has pressed Shift-Cmd->Up or Shift-Cmd-Down.
755 : // Therefore we have to take staffMove() into account using vStaffIdx().
756 22 : Staff* staffL = noteL->score()->staff(chordL->vStaffIdx());
757 22 : ClefType clefL = staffL->clef(tickL);
758 : // line represents the ledger line of the staff. 0 is the top line, 1, is the space between the top 2 lines,
759 : // ... 8 is the bottom line.
760 22 : int lineL = noteL->line();
761 22 : if (lineL == INVALID_LINE) {
762 4 : int absLine = absStep(noteL->tpc(), noteL->epitch());
763 4 : ClefType clef = noteL->staff()->clef(noteL->tick());
764 4 : lineL = relStep(absLine, clef);
765 : }
766 :
767 : // we use line - deltastep, because lines are oriented from top to bottom, while step is oriented from bottom to top.
768 22 : int lineL2 = lineL - nominalDiatonicSteps;
769 22 : Measure* measureR = chordR->segment()->measure();
770 :
771 22 : Segment* segment = noteL->chord()->segment();
772 22 : int lineR2 = convertLine(lineL2, noteL, noteR);
773 : // is there another note in this segment on the same line?
774 : // if so, use its pitch exactly.
775 22 : int halfsteps = 0;
776 22 : staff_idx_t staffIdx = noteL->chord()->staff()->idx(); // cannot use staffL->idx() because of staffMove()
777 22 : track_idx_t startTrack = staffIdx * VOICES;
778 22 : track_idx_t endTrack = startTrack + VOICES;
779 22 : bool done = false;
780 110 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
781 88 : EngravingItem* e = segment->element(track);
782 88 : if (!e || !e->isChord()) {
783 66 : continue;
784 : }
785 22 : Chord* chord = toChord(e);
786 22 : if (chord->vStaffIdx() != chordL->vStaffIdx()) {
787 0 : continue;
788 : }
789 22 : if (!done) {
790 22 : if (staffL->isPitchedStaff(segment->tick())) {
791 22 : bool error = false;
792 22 : AccidentalVal acciv2 = measureR->findAccidental(chordR->segment(), chordR->vStaffIdx(), lineR2, error);
793 22 : int acci2 = int(acciv2);
794 : // epitch (effective pitch) is a visible pitch so line2pitch returns exactly that.
795 22 : halfsteps = line2pitch(lineL - nominalDiatonicSteps, clefL, Key::C) + acci2 - epitchL;
796 : } else {
797 : // cannot rely on accidentals or key signatures
798 0 : halfsteps = nominalDiatonicSteps;
799 : }
800 : }
801 : }
802 22 : return halfsteps;
803 : }
804 :
805 0 : static void noteValToEffectivePitchAndTpc(const NoteVal& nval, const Staff* staff, const Fraction& tick, int& epitch, int& tpc)
806 : {
807 0 : const bool concertPitch = staff->concertPitch();
808 :
809 0 : if (concertPitch) {
810 0 : epitch = nval.pitch;
811 : } else {
812 0 : const int pitchOffset = staff->originPart(tick)->instrument(tick)->transpose().chromatic;
813 0 : epitch = nval.pitch - pitchOffset;
814 : }
815 :
816 0 : tpc = nval.tpc(concertPitch);
817 0 : if (tpc == static_cast<int>(mu::engraving::Tpc::TPC_INVALID)) {
818 0 : tpc = pitch2tpc(epitch, staff->key(tick), mu::engraving::Prefer::NEAREST);
819 : }
820 0 : }
821 :
822 0 : int noteValToLine(const NoteVal& nval, const Staff* staff, const Fraction& tick)
823 : {
824 0 : if (staff->isDrumStaff(tick)) {
825 0 : const Drumset* drumset = staff->originPart(tick)->instrument(tick)->drumset();
826 0 : if (drumset) {
827 0 : return drumset->line(nval.pitch);
828 : }
829 : }
830 :
831 0 : if (nval.isRest()) {
832 0 : return staff->middleLine(tick);
833 : }
834 :
835 0 : int epitch = nval.pitch;
836 0 : int tpc = static_cast<int>(mu::engraving::Tpc::TPC_INVALID);
837 0 : noteValToEffectivePitchAndTpc(nval, staff, tick, epitch, tpc);
838 :
839 0 : return relStep(epitch, tpc, staff->clef(tick));
840 : }
841 :
842 0 : AccidentalVal noteValToAccidentalVal(const NoteVal& nval, const Staff* staff, const Fraction& tick)
843 : {
844 0 : if (nval.isRest()) {
845 0 : return AccidentalVal::NATURAL;
846 : }
847 :
848 0 : if (staff->isDrumStaff(tick)) {
849 0 : return AccidentalVal::NATURAL;
850 : }
851 :
852 0 : int epitch = nval.pitch;
853 0 : int tpc = static_cast<int>(mu::engraving::Tpc::TPC_INVALID);
854 0 : noteValToEffectivePitchAndTpc(nval, staff, tick, epitch, tpc);
855 :
856 0 : return tpc2alter(tpc);
857 : }
858 :
859 4564 : int compareNotesPos(const Note* n1, const Note* n2)
860 : {
861 4564 : if (n1->line() != n2->line() && !(n1->staffType()->isTabStaff())) {
862 4470 : return n2->line() - n1->line();
863 94 : } else if (n1->string() != n2->string()) {
864 90 : return n2->string() - n1->string();
865 : } else {
866 4 : return n1->pitch() - n2->pitch();
867 : }
868 : }
869 :
870 : //---------------------------------------------------------
871 : // skipTuplet
872 : // return segment of rightmost chord/rest in a
873 : // (possible nested) tuplet
874 : //---------------------------------------------------------
875 :
876 542 : Segment* skipTuplet(Tuplet* tuplet)
877 : {
878 542 : DurationElement* nde = tuplet->elements().back();
879 557 : while (nde->isTuplet()) {
880 15 : tuplet = toTuplet(nde);
881 15 : nde = tuplet->elements().back();
882 : }
883 542 : return toChordRest(nde)->segment();
884 : }
885 :
886 : //---------------------------------------------------------
887 : // timeSigSymIdsFromString
888 : // replace ascii with bravura symbols
889 : //---------------------------------------------------------
890 :
891 13019 : SymIdList timeSigSymIdsFromString(const String& string, TimeSigStyle timeSigStyle)
892 : {
893 : static const std::map<Char, SymId> dict = {
894 : { '+', SymId::timeSigPlusSmall },
895 : { '0', SymId::timeSig0 },
896 : { '1', SymId::timeSig1 },
897 : { '2', SymId::timeSig2 },
898 : { '3', SymId::timeSig3 },
899 : { '4', SymId::timeSig4 },
900 : { '5', SymId::timeSig5 },
901 : { '6', SymId::timeSig6 },
902 : { '7', SymId::timeSig7 },
903 : { '8', SymId::timeSig8 },
904 : { '9', SymId::timeSig9 },
905 : { 'C', SymId::timeSigCommon },
906 : { '(', SymId::timeSigParensLeftSmall },
907 : { ')', SymId::timeSigParensRightSmall },
908 : { '[', SymId::timeSigBracketLeftSmall },
909 : { ']', SymId::timeSigBracketRightSmall },
910 : { u'¢', SymId::timeSigCutCommon },
911 : { u'½', SymId::timeSigFractionHalf },
912 : { u'¼', SymId::timeSigFractionQuarter },
913 : { '*', SymId::timeSigMultiply },
914 : { 'X', SymId::timeSigMultiply },
915 : { 'x', SymId::timeSigMultiply },
916 : { u'×', SymId::timeSigMultiply },
917 : { 59664, SymId::mensuralProlation1 },
918 : { 79, SymId::mensuralProlation2 }, // 'O'
919 : { 59665, SymId::mensuralProlation2 },
920 : { 216, SymId::mensuralProlation3 }, // 'Ø'
921 : { 59666, SymId::mensuralProlation3 },
922 : { 59667, SymId::mensuralProlation4 },
923 : { 59668, SymId::mensuralProlation5 },
924 : { 59669, SymId::mensuralProlation6 },
925 : { 59670, SymId::mensuralProlation7 },
926 : { 59671, SymId::mensuralProlation8 },
927 : { 59672, SymId::mensuralProlation9 },
928 : { 59673, SymId::mensuralProlation10 },
929 : { 59674, SymId::mensuralProlation11 },
930 13047 : };
931 :
932 : static const std::map<Char, SymId> dictLarge = {
933 : { '+', SymId::timeSigPlusSmallLarge },
934 : { '0', SymId::timeSig0Large },
935 : { '1', SymId::timeSig1Large },
936 : { '2', SymId::timeSig2Large },
937 : { '3', SymId::timeSig3Large },
938 : { '4', SymId::timeSig4Large },
939 : { '5', SymId::timeSig5Large },
940 : { '6', SymId::timeSig6Large },
941 : { '7', SymId::timeSig7Large },
942 : { '8', SymId::timeSig8Large },
943 : { '9', SymId::timeSig9Large },
944 : { 'C', SymId::timeSigCommonLarge },
945 : { '(', SymId::timeSigParensLeftSmallLarge },
946 : { ')', SymId::timeSigParensRightSmallLarge },
947 : { '[', SymId::timeSigBracketLeftSmallLarge },
948 : { ']', SymId::timeSigBracketRightSmallLarge },
949 : { u'¢', SymId::timeSigCutCommonLarge },
950 : { u'½', SymId::timeSigFractionHalfLarge },
951 : { u'¼', SymId::timeSigFractionQuarterLarge },
952 : { '*', SymId::timeSigMultiplyLarge },
953 : { 'X', SymId::timeSigMultiplyLarge },
954 : { 'x', SymId::timeSigMultiplyLarge },
955 : { u'×', SymId::timeSigMultiplyLarge },
956 13047 : };
957 :
958 : static const std::map<Char, SymId> dictNarrow = {
959 : { '+', SymId::timeSigPlusSmallNarrow },
960 : { '0', SymId::timeSig0Narrow },
961 : { '1', SymId::timeSig1Narrow },
962 : { '2', SymId::timeSig2Narrow },
963 : { '3', SymId::timeSig3Narrow },
964 : { '4', SymId::timeSig4Narrow },
965 : { '5', SymId::timeSig5Narrow },
966 : { '6', SymId::timeSig6Narrow },
967 : { '7', SymId::timeSig7Narrow },
968 : { '8', SymId::timeSig8Narrow },
969 : { '9', SymId::timeSig9Narrow },
970 : { 'C', SymId::timeSigCommonNarrow },
971 : { '(', SymId::timeSigParensLeftSmallNarrow },
972 : { ')', SymId::timeSigParensRightSmallNarrow },
973 : { '[', SymId::timeSigBracketLeftSmallNarrow },
974 : { ']', SymId::timeSigBracketRightSmallNarrow },
975 : { u'¢', SymId::timeSigCutCommonNarrow },
976 : { u'½', SymId::timeSigFractionHalfNarrow },
977 : { u'¼', SymId::timeSigFractionQuarterNarrow },
978 : { '*', SymId::timeSigMultiplyNarrow },
979 : { 'X', SymId::timeSigMultiplyNarrow },
980 : { 'X', SymId::timeSigMultiplyNarrow },
981 : { u'×', SymId::timeSigMultiplyNarrow },
982 13047 : };
983 :
984 13019 : SymIdList list;
985 26252 : for (size_t i = 0; i < string.size(); ++i) {
986 13233 : SymId sym = muse::value(
987 : timeSigStyle == TimeSigStyle::NARROW ? dictNarrow
988 : : timeSigStyle == TimeSigStyle::LARGE ? dictLarge
989 : : dict,
990 13233 : string.at(i), SymId::noSym);
991 13233 : if (sym != SymId::noSym) {
992 13223 : list.push_back(sym);
993 : }
994 : }
995 13019 : return list;
996 0 : }
997 :
998 : //---------------------------------------------------------
999 : // actualTicks
1000 : //---------------------------------------------------------
1001 :
1002 7006 : Fraction actualTicks(Fraction duration, Tuplet* tuplet, Fraction timeStretch)
1003 : {
1004 7006 : Fraction f = duration / timeStretch;
1005 7246 : for (Tuplet* t = tuplet; t; t = t->tuplet()) {
1006 240 : f /= t->ratio();
1007 : }
1008 7006 : return f;
1009 : }
1010 :
1011 0 : bool dragPositionToMeasure(const PointF& pos, const Score* score,
1012 : Measure** measure, staff_idx_t* staffIdx,
1013 : const double spacingFactor)
1014 : {
1015 0 : const System* preferredSystem = (*measure) ? (*measure)->system() : nullptr;
1016 :
1017 0 : MeasureBase* mb = score->searchMeasureBase(pos, /*scanMeasuresOnly*/ true, preferredSystem, spacingFactor);
1018 0 : Measure* m = mb && mb->isMeasure() ? toMeasure(mb) : nullptr;
1019 0 : if (!m) {
1020 0 : return false;
1021 : }
1022 :
1023 0 : const System* system = m->system();
1024 0 : const double y = pos.y() - system->canvasPos().y();
1025 0 : const staff_idx_t i = system->searchStaff(y, *staffIdx, spacingFactor);
1026 0 : if (!score->staff(i)) {
1027 0 : return false;
1028 : }
1029 :
1030 0 : *measure = m;
1031 0 : *staffIdx = i;
1032 0 : return true;
1033 : }
1034 :
1035 0 : bool dragPositionToSegment(const PointF& pos, const Measure* measure, const staff_idx_t staffIdx,
1036 : Segment** segment,
1037 : const double spacingFactor, const bool allowTimeAnchor)
1038 : {
1039 0 : const track_idx_t strack = staffIdx * VOICES;
1040 0 : const track_idx_t etrack = strack + VOICES;
1041 :
1042 0 : const double x = pos.x() - measure->canvasPos().x();
1043 0 : const SegmentType st = allowTimeAnchor ? SegmentType::Duration : SegmentType::ChordRest;
1044 0 : Segment* s = measure->searchSegment(x, st, strack, etrack, *segment, spacingFactor);
1045 0 : if (!s) {
1046 0 : return false;
1047 : }
1048 :
1049 0 : *segment = segmentOrChordRestSegmentAtSameTick(s);
1050 0 : return true;
1051 : }
1052 :
1053 0 : Segment* segmentOrChordRestSegmentAtSameTick(Segment* segment)
1054 : {
1055 0 : IF_ASSERT_FAILED(segment) {
1056 0 : return nullptr;
1057 : }
1058 :
1059 : // If TimeTick and ChordRest segments are at the same tick, prefer ChordRest
1060 0 : if (segment->isTimeTickType() && segment->measure()) {
1061 0 : if (Segment* crSegAtSameTick = segment->measure()->findSegmentR(SegmentType::ChordRest, segment->rtick())) {
1062 0 : return crSegAtSameTick;
1063 : }
1064 : }
1065 :
1066 0 : return segment;
1067 : }
1068 :
1069 461 : double yStaffDifference(const System* system1, const System* system2, staff_idx_t staffIdx)
1070 : {
1071 461 : if (!system1 || !system2) {
1072 0 : return 0.0;
1073 : }
1074 :
1075 461 : const SysStaff* staff1 = system1->staff(staffIdx);
1076 461 : const SysStaff* staff2 = system2->staff(staffIdx);
1077 461 : if (!staff1 || !staff2) {
1078 24 : return 0.0;
1079 : }
1080 437 : return staff1->y() - staff2->y();
1081 : }
1082 :
1083 29 : bool allowRemoveWhenRemovingStaves(EngravingItem* item, staff_idx_t startStaff, staff_idx_t endStaff)
1084 : {
1085 : // Sanity checks
1086 29 : if (!item || item->staffIdx() == muse::nidx || startStaff == muse::nidx || endStaff == muse::nidx) {
1087 0 : return false;
1088 : }
1089 :
1090 29 : Score* score = item->score();
1091 29 : if (score->nstaves() == 1) {
1092 6 : return true;
1093 : }
1094 :
1095 23 : if (endStaff == 0) { // Default initialized
1096 12 : endStaff = startStaff + 1;
1097 : }
1098 :
1099 23 : staff_idx_t staffIdx = item->staffIdx();
1100 23 : if (staffIdx < startStaff || staffIdx >= endStaff) {
1101 8 : return false;
1102 : }
1103 :
1104 15 : Staff* nextRemaining = score->staff(endStaff);
1105 15 : bool nextRemainingIsSystemObjectStaff = nextRemaining && nextRemaining->isSystemObjectStaff();
1106 15 : if (item->isTopSystemObject() && !nextRemainingIsSystemObjectStaff) {
1107 0 : return false;
1108 : }
1109 :
1110 15 : return true;
1111 : }
1112 :
1113 172 : bool moveDownWhenAddingStaves(EngravingItem* item, staff_idx_t startStaff, staff_idx_t endStaff)
1114 : {
1115 : // Sanity checks
1116 172 : if (!item || item->staffIdx() == muse::nidx || startStaff == muse::nidx || endStaff == muse::nidx) {
1117 0 : return false;
1118 : }
1119 :
1120 172 : if (item->staffIdx() < startStaff) {
1121 162 : return false;
1122 : }
1123 :
1124 10 : if (endStaff == 0) { // Default initialized
1125 10 : endStaff = startStaff + 1;
1126 : }
1127 :
1128 10 : Score* score = item->score();
1129 10 : Staff* nextAfterInserted = score->staff(endStaff);
1130 10 : bool nextAfterInsertedIsSystemObjectStaff = nextAfterInserted && nextAfterInserted->isSystemObjectStaff();
1131 10 : if (item->isTopSystemObject() && !nextAfterInsertedIsSystemObjectStaff) {
1132 0 : return false;
1133 : }
1134 :
1135 10 : return true;
1136 : }
1137 :
1138 87855 : void collectChordsAndRest(Segment* segment, staff_idx_t staffIdx, std::vector<Chord*>& chords, std::vector<Rest*>& rests)
1139 : {
1140 87855 : if (!segment) {
1141 0 : return;
1142 : }
1143 :
1144 87855 : track_idx_t startTrack = staffIdx * VOICES;
1145 87855 : track_idx_t endTrack = startTrack + VOICES;
1146 :
1147 439275 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1148 351420 : EngravingItem* e = segment->element(track);
1149 351420 : if (!e) {
1150 264310 : continue;
1151 : }
1152 87110 : if (e->isChord() && !toChordRest(e)->staffMove()) {
1153 47904 : chords.push_back(toChord(e));
1154 39206 : } else if (e->isRest() && !toChordRest(e)->staffMove() && !toRest(e)->isFullMeasureRest()) {
1155 11104 : rests.push_back(toRest(e));
1156 : }
1157 : }
1158 : }
1159 :
1160 10167 : void collectChordsOverlappingRests(Segment* segment, staff_idx_t staffIdx, std::vector<Chord*>& chords)
1161 : {
1162 : // Check if previous segments contain chords in other voices
1163 : // whose duration overlaps with rests on this segment
1164 :
1165 10167 : track_idx_t startTrack = staffIdx * VOICES;
1166 10167 : track_idx_t endTrack = startTrack + VOICES;
1167 :
1168 10167 : std::set<track_idx_t> tracksToCheck;
1169 50835 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1170 40668 : EngravingItem* item = segment->element(track);
1171 40668 : if (!item || !item->isRest()) {
1172 29556 : tracksToCheck.insert(track);
1173 : }
1174 : }
1175 :
1176 10167 : Fraction curTick = segment->rtick();
1177 48308 : for (Segment* prevSeg = segment->prev(); prevSeg; prevSeg = prevSeg->prev()) {
1178 38141 : if (!prevSeg->isChordRestType()) {
1179 9509 : continue;
1180 : }
1181 28632 : Fraction prevSegTick = prevSeg->rtick();
1182 111978 : for (track_idx_t track : tracksToCheck) {
1183 83346 : EngravingItem* e = prevSeg->element(track);
1184 83346 : if (!e || !e->isChord()) {
1185 83248 : continue;
1186 : }
1187 1146 : Chord* chord = toChord(e);
1188 1146 : Fraction chordEndTick = prevSegTick + chord->actualTicks();
1189 1146 : if (chordEndTick <= curTick) {
1190 1048 : continue;
1191 : }
1192 :
1193 98 : chords.push_back(chord);
1194 : }
1195 : }
1196 10167 : }
1197 :
1198 0 : std::vector<EngravingItem*> collectSystemObjects(const Score* score, const std::vector<Staff*>& staves)
1199 : {
1200 0 : TRACEFUNC;
1201 :
1202 0 : std::vector<EngravingItem*> result;
1203 :
1204 0 : const TimeSigPlacement timeSigPlacement = score->style().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>();
1205 0 : const bool isOnStaffTimeSig = timeSigPlacement != TimeSigPlacement::NORMAL;
1206 :
1207 0 : for (const Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
1208 0 : for (EngravingItem* measureElement : measure->el()) {
1209 0 : if (!measureElement || !measureElement->systemFlag() || measureElement->isLayoutBreak()) {
1210 0 : continue;
1211 : }
1212 0 : if (!staves.empty()) {
1213 0 : if (muse::contains(staves, measureElement->staff())) {
1214 0 : result.push_back(measureElement);
1215 : }
1216 0 : } else if (measureElement->isTopSystemObject()) {
1217 0 : result.push_back(measureElement);
1218 : }
1219 : }
1220 :
1221 0 : for (const Segment& seg : measure->segments()) {
1222 0 : if (seg.isType(SegmentType::Duration | SegmentType::EndBarLine)) {
1223 0 : for (EngravingItem* annotation : seg.annotations()) {
1224 0 : if (!annotation || !annotation->systemFlag()) {
1225 0 : continue;
1226 : }
1227 :
1228 0 : if (!staves.empty()) {
1229 0 : if (muse::contains(staves, annotation->staff())) {
1230 0 : result.push_back(annotation);
1231 : }
1232 0 : } else if (annotation->isTopSystemObject()) {
1233 0 : result.push_back(annotation);
1234 : }
1235 : }
1236 : }
1237 :
1238 0 : if (isOnStaffTimeSig && seg.isType(SegmentType::TimeSigTypes)) {
1239 0 : for (EngravingItem* item : seg.elist()) {
1240 0 : if (!item || !item->isTimeSig()) {
1241 0 : continue;
1242 : }
1243 :
1244 0 : if (!staves.empty()) {
1245 0 : if (muse::contains(staves, item->staff())) {
1246 0 : result.push_back(item);
1247 : }
1248 0 : } else if (item->staffIdx() == 0) {
1249 0 : result.push_back(item);
1250 : }
1251 : }
1252 : }
1253 : }
1254 : }
1255 :
1256 0 : for (const auto& pair : score->spanner()) {
1257 0 : Spanner* spanner = pair.second;
1258 0 : if (!spanner->systemFlag()) {
1259 0 : continue;
1260 : }
1261 :
1262 0 : if (!staves.empty()) {
1263 0 : if (muse::contains(staves, spanner->staff())) {
1264 0 : result.push_back(spanner);
1265 : }
1266 0 : } else if (spanner->isTopSystemObject()) {
1267 0 : result.push_back(spanner);
1268 : }
1269 : }
1270 :
1271 0 : return result;
1272 0 : }
1273 :
1274 13414 : std::unordered_set<EngravingItem*> collectElementsAnchoredToChordRest(const ChordRest* cr)
1275 : {
1276 13414 : std::unordered_set<EngravingItem*> elems;
1277 13444 : for (EngravingItem* lyric : cr->lyrics()) {
1278 30 : elems.emplace(lyric);
1279 : }
1280 13414 : if (!cr->isChord()) {
1281 4902 : return elems;
1282 : }
1283 8512 : const Chord* chord = toChord(cr);
1284 8512 : if (Arpeggio* arp = chord->arpeggio()) {
1285 5 : elems.emplace(arp);
1286 : }
1287 8512 : if (TremoloTwoChord* tremTwo = chord->tremoloTwoChord()) {
1288 6 : elems.emplace(tremTwo);
1289 : }
1290 8512 : if (TremoloSingleChord* tremSing = chord->tremoloSingleChord()) {
1291 5 : elems.emplace(tremSing);
1292 : }
1293 8748 : for (Articulation* art : chord->articulations()) {
1294 236 : elems.emplace(art);
1295 : }
1296 : // Chord brackets and chord lines
1297 8515 : for (EngravingItem* e : chord->el()) {
1298 3 : elems.emplace(e);
1299 : }
1300 8569 : for (Chord* grace : chord->graceNotes()) {
1301 57 : elems.emplace(grace);
1302 57 : for (Articulation* gArt : grace->articulations()) {
1303 0 : elems.emplace(gArt);
1304 : }
1305 57 : if (TremoloSingleChord* gTremSing = grace->tremoloSingleChord()) {
1306 0 : elems.emplace(gTremSing);
1307 : }
1308 : }
1309 8512 : return elems;
1310 0 : }
1311 :
1312 9911 : std::unordered_set<EngravingItem*> collectElementsAnchoredToNote(const Note* note, bool includeForwardTiesSpanners,
1313 : bool includeBackwardTiesSpanners)
1314 : {
1315 9911 : std::unordered_set<EngravingItem*> elems;
1316 9911 : LaissezVib* lv = note->laissezVib();
1317 9911 : if (lv && !lv->segmentsEmpty()) {
1318 4 : elems.emplace(lv);
1319 : }
1320 9911 : PartialTie* ipt = note->incomingPartialTie();
1321 9911 : if (ipt && !ipt->segmentsEmpty()) {
1322 15 : elems.emplace(ipt);
1323 : }
1324 9911 : PartialTie* opt = note->outgoingPartialTie();
1325 9911 : if (opt && !opt->segmentsEmpty()) {
1326 13 : elems.emplace(opt);
1327 : }
1328 : // The following is a bit of a hack - addressing properly would require a fingering rework...
1329 9915 : for (EngravingItem* elem : note->el()) {
1330 4 : if (elem->isFingering()) {
1331 4 : elems.emplace(elem);
1332 : }
1333 : }
1334 9911 : if (includeForwardTiesSpanners) {
1335 9911 : Tie* tieFor = note->tieFor();
1336 9911 : if (tieFor && !tieFor->segmentsEmpty()) {
1337 1770 : elems.emplace(tieFor);
1338 : }
1339 9946 : for (Spanner* sp : note->spannerFor()) {
1340 35 : if (sp->segmentsEmpty()) {
1341 0 : continue;
1342 : }
1343 35 : elems.emplace(sp);
1344 : }
1345 : }
1346 9911 : if (includeBackwardTiesSpanners) {
1347 0 : Tie* tieBack = note->tieBack();
1348 0 : if (tieBack && !tieBack->segmentsEmpty()) {
1349 0 : elems.emplace(tieBack);
1350 : }
1351 0 : for (Spanner* sp : note->spannerBack()) {
1352 0 : if (sp->segmentsEmpty()) {
1353 0 : continue;
1354 : }
1355 0 : elems.emplace(sp);
1356 : }
1357 : }
1358 9911 : const NoteParenthesisInfo* noteParenInfo = note->parenthesisInfo();
1359 9911 : if (noteParenInfo && noteParenInfo->notes().size()) {
1360 0 : elems.emplace(noteParenInfo->leftParen());
1361 0 : elems.emplace(noteParenInfo->rightParen());
1362 : }
1363 9911 : return elems;
1364 0 : }
1365 :
1366 1788 : bool noteAnchoredSpannerIsInRange(const Spanner* spanner, const Fraction& rangeStart, const Fraction& rangeEnd)
1367 : {
1368 1788 : IF_ASSERT_FAILED(rangeStart < rangeEnd) {
1369 0 : return false;
1370 : }
1371 1788 : const EngravingItem* startElement = spanner->startElement();
1372 1788 : const EngravingItem* endElement = spanner->endElement();
1373 1788 : IF_ASSERT_FAILED(startElement && startElement->isNote() && endElement && endElement->isNote()) {
1374 0 : LOGD() << "Cannot calculate isInRange - might be a partial tie or laissez vibrer";
1375 0 : return false;
1376 : }
1377 1788 : const Note* startNote = toNote(startElement);
1378 1788 : const Segment* startSeg = startNote->chord()->segment();
1379 1788 : if (startSeg->tick() < rangeStart) {
1380 0 : return false;
1381 : }
1382 1788 : const Note* endNote = toNote(endElement);
1383 1788 : const Segment* endSeg = endNote->chord()->segment();
1384 1788 : return !endSeg || endSeg->tick() <= rangeEnd;
1385 : }
1386 :
1387 0 : String formatUniqueExcerptName(const String& baseName, const StringList& allExcerptLowerNames)
1388 : {
1389 0 : String result = baseName;
1390 0 : int num = 0;
1391 :
1392 0 : while (allExcerptLowerNames.contains(result.toLower())) {
1393 0 : result = baseName + String(u" (%1)").arg(++num);
1394 : }
1395 :
1396 0 : return result;
1397 0 : }
1398 :
1399 11 : bool isFirstSystemKeySig(const KeySig* ks)
1400 : {
1401 11 : if (!ks) {
1402 0 : return false;
1403 : }
1404 11 : const System* sys = ks->measure()->system();
1405 11 : if (!sys) {
1406 0 : return false;
1407 : }
1408 11 : return ks->tick() == sys->firstMeasure()->tick();
1409 : }
1410 :
1411 1224 : String bendAmountToString(int fulls, int quarts, bool useFractions)
1412 : {
1413 1224 : String string = fulls != 0 ? String::number(fulls) : String();
1414 :
1415 1224 : if (useFractions) {
1416 1224 : switch (std::abs(quarts)) {
1417 118 : case 1:
1418 118 : string += u"\u00BC";
1419 118 : break;
1420 271 : case 2:
1421 271 : string += u"\u00BD";
1422 271 : break;
1423 13 : case 3:
1424 13 : string += u"\u00BE";
1425 13 : break;
1426 822 : default:
1427 822 : break;
1428 : }
1429 : } else {
1430 0 : if (!string.empty() && quarts != 0) {
1431 0 : string += u" ";
1432 : }
1433 0 : switch (std::abs(quarts)) {
1434 0 : case 1:
1435 0 : string += u"1/4";
1436 0 : break;
1437 0 : case 2:
1438 0 : string += u"1/2";
1439 0 : break;
1440 0 : case 3:
1441 0 : string += u"3/4";
1442 0 : break;
1443 0 : default:
1444 0 : break;
1445 : }
1446 : }
1447 :
1448 1224 : return string;
1449 0 : }
1450 :
1451 1009 : InstrumentTrackId makeInstrumentTrackId(const EngravingItem* item)
1452 : {
1453 1009 : const Part* part = item->part();
1454 1009 : if (!part) {
1455 0 : return InstrumentTrackId();
1456 : }
1457 :
1458 : InstrumentTrackId trackId {
1459 : part->id(),
1460 0 : part->instrumentId(item->tick())
1461 1009 : };
1462 :
1463 1009 : return trackId;
1464 1009 : }
1465 :
1466 33631 : std::vector<Measure*> findFollowingRepeatMeasures(const Measure* measure)
1467 : {
1468 33631 : const MasterScore* master = measure->masterScore();
1469 33631 : const Score* score = measure->score();
1470 :
1471 33631 : const Measure* masterMeasure = master->tick2measureMM(measure->tick());
1472 :
1473 33631 : const RepeatList& repeatList = master->repeatList(true, false);
1474 :
1475 33631 : std::vector<Measure*> measures;
1476 :
1477 76036 : for (auto it = repeatList.begin(); it != repeatList.end(); it++) {
1478 42405 : const RepeatSegment* rs = *it;
1479 42405 : const auto nextSegIt = std::next(it);
1480 42405 : if (!rs->endsWithMeasure(masterMeasure) || nextSegIt == repeatList.end()) {
1481 40684 : continue;
1482 : }
1483 :
1484 : // Get next segment
1485 1721 : const RepeatSegment* nextSeg = *nextSegIt;
1486 1721 : const Measure* firstMasterMeasure = nextSeg->firstMeasure();
1487 1721 : Measure* firstMeasure = firstMasterMeasure ? score->tick2measureMM(firstMasterMeasure->tick()) : nullptr;
1488 1721 : if (!firstMeasure) {
1489 0 : continue;
1490 : }
1491 :
1492 1721 : measures.push_back(firstMeasure);
1493 : }
1494 :
1495 33631 : return measures;
1496 0 : }
1497 :
1498 274 : std::vector<Measure*> findPreviousRepeatMeasures(const Measure* measure)
1499 : {
1500 274 : const MasterScore* master = measure->masterScore();
1501 274 : const Score* score = measure->score();
1502 :
1503 274 : const Measure* masterMeasure = master->tick2measureMM(measure->tick());
1504 :
1505 274 : const RepeatList& repeatList = master->repeatList(true, false);
1506 :
1507 274 : std::vector<Measure*> measures;
1508 :
1509 274 : if (repeatList.empty()) {
1510 0 : return measures;
1511 : }
1512 :
1513 327 : for (auto it = repeatList.begin() + 1; it != repeatList.end(); it++) {
1514 53 : const RepeatSegment* rs = *it;
1515 53 : const auto prevSegIt = std::prev(it);
1516 53 : if (!rs->startsWithMeasure(masterMeasure) || prevSegIt == repeatList.end()) {
1517 22 : continue;
1518 : }
1519 :
1520 : // Get next segment
1521 31 : const RepeatSegment* prevSeg = *prevSegIt;
1522 31 : const Measure* lastMasterMeasure = prevSeg->lastMeasure();
1523 31 : Measure* lastMeasure = lastMasterMeasure ? score->tick2measureMM(lastMasterMeasure->tick()) : nullptr;
1524 31 : if (!lastMeasure) {
1525 0 : continue;
1526 : }
1527 :
1528 31 : measures.push_back(lastMeasure);
1529 : }
1530 :
1531 274 : return measures;
1532 0 : }
1533 :
1534 3 : bool repeatHasPartialLyricLine(const Measure* endRepeatMeasure)
1535 : {
1536 3 : const std::vector<Measure*> measures = findFollowingRepeatMeasures(endRepeatMeasure);
1537 3 : const Score* score = endRepeatMeasure->score();
1538 :
1539 3 : for (const Measure* measure : measures) {
1540 3 : const SpannerMap::IntervalList& spanners = score->spannerMap().findOverlapping(measure->tick().ticks(), measure->endTick().ticks());
1541 :
1542 3 : for (auto& spanner : spanners) {
1543 3 : if (spanner.value->isPartialLyricsLine() && spanner.start == measure->tick().ticks()) {
1544 3 : return true;
1545 : }
1546 : }
1547 : }
1548 :
1549 0 : return false;
1550 3 : }
1551 :
1552 15045 : bool segmentsAreAdjacent(const Segment* firstSeg, const Segment* secondSeg)
1553 : {
1554 15045 : if (!firstSeg || !secondSeg) {
1555 39 : return false;
1556 : }
1557 15006 : const MasterScore* master = firstSeg->masterScore();
1558 :
1559 15006 : const Measure* firstMeasure = firstSeg->measure();
1560 15006 : const Measure* secondMeasure = secondSeg->measure();
1561 :
1562 15006 : if (firstMeasure == secondMeasure) {
1563 13967 : return true;
1564 : }
1565 :
1566 1039 : const Measure* firstMasterMeasure = master->tick2measure(firstMeasure->tick());
1567 1039 : const Measure* secondMasterMeasure = master->tick2measure(secondMeasure->tick());
1568 :
1569 1039 : if (firstMasterMeasure) {
1570 1039 : firstMasterMeasure = firstMasterMeasure->coveringMMRestOrThis();
1571 : }
1572 1039 : if (secondMasterMeasure) {
1573 1039 : secondMasterMeasure = secondMasterMeasure->coveringMMRestOrThis();
1574 : }
1575 :
1576 1039 : IF_ASSERT_FAILED(firstMasterMeasure && secondMasterMeasure) {
1577 0 : return false;
1578 : }
1579 :
1580 1039 : const Score* score = firstSeg->score();
1581 1039 : const RepeatList& repeatList = score->repeatList(true, false);
1582 :
1583 1039 : std::vector<const Measure*> measures;
1584 :
1585 1039 : bool firstMeasureSegmentFound = false;
1586 1039 : bool secondMeasureSegmentFound = false;
1587 :
1588 1173 : for (auto it = repeatList.begin(); it != repeatList.end(); it++) {
1589 1155 : const RepeatSegment* rs = *it;
1590 1155 : const auto nextSegIt = std::next(it);
1591 :
1592 : // Check if measures are in the same repeat segment
1593 1155 : bool containsFirstMeasure = rs->containsMeasure(firstMasterMeasure);
1594 1155 : bool containsSecondMeasure = rs->containsMeasure(secondMasterMeasure);
1595 1155 : firstMeasureSegmentFound |= containsFirstMeasure;
1596 1155 : secondMeasureSegmentFound |= containsSecondMeasure;
1597 :
1598 1155 : if (containsFirstMeasure && containsSecondMeasure) {
1599 1021 : return true;
1600 : }
1601 :
1602 : // Continue to build list of measures at the start of following repeat segments
1603 134 : if (!rs->endsWithMeasure(firstMasterMeasure) || nextSegIt == repeatList.end()) {
1604 68 : continue;
1605 : }
1606 :
1607 : // Get next segment
1608 66 : const RepeatSegment* nextSeg = *nextSegIt;
1609 66 : const Measure* nextSegFirstMeasure = nextSeg->firstMeasure();
1610 66 : if (!nextSegFirstMeasure) {
1611 0 : continue;
1612 : }
1613 :
1614 66 : measures.push_back(nextSegFirstMeasure);
1615 : }
1616 :
1617 : // Check if second segment is in a following measure in the repeat structure
1618 22 : for (const Measure* m : measures) {
1619 6 : if (m == secondMasterMeasure) {
1620 2 : return true;
1621 : }
1622 : }
1623 :
1624 16 : if (!firstMeasureSegmentFound && !secondMeasureSegmentFound) {
1625 : // The measures are outside of the (invalid) repeat structure
1626 0 : return firstMasterMeasure->nextMeasure() == secondMasterMeasure;
1627 : }
1628 :
1629 16 : return false;
1630 1039 : }
1631 :
1632 14414 : bool segmentsAreInDifferentRepeatSegments(const Segment* firstSeg, const Segment* secondSeg)
1633 : {
1634 14414 : if (!firstSeg || !secondSeg) {
1635 39 : return false;
1636 : }
1637 14375 : const MasterScore* master = firstSeg->masterScore();
1638 :
1639 14375 : Measure* firstMeasure = firstSeg->measure();
1640 14375 : Measure* secondMeasure = secondSeg->measure();
1641 :
1642 14375 : if (firstMeasure == secondMeasure) {
1643 13619 : return false;
1644 : }
1645 :
1646 756 : const Measure* firstMasterMeasure = master->tick2measure(firstMeasure->tick());
1647 756 : const Measure* secondMasterMeasure = master->tick2measure(secondMeasure->tick());
1648 :
1649 756 : Score* score = firstSeg->score();
1650 :
1651 756 : const RepeatList& repeatList = score->repeatList(true, false);
1652 :
1653 756 : std::vector<const Measure*> measures;
1654 :
1655 1502 : for (auto it = repeatList.begin(); it != repeatList.end(); it++) {
1656 778 : const RepeatSegment* rs = *it;
1657 :
1658 778 : if (!rs->containsMeasure(firstMasterMeasure) || !rs->containsMeasure(secondMasterMeasure)) {
1659 32 : return true;
1660 : }
1661 : }
1662 :
1663 724 : return false;
1664 756 : }
1665 :
1666 0 : bool isValidBarLineForRepeatSection(const Segment* firstSeg, const Segment* secondSeg)
1667 : {
1668 0 : if (!firstSeg || !secondSeg) {
1669 0 : return false;
1670 : }
1671 0 : if (!firstSeg->isType(SegmentType::BarLineTypes)) {
1672 0 : return false;
1673 : }
1674 :
1675 0 : const MasterScore* master = firstSeg->masterScore();
1676 :
1677 0 : Measure* firstMeasure = firstSeg->measure();
1678 0 : Measure* secondMeasure = secondSeg->measure();
1679 :
1680 0 : const Measure* firstMasterMeasure = master->tick2measure(firstMeasure->tick());
1681 0 : const Measure* secondMasterMeasure = master->tick2measure(secondMeasure->tick());
1682 0 : const Measure* adjacentMasterMeasure = firstMasterMeasure->nextMeasure();
1683 :
1684 0 : Score* score = firstSeg->score();
1685 :
1686 0 : const RepeatList& repeatList = score->repeatList(true, false);
1687 :
1688 0 : std::vector<const Measure*> measures;
1689 :
1690 0 : bool segEndsWithBl = false;
1691 0 : bool adjacentAndSecondShareSegment = false;
1692 :
1693 0 : for (auto it = repeatList.begin(); it != repeatList.end(); it++) {
1694 0 : const RepeatSegment* rs = *it;
1695 :
1696 0 : if (rs->endsWithMeasure(firstMasterMeasure)) {
1697 0 : segEndsWithBl = true;
1698 : }
1699 :
1700 0 : if (rs->startsWithMeasure(adjacentMasterMeasure) && rs->containsMeasure(secondMasterMeasure)) {
1701 0 : adjacentAndSecondShareSegment = true;
1702 : }
1703 : }
1704 :
1705 0 : return segEndsWithBl && adjacentAndSecondShareSegment;
1706 0 : }
1707 :
1708 47 : PartialLyricsLine* findPrevPartialLyricsLineDash(Lyrics* lyrics)
1709 : {
1710 47 : Score* score = lyrics->score();
1711 52 : for (auto sp : score->spannerMap().findOverlapping(lyrics->tick().ticks(), lyrics->tick().ticks())) {
1712 7 : if (!sp.value->isPartialLyricsLine() || sp.value->track() != lyrics->track()) {
1713 4 : continue;
1714 : }
1715 3 : PartialLyricsLine* partialLine = toPartialLyricsLine(sp.value);
1716 3 : if (partialLine->isEndMelisma() || partialLine->verse() != lyrics->verse() || partialLine->placement() != lyrics->placement()) {
1717 1 : continue;
1718 : }
1719 2 : return partialLine;
1720 : }
1721 :
1722 45 : return nullptr;
1723 : }
1724 :
1725 5 : MeasureBeat findBeat(const Score* score, int tick)
1726 : {
1727 5 : MeasureBeat measureBeat;
1728 5 : if (!score || !score->checkHasMeasures()) {
1729 0 : return measureBeat;
1730 : }
1731 :
1732 5 : int ticks = 0;
1733 5 : int beatIndex = 0;
1734 5 : score->sigmap()->tickValues(tick, &measureBeat.measureIndex, &beatIndex, &ticks);
1735 :
1736 5 : const TimeSigFrac timeSig = score->sigmap()->timesig(Fraction::fromTicks(tick)).timesig();
1737 5 : const int ticksB = ticks_beat(timeSig.denominator());
1738 :
1739 5 : measureBeat.beat = beatIndex + ticks / static_cast<float>(ticksB);
1740 5 : measureBeat.maxMeasureIndex = const_cast<Score*>(score)->measures()->size() - 1;
1741 5 : measureBeat.maxBeatIndex = timeSig.numerator() - 1;
1742 :
1743 5 : return measureBeat;
1744 : }
1745 :
1746 0 : bool isElementInFretBox(const EngravingItem* item)
1747 : {
1748 0 : if (item->isHarmony()) {
1749 0 : return toHarmony(item)->isInFretBox();
1750 0 : } else if (item->isFretDiagram()) {
1751 0 : return toFretDiagram(item)->isInFretBox();
1752 : }
1753 0 : return false;
1754 : }
1755 :
1756 16 : std::vector<EngravingItem*> filterTargetElements(const Selection& sel, EngravingItem* dropElement, bool& unique)
1757 : {
1758 16 : bool uniqueMeasures = false;
1759 16 : bool uniqueStaves = false;
1760 :
1761 16 : switch (dropElement->type()) {
1762 : // Brackets have a special logic for range selections.
1763 : // For other selections add only one to each staff:
1764 0 : case ElementType::BRACKET:
1765 0 : if (sel.isRange()) {
1766 0 : unique = true;
1767 0 : return { sel.startSegment()->firstElementForNavigation(sel.staffStart()) };
1768 : }
1769 0 : uniqueStaves = true;
1770 0 : break;
1771 :
1772 : // Barlines are only added to measures in range selections:
1773 0 : case ElementType::BAR_LINE:
1774 0 : uniqueMeasures = sel.isRange();
1775 0 : break;
1776 :
1777 : // For multi-measure measure repeats, avoid overlap by adding only one per staff:
1778 0 : case ElementType::MEASURE_REPEAT:
1779 0 : uniqueStaves = true;
1780 0 : uniqueMeasures = toMeasureRepeat(dropElement)->numMeasures() == 1;
1781 0 : break;
1782 :
1783 : // Add these elements once per measure in list selections, else once total:
1784 0 : case ElementType::MARKER:
1785 : case ElementType::JUMP:
1786 : case ElementType::SPACER: //! HACK - these should be applied "per staff" in range selections (see issue #33486)
1787 : case ElementType::STAFFTYPE_CHANGE: //! HACK - these should be applied "per staff" in range selections (see issue #33486)
1788 : case ElementType::VBOX:
1789 : case ElementType::HBOX:
1790 : case ElementType::TBOX:
1791 : case ElementType::FBOX:
1792 : case ElementType::MEASURE:
1793 0 : if (sel.isRange()) {
1794 0 : unique = true;
1795 0 : return { sel.startSegment()->firstElementForNavigation(sel.staffStart()) };
1796 : }
1797 0 : uniqueMeasures = true;
1798 0 : break;
1799 :
1800 : // Add these elements once per measure, always:
1801 0 : case ElementType::MEASURE_NUMBER:
1802 0 : uniqueMeasures = true;
1803 0 : break;
1804 :
1805 0 : case ElementType::ACTION_ICON: {
1806 0 : const ActionIconType actionType = toActionIcon(dropElement)->actionType();
1807 0 : switch (actionType) {
1808 0 : case ActionIconType::STAFF_TYPE_CHANGE: //! HACK - these should be applied "per staff" in range selections (see issue #33486)
1809 : case ActionIconType::VFRAME:
1810 : case ActionIconType::HFRAME:
1811 : case ActionIconType::TFRAME:
1812 : case ActionIconType::FFRAME:
1813 : case ActionIconType::MEASURE:
1814 0 : if (sel.isRange()) {
1815 0 : unique = true;
1816 0 : return { sel.startSegment()->firstElementForNavigation(sel.staffStart()) };
1817 : }
1818 0 : break;
1819 0 : default: break;
1820 : }
1821 0 : break;
1822 : }
1823 16 : default: break;
1824 : }
1825 :
1826 16 : unique = uniqueStaves || uniqueMeasures;
1827 16 : if (!unique) {
1828 16 : return sel.elements();
1829 : }
1830 :
1831 0 : std::vector<EngravingItem*> result;
1832 0 : if (uniqueStaves && uniqueMeasures) {
1833 0 : std::vector<MStaff*> foundMStaves;
1834 0 : for (EngravingItem* e : sel.elements()) {
1835 0 : if (Measure* m = e->findMeasure()) {
1836 0 : if (!muse::contains(foundMStaves, m->mstaves().at(e->staffIdx()))) {
1837 0 : result.emplace_back(e);
1838 0 : foundMStaves.emplace_back(m->mstaves().at(e->staffIdx()));
1839 : }
1840 : }
1841 : }
1842 0 : } else if (uniqueStaves) {
1843 0 : std::vector<staff_idx_t> foundStaves;
1844 0 : for (EngravingItem* e : sel.elements()) {
1845 0 : if (!muse::contains(foundStaves, e->staffIdx())) {
1846 0 : result.emplace_back(e);
1847 0 : foundStaves.emplace_back(e->staffIdx());
1848 : }
1849 : }
1850 0 : } else {
1851 0 : std::vector<MeasureBase*> foundMeasures;
1852 0 : for (EngravingItem* e : sel.elements()) {
1853 0 : if (MeasureBase* mb = e->findMeasureBase()) {
1854 0 : if (!muse::contains(foundMeasures, mb)) {
1855 0 : result.emplace_back(e);
1856 0 : foundMeasures.emplace_back(mb);
1857 : }
1858 : }
1859 : }
1860 0 : }
1861 0 : return result;
1862 : }
1863 :
1864 228 : Lyrics* searchNextLyrics(Segment* s, staff_idx_t staffIdx, int verse, PlacementV p)
1865 : {
1866 228 : Lyrics* l = nullptr;
1867 228 : const Segment* originalSeg = s;
1868 411 : while ((s = s->next1(SegmentType::ChordRest))) {
1869 406 : if (!segmentsAreAdjacent(originalSeg, s)) {
1870 0 : return nullptr;
1871 : }
1872 :
1873 406 : track_idx_t strack = staffIdx * VOICES;
1874 406 : track_idx_t etrack = strack + VOICES;
1875 : // search through all tracks of current staff looking for a lyric in specified verse
1876 1142 : for (track_idx_t track = strack; track < etrack; ++track) {
1877 959 : ChordRest* cr = toChordRest(s->element(track));
1878 959 : if (cr) {
1879 : // cr with lyrics found, but does it have a syllable in specified verse?
1880 337 : l = cr->lyrics(verse, p);
1881 337 : if (l) {
1882 223 : break;
1883 : }
1884 : }
1885 : }
1886 406 : if (l) {
1887 223 : break;
1888 : }
1889 : }
1890 228 : return l;
1891 : }
1892 :
1893 19393 : bool noteIsBefore(const Note* n1, const Note* n2)
1894 : {
1895 19393 : const int l1 = n1->line();
1896 19393 : const int l2 = n2->line();
1897 19393 : if (l1 != l2) {
1898 17862 : return l1 > l2;
1899 : }
1900 :
1901 1531 : const int p1 = n1->pitch();
1902 1531 : const int p2 = n2->pitch();
1903 1531 : if (p1 != p2) {
1904 1318 : return p1 < p2;
1905 : }
1906 :
1907 213 : if (n1->tieBack()) {
1908 0 : if (n2->tieBack() && !n2->incomingPartialTie()) {
1909 0 : const Note* sn1 = n1->tieBack()->startNote();
1910 0 : const Note* sn2 = n2->tieBack()->startNote();
1911 0 : if (sn1->chord() == sn2->chord()) {
1912 0 : return sn1->unisonIndex() < sn2->unisonIndex();
1913 : }
1914 0 : return sn1->chord()->isBefore(sn2->chord());
1915 : } else {
1916 0 : return true; // place tied notes before
1917 : }
1918 : }
1919 :
1920 213 : return false;
1921 : }
1922 :
1923 1128 : void updatePercussionNotes(Chord* c, const Drumset* drumset)
1924 : {
1925 1133 : TRACEFUNC;
1926 1140 : for (Chord* ch : c->graceNotes()) {
1927 12 : updatePercussionNotes(ch, drumset);
1928 : }
1929 1128 : std::vector<Note*> lnotes(c->notes()); // we need a copy!
1930 2305 : for (Note* note : lnotes) {
1931 1177 : if (!drumset) {
1932 0 : note->setLine(0);
1933 : } else {
1934 1177 : int pitch = note->pitch();
1935 1177 : if (!drumset->isValid(pitch)) {
1936 76 : note->setLine(0);
1937 : //! NOTE May be called too often
1938 : //LOGW("unmapped drum note %d", pitch);
1939 1101 : } else if (!note->fixed()) {
1940 1078 : note->undoChangeProperty(Pid::HEAD_GROUP, drumset->noteHead(pitch));
1941 1078 : note->setLine(drumset->line(pitch));
1942 : }
1943 : }
1944 : }
1945 1128 : }
1946 : }
|