MuseScore/libmscore/navigate.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

841 lines
24 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
2016-08-24 14:49:34 +02:00
2012-05-26 14:26:10 +02:00
#include "navigate.h"
#include "element.h"
#include "clef.h"
#include "score.h"
#include "note.h"
#include "rest.h"
#include "chord.h"
#include "system.h"
#include "segment.h"
#include "harmony.h"
#include "utils.h"
#include "input.h"
#include "measure.h"
#include "page.h"
#include "spanner.h"
#include "system.h"
#include "staff.h"
#include "barline.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// nextChordRest
// return next Chord or Rest
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
ChordRest* nextChordRest(ChordRest* cr, bool skipGrace)
2012-05-26 14:26:10 +02:00
{
if (!cr) {
return 0;
}
2020-05-26 15:54:26 +02:00
if (cr->isGrace()) {
2014-08-17 20:11:39 +02:00
//
// cr is a grace note
2020-05-26 15:54:26 +02:00
2016-10-20 11:32:07 +02:00
Chord* c = toChord(cr);
2016-02-06 22:03:43 +01:00
Chord* pc = toChord(cr->parent());
2020-05-26 15:54:26 +02:00
2016-02-06 22:03:43 +01:00
if (skipGrace) {
2016-10-20 11:32:07 +02:00
cr = toChordRest(cr->parent());
} else if (cr->isGraceBefore()) {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = pc->graceNotesBefore();
auto i = std::find(cl.begin(), cl.end(), c);
if (i == cl.end()) {
2015-02-19 10:28:25 +01:00
return 0; // unable to find self?
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
++i;
2015-02-19 10:28:25 +01:00
if (i != cl.end()) {
2014-04-23 18:07:38 +02:00
return *i;
2020-05-26 15:54:26 +02:00
}
2014-08-17 20:11:39 +02:00
// if this was last grace note before, return parent
return pc;
2020-05-26 15:54:26 +02:00
} else {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = pc->graceNotesAfter();
2015-02-19 10:28:25 +01:00
auto i = std::find(cl.begin(), cl.end(), c);
if (i == cl.end()) {
2014-08-17 20:11:39 +02:00
return 0; // unable to find self?
2020-05-26 15:54:26 +02:00
}
++i;
2015-02-19 10:28:25 +01:00
if (i != cl.end()) {
2014-08-17 20:11:39 +02:00
return *i;
2020-05-26 15:54:26 +02:00
}
2014-08-17 20:11:39 +02:00
// if this was last grace note after, fall through to find next main note
2020-05-26 15:54:26 +02:00
cr = pc;
}
} else {
//
2014-08-17 20:11:39 +02:00
// cr is not a grace note
2016-10-20 11:32:07 +02:00
if (cr->isChord() && !skipGrace) {
Chord* c = toChord(cr);
2014-08-17 20:11:39 +02:00
if (!c->graceNotes().empty()) {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = c->graceNotesAfter();
2014-08-17 20:11:39 +02:00
if (!cl.empty()) {
2015-02-19 10:28:25 +01:00
return cl.first();
2020-05-26 15:54:26 +02:00
}
}
}
}
int track = cr->track();
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
2020-05-26 15:54:26 +02:00
2014-11-22 11:46:47 +01:00
for (Segment* seg = cr->segment()->next1MM(st); seg; seg = seg->next1MM(st)) {
2016-10-20 11:32:07 +02:00
ChordRest* e = toChordRest(seg->element(track));
2020-05-26 15:54:26 +02:00
if (e) {
2016-10-20 11:32:07 +02:00
if (e->isChord() && !skipGrace) {
2017-12-20 16:49:30 +01:00
Chord* c = toChord(e);
2014-08-17 20:11:39 +02:00
if (!c->graceNotes().empty()) {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = c->graceNotesBefore();
2014-08-17 20:11:39 +02:00
if (!cl.empty()) {
2015-02-19 10:28:25 +01:00
return cl.first();
2020-05-26 15:54:26 +02:00
}
}
}
return e;
2020-05-26 15:54:26 +02:00
}
}
2014-08-17 20:11:39 +02:00
2012-05-26 14:26:10 +02:00
return 0;
}
//---------------------------------------------------------
// prevChordRest
// return previous Chord or Rest
// if grace is true, include grace notes
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
ChordRest* prevChordRest(ChordRest* cr, bool skipGrace)
2012-05-26 14:26:10 +02:00
{
if (!cr) {
return 0;
}
2020-05-26 15:54:26 +02:00
if (cr->isGrace()) {
2014-08-17 20:11:39 +02:00
//
// cr is a grace note
2020-05-26 15:54:26 +02:00
2014-08-17 20:11:39 +02:00
Chord* c = toChord(cr);
2016-02-06 22:03:43 +01:00
Chord* pc = toChord(cr->parent());
2020-05-26 15:54:26 +02:00
2016-02-06 22:03:43 +01:00
if (skipGrace) {
2015-02-19 10:28:25 +01:00
cr = toChordRest(cr->parent());
2016-10-20 11:32:07 +02:00
} else if (cr->isGraceBefore()) {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = pc->graceNotesBefore();
2014-11-25 16:53:43 +01:00
auto i = std::find(cl.begin(), cl.end(), c);
2017-01-18 14:16:33 +01:00
if (i == cl.end()) {
return 0; // unable to find self?
2020-05-26 15:54:26 +02:00
}
2016-02-06 22:03:43 +01:00
if (i != cl.begin()) {
return *--i;
2012-05-26 14:26:10 +02:00
}
2014-08-17 20:11:39 +02:00
// if this was first grace note before, fall through to find previous main note
2020-05-26 15:54:26 +02:00
cr = pc;
} else {
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = pc->graceNotesAfter();
2015-02-19 10:28:25 +01:00
auto i = std::find(cl.begin(), cl.end(), c);
if (i == cl.end()) {
2014-08-17 20:11:39 +02:00
return 0; // unable to find self?
2020-05-26 15:54:26 +02:00
}
2015-02-19 10:28:25 +01:00
if (i != cl.begin()) {
2014-08-17 20:11:39 +02:00
return *--i;
2020-05-26 15:54:26 +02:00
}
2014-08-17 20:11:39 +02:00
// if this was first grace note after, return parent
return pc;
2020-05-26 15:54:26 +02:00
}
} else {
//
2014-08-17 20:11:39 +02:00
// cr is not a grace note
2016-10-20 11:32:07 +02:00
if (cr->isChord() && !skipGrace) {
Chord* c = toChord(cr);
2016-02-06 22:03:43 +01:00
QVector<Chord*> cl = c->graceNotesBefore();
if (!cl.empty()) {
2015-02-19 10:28:25 +01:00
return cl.last();
2020-05-26 15:54:26 +02:00
}
}
}
2012-05-26 14:26:10 +02:00
int track = cr->track();
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
2014-11-25 16:53:43 +01:00
for (Segment* seg = cr->segment()->prev1MM(st); seg; seg = seg->prev1MM(st)) {
2016-10-20 11:32:07 +02:00
ChordRest* e = toChordRest(seg->element(track));
2020-05-26 15:54:26 +02:00
if (e) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::CHORD && !skipGrace) {
2016-10-20 11:32:07 +02:00
QVector<Chord*> cl = toChord(e)->graceNotesAfter();
2016-02-06 22:03:43 +01:00
if (!cl.empty()) {
2015-02-19 10:28:25 +01:00
return cl.last();
2020-05-26 15:54:26 +02:00
}
}
return e;
2020-05-26 15:54:26 +02:00
}
}
2014-08-17 20:11:39 +02:00
2012-05-26 14:26:10 +02:00
return 0;
}
//---------------------------------------------------------
// upAlt
2013-08-06 14:48:38 +02:00
// element: Note() or Rest()
// return: Note() or Rest()
//
// return next higher pitched note in chord
// move to previous track if at top of chord
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-08-06 14:48:38 +02:00
Element* Score::upAlt(Element* element)
2012-05-26 14:26:10 +02:00
{
Element* re = 0;
2016-02-06 22:03:43 +01:00
if (element->isRest()) {
2016-10-20 11:32:07 +02:00
re = prevTrack(toRest(element));
2016-02-06 22:03:43 +01:00
} else if (element->isNote()) {
2016-02-17 14:54:23 +01:00
Note* note = toNote(element);
2016-02-06 22:03:43 +01:00
Chord* chord = note->chord();
const std::vector<Note*>& notes = chord->notes();
auto i = std::find(notes.begin(), notes.end(), note);
++i;
if (i != notes.end()) {
re = *i;
} else {
re = prevTrack(chord);
if (re->track() == chord->track()) {
re = element;
2020-05-26 15:54:26 +02:00
}
}
2012-05-26 14:26:10 +02:00
}
if (re == 0) {
return 0;
2020-05-26 15:54:26 +02:00
}
2016-10-20 11:32:07 +02:00
if (re->isChord()) {
re = toChord(re)->notes().front();
2020-05-26 15:54:26 +02:00
}
2013-08-06 14:48:38 +02:00
return re;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// upAltCtrl
// select top note in chord
//---------------------------------------------------------
Note* Score::upAltCtrl(Note* note) const
{
return note->chord()->upNote();
}
//---------------------------------------------------------
// downAlt
// return next lower pitched note in chord
Fixes #19155, #22861 (duplicate of the former) and #23100. __References__: Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100 __Description__: Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently. The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected: - `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note. - `[Shift]+[Right]` snaps to the next chord, defaulting to its top note. - `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one). - `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one). This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on). It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff. __Known limitations__: - The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses. __Notes__: - Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line. - When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff. - The change of anchor is undoable. - The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists). - The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
// move to next track if at bottom of chord
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-08-06 14:48:38 +02:00
Element* Score::downAlt(Element* element)
2012-05-26 14:26:10 +02:00
{
Element* re = 0;
2016-02-06 22:03:43 +01:00
if (element->isRest()) {
2016-10-20 11:32:07 +02:00
re = nextTrack(toRest(element));
2016-02-06 22:03:43 +01:00
} else if (element->isNote()) {
2016-02-17 14:54:23 +01:00
Note* note = toNote(element);
2016-02-06 22:03:43 +01:00
Chord* chord = note->chord();
const std::vector<Note*>& notes = chord->notes();
auto i = std::find(notes.begin(), notes.end(), note);
if (i != notes.begin()) {
--i;
re = *i;
} else {
re = nextTrack(chord);
if (re->track() == chord->track()) {
re = element;
2020-05-26 15:54:26 +02:00
}
}
2012-05-26 14:26:10 +02:00
}
if (re == 0) {
return 0;
2020-05-26 15:54:26 +02:00
}
2016-10-20 11:32:07 +02:00
if (re->isChord()) {
re = toChord(re)->notes().back();
2020-05-26 15:54:26 +02:00
}
2013-08-06 14:48:38 +02:00
return re;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// downAltCtrl
// niedrigste Note in Chord selektieren
//---------------------------------------------------------
Note* Score::downAltCtrl(Note* note) const
{
return note->chord()->downNote();
}
//---------------------------------------------------------
// firstElement
//---------------------------------------------------------
Element* Score::firstElement(bool frame)
{
if (frame) {
MeasureBase* mb = measures()->first();
if (mb && mb->isBox()) {
return mb;
2020-05-26 15:54:26 +02:00
}
}
Segment* s = firstSegmentMM(SegmentType::All);
return s ? s->element(0) : nullptr;
}
//---------------------------------------------------------
// lastElement
//---------------------------------------------------------
Element* Score::lastElement(bool frame)
{
if (frame) {
MeasureBase* mb = measures()->last();
if (mb && mb->isBox()) {
return mb;
}
2020-05-26 15:54:26 +02:00
}
Element* re = 0;
Segment* seg = lastSegmentMM();
if (!seg) {
return nullptr;
2020-05-26 15:54:26 +02:00
}
while (true) {
2016-10-20 11:32:07 +02:00
for (int i = (staves().size() - 1) * VOICES; i < staves().size() * VOICES; i++) {
if (seg->element(i)) {
re = seg->element(i);
}
}
2016-10-20 11:32:07 +02:00
if (re) {
if (re->isChord()) {
return toChord(re)->notes().front();
2020-05-26 15:54:26 +02:00
}
return re;
2020-05-26 15:54:26 +02:00
}
2017-03-08 13:12:26 +01:00
seg = seg->prev1MM(SegmentType::All);
2020-05-26 15:54:26 +02:00
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// upStaff
//---------------------------------------------------------
ChordRest* Score::upStaff(ChordRest* cr)
{
Segment* segment = cr->segment();
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
if (cr->staffIdx() == 0) {
return cr;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
for (int track = (cr->staffIdx() - 1) * VOICES; track >= 0; --track) {
Element* el = segment->element(track);
if (!el) {
continue;
2020-05-26 15:54:26 +02:00
}
2016-10-20 11:32:07 +02:00
if (el->isNote()) {
el = toNote(el)->chord();
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
if (el->isChordRest()) {
2016-10-20 11:32:07 +02:00
return toChordRest(el);
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
return 0;
}
//---------------------------------------------------------
// downStaff
//---------------------------------------------------------
ChordRest* Score::downStaff(ChordRest* cr)
{
Segment* segment = cr->segment();
int tracks = nstaves() * VOICES;
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
if (cr->staffIdx() == nstaves() - 1) {
return cr;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
for (int track = (cr->staffIdx() + 1) * VOICES; track < tracks; --track) {
Element* el = segment->element(track);
if (!el) {
continue;
2020-05-26 15:54:26 +02:00
}
2016-10-20 11:32:07 +02:00
if (el->isNote()) {
el = toNote(el)->chord();
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
if (el->isChordRest()) {
2016-10-20 11:32:07 +02:00
return toChordRest(el);
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
return 0;
}
//---------------------------------------------------------
// nextTrack
// returns note at or just before current (cr) position
// in next track for this measure
// that contains such an element
//---------------------------------------------------------
ChordRest* Score::nextTrack(ChordRest* cr)
{
if (!cr) {
return 0;
2020-05-26 15:54:26 +02:00
}
2013-08-06 14:48:38 +02:00
ChordRest* el = 0;
Measure* measure = cr->measure();
int track = cr->track();
int tracks = nstaves() * VOICES;
2020-05-26 15:54:26 +02:00
while (!el) {
// find next non-empty track
2016-10-20 11:32:07 +02:00
while (++track < tracks) {
if (measure->hasVoice(track)) {
break;
2020-05-26 15:54:26 +02:00
}
}
// no more tracks, return original element
if (track == tracks) {
return cr;
2020-05-26 15:54:26 +02:00
}
// find element at same or previous segment within this track
2017-03-08 13:12:26 +01:00
for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
2016-10-20 11:32:07 +02:00
el = toChordRest(segment->element(track));
if (el) {
break;
}
2020-05-26 15:54:26 +02:00
}
}
2013-08-06 14:48:38 +02:00
return el;
}
//---------------------------------------------------------
// prevTrack
// returns ChordRest at or just before current (cr) position
// in previous track for this measure
// that contains such an element
//---------------------------------------------------------
ChordRest* Score::prevTrack(ChordRest* cr)
{
if (!cr) {
return 0;
2020-05-26 15:54:26 +02:00
}
2013-08-06 14:48:38 +02:00
ChordRest* el = 0;
Measure* measure = cr->measure();
int track = cr->track();
2020-05-26 15:54:26 +02:00
while (!el) {
// find next non-empty track
while (--track >= 0) {
if (measure->hasVoice(track)) {
break;
2020-05-26 15:54:26 +02:00
}
}
// no more tracks, return original element
if (track < 0) {
return cr;
2020-05-26 15:54:26 +02:00
}
// find element at same or previous segment within this track
2017-03-08 13:12:26 +01:00
for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
2016-10-20 11:32:07 +02:00
el = toChordRest(segment->element(track));
if (el) {
break;
}
2020-05-26 15:54:26 +02:00
}
}
2013-08-06 14:48:38 +02:00
return el;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// nextMeasure
//---------------------------------------------------------
2014-11-25 16:53:43 +01:00
ChordRest* Score::nextMeasure(ChordRest* element, bool selectBehavior, bool mmRest)
2012-05-26 14:26:10 +02:00
{
if (!element) {
return 0;
2020-05-26 15:54:26 +02:00
}
2014-11-25 16:53:43 +01:00
Measure* measure = 0;
if (mmRest) {
measure = element->measure()->nextMeasureMM();
} else {
measure = element->measure()->nextMeasure();
2020-05-26 15:54:26 +02:00
}
2016-10-20 11:32:07 +02:00
if (!measure) {
2013-08-06 14:48:38 +02:00
return 0;
2020-05-26 15:54:26 +02:00
}
Fraction endTick = element->measure()->last()->nextChordRest(element->track(), true)->tick();
2013-08-06 14:48:38 +02:00
bool last = false;
2020-05-26 15:54:26 +02:00
2014-05-24 12:53:50 +02:00
if (selection().isRange()) {
2012-05-26 14:26:10 +02:00
if (element->tick() != endTick && selection().tickEnd() <= endTick) {
measure = element->measure();
last = true;
} else if (element->tick() == endTick && selection().isEndActive()) {
last = true;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
} else if (element->tick() != endTick && selectBehavior) {
2014-11-25 16:53:43 +01:00
measure = element->measure();
2012-05-26 14:26:10 +02:00
last = true;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
if (!measure) {
2014-11-25 16:53:43 +01:00
measure = element->measure();
2012-05-26 14:26:10 +02:00
last = true;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
int staff = element->staffIdx();
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
Segment* startSeg = last ? measure->last() : measure->first();
for (Segment* seg = startSeg; seg; seg = last ? seg->prev() : seg->next()) {
int etrack = (staff + 1) * VOICES;
for (int track = staff * VOICES; track < etrack; ++track) {
Element* pel = seg->element(track);
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
if (pel && pel->isChordRest()) {
2016-10-20 11:32:07 +02:00
return toChordRest(pel);
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
}
return 0;
}
//---------------------------------------------------------
// prevMeasure
//---------------------------------------------------------
2014-11-25 16:53:43 +01:00
ChordRest* Score::prevMeasure(ChordRest* element, bool mmRest)
2012-05-26 14:26:10 +02:00
{
if (!element) {
return 0;
2020-05-26 15:54:26 +02:00
}
2014-11-25 16:53:43 +01:00
Measure* measure = 0;
if (mmRest) {
measure = element->measure()->prevMeasureMM();
} else {
measure = element->measure()->prevMeasure();
2020-05-26 15:54:26 +02:00
}
Fraction startTick = element->measure()->first()->nextChordRest(element->track())->tick();
2012-05-26 14:26:10 +02:00
bool last = false;
2020-05-26 15:54:26 +02:00
2016-10-20 11:32:07 +02:00
if (selection().isRange() && selection().isEndActive() && selection().startSegment()->tick() <= startTick) {
2012-05-26 14:26:10 +02:00
last = true;
} else if (element->tick() != startTick) {
measure = element->measure();
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
if (!measure) {
measure = element->measure();
last = false;
}
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
int staff = element->staffIdx();
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
Segment* startSeg = last ? measure->last() : measure->first();
for (Segment* seg = startSeg; seg; seg = last ? seg->prev() : seg->next()) {
int etrack = (staff + 1) * VOICES;
for (int track = staff * VOICES; track < etrack; ++track) {
Element* pel = seg->element(track);
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
if (pel && pel->isChordRest()) {
2016-10-20 11:32:07 +02:00
return toChordRest(pel);
2012-05-26 14:26:10 +02:00
}
}
}
return 0;
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* Score::nextElement()
{
Element* e = getSelectedElement();
if (!e) {
return nullptr;
2020-05-26 15:54:26 +02:00
}
int staffId = e->staffIdx();
while (e) {
switch (e->type()) {
case ElementType::NOTE:
case ElementType::REST:
case ElementType::CHORD: {
Element* next = e->nextElement();
if (next) {
return next;
} else {
break;
}
2020-05-26 15:54:26 +02:00
}
case ElementType::SEGMENT: {
2017-12-20 16:49:30 +01:00
Segment* s = toSegment(e);
Element* next = s->nextElement(staffId);
if (next) {
return next;
} else {
break;
}
2020-05-26 15:54:26 +02:00
}
case ElementType::MEASURE: {
2017-12-20 16:49:30 +01:00
Measure* m = toMeasure(e);
Element* next = m->nextElementStaff(staffId);
if (next) {
return next;
} else {
break;
}
2020-05-26 15:54:26 +02:00
}
2017-12-20 16:49:30 +01:00
case ElementType::CLEF:
case ElementType::KEYSIG:
case ElementType::TIMESIG:
case ElementType::BAR_LINE: {
for (; e && e->type() != ElementType::SEGMENT; e = e->parent()) {
}
2017-12-20 16:49:30 +01:00
Segment* s = toSegment(e);
Element* next = s->nextElement(staffId);
if (next) {
return next;
} else {
return score()->firstElement();
2020-05-26 15:54:26 +02:00
}
}
#if 1
case ElementType::VOLTA_SEGMENT:
#else
case ElementType::VOLTA_SEGMENT: {
// TODO: see Spanner::nextSpanner()
System* sys = toSpannerSegment(e)->system();
if (sys) {
staffId = sys->firstVisibleStaff();
2020-05-26 15:54:26 +02:00
}
}
// fall through
#endif
case ElementType::SLUR_SEGMENT:
case ElementType::TEXTLINE_SEGMENT:
case ElementType::HAIRPIN_SEGMENT:
case ElementType::OTTAVA_SEGMENT:
case ElementType::TRILL_SEGMENT:
case ElementType::VIBRATO_SEGMENT:
case ElementType::LET_RING_SEGMENT:
case ElementType::PALM_MUTE_SEGMENT:
case ElementType::PEDAL_SEGMENT: {
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
Spanner* nextSp = sp->nextSpanner(sp, staffId);
if (nextSp) {
return nextSp->spannerSegments().front();
}
2020-05-26 15:54:26 +02:00
Segment* seg = tick2segment(sp->tick());
if (seg) {
Segment* nextSegment = seg->next1();
while (nextSegment) {
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, staffId);
if (nextEl) {
return nextEl;
2020-05-26 15:54:26 +02:00
}
nextSegment = nextSegment->next1MM();
2020-05-26 15:54:26 +02:00
}
}
break;
}
case ElementType::GLISSANDO_SEGMENT:
case ElementType::TIE_SEGMENT: {
2017-12-20 16:49:30 +01:00
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
Element* elSt = sp->startElement();
Note* n = toNote(elSt);
Element* next = n->nextElement();
if (next) {
return next;
2020-05-26 15:54:26 +02:00
} else {
break;
}
}
case ElementType::VBOX:
case ElementType::HBOX:
case ElementType::TBOX: {
MeasureBase* mb = toMeasureBase(e)->nextMM();
if (!mb) {
2020-05-26 15:54:26 +02:00
break;
} else if (mb->isMeasure()) {
ChordRest* cr = selection().currentCR();
int si = cr ? cr->staffIdx() : 0;
return toMeasure(mb)->nextElementStaff(si);
2020-05-26 15:54:26 +02:00
} else {
return mb;
2020-05-26 15:54:26 +02:00
}
}
case ElementType::LAYOUT_BREAK: {
staffId = 0; // otherwise it will equal -1, which breaks the navigation
2020-05-26 15:54:26 +02:00
}
default:
break;
}
e = e->parent();
2020-05-26 15:54:26 +02:00
}
return score()->lastElement();
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* Score::prevElement()
{
Element* e = getSelectedElement();
if (!e) {
return nullptr;
2020-05-26 15:54:26 +02:00
}
int staffId = e->staffIdx();
while (e) {
switch (e->type()) {
case ElementType::NOTE:
case ElementType::REST:
case ElementType::CHORD: {
Element* prev = e->prevElement();
if (prev) {
return prev;
} else {
break;
}
2020-05-26 15:54:26 +02:00
}
case ElementType::SEGMENT: {
2017-12-20 16:49:30 +01:00
Segment* s = toSegment(e);
Element* prev = s->prevElement(staffId);
if (prev) {
return prev;
} else {
break;
}
2020-05-26 15:54:26 +02:00
}
case ElementType::MEASURE: {
2017-12-20 16:49:30 +01:00
Measure* m = toMeasure(e);
return m->prevElementStaff(staffId);
}
case ElementType::CLEF:
case ElementType::KEYSIG:
case ElementType::TIMESIG:
2017-12-20 16:49:30 +01:00
case ElementType::BAR_LINE: {
for (; e && e->type() != ElementType::SEGMENT; e = e->parent()) {
}
2017-12-20 16:49:30 +01:00
Segment* s = toSegment(e);
return s->prevElement(staffId);
}
#if 1
case ElementType::VOLTA_SEGMENT:
#else
case ElementType::VOLTA_SEGMENT: {
// TODO: see Spanner::nextSpanner()
System* sys = toSpannerSegment(e)->system();
if (sys) {
staffId = sys->firstVisibleStaff();
2020-05-26 15:54:26 +02:00
}
}
// fall through
#endif
case ElementType::SLUR_SEGMENT:
case ElementType::TEXTLINE_SEGMENT:
case ElementType::HAIRPIN_SEGMENT:
case ElementType::OTTAVA_SEGMENT:
case ElementType::TRILL_SEGMENT:
case ElementType::VIBRATO_SEGMENT:
case ElementType::PEDAL_SEGMENT: {
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
Element* stEl = sp->startElement();
Spanner* prevSp = sp->prevSpanner(sp, staffId);
if (prevSp) {
2017-12-20 16:49:30 +01:00
return prevSp->spannerSegments().front();
} else {
Segment* startSeg = sp->startSegment();
if (!startSeg->annotations().empty()) {
Element* last = startSeg->lastAnnotation(startSeg, staffId);
if (last) {
return last;
2020-05-26 15:54:26 +02:00
}
}
Element* el = startSeg->lastElementOfSegment(startSeg, staffId);
if (stEl->type() == ElementType::CHORD || stEl->type() == ElementType::REST
|| stEl->type() == ElementType::REPEAT_MEASURE || stEl->type() == ElementType::NOTE) {
ChordRest* cr = startSeg->cr(stEl->track());
if (cr) {
Element* elCr = cr->lastElementBeforeSegment();
if (elCr) {
return elCr;
}
2020-05-26 15:54:26 +02:00
}
}
2017-12-20 16:49:30 +01:00
if (el->isChord()) {
return toChord(el)->lastElementBeforeSegment();
} else if (el->isNote()) {
2017-12-20 16:49:30 +01:00
Chord* c = toNote(el)->chord();
return c->lastElementBeforeSegment();
} else {
return el;
}
}
2020-05-26 15:54:26 +02:00
}
case ElementType::GLISSANDO_SEGMENT:
case ElementType::TIE_SEGMENT: {
2017-12-20 16:49:30 +01:00
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
2017-12-20 16:49:30 +01:00
Element* elSt = sp->startElement();
Q_ASSERT(elSt->type() == ElementType::NOTE);
2017-12-20 16:49:30 +01:00
Note* n = toNote(elSt);
Element* prev = n->prevElement();
2020-05-26 15:54:26 +02:00
if (prev) {
return prev;
2020-05-26 15:54:26 +02:00
} else {
break;
}
}
case ElementType::VBOX:
case ElementType::HBOX:
case ElementType::TBOX: {
MeasureBase* mb = toMeasureBase(e)->prevMM();
if (!mb) {
2020-05-26 15:54:26 +02:00
break;
} else if (mb->isMeasure()) {
ChordRest* cr = selection().currentCR();
int si = cr ? cr->staffIdx() : 0;
2012-05-26 14:26:10 +02:00
Segment* s = toMeasure(mb)->last();
2020-05-26 15:54:26 +02:00
if (s) {
return s->lastElement(si);
2020-05-26 15:54:26 +02:00
}
} else {
return mb;
2020-05-26 15:54:26 +02:00
}
}
break;
case ElementType::LAYOUT_BREAK: {
staffId = 0; // otherwise it will equal -1, which breaks the navigation
2020-05-26 15:54:26 +02:00
}
default:
break;
}
e = e->parent();
2020-05-26 15:54:26 +02:00
}
return score()->firstElement();
}
2013-05-13 18:49:17 +02:00
}