//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2010-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 "score.h"
#include "spanner.h"
#include "system.h"
#include "chordrest.h"
#include "chord.h"
#include "segment.h"
#include "measure.h"
#include "undo.h"
#include "staff.h"
namespace Ms {
//---------------------------------------------------------
// SpannerSegment
//---------------------------------------------------------
SpannerSegment::SpannerSegment(Score* s)
: Element(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::SEGMENT | ElementFlag::ON_STAFF);
setSpannerSegmentType(SpannerSegmentType::SINGLE);
_spanner = 0;
}
SpannerSegment::SpannerSegment(const SpannerSegment& s)
: Element(s)
{
_spanner = s._spanner;
_spannerSegmentType = s._spannerSegmentType;
}
//---------------------------------------------------------
// system
//---------------------------------------------------------
System* SpannerSegment::system() const
{
return toSystem(parent());
}
//---------------------------------------------------------
// setSystem
//---------------------------------------------------------
void SpannerSegment::setSystem(System* s)
{
if (system() != s) {
if (system())
system()->remove(this);
if (s)
s->add(this);
else
setParent(0);
}
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant SpannerSegment::getProperty(P_ID id) const
{
switch (id) {
case P_ID::COLOR:
case P_ID::VISIBLE:
return spanner()->getProperty(id);
case P_ID::USER_OFF2:
return _userOff2;
default:
return Element::getProperty(id);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool SpannerSegment::setProperty(P_ID id, const QVariant& v)
{
switch (id) {
case P_ID::COLOR:
case P_ID::VISIBLE:
return spanner()->setProperty(id, v);
case P_ID::USER_OFF2:
_userOff2 = v.toPointF();
score()->setLayoutAll();
break;
default:
return Element::setProperty(id, v);
}
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant SpannerSegment::propertyDefault(P_ID id) const
{
switch (id) {
case P_ID::COLOR:
case P_ID::VISIBLE:
return spanner()->propertyDefault(id);
case P_ID::USER_OFF2:
return QVariant();
default:
return Element::propertyDefault(id);
}
}
//---------------------------------------------------------
// reset
//---------------------------------------------------------
void SpannerSegment::reset()
{
undoChangeProperty(P_ID::USER_OFF2, QPointF());
Element::reset();
spanner()->reset();
}
//---------------------------------------------------------
// setSelected
//---------------------------------------------------------
void SpannerSegment::setSelected(bool f)
{
for (SpannerSegment* ss : _spanner->spannerSegments())
ss->Element::setSelected(f);
_spanner->setSelected(f);
}
//---------------------------------------------------------
// setVisible
//---------------------------------------------------------
void SpannerSegment::setVisible(bool f)
{
if (_spanner) {
for (SpannerSegment* ss : _spanner->spannerSegments())
ss->Element::setVisible(f);
_spanner->setVisible(f);
}
else
Element::setVisible(f);
}
//---------------------------------------------------------
// setColor
//---------------------------------------------------------
void SpannerSegment::setColor(const QColor& col)
{
if (_spanner) {
for (SpannerSegment* ss : _spanner->spannerSegments())
ss->_color = col;
_spanner->_color = col;
}
else
_color = col;
}
//---------------------------------------------------------
// nextSegmentElement
//---------------------------------------------------------
Element* SpannerSegment::nextSegmentElement()
{
return spanner()->nextSegmentElement();
}
//---------------------------------------------------------
// prevSegmentElement
//---------------------------------------------------------
Element* SpannerSegment::prevSegmentElement()
{
return spanner()->prevSegmentElement();
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString SpannerSegment::accessibleInfo() const
{
return spanner()->accessibleInfo();
}
//---------------------------------------------------------
// styleChanged
//---------------------------------------------------------
void SpannerSegment::styleChanged()
{
_spanner->styleChanged();
}
//---------------------------------------------------------
// triggerLayout
//---------------------------------------------------------
void SpannerSegment::triggerLayout() const
{
_spanner->triggerLayout();
}
//---------------------------------------------------------
// Spanner
//---------------------------------------------------------
Spanner::Spanner(Score* s)
: Element(s)
{
}
Spanner::Spanner(const Spanner& s)
: Element(s)
{
_anchor = s._anchor;
_startElement = s._startElement;
_endElement = s._endElement;
_tick = s._tick;
_ticks = s._ticks;
_track2 = s._track2;
}
Spanner::~Spanner()
{
qDeleteAll(spannerSegments());
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Spanner::add(Element* e)
{
SpannerSegment* ls = static_cast<SpannerSegment*>(e);
ls->setSpanner(this);
ls->setSelected(selected());
ls->setTrack(track());
segments.append(ls);
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Spanner::remove(Element* e)
{
SpannerSegment* ss = static_cast<SpannerSegment*>(e);
if (ss->system())
ss->system()->remove(ss);
segments.removeOne(ss);
}
//---------------------------------------------------------
// removeUnmanaged
//
// Remove the Spanner and its segments from objects which may know about them
//
// This method and the following are used for spanners which are contained within compound elements
// which manage their parts themselves without using the standard management supplied by Score;
// Example can be the LyricsLine within a Lyrics element or the FiguredBassLine within a FiguredBass
// (not implemented yet).
//---------------------------------------------------------
void Spanner::removeUnmanaged()
{
for (SpannerSegment* ss : spannerSegments())
if (ss->system()) {
// ss->system()->remove(ss);
ss->setSystem(nullptr);
}
score()->removeUnmanagedSpanner(this);
}
//---------------------------------------------------------
// undoInserTimeUnmanaged
//---------------------------------------------------------
void Spanner::undoInsertTimeUnmanaged(int fromTick, int len)
{
int newTick1 = tick();
int newTick2 = tick2();
// check spanner start and end point
if (len > 0) { // adding time
if (tick() > fromTick) // start after insertion point: shift start to right
newTick1 += len;
if (tick2() > fromTick) // end after insertion point: shift end to right
newTick2 += len;
}
if (len < 0) { // removing time
int toTick = fromTick - len;
if (tick() > fromTick) { // start after beginning of removed time
if (tick() < toTick) { // start within removed time: bring start at removing point
if (parent()) {
parent()->remove(this);
return;
}
else
newTick1 = fromTick;
}
else // start after removed time: shift start to left
newTick1 += len;
}
if (tick2() > fromTick) { // end after start of removed time
if (tick2() < toTick) // end within removed time: bring end at removing point
newTick2 = fromTick;
else // end after removed time: shift end to left
newTick2 += len;
}
}
// update properties as required
if (newTick2 <= newTick1) { // if no longer any span: remove it
if (parent())
parent()->remove(this);
}
else { // if either TICKS or TICK did change, update property
if (newTick2-newTick1 != tick2()- tick())
setProperty(P_ID::SPANNER_TICKS, newTick2-newTick1);
if (newTick1 != tick())
setProperty(P_ID::SPANNER_TICK, newTick1);
}
}
//---------------------------------------------------------
// scanElements
// used in palettes
//---------------------------------------------------------
void Spanner::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
Q_UNUSED(all);
for (SpannerSegment* seg : segments)
seg->scanElements(data, func, true);
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Spanner::setScore(Score* s)
{
Element::setScore(s);
foreach(SpannerSegment* seg, segments)
seg->setScore(s);
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Spanner::getProperty(P_ID propertyId) const
{
switch (propertyId) {
case P_ID::SPANNER_TICK:
return tick();
case P_ID::SPANNER_TICKS:
return ticks();
case P_ID::SPANNER_TRACK2:
return track2();
case P_ID::ANCHOR:
return int(anchor());
default:
break;
}
return Element::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Spanner::setProperty(P_ID propertyId, const QVariant& v)
{
switch (propertyId) {
case P_ID::SPANNER_TICK:
setTick(v.toInt());
setStartElement(0); // invalidate
setEndElement(0); //
if (score() && score()->spannerMap().removeSpanner(this))
score()->addSpanner(this);
break;
case P_ID::SPANNER_TICKS:
setTicks(v.toInt());
setEndElement(0); // invalidate
break;
case P_ID::TRACK:
setTrack(v.toInt());
setStartElement(0); // invalidate
break;
case P_ID::SPANNER_TRACK2:
setTrack2(v.toInt());
setEndElement(0); // invalidate
break;
case P_ID::ANCHOR:
setAnchor(Anchor(v.toInt()));
break;
default:
return Element::setProperty(propertyId, v);
}
triggerLayout();
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Spanner::propertyDefault(P_ID propertyId) const
{
switch (propertyId) {
case P_ID::ANCHOR:
return int(Anchor::SEGMENT);
default:
break;
}
return Element::propertyDefault(propertyId);
}
//---------------------------------------------------------
// computeStartElement
//---------------------------------------------------------
void Spanner::computeStartElement()
{
switch (_anchor) {
case Anchor::SEGMENT: {
Segment* seg = score()->tick2segmentMM(tick(), false, SegmentType::ChordRest);
int strack = (track() / VOICES) * VOICES;
int etrack = strack + VOICES;
_startElement = 0;
if (seg) {
for (int t = strack; t < etrack; ++t) {
if (seg->element(t)) {
_startElement = seg->element(t);
break;
}
}
}
}
break;
case Anchor::MEASURE:
_startElement = score()->tick2measure(tick());
break;
case Anchor::CHORD:
case Anchor::NOTE:
return;
}
}
//---------------------------------------------------------
// computeEndElement
//---------------------------------------------------------
void Spanner::computeEndElement()
{
switch (_anchor) {
case Anchor::SEGMENT: {
if (track2() == -1)
setTrack2(track());
if (ticks() == 0 && isTextLine())
setTicks(score()->lastSegment()->tick() - _tick);
// find last cr on this staff that ends before tick2
_endElement = score()->findCRinStaff(tick2(), track2() / VOICES);
if (!_endElement) {
qDebug("%s no end element for tick %d", name(), tick2());
return;
}
if (!endCR()->measure()->isMMRest()) {
ChordRest* cr = endCR();
int nticks = cr->tick() + cr->actualTicks() - _tick;
// allow fudge factor for tuplets
// TODO: replace with fraction-based calculation
int fudge = cr->tuplet() ? 5 : 0;
if (qAbs(_ticks - nticks) > fudge) {
qDebug("%s ticks changed, %d -> %d", name(), _ticks, nticks);
setTicks(nticks);
if (type() == ElementType::OTTAVA)
staff()->updateOttava();
}
}
}
break;
case Anchor::MEASURE:
_endElement = score()->tick2measure(tick2() - 1);
if (!_endElement) {
qDebug("Spanner::computeEndElement(), measure not found for tick %d\n", tick2()-1);
_endElement = score()->lastMeasure();
}
break;
case Anchor::CHORD:
case Anchor::NOTE:
break;
}
}
//---------------------------------------------------------
// startElementFromSpanner
//
// Given a Spanner and an end element, determines a start element suitable for the end
// element of a new Spanner, so that it is 'parallel' to the old one.
// Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
// (Spanner(const Spanner&) copies start and end elements from the original to the copy).
// NOTES: Only spanners with Anchor::NOTE are currently supported.
// Going back from end to start ensures the 'other' anchor of this is already set up
// (for instance, while cloning staves)
//---------------------------------------------------------
Note* Spanner::startElementFromSpanner(Spanner* sp, Element* newEnd)
{
if (sp->anchor() != Anchor::NOTE)
return nullptr;
Note* oldStart = static_cast<Note*>(sp->startElement());
Note* oldEnd = static_cast<Note*>(sp->endElement());
if (oldStart == nullptr || oldEnd == nullptr)
return nullptr;
Note* newStart = nullptr;
Score* score = newEnd->score();
// determine the track where to expect the 'parallel' start element
int newTrack = (newEnd->track() - oldEnd->track()) + oldStart->track();
// look in notes linked to oldStart for a note with the
// same score as new score and appropriate track
for (ScoreElement* newEl : oldStart->linkList())
if (static_cast<Note*>(newEl)->score() == score
&& static_cast<Note*>(newEl)->track() == newTrack) {
newStart = static_cast<Note*>(newEl);
break;
}
return newStart;
}
//---------------------------------------------------------
// endElementFromSpanner
//
// Given a Spanner and a start element, determines an end element suitable for the start
// element of a new Spanner, so that it is 'parallel' to the old one.
// Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
// (Spanner(const Spanner&) copies start and end elements from the original to the copy).
// NOTES: Only spanners with Anchor::NOTE are currently supported.
//---------------------------------------------------------
Note* Spanner::endElementFromSpanner(Spanner* sp, Element* newStart)
{
if (sp->anchor() != Anchor::NOTE)
return nullptr;
Note* oldStart = static_cast<Note*>(sp->startElement());
Note* oldEnd = static_cast<Note*>(sp->endElement());
if (oldStart == nullptr || oldEnd == nullptr)
return nullptr;
Note* newEnd = nullptr;
Score* score = newStart->score();
// determine the track where to expect the 'parallel' start element
int newTrack = newStart->track() + (oldEnd->track() - oldStart->track());
// look in notes linked to oldEnd for a note with the
// same score as new score and appropriate track
for (ScoreElement* newEl : oldEnd->linkList())
if (static_cast<Note*>(newEl)->score() == score
&& static_cast<Note*>(newEl)->track() == newTrack) {
newEnd = static_cast<Note*>(newEl);
break;
}
return newEnd;
}
//---------------------------------------------------------
// setNoteSpan
//
// Sets up all the variables congruent with given start and end note anchors.
//---------------------------------------------------------
void Spanner::setNoteSpan(Note* startNote, Note* endNote)
{
if (_anchor != Anchor::NOTE)
return;
setScore(startNote->score());
setParent(startNote);
setStartElement(startNote);
setEndElement(endNote);
setTick(startNote->chord()->tick());
setTick2(endNote->chord()->tick());
setTrack(startNote->track());
setTrack2(endNote->track());
}
//---------------------------------------------------------
// startChord
//---------------------------------------------------------
Chord* Spanner::startChord()
{
Q_ASSERT(_anchor == Anchor::CHORD);
if (!_startElement)
_startElement = score()->findCR(tick(), track());
Q_ASSERT(_startElement->type() == ElementType::CHORD);
return static_cast<Chord*>(_startElement);
}
//---------------------------------------------------------
// endChord
//---------------------------------------------------------
Chord* Spanner::endChord()
{
Q_ASSERT(_anchor == Anchor::CHORD);
if (!_endElement && type() == ElementType::SLUR) {
Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
_endElement = s ? static_cast<ChordRest*>(s->element(track2())) : nullptr;
if (_endElement->type() != ElementType::CHORD)
_endElement = nullptr;
}
return static_cast<Chord*>(_endElement);
}
//---------------------------------------------------------
// startCR
//---------------------------------------------------------
ChordRest* Spanner::startCR()
{
Q_ASSERT(_anchor == Anchor::SEGMENT || _anchor == Anchor::CHORD);
if (!_startElement || _startElement->score() != score())
_startElement = score()->findCR(tick(), track());
return static_cast<ChordRest*>(_startElement);
}
//---------------------------------------------------------
// endCR
//---------------------------------------------------------
ChordRest* Spanner::endCR()
{
Q_ASSERT(_anchor == Anchor::SEGMENT || _anchor == Anchor::CHORD);
if ((!_endElement || _endElement->score() != score())) {
Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
_endElement = s ? toChordRest(s->element(track2())) : 0;
}
return toChordRest(_endElement);
}
//---------------------------------------------------------
// startSegment
//---------------------------------------------------------
Segment* Spanner::startSegment() const
{
Q_ASSERT(score() != NULL);
return score()->tick2rightSegment(tick());
}
//---------------------------------------------------------
// endSegment
//---------------------------------------------------------
Segment* Spanner::endSegment() const
{
return score()->tick2leftSegment(tick2());
}
//---------------------------------------------------------
// startMeasure
//---------------------------------------------------------
Measure* Spanner::startMeasure() const
{
Q_ASSERT(!_startElement || _startElement->type() == ElementType::MEASURE);
return static_cast<Measure*>(_startElement);
}
//---------------------------------------------------------
// endMeasure
//---------------------------------------------------------
Measure* Spanner::endMeasure() const
{
Q_ASSERT(!_endElement || _endElement->type() == ElementType::MEASURE);
return static_cast<Measure*>(_endElement);
}
//---------------------------------------------------------
// setSelected
//---------------------------------------------------------
void Spanner::setSelected(bool f)
{
for (SpannerSegment* ss : spannerSegments())
ss->Element::setSelected(f);
Element::setSelected(f);
}
//---------------------------------------------------------
// setVisible
//---------------------------------------------------------
void Spanner::setVisible(bool f)
{
for (SpannerSegment* ss : spannerSegments())
ss->Element::setVisible(f);
Element::setVisible(f);
}
//---------------------------------------------------------
// setColor
//---------------------------------------------------------
void Spanner::setColor(const QColor& col)
{
for (SpannerSegment* ss : spannerSegments())
ss->setColor(col);
_color = col;
}
//---------------------------------------------------------
// setStartElement
//---------------------------------------------------------
void Spanner::setStartElement(Element* e)
{
#ifndef NDEBUG
if (_anchor == Anchor::NOTE)
Q_ASSERT(!e || e->type() == ElementType::NOTE);
#endif
_startElement = e;
}
//---------------------------------------------------------
// setEndElement
//---------------------------------------------------------
void Spanner::setEndElement(Element* e)
{
#ifndef NDEBUG
if (_anchor == Anchor::NOTE)
Q_ASSERT(!e || e->type() == ElementType::NOTE);
#endif
_endElement = e;
}
//---------------------------------------------------------
// nextSpanner
//---------------------------------------------------------
Spanner* Spanner::nextSpanner(Element* e, int activeStaff)
{
std::multimap<int, Spanner*> mmap = score()->spanner();
auto range = mmap.equal_range(tick());
if (range.first != range.second) { // range not empty
for (auto i = range.first; i != range.second; ++i) {
if (i->second == e) {
while (i != range.second) {
++i;
if (i == range.second)
return nullptr;
Spanner* s = i->second;
Element* st = s->startElement();
if (!st)
continue;
if (s->startSegment() == static_cast<Spanner*>(e)->startSegment() &&
st->staffIdx() == activeStaff)
return s;
//else
//return nullptr;
}
break;
/* else {
break;
}*/
}
}
}
return nullptr;
}
//---------------------------------------------------------
// prevSpanner
//---------------------------------------------------------
Spanner* Spanner::prevSpanner(Element* e, int activeStaff)
{
std::multimap<int, Spanner*> mmap = score()->spanner();
auto range = mmap.equal_range(tick());
if (range.first != range.second) { // range not empty
for (auto i = range.first; i != range.second; ++i) {
if (i->second == e) {
if (i == range.first)
return nullptr;
while (i != range.first) {
--i;
Spanner* s = i->second;
if (s->startSegment() == static_cast<Spanner*>(e)->startSegment() &&
s->startElement()->staffIdx() == activeStaff)
return s;
}
break;
}
}
}
return nullptr;
}
//---------------------------------------------------------
// nextSegmentElement
//---------------------------------------------------------
Element* Spanner::nextSegmentElement()
{
Segment* s = startSegment();
if (s)
return s->firstElement(staffIdx());
return score()->firstElement();
}
//---------------------------------------------------------
// prevSegmentElement
//---------------------------------------------------------
Element* Spanner::prevSegmentElement()
{
Segment* s = endSegment();
if (s)
return s->lastElement(staffIdx());
return score()->lastElement();
}
//---------------------------------------------------------
// setTick
//---------------------------------------------------------
void Spanner::setTick(int v)
{
_tick = v;
if (score())
score()->spannerMap().setDirty();
}
//---------------------------------------------------------
// setTick2
//---------------------------------------------------------
void Spanner::setTick2(int v)
{
setTicks(v - _tick);
}
//---------------------------------------------------------
// setTicks
//---------------------------------------------------------
void Spanner::setTicks(int v)
{
_ticks = v;
if (score())
score()->spannerMap().setDirty();
}
//---------------------------------------------------------
// triggerLayout
//---------------------------------------------------------
void Spanner::triggerLayout() const
{
score()->setLayout(_tick);
score()->setLayout(_tick + _ticks);
}
//---------------------------------------------------------
// layoutSystem
//---------------------------------------------------------
SpannerSegment* Spanner::layoutSystem(System*)
{
qDebug(" %s", name());
return 0;
}
}
↑ V592 The expression was enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or misprint is present.