LCOV - code coverage report
Current view: top level - src/engraving/dom - utils.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 60.7 % 918 557
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1