MuseScore/libmscore/segment.cpp
Dmitri Ovodok c7ffeba4cf Rework edit drag for lines
Restructure lines anchors rebasing code, allow for reliable line
span change between systems
2020-04-17 00:10:42 +03:00

2285 lines
80 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=============================================================================
// 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 "mscore.h"
#include "segment.h"
#include "element.h"
#include "chord.h"
#include "note.h"
#include "score.h"
#include "beam.h"
#include "tuplet.h"
#include "text.h"
#include "measure.h"
#include "barline.h"
#include "part.h"
#include "repeat.h"
#include "staff.h"
#include "line.h"
#include "hairpin.h"
#include "ottava.h"
#include "sig.h"
#include "keysig.h"
#include "staffstate.h"
#include "instrchange.h"
#include "clef.h"
#include "timesig.h"
#include "system.h"
#include "xml.h"
#include "undo.h"
#include "harmony.h"
namespace Ms {
//---------------------------------------------------------
// subTypeName
//---------------------------------------------------------
const char* Segment::subTypeName() const
{
return subTypeName(_segmentType);
}
const char* Segment::subTypeName(SegmentType t)
{
switch(t) {
case SegmentType::Invalid: return "Invalid";
case SegmentType::BeginBarLine: return "BeginBarLine";
case SegmentType::HeaderClef: return "HeaderClef";
case SegmentType::Clef: return "Clef";
case SegmentType::KeySig: return "Key Signature";
case SegmentType::Ambitus: return "Ambitus";
case SegmentType::TimeSig: return "Time Signature";
case SegmentType::StartRepeatBarLine: return "Begin Repeat";
case SegmentType::BarLine: return "BarLine";
case SegmentType::Breath: return "Breath";
case SegmentType::ChordRest: return "ChordRest";
case SegmentType::EndBarLine: return "EndBarLine";
case SegmentType::KeySigAnnounce: return "Key Sig Precaution";
case SegmentType::TimeSigAnnounce: return "Time Sig Precaution";
default:
return "??";
}
}
//---------------------------------------------------------
// setElement
//---------------------------------------------------------
void Segment::setElement(int track, Element* el)
{
if (el) {
el->setParent(this);
_elist[track] = el;
setEmpty(false);
}
else {
_elist[track] = 0;
checkEmpty();
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Segment::removeElement(int track)
{
Element* el = element(track);
if (el->isChordRest()) {
ChordRest* cr = (ChordRest*)el;
Beam* beam = cr->beam();
if (beam)
beam->remove(cr);
Tuplet* tuplet = cr->tuplet();
if (tuplet)
tuplet->remove(cr);
}
}
//---------------------------------------------------------
// Segment
//---------------------------------------------------------
Segment::Segment(Measure* m)
: Element(m->score(), ElementFlag::EMPTY | ElementFlag::ENABLED | ElementFlag::NOT_SELECTABLE)
{
setParent(m);
init();
}
Segment::Segment(Measure* m, SegmentType st, const Fraction& t)
: Element(m->score(), ElementFlag::EMPTY | ElementFlag::ENABLED | ElementFlag::NOT_SELECTABLE)
{
setParent(m);
// Q_ASSERT(t >= Fraction(0,1));
// Q_ASSERT(t <= m->ticks());
_segmentType = st;
_tick = t;
init();
}
//---------------------------------------------------------
// Segment
//---------------------------------------------------------
Segment::Segment(const Segment& s)
: Element(s)
{
_segmentType = s._segmentType;
_tick = s._tick;
_extraLeadingSpace = s._extraLeadingSpace;
for (Element* e : s._annotations)
add(e->clone());
_elist.reserve(s._elist.size());
for (Element* e : s._elist) {
Element* ne = 0;
if (e) {
ne = e->clone();
ne->setParent(this);
}
_elist.push_back(ne);
}
_dotPosX = s._dotPosX;
_shapes = s._shapes;
}
//---------------------------------------------------------
// setSegmentType
//---------------------------------------------------------
void Segment::setSegmentType(SegmentType t)
{
Q_ASSERT(_segmentType != SegmentType::Clef || t != SegmentType::ChordRest);
_segmentType = t;
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Segment::setScore(Score* score)
{
Element::setScore(score);
for (Element* e : _elist) {
if (e)
e->setScore(score);
}
for (Element* e : _annotations)
e->setScore(score);
}
Segment::~Segment()
{
for (Element* e : _elist) {
if (!e)
continue;
if (e->isTimeSig())
e->staff()->removeTimeSig(toTimeSig(e));
delete e;
}
qDeleteAll(_annotations);
}
//---------------------------------------------------------
// init
//---------------------------------------------------------
void Segment::init()
{
int staves = score()->nstaves();
int tracks = staves * VOICES;
_elist.assign(tracks, 0);
_dotPosX.assign(staves, 0.0);
_shapes.assign(staves, Shape());
}
//---------------------------------------------------------
// tick
//---------------------------------------------------------
Fraction Segment::tick() const
{
return _tick + measure()->tick();
}
//---------------------------------------------------------
// next1
/// return next \a Segment, dont stop searching at end
/// of \a Measure
//---------------------------------------------------------
Segment* Segment::next1() const
{
if (next())
return next();
Measure* m = measure()->nextMeasure();
return m ? m->first() : 0;
}
//---------------------------------------------------------
// next1enabled
//---------------------------------------------------------
Segment* Segment::next1enabled() const
{
Segment* s = next1();
while (s && !s->enabled())
s = s->next1();
return s;
}
//---------------------------------------------------------
// next1MM
//---------------------------------------------------------
Segment* Segment::next1MM() const
{
if (next())
return next();
Measure* m = measure()->nextMeasureMM();
return m ? m->first() : 0;
}
Segment* Segment::next1(SegmentType types) const
{
for (Segment* s = next1(); s; s = s->next1()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
Segment* Segment::next1MM(SegmentType types) const
{
for (Segment* s = next1MM(); s; s = s->next1MM()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
Segment* Segment::next1MMenabled() const
{
Segment* s = next1MM();
while (s && !s->enabled())
s = s->next1MM();
return s;
}
//---------------------------------------------------------
// next
// got to next segment which has subtype in types
//---------------------------------------------------------
Segment* Segment::next(SegmentType types) const
{
for (Segment* s = next(); s; s = s->next()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
//---------------------------------------------------------
// nextInStaff
/// Returns next \c Segment in the staff with given index
//---------------------------------------------------------
Segment* Segment::nextInStaff(int staffIdx, SegmentType type) const
{
Segment* s = next(type);
const int minTrack = staffIdx * VOICES;
const int maxTrack = (staffIdx + 1) * VOICES - 1;
while (s && !s->hasElements(minTrack, maxTrack))
s = s->next(type);
return s;
}
//---------------------------------------------------------
// prev
// got to previous segment which has subtype in types
//---------------------------------------------------------
Segment* Segment::prev(SegmentType types) const
{
for (Segment* s = prev(); s; s = s->prev()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
//---------------------------------------------------------
// prev1
/// return previous \a Segment, dont stop searching at
/// \a Measure begin
//---------------------------------------------------------
Segment* Segment::prev1() const
{
if (prev())
return prev();
Measure* m = measure()->prevMeasure();
return m ? m->last() : 0;
}
Segment* Segment::prev1enabled() const
{
Segment* s = prev1();
while (s && !s->enabled())
s = s->prev1();
return s;
}
Segment* Segment::prev1MM() const
{
if (prev())
return prev();
Measure* m = measure()->prevMeasureMM();
return m ? m->last() : 0;
}
Segment* Segment::prev1MMenabled() const
{
Segment* s = prev1MM();
while (s && !s->enabled())
s = s->prev1MM();
return s;
}
Segment* Segment::prev1(SegmentType types) const
{
for (Segment* s = prev1(); s; s = s->prev1()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
Segment* Segment::prev1MM(SegmentType types) const
{
for (Segment* s = prev1MM(); s; s = s->prev1MM()) {
if (s->segmentType() & types)
return s;
}
return 0;
}
//---------------------------------------------------------
// nextCR
// get next ChordRest Segment
//---------------------------------------------------------
Segment* Segment::nextCR(int track, bool sameStaff) const
{
int strack = track;
int etrack;
if (sameStaff) {
strack &= ~(VOICES-1);
etrack = strack + VOICES;
}
else {
etrack = strack + 1;
}
for (Segment* seg = next1(); seg; seg = seg->next1()) {
if (seg->isChordRestType()) {
if (track == -1)
return seg;
for (int t = strack; t < etrack; ++t) {
if (seg->element(t))
return seg;
}
}
}
return 0;
}
//---------------------------------------------------------
// nextChordRest
// get the next ChordRest, start at this segment
//---------------------------------------------------------
ChordRest* Segment::nextChordRest(int track, bool backwards) const
{
for (const Segment* seg = this; seg; seg = backwards ? seg->prev1() : seg->next1()) {
Element* el = seg->element(track);
if (el && el->isChordRest())
return toChordRest(el);
}
return 0;
}
//---------------------------------------------------------
// insertStaff
//---------------------------------------------------------
void Segment::insertStaff(int staff)
{
int track = staff * VOICES;
for (int voice = 0; voice < VOICES; ++voice)
_elist.insert(_elist.begin() + track, 0);
_dotPosX.insert(_dotPosX.begin()+staff, 0.0);
_shapes.insert(_shapes.begin()+staff, Shape());
for (Element* e : _annotations) {
int staffIdx = e->staffIdx();
if (staffIdx >= staff && !e->systemFlag())
e->setTrack(e->track() + VOICES);
}
fixStaffIdx();
}
//---------------------------------------------------------
// removeStaff
//---------------------------------------------------------
void Segment::removeStaff(int staff)
{
int track = staff * VOICES;
_elist.erase(_elist.begin() + track, _elist.begin() + track + VOICES);
_dotPosX.erase(_dotPosX.begin() + staff);
_shapes.erase(_shapes.begin()+staff);
for (Element* e : _annotations) {
int staffIdx = e->staffIdx();
if (staffIdx > staff && !e->systemFlag())
e->setTrack(e->track() - VOICES);
}
fixStaffIdx();
}
//---------------------------------------------------------
// checkElement
//---------------------------------------------------------
void Segment::checkElement(Element* el, int track)
{
// generated elements can be overwritten
if (_elist[track] && !_elist[track]->generated()) {
qDebug("add(%s): there is already a %s at track %d tick %d",
el->name(),
_elist[track]->name(),
track,
tick().ticks()
);
// abort();
}
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Segment::add(Element* el)
{
// qDebug("%p segment %s add(%d, %d, %s)", this, subTypeName(), tick(), el->track(), el->name());
el->setParent(this);
int track = el->track();
Q_ASSERT(track != -1);
Q_ASSERT(el->score() == score());
Q_ASSERT(score()->nstaves() * VOICES == int(_elist.size()));
// make sure offset is correct for staff
if (el->isStyled(Pid::OFFSET))
el->setOffset(el->propertyDefault(Pid::OFFSET).toPointF());
switch (el->type()) {
case ElementType::REPEAT_MEASURE:
_elist[track] = el;
setEmpty(false);
break;
case ElementType::TEMPO_TEXT:
case ElementType::DYNAMIC:
case ElementType::HARMONY:
case ElementType::SYMBOL:
case ElementType::FRET_DIAGRAM:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
case ElementType::TEXT:
case ElementType::TREMOLOBAR:
case ElementType::TAB_DURATION_SYMBOL:
case ElementType::FIGURED_BASS:
case ElementType::FERMATA:
case ElementType::STICKING:
_annotations.push_back(el);
break;
case ElementType::STAFF_STATE:
if (toStaffState(el)->staffStateType() == StaffStateType::INSTRUMENT) {
StaffState* ss = toStaffState(el);
Part* part = el->part();
part->setInstrument(ss->instrument(), tick());
}
_annotations.push_back(el);
break;
case ElementType::INSTRUMENT_CHANGE: {
InstrumentChange* is = toInstrumentChange(el);
Part* part = is->part();
part->setInstrument(is->instrument(), tick());
_annotations.push_back(el);
break;
}
case ElementType::CLEF:
Q_ASSERT(_segmentType == SegmentType::Clef || _segmentType == SegmentType::HeaderClef);
checkElement(el, track);
_elist[track] = el;
if (!el->generated()) {
el->staff()->setClef(toClef(el));
// updateNoteLines(this, el->track()); TODO::necessary?
}
setEmpty(false);
break;
case ElementType::TIMESIG:
Q_ASSERT(segmentType() == SegmentType::TimeSig || segmentType() == SegmentType::TimeSigAnnounce);
checkElement(el, track);
_elist[track] = el;
el->staff()->addTimeSig(toTimeSig(el));
setEmpty(false);
break;
case ElementType::KEYSIG:
Q_ASSERT(_segmentType == SegmentType::KeySig || _segmentType == SegmentType::KeySigAnnounce);
checkElement(el, track);
_elist[track] = el;
if (!el->generated())
el->staff()->setKey(tick(), toKeySig(el)->keySigEvent());
setEmpty(false);
break;
case ElementType::CHORD:
case ElementType::REST:
Q_ASSERT(_segmentType == SegmentType::ChordRest);
{
if (track % VOICES) {
bool v;
if (el->isChord()) {
v = false;
// consider chord visible if any note is visible
Chord* c = toChord(el);
for (Note* n : c->notes()) {
if (n->visible()) {
v = true;
break;
}
}
}
else
v = el->visible();
if (v && measure()->score()->ntracks() > track)
measure()->setHasVoices(track / VOICES, true);
}
// the tick position of a tuplet is the tick position of its
// first element:
// ChordRest* cr = toChordRest(el);
// if (cr->tuplet() && !cr->tuplet()->elements().empty() && cr->tuplet()->elements().front() == cr && cr->tuplet()->tick() < 0)
// cr->tuplet()->setTick(cr->tick());
score()->setPlaylistDirty();
}
// fall through
case ElementType::BAR_LINE:
case ElementType::BREATH:
if (track < score()->nstaves() * VOICES) {
checkElement(el, track);
_elist[track] = el;
}
setEmpty(false);
break;
case ElementType::AMBITUS:
Q_ASSERT(_segmentType == SegmentType::Ambitus);
checkElement(el, track);
_elist[track] = el;
setEmpty(false);
break;
default:
qFatal("Segment::add() unknown %s", el->name());
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Segment::remove(Element* el)
{
// qDebug("%p Segment::remove %s %p", this, el->name(), el);
int track = el->track();
switch(el->type()) {
case ElementType::CHORD:
case ElementType::REST:
{
_elist[track] = 0;
int staffIdx = el->staffIdx();
measure()->checkMultiVoices(staffIdx);
// spanners with this cr as start or end element will need relayout
SpannerMap& smap = score()->spannerMap();
auto spanners = smap.findOverlapping(tick().ticks(), tick().ticks());
for (auto interval : spanners) {
Spanner* s = interval.value;
Element* start = s->startElement();
Element* end = s->endElement();
if (s->startElement() == el)
start = nullptr;
if (s->endElement() == el)
end = nullptr;
if (start != s->startElement() || end != s->endElement())
score()->undo(new ChangeStartEndSpanner(s, start, end));
}
score()->setPlaylistDirty();
}
break;
case ElementType::REPEAT_MEASURE:
_elist[track] = 0;
break;
case ElementType::DYNAMIC:
case ElementType::FIGURED_BASS:
case ElementType::FRET_DIAGRAM:
case ElementType::HARMONY:
case ElementType::IMAGE:
case ElementType::MARKER:
case ElementType::REHEARSAL_MARK:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::SYMBOL:
case ElementType::TAB_DURATION_SYMBOL:
case ElementType::TEMPO_TEXT:
case ElementType::TEXT:
case ElementType::TREMOLOBAR:
case ElementType::FERMATA:
case ElementType::STICKING:
removeAnnotation(el);
break;
case ElementType::STAFF_STATE:
if (toStaffState(el)->staffStateType() == StaffStateType::INSTRUMENT) {
Part* part = el->part();
part->removeInstrument(tick());
}
removeAnnotation(el);
break;
case ElementType::INSTRUMENT_CHANGE:
{
InstrumentChange* is = toInstrumentChange(el);
Part* part = is->part();
part->removeInstrument(tick());
}
removeAnnotation(el);
break;
case ElementType::TIMESIG:
_elist[track] = 0;
el->staff()->removeTimeSig(toTimeSig(el));
break;
case ElementType::KEYSIG:
Q_ASSERT(_elist[track] == el);
_elist[track] = 0;
if (!el->generated())
el->staff()->removeKey(tick());
break;
case ElementType::CLEF:
el->staff()->removeClef(toClef(el));
// updateNoteLines(this, el->track());
// fall through
case ElementType::BAR_LINE:
case ElementType::AMBITUS:
_elist[track] = 0;
break;
case ElementType::BREATH:
_elist[track] = 0;
score()->setPause(tick(), 0);
break;
default:
qFatal("Segment::remove() unknown %s", el->name());
}
triggerLayout();
checkEmpty();
}
//---------------------------------------------------------
// segmentType
// returns segment type suitable for storage of Element
//---------------------------------------------------------
SegmentType Segment::segmentType(ElementType type)
{
switch (type) {
case ElementType::CHORD:
case ElementType::REST:
case ElementType::REPEAT_MEASURE:
case ElementType::JUMP:
case ElementType::MARKER:
return SegmentType::ChordRest;
case ElementType::CLEF:
return SegmentType::Clef;
case ElementType::KEYSIG:
return SegmentType::KeySig;
case ElementType::TIMESIG:
return SegmentType::TimeSig;
case ElementType::BAR_LINE:
return SegmentType::StartRepeatBarLine;
case ElementType::BREATH:
return SegmentType::Breath;
default:
qDebug("Segment:segmentType(): bad type: <%s>", Element::name(type));
return SegmentType::Invalid;
}
}
//---------------------------------------------------------
// sortStaves
//---------------------------------------------------------
void Segment::sortStaves(QList<int>& dst)
{
std::vector<Element*> dl;
dl.reserve(dst.size());
for (int i = 0; i < dst.size(); ++i) {
int startTrack = dst[i] * VOICES;
int endTrack = startTrack + VOICES;
for (int k = startTrack; k < endTrack; ++k)
dl.push_back(_elist[k]);
}
std::swap(_elist, dl);
QMap<int, int> map;
for (int k = 0; k < dst.size(); ++k) {
map.insert(dst[k], k);
}
for (Element* e : _annotations) {
if (!e->systemFlag())
e->setTrack(map[e->staffIdx()] * VOICES + e->voice());
}
fixStaffIdx();
}
//---------------------------------------------------------
// fixStaffIdx
//---------------------------------------------------------
void Segment::fixStaffIdx()
{
int track = 0;
for (Element* e : _elist) {
if (e)
e->setTrack(track);
++track;
}
}
//---------------------------------------------------------
// checkEmpty
//---------------------------------------------------------
void Segment::checkEmpty() const
{
if (!_annotations.empty()) {
setEmpty(false);
return;
}
setEmpty(true);
for (const Element* e : _elist) {
if (e) {
setEmpty(false);
break;
}
}
}
//---------------------------------------------------------
// swapElements
//---------------------------------------------------------
void Segment::swapElements(int i1, int i2)
{
std::iter_swap(_elist.begin() + i1, _elist.begin() + i2);
if (_elist[i1])
_elist[i1]->setTrack(i1);
if (_elist[i2])
_elist[i2]->setTrack(i2);
triggerLayout();
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Segment::write(XmlWriter& xml) const
{
if (written())
return;
setWritten(true);
if (_extraLeadingSpace.isZero())
return;
xml.stag(this);
xml.tag("leadingSpace", _extraLeadingSpace.val());
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void Segment::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype")
e.skipCurrentElement();
else if (tag == "leadingSpace")
_extraLeadingSpace = Spatium(e.readDouble());
else if (tag == "trailingSpace") // obsolete
e.readDouble();
else
e.unknown();
}
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Segment::getProperty(Pid propertyId) const
{
switch (propertyId) {
case Pid::TICK:
return _tick;
case Pid::LEADING_SPACE:
return extraLeadingSpace();
default:
return Element::getProperty(propertyId);
}
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Segment::propertyDefault(Pid propertyId) const
{
switch (propertyId) {
case Pid::LEADING_SPACE:
return Spatium(0.0);
default:
return Element::getProperty(propertyId);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Segment::setProperty(Pid propertyId, const QVariant& v)
{
switch (propertyId) {
case Pid::TICK:
setRtick(v.value<Fraction>());
break;
case Pid::LEADING_SPACE:
setExtraLeadingSpace(v.value<Spatium>());
for (Element* e : _elist) {
if(e)
e->setGenerated(false);
}
break;
default:
return Element::setProperty(propertyId, v);
}
triggerLayout();
return true;
}
//---------------------------------------------------------
// widthInStaff
//---------------------------------------------------------
qreal Segment::widthInStaff(int staffIdx, SegmentType t) const
{
const qreal segX = x();
qreal nextSegX = segX;
Segment* nextSeg = nextInStaff(staffIdx, t);
if (nextSeg)
nextSegX = nextSeg->x();
else {
Segment* lastSeg = measure()->last();
if (lastSeg->segmentType() & t)
nextSegX = lastSeg->x() + lastSeg->width();
else
nextSegX = lastSeg->x();
}
return nextSegX - segX;
}
//---------------------------------------------------------
// ticksInStaff
//---------------------------------------------------------
Fraction Segment::ticksInStaff(int staffIdx) const
{
const Fraction segTick = tick();
Fraction nextSegTick = segTick;
Segment* nextSeg = nextInStaff(staffIdx, durationSegmentsMask);
if (nextSeg)
nextSegTick = nextSeg->tick();
else {
Segment* lastSeg = measure()->last();
nextSegTick = lastSeg->tick() + lastSeg->ticks();
}
return nextSegTick - segTick;
}
//---------------------------------------------------------
// splitsTuplet
//---------------------------------------------------------
bool Segment::splitsTuplet() const
{
for (Element* e : _elist) {
if (!(e && e->isChordRest()))
continue;
ChordRest* cr = toChordRest(e);
Tuplet* t = cr->tuplet();
while (t) {
if (cr != t->elements().front())
return true;
t = t->tuplet();
}
}
return false;
}
//---------------------------------------------------------
// operator<
/// return true if segment is before s in list
//---------------------------------------------------------
bool Segment::operator<(const Segment& s) const
{
if (tick() < s.tick())
return true;
if (tick() > s.tick())
return false;
for (Segment* ns = next1(); ns && (ns->tick() == s.tick()); ns = ns->next1()) {
if (ns == &s)
return true;
}
return false;
}
//---------------------------------------------------------
// operator>
/// return true if segment is after s in list
//---------------------------------------------------------
bool Segment::operator>(const Segment& s) const
{
if (tick() > s.tick())
return true;
if (tick() < s.tick())
return false;
for (Segment* ns = prev1(); ns && (ns->tick() == s.tick()); ns = ns->prev1()) {
if (ns == &s)
return true;
}
return false;
}
//---------------------------------------------------------
// hasElements
/// Returns true if the segment has at least one element.
/// Annotations are not considered.
//---------------------------------------------------------
bool Segment::hasElements() const
{
for (const Element* e : _elist) {
if (e)
return true;
}
return false;
}
//---------------------------------------------------------
// hasElements
/// return true if an annotation of type type or and element is found in the track range
//---------------------------------------------------------
bool Segment::hasElements(int minTrack, int maxTrack) const
{
for (int curTrack = minTrack; curTrack <= maxTrack; curTrack++)
if (element(curTrack))
return true;
return false;
}
//---------------------------------------------------------
// hasAnnotationOrElement
/// return true if an annotation of type type or and element is found in the track range
//---------------------------------------------------------
bool Segment::hasAnnotationOrElement(ElementType type, int minTrack, int maxTrack) const
{
for (const Element* e : _annotations)
if (e->type() == type && e->track() >= minTrack && e->track() <= maxTrack)
return true;
return hasElements(minTrack, maxTrack);
}
//---------------------------------------------------------
// findAnnotation
/// Returns the first found annotation of type type
/// or nullptr if nothing was found.
//---------------------------------------------------------
Element* Segment::findAnnotation(ElementType type, int minTrack, int maxTrack)
{
for (Element* e : _annotations)
if (e->type() == type && e->track() >= minTrack && e->track() <= maxTrack)
return e;
return nullptr;
}
//---------------------------------------------------------
// findAnnotations
/// Returns the list of found annotations
/// or nullptr if nothing was found.
//---------------------------------------------------------
std::vector<Element*> Segment::findAnnotations(ElementType type, int minTrack, int maxTrack)
{
std::vector<Element*> found;
for (Element* e : _annotations)
if (e->type() == type && e->track() >= minTrack && e->track() <= maxTrack)
found.push_back(e);
return found;
}
//---------------------------------------------------------
// removeAnnotation
//---------------------------------------------------------
void Segment::removeAnnotation(Element* e)
{
for (auto i = _annotations.begin(); i != _annotations.end(); ++i) {
if (*i == e) {
_annotations.erase(i);
break;
}
}
}
//---------------------------------------------------------
// clearAnnotations
//---------------------------------------------------------
void Segment::clearAnnotations()
{
_annotations.clear();
}
//---------------------------------------------------------
// elementAt
// A variant of the element(int) function,
// specifically intended to be called from QML plugins
//---------------------------------------------------------
Ms::Element* Segment::elementAt(int track) const
{
Element* e = track < int(_elist.size()) ? _elist[track] : 0;
return e;
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Segment::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
for (int track = 0; track < score()->nstaves() * VOICES; ++track) {
int staffIdx = track/VOICES;
if (!all && !(measure()->visible(staffIdx) && score()->staff(staffIdx)->show())) {
track += VOICES - 1;
continue;
}
Element* e = element(track);
if (e == 0)
continue;
e->scanElements(data, func, all);
}
for (Element* e : annotations()) {
if (all || e->systemFlag() || measure()->visible(e->staffIdx()))
e->scanElements(data, func, all);
}
}
//---------------------------------------------------------
// firstElement
// This function returns the first main element from a
// segment, or a barline if it spanns in the staff
//---------------------------------------------------------
Element* Segment::firstElement(int staff)
{
if (isChordRestType()) {
int strack = staff * VOICES;
int etrack = strack + VOICES;
for (int v = strack; v < etrack; ++v) {
Element* el = element(v);
if (!el)
continue;
return el->isChord() ? toChord(el)->notes().back() : el;
}
}
else
return getElement(staff);
return 0;
}
//---------------------------------------------------------
// lastElement
// This function returns the last main element from a
// segment, or a barline if it spanns in the staff
//---------------------------------------------------------
Element* Segment::lastElement(int staff)
{
if (segmentType() == SegmentType::ChordRest) {
for (int voice = staff * VOICES + (VOICES - 1); voice/VOICES == staff; voice--) {
Element* el = element(voice);
if (!el) { //there is no chord or rest on this voice
continue;
}
if (el->isChord()) {
return toChord(el)->notes().front();
}
else {
return el;
}
}
}
else {
return getElement(staff);
}
return 0;
}
//---------------------------------------------------------
// getElement
// protected because it is used by the firstElement and
// lastElement functions when segment types that have
// just one element to avoid duplicated code
//
// Use firstElement, or lastElement instead of this
//---------------------------------------------------------
Element* Segment::getElement(int staff)
{
segmentType();
if (segmentType() == SegmentType::ChordRest) {
return firstElement(staff);
}
else if (segmentType() & (SegmentType::EndBarLine | SegmentType::BarLine | SegmentType::StartRepeatBarLine)) {
for (int i = staff; i >= 0; i--) {
if (!element(i * VOICES))
continue;
BarLine* b = toBarLine(element(i*VOICES));
if (i + b->spanStaff() >= staff)
return element(i*VOICES);
}
}
else
return element(staff * VOICES);
return 0;
}
//---------------------------------------------------------
// nextAnnotation
// return next element in _annotations
//---------------------------------------------------------
Element* Segment::nextAnnotation(Element* e)
{
if (_annotations.empty() || e == _annotations.back())
return nullptr;
auto ei = std::find(_annotations.begin(), _annotations.end(), e);
if (ei == _annotations.end())
return nullptr; // element not found
// TODO: firstVisibleStaff() for system elements? see Spanner::nextSpanner()
auto resIt = std::find_if(ei + 1, _annotations.end(), [e](Element* nextElem){
return nextElem && nextElem->staffIdx() == e->staffIdx();
});
return _annotations.end() == resIt ? nullptr : *resIt;
}
//---------------------------------------------------------
// prevAnnotation
// return previous element in _annotations
//---------------------------------------------------------
Element* Segment::prevAnnotation(Element* e)
{
if (e == _annotations.front())
return nullptr;
auto reverseIt = std::find(_annotations.rbegin(), _annotations.rend(), e);
if (reverseIt == _annotations.rend())
return nullptr; // element not found
// TODO: firstVisibleStaff() for system elements? see Spanner::nextSpanner()
auto resIt = std::find_if(reverseIt + 1, _annotations.rend(), [e](Element* prevElem){
return prevElem && prevElem->staffIdx() == e->staffIdx();
});
return _annotations.rend() == resIt ? nullptr : *resIt;
}
//---------------------------------------------------------
// firstAnnotation
//---------------------------------------------------------
Element* Segment::firstAnnotation(Segment* s, int activeStaff)
{
for (auto i = s->annotations().begin(); i != s->annotations().end(); ++i) {
// TODO: firstVisibleStaff() for system elements? see Spanner::nextSpanner()
if ((*i)->staffIdx() == activeStaff)
return *i;
}
return nullptr;
}
//---------------------------------------------------------
// lastAnnotation
//---------------------------------------------------------
Element* Segment::lastAnnotation(Segment* s, int activeStaff)
{
for (auto i = --s->annotations().end(); i != s->annotations().begin(); --i) {
// TODO: firstVisibleStaff() for system elements? see Spanner::nextSpanner()
if ((*i)->staffIdx() == activeStaff)
return *i;
}
auto i = s->annotations().begin();
if ((*i)->staffIdx() == activeStaff)
return *i;
return nullptr;
}
//--------------------------------------------------------
// firstInNextSegments
// Searches for the next segment that has elements on the
// active staff and returns its first element
//
// Uses firstElement so it also returns a barline if it
// spans into the active staff
//--------------------------------------------------------
Element* Segment::firstInNextSegments(int activeStaff)
{
Element* re = 0;
Segment* seg = this;
while (!re) {
seg = seg->next1MMenabled();
if (!seg) //end of staff, or score
break;
re = seg->firstElement(activeStaff);
}
if (re)
return re;
if (!seg) { //end of staff
if (activeStaff + 1 >= score()->nstaves()) //end of score
return 0;
seg = score()->firstSegmentMM(SegmentType::All);
return seg->element((activeStaff + 1) * VOICES);
}
return 0;
}
//---------------------------------------------------------
// firstElementOfSegment
// returns the first non null element in the given segment
//---------------------------------------------------------
Element* Segment::firstElementOfSegment(Segment* s, int activeStaff)
{
for (auto i: s->elist()) {
if (i && i->staffIdx() == activeStaff) {
if (i->type() == ElementType::CHORD)
return toChord(i)->notes().back();
else
return i;
}
}
return nullptr;
}
//---------------------------------------------------------
// nextElementOfSegment
// returns the next element in the given segment
//---------------------------------------------------------
Element* Segment::nextElementOfSegment(Segment* s, Element* e, int activeStaff)
{
for (int track = 0; track < score()->nstaves() * VOICES - 1; ++track) {
if (s->element(track) == 0)
continue;
Element* el = s->element(track);
if (el == e) {
Element* next = s->element(track+1);
while (track < score()->nstaves() * VOICES - 1 &&
(!next || next->staffIdx() != activeStaff)) {
next = s->element(++track);
}
if (!next)
return nullptr;
if (next->isChord())
return toChord(next)->notes().back();
else
return next;
}
if (el->type() == ElementType::CHORD) {
std::vector<Note*> notes = toChord(el)->notes();
auto i = std::find(notes.begin(), notes.end(), e);
if (i == notes.end())
continue;
if (i!= notes.begin()) {
return *(i-1);
}
else {
Element* nextEl = s->element(++track);
while (track < score()->nstaves() * VOICES - 1 &&
(!nextEl || nextEl->staffIdx() != activeStaff)) {
nextEl = s->element(++track);
}
if (!nextEl || nextEl->staffIdx() != activeStaff)
return nullptr;
if (nextEl->isChord())
return toChord(nextEl)->notes().back();
return nextEl;
}
}
}
return nullptr;
}
//---------------------------------------------------------
// prevElementOfSegment
// returns the previous element in the given segment
//---------------------------------------------------------
Element* Segment::prevElementOfSegment(Segment* s, Element* e, int activeStaff)
{
for (int track = score()->nstaves() * VOICES - 1; track > 0; --track) {
if (s->element(track) == 0)
continue;
Element* el = s->element(track);
if (el == e) {
Element* prev = s->element(track-1);
while (track > 0 &&
(!prev || prev->staffIdx() != activeStaff)) {
prev = s->element(--track);
}
if (!prev)
return nullptr;
if (prev->staffIdx() == e->staffIdx()) {
if (prev->isChord())
return toChord(prev)->notes().front();
else
return prev;
}
return nullptr;
}
if (el->isChord()) {
std::vector<Note*> notes = toChord(el)->notes();
auto i = std::find(notes.begin(), notes.end(), e);
if (i == notes.end())
continue;
if (i!= --notes.end()) {
return *(i+1);
}
else {
Element* prevEl = s->element(--track);
while (track > 0 &&
(!prevEl || prevEl->staffIdx() != activeStaff)) {
prevEl = s->element(--track);
}
if (!prevEl)
return nullptr;
if (prevEl->staffIdx() == e->staffIdx()) {
if (prevEl->isChord())
return toChord(prevEl)->notes().front();
return prevEl;
}
return nullptr;
}
}
}
return nullptr;
}
//---------------------------------------------------------
// lastElementOfSegment
// returns the last element in the given segment
//---------------------------------------------------------
Element* Segment::lastElementOfSegment(Segment* s, int activeStaff)
{
std::vector<Element*> elements = s->elist();
for (auto i = --elements.end(); i != elements.begin(); --i) {
if (*i && (*i)->staffIdx() == activeStaff) {
if ((*i)->isChord())
return toChord(*i)->notes().front();
else
return *i;
}
}
auto i = elements.begin();
if (*i && (*i)->staffIdx() == activeStaff) {
if ((*i)->type() == ElementType::CHORD)
return toChord(*i)->notes().front();
else
return *i;
}
return nullptr;
}
//---------------------------------------------------------
// firstSpanner
//---------------------------------------------------------
Spanner* Segment::firstSpanner(int activeStaff)
{
std::multimap<int, Spanner*> mmap = score()->spanner();
auto range = mmap.equal_range(tick().ticks());
if (range.first != range.second){ // range not empty
for (auto i = range.first; i != range.second; ++i) {
Spanner* s = i->second;
Element* e = s->startElement();
if (!e)
continue;
if (s->startSegment() == this) {
if (e->staffIdx() == activeStaff)
return s;
#if 1
else if (e->isMeasure() && activeStaff == 0)
return s;
#else
// TODO: see Spanner::nextSpanner()
else if (e->isMeasure()) {
SpannerSegment* ss = s->frontSegment();
int top = ss && ss->system() ? ss->system()->firstVisibleStaff() : 0;
if (activeStaff == top)
return s;
}
#endif
}
}
}
return nullptr;
}
//---------------------------------------------------------
// lastSpanner
//---------------------------------------------------------
Spanner* Segment::lastSpanner(int activeStaff)
{
std::multimap<int, Spanner*> mmap = score()->spanner();
auto range = mmap.equal_range(tick().ticks());
if (range.first != range.second){ // range not empty
for (auto i = --range.second; ; --i) {
Spanner* s = i->second;
Element* e = s->startElement();
if (!e)
continue;
if (s->startSegment() == this) {
if (e->staffIdx() == activeStaff)
return s;
#if 1
else if (e->isMeasure() && activeStaff == 0)
return s;
#else
// TODO: see Spanner::nextSpanner()
else if (e->isMeasure()) {
SpannerSegment* ss = s->frontSegment();
int top = ss && ss->system() ? ss->system()->firstVisibleStaff() : 0;
if (activeStaff == top)
return s;
}
#endif
}
if (i == range.first)
break;
}
}
return nullptr;
}
//---------------------------------------------------------
// notChordRestType
//---------------------------------------------------------
bool Segment::notChordRestType(Segment* s)
{
if (s->segmentType() == SegmentType::KeySig ||
s->segmentType() == SegmentType::TimeSig ||
s->segmentType() == SegmentType::Clef ||
s->segmentType() == SegmentType::HeaderClef ||
s->segmentType() == SegmentType::BeginBarLine ||
s->segmentType() == SegmentType::EndBarLine ||
s->segmentType() == SegmentType::BarLine ||
s->segmentType() == SegmentType::KeySigAnnounce ||
s->segmentType() == SegmentType::TimeSigAnnounce) {
return true;
}
else {
return false;
}
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* Segment::nextElement(int activeStaff)
{
Element* e = score()->selection().element();
if (!e && !score()->selection().elements().isEmpty() )
e = score()->selection().elements().first();
switch (e->type()) {
case ElementType::DYNAMIC:
case ElementType::HARMONY:
case ElementType::SYMBOL:
case ElementType::FERMATA:
case ElementType::FRET_DIAGRAM:
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
case ElementType::TEXT:
case ElementType::TREMOLOBAR:
case ElementType::TAB_DURATION_SYMBOL:
case ElementType::FIGURED_BASS:
case ElementType::STAFF_STATE:
case ElementType::INSTRUMENT_CHANGE:
case ElementType::STICKING: {
Element* next = nullptr;
if (e->parent() == this)
next = nextAnnotation(e);
if (next)
return next;
else {
Spanner* s = firstSpanner(activeStaff);
if (s)
return s->spannerSegments().front();
}
Segment* nextSegment = this->next1MMenabled();
while (nextSegment) {
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1MMenabled();
}
break;
}
case ElementType::SEGMENT: {
if (!_annotations.empty()) {
Element* next = firstAnnotation(this, activeStaff);
if (next)
return next;
}
Spanner* sp = firstSpanner(activeStaff);
if (sp)
return sp->spannerSegments().front();
Segment* nextSegment = this->next1MMenabled();
while (nextSegment) {
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1MMenabled();
}
break;
}
default: {
Element* p;
if (e->isTieSegment() || e->isGlissandoSegment()) {
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
p = sp->startElement();
}
else {
p = e;
Element* pp = p->parent();
if (pp->isNote() || pp->isRest() || (pp->isChord() && !p->isNote()))
p = pp;
}
Element* el = p;
for (; p && p->type() != ElementType::SEGMENT; p = p->parent()) {
;
}
Segment* seg = toSegment(p);
// next in _elist
Element* nextEl = nextElementOfSegment(seg, el, activeStaff);
if (nextEl)
return nextEl;
if (!_annotations.empty()) {
Element* next = firstAnnotation(seg, activeStaff);
if (next)
return next;
}
Spanner* s = firstSpanner(activeStaff);
if (s)
return s->spannerSegments().front();
Segment* nextSegment = seg->next1MMenabled();
if (!nextSegment) {
MeasureBase* mb = measure()->next();
return mb && mb->isBox() ? mb : score()->lastElement();
}
Measure* nsm = nextSegment->measure();
if (nsm != measure()) {
// check for frame, measure elements
MeasureBase* nmb = measure()->nextMM();
Element* nme = nsm->el().empty() ? nullptr : nsm->el().front();
if (nsm != nmb)
return nmb;
else if (nme && nme->isTextBase() && nme->staffIdx() == e->staffIdx())
return nme;
}
while (nextSegment) {
nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1MMenabled();
}
}
break;
}
return nullptr;
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* Segment::prevElement(int activeStaff)
{
Element* e = score()->selection().element();
if (!e && !score()->selection().elements().isEmpty() )
e = score()->selection().elements().last();
switch (e->type()) {
case ElementType::DYNAMIC:
case ElementType::HARMONY:
case ElementType::SYMBOL:
case ElementType::FERMATA:
case ElementType::FRET_DIAGRAM:
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
case ElementType::TEXT:
case ElementType::TREMOLOBAR:
case ElementType::TAB_DURATION_SYMBOL:
case ElementType::FIGURED_BASS:
case ElementType::STAFF_STATE:
case ElementType::INSTRUMENT_CHANGE:
case ElementType::STICKING: {
Element* prev = nullptr;
if (e->parent() == this)
prev = prevAnnotation(e);
if (prev)
return prev;
if (notChordRestType(this)) {
Element* lastEl = lastElementOfSegment(this, activeStaff);
if (lastEl)
return lastEl;
}
int track = score()->nstaves() * VOICES - 1;
Segment* s = this;
Element* el = s->element(track);
while (track > 0 && (!el || el->staffIdx() != activeStaff)) {
el = s->element(--track);
if (track == 0) {
track = score()->nstaves() * VOICES - 1;
s = s->prev1MMenabled();
}
}
if (el->staffIdx() != activeStaff)
return nullptr;
if (el->type() == ElementType::CHORD || el->type() == ElementType::REST
|| el->type() == ElementType::REPEAT_MEASURE) {
ChordRest* cr = this->cr(el->track());
if (cr) {
Element* elCr = cr->lastElementBeforeSegment();
if (elCr) {
return elCr;
}
}
}
if (el->type() == ElementType::CHORD) {
return toChord(el)->lastElementBeforeSegment();
}
else if (el->type() == ElementType::NOTE) {
Chord* c = toNote(el)->chord();
return c->lastElementBeforeSegment();
}
else {
return el;
}
}
case ElementType::ARPEGGIO:
case ElementType::TREMOLO: {
Element* el = this->element(e->track());
Q_ASSERT(el->type() == ElementType::CHORD);
return toChord(el)->prevElement();
}
default: {
Element* el = e;
Segment* seg = this;
if (e->type() == ElementType::TIE_SEGMENT ||
e->type() == ElementType::GLISSANDO_SEGMENT) {
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
el = sp->startElement();
seg = sp->startSegment();
}
else {
Element* ep = e->parent();
if (ep->isNote() || ep->isRest() || (ep->isChord() && !e->isNote()))
el = e->parent();
}
Element* prev = seg->prevElementOfSegment(seg, el, activeStaff);
if (prev) {
if (prev->type() == ElementType::CHORD || prev->type() == ElementType::REST
|| prev->type() == ElementType::REPEAT_MEASURE) {
ChordRest* cr = seg->cr(prev->track());
if (cr) {
Element* elCr = cr->lastElementBeforeSegment();
if (elCr) {
return elCr;
}
}
}
if (prev->type() == ElementType::CHORD) {
return toChord(prev)->lastElementBeforeSegment();
}
else if (prev->type() == ElementType::NOTE) {
Chord* c = toNote(prev)->chord();
return c->lastElementBeforeSegment();
}
else {
return prev;
}
}
Segment* prevSeg = seg->prev1MMenabled();
if (!prevSeg) {
MeasureBase* mb = measure()->prev();
return mb && mb->isBox() ? mb : score()->firstElement();
}
Measure* psm = prevSeg->measure();
if (psm != measure()) {
// check for frame, measure elements
MeasureBase* pmb = measure()->prevMM();
Element* me = measure()->el().empty() ? nullptr : measure()->el().back();
if (me && me->isTextBase() && me->staffIdx() == e->staffIdx())
return me;
else if (psm != pmb)
return pmb;
}
prev = lastElementOfSegment(prevSeg, activeStaff);
while (!prev && prevSeg) {
prevSeg = prevSeg->prev1MMenabled();
prev = lastElementOfSegment(prevSeg, activeStaff);
}
if (!prevSeg)
return score()->firstElement();
if (notChordRestType(prevSeg)) {
Element* lastEl = lastElementOfSegment(prevSeg, activeStaff);
if (lastEl)
return lastEl;
}
Spanner* s1 = prevSeg->lastSpanner(activeStaff);
if (s1) {
return s1->spannerSegments().front();
}
else if (!prevSeg->annotations().empty()) {
Element* next = lastAnnotation(prevSeg, activeStaff);
if (next)
return next;
}
if (prev->type() == ElementType::CHORD || prev->type() == ElementType::REST
|| prev->type() == ElementType::REPEAT_MEASURE || prev->type() == ElementType::NOTE) {
ChordRest* cr = prevSeg->cr(prev->track());
if (cr) {
Element* elCr = cr->lastElementBeforeSegment();
if (elCr) {
return elCr;
}
}
}
if (prev->type() == ElementType::CHORD) {
return toChord(prev)->lastElementBeforeSegment();
}
else if (prev->type() == ElementType::NOTE) {
Chord* c = toNote(prev)->chord();
return c->lastElementBeforeSegment();
}
else {
return prev;
}
}
}
}
//--------------------------------------------------------
// lastInPrevSegments
// Searches for the previous segment that has elements on
// the active staff and returns its last element
//
// Uses lastElement so it also returns a barline if it
// spans into the active staff
//--------------------------------------------------------
Element* Segment::lastInPrevSegments(int activeStaff)
{
Element* re = 0;
Segment* seg = this;
while (!re) {
seg = seg->prev1MMenabled();
if (!seg) //end of staff, or score
break;
re = seg->lastElementOfSegment(seg, activeStaff);
}
if (re)
return re;
if (!seg) { //end of staff
if (activeStaff - 1 < 0) //end of score
return 0;
re = 0;
seg = score()->lastSegmentMM();
while (true) {
//if (seg->segmentType() == SegmentType::EndBarLine)
// score()->inputState().setTrack((activeStaff - 1) * VOICES ); //correction
if ((re = seg->lastElement(activeStaff - 1)) != 0)
return re;
seg = seg->prev1MMenabled();
}
}
return 0;
}
//---------------------------------------------------------
// accessibleExtraInfo
//---------------------------------------------------------
QString Segment::accessibleExtraInfo() const
{
QString rez = "";
if (!annotations().empty()) {
QString temp = "";
for (const Element* a : annotations()) {
if (!score()->selectionFilter().canSelect(a)) continue;
switch(a->type()) {
case ElementType::DYNAMIC:
//they are added in the chordrest, because they are for only one staff
break;
default:
temp = temp + " " + a->accessibleInfo();
}
}
if(!temp.isEmpty())
rez = rez + QObject::tr("Annotations:") + temp;
}
QString startSpanners = "";
QString endSpanners = "";
auto spanners = score()->spannerMap().findOverlapping(tick().ticks(), tick().ticks());
for (auto interval : spanners) {
Spanner* s = interval.value;
if (!score()->selectionFilter().canSelect(s)) continue;
if (segmentType() == SegmentType::EndBarLine ||
segmentType() == SegmentType::BarLine ||
segmentType() == SegmentType::StartRepeatBarLine) {
if (s->isVolta())
continue;
}
else {
if (s->isVolta() || s->isTie()) //ties are added in Note
continue;
}
if (s->tick() == tick())
startSpanners += QObject::tr("Start of %1").arg(s->accessibleInfo());
const Segment* seg = 0;
switch (s->type()) {
case ElementType::VOLTA:
case ElementType::SLUR:
seg = this;
break;
default:
seg = next1MM(SegmentType::ChordRest);
break;
}
if (seg && s->tick2() == seg->tick())
endSpanners += QObject::tr("End of %1").arg(s->accessibleInfo());
}
return rez + " " + startSpanners + " " + endSpanners;
}
//---------------------------------------------------------
// createShapes
//---------------------------------------------------------
void Segment::createShapes()
{
setVisible(false);
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx)
createShape(staffIdx);
}
//---------------------------------------------------------
// createShape
//---------------------------------------------------------
void Segment::createShape(int staffIdx)
{
Shape& s = _shapes[staffIdx];
s.clear();
if (segmentType() & (SegmentType::BarLine | SegmentType::EndBarLine | SegmentType::StartRepeatBarLine | SegmentType::BeginBarLine)) {
setVisible(true);
BarLine* bl = toBarLine(element(staffIdx * VOICES));
if (bl) {
QRectF r = bl->layoutRect();
#ifndef NDEBUG
s.add(r.translated(bl->pos()), bl->name());
#else
s.add(r.translated(bl->pos()));
#endif
}
s.addHorizontalSpacing(Shape::SPACING_GENERAL, 0, 0);
s.addHorizontalSpacing(Shape::SPACING_LYRICS, 0, 0);
return;
}
#if 0
for (int track = staffIdx * VOICES; track < (staffIdx + 1) * VOICES; ++track) {
Element* e = _elist[track];
if (e)
s.add(e->shape().translated(e->pos()));
}
#endif
if (!score()->staff(staffIdx)->show())
return;
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (Element* e : _elist) {
if (!e)
continue;
int effectiveTrack = e->vStaffIdx() * VOICES + e->voice();
if (effectiveTrack >= strack && effectiveTrack < etrack) {
setVisible(true);
if (e->addToSkyline())
s.add(e->shape().translated(e->pos()));
}
}
for (Element* e : _annotations) {
if (!e || e->staffIdx() != staffIdx)
continue;
setVisible(true);
if (!e->addToSkyline())
continue;
if (e->isHarmony()) {
// use same spacing calculation as for chordrest
toHarmony(e)->layout1();
const qreal margin = styleP(Sid::minHarmonyDistance) * 0.5;
qreal x1 = e->bbox().x() - margin + e->pos().x();
qreal x2 = e->bbox().x() + e->bbox().width() + margin + e->pos().x();
s.addHorizontalSpacing(Shape::SPACING_HARMONY, x1, x2);
}
else if (!e->isRehearsalMark()
&& !e->isFretDiagram()
&& !e->isHarmony()
&& !e->isTempoText()
&& !e->isDynamic()
&& !e->isFiguredBass()
&& !e->isSymbol()
&& !e->isFSymbol()
&& !e->isSystemText()
&& !e->isInstrumentChange()
&& !e->isArticulation()
&& !e->isFermata()
&& !e->isStaffText()) {
// annotations added here are candidates for collision detection
// lyrics, ...
s.add(e->shape().translated(e->pos()));
}
}
}
//---------------------------------------------------------
// minRight
// calculate minimum distance needed to the right
//---------------------------------------------------------
qreal Segment::minRight() const
{
qreal distance = 0.0;
for (const Shape& sh : shapes())
distance = qMax(distance, sh.right());
if (isClefType())
distance += score()->styleP(Sid::clefBarlineDistance);
return distance;
}
//---------------------------------------------------------
// minLeft
// Calculate minimum distance needed to the left shape
// sl. Sl is the same for all staves.
//---------------------------------------------------------
qreal Segment::minLeft(const Shape& sl) const
{
qreal distance = 0.0;
for (const Shape& sh : shapes()) {
qreal d = sl.minHorizontalDistance(sh);
if (d > distance)
distance = d;
}
return distance;
}
qreal Segment::minLeft() const
{
qreal distance = 0.0;
for (const Shape& sh : shapes()) {
qreal l = sh.left();
if (l > distance)
distance = l;
}
return distance;
}
//---------------------------------------------------------
// minHorizontalCollidingDistance
// calculate the minimum distance to ns avoiding collisions
//---------------------------------------------------------
qreal Segment::minHorizontalCollidingDistance(Segment* ns) const
{
qreal w = 0.0;
for (unsigned staffIdx = 0; staffIdx < _shapes.size(); ++staffIdx) {
qreal d = staffShape(staffIdx).minHorizontalDistance(ns->staffShape(staffIdx));
w = qMax(w, d);
}
return w;
}
//---------------------------------------------------------
// minHorizontalDistance
// calculate the minimum layout distance to Segment ns
//---------------------------------------------------------
qreal Segment::minHorizontalDistance(Segment* ns, bool systemHeaderGap) const
{
qreal ww = -1000000.0; // can remain negative
for (unsigned staffIdx = 0; staffIdx < _shapes.size(); ++staffIdx) {
qreal d = ns ? staffShape(staffIdx).minHorizontalDistance(ns->staffShape(staffIdx)) : 0.0;
// first chordrest of a staff should clear the widest header for any staff
// so make sure segment is as wide as it needs to be
if (systemHeaderGap)
d = qMax(d, staffShape(staffIdx).right());
ww = qMax(ww, d);
}
qreal w = qMax(ww, 0.0); // non-negative
SegmentType st = segmentType();
SegmentType nst = ns ? ns->segmentType() : SegmentType::Invalid;
if (isChordRestType()) {
if (nst == SegmentType::EndBarLine) {
w = qMax(w, score()->noteHeadWidth());
w += score()->styleP(Sid::noteBarDistance);
}
else if (nst == SegmentType::Clef) {
// clef likely does not exist on all staves
// and can cause very uneven spacing
// so use ww to avoid forcing margin except as necessary
w = ww + score()->styleP(Sid::clefLeftMargin);
}
else {
bool isGap = false;
for (int i = 0; i < score()->nstaves() * VOICES; i++) {
Element* el = element(i);
if (!el)
continue;
if (el->isRest() && toRest(el)->isGap())
isGap = true;
else {
isGap = false;
break;
}
}
if (isGap)
return 0.0;
// minimum distance between notes is one note head width
w = qMax(w, score()->noteHeadWidth()) + score()->styleP(Sid::minNoteDistance);
}
}
else if (nst == SegmentType::ChordRest) {
// <non ChordRest> - <ChordRest>
if (systemHeaderGap) {
if (st == SegmentType::TimeSig)
w += score()->styleP(Sid::systemHeaderTimeSigDistance);
else
w += score()->styleP(Sid::systemHeaderDistance);
}
else {
// qreal d = score()->styleP(Sid::barNoteDistance);
// qreal dd = minRight() + ns->minLeft() + spatium();
// w = qMax(d, dd);
// not header
if (st == SegmentType::Clef)
w = ww + score()->styleP(Sid::midClefKeyRightMargin);
else if (st == SegmentType::KeySig)
w += score()->styleP(Sid::midClefKeyRightMargin);
else
w += score()->styleP(Sid::barNoteDistance);
if (st == SegmentType::StartRepeatBarLine) {
if (Element* barLine = element(0)) {
const qreal blWidth = barLine->width();
if (w < blWidth)
w += blWidth;
}
}
}
// d -= ns->minLeft() * .7; // hack
// d = qMax(d, ns->minLeft());
// d = qMax(d, spatium()); // minimum distance is one spatium
// w = qMax(w, minRight()) + d;
}
else if (systemHeaderGap) {
// first segment after header is *not* a chordrest
// could be a clef
if (st == SegmentType::TimeSig)
w += score()->styleP(Sid::systemHeaderTimeSigDistance);
else
w += score()->styleP(Sid::systemHeaderDistance);
}
else if (st & (SegmentType::Clef | SegmentType::HeaderClef)) {
if (nst == SegmentType::KeySig || nst == SegmentType::KeySigAnnounce)
w += score()->styleP(Sid::clefKeyDistance);
else if (nst == SegmentType::TimeSig || nst == SegmentType::TimeSigAnnounce)
w += score()->styleP(Sid::clefTimesigDistance);
else if (nst & (SegmentType::EndBarLine | SegmentType::StartRepeatBarLine))
w += score()->styleP(Sid::clefBarlineDistance);
else if (nst == SegmentType::Ambitus)
w += score()->styleP(Sid::ambitusMargin);
}
else if ((st & (SegmentType::KeySig | SegmentType::KeySigAnnounce))
&& (nst & (SegmentType::TimeSig | SegmentType::TimeSigAnnounce))) {
w += score()->styleP(Sid::keyTimesigDistance);
}
else if (st == SegmentType::KeySig && nst == SegmentType::StartRepeatBarLine)
w += score()->styleP(Sid::keyBarlineDistance);
else if (st == SegmentType::StartRepeatBarLine)
w += score()->styleP(Sid::noteBarDistance);
else if (st == SegmentType::BeginBarLine && (nst & (SegmentType::HeaderClef | SegmentType::Clef)))
w += score()->styleP(Sid::clefLeftMargin);
else if (st == SegmentType::BeginBarLine && nst == SegmentType::KeySig)
w += score()->styleP(Sid::keysigLeftMargin);
else if (st == SegmentType::EndBarLine) {
if (nst == SegmentType::KeySigAnnounce)
w += score()->styleP(Sid::keysigLeftMargin);
else if (nst == SegmentType::TimeSigAnnounce)
w += score()->styleP(Sid::timesigLeftMargin);
else if (nst == SegmentType::Clef)
w += score()->styleP(Sid::clefLeftMargin);
}
else if (st == SegmentType::TimeSig && nst == SegmentType::StartRepeatBarLine)
w += score()->styleP(Sid::timesigBarlineDistance);
else if (st == SegmentType::Breath)
w += spatium() * 1.5;
else if (st == SegmentType::Ambitus)
w += score()->styleP(Sid::ambitusMargin);
if (w < 0.0)
w = 0.0;
if (ns)
w += ns->extraLeadingSpace().val() * spatium();
return w;
}
} // namespace Ms