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