ad3b5c2dde
Resolves: https://musescore.org/en/node/301496 Alt+Left/Right commands were skipping voltas because we were checking start element and checking against the active staff, but the start element is actually the measure for voltas. The main change here is to go ahead and visit the volta if the active staff if you are navigating the top staff. Arguably, it could make sense to check for the top *visible* staff, since that is what the volta at least appears to be attached to. So I have code here to that. But I disabled it because in practice, neither the navigation commands themsevles nor the screen reader treat invisible staves specially. So a blind user navigating would have no way of knowing the top staff is not visible. So they would likely continue to see it as relevant. I would not the same issue occurs for system text, which we always treat as attached to the top staff only. I added a TODO to indicate where this code would need updating. Eventually we could consider coming up with some way of presenting information about hidden staves. Perhaps in conjunction with a facility allow user to hide staves on specific systems only, which seems to be a fairly common request.
788 lines
28 KiB
C++
788 lines
28 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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
e = e->parent();
|
|
}
|
|
return score()->firstElement();
|
|
}
|
|
|
|
}
|
|
|