MuseScore/libmscore/segment.cpp
ws ec3be9a99a Replacd integer midi tick values by fractions.
- tick names a position on the time axis
- tick is always a Fraction()
- only Measure() and Segment() (and Tuplet?) have a tick value
- tick() for an generic element return only a sensible value if isMeasure() or isSegment() or isSegment(parent())

- "ticks" names a duration stored in a Fraction()
- the tick value for an Segment is relative to its measure

- rename "duration" to "ticks"
- rename afrac() to tick()
- rename rfrac() to rtick()
- rename some variables, changing "fraction" into "tick"
  (example: actualFraction() into actualTicks())

- Lyrics ticks are written as Fraction, on read if xmlreader sees a "/" it reads a fraction
  else midi ticks for backwards compatibility
2019-02-18 11:46:05 +01:00

2080 lines
71 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"
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)
{
_next = 0;
_prev = 0;
_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;
}
}
//---------------------------------------------------------
// 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());
_prev = 0;
_next = 0;
}
//---------------------------------------------------------
// 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;
}
//---------------------------------------------------------
// 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()));
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:
_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());
}
// 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));
}
}
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:
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());
}
score()->setLayout(tick());
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);
score()->setLayout(tick());
}
//---------------------------------------------------------
// 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>());
break;
default:
return Element::setProperty(propertyId, v);
}
score()->setLayout(tick());
return true;
}
//---------------------------------------------------------
// 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;
}
//---------------------------------------------------------
// 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;
for (int curTrack = minTrack; curTrack <= maxTrack; curTrack++)
if (element(curTrack))
return true;
return false;
}
//---------------------------------------------------------
// 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 elemnt 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);
return 0;
}
//---------------------------------------------------------
// nextAnnotation
// return next element in _annotations
//---------------------------------------------------------
Element* Segment::nextAnnotation(Element* e)
{
if (e == _annotations.back())
return nullptr;
auto i = std::find(_annotations.begin(), _annotations.end(), e);
Element* next = *(i+1);
if (next && next->staffIdx() == e->staffIdx())
return next;
return nullptr;
}
//---------------------------------------------------------
// prevAnnotation
// return previous element in _annotations
//---------------------------------------------------------
Element* Segment::prevAnnotation(Element* e)
{
if (e == _annotations.front())
return nullptr;
auto i = std::find(_annotations.begin(), _annotations.end(), e);
Element* prev = *(i-1);
if (prev && prev->staffIdx() == e->staffIdx())
return prev;
return nullptr;
}
//---------------------------------------------------------
// firstAnnotation
//---------------------------------------------------------
Element* Segment::firstAnnotation(Segment* s, int activeStaff)
{
for (auto i = s->annotations().begin(); i != s->annotations().end(); ++i) {
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) {
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
seg = score()->firstSegment(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)
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 && s->startElement()->staffIdx() == activeStaff)
return s;
}
}
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 != range.first; --i) {
Spanner* s = i->second;
Element* st = s->startElement();
if (!st)
continue;
if (s->startElement()->staffIdx() == activeStaff)
return s;
}
if ((range.first)->second->startElement()->staffIdx() == activeStaff) {
return (range.first)->second;
}
}
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::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: {
Element* next = nextAnnotation(e);
if (next)
return next;
else {
Spanner* s = firstSpanner(activeStaff);
if (s)
return s->spannerSegments().front();
}
Segment* nextSegment = this->next1enabled();
while (nextSegment) {
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1enabled();
}
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->next1enabled();
while (nextSegment) {
Element* nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1enabled();
}
break;
}
default: {
Element* p;
if (e->isTieSegment() || e->isGlissandoSegment()) {
SpannerSegment* s = toSpannerSegment(e);
Spanner* sp = s->spanner();
p = sp->startElement();
}
else if (e->type() == ElementType::ACCIDENTAL ||
e->type() == ElementType::ARTICULATION) {
p = e->parent();
}
else {
p = e;
}
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->next1enabled();
while (nextSegment) {
nextEl = nextSegment->firstElementOfSegment(nextSegment, activeStaff);
if (nextEl)
return nextEl;
nextSegment = nextSegment->next1enabled();
}
}
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::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: {
Element* 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->prev1enabled();
}
}
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 if (e->type() == ElementType::ACCIDENTAL ||
e->type() == ElementType::ARTICULATION) {
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->prev1enabled();
if (!prevSeg)
return score()->lastElement();
prev = lastElementOfSegment(prevSeg, activeStaff);
while (!prev && prevSeg) {
prevSeg = prevSeg->prev1enabled();
prev = lastElementOfSegment(prevSeg, activeStaff);
}
if (!prevSeg)
return score()->lastElement();
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()->lastSegment();
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->prev1enabled();
}
}
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 ") + 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 ") + s->accessibleInfo();
}
return rez + " " + startSpanners + " " + endSpanners;
}
//---------------------------------------------------------
// createShapes
//---------------------------------------------------------
void Segment::createShapes()
{
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)) {
BarLine* bl = toBarLine(element(0));
if (bl) {
qreal w = BarLine::layoutWidth(score(), bl->barLineType());
#ifndef NDEBUG
s.add(QRectF(0.0, 0.0, w, spatium() * 4.0).translated(bl->pos()), bl->name());
#else
s.add(QRectF(0.0, 0.0, w, spatium() * 4.0).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
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)
s.add(e->shape().translated(e->pos()));
}
for (Element* e : _annotations) {
if (e->staffIdx() == staffIdx // whats left?
&& !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())
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 w = 0.0;
for (unsigned staffIdx = 0; staffIdx < _shapes.size(); ++staffIdx) {
qreal d = staffShape(staffIdx).minHorizontalDistance(ns->staffShape(staffIdx));
w = qMax(w, d);
}
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) {
w = qMax(w, 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);
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 (st & (SegmentType::Clef | SegmentType::HeaderClef)) {
if (nst == SegmentType::KeySig)
w += score()->styleP(Sid::clefKeyDistance);
else if (nst == SegmentType::TimeSig)
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 (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