LCOV - code coverage report
Current view: top level - src/engraving/api/v1 - cursor.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 22.1 % 331 73
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 "apistructs.h"
      24              : #include "cursor.h"
      25              : #include "elements.h"
      26              : #include "score.h"
      27              : #include "engraving/dom/masterscore.h"
      28              : #include "engraving/dom/anchors.h"
      29              : #include "engraving/dom/chordrest.h"
      30              : #include "engraving/dom/chord.h"
      31              : #include "engraving/dom/rest.h"
      32              : #include "engraving/dom/note.h"
      33              : #include "engraving/dom/repeatlist.h"
      34              : #include "engraving/dom/stafftext.h"
      35              : #include "engraving/dom/tempotimeline.h"
      36              : #include "engraving/dom/measure.h"
      37              : #include "engraving/dom/page.h"
      38              : #include "engraving/dom/score.h"
      39              : #include "engraving/dom/system.h"
      40              : #include "engraving/dom/segment.h"
      41              : #include "engraving/dom/timesig.h"
      42              : #include "engraving/dom/tuplet.h"
      43              : 
      44              : #include "engraving/editing/edittimesig.h"
      45              : #include "engraving/editing/noteinput.h"
      46              : #include "engraving/editing/transaction/transaction.h"
      47              : 
      48              : using namespace mu::engraving::apiv1;
      49              : 
      50            9 : Cursor::Cursor(mu::engraving::Score* s)
      51            9 :     : QObject(0), m_filter(mu::engraving::SegmentType::ChordRest)
      52              : {
      53            9 :     setScore(s);
      54            9 : }
      55              : 
      56              : //---------------------------------------------------------
      57              : //   score
      58              : //---------------------------------------------------------
      59              : 
      60            0 : Score* Cursor::score() const
      61              : {
      62            0 :     return wrap<Score>(m_score, Ownership::SCORE);
      63              : }
      64              : 
      65              : //---------------------------------------------------------
      66              : //   setScore
      67              : //---------------------------------------------------------
      68              : 
      69            9 : void Cursor::setScore(mu::engraving::Score* s)
      70              : {
      71            9 :     if (m_score == s) {
      72            0 :         return;
      73              :     }
      74              : 
      75            9 :     m_score = s;
      76              : 
      77            9 :     switch (m_inputStateMode) {
      78            9 :     case INPUT_STATE_INDEPENDENT:
      79            9 :         is.reset(new InputState);
      80            9 :         break;
      81            0 :     case INPUT_STATE_SYNC_WITH_SCORE:
      82            0 :         break;
      83              :     }
      84              : }
      85              : 
      86            0 : void Cursor::setScore(Score* s)
      87              : {
      88            0 :     setScore(s ? s->score() : nullptr);
      89            0 : }
      90              : 
      91          177 : mu::engraving::InputState& Cursor::inputState()
      92              : {
      93          177 :     return is ? *is.get() : m_score->inputState();
      94              : }
      95              : 
      96              : //---------------------------------------------------------
      97              : //   setInputStateMode
      98              : //---------------------------------------------------------
      99              : 
     100            0 : void Cursor::setInputStateMode(InputStateMode val)
     101              : {
     102            0 :     if (val == INPUT_STATE_SYNC_WITH_SCORE) {
     103            0 :         is.reset();
     104              :     } else {
     105            0 :         is.reset(new InputState);
     106              :     }
     107              : 
     108            0 :     m_inputStateMode = val;
     109            0 : }
     110              : 
     111              : //---------------------------------------------------------
     112              : //   rewind
     113              : ///   Rewind cursor to a certain position.
     114              : ///   \param mode Determines the position where to move
     115              : ///   this cursor. See Cursor::RewindMode to see the list of
     116              : ///   available rewind modes.
     117              : ///   \note In MuseScore 2.X, this function took an integer
     118              : ///   value (0, 1 or 2) as its parameter. For compatibility
     119              : ///   reasons, the old values are still working, but it is
     120              : ///   recommended to use RewindMode enumerators instead.
     121              : //---------------------------------------------------------
     122              : 
     123           10 : void Cursor::rewind(RewindMode mode)
     124              : {
     125              :     //
     126              :     // rewind to start of score
     127              :     //
     128           10 :     if (mode == SCORE_START) {
     129           10 :         mu::engraving::Measure* m = m_score->firstMeasure();
     130           10 :         if (m) {
     131           10 :             setSegment(m->first(m_filter));
     132           10 :             nextInTrack();
     133              :         } else {
     134            0 :             setSegment(nullptr);
     135              :         }
     136              :     }
     137              :     //
     138              :     // rewind to start of selection
     139              :     //
     140            0 :     else if (mode == SELECTION_START) {
     141            0 :         if (!m_score->selection().isRange()) {
     142            0 :             return;
     143              :         }
     144            0 :         setSegment(m_score->selection().startSegment());
     145            0 :         setTrack(static_cast<int>(m_score->selection().staffStart() * VOICES));
     146            0 :         nextInTrack();
     147              :     }
     148              :     //
     149              :     // rewind to end of selection
     150              :     //
     151            0 :     else if (mode == SELECTION_END) {
     152            0 :         if (!m_score->selection().isRange()) {
     153            0 :             return;
     154              :         }
     155            0 :         setSegment(m_score->selection().endSegment());
     156            0 :         setTrack(static_cast<int>((m_score->selection().staffEnd() * VOICES) - 1));      // be sure m_track exists
     157              :     }
     158              : }
     159              : 
     160              : //---------------------------------------------------------
     161              : //   rewindToTick
     162              : ///   Rewind cursor to a position defined by tick.
     163              : ///   To avoid rounding errors, use \ref rewindToFraction.
     164              : ///   \param tick Determines the position where to move
     165              : ///   this cursor.
     166              : ///   \see \ref mu::plugins::api::Segment::tick "Segment.tick"
     167              : ///   \since MuseScore 3.5
     168              : //---------------------------------------------------------
     169              : 
     170            0 : void Cursor::rewindToTick(int tick)
     171              : {
     172              :     // integer ticks may contain numeric errors so it is
     173              :     // better to search not precisely if possible
     174            0 :     mu::engraving::Fraction fTick = mu::engraving::Fraction::fromTicks(tick + 1);
     175            0 :     mu::engraving::Segment* seg = m_score->tick2leftSegment(fTick);
     176            0 :     if (!(seg->segmentType() & m_filter)) {
     177              :         // we need another segment type, search by known tick
     178            0 :         seg = m_score->tick2segment(seg->tick(), /* first */ true, m_filter);
     179              :     }
     180              : 
     181            0 :     setSegment(seg);
     182            0 :     nextInTrack();
     183            0 : }
     184              : 
     185            0 : void Cursor::rewindToFraction(Fraction* f)
     186              : {
     187            0 :     const mu::engraving::Fraction fraction = f->fraction();
     188            0 :     if (!fraction.isValid() || fraction.negative()) {
     189            0 :         return;
     190              :     }
     191              : 
     192              :     // Since we are now dealing with precise tick values, we don't need to call tick2leftSegment.
     193              :     // We just search for a valid segment at the given tick, and if none exist, do nothing.
     194            0 :     mu::engraving::Segment* seg = m_score->tick2segment(fraction, /* first */ true, m_filter);
     195              : 
     196              :     // Exception for TimeTick segments, since here the user may want to create a new anchor.
     197            0 :     if (!seg && m_filter & mu::engraving::SegmentType::TimeTick) {
     198            0 :         mu::engraving::Measure* measure = m_score->tick2measure(fraction);
     199            0 :         if (measure) {
     200            0 :             mu::engraving::TimeTickAnchor* anchor = mu::engraving::EditTimeTickAnchors::createTimeTickAnchor(measure,
     201            0 :                                                                                                              fraction - measure->tick(),
     202            0 :                                                                                                              track2staff(track()));
     203            0 :             mu::engraving::EditTimeTickAnchors::updateLayout(measure);
     204            0 :             seg = anchor->segment();
     205              :         } else {
     206            0 :             LOGW("Cursor::rewindToFraction: Cannot create TimeTickAnchor at provided tick.");
     207              :         }
     208              :     }
     209              : 
     210            0 :     setSegment(seg);
     211              : }
     212              : 
     213              : //---------------------------------------------------------
     214              : //   prev
     215              : ///   Move the cursor to the previous segment.
     216              : ///   \return \p false if the beginning of the score is
     217              : ///   reached, \p true otherwise.
     218              : ///   \since MuseScore 3.3.4
     219              : //---------------------------------------------------------
     220              : 
     221            0 : bool Cursor::prev()
     222              : {
     223            0 :     if (!segment()) {
     224            0 :         return false;
     225              :     }
     226            0 :     prevInTrack();
     227            0 :     return segment();
     228              : }
     229              : 
     230              : //---------------------------------------------------------
     231              : //   next
     232              : ///   Move the cursor to the next segment.
     233              : ///   \return \p false if the end of the score is reached,
     234              : ///   \p true otherwise.
     235              : //---------------------------------------------------------
     236              : 
     237            1 : bool Cursor::next()
     238              : {
     239            1 :     if (!segment()) {
     240            0 :         return false;
     241              :     }
     242            1 :     setSegment(segment()->next1(m_filter));
     243            1 :     nextInTrack();
     244            1 :     return segment();
     245              : }
     246              : 
     247              : //---------------------------------------------------------
     248              : //   nextMeasure
     249              : ///   Move the cursor to the first segment of the next
     250              : ///   measure.
     251              : ///   \return \p false if the end of the score is reached,
     252              : ///   \p true otherwise.
     253              : //---------------------------------------------------------
     254              : 
     255            0 : bool Cursor::nextMeasure()
     256              : {
     257            0 :     if (!segment()) {
     258            0 :         return false;
     259              :     }
     260            0 :     mu::engraving::Measure* m = segment()->measure()->nextMeasure();
     261            0 :     if (!m) {
     262            0 :         setSegment(nullptr);
     263            0 :         return false;
     264              :     }
     265            0 :     setSegment(m->first(m_filter));
     266            0 :     nextInTrack();
     267            0 :     return segment();
     268              : }
     269              : 
     270              : //---------------------------------------------------------
     271              : //   add
     272              : ///   Adds the given element to a score at this cursor's
     273              : ///   position.
     274              : //---------------------------------------------------------
     275              : 
     276            0 : void Cursor::add(EngravingItem* wrapped)
     277              : {
     278              :     using mu::engraving::SegmentType;
     279            0 :     mu::engraving::EngravingItem* s = wrapped ? wrapped->element() : nullptr;
     280            0 :     if (!segment() || !s) {
     281            0 :         return;
     282              :     }
     283              : 
     284              :     // Ensure that the object has the expected ownership
     285            0 :     if (wrapped->ownership() == Ownership::SCORE) {
     286            0 :         LOGW("Cursor::add: Cannot add this element. The element is already part of the score.");
     287            0 :         return;            // Don't allow operation.
     288              :     }
     289              : 
     290            0 :     const int m_track = track();
     291            0 :     mu::engraving::Segment* _segment = segment();
     292              : 
     293            0 :     wrapped->setOwnership(Ownership::SCORE);
     294            0 :     s->setScore(m_score);
     295            0 :     s->setTrack(m_track);
     296            0 :     s->setOwnershipParent(_segment);
     297              : 
     298            0 :     if (s->isChordRest()) {
     299            0 :         s->score()->undoAddCR(toChordRest(s), _segment->measure(), _segment->tick());
     300            0 :         return;
     301              :     }
     302            0 :     switch (s->type()) {
     303            0 :     case ElementType::KEYSIG: {
     304            0 :         mu::engraving::Segment* ns = _segment->measure()->undoGetSegment(SegmentType::KeySig, _segment->tick());
     305            0 :         s->setOwnershipParent(ns);
     306            0 :         m_score->undoAddElement(s);
     307            0 :         break;
     308              :     }
     309            0 :     case ElementType::TIMESIG: {
     310            0 :         mu::engraving::Measure* m = _segment->measure();
     311            0 :         mu::engraving::Fraction tick = m->tick();
     312            0 :         mu::engraving::Transaction& tx = m_score->transactionManager()->currentOrDummyTransaction();
     313            0 :         mu::engraving::EditTimeSig::addTimeSig(tx, m_score, m, m_track, toTimeSig(s), false);
     314            0 :         m = m_score->tick2measure(tick);
     315            0 :         _segment = m->first(m_filter);
     316            0 :         nextInTrack();
     317            0 :         break;
     318              :     }
     319              : 
     320              :     // To be added at measurebase level
     321            0 :     case ElementType::LAYOUT_BREAK: {
     322            0 :         MeasureBase::addInternal(_segment->measure(), s);
     323            0 :         break;
     324              :     }
     325              :     // To be added at measure level
     326              :     // todo: insert boxes before/after other boxes correctly
     327            0 :     case ElementType::MEASURE_NUMBER:
     328              :     case ElementType::SPACER:
     329              :     case ElementType::JUMP:
     330              :     case ElementType::MARKER:
     331              :     case ElementType::HBOX:
     332              :     case ElementType::STAFFTYPE_CHANGE: {
     333            0 :         mu::engraving::Measure* m = _segment->measure();
     334            0 :         s->setOwnershipParent(m);
     335            0 :         m_score->undoAddElement(s);
     336            0 :         break;
     337              :     }
     338              : 
     339              :     // To be added at chord level
     340            0 :     case ElementType::NOTE:
     341              :     case ElementType::ARPEGGIO:
     342              :     case ElementType::CHORD_BRACKET:
     343              :     case ElementType::TREMOLO_SINGLECHORD:
     344              :     case ElementType::TREMOLO_TWOCHORD:
     345              :     case ElementType::CHORDLINE:
     346              :     case ElementType::ORNAMENT:
     347              :     case ElementType::TAPPING:
     348              :     case ElementType::ARTICULATION: {
     349            0 :         mu::engraving::EngravingItem* curElement = currentElement();
     350            0 :         if (curElement->isChord()) {
     351              :             // call Chord::addInternal() (i.e. do the same as a call to Chord.add())
     352            0 :             Chord::addInternal(toChord(curElement), s);
     353              :         }
     354            0 :         break;
     355              :     }
     356              : 
     357              :     // To be added at chord/rest level
     358            0 :     case ElementType::LYRICS: {
     359            0 :         mu::engraving::EngravingItem* curElement = currentElement();
     360            0 :         if (curElement->isChordRest()) {
     361            0 :             s->setOwnershipParent(curElement);
     362            0 :             m_score->undoAddElement(s);
     363              :         }
     364            0 :         break;
     365              :     }
     366              : 
     367              :     // To be added to a note (and in case of SYMBOL also to a rest)
     368            0 :     case ElementType::SYMBOL: {
     369            0 :         mu::engraving::EngravingItem* curElement = currentElement();
     370            0 :         if (curElement->isRest()) {
     371            0 :             s->setOwnershipParent(curElement);
     372            0 :             m_score->undoAddElement(s);
     373              :         }
     374              :         [[fallthrough]];
     375              :     }
     376              :     case ElementType::FINGERING:
     377              :     case ElementType::BEND:
     378              :     case ElementType::NOTEHEAD: {
     379            0 :         mu::engraving::EngravingItem* curElement = currentElement();
     380            0 :         if (curElement->isChord()) {
     381            0 :             mu::engraving::Chord* chord = toChord(curElement);
     382            0 :             if (!chord->notes().empty()) {
     383              :                 // Get first note from chord to add element
     384            0 :                 mu::engraving::Note* note = chord->notes().front();
     385            0 :                 Note::addInternal(note, s);
     386              :             }
     387              :         }
     388            0 :         break;
     389              :     }
     390              : 
     391              :     // To be added to a segment (clef subtype)
     392            0 :     case ElementType::CLEF: {
     393            0 :         mu::engraving::Clef* clef = toClef(s);
     394            0 :         if (clef->clefType() == mu::engraving::ClefType::INVALID) {
     395            0 :             clef->setClefType(mu::engraving::ClefType::G);
     396              :         }
     397            0 :         SegmentType st = SegmentType::Clef;
     398            0 :         mu::engraving::Measure* measure = _segment->measure();
     399            0 :         mu::engraving::Fraction rt = _segment->rtick();
     400            0 :         if (rt == mu::engraving::Fraction(0, 1)) {
     401            0 :             mu::engraving::Measure* prevMeasure = measure->prevMeasure();
     402            0 :             if (prevMeasure && !prevMeasure->sectionBreak()) {
     403            0 :                 measure = measure->prevMeasure();
     404            0 :                 rt      = measure->ticks();
     405              :             } else {
     406            0 :                 st = SegmentType::HeaderClef;
     407              :             }
     408              :         }
     409            0 :         mu::engraving::Segment* destSeg = measure->undoGetSegmentR(st, rt);
     410            0 :         clef->setOwnershipParent(destSeg);
     411            0 :         clef->setTrack(m_track);
     412            0 :         m_score->undoAddElement(clef);
     413            0 :         break;
     414              :     }
     415              :     // To be added to a segment (clef subtype)
     416            0 :     case ElementType::AMBITUS: {
     417              :         // Find backwards first measure containing a clef
     418            0 :         mu::engraving::Segment* parent = nullptr;
     419            0 :         for (mu::engraving::Measure* m = _segment->measure(); m; m = m->prevMeasure()) {
     420            0 :             mu::engraving::Segment* seg = m->findSegment(SegmentType::Clef | SegmentType::HeaderClef, m->tick());
     421            0 :             if (!seg) {
     422            0 :                 continue;
     423              :             }
     424            0 :             parent = m->undoGetSegmentR(SegmentType::Ambitus, mu::engraving::Fraction(0, 1));
     425            0 :             s->setOwnershipParent(parent);
     426            0 :             s->setTrack(m_track);
     427            0 :             m_score->undoAddElement(s);
     428            0 :             break;
     429              :         }
     430            0 :         break;
     431              :     }
     432              : 
     433            0 :     default:           // All others will be added to the current segment
     434            0 :         m_score->undoAddElement(s);
     435            0 :         break;
     436              :     }
     437              : }
     438              : 
     439              : //---------------------------------------------------------
     440              : //   addNote
     441              : ///   \brief Adds a note to the current cursor position.
     442              : ///   \details The duration of the added note equals to
     443              : ///   what has been set by the previous setDuration() call.
     444              : ///   \param pitch MIDI pitch of the added note.
     445              : ///   \param addToChord add note to the current chord
     446              : ///   instead of replacing it. This parameter is available
     447              : ///   since MuseScore 3.3.4.
     448              : //---------------------------------------------------------
     449              : 
     450           40 : void Cursor::addNote(int pitch, bool addToChord)
     451              : {
     452           40 :     if (!pitchIsValid(pitch)) {
     453            0 :         LOGW("Cursor::addNote: invalid pitch: %d", pitch);
     454            0 :         return;
     455              :     }
     456           40 :     if (!segment()) {
     457            0 :         LOGW("Cursor::addNote: cursor location is undefined, use rewind() to define its location");
     458            0 :         return;
     459              :     }
     460           40 :     if (!inputState().duration().isValid()) {
     461            0 :         setDuration(1, 4);
     462              :     }
     463           40 :     NoteVal nval(pitch);
     464           40 :     NoteInput::addPitch(m_score->transactionManager()->currentOrDummyTransaction(), m_score, nval, addToChord, is.get());
     465              : }
     466              : 
     467              : //---------------------------------------------------------
     468              : //   addRest
     469              : ///   \brief Adds a rest to the current cursor position.
     470              : ///   \details The duration of the added rest equals to
     471              : ///   what has been set by the previous setDuration() call.
     472              : ///   \since MuseScore 3.5
     473              : //---------------------------------------------------------
     474              : 
     475            0 : void Cursor::addRest()
     476              : {
     477            0 :     if (!segment()) {
     478            0 :         LOGW("Cursor::addRest: cursor location is undefined, use rewind() to define its location");
     479            0 :         return;
     480              :     }
     481            0 :     if (!inputState().duration().isValid()) {
     482            0 :         setDuration(1, 4);
     483              :     }
     484            0 :     m_score->enterRest(inputState().duration(), is.get());
     485              : }
     486              : 
     487              : //---------------------------------------------------------
     488              : //   addTuplet
     489              : ///   \brief Adds a tuplet to the current cursor position.
     490              : ///   \details This function provides a possibility to setup
     491              : ///   the tuplet's ratio to any value (similarly to
     492              : ///   Add > Tuplets > Other... dialog in MuseScore).
     493              : ///
     494              : ///   Examples of most typical usage:
     495              : ///   \code
     496              : ///   // add a triplet of three eighth notes
     497              : ///   cursor.addTuplet(fraction(3, 2), fraction(1, 4));
     498              : ///
     499              : ///   // add a quintuplet in place of the current chord/rest
     500              : ///   var cr = cursor.element;
     501              : ///   if (cr)
     502              : ///       cursor.addTuplet(fraction(5, 4), cr.duration);
     503              : ///   \endcode
     504              : ///
     505              : ///   \param ratio tuplet ratio. Numerator represents
     506              : ///   actual number of notes in this tuplet, denominator is
     507              : ///   a number of "normal" notes which correspond to the
     508              : ///   same total duration. For example, a triplet has a
     509              : ///   ratio of 3/2 as it has 3 notes fitting to the
     510              : ///   duration which would normally be occupied by 2 notes
     511              : ///   of the same nominal length.
     512              : ///   \param duration total duration of the tuplet. To
     513              : ///   create a tuplet with duration matching to duration of
     514              : ///   existing chord or rest, use its
     515              : ///   \ref DurationElement.duration "duration" value as
     516              : ///   a parameter.
     517              : ///   \since MuseScore 3.5
     518              : ///   \see \ref DurationElement.tuplet
     519              : //---------------------------------------------------------
     520              : 
     521            6 : void Cursor::addTuplet(Fraction* ratio, Fraction* duration)
     522              : {
     523            6 :     if (!segment()) {
     524            0 :         LOGW("Cursor::addTuplet: cursor location is undefined, use rewind() to define its location");
     525            1 :         return;
     526              :     }
     527              : 
     528            6 :     const mu::engraving::Fraction fRatio = ratio->fraction();
     529            6 :     const mu::engraving::Fraction fDuration = duration->fraction();
     530              : 
     531           12 :     if (!fRatio.isValid() || fRatio.isZero() || fRatio.negative()
     532           12 :         || !fDuration.isValid() || fDuration.isZero() || fDuration.negative()) {
     533            0 :         LOGW("Cursor::addTuplet: invalid parameter values: %s, %s", qPrintable(fRatio.toString()), qPrintable(fDuration.toString()));
     534            0 :         return;
     535              :     }
     536              : 
     537            6 :     mu::engraving::Measure* tupletMeasure = segment()->measure();
     538            6 :     const mu::engraving::Fraction tupletTick = segment()->tick();
     539            6 :     if (tupletTick + fDuration > tupletMeasure->endTick()) {
     540            0 :         LOGW(
     541              :             "Cursor::addTuplet: cannot add cross-measure tuplet (measure %d, rel.tick %s, duration %s)",
     542            0 :             tupletMeasure->measureNumber() + 1, qPrintable(segment()->rtick().toString()), qPrintable(fDuration.toString()));
     543              : 
     544            0 :         return;
     545              :     }
     546              : 
     547            6 :     const mu::engraving::Fraction baseLen = fDuration * mu::engraving::Fraction(1, fRatio.denominator());
     548            6 :     if (!TDuration::isValid(baseLen)) {
     549            0 :         LOGW(
     550              :             "Cursor::addTuplet: cannot create tuplet for ratio %s and duration %s",
     551            0 :             qPrintable(fRatio.toString()), qPrintable(fDuration.toString()));
     552              : 
     553            0 :         return;
     554              :     }
     555              : 
     556            6 :     m_score->expandVoice(inputState().segment(), inputState().track());
     557            6 :     mu::engraving::ChordRest* cr = inputState().cr();
     558            6 :     if (!cr) { // shouldn't happen?
     559            0 :         return;
     560              :     }
     561              : 
     562              :     // check first: Score::addTuplet() shows a dialog when it can't, but the Cursor only logs
     563            6 :     if (!m_score->canAddTuplet(cr, fRatio, TDuration(baseLen))) {
     564            4 :         LOGW("Cursor::addTuplet: cannot add tuplet for ratio %s and duration %s",
     565            3 :              qPrintable(fRatio.toString()), qPrintable(fDuration.toString()));
     566              : 
     567            1 :         return;
     568              :     }
     569              : 
     570            5 :     const mu::engraving::MStyle& style = m_score->style();
     571            5 :     const auto numberType = mu::engraving::TupletNumberType(style.styleI(mu::engraving::Sid::tupletNumberType));
     572            5 :     const auto bracketType = mu::engraving::TupletBracketType(style.styleI(mu::engraving::Sid::tupletBracketType));
     573            5 :     if (!m_score->addTuplet(cr, fRatio, TDuration(baseLen), numberType, bracketType)) {
     574            0 :         return;
     575              :     }
     576              : 
     577            5 :     inputState().setSegment(tupletMeasure->tick2segment(tupletTick));
     578            5 :     inputState().setDuration(baseLen);
     579              : }
     580              : 
     581              : //---------------------------------------------------------
     582              : //   setDuration
     583              : ///   Set duration of the notes added by the cursor.
     584              : ///   \param z: numerator
     585              : ///   \param n: denominator. If n == 0, sets duration to
     586              : ///   a quarter.
     587              : ///   \see addNote()
     588              : //---------------------------------------------------------
     589              : 
     590            4 : void Cursor::setDuration(int z, int n)
     591              : {
     592            4 :     TDuration d(mu::engraving::Fraction(z, n));
     593            4 :     if (!d.isValid()) {
     594            0 :         d = TDuration(DurationType::V_QUARTER);
     595              :     }
     596            4 :     inputState().setDuration(d);
     597            4 : }
     598              : 
     599              : //---------------------------------------------------------
     600              : //   tick
     601              : //---------------------------------------------------------
     602              : 
     603            0 : int Cursor::tick()
     604              : {
     605            0 :     const mu::engraving::Segment* seg = segment();
     606            0 :     return seg ? seg->tick().ticks() : 0;
     607              : }
     608              : 
     609            0 : int Cursor::utick()
     610              : {
     611            0 :     return m_score->repeatList(true).tick2utick(tick());
     612              : }
     613              : 
     614            0 : mu::engraving::Fraction Cursor::fraction() const
     615              : {
     616            0 :     const mu::engraving::Segment* seg = segment();
     617            0 :     return seg ? seg->tick() : mu::engraving::Fraction(0, 1);
     618              : }
     619              : 
     620            0 : Fraction* Cursor::qmlFraction() const
     621              : {
     622            0 :     return wrap(fraction());
     623              : }
     624              : 
     625              : //---------------------------------------------------------
     626              : //   time
     627              : /// Time at tick position, read only
     628              : /// \param includeRepeats since 4.6
     629              : //---------------------------------------------------------
     630              : 
     631            0 : double Cursor::time(bool includeRepeats)
     632              : {
     633            0 :     return m_score->tempoTimeline(includeRepeats).utick2utime(includeRepeats ? utick() : tick()) * 1000;
     634              : }
     635              : 
     636              : //---------------------------------------------------------
     637              : //   tempo
     638              : //---------------------------------------------------------
     639              : 
     640            0 : qreal Cursor::tempo()
     641              : {
     642            0 :     return m_score->tempo(fraction()).val;
     643              : }
     644              : 
     645              : //---------------------------------------------------------
     646              : //   currentElement
     647              : //---------------------------------------------------------
     648              : 
     649            0 : mu::engraving::EngravingItem* Cursor::currentElement() const
     650              : {
     651            0 :     const int t = track();
     652            0 :     mu::engraving::Segment* seg = segment();
     653            0 :     return seg && seg->element(t) ? seg->element(t) : nullptr;
     654              : }
     655              : 
     656              : //---------------------------------------------------------
     657              : //   segment
     658              : //---------------------------------------------------------
     659              : 
     660            0 : Segment* Cursor::qmlSegment() const
     661              : {
     662            0 :     mu::engraving::Segment* seg = segment();
     663            0 :     return seg ? wrap<Segment>(seg, Ownership::SCORE) : nullptr;
     664              : }
     665              : 
     666              : //---------------------------------------------------------
     667              : //   element
     668              : //---------------------------------------------------------
     669              : 
     670            0 : EngravingItem* Cursor::element() const
     671              : {
     672            0 :     mu::engraving::EngravingItem* e = currentElement();
     673            0 :     if (!e) {
     674            0 :         return nullptr;
     675              :     }
     676            0 :     return wrap(e, Ownership::SCORE);
     677              : }
     678              : 
     679              : //---------------------------------------------------------
     680              : //   measure
     681              : //---------------------------------------------------------
     682              : 
     683            0 : Measure* Cursor::measure() const
     684              : {
     685            0 :     mu::engraving::Segment* seg = segment();
     686            0 :     return seg ? wrap<Measure>(seg->measure(), Ownership::SCORE) : nullptr;
     687              : }
     688              : 
     689              : //---------------------------------------------------------
     690              : //   track
     691              : //---------------------------------------------------------
     692              : 
     693           11 : int Cursor::track() const
     694              : {
     695           11 :     return static_cast<int>(inputState().track());
     696              : }
     697              : 
     698              : //---------------------------------------------------------
     699              : //   setTrack
     700              : //---------------------------------------------------------
     701              : 
     702            0 : void Cursor::setTrack(int track)
     703              : {
     704            0 :     if (m_score->ntracks() == 0) {
     705            0 :         return;
     706              :     }
     707            0 :     int m_track = std::clamp(track, 0, int(m_score->ntracks() - 1));
     708            0 :     inputState().setTrack(static_cast<track_idx_t>(m_track));
     709              : }
     710              : 
     711              : //---------------------------------------------------------
     712              : //   voice
     713              : //---------------------------------------------------------
     714              : 
     715            0 : int Cursor::voice() const
     716              : {
     717            0 :     return track() % VOICES;
     718              : }
     719              : 
     720              : //---------------------------------------------------------
     721              : //   setVoice
     722              : //---------------------------------------------------------
     723              : 
     724            0 : void Cursor::setVoice(int v)
     725              : {
     726            0 :     int m_track = (track() / VOICES) * VOICES + v;
     727            0 :     setTrack(m_track);
     728            0 : }
     729              : 
     730              : //---------------------------------------------------------
     731              : //   staffIdx
     732              : //---------------------------------------------------------
     733              : 
     734            0 : int Cursor::staffIdx() const
     735              : {
     736            0 :     return track() / VOICES;
     737              : }
     738              : 
     739              : //---------------------------------------------------------
     740              : //   staff
     741              : //---------------------------------------------------------
     742              : 
     743            0 : Staff* Cursor::staff() const
     744              : {
     745            0 :     return wrap<Staff>(inputState().staff(), Ownership::SCORE);
     746              : }
     747              : 
     748              : //---------------------------------------------------------
     749              : //   setStaffIdx
     750              : //---------------------------------------------------------
     751              : 
     752            0 : void Cursor::setStaffIdx(int v)
     753              : {
     754            0 :     int m_track = v * VOICES + track() % VOICES;
     755            0 :     setTrack(m_track);
     756            0 : }
     757              : 
     758              : //---------------------------------------------------------
     759              : //   setStaff
     760              : ///   \brief Positions the cursor on a specific \ref Staff.
     761              : ///   \details As opposed to \ref cursor.setStaffIdx, this
     762              : ///   method is used to set the position of the cursor to
     763              : ///   a specific \ref Staff object (as obtained e.g. through
     764              : ///   \ref element.staff).
     765              : ///   \since MuseScore 4.6
     766              : ///   \see \ref Staff
     767              : //---------------------------------------------------------
     768              : 
     769            0 : void Cursor::setStaff(Staff* s)
     770              : {
     771            0 :     mu::engraving::Staff* staff = s->staff();
     772            0 :     if (!staff || staff == inputState().staff()) {
     773            0 :         return;
     774              :     }
     775              : 
     776            0 :     int m_track = int(staff->idx()) + track() % VOICES;
     777            0 :     setTrack(m_track);
     778              : }
     779              : 
     780              : //---------------------------------------------------------
     781              : //   segment
     782              : //---------------------------------------------------------
     783              : 
     784           72 : mu::engraving::Segment* Cursor::segment() const
     785              : {
     786           72 :     return inputState().segment();
     787              : }
     788              : 
     789              : //---------------------------------------------------------
     790              : //   setSegment
     791              : //---------------------------------------------------------
     792              : 
     793           22 : void Cursor::setSegment(mu::engraving::Segment* seg)
     794              : {
     795           22 :     inputState().setSegment(seg);
     796           22 : }
     797              : 
     798              : //---------------------------------------------------------
     799              : //   prevInTrack
     800              : //    go to first segment before _segment which has notes / rests in m_track
     801              : //---------------------------------------------------------
     802              : 
     803            0 : void Cursor::prevInTrack()
     804              : {
     805            0 :     const int t = track();
     806            0 :     mu::engraving::Segment* seg = segment();
     807            0 :     if (seg) {
     808            0 :         seg = seg->prev1(m_filter);
     809              :     }
     810            0 :     while (seg && !seg->element(t)) {
     811            0 :         seg = seg->prev1(m_filter);
     812              :     }
     813            0 :     setSegment(seg);
     814            0 : }
     815              : 
     816              : //---------------------------------------------------------
     817              : //   nextInTrack
     818              : //    go to first segment at or after _segment which has notes / rests in m_track
     819              : //---------------------------------------------------------
     820              : 
     821           11 : void Cursor::nextInTrack()
     822              : {
     823           11 :     const int t = track();
     824           11 :     mu::engraving::Segment* seg = segment();
     825           11 :     while (seg && !seg->element(t)) {
     826            0 :         seg = seg->next1(m_filter);
     827              :     }
     828           11 :     setSegment(seg);
     829           11 : }
     830              : 
     831              : //---------------------------------------------------------
     832              : //   qmlKeySignature
     833              : //   read access to key signature in current track
     834              : //   at current position
     835              : //---------------------------------------------------------
     836              : 
     837            0 : int Cursor::qmlKeySignature()
     838              : {
     839            0 :     return static_cast<int>(inputState().staff()->key(fraction()));
     840              : }
     841              : 
     842              : //---------------------------------------------------------
     843              : //   inputStateString
     844              : //---------------------------------------------------------
     845              : 
     846            0 : int Cursor::inputStateString() const
     847              : {
     848            0 :     const InputState& istate = inputState();
     849            0 :     return istate.staff()->staffType(istate.tick())->visualStringToPhys(istate.string());
     850              : }
     851              : 
     852            0 : void Cursor::setInputStateString(int string)
     853              : {
     854            0 :     InputState& istate = inputState();
     855            0 :     const int visString = istate.staff()->staffType(istate.tick())->visualStringToPhys(string);
     856            0 :     istate.setString(visString);
     857            0 : }
        

Generated by: LCOV version 2.0-1