4560 lines
146 KiB
C++
4560 lines
146 KiB
C++
//=============================================================================
|
||
// 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->staffMag(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()->staffMag(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, don’t 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);
|
||
}
|
||
}
|