LCOV - code coverage report
Current view: top level - src/engraving/dom - chordrest.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 46.4 % 696 323
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 "chordrest.h"
      24              : 
      25              : #include "translation.h"
      26              : 
      27              : #include "types/typesconv.h"
      28              : 
      29              : #include "actionicon.h"
      30              : #include "barline.h"
      31              : #include "beam.h"
      32              : #include "breath.h"
      33              : #include "chord.h"
      34              : #include "clef.h"
      35              : #include "factory.h"
      36              : #include "figuredbass.h"
      37              : #include "harmony.h"
      38              : #include "harppedaldiagram.h"
      39              : #include "hook.h"
      40              : #include "instrchange.h"
      41              : #include "keysig.h"
      42              : #include "lyrics.h"
      43              : #include "marker.h"
      44              : #include "measure.h"
      45              : #include "note.h"
      46              : #include "page.h"
      47              : #include "part.h"
      48              : #include "rehearsalmark.h"
      49              : #include "score.h"
      50              : #include "segment.h"
      51              : #include "slur.h"
      52              : #include "staff.h"
      53              : #include "stafftype.h"
      54              : #include "system.h"
      55              : #include "tabdurationsymbol.h"
      56              : #include "tuplet.h"
      57              : #include "utils.h"
      58              : #include "volta.h"
      59              : 
      60              : #include "editing/editclef.h"
      61              : #include "editing/editkeysig.h"
      62              : #include "editing/editrehearsalmark.h"
      63              : #include "editing/navigation.h"
      64              : #include "editing/splitjoinmeasure.h"
      65              : #include "editing/transaction/transaction.h"
      66              : #include "editing/transpose.h"
      67              : 
      68              : #include "rendering/iscorerenderer.h"
      69              : 
      70              : #include "log.h"
      71              : 
      72              : using namespace mu;
      73              : using namespace mu::engraving;
      74              : 
      75              : namespace mu::engraving {
      76              : //---------------------------------------------------------
      77              : //   ChordRest
      78              : //---------------------------------------------------------
      79              : 
      80        52096 : ChordRest::ChordRest(const ElementType& type, DummyParentOr<Segment> parent)
      81        52096 :     : DurationElement(type, parent)
      82              : {
      83        52096 :     m_staffMove    = 0;
      84        52096 :     m_beam         = 0;
      85        52096 :     m_tabDur       = 0;
      86        52096 :     m_beamMode     = BeamMode::AUTO;
      87        52096 :     m_isSmall     = false;
      88        52096 :     m_melismaEnd   = false;
      89        52096 :     m_crossMeasure = CrossMeasure::UNKNOWN;
      90        52096 : }
      91              : 
      92         5029 : ChordRest::ChordRest(const ChordRest& cr, bool link)
      93         5029 :     : DurationElement(cr, link)
      94              : {
      95         5029 :     m_durationType = cr.m_durationType;
      96         5029 :     m_staffMove    = cr.m_staffMove;
      97         5029 :     m_beam         = 0;
      98         5029 :     m_tabDur       = 0;    // tab sur. symb. depends upon context: can't be
      99              :                            // simply copied from another CR
     100              : 
     101         5029 :     m_beamMode     = cr.m_beamMode;
     102         5029 :     m_isSmall     = cr.m_isSmall;
     103         5029 :     m_melismaEnd   = cr.m_melismaEnd;
     104         5029 :     m_crossMeasure = cr.m_crossMeasure;
     105              : 
     106         5031 :     for (Lyrics* l : cr.m_lyrics) {          // make deep copy
     107            2 :         Lyrics* nl = Factory::copyLyrics(*l);
     108            2 :         if (link) {
     109            0 :             nl->linkTo(l);
     110              :         }
     111            2 :         nl->setOwnershipParent(this);
     112            2 :         nl->setTrack(track());
     113            2 :         m_lyrics.push_back(nl);
     114              :     }
     115         5029 : }
     116              : 
     117              : //---------------------------------------------------------
     118              : //   undoUnlink
     119              : //---------------------------------------------------------
     120              : 
     121          445 : void ChordRest::undoUnlink()
     122              : {
     123          445 :     DurationElement::undoUnlink();
     124          451 :     for (Lyrics* l : m_lyrics) {
     125            6 :         l->undoUnlink();
     126              :     }
     127          445 : }
     128              : 
     129              : //---------------------------------------------------------
     130              : //   ChordRest
     131              : //---------------------------------------------------------
     132              : 
     133        51060 : ChordRest::~ChordRest()
     134              : {
     135        51060 :     muse::DeleteAll(m_lyrics);
     136        51060 :     muse::DeleteAll(m_el);
     137        51060 :     delete m_tabDur;
     138              : 
     139        51060 :     if (m_beam) {
     140         6869 :         muse::remove(m_beam->elements(), this);
     141         6869 :         m_beam = nullptr;
     142              :     }
     143              : 
     144        51060 :     if (m_beamlet) {
     145           95 :         m_beamlet = nullptr;
     146              :     }
     147        51060 : }
     148              : 
     149              : //---------------------------------------------------------
     150              : //   drop
     151              : //---------------------------------------------------------
     152              : 
     153            9 : bool ChordRest::acceptDrop(EditData& data) const
     154              : {
     155            9 :     EngravingItem* e = data.dropElement;
     156              : 
     157            9 :     switch (e->type()) {
     158            9 :     case ElementType::FERMATA:
     159              :     case ElementType::CLEF:
     160              :     case ElementType::KEYSIG:
     161              :     case ElementType::TIMESIG:
     162              :     case ElementType::SYSTEM_TEXT:
     163              :     case ElementType::TRIPLET_FEEL:
     164              :     case ElementType::STAFF_TEXT:
     165              :     case ElementType::PLAYTECH_ANNOTATION:
     166              :     case ElementType::CAPO:
     167              :     case ElementType::BAR_LINE:
     168              :     case ElementType::BREATH:
     169              :     case ElementType::STAFF_STATE:
     170              :     case ElementType::INSTRUMENT_CHANGE:
     171              :     case ElementType::DYNAMIC:
     172              :     case ElementType::EXPRESSION:
     173              :     case ElementType::HARMONY:
     174              :     case ElementType::TEMPO_TEXT:
     175              :     case ElementType::REHEARSAL_MARK:
     176              :     case ElementType::FRET_DIAGRAM:
     177              :     case ElementType::TREMOLOBAR:
     178              :     case ElementType::HARP_DIAGRAM:
     179            9 :         return true;
     180            0 :     case ElementType::ACTION_ICON: {
     181            0 :         switch (toActionIcon(e)->actionType()) {
     182            0 :         case ActionIconType::BEAM_AUTO:
     183              :         case ActionIconType::BEAM_NONE:
     184              :         case ActionIconType::BEAM_BREAK_LEFT:
     185              :         case ActionIconType::BEAM_BREAK_INNER_8TH:
     186              :         case ActionIconType::BEAM_BREAK_INNER_16TH:
     187              :         case ActionIconType::BEAM_JOIN:
     188            0 :             return true;
     189            0 :         default: break;
     190              :         }
     191            0 :         break;
     192              :     }
     193            0 :     default: break;
     194              :     }
     195            0 :     return measure()->acceptDrop(data);
     196              : }
     197              : 
     198           30 : EngravingItem* ChordRest::drop(Transaction& tx, EditData& data)
     199              : {
     200           30 :     EngravingItem* e = data.dropElement;
     201           30 :     Measure* m       = measure();
     202           30 :     bool fromPalette = (e->track() == muse::nidx);
     203           30 :     switch (e->type()) {
     204           13 :     case ElementType::BREATH:
     205              :     {
     206           13 :         Breath* b = toBreath(e);
     207           13 :         b->setPos(PointF());
     208              :         // allow breath marks in voice > 1
     209           13 :         b->setTrack(track());
     210           13 :         b->setPlacement(b->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
     211              : 
     212              :         // TODO: insert automatically in all staves?
     213              : 
     214           13 :         Segment* seg = m->undoGetSegment(SegmentType::Breath, endTick());
     215              : 
     216           13 :         if (seg->element(b->track())) {
     217              :             // Already a breath present, change its symbol
     218            0 :             Breath* existingBreath = toBreath(seg->element(b->track()));
     219            0 :             existingBreath->undoChangeProperty(Pid::SYMBOL, b->symId());
     220            0 :             delete b;
     221            0 :             return existingBreath;
     222              :         } else {
     223           13 :             b->setOwnershipParent(seg);
     224           13 :             score()->undoAddElement(b);
     225           13 :             return e;
     226              :         }
     227              :     }
     228              : 
     229            0 :     case ElementType::BAR_LINE:
     230              :     {
     231            0 :         BarLine* bl = toBarLine(e);
     232            0 :         Fraction barLineTick = bl->barLineType() == BarLineType::START_REPEAT ? tick() : endTick();
     233              : 
     234            0 :         if (data.control()) {
     235            0 :             SplitJoinMeasure::splitMeasure(tx, masterScore(), barLineTick);
     236            0 :             m = score()->tick2measure(tick());
     237              :             // consume the ControlModifier flag
     238            0 :             data.modifiers &= ~ControlModifier;
     239              :         }
     240              : 
     241            0 :         if (barLineTick == m->tick() || barLineTick == m->endTick()) {
     242            0 :             return m->drop(tx, data);
     243              :         }
     244              : 
     245            0 :         bl->setPos(PointF());
     246            0 :         bl->setTrack(staffIdx() * VOICES);
     247            0 :         bl->setGenerated(false);
     248              : 
     249            0 :         BarLine* obl = nullptr;
     250            0 :         for (Staff* st : staff()->staffList()) {
     251            0 :             Score* score = st->score();
     252            0 :             Measure* measure = score->tick2measure(m->tick());
     253            0 :             Segment* seg = measure->undoGetSegment(SegmentType::BarLine, barLineTick);
     254              :             BarLine* l;
     255            0 :             if (!obl) {
     256            0 :                 obl = l = bl->clone();
     257              :             } else {
     258            0 :                 l = toBarLine(obl->linkedClone());
     259              :             }
     260            0 :             l->setTrack(st->idx() * VOICES);
     261            0 :             l->setOwnershipParent(seg);
     262            0 :             score->undoAddElement(l);
     263            0 :         }
     264              : 
     265            0 :         delete e;
     266            0 :         return nullptr;
     267              :     }
     268              : 
     269            0 :     case ElementType::CLEF:
     270            0 :         EditClef::insertClef(tx, score(), toClef(e), this);
     271            0 :         return nullptr;
     272              : 
     273            0 :     case ElementType::FERMATA:
     274            0 :         e->setPlacement(track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
     275            0 :         for (EngravingItem* el: segment()->annotations()) {
     276            0 :             if (el->isFermata() && (el->track() == track())) {
     277            0 :                 if (el->subtype() == e->subtype()) {
     278            0 :                     delete e;
     279            0 :                     return el;
     280              :                 } else {
     281            0 :                     e->setPlacement(el->placement());
     282            0 :                     e->setTrack(track());
     283            0 :                     e->setOwnershipParent(segment());
     284            0 :                     score()->undoChangeElement(el, e);
     285            0 :                     return e;
     286              :                 }
     287              :             }
     288              :         }
     289              :         [[fallthrough]];
     290              :     case ElementType::TEMPO_TEXT:
     291              :     case ElementType::EXPRESSION:
     292              :     case ElementType::FRET_DIAGRAM:
     293              :     case ElementType::TREMOLOBAR:
     294              :     case ElementType::SYMBOL:
     295              :     case ElementType::IMAGE:
     296            0 :         e->setTrack(track());
     297            0 :         e->setOwnershipParent(segment());
     298            0 :         score()->undoAddElement(e);
     299            0 :         return e;
     300              : 
     301            5 :     case ElementType::DYNAMIC:
     302            5 :         e->setTrack(track());
     303            5 :         e->checkVoiceAssignmentCompatibleWithTrack();
     304            5 :         e->setOwnershipParent(segment());
     305            5 :         score()->undoAddElement(e);
     306            5 :         return e;
     307              : 
     308            1 :     case ElementType::NOTE: {
     309            1 :         Note* note = toNote(e);
     310              :         // calculate correct transposed tpc
     311            1 :         Interval v = staff()->transpose(tick());
     312            1 :         v.flip();
     313            1 :         note->setTpc2(Transpose::transposeTpc(note->tpc1(), v, true));
     314              : 
     315            1 :         Segment* seg = segment();
     316            1 :         score()->undoRemoveElement(this);
     317            1 :         Chord* chord = Factory::createChord(score()->dummy());
     318            1 :         chord->setTrack(track());
     319            1 :         chord->setDurationType(durationType());
     320            1 :         chord->setTicks(ticks());
     321            1 :         chord->setTuplet(tuplet());
     322            1 :         chord->add(note);
     323            1 :         score()->undoAddCR(chord, seg->measure(), seg->tick());
     324            1 :         return note;
     325              :     }
     326              : 
     327            0 :     case ElementType::HARMONY:
     328              :     {
     329              :         // transpose
     330            0 :         Harmony* harmony = toHarmony(e);
     331            0 :         Interval interval = staff()->transpose(tick());
     332            0 :         if (!style().styleB(Sid::concertPitch) && !interval.isZero()) {
     333            0 :             interval.flip();
     334            0 :             Transpose::undoTransposeHarmony(tx, harmony, interval);
     335              :         }
     336              :     }
     337              :         [[fallthrough]];
     338           10 :     case ElementType::TEXT:
     339              :     case ElementType::STAFF_TEXT:
     340              :     case ElementType::SYSTEM_TEXT:
     341              :     case ElementType::TRIPLET_FEEL:
     342              :     case ElementType::PLAYTECH_ANNOTATION:
     343              :     case ElementType::CAPO:
     344              :     case ElementType::STICKING:
     345              :     case ElementType::STAFF_STATE:
     346              :     case ElementType::HARP_DIAGRAM:
     347              :     case ElementType::REHEARSAL_MARK:
     348              :     {
     349           10 :         e->setOwnershipParent(segment());
     350           10 :         e->setTrack(trackZeroVoice(track()));
     351           10 :         if (e->isRehearsalMark() && fromPalette) {
     352            0 :             RehearsalMark* r = toRehearsalMark(e);
     353            0 :             r->setXmlText(EditRehearsalMark::createRehearsalMarkText(score(), r));
     354           10 :         } else if (e->isHarpPedalDiagram() && fromPalette && originPart()) {
     355              :             // Match pedal config with previous diagram's
     356            5 :             if (HarpPedalDiagram* prevDiagram = originPart()->prevHarpDiagram(segment()->tick())) {
     357            4 :                 toHarpPedalDiagram(e)->setPedalState(prevDiagram->getPedalState());
     358              :             }
     359              :         }
     360           10 :         score()->undoAddElement(e);
     361           10 :         return e;
     362              :     }
     363            1 :     case ElementType::INSTRUMENT_CHANGE:
     364            1 :         if (part()->instruments().find(tick().ticks()) != part()->instruments().end()) {
     365            0 :             LOGD() << "InstrumentChange already exists at tick = " << tick().ticks();
     366            0 :             delete e;
     367            0 :             return nullptr;
     368              :         } else {
     369            1 :             InstrumentChange* ic = toInstrumentChange(e);
     370            1 :             ic->setOwnershipParent(segment());
     371            1 :             ic->setTrack(trackZeroVoice(track()));
     372              : 
     373            1 :             const Instrument* prevInstr = part()->instrument(tick());
     374            1 :             const Instrument instr = *ic->instrument();
     375              : 
     376            1 :             IF_ASSERT_FAILED(prevInstr) {
     377            0 :                 delete e;
     378            0 :                 return nullptr;
     379              :             }
     380              : 
     381              :             // temporarily set previous instrument, for correct transposition calculation
     382            1 :             ic->setInstrument(*prevInstr);
     383            1 :             score()->undoAddElement(ic);
     384              : 
     385            1 :             if (!fromPalette) {
     386            1 :                 ic->setupInstrument(&instr, /*updateText*/ false);
     387              :             }
     388            1 :             return e;
     389            1 :         }
     390            0 :     case ElementType::FIGURED_BASS:
     391              :     {
     392              :         bool bNew;
     393            0 :         FiguredBass* fb = toFiguredBass(e);
     394            0 :         fb->setOwnershipParent(segment());
     395            0 :         fb->setTrack(trackZeroVoice(track()));
     396            0 :         fb->setTicks(ticks());
     397            0 :         fb->setOnNote(true);
     398            0 :         FiguredBass::addFiguredBassToSegment(segment(), fb->track(), fb->ticks(), &bNew);
     399            0 :         if (bNew) {
     400            0 :             score()->undoAddElement(e);
     401              :         }
     402            0 :         return e;
     403              :     }
     404              : 
     405            0 :     case ElementType::ACTION_ICON:
     406              :     {
     407            0 :         ActionIconType actionType = toActionIcon(e)->actionType();
     408              :         static const std::unordered_map<ActionIconType, BeamMode> beamModeTable = {
     409              :             { ActionIconType::BEAM_AUTO,             BeamMode::AUTO },
     410              :             { ActionIconType::BEAM_NONE,             BeamMode::NONE },
     411              :             { ActionIconType::BEAM_BREAK_LEFT,       BeamMode::BEGIN },
     412              :             { ActionIconType::BEAM_BREAK_INNER_8TH,  BeamMode::BEGIN16 },
     413              :             { ActionIconType::BEAM_BREAK_INNER_16TH, BeamMode::BEGIN32 },
     414              :             { ActionIconType::BEAM_JOIN,             BeamMode::MID },
     415            0 :         };
     416            0 :         if (muse::contains(beamModeTable, actionType)) {
     417            0 :             undoChangeProperty(Pid::BEAM_MODE, muse::value(beamModeTable, actionType));
     418            0 :             delete e;
     419            0 :             return nullptr;
     420              :         }
     421            0 :         break;
     422              :     }
     423              : 
     424            0 :     case ElementType::KEYSIG:
     425              :     {
     426            0 :         if (data.modifiers & ControlModifier) {
     427              :             // apply only to this stave
     428            0 :             KeySigEvent k = toKeySig(e)->keySigEvent();
     429            0 :             EditKeySig::undoChangeKeySig(tx, score(), staff(), tick(), k);
     430            0 :             delete e;
     431            0 :             return nullptr;
     432            0 :         }
     433            0 :         break;
     434              :     }
     435              : 
     436            0 :     default:
     437            0 :         if (e->isSpanner()) {
     438            0 :             Spanner* spanner = toSpanner(e);
     439            0 :             spanner->setTick(tick());
     440            0 :             if (spanner->systemFlag()) {
     441            0 :                 spanner->setTrack(0);
     442            0 :                 spanner->setTrack2(0);
     443              :             } else {
     444            0 :                 spanner->setTrack(track());
     445            0 :                 spanner->setTrack2(track());
     446              :             }
     447            0 :             score()->undoAddElement(spanner);
     448            0 :             return e;
     449              :         }
     450            0 :         break;
     451              :     }
     452            0 :     return m->drop(tx, data);
     453              : }
     454              : 
     455              : //---------------------------------------------------------
     456              : //   beam
     457              : //---------------------------------------------------------
     458              : 
     459      2604771 : Beam* ChordRest::beam() const
     460              : {
     461      2604771 :     return !(measure() && measure()->stemless(staffIdx())) ? m_beam : nullptr;
     462              : }
     463              : 
     464              : //---------------------------------------------------------
     465              : //   setBeam
     466              : //---------------------------------------------------------
     467              : 
     468        11178 : void ChordRest::setBeam(Beam* b)
     469              : {
     470        11178 :     m_beam = b;
     471        11178 : }
     472              : 
     473       196164 : void ChordRest::setBeamlet(BeamSegment* b)
     474              : {
     475       196164 :     m_beamlet = b;
     476       196164 : }
     477              : 
     478              : //---------------------------------------------------------
     479              : //   setDurationType
     480              : //---------------------------------------------------------
     481              : 
     482        45662 : void ChordRest::setDurationType(DurationType t)
     483              : {
     484        45662 :     m_durationType.setType(t);
     485        45662 :     m_crossMeasure = CrossMeasure::UNKNOWN;
     486        45662 : }
     487              : 
     488           41 : void ChordRest::setDurationType(const Fraction& ticks)
     489              : {
     490           41 :     m_durationType.setVal(ticks.ticks());
     491           41 :     m_crossMeasure = CrossMeasure::UNKNOWN;
     492           41 : }
     493              : 
     494        26663 : void ChordRest::setDurationType(TDuration v)
     495              : {
     496        26663 :     m_durationType = v;
     497        26663 :     m_crossMeasure = CrossMeasure::UNKNOWN;
     498        26663 : }
     499              : 
     500              : //---------------------------------------------------------
     501              : //   durationUserName
     502              : //---------------------------------------------------------
     503              : 
     504            0 : String ChordRest::durationUserName() const
     505              : {
     506            0 :     String tupletType;
     507            0 :     if (tuplet()) {
     508            0 :         switch (tuplet()->ratio().numerator()) {
     509            0 :         case 2:
     510            0 :             tupletType = muse::mtrc("engraving", "Duplet");
     511            0 :             break;
     512            0 :         case 3:
     513            0 :             tupletType = muse::mtrc("engraving", "Triplet");
     514            0 :             break;
     515            0 :         case 4:
     516            0 :             tupletType = muse::mtrc("engraving", "Quadruplet");
     517            0 :             break;
     518            0 :         case 5:
     519            0 :             tupletType = muse::mtrc("engraving", "Quintuplet");
     520            0 :             break;
     521            0 :         case 6:
     522            0 :             tupletType = muse::mtrc("engraving", "Sextuplet");
     523            0 :             break;
     524            0 :         case 7:
     525            0 :             tupletType = muse::mtrc("engraving", "Septuplet");
     526            0 :             break;
     527            0 :         case 8:
     528            0 :             tupletType = muse::mtrc("engraving", "Octuplet");
     529            0 :             break;
     530            0 :         case 9:
     531            0 :             tupletType = muse::mtrc("engraving", "Nonuplet");
     532            0 :             break;
     533            0 :         default:
     534              :             //: %1 is tuplet ratio numerator (i.e. the number of notes in the tuplet)
     535            0 :             tupletType = muse::mtrc("engraving", "%1 note tuplet").arg(tuplet()->ratio().numerator());
     536              :         }
     537              :     }
     538            0 :     String dotString;
     539            0 :     if (!tupletType.isEmpty()) {
     540            0 :         dotString += u' ';
     541              :     }
     542              : 
     543            0 :     switch (dots()) {
     544            0 :     case 1:
     545            0 :         dotString += muse::mtrc("engraving", "Dotted %1").arg(TConv::translatedUserName(durationType().type()));
     546            0 :         break;
     547            0 :     case 2:
     548            0 :         dotString += muse::mtrc("engraving", "Double dotted %1").arg(TConv::translatedUserName(durationType().type()));
     549            0 :         break;
     550            0 :     case 3:
     551            0 :         dotString += muse::mtrc("engraving", "Triple dotted %1").arg(TConv::translatedUserName(durationType().type()));
     552            0 :         break;
     553            0 :     case 4:
     554            0 :         dotString += muse::mtrc("engraving", "Quadruple dotted %1").arg(TConv::translatedUserName(durationType().type()));
     555            0 :         break;
     556            0 :     default:
     557            0 :         dotString += TConv::translatedUserName(durationType().type());
     558              :     }
     559            0 :     return String(u"%1%2").arg(tupletType, dotString);
     560            0 : }
     561              : 
     562              : //---------------------------------------------------------
     563              : //   add
     564              : //---------------------------------------------------------
     565              : 
     566         1789 : void ChordRest::add(EngravingItem* e)
     567              : {
     568         1789 :     e->setOwnershipParent(this);
     569         1789 :     e->setTrack(track());
     570         1789 :     switch (e->type()) {
     571            0 :     case ElementType::ARTICULATION:             // for backward compatibility
     572            0 :         LOGD("ChordRest::add: unknown element %s", e->typeName());
     573            0 :         break;
     574         1789 :     case ElementType::LYRICS:
     575         1789 :         m_lyrics.push_back(toLyrics(e));
     576         1789 :         e->added();
     577         1789 :         break;
     578            0 :     default:
     579            0 :         EngravingItem::add(e);
     580            0 :         break;
     581              :     }
     582         1789 : }
     583              : 
     584              : //---------------------------------------------------------
     585              : //   remove
     586              : //---------------------------------------------------------
     587              : 
     588           18 : void ChordRest::remove(EngravingItem* e)
     589              : {
     590           18 :     switch (e->type()) {
     591           18 :     case ElementType::LYRICS: {
     592           18 :         toLyrics(e)->removeFromScore();
     593           18 :         auto i = std::find(m_lyrics.begin(), m_lyrics.end(), toLyrics(e));
     594           18 :         if (i != m_lyrics.end()) {
     595           18 :             m_lyrics.erase(i);
     596           18 :             e->removed();
     597              :         } else {
     598            0 :             LOGD("ChordRest::remove: %s %p not found", e->typeName(), e);
     599              :         }
     600              :     }
     601           18 :     break;
     602            0 :     default:
     603            0 :         EngravingItem::remove(e);
     604              :     }
     605           18 : }
     606              : 
     607              : //---------------------------------------------------------
     608              : //   removeDeleteBeam
     609              : ///   Remove ChordRest from beam, delete beam if empty.
     610              : ///   \param beamed - if the chordrest is beamed (will get
     611              : ///                   a (new) beam)
     612              : //---------------------------------------------------------
     613              : 
     614        79177 : void ChordRest::removeDeleteBeam(bool beamed)
     615              : {
     616        79177 :     setBeamlet(nullptr);
     617        79177 :     if (m_beam) {
     618          184 :         Beam* b = m_beam;
     619          184 :         m_beam->remove(this);
     620          184 :         if (b->empty()) {
     621           75 :             score()->doUndoRemoveElement(b);
     622              :         } else {
     623          109 :             renderer()->layoutBeam1(b);
     624              :         }
     625              :     }
     626        79177 :     if (!beamed && isChord()) {
     627        34216 :         Chord* c = toChord(this);
     628        34216 :         if (c->shouldHaveHook()) {
     629         2360 :             if (!c->hook()) {
     630         1269 :                 c->createHook();
     631              :             }
     632        31856 :         } else if (c->hook()) {
     633           21 :             score()->doUndoRemoveElement(c->hook());
     634              :         }
     635        34216 :         renderer()->layoutStem(c);
     636              :     }
     637        79177 : }
     638              : 
     639            0 : void ChordRest::computeUp()
     640              : {
     641            0 :     UNREACHABLE;
     642              : }
     643              : 
     644              : //---------------------------------------------------------
     645              : //   replaceBeam
     646              : //---------------------------------------------------------
     647              : 
     648        11458 : void ChordRest::replaceBeam(Beam* newBeam)
     649              : {
     650        11458 :     if (m_beam == newBeam) {
     651         4870 :         return;
     652              :     }
     653         6588 :     removeDeleteBeam(true);
     654         6588 :     newBeam->add(this);
     655              : }
     656              : 
     657            0 : Slur* ChordRest::slur(const ChordRest* secondChordRest) const
     658              : {
     659            0 :     if (secondChordRest == nullptr) {
     660            0 :         ChordRestNavigateOptions options;
     661            0 :         options.disableOverRepeats = true;
     662            0 :         secondChordRest = Navigation::nextChordRest(const_cast<ChordRest*>(this), options);
     663              :     }
     664            0 :     int currentTick = tick().ticks();
     665            0 :     Slur* result = nullptr;
     666            0 :     for (auto it : score()->spannerMap().findOverlapping(currentTick, currentTick + 1)) {
     667            0 :         Spanner* spanner = it.value;
     668            0 :         if (!spanner->isSlur()) {
     669            0 :             continue;
     670              :         }
     671            0 :         Slur* slur = toSlur(spanner);
     672            0 :         if (slur->startElement() == this && slur->endElement() == secondChordRest) {
     673            0 :             if (slur->slurDirection() == DirectionV::AUTO) {
     674            0 :                 return slur;
     675              :             }
     676            0 :             result = slur;
     677              :         }
     678              :     }
     679            0 :     return result;
     680              : }
     681              : 
     682          718 : void ChordRest::undoChangeProperty(Pid id, const PropertyValue& newValue, PropertyFlags ps)
     683              : {
     684          718 :     if (id == Pid::BEAM_MODE) {
     685           46 :         if (isChord() && m_durationType.hooks() == 0) {
     686           43 :             const StaffType* tab = staffType();
     687           43 :             if (!tab || !tab->isTabStaff() || !tab->genDurations()) {
     688           37 :                 return;
     689              :             }
     690              :         }
     691            9 :         BeamMode newBeamMode = newValue.value<BeamMode>();
     692            1 :         if ((newBeamMode == BeamMode::BEGIN16 && m_durationType.hooks() < 2)
     693           10 :             || (newBeamMode == BeamMode::BEGIN32 && m_durationType.hooks() < 3)) {
     694            2 :             return;
     695              :         }
     696              :     }
     697              : 
     698          679 :     EngravingItem::undoChangeProperty(id, newValue, ps);
     699              : }
     700              : 
     701              : //---------------------------------------------------------
     702              : //   localSpatiumChanged
     703              : //---------------------------------------------------------
     704              : 
     705            0 : void ChordRest::localSpatiumChanged(double oldValue, double newValue)
     706              : {
     707            0 :     DurationElement::localSpatiumChanged(oldValue, newValue);
     708            0 :     for (EngravingItem* e : lyrics()) {
     709            0 :         e->localSpatiumChanged(oldValue, newValue);
     710              :     }
     711            0 :     for (EngravingItem* e : el()) {
     712            0 :         e->localSpatiumChanged(oldValue, newValue);
     713              :     }
     714            0 : }
     715              : 
     716              : //---------------------------------------------------------
     717              : //   getProperty
     718              : //---------------------------------------------------------
     719              : 
     720       407515 : PropertyValue ChordRest::getProperty(Pid propertyId) const
     721              : {
     722       407515 :     switch (propertyId) {
     723        33464 :     case Pid::SMALL:      return PropertyValue::fromValue(isSmall());
     724           35 :     case Pid::BEAM_MODE:  return beamMode();
     725        33352 :     case Pid::STAFF_MOVE: return staffMove();
     726          212 :     case Pid::DURATION_TYPE_WITH_DOTS: return actualDurationType().typeWithDots();
     727       340452 :     default:              return DurationElement::getProperty(propertyId);
     728              :     }
     729              : }
     730              : 
     731              : //---------------------------------------------------------
     732              : //   setProperty
     733              : //---------------------------------------------------------
     734              : 
     735          980 : bool ChordRest::setProperty(Pid propertyId, const PropertyValue& v)
     736              : {
     737          980 :     switch (propertyId) {
     738           56 :     case Pid::SMALL:
     739           56 :         setSmall(v.toBool());
     740           56 :         break;
     741           18 :     case Pid::BEAM_MODE:
     742           18 :         setBeamMode(v.value<BeamMode>());
     743           18 :         break;
     744            0 :     case Pid::STAFF_MOVE:
     745            0 :         setStaffMove(v.toInt());
     746            0 :         break;
     747            1 :     case Pid::VISIBLE:
     748            1 :         setVisible(v.toBool());
     749            1 :         measure()->checkMultiVoices(staffIdx());
     750            1 :         break;
     751           74 :     case Pid::DURATION_TYPE_WITH_DOTS:
     752           74 :         setDurationType(v.value<DurationTypeWithDots>());
     753           74 :         break;
     754          831 :     default:
     755          831 :         return DurationElement::setProperty(propertyId, v);
     756              :     }
     757          149 :     triggerLayout();
     758          149 :     return true;
     759              : }
     760              : 
     761              : //---------------------------------------------------------
     762              : //   propertyDefault
     763              : //---------------------------------------------------------
     764              : 
     765       367669 : PropertyValue ChordRest::propertyDefault(Pid propertyId) const
     766              : {
     767       367669 :     switch (propertyId) {
     768        33352 :     case Pid::SMALL:
     769        33352 :         return false;
     770            0 :     case Pid::BEAM_MODE:
     771            0 :         return BeamMode::AUTO;
     772        33352 :     case Pid::STAFF_MOVE:
     773        33352 :         return 0;
     774       300965 :     default:
     775       300965 :         return DurationElement::propertyDefault(propertyId);
     776              :     }
     777              :     // Prevent unreachable code warning
     778              :     // triggerLayout();
     779              : }
     780              : 
     781              : //---------------------------------------------------------
     782              : //   isGrace
     783              : //---------------------------------------------------------
     784              : 
     785       199975 : bool ChordRest::isGrace() const
     786              : {
     787       199975 :     return isChord() && toChord(this)->isGrace();
     788              : }
     789              : 
     790              : //---------------------------------------------------------
     791              : //   isGraceBefore
     792              : //---------------------------------------------------------
     793              : 
     794         7975 : bool ChordRest::isGraceBefore() const
     795              : {
     796         7975 :     return isChord()
     797         7975 :            && (toChord(this)->noteType() & (
     798              :                    NoteType::ACCIACCATURA | NoteType::APPOGGIATURA | NoteType::GRACE4 | NoteType::GRACE16 | NoteType::GRACE32
     799         7975 :                    ));
     800              : }
     801              : 
     802              : //---------------------------------------------------------
     803              : //   isGraceAfter
     804              : //---------------------------------------------------------
     805              : 
     806        21969 : bool ChordRest::isGraceAfter() const
     807              : {
     808        21969 :     return isChord()
     809        21969 :            && (toChord(this)->noteType() & (NoteType::GRACE8_AFTER | NoteType::GRACE16_AFTER | NoteType::GRACE32_AFTER));
     810              : }
     811              : 
     812              : //---------------------------------------------------------
     813              : //   hasBreathMark - determine if chordrest has breath-mark
     814              : //---------------------------------------------------------
     815         8231 : Breath* ChordRest::hasBreathMark() const
     816              : {
     817         8231 :     Segment* s = measure()->findSegment(SegmentType::Breath, endTick());
     818         8231 :     return s ? toBreath(s->element(track())) : nullptr;
     819              : }
     820              : 
     821              : //---------------------------------------------------------
     822              : //   nextSegmentAfterCR
     823              : //    returns first segment at tick CR->tick + CR->actualTicks
     824              : //    of given types
     825              : //---------------------------------------------------------
     826              : 
     827          248 : Segment* ChordRest::nextSegmentAfterCR(SegmentType types) const
     828              : {
     829          248 :     Fraction end = tick() + actualTicks();
     830          248 :     for (Segment* s = segment()->next1MM(types); s; s = s->next1MM(types)) {
     831          248 :         if (s->tick() >= end) {
     832          248 :             return s;
     833              :         }
     834              :     }
     835            0 :     return nullptr;
     836              : }
     837              : 
     838              : //---------------------------------------------------------
     839              : //   setTrack
     840              : //---------------------------------------------------------
     841              : 
     842        64344 : void ChordRest::setTrack(track_idx_t val)
     843              : {
     844        64344 :     EngravingItem::setTrack(val);
     845        65507 :     processSiblings([val](EngravingItem* e) { e->setTrack(val); });
     846        64344 : }
     847              : 
     848              : //---------------------------------------------------------
     849              : //   setScore
     850              : //---------------------------------------------------------
     851              : 
     852         7758 : void ChordRest::setScore(Score* s)
     853              : {
     854         7758 :     EngravingItem::setScore(s);
     855         8289 :     processSiblings([s](EngravingItem* e) { e->setScore(s); });
     856         7758 : }
     857              : 
     858              : //---------------------------------------------------------
     859              : //   processSiblings
     860              : //---------------------------------------------------------
     861              : 
     862        72102 : void ChordRest::processSiblings(std::function<void(EngravingItem*)> func)
     863              : {
     864        72102 :     if (m_beam) {
     865          399 :         func(m_beam);
     866              :     }
     867        72102 :     if (m_tabDur) {
     868            0 :         func(m_tabDur);
     869              :     }
     870        72109 :     for (Lyrics* l : m_lyrics) {
     871            7 :         func(l);
     872              :     }
     873        72102 :     if (tuplet()) {
     874         1278 :         func(tuplet());
     875              :     }
     876        72112 :     for (EngravingItem* e : m_el) {
     877           10 :         func(e);
     878              :     }
     879        72102 : }
     880              : 
     881              : //---------------------------------------------------------
     882              : //   nextArticulationOrLyric
     883              : //---------------------------------------------------------
     884              : 
     885            0 : EngravingItem* ChordRest::nextArticulationOrLyric(EngravingItem* e)
     886              : {
     887            0 :     if (e->isLyrics()) {
     888              :         // The next element after Lyrics is the LyricsLine (if it exists)...
     889            0 :         if (LyricsLine* line = toLyrics(e)->separator()) {
     890            0 :             return line;
     891              :         }
     892              :     }
     893            0 :     if (e->isLyricsLine()) {
     894            0 :         LyricsLine* lyricsLine = toLyricsLine(e);
     895            0 :         Lyrics* lyrics = lyricsLine->lyrics();
     896            0 :         IF_ASSERT_FAILED(lyrics) {
     897            0 :             return nullptr;
     898              :         }
     899              :         // We were on a LyricsLine - now we'll try to move to the next Lyrics...
     900            0 :         e = lyrics;
     901              :     }
     902              : 
     903            0 :     if (!isChord() || !e->isArticulationFamily()) {
     904              :         // Move to the next Lyrics...
     905            0 :         auto i = std::find(m_lyrics.begin(), m_lyrics.end(), e);
     906            0 :         if (i == m_lyrics.end() || i == m_lyrics.end() - 1) {
     907            0 :             return nullptr;
     908              :         }
     909            0 :         return *(i + 1);
     910              :     }
     911              : 
     912              :     // Cycle through articulations...
     913            0 :     Chord* c = toChord(this);
     914            0 :     auto i = std::find(c->articulations().begin(), c->articulations().end(), e);
     915            0 :     if (i == c->articulations().end()) {
     916            0 :         return nullptr;
     917              :     }
     918            0 :     if (i != c->articulations().end() - 1) {
     919            0 :         return *(i + 1);
     920              :     }
     921            0 :     if (!m_lyrics.empty()) {
     922            0 :         return m_lyrics[0];
     923              :     }
     924              : 
     925            0 :     return nullptr;
     926              : }
     927              : 
     928              : //---------------------------------------------------------
     929              : //   prevArticulationOrLyric
     930              : //---------------------------------------------------------
     931              : 
     932            0 : EngravingItem* ChordRest::prevArticulationOrLyric(EngravingItem* e)
     933              : {
     934            0 :     const std::vector<Articulation*> artics = isChord() ? toChord(this)->articulations() : std::vector<Articulation*>();
     935              : 
     936            0 :     if (e->isLyricsLine()) {
     937              :         // The previous element to a LyricsLine is the associated Lyrics...
     938            0 :         return toLyricsLine(e)->lyrics();
     939              :     }
     940              : 
     941            0 :     auto i = std::find(m_lyrics.begin(), m_lyrics.end(), e);
     942            0 :     if (i == m_lyrics.end()) {
     943              :         // Cycle through articulations...
     944            0 :         if (artics.empty()) {
     945            0 :             return nullptr;
     946              :         }
     947            0 :         auto j = std::find(artics.begin(), artics.end(), e);
     948            0 :         if (j == artics.end() || j == artics.begin()) {
     949            0 :             return nullptr;
     950              :         }
     951            0 :         return *(j - 1);
     952              :     }
     953            0 :     if (i == m_lyrics.begin()) {
     954            0 :         return !artics.empty() ? artics.back() : nullptr;
     955              :     }
     956              : 
     957              :     // Move to the previous Lyrics...
     958            0 :     Lyrics* lyrics = *(i - 1);
     959            0 :     IF_ASSERT_FAILED(lyrics) {
     960            0 :         return nullptr;
     961              :     }
     962            0 :     if (lyrics->separator()) {
     963              :         // Move to the LyricsLine of the previous Lyrics (if it exists)...
     964            0 :         return lyrics->separator();
     965              :     }
     966            0 :     return lyrics;
     967            0 : }
     968              : 
     969              : //---------------------------------------------------------
     970              : //   nextElement
     971              : //---------------------------------------------------------
     972              : 
     973            0 : EngravingItem* ChordRest::nextElement()
     974              : {
     975            0 :     EngravingItem* e = score()->selection().element();
     976            0 :     if (!e && !score()->selection().elements().empty()) {
     977            0 :         e = score()->selection().elements().front();
     978              :     }
     979            0 :     switch (e->type()) {
     980            0 :     case ElementType::ARTICULATION:
     981              :     case ElementType::ORNAMENT:
     982              :     case ElementType::TAPPING:
     983              :     case ElementType::LYRICS:
     984              :     case ElementType::LYRICSLINE: {
     985            0 :         EngravingItem* next = nextArticulationOrLyric(e);
     986            0 :         if (next) {
     987            0 :             return next;
     988              :         } else {
     989            0 :             break;
     990              :         }
     991              :     }
     992            0 :     default: {
     993            0 :         if (isChord() && !toChord(this)->articulations().empty()) {
     994            0 :             return toChord(this)->articulations()[0];
     995            0 :         } else if (!m_lyrics.empty()) {
     996            0 :             return m_lyrics[0];
     997              :         } else {
     998            0 :             break;
     999              :         }
    1000              :     }
    1001              :     }
    1002            0 :     staff_idx_t staffId = e->staffIdx();
    1003            0 :     return segment()->nextElement(staffId);
    1004              : }
    1005              : 
    1006              : //---------------------------------------------------------
    1007              : //   prevElement
    1008              : //---------------------------------------------------------
    1009              : 
    1010            0 : EngravingItem* ChordRest::prevElement()
    1011              : {
    1012            0 :     EngravingItem* e = score()->selection().element();
    1013            0 :     if (!e && !score()->selection().elements().empty()) {
    1014            0 :         e = score()->selection().elements().back();
    1015              :     }
    1016            0 :     switch (e->type()) {
    1017            0 :     case ElementType::ARTICULATION:
    1018              :     case ElementType::ORNAMENT:
    1019              :     case ElementType::TAPPING:
    1020              :     case ElementType::LYRICS:
    1021              :     case ElementType::LYRICSLINE: {
    1022            0 :         EngravingItem* prev = prevArticulationOrLyric(e);
    1023            0 :         if (prev) {
    1024            0 :             return prev;
    1025              :         } else {
    1026            0 :             if (isChord()) {
    1027            0 :                 return toChord(this)->lastElementBeforeSegment();
    1028              :             }
    1029              :         }
    1030              :         // fall through
    1031              :     }
    1032              :     default: {
    1033            0 :         break;
    1034              :     }
    1035              :     }
    1036              : 
    1037            0 :     Tuplet* tuplet = this->tuplet();
    1038            0 :     if (tuplet && this == tuplet->elements().front()) {
    1039            0 :         return tuplet;
    1040              :     }
    1041              : 
    1042            0 :     staff_idx_t staffId = e->staffIdx();
    1043            0 :     EngravingItem* prevItem = segment()->prevElement(staffId);
    1044            0 :     if (prevItem && prevItem->isNote()) {
    1045            0 :         const Chord* prevChord = toNote(prevItem)->chord();
    1046            0 :         if (prevChord && !prevChord->articulations().empty()) {
    1047            0 :             return prevChord->articulations().back();
    1048              :         }
    1049              :     }
    1050            0 :     return prevItem;
    1051              : }
    1052              : 
    1053              : //---------------------------------------------------------
    1054              : //   lastElementBeforeSegment
    1055              : //---------------------------------------------------------
    1056              : 
    1057            0 : EngravingItem* ChordRest::lastElementBeforeSegment()
    1058              : {
    1059            0 :     if (m_lyrics.empty()) {
    1060            0 :         return nullptr;
    1061              :     }
    1062            0 :     Lyrics* lyrics = m_lyrics.back();
    1063            0 :     IF_ASSERT_FAILED(lyrics) {
    1064            0 :         return nullptr;
    1065              :     }
    1066            0 :     if (LyricsLine* lyricsLine = lyrics->separator()) {
    1067            0 :         return lyricsLine;
    1068              :     }
    1069            0 :     return lyrics;
    1070              : }
    1071              : 
    1072              : //---------------------------------------------------------
    1073              : //   nextSegmentElement
    1074              : //---------------------------------------------------------
    1075              : 
    1076            0 : EngravingItem* ChordRest::nextSegmentElement()
    1077              : {
    1078            0 :     return segment()->firstInNextSegments(staffIdx());
    1079              : }
    1080              : 
    1081              : //---------------------------------------------------------
    1082              : //   scanElements
    1083              : //---------------------------------------------------------
    1084              : 
    1085        20849 : void ChordRest::scanElements(std::function<void(EngravingItem*)> func)
    1086              : {
    1087         5739 :     if (m_beam && (m_beam->elements().front() == this)
    1088        26588 :         && !measure()->stemless(staffIdx())) {
    1089         2601 :         m_beam->scanElements(func);
    1090              :     }
    1091        21077 :     for (Lyrics* l : m_lyrics) {
    1092          228 :         l->scanElements(func);
    1093              :     }
    1094        20849 :     DurationElement* de = this;
    1095        22233 :     while (de->tuplet() && de->tuplet()->elements().front() == de) {
    1096         1384 :         de->tuplet()->scanElements(func);
    1097         1384 :         de = de->tuplet();
    1098              :     }
    1099        20849 :     if (m_tabDur) {
    1100            0 :         func(m_tabDur);
    1101              :     }
    1102        20849 : }
    1103              : 
    1104              : //---------------------------------------------------------
    1105              : //   prevSegmentElement
    1106              : //---------------------------------------------------------
    1107              : 
    1108            0 : EngravingItem* ChordRest::prevSegmentElement()
    1109              : {
    1110            0 :     return segment()->lastInPrevSegments(staffIdx());
    1111              : }
    1112              : 
    1113            0 : String ChordRest::accessibleExtraInfo() const
    1114              : {
    1115            0 :     String rez;
    1116            0 :     for (EngravingItem* l : lyrics()) {
    1117            0 :         if (!score()->selectionFilter().canSelect(l)) {
    1118            0 :             continue;
    1119              :         }
    1120            0 :         rez = String(u"%1 %2").arg(rez, l->screenReaderInfo());
    1121              :     }
    1122              : 
    1123            0 :     if (segment()) {
    1124            0 :         for (EngravingItem* e : segment()->annotations()) {
    1125            0 :             if (!score()->selectionFilter().canSelect(e)) {
    1126            0 :                 continue;
    1127              :             }
    1128            0 :             if (e->track() == track()) {
    1129            0 :                 rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
    1130              :             }
    1131              :         }
    1132              : 
    1133            0 :         SpannerMap& smap = score()->spannerMap();
    1134            0 :         auto spanners = smap.findOverlapping(tick().ticks(), tick().ticks());
    1135            0 :         for (auto interval : spanners) {
    1136            0 :             Spanner* s = interval.value;
    1137            0 :             if (!score()->selectionFilter().canSelect(s)) {
    1138            0 :                 continue;
    1139              :             }
    1140              :             // voltas are added for barlines, ties are added in notes
    1141            0 :             if (s->isVolta() || s->isTie()) {
    1142            0 :                 continue;
    1143              :             }
    1144              : 
    1145            0 :             if (s->isSlur()) {
    1146            0 :                 if (s->tick() == tick() && s->track() == track()) {
    1147            0 :                     rez += u" " + muse::mtrc("engraving", "Start of %1").arg(s->screenReaderInfo());
    1148              :                 }
    1149            0 :                 if (s->tick2() == tick() && s->track2() == track()) {
    1150            0 :                     rez += u" " + muse::mtrc("engraving", "End of %1").arg(s->screenReaderInfo());
    1151              :                 }
    1152            0 :             } else if (s->staffIdx() == staffIdx()) {
    1153            0 :                 bool start = s->tick() == tick();
    1154            0 :                 bool end   = s->tick2() == tick() + ticks();
    1155            0 :                 if (start && end) {
    1156            0 :                     rez += u" " + muse::mtrc("engraving", "Start and end of %1").arg(s->screenReaderInfo());
    1157            0 :                 } else if (start) {
    1158            0 :                     rez += u" " + muse::mtrc("engraving", "Start of %1").arg(s->screenReaderInfo());
    1159            0 :                 } else if (end) {
    1160            0 :                     rez += u" " + muse::mtrc("engraving", "End of %1").arg(s->screenReaderInfo());
    1161              :                 }
    1162              :             }
    1163              :         }
    1164            0 :     }
    1165            0 :     return rez;
    1166            0 : }
    1167              : 
    1168              : //---------------------------------------------------------
    1169              : //   isMelismaEnd
    1170              : //    returns true if chordrest represents the end of a melisma
    1171              : //---------------------------------------------------------
    1172              : 
    1173         9925 : bool ChordRest::isMelismaEnd() const
    1174              : {
    1175         9925 :     return m_melismaEnd;
    1176              : }
    1177              : 
    1178              : //---------------------------------------------------------
    1179              : //   setMelismaEnd
    1180              : //---------------------------------------------------------
    1181              : 
    1182          106 : void ChordRest::setMelismaEnd(bool v)
    1183              : {
    1184          106 :     m_melismaEnd = v;
    1185              :     // TODO: don't take "false" at face value
    1186              :     // check to see if some other melisma ends here,
    1187              :     // in which case we can leave this set to true
    1188              :     // for now, rely on the fact that we'll generate the value correctly on layout
    1189          106 : }
    1190              : 
    1191              : //---------------------------------------------------------
    1192              : //   lyrics
    1193              : //---------------------------------------------------------
    1194              : 
    1195           34 : Lyrics* ChordRest::lyrics(int verse) const
    1196              : {
    1197           49 :     for (Lyrics* l : m_lyrics) {
    1198           46 :         if (l->verse() == verse) {
    1199           31 :             return l;
    1200              :         }
    1201              :     }
    1202            3 :     return 0;
    1203              : }
    1204              : 
    1205          749 : Lyrics* ChordRest::lyrics(int verse, PlacementV p) const
    1206              : {
    1207         1000 :     for (Lyrics* l : m_lyrics) {
    1208          792 :         if (l->placement() == p && l->verse() == verse) {
    1209          541 :             return l;
    1210              :         }
    1211              :     }
    1212          208 :     return 0;
    1213              : }
    1214              : 
    1215              : //---------------------------------------------------------
    1216              : //   lastVerse
    1217              : //    return last verse number (starting from 0)
    1218              : //    return -1 if there are no lyrics;
    1219              : //---------------------------------------------------------
    1220              : 
    1221            0 : int ChordRest::lastVerse(PlacementV p) const
    1222              : {
    1223            0 :     int lastVerse = -1;
    1224              : 
    1225            0 :     for (Lyrics* l : m_lyrics) {
    1226            0 :         if (l->placement() == p && l->verse() > lastVerse) {
    1227            0 :             lastVerse = l->verse();
    1228              :         }
    1229              :     }
    1230              : 
    1231            0 :     return lastVerse;
    1232              : }
    1233              : 
    1234              : //---------------------------------------------------------
    1235              : //   removeMarkings
    1236              : //    - this is normally called after cloning a chord to tie a note over the barline
    1237              : //    - there is no special undo handling; the assumption is that undo will simply remove the cloned chord
    1238              : //    - two note tremolos are converted into simple notes
    1239              : //    - single note tremolos are optionally retained
    1240              : //---------------------------------------------------------
    1241              : 
    1242          133 : void ChordRest::removeMarkings(bool /* keepTremolo */)
    1243              : {
    1244          133 :     muse::DeleteAll(el());
    1245          133 :     clearEls();
    1246          133 :     muse::DeleteAll(lyrics());
    1247          133 :     lyrics().clear();
    1248          133 : }
    1249              : 
    1250              : //---------------------------------------------------------
    1251              : //   isBefore
    1252              : //---------------------------------------------------------
    1253              : 
    1254            0 : bool ChordRest::isBefore(const EngravingItem* o) const
    1255              : {
    1256            0 :     if (!o || this == o) {
    1257            0 :         return false;
    1258              :     }
    1259              : 
    1260            0 :     const ChordRest* otherCr = nullptr;
    1261            0 :     if (o->isChordRest()) {
    1262            0 :         otherCr = toChordRest(o);
    1263            0 :     } else if (o->isNote()) {
    1264            0 :         otherCr = toNote(o)->chord();
    1265              :     }
    1266              : 
    1267            0 :     if (!otherCr) {
    1268            0 :         return EngravingItem::isBefore(o);
    1269              :     }
    1270              : 
    1271            0 :     Fraction thisTick = tick();
    1272            0 :     Fraction otherTick = otherCr->tick();
    1273            0 :     if (thisTick != otherTick) {
    1274            0 :         return thisTick < otherTick;
    1275              :     }
    1276              : 
    1277            0 :     bool thisIsGraceBefore = isGraceBefore();
    1278            0 :     bool otherIsGraceBefore = otherCr->isGraceBefore();
    1279            0 :     if (thisIsGraceBefore != otherIsGraceBefore) {
    1280            0 :         return thisIsGraceBefore;
    1281            0 :     } else if (thisIsGraceBefore) {
    1282            0 :         return toChord(this)->graceIndex() < toChord(otherCr)->graceIndex();
    1283              :     }
    1284              : 
    1285            0 :     bool thisIsGraceAfter = isGraceAfter();
    1286            0 :     bool otherIsGraceAfter = otherCr->isGraceAfter();
    1287            0 :     if (thisIsGraceAfter != otherIsGraceAfter) {
    1288            0 :         return !thisIsGraceAfter;
    1289            0 :     } else if (thisIsGraceAfter) {
    1290            0 :         return toChord(this)->graceIndex() > toChord(otherCr)->graceIndex();
    1291              :     }
    1292              : 
    1293            0 :     return false;
    1294              : }
    1295              : 
    1296        30771 : bool ChordRest::isBelowCrossBeam(const BeamBase* beamBase) const
    1297              : {
    1298        30771 :     return staffMove() >= beamBase->crossStaffIdx();
    1299              : }
    1300              : 
    1301          703 : void ChordRest::checkStaffMoveValidity()
    1302              : {
    1303          703 :     if (!staff()) {
    1304            0 :         return;
    1305              :     }
    1306          703 :     staff_idx_t idx = m_staffMove ? vStaffIdx() : staffIdx() + m_storedStaffMove;
    1307          703 :     const Staff* baseStaff = staff();
    1308          703 :     const StaffType* baseStaffType = baseStaff->staffTypeForElement(this);
    1309          703 :     const Staff* targetStaff  = score()->staff(idx);
    1310          703 :     const StaffType* targetStaffType = targetStaff ? targetStaff->staffTypeForElement(this) : nullptr;
    1311              :     // check that destination staff makes sense
    1312          703 :     const TrackRange trackRange = part()->trackRange();
    1313          703 :     staff_idx_t minStaff = track2staff(trackRange.startTrack);
    1314          703 :     staff_idx_t maxStaff = track2staff(trackRange.endTrack);
    1315          703 :     bool isDestinationValid = targetStaff && targetStaff->visible() && idx >= minStaff && idx < maxStaff
    1316         1406 :                               && targetStaffType->group() == baseStaffType->group() && targetStaff->isLinked() == baseStaff->isLinked();
    1317          703 :     if (!isDestinationValid) {
    1318            1 :         LOGD("staffMove out of scope %zu + %d min %zu max %zu",
    1319              :              staffIdx(), m_staffMove, minStaff, maxStaff);
    1320              :         // If destination staff is invalid, reset to base staff
    1321            1 :         if (m_staffMove) {
    1322              :             // Remember the intended staff move, so it can be re-applied if
    1323              :             // destination staff becomes valid (e.g. unihidden)
    1324            1 :             m_storedStaffMove = m_staffMove;
    1325              :         }
    1326            1 :         setStaffMove(0);
    1327          702 :     } else if (!m_staffMove && m_storedStaffMove) {
    1328            1 :         setStaffMove(m_storedStaffMove);
    1329            1 :         m_storedStaffMove = 0;
    1330              :     }
    1331              : 
    1332          703 :     if (isDestinationValid) {
    1333              :         // Move valid, clear stored move
    1334          702 :         m_storedStaffMove = 0;
    1335              :     }
    1336              : }
    1337              : 
    1338        64976 : bool ChordRest::hasFollowingJumpItem() const
    1339              : {
    1340        64976 :     const Segment* seg = segment();
    1341        64976 :     const Measure* measure = seg ? seg->measure() : nullptr;
    1342        64976 :     if (!measure) {
    1343            0 :         return false;
    1344              :     }
    1345              : 
    1346        64976 :     if (endTick() != measure->endTick()) {
    1347        32204 :         return false;
    1348              :     }
    1349              : 
    1350        32772 :     std::vector<Measure*> followingRepeatMeasures = findFollowingRepeatMeasures(measure);
    1351              : 
    1352        32772 :     return !followingRepeatMeasures.empty();
    1353        32772 : }
    1354              : 
    1355          687 : bool ChordRest::hasPrecedingJumpItem() const
    1356              : {
    1357          693 :     TRACEFUNC;
    1358          687 :     const Segment* seg = segment();
    1359          687 :     const Measure* measure = seg->measure();
    1360              : 
    1361          687 :     if (tick() != measure->tick()) {
    1362          422 :         return false;
    1363              :     }
    1364              : 
    1365          265 :     std::vector<Measure*> precedingRepeatMeasures = findPreviousRepeatMeasures(measure);
    1366              : 
    1367          265 :     return !precedingRepeatMeasures.empty();
    1368          687 : }
    1369              : }
        

Generated by: LCOV version 2.0-1