MuseScore/libmscore/navigate.cpp
MarcSabatella ad3b5c2dde fix #301496: voltas excluded from navigation
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.
2020-02-24 08:02:38 -07:00

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();
}
}