840 lines
24 KiB
C++
840 lines
24 KiB
C++
//=============================================================================
|
|
// 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
|
|
//=============================================================================
|
|
|
|
#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"
|
|
|
|
namespace Ms {
|
|
//---------------------------------------------------------
|
|
// nextChordRest
|
|
// return next Chord or Rest
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* nextChordRest(ChordRest* cr, bool skipGrace)
|
|
{
|
|
if (!cr) {
|
|
return 0;
|
|
}
|
|
|
|
if (cr->isGrace()) {
|
|
//
|
|
// cr is a grace note
|
|
|
|
Chord* c = toChord(cr);
|
|
Chord* pc = toChord(cr->parent());
|
|
|
|
if (skipGrace) {
|
|
cr = toChordRest(cr->parent());
|
|
} else if (cr->isGraceBefore()) {
|
|
QVector<Chord*> cl = pc->graceNotesBefore();
|
|
auto i = std::find(cl.begin(), cl.end(), c);
|
|
if (i == cl.end()) {
|
|
return 0; // unable to find self?
|
|
}
|
|
++i;
|
|
if (i != cl.end()) {
|
|
return *i;
|
|
}
|
|
// if this was last grace note before, return parent
|
|
return pc;
|
|
} else {
|
|
QVector<Chord*> cl = pc->graceNotesAfter();
|
|
auto i = std::find(cl.begin(), cl.end(), c);
|
|
if (i == cl.end()) {
|
|
return 0; // unable to find self?
|
|
}
|
|
++i;
|
|
if (i != cl.end()) {
|
|
return *i;
|
|
}
|
|
// if this was last grace note after, fall through to find next main note
|
|
cr = pc;
|
|
}
|
|
} else {
|
|
//
|
|
// cr is not a grace note
|
|
if (cr->isChord() && !skipGrace) {
|
|
Chord* c = toChord(cr);
|
|
if (!c->graceNotes().empty()) {
|
|
QVector<Chord*> cl = c->graceNotesAfter();
|
|
if (!cl.empty()) {
|
|
return cl.first();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int track = cr->track();
|
|
SegmentType st = SegmentType::ChordRest;
|
|
|
|
for (Segment* seg = cr->segment()->next1MM(st); seg; seg = seg->next1MM(st)) {
|
|
ChordRest* e = toChordRest(seg->element(track));
|
|
if (e) {
|
|
if (e->isChord() && !skipGrace) {
|
|
Chord* c = toChord(e);
|
|
if (!c->graceNotes().empty()) {
|
|
QVector<Chord*> cl = c->graceNotesBefore();
|
|
if (!cl.empty()) {
|
|
return cl.first();
|
|
}
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// prevChordRest
|
|
// return previous Chord or Rest
|
|
// if grace is true, include grace notes
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* prevChordRest(ChordRest* cr, bool skipGrace)
|
|
{
|
|
if (!cr) {
|
|
return 0;
|
|
}
|
|
|
|
if (cr->isGrace()) {
|
|
//
|
|
// cr is a grace note
|
|
|
|
Chord* c = toChord(cr);
|
|
Chord* pc = toChord(cr->parent());
|
|
|
|
if (skipGrace) {
|
|
cr = toChordRest(cr->parent());
|
|
} else if (cr->isGraceBefore()) {
|
|
QVector<Chord*> cl = pc->graceNotesBefore();
|
|
auto i = std::find(cl.begin(), cl.end(), c);
|
|
if (i == cl.end()) {
|
|
return 0; // unable to find self?
|
|
}
|
|
if (i != cl.begin()) {
|
|
return *--i;
|
|
}
|
|
// if this was first grace note before, fall through to find previous main note
|
|
cr = pc;
|
|
} else {
|
|
QVector<Chord*> cl = pc->graceNotesAfter();
|
|
auto i = std::find(cl.begin(), cl.end(), c);
|
|
if (i == cl.end()) {
|
|
return 0; // unable to find self?
|
|
}
|
|
if (i != cl.begin()) {
|
|
return *--i;
|
|
}
|
|
// if this was first grace note after, return parent
|
|
return pc;
|
|
}
|
|
} else {
|
|
//
|
|
// cr is not a grace note
|
|
if (cr->isChord() && !skipGrace) {
|
|
Chord* c = toChord(cr);
|
|
QVector<Chord*> cl = c->graceNotesBefore();
|
|
if (!cl.empty()) {
|
|
return cl.last();
|
|
}
|
|
}
|
|
}
|
|
|
|
int track = cr->track();
|
|
SegmentType st = SegmentType::ChordRest;
|
|
for (Segment* seg = cr->segment()->prev1MM(st); seg; seg = seg->prev1MM(st)) {
|
|
ChordRest* e = toChordRest(seg->element(track));
|
|
if (e) {
|
|
if (e->type() == ElementType::CHORD && !skipGrace) {
|
|
QVector<Chord*> cl = toChord(e)->graceNotesAfter();
|
|
if (!cl.empty()) {
|
|
return cl.last();
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// upAlt
|
|
// element: Note() or Rest()
|
|
// return: Note() or Rest()
|
|
//
|
|
// return next higher pitched note in chord
|
|
// move to previous track if at top of chord
|
|
//---------------------------------------------------------
|
|
|
|
Element* Score::upAlt(Element* element)
|
|
{
|
|
Element* re = 0;
|
|
if (element->isRest()) {
|
|
re = prevTrack(toRest(element));
|
|
} else if (element->isNote()) {
|
|
Note* note = toNote(element);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
if (re == 0) {
|
|
return 0;
|
|
}
|
|
if (re->isChord()) {
|
|
re = toChord(re)->notes().front();
|
|
}
|
|
return re;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// upAltCtrl
|
|
// select top note in chord
|
|
//---------------------------------------------------------
|
|
|
|
Note* Score::upAltCtrl(Note* note) const
|
|
{
|
|
return note->chord()->upNote();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// downAlt
|
|
// return next lower pitched note in chord
|
|
// move to next track if at bottom of chord
|
|
//---------------------------------------------------------
|
|
|
|
Element* Score::downAlt(Element* element)
|
|
{
|
|
Element* re = 0;
|
|
if (element->isRest()) {
|
|
re = nextTrack(toRest(element));
|
|
} else if (element->isNote()) {
|
|
Note* note = toNote(element);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
if (re == 0) {
|
|
return 0;
|
|
}
|
|
if (re->isChord()) {
|
|
re = toChord(re)->notes().back();
|
|
}
|
|
return re;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
Element* re = 0;
|
|
Segment* seg = lastSegmentMM();
|
|
if (!seg) {
|
|
return nullptr;
|
|
}
|
|
while (true) {
|
|
for (int i = (staves().size() - 1) * VOICES; i < staves().size() * VOICES; i++) {
|
|
if (seg->element(i)) {
|
|
re = seg->element(i);
|
|
}
|
|
}
|
|
if (re) {
|
|
if (re->isChord()) {
|
|
return toChord(re)->notes().front();
|
|
}
|
|
return re;
|
|
}
|
|
seg = seg->prev1MM(SegmentType::All);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// upStaff
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* Score::upStaff(ChordRest* cr)
|
|
{
|
|
Segment* segment = cr->segment();
|
|
|
|
if (cr->staffIdx() == 0) {
|
|
return cr;
|
|
}
|
|
|
|
for (int track = (cr->staffIdx() - 1) * VOICES; track >= 0; --track) {
|
|
Element* el = segment->element(track);
|
|
if (!el) {
|
|
continue;
|
|
}
|
|
if (el->isNote()) {
|
|
el = toNote(el)->chord();
|
|
}
|
|
if (el->isChordRest()) {
|
|
return toChordRest(el);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// downStaff
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* Score::downStaff(ChordRest* cr)
|
|
{
|
|
Segment* segment = cr->segment();
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
if (cr->staffIdx() == nstaves() - 1) {
|
|
return cr;
|
|
}
|
|
|
|
for (int track = (cr->staffIdx() + 1) * VOICES; track < tracks; --track) {
|
|
Element* el = segment->element(track);
|
|
if (!el) {
|
|
continue;
|
|
}
|
|
if (el->isNote()) {
|
|
el = toNote(el)->chord();
|
|
}
|
|
if (el->isChordRest()) {
|
|
return toChordRest(el);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
ChordRest* el = 0;
|
|
Measure* measure = cr->measure();
|
|
int track = cr->track();
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
while (!el) {
|
|
// find next non-empty track
|
|
while (++track < tracks) {
|
|
if (measure->hasVoice(track)) {
|
|
break;
|
|
}
|
|
}
|
|
// no more tracks, return original element
|
|
if (track == tracks) {
|
|
return cr;
|
|
}
|
|
// find element at same or previous segment within this track
|
|
for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
|
|
el = toChordRest(segment->element(track));
|
|
if (el) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
ChordRest* el = 0;
|
|
Measure* measure = cr->measure();
|
|
int track = cr->track();
|
|
|
|
while (!el) {
|
|
// find next non-empty track
|
|
while (--track >= 0) {
|
|
if (measure->hasVoice(track)) {
|
|
break;
|
|
}
|
|
}
|
|
// no more tracks, return original element
|
|
if (track < 0) {
|
|
return cr;
|
|
}
|
|
// find element at same or previous segment within this track
|
|
for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
|
|
el = toChordRest(segment->element(track));
|
|
if (el) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return el;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// nextMeasure
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* Score::nextMeasure(ChordRest* element, bool selectBehavior, bool mmRest)
|
|
{
|
|
if (!element) {
|
|
return 0;
|
|
}
|
|
|
|
Measure* measure = 0;
|
|
if (mmRest) {
|
|
measure = element->measure()->nextMeasureMM();
|
|
} else {
|
|
measure = element->measure()->nextMeasure();
|
|
}
|
|
|
|
if (!measure) {
|
|
return 0;
|
|
}
|
|
|
|
Fraction endTick = element->measure()->last()->nextChordRest(element->track(), true)->tick();
|
|
bool last = false;
|
|
|
|
if (selection().isRange()) {
|
|
if (element->tick() != endTick && selection().tickEnd() <= endTick) {
|
|
measure = element->measure();
|
|
last = true;
|
|
} else if (element->tick() == endTick && selection().isEndActive()) {
|
|
last = true;
|
|
}
|
|
} else if (element->tick() != endTick && selectBehavior) {
|
|
measure = element->measure();
|
|
last = true;
|
|
}
|
|
if (!measure) {
|
|
measure = element->measure();
|
|
last = true;
|
|
}
|
|
int staff = element->staffIdx();
|
|
|
|
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);
|
|
|
|
if (pel && pel->isChordRest()) {
|
|
return toChordRest(pel);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// prevMeasure
|
|
//---------------------------------------------------------
|
|
|
|
ChordRest* Score::prevMeasure(ChordRest* element, bool mmRest)
|
|
{
|
|
if (!element) {
|
|
return 0;
|
|
}
|
|
|
|
Measure* measure = 0;
|
|
if (mmRest) {
|
|
measure = element->measure()->prevMeasureMM();
|
|
} else {
|
|
measure = element->measure()->prevMeasure();
|
|
}
|
|
|
|
Fraction startTick = element->measure()->first()->nextChordRest(element->track())->tick();
|
|
bool last = false;
|
|
|
|
if (selection().isRange() && selection().isEndActive() && selection().startSegment()->tick() <= startTick) {
|
|
last = true;
|
|
} else if (element->tick() != startTick) {
|
|
measure = element->measure();
|
|
}
|
|
if (!measure) {
|
|
measure = element->measure();
|
|
last = false;
|
|
}
|
|
|
|
int staff = element->staffIdx();
|
|
|
|
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);
|
|
|
|
if (pel && pel->isChordRest()) {
|
|
return toChordRest(pel);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// nextElement
|
|
//---------------------------------------------------------
|
|
|
|
Element* Score::nextElement()
|
|
{
|
|
Element* e = getSelectedElement();
|
|
if (!e) {
|
|
return nullptr;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
case ElementType::SEGMENT: {
|
|
Segment* s = toSegment(e);
|
|
Element* next = s->nextElement(staffId);
|
|
if (next) {
|
|
return next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
case ElementType::MEASURE: {
|
|
Measure* m = toMeasure(e);
|
|
Element* next = m->nextElementStaff(staffId);
|
|
if (next) {
|
|
return next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
case ElementType::CLEF:
|
|
case ElementType::KEYSIG:
|
|
case ElementType::TIMESIG:
|
|
case ElementType::BAR_LINE: {
|
|
for (; e && e->type() != ElementType::SEGMENT; e = e->parent()) {
|
|
}
|
|
Segment* s = toSegment(e);
|
|
Element* next = s->nextElement(staffId);
|
|
if (next) {
|
|
return next;
|
|
} else {
|
|
return score()->firstElement();
|
|
}
|
|
}
|
|
#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();
|
|
}
|
|
}
|
|
// 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();
|
|
}
|
|
|
|
Segment* seg = tick2segment(sp->tick());
|
|
if (seg) {
|
|
Segment* nextSegment = seg->next1();
|
|
while (nextSegment) {
|
|
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, staffId);
|
|
if (nextEl) {
|
|
return nextEl;
|
|
}
|
|
nextSegment = nextSegment->next1MM();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ElementType::GLISSANDO_SEGMENT:
|
|
case ElementType::TIE_SEGMENT: {
|
|
SpannerSegment* s = toSpannerSegment(e);
|
|
Spanner* sp = s->spanner();
|
|
Element* elSt = sp->startElement();
|
|
Note* n = toNote(elSt);
|
|
Element* next = n->nextElement();
|
|
if (next) {
|
|
return next;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
case ElementType::VBOX:
|
|
case ElementType::HBOX:
|
|
case ElementType::TBOX: {
|
|
MeasureBase* mb = toMeasureBase(e)->nextMM();
|
|
if (!mb) {
|
|
break;
|
|
} else if (mb->isMeasure()) {
|
|
ChordRest* cr = selection().currentCR();
|
|
int si = cr ? cr->staffIdx() : 0;
|
|
return toMeasure(mb)->nextElementStaff(si);
|
|
} else {
|
|
return mb;
|
|
}
|
|
}
|
|
case ElementType::LAYOUT_BREAK: {
|
|
staffId = 0; // otherwise it will equal -1, which breaks the navigation
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
e = e->parent();
|
|
}
|
|
return score()->lastElement();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// prevElement
|
|
//---------------------------------------------------------
|
|
|
|
Element* Score::prevElement()
|
|
{
|
|
Element* e = getSelectedElement();
|
|
if (!e) {
|
|
return nullptr;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
case ElementType::SEGMENT: {
|
|
Segment* s = toSegment(e);
|
|
Element* prev = s->prevElement(staffId);
|
|
if (prev) {
|
|
return prev;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
case ElementType::MEASURE: {
|
|
Measure* m = toMeasure(e);
|
|
return m->prevElementStaff(staffId);
|
|
}
|
|
case ElementType::CLEF:
|
|
case ElementType::KEYSIG:
|
|
case ElementType::TIMESIG:
|
|
case ElementType::BAR_LINE: {
|
|
for (; e && e->type() != ElementType::SEGMENT; e = e->parent()) {
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
// 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) {
|
|
return prevSp->spannerSegments().front();
|
|
} else {
|
|
Segment* startSeg = sp->startSegment();
|
|
if (!startSeg->annotations().empty()) {
|
|
Element* last = startSeg->lastAnnotation(startSeg, staffId);
|
|
if (last) {
|
|
return last;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
if (el->isChord()) {
|
|
return toChord(el)->lastElementBeforeSegment();
|
|
} else if (el->isNote()) {
|
|
Chord* c = toNote(el)->chord();
|
|
return c->lastElementBeforeSegment();
|
|
} else {
|
|
return el;
|
|
}
|
|
}
|
|
}
|
|
case ElementType::GLISSANDO_SEGMENT:
|
|
case ElementType::TIE_SEGMENT: {
|
|
SpannerSegment* s = toSpannerSegment(e);
|
|
Spanner* sp = s->spanner();
|
|
Element* elSt = sp->startElement();
|
|
Q_ASSERT(elSt->type() == ElementType::NOTE);
|
|
Note* n = toNote(elSt);
|
|
Element* prev = n->prevElement();
|
|
if (prev) {
|
|
return prev;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
case ElementType::VBOX:
|
|
case ElementType::HBOX:
|
|
case ElementType::TBOX: {
|
|
MeasureBase* mb = toMeasureBase(e)->prevMM();
|
|
if (!mb) {
|
|
break;
|
|
} else if (mb->isMeasure()) {
|
|
ChordRest* cr = selection().currentCR();
|
|
int si = cr ? cr->staffIdx() : 0;
|
|
Segment* s = toMeasure(mb)->last();
|
|
if (s) {
|
|
return s->lastElement(si);
|
|
}
|
|
} else {
|
|
return mb;
|
|
}
|
|
}
|
|
break;
|
|
case ElementType::LAYOUT_BREAK: {
|
|
staffId = 0; // otherwise it will equal -1, which breaks the navigation
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
e = e->parent();
|
|
}
|
|
return score()->firstElement();
|
|
}
|
|
}
|