MuseScore/libmscore/measure.cpp
anatoly-os 08a16c86af Merge pull request #6073 from IsaacWeiss/305265-bracket-span
Fix #305265: Bracket added from palette only spans one staff regardless of selection
2020-05-20 14:14:03 +02:00

4408 lines
166 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-2016 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
//=============================================================================
/**
\file
Implementation of most part of class Measure.
*/
#include "log.h"
#include "measure.h"
#include "accidental.h"
#include "ambitus.h"
#include "articulation.h"
#include "barline.h"
#include "beam.h"
#include "box.h"
#include "bracket.h"
#include "breath.h"
#include "chord.h"
#include "clef.h"
#include "drumset.h"
#include "duration.h"
#include "dynamic.h"
#include "fermata.h"
#include "fret.h"
#include "glissando.h"
#include "hairpin.h"
#include "harmony.h"
#include "hook.h"
#include "icon.h"
#include "image.h"
#include "key.h"
#include "keysig.h"
#include "layoutbreak.h"
#include "layout.h"
#include "note.h"
#include "ottava.h"
#include "page.h"
#include "part.h"
#include "pedal.h"
#include "pitchspelling.h"
#include "repeat.h"
#include "rest.h"
#include "score.h"
#include "segment.h"
#include "select.h"
#include "sig.h"
#include "slur.h"
#include "spacer.h"
#include "staff.h"
#include "stafftext.h"
#include "stafftype.h"
#include "stringdata.h"
#include "style.h"
#include "sym.h"
#include "system.h"
#include "tempotext.h"
#include "measurenumber.h"
#include "tie.h"
#include "tiemap.h"
#include "timesig.h"
#include "tremolo.h"
#include "trill.h"
#include "tuplet.h"
#include "tupletmap.h"
#include "undo.h"
#include "utils.h"
#include "volta.h"
#include "xml.h"
#include "systemdivider.h"
#include "stafftypechange.h"
#include "stafflines.h"
#include "bracketItem.h"
namespace Ms {
//---------------------------------------------------------
// MStaff
/// Per staff values of measure.
//---------------------------------------------------------
class MStaff {
MeasureNumber* _noText { 0 }; ///< Measure number text object
StaffLines* _lines { 0 };
Spacer* _vspacerUp { 0 };
Spacer* _vspacerDown { 0 };
bool _hasVoices { false }; ///< indicates that MStaff contains more than one voice,
///< this changes some layout rules
bool _visible { true };
bool _stemless { false };
#ifndef NDEBUG
bool _corrupted { false };
#endif
public:
MStaff() {}
~MStaff();
MStaff(const MStaff&);
void setScore(Score*);
void setTrack(int);
MeasureNumber* noText() const { return _noText; }
void setNoText(MeasureNumber* t) { _noText = t; }
StaffLines* lines() const { return _lines; }
void setLines(StaffLines* l) { _lines = l; }
Spacer* vspacerUp() const { return _vspacerUp; }
void setVspacerUp(Spacer* s) { _vspacerUp = s; }
Spacer* vspacerDown() const { return _vspacerDown; }
void setVspacerDown(Spacer* s) { _vspacerDown = s; }
bool hasVoices() const { return _hasVoices; }
void setHasVoices(bool val) { _hasVoices = val; }
bool visible() const { return _visible; }
void setVisible(bool val) { _visible = val; }
bool stemless() const { return _stemless; }
void setStemless(bool val) { _stemless = val; }
#ifndef NDEBUG
bool corrupted() const { return _corrupted; }
void setCorrupted(bool val) { _corrupted = val; }
#endif
};
MStaff::~MStaff()
{
delete _noText;
delete _lines;
delete _vspacerUp;
delete _vspacerDown;
}
MStaff::MStaff(const MStaff& m)
{
_noText = 0;
_lines = new StaffLines(*m._lines);
_hasVoices = m._hasVoices;
_vspacerUp = 0;
_vspacerDown = 0;
_visible = m._visible;
_stemless = m._stemless;
#ifndef NDEBUG
_corrupted = m._corrupted;
#endif
}
//---------------------------------------------------------
// MStaff::setScore
//---------------------------------------------------------
void MStaff::setScore(Score* score)
{
if (_lines)
_lines->setScore(score);
if (_vspacerUp)
_vspacerUp->setScore(score);
if (_vspacerDown)
_vspacerDown->setScore(score);
if (_noText)
_noText->setScore(score);
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void MStaff::setTrack(int track)
{
if (_lines)
_lines->setTrack(track);
if (_vspacerUp)
_vspacerUp->setTrack(track);
if (_vspacerDown)
_vspacerDown->setTrack(track);
if (_noText)
_noText->setTrack(track);
}
//---------------------------------------------------------
// Measure
//---------------------------------------------------------
Measure::Measure(Score* s)
: MeasureBase(s), _timesig(4,4)
{
setTicks(Fraction(4,4));
_repeatCount = 2;
int n = score()->nstaves();
_mstaves.reserve(n);
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
MStaff* ms = new MStaff;
Staff* staff = score()->staff(staffIdx);
ms->setLines(new StaffLines(score()));
ms->lines()->setTrack(staffIdx * VOICES);
ms->lines()->setParent(this);
ms->lines()->setVisible(!staff->invisible());
_mstaves.push_back(ms);
}
setIrregular(false);
_noMode = MeasureNumberMode::AUTO;
_userStretch = 1.0;
_breakMultiMeasureRest = false;
_mmRest = 0;
_mmRestCount = 0;
setFlag(ElementFlag::MOVABLE, true);
}
//---------------------------------------------------------
// measure
//---------------------------------------------------------
Measure::Measure(const Measure& m)
: MeasureBase(m)
{
_segments = m._segments.clone();
_timesig = m._timesig;
_len = m._len;
_repeatCount = m._repeatCount;
_noMode = m._noMode;
_userStretch = m._userStretch;
_mstaves.reserve(m._mstaves.size());
for (MStaff* ms : m._mstaves)
_mstaves.push_back(new MStaff(*ms));
_breakMultiMeasureRest = m._breakMultiMeasureRest;
_mmRest = m._mmRest;
_mmRestCount = m._mmRestCount;
_playbackCount = m._playbackCount;
}
//---------------------------------------------------------
// layoutStaffLines
//---------------------------------------------------------
void Measure::layoutStaffLines()
{
for (MStaff* ms : _mstaves)
ms->lines()->layout();
}
//---------------------------------------------------------
// createStaves
//---------------------------------------------------------
void Measure::createStaves(int staffIdx)
{
for (int n = int(_mstaves.size()); n <= staffIdx; ++n) {
Staff* staff = score()->staff(n);
MStaff* s = new MStaff;
s->setLines(new StaffLines(score()));
s->lines()->setParent(this);
s->lines()->setTrack(n * VOICES);
s->lines()->setVisible(!staff->invisible());
_mstaves.push_back(s);
}
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Measure::setScore(Score* score)
{
MeasureBase::setScore(score);
for (Segment* s = first(); s; s = s->next())
s->setScore(score);
}
bool Measure::hasVoices(int staffIdx) const { return _mstaves[staffIdx]->hasVoices(); }
void Measure::setHasVoices(int staffIdx, bool v) { return _mstaves[staffIdx]->setHasVoices(v); }
StaffLines* Measure::staffLines(int staffIdx) { return _mstaves[staffIdx]->lines(); }
Spacer* Measure::vspacerDown(int staffIdx) const { return _mstaves[staffIdx]->vspacerDown(); }
Spacer* Measure::vspacerUp(int staffIdx) const { return _mstaves[staffIdx]->vspacerUp(); }
void Measure::setStaffVisible(int staffIdx, bool visible) { _mstaves[staffIdx]->setVisible(visible); }
void Measure::setStaffStemless(int staffIdx, bool stemless) { _mstaves[staffIdx]->setStemless(stemless); }
#ifndef NDEBUG
bool Measure::corrupted(int staffIdx) const { return _mstaves[staffIdx]->corrupted(); }
void Measure::setCorrupted(int staffIdx, bool val) { _mstaves[staffIdx]->setCorrupted(val); }
#endif
void Measure::setNoText(int staffIdx, MeasureNumber* t) { _mstaves[staffIdx]->setNoText(t); }
MeasureNumber* Measure::noText(int staffIdx) const { return _mstaves[staffIdx]->noText(); }
//---------------------------------------------------------
// Measure
//---------------------------------------------------------
Measure::~Measure()
{
for (Segment* s = first(); s;) {
Segment* ns = s->next();
delete s;
s = ns;
}
qDeleteAll(_mstaves);
}
//---------------------------------------------------------
// AcEl
//---------------------------------------------------------
struct AcEl {
Note* note;
qreal x;
};
//---------------------------------------------------------
// findAccidental
/// return current accidental value at note position
//---------------------------------------------------------
AccidentalVal Measure::findAccidental(Note* note) const
{
Chord* chord = note->chord();
AccidentalState tversatz; // state of already set accidentals for this measure
tversatz.init(chord->staff()->keySigEvent(tick()), chord->staff()->clef(tick()));
for (Segment* segment = first(); segment; segment = segment->next()) {
int startTrack = chord->staffIdx() * VOICES;
if (segment->isKeySigType()) {
KeySig* ks = toKeySig(segment->element(startTrack));
if (!ks)
continue;
tversatz.init(chord->staff()->keySigEvent(segment->tick()), chord->staff()->clef(segment->tick()));
}
else if (segment->segmentType() == SegmentType::ChordRest) {
int endTrack = startTrack + VOICES;
for (int track = startTrack; track < endTrack; ++track) {
Element* e = segment->element(track);
if (!e || !e->isChord())
continue;
Chord* crd = toChord(e);
for (Chord* chord1 : crd->graceNotes()) {
for (Note* note1 : chord1->notes()) {
if (note1->tieBack() && note1->accidental() == 0)
continue;
//
// compute accidental
//
int tpc = note1->tpc();
int line = absStep(tpc, note1->epitch());
if (note == note1)
return tversatz.accidentalVal(line);
tversatz.setAccidentalVal(line, tpc2alter(tpc));
}
}
for (Note* note1 : crd->notes()) {
if (note1->tieBack() && note1->accidental() == 0)
continue;
//
// compute accidental
//
int tpc = note1->tpc();
int line = absStep(tpc, note1->epitch());
if (note == note1)
return tversatz.accidentalVal(line);
tversatz.setAccidentalVal(line, tpc2alter(tpc));
}
}
}
}
qDebug("Measure::findAccidental: note not found");
return AccidentalVal::NATURAL;
}
//---------------------------------------------------------
// findAccidental
/// Compute accidental state at segment/staffIdx for
/// relative staff line.
//---------------------------------------------------------
AccidentalVal Measure::findAccidental(Segment* s, int staffIdx, int line, bool &error) const
{
AccidentalState tversatz; // state of already set accidentals for this measure
Staff* staff = score()->staff(staffIdx);
tversatz.init(staff->keySigEvent(tick()), staff->clef(tick()));
SegmentType st = SegmentType::ChordRest;
int startTrack = staffIdx * VOICES;
int endTrack = startTrack + VOICES;
for (Segment* segment = first(st); segment; segment = segment->next(st)) {
if (segment == s && staff->isPitchedStaff(tick())) {
ClefType clef = staff->clef(s->tick());
int l = relStep(line, clef);
return tversatz.accidentalVal(l, error);
}
for (int track = startTrack; track < endTrack; ++track) {
Element* e = segment->element(track);
if (!e || !e->isChord())
continue;
Chord* chord = toChord(e);
for (Chord* chord1 : chord->graceNotes()) {
for (Note* note : chord1->notes()) {
if (note->tieBack() && note->accidental() == 0)
continue;
int tpc = note->tpc();
int l = absStep(tpc, note->epitch());
tversatz.setAccidentalVal(l, tpc2alter(tpc));
}
}
for (Note* note : chord->notes()) {
if (note->tieBack() && note->accidental() == 0)
continue;
int tpc = note->tpc();
int l = absStep(tpc, note->epitch());
tversatz.setAccidentalVal(l, tpc2alter(tpc));
}
}
}
qDebug("segment not found");
return AccidentalVal::NATURAL;
}
//---------------------------------------------------------
// tick2pos
// return x position for tick relative to System
//---------------------------------------------------------
qreal Measure::tick2pos(Fraction tck) const
{
tck -= ticks();
if (isMMRest()) {
Segment* s = first(SegmentType::ChordRest);
qreal x1 = s->x();
qreal w = width() - x1;
return x1 + (tck.ticks() * w) / (ticks().ticks() * mmRestCount());
}
Segment* s;
qreal x1 = 0;
qreal x2 = 0;
Fraction tick1 = Fraction(0,1);
Fraction tick2 = Fraction(0,1);
for (s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
x2 = s->x();
tick2 = s->rtick();
if (tck == tick2)
return x2 + pos().x();
if (tck <= tick2)
break;
x1 = x2;
tick1 = tick2;
}
if (s == 0) {
x2 = width();
tick2 = ticks();
}
qreal dx = x2 - x1;
Fraction dt = tick2 - tick1;
x1 += dt.isZero() ? 0.0 : (dx * (tck.ticks() - tick1.ticks()) / dt.ticks());
return x1 + pos().x();
}
//---------------------------------------------------------
// showsMeasureNumberInAutoMode
/// Wheter the measure will show measure number(s) when MeasureNumberMode is set to AUTO
//---------------------------------------------------------
bool Measure::showsMeasureNumberInAutoMode()
{
// Check wheter any measure number should be shown
if (!score()->styleB(Sid::showMeasureNumber))
return false;
// Measure numbers should not be shown on irregular measures.
if (irregular())
return false;
// Measure numbers should not show on first measure unless specified with Sid::showMeasureNumberOne
if (!no())
return score()->styleB(Sid::showMeasureNumberOne);
if (score()->styleB(Sid::measureNumberSystem))
// Show either if
// 1) This is the first measure of the system OR
// 2) The previous measure in the system is the first, and is irregular.
return (isFirstInSystem()
|| (prevMeasure() && prevMeasure()->irregular() && prevMeasure()->isFirstInSystem()));
else {
// In the case of an interval, we should show the measure number either if:
// 1) We should show them every measure
int interval = score()->styleI(Sid::measureNumberInterval);
if (interval == 1)
return true;
// 2) (measureNumber + 1) % interval == 0 (or 1 if measure number one is numbered.)
// If measure number 1 is numbered, and the interval is let's say 5, then we should number #1, 6, 11, 16, etc.
// If measure number 1 is not numbered, with the same interval (5), then we should number #5, 10, 15, 20, etc.
return (((no() + 1) % score()->styleI(Sid::measureNumberInterval)) == (score()->styleB(Sid::showMeasureNumberOne) ? 1 : 0));
}
}
//---------------------------------------------------------
// showsMeasureNumber
/// Wheter the Measure shows a MeasureNumber
//---------------------------------------------------------
bool Measure::showsMeasureNumber()
{
if (_noMode == MeasureNumberMode::SHOW)
return true;
else if (_noMode == MeasureNumberMode::HIDE)
return false;
else {
return showsMeasureNumberInAutoMode();
}
}
//---------------------------------------------------------
// layoutMeasureNumber
/// Layouts the Measure Numbers according to the Measure's MeasureNumberMode
//---------------------------------------------------------
void Measure::layoutMeasureNumber()
{
bool smn = showsMeasureNumber();
QString s;
if (smn)
s = QString("%1").arg(no() + 1);
unsigned nn = 1;
bool nas = score()->styleB(Sid::measureNumberAllStaves);
if (!nas) {
//find first non invisible staff
for (unsigned staffIdx = 0; staffIdx < _mstaves.size(); ++staffIdx) {
MStaff* ms = _mstaves[staffIdx];
SysStaff* ss = system()->staff(staffIdx);
Staff* staff = score()->staff(staffIdx);
if (ms->visible() && staff->show() && ss->show()) {
nn = staffIdx;
break;
}
}
}
for (unsigned staffIdx = 0; staffIdx < _mstaves.size(); ++staffIdx) {
MStaff* ms = _mstaves[staffIdx];
MeasureNumber* t = ms->noText();
if (t)
t->setTrack(staffIdx * VOICES);
if (smn && ((staffIdx == nn) || nas)) {
if (t == 0) {
t = new MeasureNumber(score());
t->setTrack(staffIdx * VOICES);
t->setGenerated(true);
t->setParent(this);
add(t);
}
t->setXmlText(s);
t->layout();
}
else {
if (t) {
if (t->generated())
score()->removeElement(t);
else
score()->undo(new RemoveElement(t));
}
}
}
}
//---------------------------------------------------------
// layout2
// called after layout of page
//---------------------------------------------------------
void Measure::layout2()
{
Q_ASSERT(parent());
Q_ASSERT(score()->nstaves() == int(_mstaves.size()));
qreal _spatium = spatium();
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
MStaff* ms = _mstaves[staffIdx];
Spacer* sp = ms->vspacerDown();
if (sp) {
sp->layout();
Staff* staff = score()->staff(staffIdx);
int n = staff->lines(tick()) - 1;
qreal y = system()->staff(staffIdx)->y();
sp->setPos(_spatium * .5, y + n * _spatium * staff->mag(tick()));
}
sp = ms->vspacerUp();
if (sp) {
sp->layout();
qreal y = system()->staff(staffIdx)->y();
sp->setPos(_spatium * .5, y - sp->gap());
}
}
MeasureBase::layout(); // layout LAYOUT_BREAK elements
//---------------------------------------------------
// layout ties
//---------------------------------------------------
Fraction stick = system()->measures().front()->tick();
int tracks = score()->ntracks();
static const SegmentType st { SegmentType::ChordRest };
for (int track = 0; track < tracks; ++track) {
if (!score()->staff(track / VOICES)->show()) {
track += VOICES-1;
continue;
}
for (Segment* seg = first(st); seg; seg = seg->next(st)) {
ChordRest* cr = seg->cr(track);
if (!cr)
continue;
if (cr->isChord()) {
Chord* c = toChord(cr);
c->layoutSpanners(system(), stick);
}
}
}
}
//---------------------------------------------------------
// findChord
/// Search for chord at position \a tick in \a track
//---------------------------------------------------------
Chord* Measure::findChord(Fraction t, int track)
{
t -= tick();
for (Segment* seg = last(); seg; seg = seg->prev()) {
if (seg->rtick() < t)
return 0;
if (seg->rtick() == t) {
Element* el = seg->element(track);
if (el && el->isChord())
return toChord(el);
}
}
return 0;
}
//---------------------------------------------------------
// findChordRest
/// Search for chord or rest at position \a tick at \a staff in \a voice.
//---------------------------------------------------------
ChordRest* Measure::findChordRest(Fraction t, int track)
{
t -= tick();
for (const Segment& seg : _segments) {
if (seg.rtick() > t)
return 0;
if (seg.rtick() == t) {
Element* el = seg.element(track);
if (el && el->isChordRest())
return toChordRest(el);
}
}
return 0;
}
//---------------------------------------------------------
// tick2segment
//---------------------------------------------------------
Segment* Measure::tick2segment(const Fraction& _t, SegmentType st)
{
Fraction t = _t - tick();
for (Segment& s : _segments) {
if (s.rtick() == t) {
if (s.segmentType() & st)
return &s;
}
if (s.rtick() > t)
break;
}
return 0;
}
//---------------------------------------------------------
// findSegmentR
// Search for a segment of type st at measure relative
// position t.
//---------------------------------------------------------
Segment* Measure::findSegmentR(SegmentType st, const Fraction& t) const
{
Segment* s;
if (t > (ticks() * Fraction(1,2))) {
// search backwards
for (s = last(); s && s->rtick() > t; s = s->prev())
;
while (s && s->prev() && s->prev()->rtick() == t)
s = s->prev();
}
else {
// search forwards
for (s = first(); s && s->rtick() < t; s = s->next())
;
}
for (; s && s->rtick() == t; s = s->next()) {
if (s->segmentType() & st)
return s;
}
return 0;
}
//---------------------------------------------------------
// undoGetSegmentR
//---------------------------------------------------------
Segment* Measure::undoGetSegmentR(SegmentType type, const Fraction& t)
{
Segment* s = findSegmentR(type, t);
if (s == 0) {
s = new Segment(this, type, t);
score()->undoAddElement(s);
}
return s;
}
//---------------------------------------------------------
// findFirstR
// return first segment of type st at relative
// position t.
//---------------------------------------------------------
Segment* Measure::findFirstR(SegmentType st, const Fraction& t) const
{
Segment* s;
// search forwards
for (s = first(); s && s->rtick() <= t; s = s->next()) {
if (s->segmentType() == st)
return s;
}
return 0;
}
//---------------------------------------------------------
// getSegmentR
/// Get a segment of type st at relative tick position t.
/// If the segment does not exist, it is created.
//---------------------------------------------------------
Segment* Measure::getSegmentR(SegmentType st, const Fraction& t)
{
Segment* s = findSegmentR(st, t);
if (!s) {
s = new Segment(this, st, t);
add(s);
}
return s;
}
//---------------------------------------------------------
// add
/// Add new Element \a el to Measure.
//---------------------------------------------------------
void Measure::add(Element* e)
{
e->setParent(this);
ElementType type = e->type();
switch (type) {
case ElementType::SEGMENT:
{
Segment* seg = toSegment(e);
Fraction t = seg->rtick();
SegmentType st = seg->segmentType();
Segment* s;
for (s = first(); s && s->rtick() < t; s = s->next())
;
while (s && s->rtick() == t) {
if (!seg->isChordRestType() && (seg->segmentType() == s->segmentType())) {
qDebug("there is already a <%s> segment", seg->subTypeName());
return;
}
if (s->segmentType() > st)
break;
s = s->next();
}
seg->setParent(this);
_segments.insert(seg, s);
//
// update measure flags
//
if (seg->header())
seg->measure()->setHeader(true);
if (seg->trailer())
seg->measure()->setTrailer(true);
}
break;
case ElementType::MEASURE_NUMBER:
if (e->staffIdx() < int(_mstaves.size()))
_mstaves[e->staffIdx()]->setNoText(toMeasureNumber(e));
break;
case ElementType::SPACER:
{
Spacer* sp = toSpacer(e);
switch (sp->spacerType()) {
case SpacerType::UP:
_mstaves[e->staffIdx()]->setVspacerUp(sp);
break;
case SpacerType::DOWN:
case SpacerType::FIXED:
_mstaves[e->staffIdx()]->setVspacerDown(sp);
break;
}
sp->setGap(sp->gap()); // trigger relayout
}
break;
case ElementType::JUMP:
setRepeatJump(true);
// fall through
case ElementType::MARKER:
el().push_back(e);
break;
case ElementType::HBOX:
if (e->staff())
e->setMag(e->staff()->mag(tick())); // ?!
el().push_back(e);
break;
case ElementType::MEASURE:
_mmRest = toMeasure(e);
break;
case ElementType::STAFFTYPE_CHANGE:
{
StaffTypeChange* stc = toStaffTypeChange(e);
Staff* staff = stc->staff();
const StaffType* st = stc->staffType();
StaffType* nst;
// st needs to point to the stafftype element within the stafftypelist for the staff
if (st) {
// executed on read, undo/redo, clone
// setStaffType adds a copy to list and returns a pointer to that element within list
// we won't need the original after that
// this requires that st was allocated via new to begin with!
nst = staff->setStaffType(tick(), *st);
delete st;
}
else {
// executed on add from palette
// staffType returns a pointer to the current stafftype element in the list
// setStaffType will make a copy and return a pointer to that element within list
st = staff->staffType(tick());
nst = staff->setStaffType(tick(), *st);
}
staff->staffTypeListChanged(tick());
stc->setStaffType(nst);
MeasureBase::add(e);
}
break;
default:
MeasureBase::add(e);
break;
}
}
//---------------------------------------------------------
// remove
/// Remove Element \a el from Measure.
//---------------------------------------------------------
void Measure::remove(Element* e)
{
Q_ASSERT(e->parent() == this);
Q_ASSERT(e->score() == score());
switch (e->type()) {
case ElementType::SEGMENT:
{
Segment* s = toSegment(e);
_segments.remove(s);
//
// update measure flags
//
if (s->header())
s->measure()->checkHeader();
if (s->trailer())
s->measure()->checkTrailer();
}
break;
case ElementType::MEASURE_NUMBER:
_mstaves[e->staffIdx()]->setNoText(nullptr);
break;
case ElementType::SPACER:
switch (toSpacer(e)->spacerType()) {
case SpacerType::DOWN:
case SpacerType::FIXED:
_mstaves[e->staffIdx()]->setVspacerDown(0);
break;
case SpacerType::UP:
_mstaves[e->staffIdx()]->setVspacerUp(0);
break;
}
break;
case ElementType::JUMP:
setRepeatJump(false);
// fall through
case ElementType::MARKER:
case ElementType::HBOX:
if (!el().remove(e)) {
qDebug("Measure(%p)::remove(%s,%p) not found", this, e->name(), e);
}
break;
case ElementType::CLEF:
case ElementType::CHORD:
case ElementType::REST:
case ElementType::TIMESIG:
for (Segment* segment = first(); segment; segment = segment->next()) {
int staves = score()->nstaves();
int tracks = staves * VOICES;
for (int track = 0; track < tracks; ++track) {
Element* ee = segment->element(track);
if (ee == e) {
segment->setElement(track, 0);
return;
}
}
}
qDebug("Measure::remove: %s %p not found", e->name(), e);
break;
case ElementType::MEASURE:
_mmRest = 0;
break;
case ElementType::STAFFTYPE_CHANGE:
{
StaffTypeChange* stc = toStaffTypeChange(e);
Staff* staff = stc->staff();
if (staff) {
// st currently points to an list element that is about to be removed
// make a copy now to use on undo/redo
StaffType* st = new StaffType(*stc->staffType());
if (!tick().isZero())
staff->removeStaffType(tick());
stc->setStaffType(st);
}
MeasureBase::remove(e);
}
break;
default:
MeasureBase::remove(e);
break;
}
}
//---------------------------------------------------------
// change
//---------------------------------------------------------
void Measure::change(Element* o, Element* n)
{
if (o->isTuplet()) {
Tuplet* t = toTuplet(n);
for (DurationElement* e : t->elements())
e->setTuplet(t);
}
else {
remove(o);
add(n);
}
}
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Measure::spatiumChanged(qreal /*oldValue*/, qreal /*newValue*/)
{
}
//-------------------------------------------------------------------
// moveTicks
// Also adjust endBarLine if measure len has changed. For this
// diff == 0 cannot be optimized away
//-------------------------------------------------------------------
void Measure::moveTicks(const Fraction& diff)
{
std::set<Tuplet*> tuplets;
setTick(tick() + diff);
for (Segment* segment = last(); segment; segment = segment->prev()) {
if (segment->segmentType() & (SegmentType::EndBarLine | SegmentType::TimeSigAnnounce))
segment->setRtick(ticks());
else if (segment->isChordRestType()) {
// Tuplet ticks are stored as absolute ticks, so they must be adjusted.
// But each tuplet must only be adjusted once.
for (Element* e : segment->elist()) {
if (e) {
ChordRest* cr = toChordRest(e);
Tuplet* tuplet = cr->tuplet();
if (tuplet && tuplets.count(tuplet) == 0)
tuplets.insert(tuplet);
}
}
}
}
tuplets.clear();
}
//---------------------------------------------------------
// removeStaves
//---------------------------------------------------------
void Measure::removeStaves(int sStaff, int eStaff)
{
for (Segment* s = first(); s; s = s->next()) {
for (int staff = eStaff-1; staff >= sStaff; --staff) {
s->removeStaff(staff);
}
}
for (Element* e : el()) {
if (e->track() == -1)
continue;
int voice = e->voice();
int staffIdx = e->staffIdx();
if (staffIdx >= eStaff) {
staffIdx -= eStaff - sStaff;
e->setTrack(staffIdx * VOICES + voice);
}
}
}
//---------------------------------------------------------
// insertStaves
//---------------------------------------------------------
void Measure::insertStaves(int sStaff, int eStaff)
{
for (Element* e : el()) {
if (e->track() == -1)
continue;
int staffIdx = e->staffIdx();
if (staffIdx >= sStaff && !e->systemFlag()) {
int voice = e->voice();
staffIdx += eStaff - sStaff;
e->setTrack(staffIdx * VOICES + voice);
}
}
for (Segment* s = first(); s; s = s->next()) {
for (int staff = sStaff; staff < eStaff; ++staff) {
s->insertStaff(staff);
}
}
}
//---------------------------------------------------------
// cmdRemoveStaves
//---------------------------------------------------------
void Measure::cmdRemoveStaves(int sStaff, int eStaff)
{
int sTrack = sStaff * VOICES;
int eTrack = eStaff * VOICES;
for (Segment* s = first(); s; s = s->next()) {
for (int track = eTrack - 1; track >= sTrack; --track) {
Element* el = s->element(track);
if (el) {
el->undoUnlink();
score()->undo(new RemoveElement(el));
}
}
foreach (Element* e, s->annotations()) {
int staffIdx = e->staffIdx();
if ((staffIdx >= sStaff) && (staffIdx < eStaff) && !e->systemFlag()) {
e->undoUnlink();
score()->undo(new RemoveElement(e));
}
}
}
for (Element* e : el()) {
if (e->track() == -1)
continue;
int staffIdx = e->staffIdx();
if (staffIdx >= sStaff && (staffIdx < eStaff) && !e->systemFlag()) {
e->undoUnlink();
score()->undo(new RemoveElement(e));
}
}
score()->undo(new RemoveStaves(this, sStaff, eStaff));
for (int i = eStaff - 1; i >= sStaff; --i) {
MStaff* ms = *(_mstaves.begin()+i);
score()->undo(new RemoveMStaff(this, ms, i));
}
// barLine
// TODO
}
//---------------------------------------------------------
// cmdAddStaves
//---------------------------------------------------------
void Measure::cmdAddStaves(int sStaff, int eStaff, bool createRest)
{
score()->undo(new InsertStaves(this, sStaff, eStaff));
Segment* ts = findSegment(SegmentType::TimeSig, tick());
Segment* bs = findSegmentR(SegmentType::EndBarLine, ticks());
for (int i = sStaff; i < eStaff; ++i) {
Staff* staff = score()->staff(i);
MStaff* ms = new MStaff;
ms->setLines(new StaffLines(score()));
ms->lines()->setTrack(i * VOICES);
ms->lines()->setParent(this);
ms->lines()->setVisible(!staff->invisible());
score()->undo(new InsertMStaff(this, ms, i));
}
if (!createRest && !ts)
return;
// create list of unique staves (only one instance for linked staves):
QList<int> sl;
for (int staffIdx = sStaff; staffIdx < eStaff; ++staffIdx) {
Staff* s = score()->staff(staffIdx);
if (s->links()) {
bool alreadyInList = false;
for (int idx : sl) {
if (s->links()->contains(score()->staff(idx))) {
alreadyInList = true;
break;
}
}
if (alreadyInList)
continue;
}
sl.append(staffIdx);
}
for (int staffIdx : sl) {
if (createRest)
score()->setRest(tick(), staffIdx * VOICES, ticks(), false, 0, _timesig == ticks());
// replicate time signature
if (ts) {
TimeSig* ots = 0;
bool constructed = false;
for (unsigned track = 0; track < _mstaves.size() * VOICES; ++track) {
if (ts->element(track)) {
ots = toTimeSig(ts->element(track));
break;
}
}
if (!ots) {
// no time signature found; use measure timesig to construct one
ots = new TimeSig(score());
ots->setSig(timesig());
constructed = true;
}
// do no replicate local time signatures
if (ots && !ots->isLocal()) {
TimeSig* timesig = new TimeSig(*ots);
timesig->setTrack(staffIdx * VOICES);
timesig->setParent(ts);
timesig->setSig(ots->sig(), ots->timeSigType());
score()->undoAddElement(timesig);
if (constructed)
delete ots;
}
}
// replicate barline
if (bs) {
BarLine* obl = nullptr;
for (unsigned track = 0; track < _mstaves.size() * VOICES; ++track) {
Element* e = bs->element(track);
if (e && !e->generated()) {
obl = toBarLine(e);
break;
}
}
if (obl) {
BarLine* barline = new BarLine(*obl);
barline->setSpanStaff(score()->staff(staffIdx)->barLineSpan());
barline->setTrack(staffIdx * VOICES);
barline->setParent(bs);
barline->setGenerated(false);
score()->undoAddElement(barline);
}
}
}
}
//---------------------------------------------------------
// insertMStaff
//---------------------------------------------------------
void Measure::insertMStaff(MStaff* staff, int idx)
{
_mstaves.insert(_mstaves.begin()+idx, staff);
for (unsigned staffIdx = 0; staffIdx < _mstaves.size(); ++staffIdx)
_mstaves[staffIdx]->setTrack(staffIdx * VOICES);
}
//---------------------------------------------------------
// removeMStaff
//---------------------------------------------------------
void Measure::removeMStaff(MStaff* /*staff*/, int idx)
{
_mstaves.erase(_mstaves.begin()+idx);
for (unsigned staffIdx = 0; staffIdx < _mstaves.size(); ++staffIdx)
_mstaves[staffIdx]->setTrack(staffIdx * VOICES);
}
//---------------------------------------------------------
// insertStaff
//---------------------------------------------------------
void Measure::insertStaff(Staff* staff, int staffIdx)
{
for (Segment* s = first(); s; s = s->next())
s->insertStaff(staffIdx);
MStaff* ms = new MStaff;
ms->setLines(new StaffLines(score()));
ms->lines()->setParent(this);
ms->lines()->setTrack(staffIdx * VOICES);
ms->lines()->setVisible(!staff->invisible());
insertMStaff(ms, staffIdx);
}
//---------------------------------------------------------
// staffabbox
//---------------------------------------------------------
QRectF Measure::staffabbox(int staffIdx) const
{
System* s = system();
QRectF sb(s->staff(staffIdx)->bbox());
QRectF rrr(sb.translated(s->pagePos()));
QRectF rr(abbox());
QRectF r(rr.x(), rrr.y(), rr.width(), rrr.height());
return r;
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
/**
Return true if an Element of type \a type can be dropped on a Measure
*/
bool Measure::acceptDrop(EditData& data) const
{
MuseScoreView* viewer = data.view;
QPointF pos = data.pos;
Element* e = data.dropElement;
int staffIdx;
Segment* seg;
if (!score()->pos2measure(pos, &staffIdx, 0, &seg, 0))
return false;
QRectF staffR = system()->staff(staffIdx)->bbox().translated(system()->canvasPos());
staffR &= canvasBoundingRect();
switch (e->type()) {
case ElementType::MEASURE_LIST:
case ElementType::JUMP:
case ElementType::MARKER:
case ElementType::LAYOUT_BREAK:
case ElementType::STAFF_LIST:
viewer->setDropRectangle(canvasBoundingRect());
return true;
case ElementType::KEYSIG:
case ElementType::TIMESIG:
if (data.modifiers & Qt::ControlModifier)
viewer->setDropRectangle(staffR);
else
viewer->setDropRectangle(canvasBoundingRect());
return true;
case ElementType::MEASURE_NUMBER:
viewer->setDropRectangle(canvasBoundingRect());
return true;
case ElementType::BRACKET:
case ElementType::REPEAT_MEASURE:
case ElementType::MEASURE:
case ElementType::SPACER:
case ElementType::IMAGE:
case ElementType::BAR_LINE:
case ElementType::SYMBOL:
case ElementType::CLEF:
case ElementType::STAFFTYPE_CHANGE:
viewer->setDropRectangle(staffR);
return true;
case ElementType::ICON:
switch (toIcon(e)->iconType()) {
case IconType::VFRAME:
case IconType::HFRAME:
case IconType::TFRAME:
case IconType::FFRAME:
case IconType::MEASURE:
viewer->setDropRectangle(canvasBoundingRect());
return true;
default:
break;
}
break;
default:
break;
}
return false;
}
//---------------------------------------------------------
// drop
/// Drop element.
/// Handle a dropped element at position \a pos of given
/// element \a type and \a subtype.
//---------------------------------------------------------
Element* Measure::drop(EditData& data)
{
Element* e = data.dropElement;
int staffIdx = -1;
Segment* seg;
score()->pos2measure(data.pos, &staffIdx, 0, &seg, 0);
if (e->systemFlag())
staffIdx = 0;
if (staffIdx < 0)
return 0;
Staff* staff = score()->staff(staffIdx);
//bool fromPalette = (e->track() == -1);
switch (e->type()) {
case ElementType::MEASURE_LIST:
delete e;
break;
case ElementType::STAFF_LIST:
//TODO score()->pasteStaff(e, this, staffIdx);
delete e;
break;
case ElementType::MARKER:
case ElementType::JUMP:
e->setParent(this);
e->setTrack(0);
score()->undoAddElement(e);
return e;
case ElementType::DYNAMIC:
case ElementType::FRET_DIAGRAM:
e->setParent(seg);
e->setTrack(staffIdx * VOICES);
score()->undoAddElement(e);
return e;
case ElementType::IMAGE:
case ElementType::SYMBOL:
e->setParent(seg);
e->setTrack(staffIdx * VOICES);
e->layout();
{
QPointF uo(data.pos - e->canvasPos() - data.dragOffset);
e->setOffset(uo);
}
score()->undoAddElement(e);
return e;
case ElementType::MEASURE_NUMBER:
undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::SHOW));
delete e;
break;
case ElementType::BRACKET:
{
Bracket* b = toBracket(e);
int level = 0;
int firstStaff = 0;
for (Staff* s : score()->staves()) {
for (const BracketItem* bi : s->brackets()) {
int lastStaff = firstStaff + bi->bracketSpan() - 1;
if (staffIdx >= firstStaff && staffIdx <= lastStaff)
++level;
}
firstStaff++;
}
Selection sel = score()->selection();
score()->undoAddBracket(staff, level, b->bracketType(), sel.staffEnd() - sel.staffStart());
delete b;
}
break;
case ElementType::CLEF:
score()->undoChangeClef(staff, this, toClef(e)->clefType());
delete e;
break;
case ElementType::KEYSIG:
{
KeySigEvent k = toKeySig(e)->keySigEvent();
delete e;
if (data.modifiers & Qt::ControlModifier) {
// apply only to this stave
score()->undoChangeKeySig(staff, tick(), k);
}
else {
// apply to all staves:
for (Staff* s : score()->staves())
score()->undoChangeKeySig(s, tick(), k);
}
break;
}
case ElementType::TIMESIG:
score()->cmdAddTimeSig(this, staffIdx, toTimeSig(e), data.modifiers & Qt::ControlModifier);
break;
case ElementType::LAYOUT_BREAK: {
LayoutBreak* b = toLayoutBreak(e);
Measure* measure = isMMRest() ? mmRestLast() : this;
switch (b->layoutBreakType()) {
case LayoutBreak::PAGE:
if (measure->pageBreak()) {
delete b;
b = 0;
}
else
measure->setLineBreak(false);
break;
case LayoutBreak::LINE:
if (measure->lineBreak()) {
delete b;
b = 0;
}
else
measure->setPageBreak(false);
break;
case LayoutBreak::SECTION:
if (measure->sectionBreak()) {
delete b;
b = 0;
}
else
measure->setLineBreak(false);
break;
case LayoutBreak::NOBREAK:
if (measure->noBreak()) {
delete b;
b = 0;
}
else {
measure->setLineBreak(false);
measure->setPageBreak(false);
}
break;
}
if (b) {
b->setTrack(-1); // these are system elements
b->setParent(measure);
score()->undoAddElement(b);
}
measure->cleanupLayoutBreaks(true);
return b;
}
case ElementType::SPACER:
{
Spacer* spacer = toSpacer(e);
spacer->setTrack(staffIdx * VOICES);
spacer->setParent(this);
if (spacer->spacerType() == SpacerType::FIXED) {
qreal gap = spatium() * 10;
System* s = system();
const int nextVisStaffIdx = s->nextVisibleStaff(staffIdx);
const bool systemEnd = (nextVisStaffIdx == score()->nstaves());
if (systemEnd) {
System* ns = 0;
for (System* ts : score()->systems()) {
if (ns) {
ns = ts;
break;
}
if (ts == s)
ns = ts;
}
if (ns && ns->page() == s->page()) {
qreal y1 = s->staffYpage(staffIdx);
qreal y2 = ns->staffYpage(0);
gap = y2 - y1 - score()->staff(staffIdx)->height();
}
}
else {
qreal y1 = s->staffYpage(staffIdx);
qreal y2 = s->staffYpage(nextVisStaffIdx);
gap = y2 - y1 - score()->staff(staffIdx)->height();
}
spacer->setGap(gap);
}
score()->undoAddElement(spacer);
triggerLayout();
return spacer;
}
case ElementType::BAR_LINE:
{
BarLine* bl = toBarLine(e);
// if dropped bar line refers to span rather than to subtype
// or if Ctrl key used
if ((bl->spanFrom() && bl->spanTo()) || data.control()) {
// get existing bar line for this staff, and drop the change to it
seg = undoGetSegmentR(SegmentType::EndBarLine, ticks());
BarLine* cbl = toBarLine(seg->element(staffIdx * VOICES));
if (cbl)
cbl->drop(data);
}
else if (bl->barLineType() == BarLineType::START_REPEAT) {
Measure* m2 = isMMRest() ? mmRestFirst() : this;
for (Score* lscore : score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
}
}
else if (bl->barLineType() == BarLineType::END_REPEAT) {
Measure* m2 = isMMRest() ? mmRestLast() : this;
for (Score* lscore : score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
}
}
else if (bl->barLineType() == BarLineType::END_START_REPEAT) {
Measure* m2 = isMMRest() ? mmRestLast() : this;
for (Score* lscore : score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure) {
lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
lmeasure = lmeasure->nextMeasure();
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
}
}
}
else {
// drop to first end barline
seg = findSegmentR(SegmentType::EndBarLine, ticks());
if (seg) {
for (Element* ee : seg->elist()) {
if (ee) {
ee->drop(data);
break;
}
}
}
else
delete e;
}
break;
}
case ElementType::REPEAT_MEASURE:
{
delete e;
return cmdInsertRepeatMeasure(staffIdx);
}
case ElementType::ICON:
switch(toIcon(e)->iconType()) {
case IconType::VFRAME:
score()->insertMeasure(ElementType::VBOX, this);
break;
case IconType::HFRAME:
score()->insertMeasure(ElementType::HBOX, this);
break;
case IconType::TFRAME:
score()->insertMeasure(ElementType::TBOX, this);
break;
case IconType::FFRAME:
score()->insertMeasure(ElementType::FBOX, this);
break;
case IconType::MEASURE:
score()->insertMeasure(ElementType::MEASURE, this);
break;
default:
break;
}
break;
case ElementType::STAFFTYPE_CHANGE:
{
e->setParent(this);
e->setTrack(staffIdx * VOICES);
score()->undoAddElement(e);
}
break;
default:
qDebug("Measure: cannot drop %s here", e->name());
delete e;
break;
}
return 0;
}
//---------------------------------------------------------
// cmdInsertRepeatMeasure
//---------------------------------------------------------
RepeatMeasure* Measure::cmdInsertRepeatMeasure(int staffIdx)
{
//
// see also cmdDeleteSelection()
//
score()->select(0, SelectType::SINGLE, 0);
for (Segment* s = first(); s; s = s->next()) {
if (s->segmentType() & SegmentType::ChordRest) {
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (int track = strack; track < etrack; ++track) {
Element* el = s->element(track);
if (el)
score()->undoRemoveElement(el);
}
}
}
//
// add repeat measure
//
Segment* seg = undoGetSegment(SegmentType::ChordRest, tick());
RepeatMeasure* rm = new RepeatMeasure(score());
rm->setTrack(staffIdx * VOICES);
rm->setParent(seg);
rm->setDurationType(TDuration::DurationType::V_MEASURE);
rm->setTicks(stretchedLen(score()->staff(staffIdx)));
score()->undoAddCR(rm, this, tick());
for (Element* e : el()) {
if (e->isSlur() && e->staffIdx() == staffIdx)
score()->undoRemoveElement(e);
}
return rm;
}
//---------------------------------------------------------
// adjustToLen
// change actual measure len, adjust elements to
// new len
//---------------------------------------------------------
void Measure::adjustToLen(Fraction nf, bool appendRestsIfNecessary)
{
Fraction ol = ticks();
Fraction nl = nf;
Fraction diff = nl - ol;
Fraction startTick = endTick();
if (diff < Fraction(0,1))
startTick += diff;
score()->undoInsertTime(startTick, diff);
score()->undo(new InsertTime(score(), startTick, diff));
for (Score* s : score()->scoreList()) {
Measure* m = s->tick2measure(tick());
s->undo(new ChangeMeasureLen(m, nf));
if (nl > ol) {
// move EndBarLine, TimeSigAnnounce, KeySigAnnounce
for (Segment* seg = m->first(); seg; seg = seg->next()) {
if (seg->segmentType() & (SegmentType::EndBarLine|SegmentType::TimeSigAnnounce|SegmentType::KeySigAnnounce)) {
seg->setRtick(nl);
}
}
}
}
Score* s = score()->masterScore();
Measure* m = s->tick2measure(tick());
QList<int> sl = s->uniqueStaves();
for (int staffIdx : sl) {
int rests = 0;
int chords = 0;
Rest* rest = 0;
for (Segment* segment = m->first(); segment; segment = segment->next()) {
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (int track = strack; track < etrack; ++track) {
Element* e = segment->element(track);
if (e) {
if (e->isRest()) {
++rests;
rest = toRest(e);
}
else if (e->isChord())
++chords;
}
}
}
Fraction stretch = s->staff(staffIdx)->timeStretch(tick());
// if just a single rest
if (rests == 1 && chords == 0) {
// if measure value didn't change, stick to whole measure rest
if (_timesig == nf) {
rest->undoChangeProperty(Pid::DURATION, QVariant::fromValue<Fraction>(nf * stretch));
rest->undoChangeProperty(Pid::DURATION_TYPE, QVariant::fromValue<TDuration>(TDuration::DurationType::V_MEASURE));
}
else { // if measure value did change, represent with rests actual measure value
#if 0
// any reason not to do this instead?
s->undoRemoveElement(rest);
s->setRest(tick(), staffIdx * VOICES, nf * stretch, false, 0, false);
#else
// convert the measure duration in a list of values (no dots for rests)
std::vector<TDuration> durList = toDurationList(nf * stretch, false, 0);
// set the existing rest to the first value of the duration list
for (ScoreElement* e : rest->linkList()) {
e->undoChangeProperty(Pid::DURATION, QVariant::fromValue<Fraction>(durList[0].fraction()));
e->undoChangeProperty(Pid::DURATION_TYPE, QVariant::fromValue<TDuration>(durList[0]));
}
// add rests for any other duration list value
Fraction tickOffset = tick() + rest->actualTicks();
for (unsigned i = 1; i < durList.size(); i++) {
Rest* newRest = new Rest(s);
newRest->setDurationType(durList.at(i));
newRest->setTicks(durList.at(i).fraction());
newRest->setTrack(rest->track());
score()->undoAddCR(newRest, this, tickOffset);
tickOffset += newRest->actualTicks();
}
#endif
}
continue;
}
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (int trk = strack; trk < etrack; ++trk) {
Fraction n = diff;
bool rFlag = false;
if (n < Fraction(0,1)) {
for (Segment* segment = m->last(); segment;) {
Segment* pseg = segment->prev();
if (segment->segmentType() == SegmentType::ChordRest) {
for (Element* a : segment->annotations())
if (a->track() == trk)
s->undoRemoveElement(a);
Element* e = segment->element(trk);
if (e && e->isChordRest()) {
ChordRest* cr = toChordRest(e);
if (cr->durationType() == TDuration::DurationType::V_MEASURE) {
Fraction actualTicks = cr->actualTicks();
n += actualTicks;
cr->setDurationType(TDuration(actualTicks));
}
else
n += cr->actualTicks();
s->undoRemoveElement(e);
if (n >= Fraction(0,1))
break;
}
}
else if (segment->segmentType() == SegmentType::Breath) {
Element* e = segment->element(trk);
if (e)
s->undoRemoveElement(e);
}
segment = pseg;
}
rFlag = true;
}
int voice = trk % VOICES;
if (appendRestsIfNecessary && (n > Fraction(0,1)) && (rFlag || voice == 0)) {
// add rest to measure
Fraction rtick = tick() + nl - n;
int track = staffIdx * VOICES + voice;
s->setRest(rtick, track, n * stretch, false, 0, false);
}
}
}
if (diff < Fraction(0,1)) {
//
// CHECK: do not remove all slurs
//
for (Element* e : m->el()) {
if (e->isSlur())
s->undoRemoveElement(e);
}
}
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Measure::write(XmlWriter& xml, int staff, bool writeSystemElements, bool forceTimeSig) const
{
if (MScore::debugMode) {
const int mno = no() + 1;
xml.comment(QString("Measure %1").arg(mno));
}
if (_len != _timesig) {
// this is an irregular measure
xml.stag(this, QString("len=\"%1/%2\"").arg(_len.numerator()).arg(_len.denominator()));
}
else
xml.stag(this);
xml.setCurTick(tick());
xml.setCurTrack(staff * VOICES);
if (_mmRestCount > 0)
xml.tag("multiMeasureRest", _mmRestCount);
if (writeSystemElements) {
if (repeatStart())
xml.tagE("startRepeat");
if (repeatEnd())
xml.tag("endRepeat", _repeatCount);
writeProperty(xml, Pid::IRREGULAR);
writeProperty(xml, Pid::BREAK_MMR);
writeProperty(xml, Pid::USER_STRETCH);
writeProperty(xml, Pid::NO_OFFSET);
writeProperty(xml, Pid::MEASURE_NUMBER_MODE);
}
qreal _spatium = spatium();
MStaff* mstaff = _mstaves[staff];
if (mstaff->noText() && !mstaff->noText()->generated())
mstaff->noText()->write(xml);
if (mstaff->vspacerUp())
xml.tag("vspacerUp", mstaff->vspacerUp()->gap() / _spatium);
if (mstaff->vspacerDown()) {
if (mstaff->vspacerDown()->spacerType() == SpacerType::FIXED)
xml.tag("vspacerFixed", mstaff->vspacerDown()->gap() / _spatium);
else
xml.tag("vspacerDown", mstaff->vspacerDown()->gap() / _spatium);
}
if (!mstaff->visible())
xml.tag("visible", mstaff->visible());
if (mstaff->stemless()) {
xml.tag("slashStyle", mstaff->stemless()); // for backwards compatibility
xml.tag("stemless", mstaff->stemless());
}
int strack = staff * VOICES;
int etrack = strack + VOICES;
for (const Element* e : el()) {
if (!e->generated() && ((e->staffIdx() == staff) || (e->systemFlag() && writeSystemElements)))
e->write(xml);
}
Q_ASSERT(first());
Q_ASSERT(last());
if (first() && last())
score()->writeSegments(xml, strack, etrack, first(), last()->next1(), writeSystemElements, forceTimeSig);
xml.etag();
}
//---------------------------------------------------------
// Measure::read
//---------------------------------------------------------
void Measure::read(XmlReader& e, int staffIdx)
{
qreal _spatium = spatium();
e.setCurrentMeasure(this);
int nextTrack = staffIdx * VOICES;
e.setTrack(nextTrack);
for (int n = int(_mstaves.size()); n <= staffIdx; ++n) {
Staff* staff = score()->staff(n);
MStaff* s = new MStaff;
s->setLines(new StaffLines(score()));
s->lines()->setParent(this);
s->lines()->setTrack(n * VOICES);
s->lines()->setVisible(!staff->invisible());
_mstaves.push_back(s);
}
bool irregular;
if (e.hasAttribute("len")) {
QStringList sl = e.attribute("len").split('/');
if (sl.size() == 2)
_len = Fraction(sl[0].toInt(), sl[1].toInt());
else
qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
irregular = true;
if (_len.numerator() <= 0 || _len.denominator() <= 0) {
e.raiseError(QObject::tr("MSCX error at line %1: invalid measure length: %2").arg(e.lineNumber()).arg(_len.toString()));
return;
}
score()->sigmap()->add(tick().ticks(), SigEvent(_len, _timesig));
score()->sigmap()->add((tick() + ticks()).ticks(), SigEvent(_timesig));
}
else
irregular = false;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "voice") {
e.setTrack(nextTrack++);
e.setTick(tick());
readVoice(e, staffIdx, irregular);
}
else if (tag == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Element* el = Element::name2Element(tag, score());
el->setTrack(staffIdx * VOICES);
el->read(e);
add(el);
}
}
else if (tag == "Marker" || tag == "Jump") {
Element* el = Element::name2Element(tag, score());
el->setTrack(e.track());
el->read(e);
add(el);
}
else if (tag == "stretch") {
double val = e.readDouble();
if (val < 0.0)
val = 0;
setUserStretch(val);
}
else if (tag == "noOffset")
setNoOffset(e.readInt());
else if (tag == "measureNumberMode")
setMeasureNumberMode(MeasureNumberMode(e.readInt()));
else if (tag == "irregular")
setIrregular(e.readBool());
else if (tag == "breakMultiMeasureRest")
_breakMultiMeasureRest = e.readBool();
else if (tag == "startRepeat") {
setRepeatStart(true);
e.readNext();
}
else if (tag == "endRepeat") {
_repeatCount = e.readInt();
setRepeatEnd(true);
}
else if (tag == "vspacer" || tag == "vspacerDown") {
if (!_mstaves[staffIdx]->vspacerDown()) {
Spacer* spacer = new Spacer(score());
spacer->setSpacerType(SpacerType::DOWN);
spacer->setTrack(staffIdx * VOICES);
add(spacer);
}
_mstaves[staffIdx]->vspacerDown()->setGap(e.readDouble() * _spatium);
}
else if (tag == "vspacerFixed") {
if (!_mstaves[staffIdx]->vspacerDown()) {
Spacer* spacer = new Spacer(score());
spacer->setSpacerType(SpacerType::FIXED);
spacer->setTrack(staffIdx * VOICES);
add(spacer);
}
_mstaves[staffIdx]->vspacerDown()->setGap(e.readDouble() * _spatium);
}
else if (tag == "vspacerUp") {
if (!_mstaves[staffIdx]->vspacerUp()) {
Spacer* spacer = new Spacer(score());
spacer->setSpacerType(SpacerType::UP);
spacer->setTrack(staffIdx * VOICES);
add(spacer);
}
_mstaves[staffIdx]->vspacerUp()->setGap(e.readDouble() * _spatium);
}
else if (tag == "visible")
_mstaves[staffIdx]->setVisible(e.readInt());
else if ((tag == "slashStyle") || (tag == "stemless"))
_mstaves[staffIdx]->setStemless(e.readInt());
else if (tag == "SystemDivider") {
SystemDivider* sd = new SystemDivider(score());
sd->read(e);
add(sd);
}
else if (tag == "multiMeasureRest") {
_mmRestCount = e.readInt();
// set tick to previous measure
setTick(e.lastMeasure()->tick());
e.setTick(e.lastMeasure()->tick());
}
else if (tag == "MeasureNumber") {
MeasureNumber* noText = new MeasureNumber(score());
noText->read(e);
noText->setTrack(e.track());
add(noText);
}
else if (MeasureBase::readProperties(e))
;
else
e.unknown();
}
e.checkConnectors();
if (isMMRest()) {
Measure* lm = e.lastMeasure();
e.setTick(lm->tick() + lm->ticks());
}
e.setCurrentMeasure(nullptr);
connectTremolo();
}
//---------------------------------------------------------
// Measure::readVoice
//---------------------------------------------------------
void Measure::readVoice(XmlReader& e, int staffIdx, bool irregular)
{
Segment* segment = nullptr;
QList<Chord*> graceNotes;
Beam* startingBeam = nullptr;
Tuplet* tuplet = nullptr;
Fermata* fermata = nullptr;
Staff* staff = score()->staff(staffIdx);
Fraction timeStretch(staff->timeStretch(tick()));
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "location") {
Location loc = Location::relative();
loc.read(e);
e.setLocation(loc);
}
else if (tag == "tick") { // obsolete?
qDebug("read midi tick");
e.setTick(Fraction::fromTicks(score()->fileDivision(e.readInt())));
}
else if (tag == "BarLine") {
BarLine* barLine = new BarLine(score());
barLine->setTrack(e.track());
barLine->read(e);
//
// StartRepeatBarLine: at rtick == 0, always BarLineType::START_REPEAT
// BarLine: in the middle of a measure, has no semantic
// EndBarLine: at the end of a measure
// BeginBarLine: first segment of a measure, systemic barline
SegmentType st = SegmentType::Invalid;
Fraction t = e.tick() - tick();
if (t.isNotZero() && (t != ticks()))
st = SegmentType::BarLine;
else if (barLine->barLineType() == BarLineType::START_REPEAT && t.isZero())
st = SegmentType::StartRepeatBarLine;
else if (barLine->barLineType() == BarLineType::START_REPEAT && t == ticks()) {
// old version, ignore
delete barLine;
barLine = 0;
}
else if (t.isZero() && segment == 0)
st = SegmentType::BeginBarLine;
else
st = SegmentType::EndBarLine;
if (barLine) {
segment = getSegmentR(st, t);
segment->add(barLine);
barLine->layout();
}
if (fermata) {
segment->add(fermata);
fermata = nullptr;
}
}
else if (tag == "Chord") {
Chord* chord = new Chord(score());
chord->setTrack(e.track());
chord->read(e);
if (startingBeam) {
startingBeam->add(chord); // also calls chord->setBeam(startingBeam)
startingBeam = nullptr;
}
// if (tuplet && !chord->isGrace())
// chord->readAddTuplet(tuplet);
segment = getSegment(SegmentType::ChordRest, e.tick());
if (chord->noteType() != NoteType::NORMAL)
graceNotes.push_back(chord);
else {
segment->add(chord);
for (int i = 0; i < graceNotes.size(); ++i) {
Chord* gc = graceNotes[i];
gc->setGraceIndex(i);
chord->add(gc);
}
graceNotes.clear();
if (tuplet)
tuplet->add(chord);
e.incTick(chord->actualTicks());
}
if (fermata) {
segment->add(fermata);
fermata = nullptr;
}
}
else if (tag == "Rest") {
Rest* rest = new Rest(score());
rest->setDurationType(TDuration::DurationType::V_MEASURE);
rest->setTicks(timesig()/timeStretch);
rest->setTrack(e.track());
rest->read(e);
if (startingBeam) {
startingBeam->add(rest); // also calls rest->setBeam(startingBeam)
startingBeam = nullptr;
}
segment = getSegment(SegmentType::ChordRest, e.tick());
segment->add(rest);
if (fermata) {
segment->add(fermata);
fermata = nullptr;
}
if (!rest->ticks().isValid()) // hack
rest->setTicks(timesig()/timeStretch);
if (tuplet)
tuplet->add(rest);
e.incTick(rest->actualTicks());
}
else if (tag == "Breath") {
Breath* breath = new Breath(score());
breath->setTrack(e.track());
breath->read(e);
segment = getSegment(SegmentType::Breath, e.tick());
segment->add(breath);
}
else if (tag == "Spanner")
Spanner::readSpanner(e, this, e.track());
else if (tag == "RepeatMeasure") {
RepeatMeasure* rm = new RepeatMeasure(score());
rm->setTrack(e.track());
rm->read(e);
segment = getSegment(SegmentType::ChordRest, e.tick());
segment->add(rm);
e.incTick(ticks());
}
else if (tag == "Clef") {
Clef* clef = new Clef(score());
clef->setTrack(e.track());
clef->read(e);
clef->setGenerated(false);
// there may be more than one clef segment for same tick position
// the first clef may be missing and is added later in layout
bool header;
if (e.tick() != tick())
header = false;
else if (!segment)
header = true;
else {
header = true;
for (Segment* s = _segments.first(); s && s->rtick().isZero(); s = s->next()) {
if (s->isKeySigType() || s->isTimeSigType()) {
// hack: there may be other segment types which should
// generate a clef at current position
header = false;
break;
}
}
}
segment = getSegment(header ? SegmentType::HeaderClef : SegmentType::Clef, e.tick());
segment->add(clef);
}
else if (tag == "TimeSig") {
TimeSig* ts = new TimeSig(score());
ts->setTrack(e.track());
ts->read(e);
// if time sig not at beginning of measure => courtesy time sig
Fraction currTick = e.tick();
bool courtesySig = (currTick > tick());
if (courtesySig) {
// if courtesy sig., just add it without map processing
segment = getSegment(SegmentType::TimeSigAnnounce, currTick);
segment->add(ts);
}
else {
// if 'real' time sig., do full process
segment = getSegment(SegmentType::TimeSig, currTick);
segment->add(ts);
timeStretch = ts->stretch().reduced();
_timesig = ts->sig() / timeStretch;
if (irregular) {
score()->sigmap()->add(tick().ticks(), SigEvent(_len, _timesig));
score()->sigmap()->add((tick() + ticks()).ticks(), SigEvent(_timesig));
}
else {
_len = _timesig;
score()->sigmap()->add(tick().ticks(), SigEvent(_timesig));
}
}
}
else if (tag == "KeySig") {
KeySig* ks = new KeySig(score());
ks->setTrack(e.track());
ks->read(e);
Fraction curTick = e.tick();
if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick.isZero()) {
// ignore empty key signature
qDebug("remove keysig c at tick 0");
}
else {
// if key sig not at beginning of measure => courtesy key sig
bool courtesySig = (curTick == endTick());
segment = getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
segment->add(ks);
if (!courtesySig)
staff->setKey(curTick, ks->keySigEvent());
}
}
else if (tag == "Text") {
StaffText* t = new StaffText(score());
t->setTrack(e.track());
t->read(e);
if (t->empty()) {
qDebug("==reading empty text: deleted");
delete t;
}
else {
segment = getSegment(SegmentType::ChordRest, e.tick());
segment->add(t);
}
}
//----------------------------------------------------
// Annotation
else if (tag == "Dynamic") {
Dynamic* dyn = new Dynamic(score());
dyn->setTrack(e.track());
dyn->read(e);
segment = getSegment(SegmentType::ChordRest, e.tick());
segment->add(dyn);
}
else if (tag == "Harmony"
|| tag == "FretDiagram"
|| tag == "TremoloBar"
|| tag == "Symbol"
|| tag == "Tempo"
|| tag == "StaffText"
|| tag == "Sticking"
|| tag == "SystemText"
|| tag == "RehearsalMark"
|| tag == "InstrumentChange"
|| tag == "StaffState"
|| tag == "FiguredBass"
) {
Element* el = Element::name2Element(tag, score());
// hack - needed because tick tags are unreliable in 1.3 scores
// for symbols attached to anything but a measure
el->setTrack(e.track());
el->read(e);
segment = getSegment(SegmentType::ChordRest, e.tick());
segment->add(el);
}
else if (tag == "Fermata") {
fermata = new Fermata(score());
fermata->setTrack(e.track());
fermata->setPlacement(fermata->track() & 1 ? Placement::BELOW : Placement::ABOVE);
fermata->read(e);
}
// There could be an Image here if the score was saved with an earlier version of MuseScore 3.
// This image would not have been visible upon reload. Let's read it in and add it directly
// to the measure so that it can be displayed.
else if (tag == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Element* el = Element::name2Element(tag, score());
el->setTrack(e.track());
el->read(e);
add(el);
}
}
//----------------------------------------------------
else if (tag == "Tuplet") {
Tuplet* oldTuplet = tuplet;
tuplet = new Tuplet(score());
tuplet->setTrack(e.track());
tuplet->setTick(e.tick());
tuplet->setParent(this);
tuplet->read(e);
if (oldTuplet)
oldTuplet->add(tuplet);
}
else if (tag == "endTuplet") {
if (!tuplet) {
qDebug("Measure::read: encountered <endTuplet/> when no tuplet was started");
e.skipCurrentElement();
continue;
}
Tuplet* oldTuplet = tuplet;
tuplet = tuplet->tuplet();
if (oldTuplet->elements().empty()) {
// this should not happen and is a sign of input file corruption
qDebug("Measure:read: empty tuplet in measure index=%d, input file corrupted?", e.currentMeasureIndex());
if (tuplet)
tuplet->remove(oldTuplet);
delete oldTuplet;
}
e.readNext();
}
else if (tag == "Beam") {
Beam* beam = new Beam(score());
beam->setTrack(e.track());
beam->read(e);
beam->setParent(0);
if (startingBeam) {
qDebug("The read beam was not used");
delete startingBeam;
}
startingBeam = beam;
}
else if (tag == "Segment" && segment)
segment->read(e);
else if (tag == "Ambitus") {
Ambitus* range = new Ambitus(score());
range->read(e);
segment = getSegment(SegmentType::Ambitus, e.tick());
range->setParent(segment); // a parent segment is needed for setTrack() to work
range->setTrack(trackZeroVoice(e.track()));
segment->add(range);
}
else
e.unknown();
}
if (startingBeam) {
qDebug("The read beam was not used");
delete startingBeam;
}
if (tuplet) {
qDebug("Measure:readVoice: measure index=%d, <endTuplet/> not found", e.currentMeasureIndex());
if (tuplet->elements().empty()) {
if (tuplet->tuplet())
tuplet->tuplet()->remove(tuplet);
delete tuplet;
}
}
if (fermata) {
SegmentType st = (e.tick() == endTick() ? SegmentType::EndBarLine : SegmentType::ChordRest);
segment = getSegment(st, e.tick());
segment->add(fermata);
fermata = nullptr;
}
}
//---------------------------------------------------------
// Measure::readAddConnector
//---------------------------------------------------------
void Measure::readAddConnector(ConnectorInfoReader* info, bool pasteMode)
{
const ElementType type = info->type();
switch(type) {
case ElementType::HAIRPIN:
case ElementType::PEDAL:
case ElementType::OTTAVA:
case ElementType::TRILL:
case ElementType::TEXTLINE:
case ElementType::LET_RING:
case ElementType::VIBRATO:
case ElementType::PALM_MUTE:
case ElementType::VOLTA:
{
Spanner* sp = toSpanner(info->connector());
const Location& l = info->location();
Fraction lTick = l.frac();
Fraction spTick = pasteMode ? lTick : (tick() + lTick);
if (info->isStart()) {
sp->setTrack(l.track());
sp->setTick(spTick);
score()->addSpanner(sp);
}
else if (info->isEnd()) {
sp->setTrack2(l.track());
sp->setTick2(spTick);
}
}
break;
default:
break;
}
}
//---------------------------------------------------------
// visible
//---------------------------------------------------------
bool Measure::visible(int staffIdx) const
{
if (staffIdx >= score()->staves().size()) {
qDebug("Measure::visible: bad staffIdx: %d", staffIdx);
return false;
}
if (system() && (system()->staves()->empty() || !system()->staff(staffIdx)->show()))
return false;
if (score()->staff(staffIdx)->cutaway() && isEmpty(staffIdx))
return false;
return score()->staff(staffIdx)->show() && _mstaves[staffIdx]->visible();
}
//---------------------------------------------------------
// stemless
//---------------------------------------------------------
bool Measure::stemless(int staffIdx) const
{
const Staff* staff = score()->staff(staffIdx);
return staff->stemless(tick()) || _mstaves[staffIdx]->stemless() || staff->staffType(tick())->stemless();
}
//---------------------------------------------------------
// isFinalMeasureOfSection
// returns true if this measure is final actual measure of a section
// takes into consideration fact that subsequent measures base objects
// may have section break before encountering next actual measure
//---------------------------------------------------------
bool Measure::isFinalMeasureOfSection() const
{
const MeasureBase* mb = static_cast<const MeasureBase*>(this);
do {
if (mb->sectionBreak())
return true;
mb = mb->next();
} while (mb && !mb->isMeasure()); // loop until reach next actual measure or end of score
return false;
}
//---------------------------------------------------------
// isAnacrusis
//---------------------------------------------------------
bool Measure::isAnacrusis() const
{
TimeSigFrac timeSig = score()->sigmap()->timesig(tick().ticks()).nominal();
return irregular() && ticks() < Fraction::fromTicks(timeSig.ticksPerMeasure());
}
//---------------------------------------------------------
// isFirstInSystem
//---------------------------------------------------------
bool Measure::isFirstInSystem() const
{
IF_ASSERT_FAILED(system()) {
return false;
}
return system()->firstMeasure() == this;
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Measure::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
MeasureBase::scanElements(data, func, all);
int nstaves = score()->nstaves();
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!all && !(visible(staffIdx) && score()->staff(staffIdx)->show()))
continue;
MStaff* ms = _mstaves[staffIdx];
func(data, ms->lines());
if (ms->vspacerUp())
func(data, ms->vspacerUp());
if (ms->vspacerDown())
func(data, ms->vspacerDown());
if (ms->noText())
func(data, ms->noText());
}
for (Segment* s = first(); s; s = s->next()) {
if (!s->enabled())
continue;
s->scanElements(data, func, all);
}
}
//---------------------------------------------------------
// connectTremolo
/// Connect two-notes tremolo and update duration types
/// for the involved chords.
//---------------------------------------------------------
void Measure::connectTremolo()
{
const int ntracks = score()->ntracks();
constexpr SegmentType st = SegmentType::ChordRest;
for (Segment* s = first(st); s; s = s->next(st)) {
for (int i = 0; i < ntracks; ++i) {
Element* e = s->element(i);
if (!e || !e->isChord())
continue;
Chord* c = toChord(e);
Tremolo* tremolo = c->tremolo();
if (tremolo && tremolo->twoNotes()) {
// Ensure correct duration type for chord
c->setDurationType(tremolo->durationType());
// If it is the first tremolo's chord, find the second
// chord for tremolo, if needed.
if (!tremolo->chord1())
tremolo->setChords(c, tremolo->chord2());
else if (tremolo->chord1() != c || tremolo->chord2())
continue;
for (Segment* ls = s->next(st); ls; ls = ls->next(st)) {
if (Element* element = ls->element(i)) {
if (!element->isChord()) {
qDebug("cannot connect tremolo");
continue;
}
Chord* nc = toChord(element);
tremolo->setChords(c, nc);
nc->setTremolo(tremolo);
break;
}
}
}
}
}
}
//---------------------------------------------------------
// createVoice
// Create a voice on demand by filling the measure
// with a whole measure rest.
// Check if there are any chord/rests in track; if
// not create a whole measure rest
//---------------------------------------------------------
void Measure::createVoice(int track)
{
for (Segment* s = first(); s; s = s->next()) {
if (s->segmentType() != SegmentType::ChordRest)
continue;
if (s->element(track) == 0)
score()->setRest(s->tick(), track, ticks(), true, 0);
break;
}
}
//---------------------------------------------------------
// sortStaves
//---------------------------------------------------------
void Measure::sortStaves(QList<int>& dst)
{
std::vector<MStaff*> ms;
for (int idx : dst)
ms.push_back(_mstaves[idx]);
_mstaves = ms;
for (unsigned staffIdx = 0; staffIdx < _mstaves.size(); ++staffIdx)
_mstaves[staffIdx]->lines()->setTrack(staffIdx * VOICES);
for (Segment& s : _segments)
s.sortStaves(dst);
for (Element* e : el()) {
if (e->track() == -1 || e->systemFlag())
continue;
int voice = e->voice();
int staffIdx = e->staffIdx();
int idx = dst.indexOf(staffIdx);
e->setTrack(idx * VOICES + voice);
}
}
//---------------------------------------------------------
// exchangeVoice
//---------------------------------------------------------
void Measure::exchangeVoice(int strack, int dtrack, int staffIdx)
{
for (Segment* s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
s->swapElements(strack, dtrack);
}
auto spanners = score()->spannerMap().findOverlapping(tick().ticks(), endTick().ticks()-1);
Fraction start = tick();
Fraction end = start + ticks();
for (auto i = spanners.begin(); i < spanners.end(); i++) {
Spanner* sp = i->value;
Fraction spStart = sp->tick();
Fraction spEnd = spStart + sp->ticks();
qDebug("Start %d End %d Diff %d \n Measure Start %d End %d", spStart.ticks(), spEnd.ticks(), (spEnd-spStart).ticks(), start.ticks(), end.ticks());
if (sp->isSlur() && (spStart >= start || spEnd < end)) {
if (sp->track() == strack && spStart >= start){
sp->setTrack(dtrack);
}
else if (sp->track() == dtrack && spStart >= start){
sp->setTrack(strack);
}
if (sp->track2() == strack && spEnd < end){
sp->setTrack2(dtrack);
}
else if (sp->track2() == dtrack && spEnd < end){
sp->setTrack2(strack);
}
}
}
checkMultiVoices(staffIdx); // probably true, but check for invisible notes & rests
}
//---------------------------------------------------------
// checkMultiVoices
/// Check for more than on voice in this measure and staff and
/// set MStaff->hasVoices
//---------------------------------------------------------
void Measure::checkMultiVoices(int staffIdx)
{
if (hasVoices(staffIdx, tick(), ticks()))
_mstaves[staffIdx]->setHasVoices(true);
else
_mstaves[staffIdx]->setHasVoices(false);
}
//---------------------------------------------------------
// hasVoices
//---------------------------------------------------------
bool Measure::hasVoices(int staffIdx, Fraction stick, Fraction len) const
{
int strack = staffIdx * VOICES + 1;
int etrack = staffIdx * VOICES + VOICES;
Fraction etick = stick + len;
for (Segment* s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
if (s->tick() >= etick)
break;
for (int track = strack; track < etrack; ++track) {
ChordRest* cr = toChordRest(s->element(track));
if (cr) {
if (cr->tick() + cr->actualTicks() <= stick)
continue;
bool v = false;
if (cr->isChord()) {
// consider chord visible if any note is visible
Chord* c = toChord(cr);
for (Note* n : c->notes()) {
if (n->visible()) {
v = true;
break;
}
}
}
else if (cr->isRest())
v = cr->visible() && !toRest(cr)->isGap();
if (v)
return true;
}
}
}
return false;
}
//---------------------------------------------------------
// hasVoice
//---------------------------------------------------------
bool Measure::hasVoice(int track) const
{
if (track >= score()->ntracks())
return false;
for (Segment* s = first(); s; s = s->next()) {
if (s->segmentType() != SegmentType::ChordRest)
continue;
if (s->element(track))
return true;
}
return false;
}
//-------------------------------------------------------------------
// isEmpty
/// Check if the measure is filled by a full-measure rest, or is
/// full of rests on this staff, that may have fermatas on them.
/// If staff is -1, then check for all staves.
//-------------------------------------------------------------------
bool Measure::isEmpty(int staffIdx) const
{
int strack;
int etrack;
if (staffIdx < 0) {
strack = 0;
etrack = score()->nstaves() * VOICES;
}
else {
strack = staffIdx * VOICES;
etrack = strack + VOICES;
}
for (Segment* s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e && !e->isRest())
return false;
}
for (Element* a : s->annotations()) {
if (!a || a->systemFlag() || !a->visible() || a->isFermata())
continue;
int atrack = a->track();
if (atrack >= strack && atrack < etrack)
return false;
}
}
return true;
}
//---------------------------------------------------------
// isFullMeasureRest
// Check for an empty measure, filled with full measure
// rests.
//---------------------------------------------------------
bool Measure::isFullMeasureRest() const
{
int strack = 0;
int etrack = score()->nstaves() * VOICES;
Segment* s = first(SegmentType::ChordRest);
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e) {
if (!e->isRest())
return false;
Rest* rest = toRest(e);
if (rest->durationType().type() != TDuration::DurationType::V_MEASURE)
return false;
}
}
return true;
}
//---------------------------------------------------------
// isRepeatMeasure
//---------------------------------------------------------
bool Measure::isRepeatMeasure(const Staff* staff) const
{
int staffIdx = staff->idx();
int strack = staffIdx * VOICES;
int etrack = (staffIdx + 1) * VOICES;
Segment* s = first(SegmentType::ChordRest);
if (s == 0)
return false;
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e && e->isRepeatMeasure())
return true;
}
return false;
}
//---------------------------------------------------------
// isEmpty
//---------------------------------------------------------
bool Measure::empty() const
{
if (irregular())
return false;
int n = 0;
int tracks = int(_mstaves.size()) * VOICES;
static const SegmentType st = SegmentType::ChordRest ;
for (const Segment* s = first(st); s; s = s->next(st)) {
bool restFound = false;
for (int track = 0; track < tracks; ++track) {
if ((track % VOICES) == 0 && !score()->staff(track/VOICES)->show()) {
track += VOICES-1;
continue;
}
if (s->element(track)) {
if (!s->element(track)->isRest())
return false;
restFound = true;
}
}
if (restFound)
++n;
// measure is not empty if there is more than one rest
if (n > 1)
return false;
}
return true;
}
//---------------------------------------------------------
// isOnlyRests
//---------------------------------------------------------
bool Measure::isOnlyRests(int track) const
{
static const SegmentType st = SegmentType::ChordRest;
for (const Segment* s = first(st); s; s = s->next(st)) {
if (s->segmentType() != st || !s->element(track))
continue;
if (!s->element(track)->isRest())
return false;
}
return true;
}
//---------------------------------------------------------
// isOnlyDeletedRests
//---------------------------------------------------------
bool Measure::isOnlyDeletedRests(int track) const
{
static const SegmentType st { SegmentType::ChordRest };
for (const Segment* s = first(st); s; s = s->next(st)) {
if (s->segmentType() != st || !s->element(track))
continue;
if (s->element(track)->isRest() ? !toRest(s->element(track))->isGap() : !s->element(track)->isRest())
return false;
}
return true;
}
//---------------------------------------------------------
// stretchedLen
//---------------------------------------------------------
Fraction Measure::stretchedLen(Staff* staff) const
{
return ticks() * staff->timeStretch(tick());
}
//---------------------------------------------------------
// cloneMeasure
//---------------------------------------------------------
Measure* Measure::cloneMeasure(Score* sc, const Fraction& tick, TieMap* tieMap)
{
Measure* m = new Measure(sc);
m->_timesig = _timesig;
m->_len = _len;
m->_repeatCount = _repeatCount;
Q_ASSERT(sc->staves().size() >= int(_mstaves.size())); // destination score we're cloning into must have at least as many staves as measure being cloned
m->setNo(no());
m->setNoOffset(noOffset());
m->setIrregular(irregular());
m->_userStretch = _userStretch;
m->_breakMultiMeasureRest = _breakMultiMeasureRest;
m->_playbackCount = _playbackCount;
m->setTick(tick);
m->setLineBreak(lineBreak());
m->setPageBreak(pageBreak());
m->setSectionBreak(sectionBreak() ? new LayoutBreak(*sectionBreakElement()) : 0);
m->setHeader(header()); m->setTrailer(trailer());
int tracks = sc->nstaves() * VOICES;
TupletMap tupletMap;
for (Segment* oseg = first(); oseg; oseg = oseg->next()) {
Segment* s = new Segment(m, oseg->segmentType(), oseg->rtick());
s->setEnabled(oseg->enabled()); s->setVisible(oseg->visible());
s->setHeader(oseg->header()); s->setTrailer(oseg->trailer());
m->_segments.push_back(s);
for (int track = 0; track < tracks; ++track) {
Element* oe = oseg->element(track);
for (Element* e : oseg->annotations()) {
if (e->generated() || e->track() != track)
continue;
Element* ne = e->clone();
ne->setTrack(track);
ne->setOffset(e->offset());
ne->setScore(sc);
s->add(ne);
}
if (!oe)
continue;
Element* ne = oe->clone();
if (oe->isChordRest()) {
ChordRest* ocr = toChordRest(oe);
ChordRest* ncr = toChordRest(ne);
Tuplet* ot = ocr->tuplet();
if (ot) {
Tuplet* nt = tupletMap.findNew(ot);
if (nt == 0) {
nt = new Tuplet(*ot);
nt->clear();
nt->setTrack(track);
nt->setScore(sc);
nt->setParent(m);
nt->setTick(m->tick() + ot->rtick());
tupletMap.add(ot, nt);
}
ncr->setTuplet(nt);
nt->add(ncr);
}
if (oe->isChord()) {
Chord* och = toChord(ocr);
Chord* nch = toChord(ncr);
size_t n = och->notes().size();
for (size_t i = 0; i < n; ++i) {
Note* on = och->notes().at(i);
Note* nn = nch->notes().at(i);
if (on->tieFor()) {
Tie* tie = on->tieFor()->clone();
tie->setScore(sc);
nn->setTieFor(tie);
tie->setStartNote(nn);
tieMap->add(on->tieFor(), tie);
}
if (on->tieBack()) {
Tie* tie = tieMap->findNew(on->tieBack());
if (tie) {
nn->setTieBack(tie);
tie->setEndNote(nn);
}
else {
qDebug("cloneMeasure: cannot find tie, track %d", track);
}
}
}
}
}
ne->setOffset(oe->offset());
ne->setScore(sc);
s->add(ne);
}
}
for (Element* e : el()) {
Element* ne = e->clone();
ne->setScore(sc);
ne->setOffset(e->offset());
m->add(ne);
}
return m;
}
//---------------------------------------------------------
// snap
//---------------------------------------------------------
Fraction Measure::snap(const Fraction& tick, const QPointF p) const
{
Segment* s = first();
for (; s->next(); s = s->next()) {
qreal x = s->x();
qreal dx = s->next()->x() - x;
if (s->tick() == tick)
x += dx / 3.0 * 2.0;
else if (s->next()->tick() == tick)
x += dx / 3.0;
else
x += dx * .5;
if (p.x() < x)
break;
}
return s->tick();
}
//---------------------------------------------------------
// snapNote
//---------------------------------------------------------
Fraction Measure::snapNote(const Fraction& /*tick*/, const QPointF p, int staff) const
{
Segment* s = first();
for (;;) {
Segment* ns = s->next();
while (ns && ns->element(staff) == 0)
ns = ns->next();
if (ns == 0)
break;
qreal x = s->x();
qreal nx = x + (ns->x() - x) * .5;
if (p.x() < nx)
break;
s = ns;
}
return s->tick();
}
//---------------------------------------------------------
// searchSegment
/// Finds a segment which x position is most close to the
/// given \p x.
/// \param x The x coordinate in measure coordinates.
/// \param st Type of segments to search.
/// \param strack start of track range (strack included)
/// in which the found segment should contain elements.
/// \param etrack end of track range (etrack excluded)
/// in which the found segment should contain elements.
/// \param preferredSegment If not nullptr, will give
/// more space to the given segment when searching it by
/// coordinate.
/// \returns The segment that was found.
//---------------------------------------------------------
Segment* Measure::searchSegment(qreal x, SegmentType st, int strack, int etrack, const Segment* preferredSegment, qreal spacingFactor) const
{
const int lastTrack = etrack - 1;
for (Segment* segment = first(st); segment; segment = segment->next(st)) {
if (!segment->hasElements(strack, lastTrack))
continue;
Segment* ns = segment->next(st);
for (; ns; ns = ns->next(st)) {
if (ns->hasElements(strack, lastTrack))
break;
}
if (!ns)
return segment;
if (preferredSegment == segment) {
if (x < (segment->x() + (ns->x() - segment->x())))
return segment;
}
else if (preferredSegment == ns) {
if (x <= segment->x())
return segment;
}
else {
if (x < (segment->x() + (ns->x() - segment->x()) * spacingFactor))
return segment;
}
}
return nullptr;
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Measure::getProperty(Pid propertyId) const
{
switch(propertyId) {
case Pid::TIMESIG_NOMINAL:
return QVariant::fromValue(_timesig);
case Pid::TIMESIG_ACTUAL:
return QVariant::fromValue(_len);
case Pid::MEASURE_NUMBER_MODE:
return int(measureNumberMode());
case Pid::BREAK_MMR:
return breakMultiMeasureRest();
case Pid::REPEAT_COUNT:
return repeatCount();
case Pid::USER_STRETCH:
return userStretch();
default:
return MeasureBase::getProperty(propertyId);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Measure::setProperty(Pid propertyId, const QVariant& value)
{
switch (propertyId) {
case Pid::TIMESIG_NOMINAL:
_timesig = value.value<Fraction>();
break;
case Pid::TIMESIG_ACTUAL:
_len = value.value<Fraction>();
break;
case Pid::MEASURE_NUMBER_MODE:
setMeasureNumberMode(MeasureNumberMode(value.toInt()));
break;
case Pid::BREAK_MMR:
setBreakMultiMeasureRest(value.toBool());
break;
case Pid::REPEAT_COUNT:
setRepeatCount(value.toInt());
break;
case Pid::USER_STRETCH:
setUserStretch(value.toDouble());
break;
default:
return MeasureBase::setProperty(propertyId, value);
}
triggerLayout();
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Measure::propertyDefault(Pid propertyId) const
{
switch(propertyId) {
case Pid::TIMESIG_NOMINAL:
case Pid::TIMESIG_ACTUAL:
return QVariant();
case Pid::MEASURE_NUMBER_MODE:
return int(MeasureNumberMode::AUTO);
case Pid::BREAK_MMR:
return false;
case Pid::REPEAT_COUNT:
return 2;
case Pid::USER_STRETCH:
return 1.0;
case Pid::NO_OFFSET:
return 0;
case Pid::IRREGULAR:
return false;
default:
break;
}
return MeasureBase::propertyDefault(propertyId);
}
//-------------------------------------------------------------------
// mmRestFirst
// this is a multi measure rest
// returns first measure of replaced sequence of empty measures
//-------------------------------------------------------------------
Measure* Measure::mmRestFirst() const
{
Q_ASSERT(isMMRest());
if (prev())
return toMeasure(prev()->next());
return score()->firstMeasure();
}
//-------------------------------------------------------------------
// mmRestLast
// this is a multi measure rest
// returns last measure of replaced sequence of empty measures
//-------------------------------------------------------------------
Measure* Measure::mmRestLast() const
{
Q_ASSERT(isMMRest());
if (next())
return toMeasure(next()->prev());
return score()->lastMeasure();
}
//---------------------------------------------------------
// mmRest1
// return the multi measure rest this measure is covered
// by
//---------------------------------------------------------
const Measure* Measure::mmRest1() const
{
if (_mmRest)
return _mmRest;
if (_mmRestCount != -1)
// return const_cast<Measure*>(this);
return this;
const Measure* m = this;
while (m && !m->_mmRest)
m = m->prevMeasure();
if (m)
return const_cast<Measure*>(m->_mmRest);
return 0;
}
//-------------------------------------------------------------------
// userStretch
//-------------------------------------------------------------------
qreal Measure::userStretch() const
{
return (score()->layoutMode() == LayoutMode::FLOAT ? 1.0 : _userStretch);
}
//---------------------------------------------------------
// nextElementStaff
//---------------------------------------------------------
Element* Measure::nextElementStaff(int staff)
{
Element* e = score()->selection().element();
if (!e && !score()->selection().elements().isEmpty())
e = score()->selection().elements().first();
// handle measure elements
if (e->parent() == this) {
auto i = std::find(el().begin(), el().end(), e);
if (i != el().end()) {
if (++i != el().end()) {
Element* resElement = *i;
if (resElement)
return resElement;
}
}
}
for (; e && e->type() != ElementType::SEGMENT; e = e->parent()) {
;
}
Segment* seg = toSegment(e);
Segment* nextSegment = seg ? seg->next() : first();
Element* next = seg->firstElementOfSegment(nextSegment, staff);
if (next)
return next;
return score()->lastElement();
}
//---------------------------------------------------------
// prevElementStaff
//---------------------------------------------------------
Element* Measure::prevElementStaff(int staff)
{
Element* e = score()->selection().element();
if (!e && !score()->selection().elements().isEmpty())
e = score()->selection().elements().first();
// handle measure elements
if (e->parent() == this) {
auto i = std::find(el().rbegin(), el().rend(), e);
if (i != el().rend()) {
if (++i != el().rend()) {
Element* resElement = *i;
if (resElement)
return resElement;
}
}
}
Measure* prevM = prevMeasureMM();
if (prevM) {
Segment* seg = prevM->last();
if (seg)
return seg->lastElement(staff);
}
return score()->firstElement();
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString Measure::accessibleInfo() const
{
return QString("%1: %2").arg(Element::accessibleInfo()).arg(QString::number(no() + 1));
}
//-----------------------------------------------------------------------------
// stretchMeasure
// resize width of measure to targetWidth
//-----------------------------------------------------------------------------
void Measure::stretchMeasure(qreal targetWidth)
{
bbox().setWidth(targetWidth);
Fraction minTick = computeTicks();
//---------------------------------------------------
// compute stretch
//---------------------------------------------------
std::multimap<qreal, Segment*> springs;
Segment* seg = first();
while (seg && !seg->enabled())
seg = seg->next();
qreal minimumWidth = seg ? seg->x() : 0.0;
for (Segment& s : _segments) {
if (!s.enabled() || !s.visible())
continue;
Fraction t = s.ticks();
if (t.isNotZero()) {
qreal str = 1.0 + 0.865617 * log(qreal(t.ticks()) / qreal(minTick.ticks())); // .6 * log(t / minTick.ticks()) / log(2);
qreal d = s.width() / str;
s.setStretch(str);
springs.insert(std::pair<qreal, Segment*>(d, &s));
}
minimumWidth += s.width();
}
//---------------------------------------------------
// compute 1/Force for a given Extend
//---------------------------------------------------
if (targetWidth > minimumWidth) {
qreal force = 0;
qreal c = 0.0;
for (auto i = springs.begin(); i != springs.end();) {
c += i->second->stretch();
minimumWidth -= i->second->width();
qreal f = (targetWidth - minimumWidth) / c;
++i;
if (i == springs.end() || f <= i->first) {
force = f;
break;
}
}
//---------------------------------------------------
// distribute stretch to segments
//---------------------------------------------------
for (auto& i : springs) {
qreal width = force * i.second->stretch();
if (width > i.second->width())
i.second->setWidth(width);
}
//---------------------------------------------------
// move segments to final position
//---------------------------------------------------
Segment* s = first();
while (s && !s->enabled())
s = s->next();
qreal x = s->pos().x();
while (s) {
s->rxpos() = x;
x += s->width();
s = s->nextEnabled();
}
}
//---------------------------------------------------
// layout individual elements
//---------------------------------------------------
for (Segment& s : _segments) {
if (!s.enabled())
continue;
for (Element* e : s.elist()) {
if (!e)
continue;
ElementType t = e->type();
int staffIdx = e->staffIdx();
if (t == ElementType::REPEAT_MEASURE || (t == ElementType::REST && (isMMRest() || toRest(e)->isFullMeasureRest()))) {
//
// element has to be centered in free space
// x1 - left measure position of free space
// x2 - right measure position of free space
Segment* s1;
for (s1 = s.prev(); s1 && !s1->enabled(); s1 = s1->prev())
;
Segment* s2;
for (s2 = s.next(); s2; s2 = s2->next()) {
if (s2->enabled() && !s2->isChordRestType() && s2->element(staffIdx * VOICES))
break;
}
qreal x1 = s1 ? s1->x() + s1->minRight() : 0;
qreal x2 = s2 ? s2->x() - s2->minLeft() : targetWidth;
if (isMMRest()) {
Rest* rest = toRest(e);
//
// center multi measure rest
//
qreal d = score()->styleP(Sid::multiMeasureRestMargin);
qreal w = x2 - x1 - 2 * d;
rest->layoutMMRest(w);
e->setPos(x1 - s.x() + d, e->staff()->height() * .5); // center vertically in measure
s.createShape(staffIdx);
}
else { // if (rest->isFullMeasureRest()) {
//
// center full measure rest
//
e->rxpos() = (x2 - x1 - e->width()) * .5 + x1 - s.x() - e->bbox().x();
s.createShape(staffIdx); // DEBUG
}
}
else if (t == ElementType::REST)
e->rxpos() = 0;
else if (t == ElementType::CHORD) {
Chord* c = toChord(e);
c->layout2();
if (c->tremolo()) {
Tremolo* tr = c->tremolo();
Chord* c1 = tr->chord1();
Chord* c2 = tr->chord2();
if (!tr->twoNotes() || (c1 && !c1->staffMove() && c2 && !c2->staffMove()))
tr->layout();
}
}
else if (t == ElementType::BAR_LINE) {
e->rypos() = 0.0;
// for end barlines, x position was set in createEndBarLines
if (s.segmentType() != SegmentType::EndBarLine)
e->rxpos() = 0.0;
}
}
}
}
//---------------------------------------------------
// computeTicks
// set ticks for all segments
// return minTick
//---------------------------------------------------
Fraction Measure::computeTicks()
{
Fraction minTick = ticks();
if (minTick <= Fraction(0,1)) {
qDebug("=====minTick %d measure %p", minTick.ticks(), this);
}
Q_ASSERT(minTick > Fraction(0,1));
Segment* ns = first();
while (ns && !ns->enabled())
ns = ns->next();
while (ns) {
Segment* s = ns;
ns = s->nextActive();
Fraction nticks = (ns ? ns->rtick() : ticks()) - s->rtick();
if (nticks.isNotZero()) {
if (nticks < minTick)
minTick = nticks;
}
s->setTicks(nticks);
}
return minTick;
}
//---------------------------------------------------------
// endBarLine
// return the first one
//---------------------------------------------------------
const BarLine* Measure::endBarLine() const
{
// search barline segment:
Segment* s = last();
while (s && !s->isEndBarLineType())
s = s->prev();
// search first element
if (s) {
for (const Element* e : s->elist()) {
if (e)
return toBarLine(e);
}
}
return 0;
}
//---------------------------------------------------------
// endBarLineType
// Assume all barlines have same type if there is more
// than one.
//---------------------------------------------------------
BarLineType Measure::endBarLineType() const
{
const BarLine* bl = endBarLine();
return bl ? bl->barLineType() : BarLineType::NORMAL;
}
//---------------------------------------------------------
// endBarLineType
// Assume all barlines have same visibility if there is more
// than one.
//---------------------------------------------------------
bool Measure::endBarLineVisible() const
{
const BarLine* bl = endBarLine();
return bl ? bl->visible() : true;
}
//---------------------------------------------------------
// triggerLayout
//---------------------------------------------------------
void Measure::triggerLayout() const
{
if (prev() || next()) // avoid triggering layout before getting added to a score
score()->setLayout(tick(), endTick(), 0, score()->nstaves() - 1, this);
}
//---------------------------------------------------------
// setEndBarLineType
// Create a *generated* barline with the given type and
// properties if none exists. Modify if it exists.
// Useful for import filters.
//---------------------------------------------------------
void Measure::setEndBarLineType(BarLineType val, int track, bool visible, QColor color)
{
Segment* seg = undoGetSegment(SegmentType::EndBarLine, endTick());
// get existing bar line for this staff, if any
BarLine* bl = toBarLine(seg->element(track));
if (!bl) {
// no suitable bar line: create a new one
bl = new BarLine(score());
bl->setParent(seg);
bl->setTrack(track);
score()->addElement(bl);
}
bl->setGenerated(false);
bl->setBarLineType(val);
bl->setVisible(visible);
bl->setColor(color.isValid() ? color : curColor());
}
//---------------------------------------------------------
// barLinesSetSpan
//---------------------------------------------------------
void Measure::barLinesSetSpan(Segment* seg)
{
int track = 0;
for (Staff* staff : score()->staves()) {
BarLine* bl = toBarLine(seg->element(track)); // get existing bar line for this staff, if any
if (bl) {
if (bl->generated()) {
bl->setSpanStaff(staff->barLineSpan());
bl->setSpanFrom(staff->barLineFrom());
bl->setSpanTo(staff->barLineTo());
}
}
else {
bl = new BarLine(score());
bl->setParent(seg);
bl->setTrack(track);
bl->setGenerated(true);
bl->setSpanStaff(staff->barLineSpan());
bl->setSpanFrom(staff->barLineFrom());
bl->setSpanTo(staff->barLineTo());
bl->layout();
score()->addElement(bl);
}
track += VOICES;
}
}
//---------------------------------------------------------
// createEndBarLines
// actually creates or modifies barlines
// return the width change for measure
//---------------------------------------------------------
qreal Measure::createEndBarLines(bool isLastMeasureInSystem)
{
int nstaves = score()->nstaves();
Segment* seg = findSegmentR(SegmentType::EndBarLine, ticks());
Measure* nm = nextMeasure();
qreal blw = 0.0;
#if 0
#ifndef NDEBUG
computeMinWidth();
#endif
#endif
qreal oldWidth = width();
if (nm && nm->repeatStart() && !repeatEnd() && !isLastMeasureInSystem && next() == nm) {
// we may skip barline at end of a measure immediately before a start repeat:
// next measure is repeat start, this measure is not a repeat end,
// this is not last measure of system, no intervening frame
if (!seg)
return 0.0;
seg->setEnabled(false);
}
else {
BarLineType t = nm ? BarLineType::NORMAL : BarLineType::END;
if (!seg)
seg = getSegmentR(SegmentType::EndBarLine, ticks());
seg->setEnabled(true);
//
// Set flag "hasCourtesyKeySig" if this measure needs a courtesy key sig.
// This flag is later used to set a double end bar line and to actually
// create the courtesy key sig.
//
bool show = score()->styleB(Sid::genCourtesyKeysig) && !sectionBreak() && nm;
setHasCourtesyKeySig(false);
if (isLastMeasureInSystem && show) {
Fraction tick = endTick();
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
Staff* staff = score()->staff(staffIdx);
KeySigEvent key1 = staff->keySigEvent(tick - Fraction::fromTicks(1));
KeySigEvent key2 = staff->keySigEvent(tick);
if (!(key1 == key2)) {
// locate a key sig. in next measure and, if found,
// check if it has court. sig turned off
Segment* s = nm->findSegment(SegmentType::KeySig, tick);
if (s) {
KeySig* ks = toKeySig(s->element(staffIdx * VOICES));
if (ks && !ks->showCourtesy())
continue;
}
setHasCourtesyKeySig(true);
t = BarLineType::DOUBLE;
break;
}
}
}
bool force = false;
if (repeatEnd()) {
t = BarLineType::END_REPEAT;
force = true;
}
else if (isLastMeasureInSystem && nextMeasure() && nextMeasure()->repeatStart()) {
t = BarLineType::NORMAL;
// force = true;
}
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
int track = staffIdx * VOICES;
BarLine* bl = toBarLine(seg->element(track));
Staff* staff = score()->staff(staffIdx);
if (!bl) {
bl = new BarLine(score());
bl->setParent(seg);
bl->setTrack(track);
bl->setGenerated(true);
bl->setSpanStaff(staff->barLineSpan());
bl->setSpanFrom(staff->barLineFrom());
bl->setSpanTo(staff->barLineTo());
bl->setBarLineType(t);
score()->addElement(bl);
}
else {
// do not change bar line type if bar line is user modified
// and its not a repeat start/end barline (forced)
if (bl->generated()) {
bl->setSpanStaff(staff->barLineSpan());
bl->setSpanFrom(staff->barLineFrom());
bl->setSpanTo(staff->barLineTo());
bl->setBarLineType(t);
}
else {
if (bl->barLineType() != t) {
if (force) {
bl->undoChangeProperty(Pid::BARLINE_TYPE, QVariant::fromValue(t));
bl->setGenerated(true);
}
}
}
}
bl->layout();
blw = qMax(blw, bl->width());
}
// right align within segment
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
int track = staffIdx * VOICES;
BarLine* bl = toBarLine(seg->element(track));
if (bl)
bl->rxpos() += blw - bl->width();
}
seg->createShapes();
}
// set relative position of end barline and clef
// if end repeat, clef goes after, otherwise clef goes before
Segment* clefSeg = findSegmentR(SegmentType::Clef, ticks());
if (clefSeg) {
bool wasVisible = clefSeg->visible();
int visibleInt = 0;
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
int track = staffIdx * VOICES;
Clef* clef = toClef(clefSeg->element(track));
if (clef) {
bool showCourtesy = score()->genCourtesyClef() && clef->showCourtesy(); // normally show a courtesy clef
// check if the measure is the last measure of the system or the last measure before a frame
bool lastMeasure = isLastMeasureInSystem || (nm ? !(next() == nm) : true);
if (!nm || isFinalMeasureOfSection() || (lastMeasure && !showCourtesy)) {
// hide the courtesy clef in the final measure of a section, or if the measure is the final measure of a system
// and the score style or the clef style is set to "not show courtesy clef",
// or if the clef is at the end of the very last measure of the score
clef->clear();
clefSeg->createShape(staffIdx);
if (visibleInt == 0)
visibleInt = 1;
}
else {
clef->layout();
clefSeg->createShape(staffIdx);
visibleInt = 2;
}
}
}
if (visibleInt == 2) // there is at least one visible clef in the clef segment
clefSeg->setVisible(true);
else if (visibleInt == 1) // all (courtesy) clefs in the clef segment are not visible
clefSeg->setVisible(false);
else // should never happen
qDebug("Clef Segment without Clef elements at tick %d/%d",clefSeg->tick().numerator(),clefSeg->tick().denominator());
if ((wasVisible != clefSeg->visible()) && system()) // recompute the width only if necessary
computeMinWidth();
if (seg) {
Segment* s1;
Segment* s2;
if (repeatEnd()) {
s1 = seg;
s2 = clefSeg;
}
else {
s1 = clefSeg;
s2 = seg;
}
if (s1->next() != s2) {
_segments.remove(s1);
_segments.insert(s1, s2);
}
}
}
// fix segment layout
Segment* s = seg->prevActive();
if (s) {
qreal x = s->rxpos();
computeMinWidth(s, x, false);
}
#if 0
#ifndef NDEBUG
qreal w = width();
computeMinWidth();
if (!qFuzzyCompare(w, width()))
qDebug("width mismatch %f != %f at %d", w, width(), tick());
#endif
#endif
return width() - oldWidth;
}
//---------------------------------------------------------
// basicStretch
//---------------------------------------------------------
qreal Measure::basicStretch() const
{
qreal stretch = userStretch() * score()->styleD(Sid::measureSpacing);
if (stretch < 1.0)
stretch = 1.0;
return stretch;
}
//---------------------------------------------------------
// basicWidth
//---------------------------------------------------------
qreal Measure::basicWidth() const
{
Segment* ls = last();
qreal w = (ls->x() + ls->width()) * basicStretch();
qreal minMeasureWidth = score()->styleP(Sid::minMeasureWidth);
if (w < minMeasureWidth)
w = minMeasureWidth;
return w;
}
//---------------------------------------------------------
// layoutWeight
//---------------------------------------------------------
int Measure::layoutWeight(int maxMMRestLength) const
{
int w = ticks().ticks();
// reduce weight of mmrests
// so the nominal width is not directly proportional to duration (still linear, just not 1:1)
// and they are not so "greedy" in taking up available space on a system
if (isMMRest()) {
int timesigTicks = timesig().ticks();
// TODO: style setting
if (maxMMRestLength) {
int maxW = timesigTicks * maxMMRestLength;
w = qMin(w, maxW);
}
w -= timesigTicks;
w = timesigTicks + w / 32;
}
return w;
}
//-------------------------------------------------------------------
// addSystemHeader
/// Add elements to make this measure suitable as the first measure
/// of a system.
// The system header can contain a starting BarLine, a Clef,
// and a KeySig
//-------------------------------------------------------------------
void Measure::addSystemHeader(bool isFirstSystem)
{
int staffIdx = 0;
Segment* kSegment = findFirstR(SegmentType::KeySig, Fraction(0,1));
Segment* cSegment = findFirstR(SegmentType::HeaderClef, Fraction(0,1));
for (const Staff* staff : score()->staves()) {
const int track = staffIdx * VOICES;
if (isFirstSystem || score()->styleB(Sid::genClef)) {
// find the clef type at the previous tick
ClefTypeList cl = staff->clefType(tick() - Fraction::fromTicks(1));
Segment* s = nullptr;
if (prevMeasure())
// look for a clef change at the end of the previous measure
s = prevMeasure()->findSegment(SegmentType::Clef, tick());
else if (isMMRest())
// look for a header clef at the beginning of the first underlying measure
s = mmRestFirst()->findFirstR(SegmentType::HeaderClef, Fraction(0,1));
if (s) {
Clef* c = toClef(s->element(track));
if (c)
cl = c->clefTypeList();
}
Clef* clef;
if (!cSegment) {
cSegment = new Segment(this, SegmentType::HeaderClef, Fraction(0,1));
cSegment->setHeader(true);
add(cSegment);
clef = 0;
}
else
clef = toClef(cSegment->element(track));
if (staff->staffType(tick())->genClef()) {
if (!clef) {
//
// create missing clef
//
clef = new Clef(score());
clef->setTrack(track);
clef->setGenerated(true);
clef->setParent(cSegment);
cSegment->add(clef);
}
if (clef->generated())
clef->setClefType(cl);
clef->setSmall(false);
clef->layout();
}
else if (clef) {
clef->parent()->remove(clef);
delete clef;
}
//cSegment->createShape(staffIdx);
cSegment->setEnabled(true);
}
else {
if (cSegment)
cSegment->setEnabled(false);
}
// keep key sigs in TABs: TABs themselves should hide them
bool needKeysig = isFirstSystem || score()->styleB(Sid::genKeysig);
// If we need a Key::C KeySig (which would be invisible) and there is
// a courtesy key sig, dont create it and switch generated flags.
// This avoids creating an invisible KeySig which can distort layout.
KeySigEvent keyIdx = staff->keySigEvent(tick());
KeySig* ksAnnounce = 0;
if (needKeysig && (keyIdx.key() == Key::C)) {
Measure* pm = prevMeasure();
if (pm && pm->hasCourtesyKeySig()) {
Segment* ks = pm->first(SegmentType::KeySigAnnounce);
if (ks) {
ksAnnounce = toKeySig(ks->element(track));
if (ksAnnounce) {
needKeysig = false;
// if (keysig) {
// ksAnnounce->setGenerated(false);
//TODO keysig->setGenerated(true);
// }
}
}
}
}
needKeysig = needKeysig && (keyIdx.key() != Key::C || keyIdx.custom() || keyIdx.isAtonal());
if (needKeysig) {
KeySig* keysig;
if (!kSegment) {
kSegment = new Segment(this, SegmentType::KeySig, Fraction(0,1));
kSegment->setHeader(true);
add(kSegment);
keysig = 0;
}
else
keysig = toKeySig(kSegment->element(track));
if (!keysig) {
//
// create missing key signature
//
keysig = new KeySig(score());
keysig->setTrack(track);
keysig->setGenerated(true);
keysig->setParent(kSegment);
kSegment->add(keysig);
}
keysig->setKeySigEvent(keyIdx);
keysig->layout();
//kSegment->createShape(staffIdx);
kSegment->setEnabled(true);
}
else {
if (kSegment && staff->isPitchedStaff(tick())) {
// do not disable user modified keysigs
bool disable = true;
for (int i = 0; i < score()->nstaves(); ++i) {
Element* e = kSegment->element(i * VOICES);
Key key = score()->staff(i)->key(tick());
if ((e && !e->generated()) || (key != keyIdx.key())) {
disable = false;
}
else if (e && e->generated() && key == keyIdx.key() && keyIdx.key() == Key::C){
// If a key sig segment is disabled, it may be re-enabled if there is
// a transposing instrument using a different key sig.
// To prevent this from making the wrong key sig display, remove any key
// sigs on staves where the key in this measure is C.
kSegment->remove(e);
}
}
if (disable)
kSegment->setEnabled(false);
else {
Element* e = kSegment->element(track);
if (e && e->isKeySig()) {
KeySig* keysig = toKeySig(e);
keysig->layout();
}
}
}
}
++staffIdx;
}
if (cSegment)
cSegment->createShapes();
if (kSegment)
kSegment->createShapes();
//
// create systemic barline
//
Segment* s = findSegment(SegmentType::BeginBarLine, tick());
int n = score()->nstaves();
if ((n > 1 && score()->styleB(Sid::startBarlineMultiple)) || (n == 1 && score()->styleB(Sid::startBarlineSingle))) {
if (!s) {
s = new Segment(this, SegmentType::BeginBarLine, Fraction(0,1));
add(s);
}
for (int track = 0; track < score()->ntracks(); track += VOICES) {
BarLine* bl = toBarLine(s->element(track));
if (!bl) {
bl = new BarLine(score());
bl->setTrack(track);
bl->setGenerated(true);
bl->setParent(s);
bl->setBarLineType(BarLineType::NORMAL);
bl->setSpanStaff(true);
bl->layout();
s->add(bl);
}
}
s->createShapes();
s->setEnabled(true);
s->setHeader(true);
setHeader(true);
}
else if (s)
s->setEnabled(false);
checkHeader();
}
//---------------------------------------------------------
// addSystemTrailer
//---------------------------------------------------------
void Measure::addSystemTrailer(Measure* nm)
{
Fraction _rtick = ticks();
bool isFinalMeasure = isFinalMeasureOfSection();
// locate a time sig. in the next measure and, if found,
// check if it has court. sig. turned off
TimeSig* ts = nullptr;
bool showCourtesySig = false;
Segment* s = findSegmentR(SegmentType::TimeSigAnnounce, _rtick);
if (nm && score()->genCourtesyTimesig() && !isFinalMeasure && !score()->floatMode()) {
Segment* tss = nm->findSegmentR(SegmentType::TimeSig, Fraction(0,1));
if (tss) {
int nstaves = score()->nstaves();
for (int track = 0; track < nstaves * VOICES; track += VOICES) {
ts = toTimeSig(tss->element(track));
if (ts)
break;
}
if (ts && ts->showCourtesySig()) {
showCourtesySig = true;
// if due, create a new courtesy time signature for each staff
if (!s) {
s = new Segment(this, SegmentType::TimeSigAnnounce, _rtick);
s->setTrailer(true);
add(s);
}
s->setEnabled(true);
for (int track = 0; track < nstaves * VOICES; track += VOICES) {
TimeSig* nts = toTimeSig(tss->element(track));
if (!nts)
continue;
ts = toTimeSig(s->element(track));
if (!ts) {
ts = new TimeSig(score());
ts->setTrack(track);
ts->setGenerated(true);
ts->setParent(s);
score()->undoAddElement(ts);
}
ts->setFrom(nts);
ts->layout();
//s->createShape(track / VOICES);
}
s->createShapes();
}
}
}
if (!showCourtesySig && s) {
// remove any existing time signatures
s->setEnabled(false);
}
// courtesy key signatures, clefs
int n = score()->nstaves();
bool show = hasCourtesyKeySig();
s = findSegmentR(SegmentType::KeySigAnnounce, _rtick);
Segment* clefSegment = findSegmentR(SegmentType::Clef, ticks());
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
int track = staffIdx * VOICES;
Staff* staff = score()->staff(staffIdx);
if (show) {
if (!s) {
s = new Segment(this, SegmentType::KeySigAnnounce, _rtick);
s->setTrailer(true);
add(s);
}
KeySig* ks = toKeySig(s->element(track));
KeySigEvent key2 = staff->keySigEvent(endTick());
if (!ks) {
ks = new KeySig(score());
ks->setTrack(track);
ks->setGenerated(true);
ks->setParent(s);
s->add(ks);
}
//else if (!(ks->keySigEvent() == key2)) {
// score()->undo(new ChangeKeySig(ks, key2, ks->showCourtesy()));
// }
ks->setKeySigEvent(key2);
ks->layout();
//s->createShape(track / VOICES);
s->setEnabled(true);
}
else {
// remove any existent courtesy key signature
if (s)
s->setEnabled(false);
}
if (clefSegment) {
Clef* clef = toClef(clefSegment->element(track));
if (clef)
clef->setSmall(true);
}
}
if (s)
s->createShapes();
if (clefSegment)
clefSegment->createShapes();
checkTrailer();
}
//---------------------------------------------------------
// removeSystemHeader
//---------------------------------------------------------
void Measure::removeSystemHeader()
{
if (!header())
return;
for (Segment* seg = first(); seg; seg = seg->next()) {
if (!seg->header())
break;
seg->setEnabled(false);
}
setHeader(false);
}
//---------------------------------------------------------
// removeSystemTrailer
//---------------------------------------------------------
void Measure::removeSystemTrailer()
{
bool changed = false;
for (Segment* seg = last(); seg != first(); seg = seg->prev()) {
if (!seg->trailer())
break;
if (seg->enabled())
seg->setEnabled(false);
changed = true;
}
setTrailer(false);
if (system() && changed)
computeMinWidth();
}
//---------------------------------------------------------
// checkHeader
//---------------------------------------------------------
void Measure::checkHeader()
{
for (Segment* seg = first(); seg; seg = seg->next()) {
if (seg->enabled() && seg->header()) {
setHeader(seg->header());
break;
}
}
}
//---------------------------------------------------------
// checkTrailer
//---------------------------------------------------------
void Measure::checkTrailer()
{
for (Segment* seg = last(); seg != first(); seg = seg->prev()) {
if (seg->enabled() && seg->trailer()) {
setTrailer(seg->trailer());
break;
}
}
}
//---------------------------------------------------------
// setStretchedWidth
//---------------------------------------------------------
void Measure::setStretchedWidth(qreal w)
{
qreal minWidth = isMMRest() ? score()->styleP(Sid::minMMRestWidth) : score()->styleP(Sid::minMeasureWidth);
if (w < minWidth)
w = minWidth;
qreal stretchableWidth = 0.0;
for (Segment* s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
if (!s->enabled())
continue;
stretchableWidth += s->width();
}
const int maxMMRestLength = 32; // TODO: style
int weight = layoutWeight(maxMMRestLength);
w += stretchableWidth * (basicStretch() - 1.0) * weight / 1920.0;
setWidth(w);
}
//---------------------------------------------------------
// hasAccidental
//---------------------------------------------------------
static bool hasAccidental(Segment* s)
{
Score* score = s->score();
for (int track = 0; track < s->score()->ntracks(); ++track) {
Staff* staff = score->staff(track2staff(track));
if (!staff->show())
continue;
Element* e = s->element(track);
if (!e || !e->isChord())
continue;
Chord* c = toChord(e);
for (Note* n : c->notes()) {
if (n->accidental())
return true;
}
}
return false;
}
//---------------------------------------------------------
// computeMinWidth
// sets the minimum stretched width of segment list s
// set the width and x position for all segments
//---------------------------------------------------------
void Measure::computeMinWidth(Segment* s, qreal x, bool isSystemHeader)
{
Segment* fs = firstEnabled();
if (!fs->visible()) // first enabled could be a clef change on invisible staff
fs = fs->nextActive();
bool first = isFirstInSystem();
const Shape ls(first ? QRectF(0.0, -1000000.0, 0.0, 2000000.0) : QRectF(0.0, 0.0, 0.0, spatium() * 4));
if (isMMRest()) {
// Reset MM rest to initial size and position
Segment* seg = findSegmentR(SegmentType::ChordRest, Fraction(0,1));
const int nstaves = score()->nstaves();
for (int st = 0; st < nstaves; ++st) {
Rest* mmRest = toRest(seg->element(staff2track(st)));
if (mmRest) {
mmRest->rxpos() = 0;
mmRest->layoutMMRest(score()->styleP(Sid::minMMRestWidth) * mag());
mmRest->segment()->createShapes();
}
}
}
while (s) {
s->rxpos() = x;
if (!s->enabled() || !s->visible()) {
s->setWidth(0);
s = s->next();
continue;
}
Segment* ns = s->nextActive();
// end barline might be disabled
// but still consider it for spacing of previous segment
if (!ns)
ns = s->next(SegmentType::BarLineType);
qreal w;
if (ns) {
if (isSystemHeader && (ns->isChordRestType() || (ns->isClefType() && !ns->header()))) {
// this is the system header gap
w = s->minHorizontalDistance(ns, true);
isSystemHeader = false;
}
else {
w = s->minHorizontalDistance(ns, false);
}
// printf(" min %f <%s>(%d) <%s>(%d)\n", s->x(), s->subTypeName(), s->enabled(), ns->subTypeName(), ns->enabled());
#if 1
// look back for collisions with previous segments
// this is time consuming (ca. +5%) and probably requires more optimization
if (s == fs) // don't let the second segment cross measure start (not covered by the loop below)
w = std::max(w, ns->minLeft(ls) - s->x());
int n = 1;
for (Segment* ps = s; ps != fs;) {
qreal ww;
ps = ps->prevActive();
Q_ASSERT(ps); // ps should never be nullptr but better be safe.
if (!ps)
break;
if (ps->isChordRestType())
++n;
ww = ps->minHorizontalCollidingDistance(ns) - (s->x() - ps->x());
if (ps == fs)
ww = std::max(ww, ns->minLeft(ls) - s->x());
if (ww > w) {
// overlap !
// distribute extra space between segments ps - ss;
// only ChordRest segments get more space
// TODO: is there a special case n == 0 ?
qreal d = (ww - w) / n;
qreal xx = ps->x();
for (Segment* ss = ps; ss != s;) {
Segment* ns1 = ss->nextActive();
qreal ww1 = ss->width();
if (ss->isChordRestType()) {
ww1 += d;
ss->setWidth(ww1);
}
xx += ww1;
ns1->rxpos() = xx;
ss = ns1;
}
w += d;
x = xx;
break;
}
}
#endif
}
else
w = s->minRight();
s->setWidth(w);
x += w;
s = s->next();
}
setStretchedWidth(x);
}
void Measure::computeMinWidth()
{
Segment* s;
//
// skip disabled segment
//
for (s = first(); s && !s->enabled(); s = s->next()) {
s->rxpos() = 0;
s->setWidth(0);
}
if (!s) {
setWidth(0.0);
return;
}
qreal x;
bool first = isFirstInSystem();
// left barriere:
// Make sure no elements crosses the left boarder if first measure in a system.
//
Shape ls(first ? QRectF(0.0, -1000000.0, 0.0, 2000000.0) : QRectF(0.0, 0.0, 0.0, spatium() * 4));
x = s->minLeft(ls);
if (s->isStartRepeatBarLineType()) {
System* sys = system();
MeasureBase* pmb = prev();
if (pmb->isMeasure() && pmb->system() == sys && pmb->repeatEnd()) {
Segment* seg = toMeasure(pmb)->last();
// overlap end repeat barline with start repeat barline
if (seg->isEndBarLineType())
x -= score()->styleP(Sid::endBarWidth) * mag();
}
}
if (s->isChordRestType())
x += score()->styleP(hasAccidental(s) ? Sid::barAccidentalDistance : Sid::barNoteDistance);
else if (s->isClefType() || s->isHeaderClefType())
x += score()->styleP(Sid::clefLeftMargin);
else if (s->isKeySigType())
x = qMax(x, score()->styleP(Sid::keysigLeftMargin));
else if (s->isTimeSigType())
x = qMax(x, score()->styleP(Sid::timesigLeftMargin));
x += s->extraLeadingSpace().val() * spatium();
bool isSystemHeader = s->header();
computeMinWidth(s, x, isSystemHeader);
}
}