MuseScore/libmscore/measure.cpp

4230 lines
172 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
/**
\file
Implementation of most part of class Measure.
*/
#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"
2012-05-26 14:26:10 +02:00
#include "chord.h"
#include "clef.h"
#include "drumset.h"
#include "duration.h"
2012-05-26 14:26:10 +02:00
#include "dynamic.h"
#include "fret.h"
#include "glissando.h"
2012-05-26 14:26:10 +02:00
#include "hairpin.h"
#include "harmony.h"
#include "hook.h"
#include "icon.h"
#include "image.h"
#include "key.h"
#include "keysig.h"
2012-05-26 14:26:10 +02:00
#include "layoutbreak.h"
#include "layout.h"
2012-05-26 14:26:10 +02:00
#include "lyrics.h"
#include "note.h"
#include "ottava.h"
#include "page.h"
#include "part.h"
#include "pedal.h"
2012-05-26 14:26:10 +02:00
#include "pitchspelling.h"
#include "repeat.h"
#include "rest.h"
#include "score.h"
#include "segment.h"
#include "select.h"
#include "sig.h"
#include "slur.h"
2012-05-26 14:26:10 +02:00
#include "spacer.h"
#include "staff.h"
#include "stafftext.h"
2012-05-26 14:26:10 +02:00
#include "stafftype.h"
#include "stringdata.h"
#include "style.h"
#include "sym.h"
#include "system.h"
#include "tempotext.h"
#include "text.h"
#include "tie.h"
2012-05-26 14:26:10 +02:00
#include "tiemap.h"
#include "timesig.h"
#include "tremolo.h"
#include "trill.h"
#include "tuplet.h"
2012-05-26 14:26:10 +02:00
#include "tupletmap.h"
#include "undo.h"
#include "utils.h"
#include "volta.h"
#include "xml.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// MStaff
//---------------------------------------------------------
MStaff::MStaff()
{
_noText = 0;
2012-05-26 14:26:10 +02:00
distanceUp = .0;
distanceDown = .0;
lines = 0;
hasVoices = false;
_vspacerUp = 0;
_vspacerDown = 0;
_visible = true;
_slashStyle = false;
}
MStaff::~MStaff()
{
delete _noText;
2012-05-26 14:26:10 +02:00
delete lines;
delete _vspacerUp;
delete _vspacerDown;
}
MStaff::MStaff(const MStaff& m)
{
_noText = 0;
distanceUp = m.distanceUp;
distanceDown = m.distanceDown;
lines = m.lines;
hasVoices = m.hasVoices;
_vspacerUp = 0;
_vspacerDown = 0;
_visible = m._visible;
_slashStyle = m._slashStyle;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Measure
//---------------------------------------------------------
Measure::Measure(Score* s)
: MeasureBase(s),
_timesig(4,4), _len(4,4)
{
_repeatCount = 2;
_repeatFlags = Repeat::NONE;
2012-05-26 14:26:10 +02:00
int n = _score->nstaves();
staves.reserve(n);
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
MStaff* s = new MStaff;
Staff* staff = score()->staff(staffIdx);
s->lines = new StaffLines(score());
s->lines->setTrack(staffIdx * VOICES);
s->lines->setParent(this);
s->lines->setVisible(!staff->invisible());
staves.push_back(s);
}
_minWidth1 = 0.0;
_minWidth2 = 0.0;
2012-05-26 14:26:10 +02:00
_no = 0;
_noOffset = 0;
_noMode = MeasureNumberMode::AUTO;
2012-05-26 14:26:10 +02:00
_userStretch = 1.0; // ::style->measureSpacing;
_irregular = false;
_breakMultiMeasureRest = false;
_breakMMRest = false;
Bar lines: Fix custom span and generated management. This PR should complete the revision of bar line flag managements. - Make sure single bar line span changes affect both the `_customSpan` and the `_generated` flags. - Make sure that manually bringing a custom-spanned bar line to default span resets the `_customSpan` flag and, if no other customization is in effect. the `_generated` flag. - Deleting a measure bar line resets it to default configuration. - Fix a missing initialization of `Measure::_endBarLineColor` variable. - To simplify tests and debug, check boxes for the `BarLine::_customSpan` and the `BarLine::_customSubtype` have been added to the debugger dialogue box. As far as system-initial bar lines are concerned, they were made un-editable by a recent commit (https://github.com/musescore/MuseScore/pull/1300). This PR add a few consistency changes: - system-initial bar lines do not accept drops; - they are not saved to score output files and are ignored when reading from them; - their internal `_customSybtype` and _customSpan` flags are always false; - they do not show up in the Inspector (if a system bar line is selected, the Inspector remains blank) This PR DOES NOT include the special system-initial double bar management recently discussed. This will be part of a specific PR in the next days. Together with previous PR's, this should ensure that: - bar lines are written to a score output file only when some customization is in effect which cannot be reconstructed when reading back; - any measure bar line can be edited; - any measure bar line user edit is saved, written to the score file and read back properly; - no system bar line edit is possible.
2014-09-11 01:31:10 +02:00
_endBarLineColor = MScore::defaultColor;
2012-05-26 14:26:10 +02:00
_endBarLineGenerated = true;
_endBarLineVisible = true;
_endBarLineType = BarLineType::NORMAL;
_systemInitialBarLineType = BarLineType::NORMAL;
_mmRest = 0;
_mmRestCount = 0;
setFlag(ElementFlag::MOVABLE, true);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// measure
//---------------------------------------------------------
Measure::Measure(const Measure& m)
: MeasureBase(m)
{
_segments = m._segments.clone();
_timesig = m._timesig;
_len = m._len;
_repeatCount = m._repeatCount;
_repeatFlags = m._repeatFlags;
2013-05-29 10:31:26 +02:00
staves.reserve(m.staves.size());
2012-05-26 14:26:10 +02:00
foreach(MStaff* ms, m.staves)
staves.append(new MStaff(*ms));
_minWidth1 = m._minWidth1;
_minWidth2 = m._minWidth2;
2012-05-26 14:26:10 +02:00
_no = m._no;
_noOffset = m._noOffset;
_userStretch = m._userStretch;
2012-05-26 14:26:10 +02:00
_irregular = m._irregular;
_breakMultiMeasureRest = m._breakMultiMeasureRest;
_breakMMRest = m._breakMMRest;
_endBarLineGenerated = m._endBarLineGenerated;
_endBarLineVisible = m._endBarLineVisible;
_endBarLineType = m._endBarLineType;
_systemInitialBarLineType = m._systemInitialBarLineType; // possibly should be reset to NORMAL?
_mmRest = m._mmRest;
_mmRestCount = m._mmRestCount;
2012-05-26 14:26:10 +02:00
_playbackCount = m._playbackCount;
_endBarLineColor = m._endBarLineColor;
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Measure::setScore(Score* score)
{
MeasureBase::setScore(score);
for (Segment* s = first(); s; s = s->next())
s->setScore(score);
}
//---------------------------------------------------------
// MStaff::setScore
//---------------------------------------------------------
void MStaff::setScore(Score* score)
{
if (lines)
lines->setScore(score);
if (_vspacerUp)
_vspacerUp->setScore(score);
if (_vspacerDown)
_vspacerDown->setScore(score);
}
//---------------------------------------------------------
// Measure
//---------------------------------------------------------
Measure::~Measure()
{
for (Segment* s = first(); s;) {
Segment* ns = s->next();
delete s;
s = ns;
}
qDeleteAll(staves);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// dump
//---------------------------------------------------------
/**
Debug only.
*/
void Measure::dump() const
{
qDebug("dump measure:");
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Measure::remove(Segment* el)
{
#ifndef NDEBUG
2013-10-25 12:17:42 +02:00
if (score()->undoRedo()) {
2014-05-30 17:25:32 +02:00
qFatal("remove segment <%s> in undo/redo", el->subTypeName());
2013-10-25 12:17:42 +02:00
}
// Q_ASSERT(!score()->undoRedo());
Q_ASSERT(el->type() == Element::Type::SEGMENT);
2014-08-27 14:35:39 +02:00
Q_ASSERT(el->score() == score());
2012-05-26 14:26:10 +02:00
if (el->prev()) {
Q_ASSERT(el->prev()->next() == el);
}
else {
Q_ASSERT(el == _segments.first());
}
if (el->next()) {
Q_ASSERT(el->next()->prev() == el);
}
else {
Q_ASSERT(el == _segments.last());
}
#endif
2014-06-03 15:28:10 +02:00
#if 0
2012-05-26 14:26:10 +02:00
int tracks = staves.size() * VOICES;
for (int track = 0; track < tracks; track += VOICES) {
if (!el->element(track))
continue;
if (el->segmentType() == Segment::Type::KeySig)
2012-05-26 14:26:10 +02:00
score()->staff(track/VOICES)->setUpdateKeymap(true);
}
2014-06-03 15:28:10 +02:00
#endif
2012-05-26 14:26:10 +02:00
_segments.remove(el);
2012-08-08 20:46:29 +02:00
setDirty();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// AcEl
//---------------------------------------------------------
struct AcEl {
Note* note;
qreal x;
};
2013-06-20 10:19:04 +02:00
//---------------------------------------------------------
// layoutCR0
//---------------------------------------------------------
2013-01-02 14:33:23 +01:00
2015-02-19 10:28:25 +01:00
void Measure::layoutCR0(ChordRest* cr, qreal mm, AccidentalState* as)
2013-06-20 10:19:04 +02:00
{
qreal m = mm;
if (cr->small())
2014-05-26 15:31:36 +02:00
m *= score()->styleD(StyleIdx::smallNoteMag);
2013-06-20 10:19:04 +02:00
if (cr->type() == Element::Type::CHORD) {
2013-06-20 10:19:04 +02:00
Chord* chord = static_cast<Chord*>(cr);
for (Chord* c : chord->graceNotes())
2015-02-19 10:28:25 +01:00
layoutCR0(c, mm, as);
if (!chord->isGrace())
chord->cmdUpdateNotes(as); // TODO: merge cmdUpdateNotes()
2013-06-20 10:19:04 +02:00
2014-05-27 10:35:28 +02:00
if (chord->noteType() != NoteType::NORMAL)
2014-05-26 15:31:36 +02:00
m *= score()->styleD(StyleIdx::graceNoteMag);
2015-02-19 10:28:25 +01:00
const Drumset* drumset = 0;
if (cr->part()->instrument()->useDrumset())
drumset = cr->part()->instrument()->drumset();
2013-06-20 10:19:04 +02:00
if (drumset) {
2014-03-26 11:02:23 +01:00
for (Note* note : chord->notes()) {
2013-06-20 10:19:04 +02:00
int pitch = note->pitch();
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
}
else if (!note->fixed()) {
note->undoChangeProperty(P_ID::HEAD_GROUP, int(drumset->noteHead(pitch)));
// note->setHeadGroup(drumset->noteHead(pitch));
2013-06-20 10:19:04 +02:00
note->setLine(drumset->line(pitch));
continue;
2012-05-26 14:26:10 +02:00
}
}
2012-08-01 18:00:27 +02:00
}
2013-06-20 10:19:04 +02:00
chord->computeUp();
chord->layoutStem1();
}
if (m != mag()) {
cr->setMag(m);
setDirty();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// findAccidental
2012-08-07 12:44:19 +02:00
/// return current accidental value at note position
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-08-08 20:46:29 +02:00
AccidentalVal Measure::findAccidental(Note* note) const
2012-05-26 14:26:10 +02:00
{
Chord* chord = note->chord();
2012-05-26 14:26:10 +02:00
AccidentalState tversatz; // state of already set accidentals for this measure
tversatz.init(chord->staff()->key(tick()));
2012-05-26 14:26:10 +02:00
for (Segment* segment = first(); segment; segment = segment->next()) {
int startTrack = chord->staffIdx() * VOICES;
if (segment->segmentType() == Segment::Type::KeySig) {
KeySig* ks = static_cast<KeySig*>(segment->element(startTrack));
if (!ks)
2012-05-26 14:26:10 +02:00
continue;
tversatz.init(chord->staff()->key(segment->tick()));
}
else if (segment->segmentType() == Segment::Type::ChordRest) {
int endTrack = startTrack + VOICES;
for (int track = startTrack; track < endTrack; ++track) {
Element* e = segment->element(track);
if (!e || e->type() != Element::Type::CHORD)
continue;
Chord* chord = static_cast<Chord*>(e);
for (Chord* chord1 : chord->graceNotes()) {
for (Note* note1 : chord1->notes()) {
if (note1->tieBack())
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 : chord->notes()) {
if (note1->tieBack())
continue;
//
// compute accidental
//
int tpc = note1->tpc();
int line = absStep(tpc, note1->epitch());
2012-05-26 14:26:10 +02:00
if (note == note1)
return tversatz.accidentalVal(line);
tversatz.setAccidentalVal(line, tpc2alter(tpc));
}
}
2012-05-26 14:26:10 +02:00
}
}
qDebug("Measure::findAccidental: note not found");
return AccidentalVal::NATURAL;
2012-05-26 14:26:10 +02:00
}
2012-08-07 12:44:19 +02:00
//---------------------------------------------------------
// findAccidental
/// Compute accidental state at segment/staffIdx for
/// relative staff line.
//---------------------------------------------------------
2012-08-08 20:46:29 +02:00
AccidentalVal Measure::findAccidental(Segment* s, int staffIdx, int line) const
2012-08-06 21:55:51 +02:00
{
AccidentalState tversatz; // state of already set accidentals for this measure
Staff* staff = score()->staff(staffIdx);
2014-06-03 15:28:10 +02:00
tversatz.init(staff->key(tick()));
2012-08-06 21:55:51 +02:00
Segment::Type st = Segment::Type::ChordRest;
2014-06-01 20:24:29 +02:00
int startTrack = staffIdx * VOICES;
int endTrack = startTrack + VOICES;
2012-08-06 21:55:51 +02:00
for (Segment* segment = first(st); segment; segment = segment->next(st)) {
if (segment == s && staff->isPitchedStaff()) {
2012-08-07 12:44:19 +02:00
ClefType clef = staff->clef(s->tick());
2012-08-07 16:05:37 +02:00
int l = relStep(line, clef);
2012-08-06 21:55:51 +02:00
return tversatz.accidentalVal(l);
}
for (int track = startTrack; track < endTrack; ++track) {
Element* e = segment->element(track);
if (!e || e->type() != Element::Type::CHORD)
2012-08-06 21:55:51 +02:00
continue;
Chord* chord = static_cast<Chord*>(e);
for (Chord* chord1 : chord->graceNotes()) {
for (Note* note : chord1->notes()) {
if (note->tieBack())
continue;
int tpc = note->tpc();
int l = absStep(tpc, note->epitch());
tversatz.setAccidentalVal(l, tpc2alter(tpc));
}
}
2012-08-06 21:55:51 +02:00
for (Note* note : chord->notes()) {
2012-08-06 21:55:51 +02:00
if (note->tieBack())
continue;
int tpc = note->tpc();
int l = absStep(tpc, note->epitch());
2012-08-06 21:55:51 +02:00
tversatz.setAccidentalVal(l, tpc2alter(tpc));
}
}
}
qDebug("segment not found");
return AccidentalVal::NATURAL;
2012-08-06 21:55:51 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Measure::layout
/// Layout measure; must fit into \a width.
///
/// Note: minWidth = width - stretch
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Measure::layoutWidth(qreal width)
2012-05-26 14:26:10 +02:00
{
int nstaves = _score->nstaves();
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
staves[staffIdx]->distanceUp = 0.0;
staves[staffIdx]->distanceDown = 0.0;
StaffLines* sl = staves[staffIdx]->lines;
if (sl)
sl->setMag(score()->staff(staffIdx)->mag());
staves[staffIdx]->lines->layout();
}
// height of boundingRect will be set in system->layout2()
// keep old value for relayout
2013-01-02 09:29:17 +01:00
bbox().setRect(0.0, 0.0, width, height());
layoutX(width);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// tick2pos
2013-06-19 16:25:29 +02:00
// return x position for tick relative to System
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Measure::tick2pos(int tck) const
{
if (isMMRest()) {
Segment* s = first(Segment::Type::ChordRest);
qreal x1 = s->x();
qreal w = width() - x1;
return x1 + (tck * w) / (ticks() * mmRestCount());
}
2012-05-26 14:26:10 +02:00
Segment* s;
qreal x1 = 0;
qreal x2 = 0;
int tick1 = tick();
int tick2 = tick1;
for (s = first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
2013-06-19 16:25:29 +02:00
x2 = s->x();
2012-05-26 14:26:10 +02:00
tick2 = s->tick();
2013-06-19 16:25:29 +02:00
if (tck == tick2)
return x2 + pos().x();
if (tck <= tick2)
2012-05-26 14:26:10 +02:00
break;
x1 = x2;
tick1 = tick2;
}
if (s == 0) {
x2 = width();
2013-06-19 16:25:29 +02:00
tick2 = endTick();
2012-05-26 14:26:10 +02:00
}
2013-06-19 16:25:29 +02:00
qreal dx = x2 - x1;
int dt = tick2 - tick1;
x1 += (dt == 0) ? 0.0 : (dx * (tck - tick1) / dt);
return x1 + pos().x();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layout2
// called after layout of all pages
//---------------------------------------------------------
void Measure::layout2()
{
if (parent() == 0)
return;
Q_ASSERT(score()->nstaves() == staves.size());
int tracks = score()->nstaves() * VOICES;
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
static const Segment::Type st { Segment::Type::ChordRest };
2012-05-26 14:26:10 +02:00
for (int track = 0; track < tracks; ++track) {
for (Segment* s = first(st); s; s = s->next(st)) {
ChordRest* cr = s->cr(track);
2012-05-26 14:26:10 +02:00
if (!cr)
continue;
/* Lyrics line segments are now Spanner's belonging to System's: System takes care of them
int n = cr->lyricsList().size();
for (int i = 0; i < n; ++i) {
Lyrics* lyrics = cr->lyricsList().at(i);
2012-05-26 14:26:10 +02:00
if (lyrics)
system()->layoutLyrics(lyrics, s, track/VOICES);
} */
2012-05-26 14:26:10 +02:00
}
if (track % VOICES == 0) {
int staffIdx = track / VOICES;
qreal y = system()->staff(staffIdx)->y();
Spacer* sp = staves[staffIdx]->_vspacerDown;
if (sp) {
sp->layout();
int n = score()->staff(staffIdx)->lines() - 1;
sp->setPos(_spatium * .5, y + n * _spatium);
}
sp = staves[staffIdx]->_vspacerUp;
if (sp) {
sp->layout();
sp->setPos(_spatium * .5, y - sp->gap());
}
}
}
for (MStaff* ms : staves)
ms->lines->setWidth(width());
2012-05-26 14:26:10 +02:00
MeasureBase::layout(); // layout LAYOUT_BREAK elements
//
// set measure number
//
bool smn = false;
2012-10-17 10:39:39 +02:00
2014-08-11 15:25:55 +02:00
if (_noMode == MeasureNumberMode::SHOW)
smn = true;
else if (_noMode == MeasureNumberMode::HIDE)
smn = false;
else {
if (score()->styleB(StyleIdx::showMeasureNumber)
&& !_irregular
&& (_no || score()->styleB(StyleIdx::showMeasureNumberOne))) {
if (score()->styleB(StyleIdx::measureNumberSystem))
smn = system()->firstMeasure() == this;
else {
smn = (_no == 0 && score()->styleB(StyleIdx::showMeasureNumberOne)) ||
( ((_no+1) % score()->style(StyleIdx::measureNumberInterval).toInt()) == 0 );
}
}
2014-08-11 15:25:55 +02:00
}
QString s;
if (smn)
s = QString("%1").arg(_no + 1);
int nn = 1;
bool nas = score()->styleB(StyleIdx::measureNumberAllStaffs);
if (!nas) {
//find first non invisible staff
for (int staffIdx = 0; staffIdx < staves.size(); ++staffIdx) {
MStaff* ms = staves.at(staffIdx);
2014-08-11 15:25:55 +02:00
SysStaff* s = system()->staff(staffIdx);
Staff* staff = score()->staff(staffIdx);
if (ms->visible() && staff->show() && s->show()) {
nn = staffIdx;
break;
}
2014-08-11 15:25:55 +02:00
}
}
for (int staffIdx = 0; staffIdx < staves.size(); ++staffIdx) {
MStaff* ms = staves.at(staffIdx);
Text* t = ms->noText();
2014-08-25 19:30:56 +02:00
if (t) {
Q_ASSERT(t->score() == score());
Q_ASSERT(t->parent() == this);
2014-08-11 15:25:55 +02:00
t->setTrack(staffIdx * VOICES);
2014-08-25 19:30:56 +02:00
}
2014-08-11 15:25:55 +02:00
if (smn && ((staffIdx == nn) || nas)) {
if (t == 0) {
t = new Text(score());
t->setFlag(ElementFlag::ON_STAFF, true);
t->setTrack(staffIdx * VOICES);
t->setGenerated(true);
t->setTextStyleType(TextStyleType::MEASURE_NUMBER);
t->setParent(this);
2014-08-25 19:30:56 +02:00
score()->undo(new AddElement(t));
// score()->undoAddElement(t);
2012-10-17 10:39:39 +02:00
}
t->setXmlText(s);
2014-08-11 15:25:55 +02:00
t->layout();
}
else {
if (t)
2014-08-25 19:30:56 +02:00
score()->undo(new RemoveElement(t));
2012-05-26 14:26:10 +02:00
}
2014-08-11 15:25:55 +02:00
}
2012-05-26 14:26:10 +02:00
//
// slur layout needs articulation layout first
//
for (Segment* s = first(st); s; s = s->next(st)) {
for (int track = 0; track < tracks; ++track) {
if (!score()->staff(track / VOICES)->show()) {
2013-03-14 16:36:24 +01:00
track += VOICES-1;
continue;
}
2014-07-02 09:55:50 +02:00
ChordRest* cr = static_cast<ChordRest*>(s->element(track));
if (!cr)
continue;
if (cr->type() == Element::Type::CHORD) {
Chord* c = static_cast<Chord*>(cr);
for (const Note* note : c->notes()) {
Tie* tie = note->tieFor();
if (tie)
tie->layout();
foreach (Spanner* sp, note->spannerFor())
sp->layout();
2012-05-26 14:26:10 +02:00
}
}
2014-07-02 09:55:50 +02:00
DurationElement* de = cr;
while (de->tuplet() && de->tuplet()->elements().front() == de) {
de->tuplet()->layout();
de = de->tuplet();
}
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// findChord
2013-06-24 13:46:21 +02:00
/// Search for chord at position \a tick in \a track
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-06-24 13:46:21 +02:00
Chord* Measure::findChord(int tick, int track)
2012-05-26 14:26:10 +02:00
{
for (Segment* seg = last(); seg; seg = seg->prev()) {
if (seg->tick() < tick)
return 0;
if (seg->tick() == tick) {
Element* el = seg->element(track);
if (el && el->type() == Element::Type::CHORD)
return static_cast<Chord*>(el);
2012-05-26 14:26:10 +02:00
}
}
return 0;
}
//---------------------------------------------------------
// findChordRest
2013-06-24 13:46:21 +02:00
/// Search for chord or rest at position \a tick at \a staff in \a voice.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
ChordRest* Measure::findChordRest(int tick, int track)
{
for (Segment* seg = first(); seg; seg = seg->next()) {
if (seg->tick() > tick)
return 0;
if (seg->tick() == tick) {
Element* el = seg->element(track);
if (el && (el->type() == Element::Type::CHORD || el->type() == Element::Type::REST)) {
2012-05-26 14:26:10 +02:00
return (ChordRest*)el;
}
}
}
return 0;
}
//---------------------------------------------------------
// tick2segment
//---------------------------------------------------------
Segment* Measure::tick2segment(int tick, Segment::Type st) const
2012-05-26 14:26:10 +02:00
{
for (Segment* s = first(); s; s = s->next()) {
if (s->tick() == tick) {
if ( (s->segmentType() & st) != 0)
2012-05-26 14:26:10 +02:00
return s;
}
if (s->tick() > tick)
return 0;
}
return 0;
}
//---------------------------------------------------------
// findSegment
2013-05-29 10:31:26 +02:00
/// Search for a segment of type \a st at position \a t.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
Segment* Measure::findSegment(Segment::Type st, int t)
2012-05-26 14:26:10 +02:00
{
Segment* s;
for (s = first(); s && s->tick() < t; s = s->next())
;
2013-06-25 19:52:00 +02:00
for (; s && s->tick() == t; s = s->next()) {
2014-08-21 15:01:54 +02:00
if (s->segmentType() & st)
return s;
}
return 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// undoGetSegment
//---------------------------------------------------------
Segment* Measure::undoGetSegment(Segment::Type type, int tick)
2012-05-26 14:26:10 +02:00
{
Segment* s = findSegment(type, tick);
if (s == 0) {
s = new Segment(this, type, tick);
score()->undoAddElement(s);
}
return s;
}
//---------------------------------------------------------
// getSegment
//---------------------------------------------------------
Segment* Measure::getSegment(Element* e, int tick)
{
2013-06-25 19:52:00 +02:00
return getSegment(Segment::segmentType(e->type()), tick);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// getSegment
2012-09-10 16:10:25 +02:00
/// Get a segment of type \a st at tick position \a t.
/// If the segment does not exist, it is created.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
Segment* Measure::getSegment(Segment::Type st, int tick)
2012-05-26 14:26:10 +02:00
{
Segment* s = findSegment(st, tick);
2012-05-26 14:26:10 +02:00
if (!s) {
s = new Segment(this, st, tick);
2012-05-26 14:26:10 +02:00
add(s);
}
return s;
}
//---------------------------------------------------------
// add
/// Add new Element \a el to Measure.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Measure::add(Element* el)
{
2012-08-08 20:46:29 +02:00
setDirty();
2012-05-26 14:26:10 +02:00
el->setParent(this);
Element::Type type = el->type();
2012-05-26 14:26:10 +02:00
// if (MScore::debugMode)
// qDebug("measure %p(%d): add %s %p", this, _no, el->name(), el);
switch (type) {
case Element::Type::TEXT:
{
if (el->staffIdx() < staves.size())
staves[el->staffIdx()]->setNoText(static_cast<Text*>(el));
}
2012-10-17 10:39:39 +02:00
break;
case Element::Type::SPACER:
2012-05-26 14:26:10 +02:00
{
Spacer* sp = static_cast<Spacer*>(el);
if (sp->spacerType() == SpacerType::UP)
2012-05-26 14:26:10 +02:00
staves[el->staffIdx()]->_vspacerUp = sp;
else if (sp->spacerType() == SpacerType::DOWN)
2012-05-26 14:26:10 +02:00
staves[el->staffIdx()]->_vspacerDown = sp;
}
break;
case Element::Type::SEGMENT:
2012-05-26 14:26:10 +02:00
{
Segment* seg = static_cast<Segment*>(el);
// insert segment at specific position
if (seg->next()) {
_segments.insert(seg, seg->next());
break;
}
2012-05-26 14:26:10 +02:00
int t = seg->tick();
Segment::Type st = seg->segmentType();
Segment* s;
2013-06-12 14:23:57 +02:00
for (s = first(); s && s->tick() < t; s = s->next())
;
if (s) {
if (st == Segment::Type::ChordRest) {
// add ChordRest segment after all other segments with same tick
// except EndBarLine
2013-06-12 14:23:57 +02:00
while (s && s->segmentType() != st && s->tick() == t) {
if (s->segmentType() == Segment::Type::EndBarLine) {
2013-06-12 14:23:57 +02:00
break;
}
2013-06-12 14:23:57 +02:00
s = s->next();
}
2013-06-12 14:23:57 +02:00
}
else {
// use order of segments in segment.h
if (s && s->tick() == t) {
while (s && s->segmentType() <= st) {
s = s->next();
if (s && s->tick() != t)
break;
}
2012-05-26 14:26:10 +02:00
}
}
}
seg->setParent(this);
_segments.insert(seg, s);
if ((seg->segmentType() == Segment::Type::TimeSig) && seg->element(0)) {
score()->addLayoutFlags(LayoutFlag::FIX_TICKS);
2012-05-26 14:26:10 +02:00
}
}
break;
case Element::Type::JUMP:
2014-07-17 09:32:30 +02:00
_repeatFlags = _repeatFlags | Repeat::JUMP;
// fall through
case Element::Type::MARKER:
2013-03-25 16:27:20 +01:00
_el.push_back(el);
2012-05-26 14:26:10 +02:00
break;
case Element::Type::HBOX:
2012-05-26 14:26:10 +02:00
if (el->staff())
el->setMag(el->staff()->mag()); // ?!
2013-03-25 16:27:20 +01:00
_el.push_back(el);
2012-05-26 14:26:10 +02:00
break;
case Element::Type::MEASURE:
2013-10-30 14:21:08 +01:00
_mmRest = static_cast<Measure*>(el);
break;
2012-05-26 14:26:10 +02:00
default:
MeasureBase::add(el);
break;
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// remove
2012-08-08 20:46:29 +02:00
/// Remove Element \a el from Measure.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Measure::remove(Element* el)
{
2014-08-25 19:30:56 +02:00
Q_ASSERT(el->parent() == this);
Q_ASSERT(el->score() == score());
2012-08-08 20:46:29 +02:00
setDirty();
2012-05-26 14:26:10 +02:00
switch(el->type()) {
case Element::Type::TEXT:
staves[el->staffIdx()]->setNoText(static_cast<Text*>(0));
2012-10-17 10:39:39 +02:00
break;
case Element::Type::SPACER:
if (static_cast<Spacer*>(el)->spacerType() == SpacerType::DOWN)
2012-05-26 14:26:10 +02:00
staves[el->staffIdx()]->_vspacerDown = 0;
else if (static_cast<Spacer*>(el)->spacerType() == SpacerType::UP)
2012-05-26 14:26:10 +02:00
staves[el->staffIdx()]->_vspacerUp = 0;
break;
case Element::Type::SEGMENT:
2012-05-26 14:26:10 +02:00
remove(static_cast<Segment*>(el));
break;
case Element::Type::JUMP:
2014-07-17 09:32:30 +02:00
resetRepeatFlag(Repeat::JUMP);
2012-05-26 14:26:10 +02:00
// fall through
case Element::Type::MARKER:
case Element::Type::HBOX:
2012-05-26 14:26:10 +02:00
if (!_el.remove(el)) {
qDebug("Measure(%p)::remove(%s,%p) not found",
this, el->name(), el);
}
break;
case Element::Type::CLEF:
case Element::Type::CHORD:
case Element::Type::REST:
case Element::Type::TIMESIG:
2012-05-26 14:26:10 +02:00
for (Segment* segment = first(); segment; segment = segment->next()) {
int staves = _score->nstaves();
int tracks = staves * VOICES;
for (int track = 0; track < tracks; ++track) {
Element* e = segment->element(track);
if (el == e) {
segment->setElement(track, 0);
return;
}
}
}
qDebug("Measure::remove: %s %p not found", el->name(), el);
break;
case Element::Type::MEASURE:
2013-10-30 14:21:08 +01:00
_mmRest = 0;
break;
2012-05-26 14:26:10 +02:00
default:
MeasureBase::remove(el);
break;
}
}
//---------------------------------------------------------
// change
//---------------------------------------------------------
void Measure::change(Element* o, Element* n)
{
if (o->type() == Element::Type::TUPLET) {
2012-05-26 14:26:10 +02:00
Tuplet* t = static_cast<Tuplet*>(n);
foreach(DurationElement* e, t->elements()) {
e->setTuplet(t);
}
}
else {
remove(o);
add(n);
}
}
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Measure::spatiumChanged(qreal /*oldValue*/, qreal /*newValue*/)
{
setDirty();
}
2012-05-26 14:26:10 +02:00
//-------------------------------------------------------------------
// moveTicks
// Also adjust endBarLine if measure len has changed. For this
// diff == 0 cannot be optimized away
//-------------------------------------------------------------------
void Measure::moveTicks(int diff)
{
setTick(tick() + diff);
for (Segment* segment = first(); segment; segment = segment->next()) {
if (segment->segmentType() & (Segment::Type::EndBarLine | Segment::Type::TimeSigAnnounce))
2012-05-26 14:26:10 +02:00
segment->setTick(tick() + ticks());
}
}
//---------------------------------------------------------
// 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);
}
}
foreach (Element* e, _el) {
2012-05-26 14:26:10 +02:00
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)
{
foreach (Element* e, _el) {
2012-05-26 14:26:10 +02:00
if (e->track() == -1)
continue;
int staffIdx = e->staffIdx();
if (staffIdx >= sStaff && !e->systemFlag()) {
2012-05-26 14:26:10 +02:00
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);
2013-07-15 07:53:16 +02:00
if (el) {
2014-05-21 20:08:37 +02:00
el->undoUnlink();
_score->undo(new RemoveElement(el));
2012-05-26 14:26:10 +02:00
}
}
2014-05-21 20:08:37 +02:00
foreach (Element* e, s->annotations()) {
2012-05-26 14:26:10 +02:00
int staffIdx = e->staffIdx();
if ((staffIdx >= sStaff) && (staffIdx < eStaff) && !e->systemFlag()) {
2014-05-21 20:08:37 +02:00
e->undoUnlink();
_score->undo(new RemoveElement(e));
2012-05-26 14:26:10 +02:00
}
}
}
2014-05-21 20:08:37 +02:00
foreach (Element* e, _el) {
2012-05-26 14:26:10 +02:00
if (e->track() == -1)
continue;
int staffIdx = e->staffIdx();
if (staffIdx >= sStaff && (staffIdx < eStaff) && !e->systemFlag()) {
2014-05-21 20:08:37 +02:00
e->undoUnlink();
_score->undo(new RemoveElement(e));
}
2012-05-26 14:26:10 +02:00
}
_score->undo(new RemoveStaves(this, sStaff, eStaff));
2014-08-25 19:30:56 +02:00
for (int i = eStaff - 1; i >= sStaff; --i) {
MStaff* ms = *(staves.begin()+i);
Text* t = ms->noText();
if (t) {
// t->undoUnlink();
// _score->undo(new RemoveElement(t));
}
_score->undo(new RemoveMStaff(this, ms, i));
}
2012-05-26 14:26:10 +02:00
// barLine
// TODO
}
//---------------------------------------------------------
// cmdAddStaves
//---------------------------------------------------------
void Measure::cmdAddStaves(int sStaff, int eStaff, bool createRest)
{
_score->undo(new InsertStaves(this, sStaff, eStaff));
Segment* ts = findSegment(Segment::Type::TimeSig, tick());
2012-05-26 14:26:10 +02:00
for (int i = sStaff; i < eStaff; ++i) {
Staff* staff = _score->staff(i);
MStaff* ms = new MStaff;
ms->lines = new StaffLines(score());
ms->lines->setTrack(i * VOICES);
// ms->lines->setLines(staff->lines());
ms->lines->setParent(this);
ms->lines->setVisible(!staff->invisible());
_score->undo(new InsertMStaff(this, ms, i));
2014-07-27 15:06:49 +02:00
}
2012-05-26 14:26:10 +02:00
2014-07-27 15:06:49 +02:00
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->linkedStaves()) {
bool alreadyInList = false;
for (int idx : sl) {
if (s->linkedStaves()->staves().contains(_score->staff(idx))) {
alreadyInList = true;
break;
}
}
2014-07-27 15:06:49 +02:00
if (alreadyInList)
continue;
2012-05-26 14:26:10 +02:00
}
2014-07-27 15:06:49 +02:00
sl.append(staffIdx);
}
for (int staffIdx : sl) {
if (createRest)
score()->setRest(tick(), staffIdx * VOICES, len(), false, 0, _timesig == len());
2012-05-26 14:26:10 +02:00
// replicate time signature
if (ts) {
TimeSig* ots = 0;
for (int track = 0; track < staves.size() * VOICES; ++track) {
if (ts->element(track)) {
2014-07-27 15:06:49 +02:00
ots = static_cast<TimeSig*>(ts->element(track));
2012-05-26 14:26:10 +02:00
break;
}
}
// do no replicate local time signatures
if (ots && !ots->isLocal()) {
2012-05-26 14:26:10 +02:00
TimeSig* timesig = new TimeSig(*ots);
2014-07-27 15:06:49 +02:00
timesig->setTrack(staffIdx * VOICES);
timesig->setParent(ts);
timesig->setSig(ots->sig(), ots->timeSigType());
timesig->setNeedLayout(true);
2012-05-26 14:26:10 +02:00
score()->undoAddElement(timesig);
}
}
}
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void MStaff::setTrack(int track)
{
if (lines)
lines->setTrack(track);
if (_vspacerUp)
_vspacerUp->setTrack(track);
if (_vspacerDown)
_vspacerDown->setTrack(track);
}
//---------------------------------------------------------
// insertMStaff
//---------------------------------------------------------
void Measure::insertMStaff(MStaff* staff, int idx)
{
staves.insert(idx, staff);
for (int staffIdx = 0; staffIdx < staves.size(); ++staffIdx)
staves[staffIdx]->setTrack(staffIdx * VOICES);
}
//---------------------------------------------------------
// removeMStaff
//---------------------------------------------------------
void Measure::removeMStaff(MStaff* /*staff*/, int idx)
{
staves.removeAt(idx);
for (int staffIdx = 0; staffIdx < staves.size(); ++staffIdx)
staves[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->lines = new StaffLines(score());
ms->lines->setParent(this);
ms->lines->setTrack(staffIdx * VOICES);
// ms->distanceUp = 0.0;
2014-05-26 15:31:36 +02:00
// ms->distanceDown = 0.0; // TODO point(staffIdx == 0 ? score()->styleS(StyleIdx::minSystemDistance) : score()->styleS(StyleIdx::staffDistance));
2012-05-26 14:26:10 +02:00
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
*/
2014-08-13 21:01:21 +02:00
bool Measure::acceptDrop(const DropData& data) const
2012-05-26 14:26:10 +02:00
{
2014-08-13 21:01:21 +02:00
MuseScoreView* viewer = data.view;
QPointF pos = data.pos;
Element* e = data.element;
int staffIdx;
Segment* seg;
2014-10-04 18:22:36 +02:00
if (_score->pos2measure(pos, &staffIdx, 0, &seg, 0) == nullptr)
return false;
2014-08-13 21:01:21 +02:00
QRectF staffR = system()->staff(staffIdx)->bbox().translated(system()->canvasPos());
staffR &= canvasBoundingRect();
2014-06-19 15:27:44 +02:00
switch (e->type()) {
case Element::Type::MEASURE_LIST:
case Element::Type::JUMP:
case Element::Type::MARKER:
case Element::Type::LAYOUT_BREAK:
case Element::Type::STAFF_LIST:
2014-08-13 21:01:21 +02:00
viewer->setDropRectangle(canvasBoundingRect());
return true;
case Element::Type::KEYSIG:
2014-08-13 21:01:21 +02:00
case Element::Type::TIMESIG:
if (data.modifiers & Qt::ControlModifier)
viewer->setDropRectangle(staffR);
else
viewer->setDropRectangle(canvasBoundingRect());
return true;
case Element::Type::BRACKET:
case Element::Type::REPEAT_MEASURE:
case Element::Type::MEASURE:
case Element::Type::SPACER:
case Element::Type::IMAGE:
case Element::Type::BAR_LINE:
case Element::Type::SYMBOL:
case Element::Type::CLEF:
2014-08-13 21:01:21 +02:00
viewer->setDropRectangle(staffR);
2012-05-26 14:26:10 +02:00
return true;
case Element::Type::ICON:
switch(static_cast<Icon*>(e)->iconType()) {
case IconType::VFRAME:
case IconType::HFRAME:
case IconType::TFRAME:
case IconType::FFRAME:
case IconType::MEASURE:
2014-06-19 15:27:44 +02:00
viewer->setDropRectangle(canvasBoundingRect());
2012-05-26 14:26:10 +02:00
return true;
default:
break;
2012-05-26 14:26:10 +02:00
}
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(const DropData& data)
{
Element* e = data.element;
int staffIdx = -1;
2012-05-26 14:26:10 +02:00
Segment* seg;
_score->pos2measure(data.pos, &staffIdx, 0, &seg, 0);
if (e->systemFlag())
staffIdx = 0;
if (staffIdx < 0)
return 0;
2014-06-04 13:48:10 +02:00
#if 0 // yet(?) unused
2012-05-26 14:26:10 +02:00
QPointF mrp(data.pos - pagePos());
2014-06-04 13:48:10 +02:00
#endif
2012-05-26 14:26:10 +02:00
Staff* staff = score()->staff(staffIdx);
switch(e->type()) {
case Element::Type::MEASURE_LIST:
2012-05-26 14:26:10 +02:00
qDebug("drop measureList or StaffList");
delete e;
break;
case Element::Type::STAFF_LIST:
2012-05-26 14:26:10 +02:00
qDebug("drop staffList");
//TODO score()->pasteStaff(e, this, staffIdx);
delete e;
break;
case Element::Type::MARKER:
case Element::Type::JUMP:
e->setParent(this);
2012-05-26 14:26:10 +02:00
e->setTrack(0);
score()->undoAddElement(e);
return e;
case Element::Type::DYNAMIC:
case Element::Type::FRET_DIAGRAM:
2012-05-26 14:26:10 +02:00
e->setParent(seg);
e->setTrack(staffIdx * VOICES);
score()->undoAddElement(e);
return e;
case Element::Type::IMAGE:
case Element::Type::SYMBOL:
2012-05-26 14:26:10 +02:00
e->setParent(seg);
e->setTrack(staffIdx * VOICES);
e->layout();
{
QPointF uo(data.pos - e->canvasPos() - data.dragOffset);
e->setUserOff(uo);
}
score()->undoAddElement(e);
return e;
case Element::Type::BRACKET:
2013-06-28 17:46:24 +02:00
{
Bracket* b = static_cast<Bracket*>(e);
int level = 0;
int firstStaff = 0;
foreach (Staff* s, score()->staves()) {
foreach (const BracketItem& bi, s->brackets()) {
int lastStaff = firstStaff + bi._bracketSpan - 1;
if (staffIdx >= firstStaff && staffIdx <= lastStaff)
++level;
}
firstStaff++;
2013-06-28 17:46:24 +02:00
}
score()->undoAddBracket(staff, level, b->bracketType(), 1);
delete b;
}
return 0;
2012-05-26 14:26:10 +02:00
case Element::Type::CLEF:
2012-05-26 14:26:10 +02:00
score()->undoChangeClef(staff, first(), static_cast<Clef*>(e)->clefType());
delete e;
break;
case Element::Type::KEYSIG:
2012-05-26 14:26:10 +02:00
{
KeySig* ks = static_cast<KeySig*>(e);
KeySigEvent k = ks->keySigEvent();
delete ks;
2014-06-03 15:28:10 +02:00
2012-05-26 14:26:10 +02:00
if (data.modifiers & Qt::ControlModifier) {
// apply only to this stave
score()->undoChangeKeySig(staff, tick(), k);
}
else {
2012-05-26 14:26:10 +02:00
// apply to all staves:
foreach(Staff* s, score()->staves())
score()->undoChangeKeySig(s, tick(), k);
2012-05-26 14:26:10 +02:00
}
2012-08-10 10:14:17 +02:00
2012-05-26 14:26:10 +02:00
break;
}
case Element::Type::TIMESIG:
score()->cmdAddTimeSig(this, staffIdx, static_cast<TimeSig*>(e),
data.modifiers & Qt::ControlModifier);
2012-05-26 14:26:10 +02:00
return 0;
case Element::Type::LAYOUT_BREAK:
2012-05-26 14:26:10 +02:00
{
LayoutBreak* lb = static_cast<LayoutBreak*>(e);
if (
(lb->layoutBreakType() == LayoutBreak::Type::PAGE && _pageBreak)
|| (lb->layoutBreakType() == LayoutBreak::Type::LINE && _lineBreak)
|| (lb->layoutBreakType() == LayoutBreak::Type::SECTION && _sectionBreak)
2012-05-26 14:26:10 +02:00
) {
//
// if break already set
//
delete lb;
break;
}
// make sure there is only LayoutBreak::Type::LINE or LayoutBreak::Type::PAGE
if ((lb->layoutBreakType() != LayoutBreak::Type::SECTION) && (_pageBreak || _lineBreak)) {
2012-05-26 14:26:10 +02:00
foreach(Element* le, _el) {
if (le->type() == Element::Type::LAYOUT_BREAK
&& (static_cast<LayoutBreak*>(le)->layoutBreakType() == LayoutBreak::Type::LINE
|| static_cast<LayoutBreak*>(le)->layoutBreakType() == LayoutBreak::Type::PAGE)) {
2012-05-26 14:26:10 +02:00
score()->undoChangeElement(le, e);
break;
}
}
break;
}
lb->setTrack(-1); // these are system elements
2012-05-26 14:26:10 +02:00
lb->setParent(this);
score()->undoAddElement(lb);
return lb;
}
case Element::Type::SPACER:
2012-05-26 14:26:10 +02:00
{
Spacer* spacer = static_cast<Spacer*>(e);
spacer->setTrack(staffIdx * VOICES);
spacer->setParent(this);
score()->undoAddElement(spacer);
return spacer;
}
case Element::Type::BAR_LINE:
{
BarLine* bl = static_cast<BarLine*>(e);
// if dropped bar line refers to span rather than to subtype
// or if Ctrl key used
if ((bl->spanFrom() != 0 && bl->spanTo() != DEFAULT_BARLINE_TO) || (data.modifiers & Qt::ControlModifier)) {
// get existing bar line for this staff, and drop the change to it
Segment* seg = undoGetSegment(Segment::Type::EndBarLine, tick() + ticks());
BarLine* cbl = static_cast<BarLine*>(seg->element(staffIdx * VOICES));
if (cbl)
cbl->drop(data);
}
// if dropped bar line refers to line subtype
else {
score()->undoChangeBarLine(this, bl->barLineType());
delete e;
}
2012-05-26 14:26:10 +02:00
break;
}
2012-05-26 14:26:10 +02:00
case Element::Type::REPEAT_MEASURE:
2012-05-26 14:26:10 +02:00
{
delete e;
return cmdInsertRepeatMeasure(staffIdx);
2012-05-26 14:26:10 +02:00
}
case Element::Type::ICON:
switch(static_cast<Icon*>(e)->iconType()) {
case IconType::VFRAME:
score()->insertMeasure(Element::Type::VBOX, this);
2012-05-26 14:26:10 +02:00
break;
case IconType::HFRAME:
score()->insertMeasure(Element::Type::HBOX, this);
2012-05-26 14:26:10 +02:00
break;
case IconType::TFRAME:
score()->insertMeasure(Element::Type::TBOX, this);
2012-05-26 14:26:10 +02:00
break;
case IconType::FFRAME:
score()->insertMeasure(Element::Type::FBOX, this);
2012-05-26 14:26:10 +02:00
break;
case IconType::MEASURE:
score()->insertMeasure(Element::Type::MEASURE, this);
2012-05-26 14:26:10 +02:00
break;
default:
break;
2012-05-26 14:26:10 +02:00
}
break;
default:
qDebug("Measure: cannot drop %s here", e->name());
delete e;
break;
}
return 0;
}
//---------------------------------------------------------
// cmdInsertRepeatMeasure
//---------------------------------------------------------
2014-05-20 17:26:26 +02:00
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() & Segment::Type::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(Segment::Type::ChordRest, tick());
RepeatMeasure* rm = new RepeatMeasure(_score);
rm->setTrack(staffIdx * VOICES);
rm->setParent(seg);
rm->setDurationType(TDuration::DurationType::V_MEASURE);
rm->setDuration(stretchedLen(_score->staff(staffIdx)));
2014-05-29 14:04:14 +02:00
_score->undoAddCR(rm, this, tick());
2014-05-20 17:26:26 +02:00
foreach (Element* el, _el) {
if (el->type() == Element::Type::SLUR && el->staffIdx() == staffIdx)
_score->undoRemoveElement(el);
}
return rm;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// adjustToLen
// change actual measure len, adjust elements to
// new len
//---------------------------------------------------------
void Measure::adjustToLen(Fraction nf)
{
2014-05-20 17:26:26 +02:00
int ol = len().ticks();
int nl = nf.ticks();
int diff = nl - ol;
int startTick = endTick();
if (diff < 0)
startTick += diff;
2013-10-21 13:11:04 +02:00
2014-05-20 17:26:26 +02:00
score()->undoInsertTime(startTick, diff);
2014-07-27 12:38:45 +02:00
score()->undo(new InsertTime(score(), startTick, diff));
2012-05-26 14:26:10 +02:00
2014-05-20 17:26:26 +02:00
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* s = m->first(); s; s = s->next()) {
if (s->segmentType() & (Segment::Type::EndBarLine|Segment::Type::TimeSigAnnounce|Segment::Type::KeySigAnnounce)) {
2014-05-20 17:26:26 +02:00
s->setTick(tick() + nl);
}
2012-05-26 14:26:10 +02:00
}
}
}
2014-05-22 16:18:35 +02:00
Score* s = score()->rootScore();
Measure* m = this;
QList<int> sl = s->uniqueStaves();
2012-05-26 14:26:10 +02:00
2014-05-22 16:18:35 +02:00
for (int staffIdx : sl) {
2012-05-26 14:26:10 +02:00
int rests = 0;
int chords = 0;
Rest* rest = 0;
2014-05-20 17:26:26 +02:00
for (Segment* segment = m->first(); segment; segment = segment->next()) {
2012-05-26 14:26:10 +02:00
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (int track = strack; track < etrack; ++track) {
Element* e = segment->element(track);
2013-10-21 13:11:04 +02:00
if (e) {
if (e->type() == Element::Type::REST) {
2013-10-21 13:11:04 +02:00
++rests;
rest = static_cast<Rest*>(e);
}
else if (e->type() == Element::Type::CHORD)
2013-10-21 13:11:04 +02:00
++chords;
2012-05-26 14:26:10 +02:00
}
}
}
// if just a single rest
2013-10-21 13:11:04 +02:00
if (rests == 1 && chords == 0) {
// if measure value didn't change, stick to whole measure rest
if (_timesig == nf) {
rest->undoChangeProperty(P_ID::DURATION, QVariant::fromValue<Fraction>(nf));
rest->undoChangeProperty(P_ID::DURATION_TYPE, QVariant::fromValue<TDuration>(TDuration::DurationType::V_MEASURE));
}
2014-05-20 17:26:26 +02:00
else { // if measure value did change, represent with rests actual measure value
// convert the measure duration in a list of values (no dots for rests)
QList<TDuration> durList = toDurationList(nf, false, 0);
2014-05-20 17:26:26 +02:00
// set the existing rest to the first value of the duration list
for (ScoreElement* e : rest->linkList()) {
e->undoChangeProperty(P_ID::DURATION, QVariant::fromValue<Fraction>(durList[0].fraction()));
e->undoChangeProperty(P_ID::DURATION_TYPE, QVariant::fromValue<TDuration>(durList[0]));
2014-09-29 11:58:44 +02:00
}
2014-05-20 17:26:26 +02:00
// add rests for any other duration list value
int tickOffset = tick() + durList[0].ticks();
for (int i = 1; i < durList.count(); i++) {
2014-05-20 17:26:26 +02:00
Rest* newRest = new Rest(s);
newRest->setDurationType(durList.at(i));
newRest->setDuration(durList.at(i).fraction());
newRest->setTrack(rest->track());
score()->undoAddCR(newRest, this, tickOffset);
tickOffset += durList.at(i).ticks();
}
}
2014-05-20 17:26:26 +02:00
continue;
2012-05-26 14:26:10 +02:00
}
2013-10-21 13:11:04 +02:00
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (int trk = strack; trk < etrack; ++trk) {
int n = diff;
bool rFlag = false;
if (n < 0) {
2014-05-20 17:26:26 +02:00
for (Segment* segment = m->last(); segment;) {
2013-10-21 13:11:04 +02:00
Segment* pseg = segment->prev();
Element* e = segment->element(trk);
if (e && e->isChordRest()) {
ChordRest* cr = static_cast<ChordRest*>(e);
if (cr->durationType() == TDuration::DurationType::V_MEASURE) {
int actualTicks = cr->actualTicks();
n += actualTicks;
cr->setDurationType(TDuration(actualTicks));
}
2013-10-21 13:11:04 +02:00
else
n += cr->actualTicks();
2014-05-20 17:26:26 +02:00
s->undoRemoveElement(e);
2013-10-21 13:11:04 +02:00
if (n >= 0)
break;
2012-05-26 14:26:10 +02:00
}
2013-10-21 13:11:04 +02:00
segment = pseg;
2012-05-26 14:26:10 +02:00
}
2013-10-21 13:11:04 +02:00
rFlag = true;
}
int voice = trk % VOICES;
if ((n > 0) && (rFlag || voice == 0)) {
// add rest to measure
int rtick = tick() + nl - n;
int track = staffIdx * VOICES + voice;
2014-05-20 17:26:26 +02:00
s->setRest(rtick, track, Fraction::fromTicks(n), false, 0, false);
2012-05-26 14:26:10 +02:00
}
}
}
if (diff < 0) {
//
// CHECK: do not remove all slurs
//
foreach(Element* e, m->el()) {
if (e->type() == Element::Type::SLUR)
2014-05-20 17:26:26 +02:00
s->undoRemoveElement(e);
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Measure::write(Xml& xml, int staff, bool writeSystemElements) const
{
int mno = _no + 1;
if (_len != _timesig) {
// this is an irregular measure
xml.stag(QString("Measure number=\"%1\" len=\"%2/%3\"").arg(mno).arg(_len.numerator()).arg(_len.denominator()));
}
else
xml.stag(QString("Measure number=\"%1\"").arg(mno));
2014-02-28 14:25:47 +01:00
2012-05-26 14:26:10 +02:00
xml.curTick = tick();
2014-02-26 19:06:42 +01:00
if (_mmRestCount > 0)
xml.tag("multiMeasureRest", _mmRestCount);
2012-05-26 14:26:10 +02:00
if (writeSystemElements) {
if (_repeatFlags & Repeat::START)
2012-05-26 14:26:10 +02:00
xml.tagE("startRepeat");
if (_repeatFlags & Repeat::END)
2012-05-26 14:26:10 +02:00
xml.tag("endRepeat", _repeatCount);
writeProperty(xml, P_ID::IRREGULAR);
writeProperty(xml, P_ID::BREAK_MMR);
writeProperty(xml, P_ID::USER_STRETCH);
writeProperty(xml, P_ID::NO_OFFSET);
writeProperty(xml, P_ID::MEASURE_NUMBER_MODE);
writeProperty(xml, P_ID::SYSTEM_INITIAL_BARLINE_TYPE);
2012-05-26 14:26:10 +02:00
}
qreal _spatium = spatium();
MStaff* mstaff = staves[staff];
if (mstaff->noText() && !mstaff->noText()->generated()) {
xml.stag("MeasureNumber");
mstaff->noText()->writeProperties(xml);
xml.etag();
}
2012-05-26 14:26:10 +02:00
if (mstaff->_vspacerUp)
xml.tag("vspacerUp", mstaff->_vspacerUp->gap() / _spatium);
if (mstaff->_vspacerDown)
xml.tag("vspacerDown", mstaff->_vspacerDown->gap() / _spatium);
if (!mstaff->_visible)
xml.tag("visible", mstaff->_visible);
if (mstaff->_slashStyle)
xml.tag("slashStyle", mstaff->_slashStyle);
int strack = staff * VOICES;
int etrack = strack + VOICES;
foreach (const Element* el, _el) {
2015-07-25 08:43:02 +02:00
if (!el->generated() && ((el->staffIdx() == staff) || (el->systemFlag() && writeSystemElements)))
2012-05-26 14:26:10 +02:00
el->write(xml);
}
Q_ASSERT(first());
Q_ASSERT(last());
2013-10-13 13:06:32 +02:00
score()->writeSegments(xml, strack, etrack, first(), last()->next1(), writeSystemElements, false, false);
2012-05-26 14:26:10 +02:00
xml.etag();
}
2014-02-26 19:06:42 +01:00
//---------------------------------------------------------
// ticks
//---------------------------------------------------------
int Measure::ticks() const
{
return _len.ticks();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Measure::read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Measure::read(XmlReader& e, int staffIdx)
2012-05-26 14:26:10 +02:00
{
Segment* segment = 0;
qreal _spatium = spatium();
2013-06-16 23:33:37 +02:00
QList<Chord*> graceNotes;
//sort tuplet elements. needed for nested tuplets #22537
if (score()->mscVersion() <= 114) {
2014-04-23 20:59:32 +02:00
for (Tuplet* t : e.tuplets()) {
t->sortElements();
}
}
2013-01-23 14:14:09 +01:00
e.tuplets().clear();
2013-06-10 21:13:04 +02:00
e.setTrack(staffIdx * VOICES);
2013-01-23 14:14:09 +01:00
2012-05-26 14:26:10 +02:00
for (int n = staves.size(); n <= staffIdx; ++n) {
Staff* staff = score()->staff(n);
2012-09-14 10:09:06 +02:00
MStaff* s = new MStaff;
2012-05-26 14:26:10 +02:00
s->lines = new StaffLines(score());
s->lines->setParent(this);
s->lines->setTrack(n * VOICES);
s->lines->setVisible(!staff->invisible());
staves.append(s);
}
// tick is obsolete
2013-01-11 18:10:18 +01:00
if (e.hasAttribute("tick"))
2014-07-21 14:16:16 +02:00
e.initTick(score()->fileDivision(e.intAttribute("tick")));
2012-05-26 14:26:10 +02:00
2013-06-04 18:29:14 +02:00
bool irregular;
2013-01-11 18:10:18 +01:00
if (e.hasAttribute("len")) {
QStringList sl = e.attribute("len").split('/');
2012-05-26 14:26:10 +02:00
if (sl.size() == 2)
_len = Fraction(sl[0].toInt(), sl[1].toInt());
else
2013-01-11 18:10:18 +01:00
qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
2012-05-26 14:26:10 +02:00
irregular = true;
score()->sigmap()->add(tick(), SigEvent(_len, _timesig));
score()->sigmap()->add(tick() + ticks(), SigEvent(_timesig));
}
2013-06-04 18:29:14 +02:00
else
irregular = false;
2013-06-04 18:29:14 +02:00
2012-05-26 14:26:10 +02:00
Staff* staff = score()->staff(staffIdx);
2012-06-28 15:12:17 +02:00
Fraction timeStretch(staff->timeStretch(tick()));
2012-05-26 14:26:10 +02:00
// keep track of tick of previous element
// this allows markings that need to apply to previous element to do so
// even though we may have already advanced to next tick position
int lastTick = e.tick();
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "tick") {
e.initTick(score()->fileDivision(e.readInt()));
lastTick = e.tick();
}
2012-05-26 14:26:10 +02:00
else if (tag == "BarLine") {
BarLine* barLine = new BarLine(score());
2013-01-21 20:21:41 +01:00
barLine->setTrack(e.track());
2012-05-26 14:26:10 +02:00
barLine->read(e);
Segment::Type st;
//
// SegStartRepeatBarLine: always at the beginning tick of a measure
// SegBarLine: in the middle of a measure, has no semantic
// SegEndBarLine: at the end tick of a measure
2014-10-10 14:24:37 +02:00
if ((e.tick() != tick()) && (e.tick() != endTick()))
st = Segment::Type::BarLine;
else if (barLine->barLineType() == BarLineType::START_REPEAT && e.tick() == tick())
st = Segment::Type::StartRepeatBarLine;
2014-10-10 14:24:37 +02:00
else
st = Segment::Type::EndBarLine;
segment = getSegment(st, e.tick()); // let the bar line know it belongs to a Segment,
segment->add(barLine); // before setting its flags
if (st == Segment::Type::EndBarLine) {
if (!barLine->customSubtype()) {
BarLineType blt = barLine->barLineType();
// Measure::_endBarLineGenerated is true if the bar line is of a type which can
// be reconstructed from measure flags
bool endBarLineGenerated = (blt == BarLineType::NORMAL || blt == BarLineType::END_REPEAT
|| blt == BarLineType::END_START_REPEAT || blt == BarLineType::START_REPEAT);
setEndBarLineType(blt, endBarLineGenerated, true);
}
if (!barLine->customSpan()) {
Staff* staff = score()->staff(staffIdx);
barLine->setSpan(staff->barLineSpan());
barLine->setSpanFrom(staff->barLineFrom());
barLine->setSpanTo(staff->barLineTo());
}
2012-05-26 14:26:10 +02:00
}
}
else if (tag == "Chord") {
2013-06-16 23:33:37 +02:00
Chord* chord = new Chord(score());
chord->setTrack(e.track());
chord->read(e);
segment = getSegment(Segment::Type::ChordRest, e.tick());
2014-11-28 22:35:25 +01:00
if (chord->noteType() != NoteType::NORMAL) {
graceNotes.push_back(chord);
if (chord->tremolo() && chord->tremolo()->tremoloType() < TremoloType::R8) {
// old style tremolo found
Tremolo* tremolo = chord->tremolo();
TremoloType st;
switch (tremolo->tremoloType()) {
default:
case TremoloType::OLD_R8: st = TremoloType::R8; break;
case TremoloType::OLD_R16: st = TremoloType::R16; break;
case TremoloType::OLD_R32: st = TremoloType::R32; break;
case TremoloType::OLD_C8: st = TremoloType::C8; break;
case TremoloType::OLD_C16: st = TremoloType::C16; break;
case TremoloType::OLD_C32: st = TremoloType::C32; break;
}
tremolo->setTremoloType(st);
}
}
2012-05-26 14:26:10 +02:00
else {
2013-06-16 23:33:37 +02:00
segment->add(chord);
Q_ASSERT(segment->segmentType() == Segment::Type::ChordRest);
2013-06-24 13:46:21 +02:00
2013-06-16 23:33:37 +02:00
for (int i = 0; i < graceNotes.size(); ++i) {
Chord* gc = graceNotes[i];
gc->setGraceIndex(i);
chord->add(gc);
}
graceNotes.clear();
2014-07-28 15:54:47 +02:00
int crticks = chord->actualTicks();
2013-06-10 21:13:04 +02:00
if (chord->tremolo() && chord->tremolo()->tremoloType() < TremoloType::R8) {
2013-06-10 21:13:04 +02:00
// old style tremolo found
2014-11-28 22:35:25 +01:00
2013-06-10 21:13:04 +02:00
Tremolo* tremolo = chord->tremolo();
2014-11-28 22:35:25 +01:00
TremoloType st;
switch (tremolo->tremoloType()) {
default:
case TremoloType::OLD_R8: st = TremoloType::R8; break;
case TremoloType::OLD_R16: st = TremoloType::R16; break;
case TremoloType::OLD_R32: st = TremoloType::R32; break;
case TremoloType::OLD_C8: st = TremoloType::C8; break;
case TremoloType::OLD_C16: st = TremoloType::C16; break;
case TremoloType::OLD_C32: st = TremoloType::C32; break;
}
tremolo->setTremoloType(st);
2013-06-10 21:13:04 +02:00
if (tremolo->twoNotes()) {
int track = chord->track();
Segment* ss = 0;
for (Segment* ps = first(Segment::Type::ChordRest); ps; ps = ps->next(Segment::Type::ChordRest)) {
2013-06-10 21:13:04 +02:00
if (ps->tick() >= e.tick())
break;
if (ps->element(track))
ss = ps;
}
Chord* pch = 0; // previous chord
if (ss) {
ChordRest* cr = static_cast<ChordRest*>(ss->element(track));
if (cr && cr->type() == Element::Type::CHORD)
2013-06-10 21:13:04 +02:00
pch = static_cast<Chord*>(cr);
}
if (pch) {
tremolo->setParent(pch);
pch->setTremolo(tremolo);
chord->setTremolo(0);
// force duration to half
2013-06-27 10:03:36 +02:00
Fraction pts(timeStretch * pch->globalDuration());
int pcrticks = pts.ticks();
pch->setDuration(Fraction::fromTicks(pcrticks / 2));
chord->setDuration(Fraction::fromTicks(crticks / 2));
2013-06-10 21:13:04 +02:00
}
else {
qDebug("tremolo: first note not found");
}
crticks /= 2;
}
2014-11-28 22:35:25 +01:00
else {
tremolo->setParent(chord);
}
2013-06-10 21:13:04 +02:00
}
lastTick = e.tick();
2014-07-21 14:16:16 +02:00
e.incTick(crticks);
2012-05-26 14:26:10 +02:00
}
}
else if (tag == "Rest") {
Rest* rest = new Rest(score());
rest->setDurationType(TDuration::DurationType::V_MEASURE);
2012-05-26 14:26:10 +02:00
rest->setDuration(timesig()/timeStretch);
2013-01-21 20:21:41 +01:00
rest->setTrack(e.track());
rest->read(e);
2013-01-21 20:21:41 +01:00
segment = getSegment(rest, e.tick());
2012-05-26 14:26:10 +02:00
segment->add(rest);
2014-07-28 15:54:47 +02:00
2012-05-26 14:26:10 +02:00
if (!rest->duration().isValid()) // hack
rest->setDuration(timesig()/timeStretch);
lastTick = e.tick();
2014-07-28 15:54:47 +02:00
e.incTick(rest->actualTicks());
2012-05-26 14:26:10 +02:00
}
else if (tag == "Breath") {
Breath* breath = new Breath(score());
2013-01-21 20:21:41 +01:00
breath->setTrack(e.track());
int tick = e.tick();
2012-05-26 14:26:10 +02:00
breath->read(e);
if (score()->mscVersion() < 205) {
// older scores placed the breath segment right after the chord to which it applies
// rather than before the next chordrest segment with an element for the staff
// result would be layout too far left if there are other segments due to notes in other staves
// we need to find tick of chord to which this applies, and add its duration
int prevTick;
if (e.tick() < tick)
prevTick = e.tick(); // use our own tick if we explicitly reset to earlier position
else
prevTick = lastTick; // otherwise use tick of previous tick/chord/rest tag
// find segment
Segment* prev = findSegment(Segment::Type::ChordRest, prevTick);
if (prev) {
// find chordrest
ChordRest* lastCR = static_cast<ChordRest*>(prev->element(e.track()));
if (lastCR)
tick = prevTick + lastCR->actualTicks();
}
}
segment = getSegment(Segment::Type::Breath, tick);
2012-05-26 14:26:10 +02:00
segment->add(breath);
}
else if (tag == "endSpanner") {
int id = e.attribute("id").toInt();
2014-07-21 13:24:21 +02:00
Spanner* spanner = e.findSpanner(id);
2013-01-11 18:10:18 +01:00
if (spanner) {
2014-08-13 15:42:40 +02:00
spanner->setTicks(e.tick() - spanner->tick());
// if (spanner->track2() == -1)
// the absence of a track tag [?] means the
// track is the same as the beginning of the slur
2014-03-15 12:57:36 +01:00
if (spanner->track2() == -1)
spanner->setTrack2(spanner->track() ? spanner->track() : e.track());
2012-05-26 14:26:10 +02:00
}
2013-10-13 13:06:32 +02:00
else {
// remember "endSpanner" values
SpannerValues sv;
sv.spannerId = id;
sv.track2 = e.track();
sv.tick2 = e.tick();
e.addSpannerValues(sv);
}
2013-01-11 18:10:18 +01:00
e.readNext();
2012-05-26 14:26:10 +02:00
}
else if (tag == "Slur") {
Slur *sl = new Slur(score());
sl->setTick(e.tick());
sl->read(e);
//
// check if we already saw "endSpanner"
//
2014-07-21 13:24:21 +02:00
int id = e.spannerId(sl);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sl->setTick2(sv->tick2);
2015-03-03 15:45:25 +01:00
sl->setTrack2(sv->track2);
}
2015-03-03 15:45:25 +01:00
score()->addSpanner(sl);
}
2012-05-26 14:26:10 +02:00
else if (tag == "HairPin"
|| tag == "Pedal"
|| tag == "Ottava"
|| tag == "Trill"
|| tag == "TextLine"
|| tag == "Volta") {
2013-01-18 10:55:52 +01:00
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, score()));
2013-06-10 11:03:34 +02:00
sp->setTrack(e.track());
sp->setTick(e.tick());
2014-06-18 20:57:45 +02:00
// ?? sp->setAnchor(Spanner::Anchor::SEGMENT);
2012-05-26 14:26:10 +02:00
sp->read(e);
2013-06-20 13:57:15 +02:00
score()->addSpanner(sp);
2013-10-13 13:06:32 +02:00
//
// check if we already saw "endSpanner"
//
2014-07-21 13:24:21 +02:00
int id = e.spannerId(sp);
const SpannerValues* sv = e.spannerValues(id);
2013-10-13 13:06:32 +02:00
if (sv) {
2014-08-13 15:42:40 +02:00
sp->setTicks(sv->tick2 - sp->tick());
2013-10-13 13:06:32 +02:00
sp->setTrack2(sv->track2);
}
2012-05-26 14:26:10 +02:00
}
else if (tag == "RepeatMeasure") {
RepeatMeasure* rm = new RepeatMeasure(score());
2013-01-21 20:21:41 +01:00
rm->setTrack(e.track());
rm->read(e);
segment = getSegment(Segment::Type::ChordRest, e.tick());
2012-05-26 14:26:10 +02:00
segment->add(rm);
if (rm->actualDuration().isZero()) { // might happen with 1.3 scores
rm->setDuration(len());
}
lastTick = e.tick();
2014-07-21 14:16:16 +02:00
e.incTick(ticks());
2012-05-26 14:26:10 +02:00
}
else if (tag == "Clef") {
Clef* clef = new Clef(score());
2013-01-21 20:21:41 +01:00
clef->setTrack(e.track());
2012-05-26 14:26:10 +02:00
clef->read(e);
clef->setGenerated(false);
// in some 1.3 scores, clefs can be in score but not in cleflist
2014-07-25 17:13:27 +02:00
// if (score()->mscVersion() > 114)
// staff->setClef(e.tick(), clef->clefTypeList());
2013-07-12 18:41:33 +02:00
// there may be more than one clef segment for same tick position
2014-05-05 17:56:13 +02:00
if (!segment) {
// this is the first segment of measure
segment = getSegment(Segment::Type::Clef, e.tick());
2014-05-05 17:56:13 +02:00
}
2013-07-12 18:41:33 +02:00
else {
2014-05-05 17:56:13 +02:00
bool firstSegment = false;
// the first clef may be missing and is added later in layout
2014-05-05 17:56:13 +02:00
for (Segment* s = _segments.first(); s && s->tick() == e.tick(); s = s->next()) {
if (s->segmentType() == Segment::Type::Clef
// hack: there may be other segment types which should
// generate a clef at current position
|| s->segmentType() == Segment::Type::StartRepeatBarLine
) {
2014-05-05 17:56:13 +02:00
firstSegment = true;
2013-07-12 18:41:33 +02:00
break;
}
}
2014-05-05 17:56:13 +02:00
if (firstSegment) {
Segment* ns = 0;
if (segment->next()) {
ns = segment->next();
while (ns && ns->tick() < e.tick())
ns = ns->next();
}
segment = 0;
for (Segment* s = ns; s && s->tick() == e.tick(); s = s->next()) {
if (s->segmentType() == Segment::Type::Clef) {
2014-05-05 17:56:13 +02:00
segment = s;
break;
}
}
if (!segment) {
segment = new Segment(this, Segment::Type::Clef, e.tick());
2014-05-05 17:56:13 +02:00
_segments.insert(segment, ns);
}
}
else {
// this is the first clef: move to left
segment = getSegment(Segment::Type::Clef, e.tick());
2013-07-12 18:41:33 +02:00
}
2012-05-26 14:26:10 +02:00
}
2014-10-28 11:47:04 +01:00
if (e.tick() != tick())
clef->setSmall(true); // layout does this ?
2012-05-26 14:26:10 +02:00
segment->add(clef);
}
else if (tag == "TimeSig") {
TimeSig* ts = new TimeSig(score());
2013-01-21 20:21:41 +01:00
ts->setTrack(e.track());
2012-10-24 13:15:46 +02:00
ts->read(e);
// if time sig not at begining of measure => courtesy time sig
2014-04-16 09:35:23 +02:00
int currTick = e.tick();
bool courtesySig = (currTick > tick());
if (courtesySig) {
// if courtesy sig., just add it without map processing
segment = getSegment(Segment::Type::TimeSigAnnounce, currTick);
segment->add(ts);
2014-07-28 15:54:47 +02:00
}
else {
// if 'real' time sig., do full process
segment = getSegment(Segment::Type::TimeSig, currTick);
segment->add(ts);
2013-06-04 18:29:14 +02:00
2014-10-08 10:57:24 +02:00
timeStretch = ts->stretch().reduced();
_timesig = ts->sig() / timeStretch;
if (score()->mscVersion() > 114) {
if (irregular) {
score()->sigmap()->add(tick(), SigEvent(_len, _timesig));
score()->sigmap()->add(tick() + ticks(), SigEvent(_timesig));
}
else {
_len = _timesig;
score()->sigmap()->add(tick(), SigEvent(_timesig));
}
}
2012-06-28 15:12:17 +02:00
}
2012-05-26 14:26:10 +02:00
}
else if (tag == "KeySig") {
KeySig* ks = new KeySig(score());
2013-01-21 20:21:41 +01:00
ks->setTrack(e.track());
2012-05-26 14:26:10 +02:00
ks->read(e);
2014-08-24 13:10:51 +02:00
int curTick = e.tick();
if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick == 0) {
2014-08-24 13:10:51 +02:00
// ignore empty key signature
qDebug("remove keysig c at tick 0");
delete ks;
}
else {
// if key sig not at beginning of measure => courtesy key sig
// bool courtesySig = (curTick > tick());
bool courtesySig = (curTick == endTick());
2014-08-24 13:10:51 +02:00
segment = getSegment(courtesySig ? Segment::Type::KeySigAnnounce : Segment::Type::KeySig, curTick);
segment->add(ks);
if (!courtesySig)
staff->setKey(curTick, ks->keySigEvent());
2014-08-24 13:10:51 +02:00
}
2012-05-26 14:26:10 +02:00
}
else if (tag == "Lyrics") { // obsolete, keep for compatibility with version 114
Element* element = Element::name2Element(tag, score());
element->setTrack(e.track());
element->read(e);
segment = getSegment(Segment::Type::ChordRest, e.tick());
ChordRest* cr = static_cast<ChordRest*>(segment->element(element->track()));
if (!cr)
cr = static_cast<ChordRest*>(segment->element(e.track())); // in case lyric itself has bad track info
2012-05-26 14:26:10 +02:00
if (!cr)
qDebug("Internal error: no chord/rest for lyrics");
2012-05-26 14:26:10 +02:00
else
cr->add(element);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Text") {
2014-10-06 18:44:08 +02:00
Text* t = new StaffText(score());
2013-01-21 20:21:41 +01:00
t->setTrack(e.track());
2012-05-26 14:26:10 +02:00
t->read(e);
// previous versions stored measure number, delete it
if ((score()->mscVersion() <= 114) && (t->textStyleType() == TextStyleType::MEASURE_NUMBER))
2012-09-21 12:23:18 +02:00
delete t;
else if (t->isEmpty()) {
qDebug("reading empty text: deleted");
delete t;
}
else if ((score()->mscVersion() <= 114) && t->textStyleType() == TextStyleType::REHEARSAL_MARK) {
RehearsalMark* rh = new RehearsalMark(score());
rh->setXmlText(t->xmlText());
rh->setTrack(t->track());
segment = getSegment(Segment::Type::ChordRest, e.tick());
segment->add(rh);
delete t;
}
2012-09-21 12:23:18 +02:00
else {
segment = getSegment(Segment::Type::ChordRest, e.tick());
2012-09-21 12:23:18 +02:00
segment->add(t);
}
2012-05-26 14:26:10 +02:00
}
//----------------------------------------------------
// Annotation
else if (tag == "Dynamic") {
Dynamic* dyn = new Dynamic(score());
2013-01-21 20:21:41 +01:00
dyn->setTrack(e.track());
2012-05-26 14:26:10 +02:00
dyn->read(e);
2013-02-14 13:50:59 +01:00
if (score()->mscVersion() <= 114)
dyn->setDynamicType(dyn->xmlText());
segment = getSegment(Segment::Type::ChordRest, e.tick());
2012-05-26 14:26:10 +02:00
segment->add(dyn);
}
else if (tag == "Harmony"
|| tag == "FretDiagram"
2014-08-26 15:07:24 +02:00
|| tag == "TremoloBar"
2012-05-26 14:26:10 +02:00
|| tag == "Symbol"
|| tag == "Tempo"
|| tag == "StaffText"
|| tag == "RehearsalMark"
|| tag == "InstrumentChange"
|| tag == "StaffState"
|| tag == "FiguredBass"
) {
2013-01-18 10:55:52 +01:00
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
if (score()->mscVersion() <= 114 && el->type() == Element::Type::SYMBOL)
el->setParent(this); // this will get reset when adding to segment
2013-01-21 20:21:41 +01:00
el->setTrack(e.track());
2012-05-26 14:26:10 +02:00
el->read(e);
segment = getSegment(Segment::Type::ChordRest, e.tick());
2012-05-26 14:26:10 +02:00
segment->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 == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Element* el = Element::name2Element(tag, score());
el->setTrack(e.track());
el->read(e);
segment = getSegment(Segment::Type::ChordRest, e.tick());
segment->add(el);
}
}
2012-05-26 14:26:10 +02:00
//----------------------------------------------------
else if (tag == "stretch") {
double val = e.readDouble();
if (val < 0.0)
val = 0;
setUserStretch(val);
}
2012-05-26 14:26:10 +02:00
else if (tag == "LayoutBreak") {
LayoutBreak* lb = new LayoutBreak(score());
lb->read(e);
add(lb);
}
else if (tag == "noOffset")
2013-01-11 18:10:18 +01:00
_noOffset = e.readInt();
else if (tag == "measureNumberMode")
setMeasureNumberMode(MeasureNumberMode(e.readInt()));
else if (tag == "irregular")
_irregular = e.readBool();
else if (tag == "breakMultiMeasureRest")
_breakMultiMeasureRest = e.readBool();
else if (tag == "sysInitBarLineType") {
const QString& val(e.readElementText());
_systemInitialBarLineType = BarLineType::NORMAL;
2015-02-25 11:54:31 +01:00
for (unsigned i = 0; i < BarLine::barLineTableSize(); ++i) {
if (BarLine::barLineTypeName(BarLineType(i)) == val) {
_systemInitialBarLineType = BarLineType(i);
break;
}
}
}
2012-05-26 14:26:10 +02:00
else if (tag == "Tuplet") {
Tuplet* tuplet = new Tuplet(score());
2013-01-21 20:21:41 +01:00
tuplet->setTrack(e.track());
tuplet->setTick(e.tick());
2012-05-26 14:26:10 +02:00
tuplet->setParent(this);
tuplet->read(e);
2013-01-21 20:21:41 +01:00
e.addTuplet(tuplet);
2012-05-26 14:26:10 +02:00
}
2013-01-11 18:10:18 +01:00
else if (tag == "startRepeat") {
2014-07-17 09:32:30 +02:00
_repeatFlags = _repeatFlags | Repeat::START;
2013-01-11 18:10:18 +01:00
e.readNext();
}
2012-05-26 14:26:10 +02:00
else if (tag == "endRepeat") {
2013-01-11 18:10:18 +01:00
_repeatCount = e.readInt();
2014-07-17 09:32:30 +02:00
_repeatFlags = _repeatFlags | Repeat::END;
2012-05-26 14:26:10 +02:00
}
else if (tag == "vspacer" || tag == "vspacerDown") {
if (staves[staffIdx]->_vspacerDown == 0) {
Spacer* spacer = new Spacer(score());
spacer->setSpacerType(SpacerType::DOWN);
2012-05-26 14:26:10 +02:00
spacer->setTrack(staffIdx * VOICES);
add(spacer);
}
2013-01-11 18:10:18 +01:00
staves[staffIdx]->_vspacerDown->setGap(e.readDouble() * _spatium);
2012-05-26 14:26:10 +02:00
}
else if (tag == "vspacer" || tag == "vspacerUp") {
if (staves[staffIdx]->_vspacerUp == 0) {
Spacer* spacer = new Spacer(score());
spacer->setSpacerType(SpacerType::UP);
2012-05-26 14:26:10 +02:00
spacer->setTrack(staffIdx * VOICES);
add(spacer);
}
2013-01-11 18:10:18 +01:00
staves[staffIdx]->_vspacerUp->setGap(e.readDouble() * _spatium);
2012-05-26 14:26:10 +02:00
}
else if (tag == "visible")
2013-01-11 18:10:18 +01:00
staves[staffIdx]->_visible = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "slashStyle")
2013-01-11 18:10:18 +01:00
staves[staffIdx]->_slashStyle = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "Beam") {
Beam* beam = new Beam(score());
2013-01-21 20:21:41 +01:00
beam->setTrack(e.track());
2012-05-26 14:26:10 +02:00
beam->read(e);
beam->setParent(0);
2013-01-21 20:21:41 +01:00
e.addBeam(beam);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Segment")
2013-06-24 19:01:31 +02:00
segment->read(e);
2012-10-17 10:39:39 +02:00
else if (tag == "MeasureNumber") {
Text* noText = new Text(score());
noText->read(e);
noText->setFlag(ElementFlag::ON_STAFF, true);
// noText->setFlag(ElementFlag::MOVABLE, false); ??
noText->setTrack(e.track());
noText->setParent(this);
staves[noText->staffIdx()]->setNoText(noText);
2012-10-17 10:39:39 +02:00
}
2015-07-25 08:43:02 +02:00
else if (tag == "SystemDivider") {
SystemDivider* sd = new SystemDivider(score());
sd->read(e);
add(sd);
}
2013-11-25 12:17:12 +01:00
else if (tag == "Ambitus") {
Ambitus* range = new Ambitus(score());
range->read(e);
segment = getSegment(Segment::Type::Ambitus, e.tick());
range->setParent(segment); // a parent segment is needed for setTrack() to work
range->setTrack(trackZeroVoice(e.track()));
segment->add(range);
}
else if (tag == "multiMeasureRest") {
2014-02-26 19:06:42 +01:00
_mmRestCount = e.readInt();
// set tick to previous measure
setTick(e.lastMeasure()->tick());
e.initTick(e.lastMeasure()->tick());
}
2013-01-17 12:56:14 +01:00
else if (Element::readProperties(e))
;
2012-05-26 14:26:10 +02:00
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
if (staffIdx == 0) {
Segment* s = last();
if (s && s->segmentType() == Segment::Type::BarLine) {
2012-05-26 14:26:10 +02:00
BarLine* b = static_cast<BarLine*>(s->element(0));
setEndBarLineType(b->barLineType(), false, b->visible(), b->color());
2012-05-26 14:26:10 +02:00
// s->remove(b);
// delete b;
}
}
//
// for compatibility with 1.22:
//
if (score()->mscVersion() == 122) {
int ticks1 = 0;
for (Segment* s = last(); s; s = s->prev()) {
if (s->segmentType() == Segment::Type::ChordRest) {
if (s->element(0)) {
ChordRest* cr = static_cast<ChordRest*>(s->element(0));
if (cr->type() == Element::Type::REPEAT_MEASURE)
ticks1 = ticks();
else
ticks1 = s->rtick() + cr->actualTicks();
break;
}
}
}
if (ticks() != ticks1) {
// this is a irregular measure
_len = Fraction::fromTicks(ticks1);
_len.reduce();
for (Segment* s = last(); s; s = s->prev()) {
if (s->tick() < tick() + ticks())
break;
if (s->segmentType() == Segment::Type::BarLine) {
qDebug("reduce BarLine to EndBarLine");
s->setSegmentType(Segment::Type::EndBarLine);
}
}
}
2012-05-26 14:26:10 +02:00
}
2013-01-21 20:21:41 +01:00
foreach (Tuplet* tuplet, e.tuplets()) {
2012-05-26 14:26:10 +02:00
if (tuplet->elements().isEmpty()) {
// this should not happen and is a sign of input file corruption
2013-01-24 09:31:41 +01:00
qDebug("Measure:read(): empty tuplet id %d (%p), input file corrupted?",
tuplet->id(), tuplet);
2012-05-26 14:26:10 +02:00
delete tuplet;
}
else
tuplet->setParent(this);
}
}
//---------------------------------------------------------
// visible
//---------------------------------------------------------
bool Measure::visible(int staffIdx) const
{
if (system() && (system()->staves()->isEmpty() || !system()->staff(staffIdx)->show()))
return false;
2014-11-10 14:18:42 +01:00
if (staffIdx >= score()->staves().size()) {
qDebug("Measure::visible: bad staffIdx: %d", staffIdx);
return false;
}
2012-05-26 14:26:10 +02:00
return score()->staff(staffIdx)->show() && staves[staffIdx]->_visible;
}
//---------------------------------------------------------
// slashStyle
//---------------------------------------------------------
bool Measure::slashStyle(int staffIdx) const
{
return score()->staff(staffIdx)->slashStyle() || staves[staffIdx]->_slashStyle || score()->staff(staffIdx)->staffType()->slashStyle();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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()))
2012-05-26 14:26:10 +02:00
continue;
MStaff* ms = staves[staffIdx];
if (ms->lines)
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());
2012-05-26 14:26:10 +02:00
}
for (Segment* s = first(); s; s = s->next())
s->scanElements(data, func, all);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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() != Segment::Type::ChordRest)
2012-05-26 14:26:10 +02:00
continue;
if (s->element(track) == 0)
score()->setRest(s->tick(), track, len(), true, 0);
break;
}
}
//---------------------------------------------------------
// setStartRepeatBarLine
// return true if bar line type changed
//---------------------------------------------------------
bool Measure::setStartRepeatBarLine(bool val)
{
2014-07-17 09:32:30 +02:00
bool changed = false;
Segment* s = findSegment(Segment::Type::StartRepeatBarLine, tick());
bool customSpan = false;
int numStaves = score()->nstaves();
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < numStaves;) {
2012-05-26 14:26:10 +02:00
int track = staffIdx * VOICES;
Staff* staff = score()->staff(staffIdx);
BarLine* bl = s ? static_cast<BarLine*>(s->element(track)) : nullptr;
int span, spanFrom, spanTo;
// if there is a bar line and has custom span, take span from it
if (bl && bl->customSpan()) {
span = bl->span();
spanFrom = bl->spanFrom();
spanTo = bl->spanTo();
customSpan = bl->customSpan();
}
else {
span = staff->barLineSpan();
spanFrom = staff->barLineFrom();
spanTo = staff->barLineTo();
if (span == 0 && customSpan) {
// spanned staves have already been skipped by the loop at the end;
// if a staff with span 0 is found and the previous bar line had custom span
// this staff shall have an aditional bar line, because the previous staff bar
// line has been shortened
int staffLines = staff->lines();
span = 1;
spanFrom = staffLines == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0;
spanTo = staffLines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (staffLines-1) * 2;
}
customSpan = false;
}
// make sure we do not span more staves than actually exist
if (staffIdx + span > numStaves)
span = numStaves - staffIdx;
2012-05-26 14:26:10 +02:00
if (span && val && (bl == 0)) {
// no barline were we need one:
if (s == 0) {
if (score()->undoRedo()) {
return false;
}
s = undoGetSegment(Segment::Type::StartRepeatBarLine, tick());
}
Bar lines: fixing custom type and generated management There are some inconsistencies in the current management of bar line `_generated` flag and user-modified type: - Bar lines created by the New Score Wizard are flagged as non-generated, as well as bar lines of measures manually **appended** by the user, while bar lines of measures **inserted** are flagged as generated. - If a generated bar line is individually changed of typed, it remains flagged as generated, it is not saved and the change is lost upon saving and re-loading. - The management of the internal flag `BarLine::_customSubtype` is not always consistent. - The `Measure::_endBarLineGenerated` flag was not always restored properly by undo. This PR introduces the following fixes: - The `_generated` flag is consistently used for bar lines whose type can be reconstructed from the context and then do not need to be saved to the output file. - Normal bar lines are **always** created as generated: initially created by the Wizard, manually appended or inserted. - Bar lines with custom type (i.e. different from the type which can be expected according to the bar line context) are always flagged as non-generated, ensuring the custom type is written to the output file. - The `Measure::_endBarLineGenerated` flag is stored by `ChangeEndBarLineType()` and restore upon undo. - Some test reference scores, based on the inconsistent bar line `_generated` flag, have been uniformed. Notes: - Tests about measure (and then bar line) appending, inserting and relative undo's are already included in the `tst_parts` test suite. - Some inconsistencies remain in the management of custom bar line span and of system-initial bar lines: for the sake of simplicity, they will be dealt with in separate PR's.
2014-09-06 10:36:35 +02:00
bl = new BarLine(score());
bl->setTrack(track);
bl->setParent(s); // let the bar line know it belongs to a StartRepeatBarLine segment
bl->setBarLineType(BarLineType::START_REPEAT);
2012-05-26 14:26:10 +02:00
score()->undoAddElement(bl);
changed = true;
}
else if (bl && !val) {
// barline were we do not need one:
2013-10-25 12:17:42 +02:00
if (!score()->undoRedo()) // DEBUG
score()->undoRemoveElement(bl);
2012-05-26 14:26:10 +02:00
changed = true;
}
if (bl && val && span) {
2012-05-26 14:26:10 +02:00
bl->setSpan(span);
bl->setSpanFrom(spanFrom);
bl->setSpanTo(spanTo);
}
2012-05-26 14:26:10 +02:00
++staffIdx;
//
// remove any unwanted barlines:
//
// if spanning several staves but not entering INTO last staff,
if (span > 1 && spanTo <= 0)
span--; // count one span less
2012-05-26 14:26:10 +02:00
if (s) {
for (int i = 1; i < span; ++i) {
BarLine* bl = static_cast<BarLine*>(s->element(staffIdx * VOICES));
if (bl) {
score()->undoRemoveElement(bl);
changed = true;
}
++staffIdx;
}
}
}
return changed;
}
//---------------------------------------------------------
// createEndBarLines
// actually creates or modifies barlines
// returns true if layout changes
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
bool Measure::createEndBarLines()
{
bool changed = false;
int nstaves = score()->nstaves();
Segment* seg = undoGetSegment(Segment::Type::EndBarLine, endTick());
2012-05-26 14:26:10 +02:00
2012-09-19 14:13:07 +02:00
BarLine* bl = 0;
int span = 0; // span counter
int aspan = 0; // actual span
bool mensur = false; // keep note of Mensurstrich case
int spanTot; // to keep track of the target span
2012-10-14 09:25:33 +02:00
int spanFrom;
int spanTo;
2012-09-19 14:13:07 +02:00
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
2012-05-26 14:26:10 +02:00
Staff* staff = score()->staff(staffIdx);
2014-07-17 09:32:30 +02:00
int track = staffIdx * VOICES;
2012-10-14 09:25:33 +02:00
// get existing bar line for this staff, if any
2012-09-19 14:13:07 +02:00
BarLine* cbl = static_cast<BarLine*>(seg->element(track));
2014-07-17 09:32:30 +02:00
// if span counter has been counted off, get new span values
// and forget about any previous bar line
2014-07-17 09:32:30 +02:00
2012-09-19 14:13:07 +02:00
if (span == 0) {
2014-08-05 16:09:14 +02:00
if (cbl && cbl->customSpan()) { // if there is a bar line and has custom span,
span = cbl->span(); // get span values from it
spanFrom = cbl->spanFrom();
spanTo = cbl->spanTo();
// if bar span values == staff span values, set bar as not custom
if(span == staff->barLineSpan() && spanFrom == staff->barLineFrom()
&& spanTo == staff->barLineTo())
cbl->setCustomSpan(false);
}
else { // otherwise, get from staff
span = staff->barLineSpan();
// if some span OR last staff (span=0) of a Mensurstrich case, get From/To from staff
if (span || mensur) {
spanFrom = staff->barLineFrom();
spanTo = staff->barLineTo();
mensur = false;
}
// but if staff is set to no span, a multi-staff spanning bar line
// has been shortened to span less staves and following staves left without bars;
// set bar line span values to default
else {
span = 1;
spanFrom = 0;
spanTo = (staff->lines()-1)*2;
}
}
2012-09-19 14:13:07 +02:00
if ((staffIdx + span) > nstaves)
span = nstaves - staffIdx;
spanTot = span;
2012-09-19 14:13:07 +02:00
bl = 0;
}
if (staff->show() && span) {
//
// there should be a barline in this staff
//
// if we already have a bar line, keep extending this bar line down until span exhausted;
// if no barline yet, re-use the bar line existing in this staff if any,
// restarting actual span
2012-09-19 14:13:07 +02:00
if (!bl) {
bl = cbl;
aspan = 0;
2012-05-26 14:26:10 +02:00
}
2012-09-19 14:13:07 +02:00
if (!bl) {
// no suitable bar line: create a new one
2012-09-19 14:13:07 +02:00
bl = new BarLine(score());
bl->setVisible(_endBarLineVisible);
bl->setColor(_endBarLineColor);
Bar lines: fixing custom type and generated management There are some inconsistencies in the current management of bar line `_generated` flag and user-modified type: - Bar lines created by the New Score Wizard are flagged as non-generated, as well as bar lines of measures manually **appended** by the user, while bar lines of measures **inserted** are flagged as generated. - If a generated bar line is individually changed of typed, it remains flagged as generated, it is not saved and the change is lost upon saving and re-loading. - The management of the internal flag `BarLine::_customSubtype` is not always consistent. - The `Measure::_endBarLineGenerated` flag was not always restored properly by undo. This PR introduces the following fixes: - The `_generated` flag is consistently used for bar lines whose type can be reconstructed from the context and then do not need to be saved to the output file. - Normal bar lines are **always** created as generated: initially created by the Wizard, manually appended or inserted. - Bar lines with custom type (i.e. different from the type which can be expected according to the bar line context) are always flagged as non-generated, ensuring the custom type is written to the output file. - The `Measure::_endBarLineGenerated` flag is stored by `ChangeEndBarLineType()` and restore upon undo. - Some test reference scores, based on the inconsistent bar line `_generated` flag, have been uniformed. Notes: - Tests about measure (and then bar line) appending, inserting and relative undo's are already included in the `tst_parts` test suite. - Some inconsistencies remain in the management of custom bar line span and of system-initial bar lines: for the sake of simplicity, they will be dealt with in separate PR's.
2014-09-06 10:36:35 +02:00
bl->setParent(seg); // let the bar line know the segment and track it belongs to
2012-09-19 14:13:07 +02:00
bl->setTrack(track);
Bar lines: fixing custom type and generated management There are some inconsistencies in the current management of bar line `_generated` flag and user-modified type: - Bar lines created by the New Score Wizard are flagged as non-generated, as well as bar lines of measures manually **appended** by the user, while bar lines of measures **inserted** are flagged as generated. - If a generated bar line is individually changed of typed, it remains flagged as generated, it is not saved and the change is lost upon saving and re-loading. - The management of the internal flag `BarLine::_customSubtype` is not always consistent. - The `Measure::_endBarLineGenerated` flag was not always restored properly by undo. This PR introduces the following fixes: - The `_generated` flag is consistently used for bar lines whose type can be reconstructed from the context and then do not need to be saved to the output file. - Normal bar lines are **always** created as generated: initially created by the Wizard, manually appended or inserted. - Bar lines with custom type (i.e. different from the type which can be expected according to the bar line context) are always flagged as non-generated, ensuring the custom type is written to the output file. - The `Measure::_endBarLineGenerated` flag is stored by `ChangeEndBarLineType()` and restore upon undo. - Some test reference scores, based on the inconsistent bar line `_generated` flag, have been uniformed. Notes: - Tests about measure (and then bar line) appending, inserting and relative undo's are already included in the `tst_parts` test suite. - Some inconsistencies remain in the management of custom bar line span and of system-initial bar lines: for the sake of simplicity, they will be dealt with in separate PR's.
2014-09-06 10:36:35 +02:00
bl->setBarLineType(_endBarLineType);
bl->setGenerated(_endBarLineGenerated);
2012-09-19 14:13:07 +02:00
score()->undoAddElement(bl);
changed = true;
}
else {
// a bar line is there (either existing or newly created):
// adjust subtype, if not fitting
if (bl->barLineType() != _endBarLineType && !bl->customSubtype()) {
score()->undoChangeProperty(bl, P_ID::SUBTYPE, int(_endBarLineType));
2013-03-25 16:27:20 +01:00
bl->setGenerated(bl->el()->empty() && _endBarLineGenerated);
2012-05-26 14:26:10 +02:00
changed = true;
}
// or clear custom subtype flag if same type as measure
Bar lines: fixing custom type and generated management There are some inconsistencies in the current management of bar line `_generated` flag and user-modified type: - Bar lines created by the New Score Wizard are flagged as non-generated, as well as bar lines of measures manually **appended** by the user, while bar lines of measures **inserted** are flagged as generated. - If a generated bar line is individually changed of typed, it remains flagged as generated, it is not saved and the change is lost upon saving and re-loading. - The management of the internal flag `BarLine::_customSubtype` is not always consistent. - The `Measure::_endBarLineGenerated` flag was not always restored properly by undo. This PR introduces the following fixes: - The `_generated` flag is consistently used for bar lines whose type can be reconstructed from the context and then do not need to be saved to the output file. - Normal bar lines are **always** created as generated: initially created by the Wizard, manually appended or inserted. - Bar lines with custom type (i.e. different from the type which can be expected according to the bar line context) are always flagged as non-generated, ensuring the custom type is written to the output file. - The `Measure::_endBarLineGenerated` flag is stored by `ChangeEndBarLineType()` and restore upon undo. - Some test reference scores, based on the inconsistent bar line `_generated` flag, have been uniformed. Notes: - Tests about measure (and then bar line) appending, inserting and relative undo's are already included in the `tst_parts` test suite. - Some inconsistencies remain in the management of custom bar line span and of system-initial bar lines: for the sake of simplicity, they will be dealt with in separate PR's.
2014-09-06 10:36:35 +02:00
if (bl->barLineType() == _endBarLineType && bl->customSubtype()) {
bl->setCustomSubtype(false);
Bar lines: fixing custom type and generated management There are some inconsistencies in the current management of bar line `_generated` flag and user-modified type: - Bar lines created by the New Score Wizard are flagged as non-generated, as well as bar lines of measures manually **appended** by the user, while bar lines of measures **inserted** are flagged as generated. - If a generated bar line is individually changed of typed, it remains flagged as generated, it is not saved and the change is lost upon saving and re-loading. - The management of the internal flag `BarLine::_customSubtype` is not always consistent. - The `Measure::_endBarLineGenerated` flag was not always restored properly by undo. This PR introduces the following fixes: - The `_generated` flag is consistently used for bar lines whose type can be reconstructed from the context and then do not need to be saved to the output file. - Normal bar lines are **always** created as generated: initially created by the Wizard, manually appended or inserted. - Bar lines with custom type (i.e. different from the type which can be expected according to the bar line context) are always flagged as non-generated, ensuring the custom type is written to the output file. - The `Measure::_endBarLineGenerated` flag is stored by `ChangeEndBarLineType()` and restore upon undo. - Some test reference scores, based on the inconsistent bar line `_generated` flag, have been uniformed. Notes: - Tests about measure (and then bar line) appending, inserting and relative undo's are already included in the `tst_parts` test suite. - Some inconsistencies remain in the management of custom bar line span and of system-initial bar lines: for the sake of simplicity, they will be dealt with in separate PR's.
2014-09-06 10:36:35 +02:00
bl->setGenerated(bl->el()->empty() && _endBarLineGenerated);
}
2014-07-17 09:32:30 +02:00
// if a bar line exists for this staff (cbl) but
// it is not the bar line we are dealing with (bl),
// we are extending down the bar line of a staff above (bl)
// and the bar line for this staff (cbl) is not needed:
// DELETE it
2012-09-19 14:13:07 +02:00
if (cbl && cbl != bl) {
// Mensurstrich special case:
// if span arrives inside the end staff (spanTo>0) OR
// span is not multi-staff (spanTot<=1) OR
// current staff is not the last spanned staff (span!=1) OR
// staff is the last score staff
// remove bar line for this staff
// If NONE of the above conditions holds, the staff is the last staff of
// a Mensurstrich(-like) span: keep its bar line, as it may span to next staff
2013-01-02 20:13:58 +01:00
if (spanTo > 0 || spanTot <= 1 || span != 1 || staffIdx == nstaves-1) {
score()->undoRemoveElement(cbl);
changed = true;
}
2012-05-26 14:26:10 +02:00
}
}
}
2012-09-19 14:13:07 +02:00
else {
//
2012-09-19 14:13:07 +02:00
// there should be no barline in this staff
//
2012-09-19 14:13:07 +02:00
if (cbl) {
score()->undoRemoveElement(cbl);
changed = true;
}
}
// if span not counted off AND we have a bar line AND this staff is shown,
// set bar line span values (this may result in extending down a bar line
// for a previous staff, if we are counting off a span > 1)
2012-09-19 14:13:07 +02:00
if (span) {
if (bl) {
++aspan;
2012-10-14 09:25:33 +02:00
if (staff->show()) { // update only if visible
2012-09-19 14:13:07 +02:00
bl->setSpan(aspan);
2012-10-14 09:25:33 +02:00
bl->setSpanFrom(spanFrom);
// if current actual span < target span, set spanTo to full staff height
if(aspan < spanTot)
bl->setSpanTo((staff->lines()-1)*2);
// if we reached target span, set spanTo to intended value
else
bl->setSpanTo(spanTo);
2012-10-14 09:25:33 +02:00
}
2012-09-19 14:13:07 +02:00
}
--span;
}
// if just finished (span==0) a multi-staff span (spanTot>1) ending at the top of a staff (spanTo<=0)
// scan this staff again, as it may have its own bar lines (Mensurstrich(-like) span)
if (spanTot > 1 && spanTo <= 0 && span == 0) {
mensur = true;
staffIdx--;
}
2012-05-26 14:26:10 +02:00
}
return changed;
}
//---------------------------------------------------------
// setEndBarLineType
//---------------------------------------------------------
void Measure::setEndBarLineType(BarLineType val, bool g, bool visible, QColor color)
{
_endBarLineType = val;
_endBarLineGenerated = g;
_endBarLineVisible = visible;
2014-07-17 09:32:30 +02:00
if (color.isValid())
_endBarLineColor = color;
else
_endBarLineColor = curColor();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// sortStaves
//---------------------------------------------------------
void Measure::sortStaves(QList<int>& dst)
{
QList<MStaff*> ms;
foreach (int idx, dst)
2012-05-26 14:26:10 +02:00
ms.push_back(staves[idx]);
staves = ms;
for (int staffIdx = 0; staffIdx < staves.size(); ++staffIdx) {
if (staves[staffIdx]->lines)
staves[staffIdx]->lines->setTrack(staffIdx * VOICES);
}
for (Segment* s = first(); s; s = s->next())
s->sortStaves(dst);
2013-10-14 15:34:46 +02:00
foreach (Element* e, _el) {
if (e->track() == -1 || e->systemFlag())
2012-05-26 14:26:10 +02:00
continue;
int voice = e->voice();
int staffIdx = e->staffIdx();
int idx = dst.indexOf(staffIdx);
e->setTrack(idx * VOICES + voice);
}
}
//---------------------------------------------------------
// exchangeVoice
//---------------------------------------------------------
2014-08-29 10:35:17 +02:00
void Measure::exchangeVoice(int v1, int v2, int staffIdx)
2012-05-26 14:26:10 +02:00
{
2014-08-29 10:35:17 +02:00
for (Segment* s = first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
int strack = staffIdx * VOICES + v1;
int dtrack = staffIdx * VOICES + v2;
s->swapElements(strack, dtrack);
2012-05-26 14:26:10 +02:00
}
// MStaff* ms = mstaff(staffIdx);
// ms->hasVoices = true;
checkMultiVoices(staffIdx); // probably true, but check for invisible notes & rests
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// checkMultiVoices
2012-08-02 18:33:43 +02:00
/// Check for more than on voice in this measure and staff and
/// set MStaff->hasVoices
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Measure::checkMultiVoices(int staffIdx)
{
int strack = staffIdx * VOICES + 1;
int etrack = staffIdx * VOICES + VOICES;
staves[staffIdx]->hasVoices = false;
for (Segment* s = first(); s; s = s->next()) {
if (s->segmentType() != Segment::Type::ChordRest)
2012-05-26 14:26:10 +02:00
continue;
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e) {
bool v;
if (e->type() == Element::Type::CHORD) {
v = false;
// consider chord visible if any note is visible
Chord* c = static_cast<Chord*>(e);
for (Note* n : c->notes()) {
if (n->visible()) {
v = true;
break;
}
}
}
else
v = e->visible();
if (v) {
staves[staffIdx]->hasVoices = true;
return;
}
2012-05-26 14:26:10 +02:00
}
}
}
}
//---------------------------------------------------------
// hasVoice
//---------------------------------------------------------
bool Measure::hasVoice(int track) const
{
for (Segment* s = first(); s; s = s->next()) {
if (s->segmentType() != Segment::Type::ChordRest)
2012-05-26 14:26:10 +02:00
continue;
if (s->element(track))
return true;
}
return false;
}
2012-08-02 18:33:43 +02:00
//-------------------------------------------------------------------
2012-05-26 14:26:10 +02:00
// isMeasureRest
2012-08-02 18:33:43 +02:00
/// Check if the measure is filled by a full-measure rest or full
/// of rests on this staff. If staff is -1, then check for
/// all staves.
//-------------------------------------------------------------------
2012-05-26 14:26:10 +02:00
bool Measure::isMeasureRest(int staffIdx)
{
int strack;
int etrack;
if (staffIdx < 0) {
strack = 0;
etrack = score()->nstaves() * VOICES;
}
else {
strack = staffIdx * VOICES;
2014-08-26 21:01:21 +02:00
etrack = strack + VOICES;
2012-05-26 14:26:10 +02:00
}
for (Segment* s = first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e && e->type() != Element::Type::REST)
2012-05-26 14:26:10 +02:00
return false;
}
}
return true;
}
//---------------------------------------------------------
// isFullMeasureRest
// Check for an empty measure, filled with full measure
// rests.
//---------------------------------------------------------
bool Measure::isFullMeasureRest()
{
int strack = 0;
int etrack = score()->nstaves() * VOICES;
Segment* s = first(Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e) {
if (e->type() != Element::Type::REST)
2012-05-26 14:26:10 +02:00
return false;
Rest* rest = static_cast<Rest*>(e);
if (rest->durationType().type() != TDuration::DurationType::V_MEASURE)
2012-05-26 14:26:10 +02:00
return false;
}
}
return true;
}
//---------------------------------------------------------
// isRepeatMeasure
//---------------------------------------------------------
bool Measure::isRepeatMeasure(Staff* staff)
2012-05-26 14:26:10 +02:00
{
int staffIdx = score()->staffIdx(staff);
int strack = staffIdx * VOICES;
int etrack = (staffIdx + 1) * VOICES;
Segment* s = first(Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
2012-08-02 18:33:43 +02:00
if (s == 0)
2012-05-26 14:26:10 +02:00
return false;
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e && e->type() == Element::Type::REPEAT_MEASURE)
2012-05-26 14:26:10 +02:00
return true;
}
return false;
}
//---------------------------------------------------------
// distanceDown
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Measure::distanceDown(int i) const
2012-05-26 14:26:10 +02:00
{
if (staves[i]->_vspacerDown)
return qMax(staves[i]->distanceDown, staves[i]->_vspacerDown->gap());
return staves[i]->distanceDown;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// distanceUp
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Measure::distanceUp(int i) const
2012-05-26 14:26:10 +02:00
{
if (staves[i]->_vspacerUp)
return qMax(staves[i]->distanceUp, staves[i]->_vspacerUp->gap());
return staves[i]->distanceUp;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// isEmpty
//---------------------------------------------------------
bool Measure::isEmpty() const
{
if (_irregular)
return false;
int n = 0;
int tracks = staves.size() * VOICES;
static const Segment::Type st { Segment::Type::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)->type() != Element::Type::REST)
2012-05-26 14:26:10 +02:00
return false;
restFound = true;
2012-05-26 14:26:10 +02:00
}
}
if (restFound)
++n;
// measure is not empty if there is more than one rest
if (n > 1)
return false;
2012-05-26 14:26:10 +02:00
}
return true;
}
//---------------------------------------------------------
// isOnlyRests
//---------------------------------------------------------
bool Measure::isOnlyRests(int track) const
{
static const Segment::Type st { Segment::Type::ChordRest };
for (const Segment* s = first(st); s; s = s->next(st)) {
if (s->segmentType() != Segment::Type::ChordRest || !s->element(track))
continue;
if (s->element(track)->type() != Element::Type::REST)
return false;
}
return true;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Space::max
//---------------------------------------------------------
void Space::max(const Space& s)
{
if (s._lw > _lw)
_lw = s._lw;
if (s._rw > _rw)
_rw = s._rw;
}
2012-08-01 18:00:27 +02:00
//---------------------------------------------------------
// setDirty
//---------------------------------------------------------
2012-08-01 18:00:27 +02:00
void Measure::setDirty()
{
_minWidth1 = 0.0;
_minWidth2 = 0.0;
}
//---------------------------------------------------------
// systemHeader
2012-08-02 18:33:43 +02:00
/// return true if the measure contains a system header
// The system header is identified by a generated Clef in
// the first segment.
//---------------------------------------------------------
bool Measure::systemHeader() const
{
Segment* s = first();
return s && (s->segmentType() == Segment::Type::Clef) && s->element(0) && s->element(0)->generated();
}
//---------------------------------------------------------
// minWidth1
2012-10-08 08:28:19 +02:00
/// return minimum width of measure excluding system
/// header
//---------------------------------------------------------
qreal Measure::minWidth1() const
{
2012-10-08 09:34:46 +02:00
if (_minWidth1 == 0.0) {
int nstaves = score()->nstaves();
Segment* s = first();
Segment::Type st = Segment::Type::Clef | Segment::Type::KeySig | Segment::Type::StartRepeatBarLine;
while ((s->segmentType() & st) && s->next()) {
// found a segment that we might be able to skip
// we can do so only if it contains no non-generated elements
// note that it is possible for the same segment to contain both generated and non-generated elements
// consider, a keysig segment at the start of a system in which one staff has a local key change
bool generated = true;
for (int i = 0; i < nstaves; ++i) {
Element* e = s->element(i * VOICES);
if (e && !e->generated()) {
generated = false;
break;
}
}
if (!generated)
break;
2013-03-04 12:00:02 +01:00
s = s->next();
2012-08-01 18:00:27 +02:00
}
_minWidth1 = score()->computeMinWidth(s, false);
2012-10-08 09:34:46 +02:00
}
return _minWidth1;
}
//---------------------------------------------------------
// minWidth2
/// return minimum width of measure including system
/// header if present
//---------------------------------------------------------
qreal Measure::minWidth2() const
{
2012-10-08 09:34:46 +02:00
if (_minWidth2 == 0.0)
_minWidth2 = score()->computeMinWidth(first(), system()->firstMeasure() == this);
return _minWidth2;
}
//-----------------------------------------------------------------------------
// computeStretch
/// \brief distribute stretch across a range of segments
//-----------------------------------------------------------------------------
void computeStretch(int minTick, qreal minimum, qreal stretch, int first, int last, int ticksList[], qreal xpos[], qreal width[])
{
SpringMap springs;
for (int i = first; i < last; ++i) {
qreal str = 1.0;
qreal d;
qreal w = width[i];
int t = ticksList[i];
if (t) {
if (minTick > 0)
// str += .6 * log(qreal(t) / qreal(minTick)) / log(2.0);
str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
d = w / str;
}
else {
str = 0.0; // dont stretch timeSig and key
d = 100000000.0; // CHECK
}
springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
minimum += w;
}
//---------------------------------------------------
// distribute stretch to segments
//---------------------------------------------------
qreal force = sff(stretch, minimum, springs);
for (iSpring i = springs.begin(); i != springs.end(); ++i) {
qreal stretch = force * i->second.stretch;
if (stretch < i->second.fix)
stretch = i->second.fix;
width[i->second.seg] = stretch;
}
qreal x = xpos[first];
for (int i = first; i < last; ++i) {
x += width[i];
xpos[i+1] = x;
}
}
2012-05-26 14:26:10 +02:00
//-----------------------------------------------------------------------------
// layoutX
/// \brief main layout routine for note spacing
/// Return width of measure (in MeasureWidth), taking into account \a stretch.
//-----------------------------------------------------------------------------
void Measure::layoutX(qreal stretch)
2012-05-26 14:26:10 +02:00
{
int nstaves = _score->nstaves();
int segs = 0;
for (const Segment* s = first(); s; s = s->next()) {
if (s->segmentType() == Segment::Type::Clef && (s != first()))
2012-05-26 14:26:10 +02:00
continue;
++segs;
}
if (nstaves == 0 || segs == 0)
2012-05-26 14:26:10 +02:00
return;
qreal _spatium = spatium();
int tracks = nstaves * VOICES;
qreal clefKeyRightMargin = score()->styleS(StyleIdx::clefKeyRightMargin).val() * _spatium;
qreal minHarmonyDistance = score()->styleS(StyleIdx::minHarmonyDistance).val() * _spatium;
2014-05-26 15:31:36 +02:00
qreal maxHarmonyBarDistance = score()->styleS(StyleIdx::maxHarmonyBarDistance).val() * _spatium;
qreal minLyricsDashWidth = (score()->styleS(StyleIdx::lyricsDashMinLength).val()
+ Lyrics::LYRICS_DASH_DEFAULT_PAD * 2) * _spatium;
2012-05-26 14:26:10 +02:00
qreal rest[nstaves]; // fixed space needed from previous segment
memset(rest, 0, nstaves * sizeof(qreal));
qreal hRest[nstaves]; // fixed space needed from previous harmony
memset(hRest, 0, nstaves * sizeof(qreal));
2012-05-26 14:26:10 +02:00
//--------tick table for segments
int ticksList[segs];
memset(ticksList, 0, segs * sizeof(int));
qreal xpos[segs+1];
qreal width[segs];
int segmentIdx = 0;
qreal x = 0.0;
qreal lastx = 0.0;
2012-05-26 14:26:10 +02:00
int minTick = 100000;
int hMinTick = 100000;
int hLastIdx = -1;
2013-05-06 14:20:31 +02:00
int ntick = ticks(); // position of next measure
2012-05-26 14:26:10 +02:00
2013-05-29 11:49:58 +02:00
if (system()->firstMeasure() == this && system()->barLine()) {
BarLine* bl = system()->barLine();
x += BarLine::layoutWidth(score(), bl->barLineType(), bl->magS());
}
2013-03-04 12:00:02 +01:00
2014-05-26 15:31:36 +02:00
qreal minNoteDistance = score()->styleS(StyleIdx::minNoteDistance).val() * _spatium;
2012-05-26 14:26:10 +02:00
qreal clefWidth[nstaves];
memset(clefWidth, 0, nstaves * sizeof(qreal));
2013-04-08 18:26:53 +02:00
std::vector<QRectF> hLastBbox(nstaves); // bbox of previous harmony to test vertical separation
const Segment* s = first();
const Segment* pSeg = 0;
for (; s; s = s->next(), ++segmentIdx) {
qreal elsp = s->extraLeadingSpace().val() * _spatium;
2012-05-26 14:26:10 +02:00
qreal etsp = s->extraTrailingSpace().val() * _spatium;
if ((s->segmentType() == Segment::Type::Clef) && (s != first())) {
2012-05-26 14:26:10 +02:00
--segmentIdx;
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
2012-05-26 14:26:10 +02:00
int track = staffIdx * VOICES;
Element* e = s->element(track);
if (e) {
clefWidth[staffIdx] = e->width() + _spatium + elsp;
}
}
pSeg = s;
2012-05-26 14:26:10 +02:00
continue;
}
bool rest2[nstaves];
bool hRest2[nstaves];
2014-06-01 20:24:29 +02:00
bool spaceHarmony = false;
Segment::Type segType = s->segmentType();
2014-06-01 20:24:29 +02:00
qreal segmentWidth = 0.0;
qreal harmonyWidth = 0.0;
qreal stretchDistance = 0.0;
Segment::Type pt = pSeg ? pSeg->segmentType() : Segment::Type::BarLine;
#if 0
qreal firstHarmonyDistance = 0.0;
#endif
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
2012-05-26 14:26:10 +02:00
qreal minDistance = 0.0;
Space space;
Space hSpace;
QRectF hBbox;
2012-05-26 14:26:10 +02:00
int track = staffIdx * VOICES;
bool found = false;
bool hFound = false;
bool eFound = false;
#if 0
qreal harmonyCarryOver = system()->firstMeasure() == this ? 0.0 : // calculate value for this staff; but how to duplicate in Score::computeMinWidth?
#endif
2014-03-25 18:33:53 +01:00
if (segType & (Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
qreal llw = 0.0;
qreal rrw = 0.0;
Lyrics* lyrics = 0;
bool accidentalStaff = false;
2014-03-25 18:33:53 +01:00
bool accidental = false;
bool grace = false;
qreal accidentalX = 0.0;
qreal noteX = 0.0;
if (pt & (Segment::Type::StartRepeatBarLine | Segment::Type::BarLine | Segment::Type::TimeSig) && !accidentalStaff) {
2014-03-25 18:33:53 +01:00
for (int voice = 0; voice < VOICES; ++voice) {
ChordRest* cr = static_cast<ChordRest*>(s->element(track+voice));
if (!cr)
continue;
// check for accidentals in chord
if (cr->type() == Element::Type::CHORD) {
Chord* c = static_cast<Chord*>(cr);
2015-02-19 10:28:25 +01:00
if (c->graceNotesBefore().size())
grace = true;
else {
for (Note* note : c->notes()) {
if (note->accidental() && !note->fixed()) {
accidental = true;
accidentalX = qMin(accidentalX, note->accidental()->x() + note->x() + c->x());
}
else
noteX = qMin(noteX, note->x() + c->x());
}
}
}
2014-03-25 18:33:53 +01:00
}
}
for (int voice = 0; voice < VOICES; ++voice) {
ChordRest* cr = static_cast<ChordRest*>(s->element(track+voice));
if (!cr)
continue;
found = true;
if (pt & (Segment::Type::StartRepeatBarLine | Segment::Type::BarLine | Segment::Type::TimeSig) && !accidentalStaff) {
2013-07-18 15:54:05 +02:00
// no distance to full measure rest
if (!(cr->type() == Element::Type::REST && static_cast<Rest*>(cr)->durationType() == TDuration::DurationType::V_MEASURE)) {
accidentalStaff = true;
qreal sp;
2014-05-26 15:31:36 +02:00
qreal bnd = score()->styleS(StyleIdx::barNoteDistance).val() * _spatium;
if (accidental) {
2014-05-26 15:31:36 +02:00
qreal bad = score()->styleS(StyleIdx::barAccidentalDistance).val() * _spatium;
qreal diff = qMax(noteX - accidentalX, 0.0);
sp = qMax(bad, bnd - diff);
}
else if (grace)
sp = score()->styleS(StyleIdx::barGraceDistance).val() * _spatium;
else
sp = bnd;
if (pt & Segment::Type::TimeSig)
sp += clefKeyRightMargin - bnd;
2013-07-18 15:54:05 +02:00
minDistance = qMax(minDistance, sp);
}
else if (pt & Segment::Type::TimeSig)
2014-04-14 20:35:02 +02:00
minDistance = qMax(minDistance, clefKeyRightMargin);
2012-05-26 14:26:10 +02:00
}
else if (pt & (Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
minDistance = qMax(minDistance, minNoteDistance);
}
else {
bool firstClef = (pt == Segment::Type::Clef) && (pSeg && pSeg->rtick() == 0);
if ((pt & Segment::Type::KeySig) || firstClef)
2012-05-26 14:26:10 +02:00
minDistance = qMax(minDistance, clefKeyRightMargin);
}
2014-10-28 14:41:27 +01:00
// special case:
// make extra space for ties or glissandi continued from previous system
2014-10-28 14:41:27 +01:00
if (cr->type() == Element::Type::CHORD) {
Chord* c = static_cast<Chord*>(cr);
if (system()->firstMeasure() == this && c->tick() == tick()) {
if (c->endsGlissando()) {
minDistance = qMax(minDistance, _spatium * GLISS_STARTOFSYSTEM_WIDTH);
}
else {
for (Note* note : c->notes()) {
if (note->tieBack()) {
minDistance = qMax(minDistance, _spatium * 2);
break;
}
2014-10-28 14:41:27 +01:00
}
}
}
}
// calculate space needed for segment
// take cr position into account
// by converting to segment-relative space
// chord space itself already has ipos offset built in
// but lyrics do not
// and neither have user offsets
qreal cx = cr->ipos().x();
qreal cxu = cr->userOff().x();
qreal lx = qMax(cxu, 0.0); // nudge left shouldn't require more leading space
qreal rx = qMin(cxu, 0.0); // nudge right shouldn't require more trailing space
// Score::computeMinWidth already allocated enough space for full measure rests
// but do not account for them when spacing within the measure
// because they do not need to align with the other elements in segment
if (cr->durationType() != TDuration::DurationType::V_MEASURE) {
Space crSpace = cr->space();
Space segRelSpace(crSpace.lw()-lx, crSpace.rw()+rx);
space.max(segRelSpace);
}
// lyrics
int n = cr->lyricsList().size();
for (int i = 0; i < n; ++i) {
Lyrics* l = cr->lyricsList().at(i);
2012-10-23 14:42:00 +02:00
if (!l || l->isEmpty())
2012-05-26 14:26:10 +02:00
continue;
2012-10-23 14:42:00 +02:00
lyrics = l;
QRectF b(l->bbox().translated(l->pos()));
qreal brgt = b.right();
// if lyrics followed by a dash & score style requires the dash in any case,
// reserve at least the min dash length plus before and after padding
if ( (l->syllabic() == Lyrics::Syllabic::BEGIN
|| l->syllabic() == Lyrics::Syllabic::MIDDLE)
&& score()->styleB(StyleIdx::lyricsDashForce) )
brgt += minLyricsDashWidth;
llw = qMax(llw, -(b.left()+lx+cx));
rrw = qMax(rrw, brgt+rx+cx);
2012-05-26 14:26:10 +02:00
}
}
if (lyrics) {
2014-05-26 15:31:36 +02:00
qreal y = lyrics->ipos().y() + score()->styleS(StyleIdx::lyricsMinBottomDistance).val() * _spatium;
y -= score()->staff(staffIdx)->height();
2012-05-26 14:26:10 +02:00
if (y > staves[staffIdx]->distanceDown)
staves[staffIdx]->distanceDown = y;
space.max(Space(llw, rrw));
}
// add spacing for chord symbols
2013-05-06 14:20:31 +02:00
foreach (const Element* e, s->annotations()) {
if (e->type() != Element::Type::HARMONY || e->track() < track || e->track() >= track+VOICES)
continue;
2013-05-06 14:20:31 +02:00
const Harmony* h = static_cast<const Harmony*>(e);
QRectF b(h->bboxtight().translated(h->pos()));
if (hFound)
hBbox |= b;
else
hBbox = b;
hFound = true;
spaceHarmony = true;
// allow chord to be dragged
qreal xoff = h->pos().x();
qreal bl = -b.left() + qMin(xoff, 0.0);
qreal br = b.right() - qMax(xoff, 0.0);
hSpace.max(Space(bl, br));
#if 0
hSpace.max(Space(s->rtick()?-b.left():0.0, b.right()));
// account for carryover from last measure
if (harmonyCarryOver > 0.0) {
if (!s->rtick()) {
// first ChordRest of measure
// use minDistance to clear carryover harmony
minDistance = qMax(minDistance, harmonyCarryOver - x);
}
else {
// otherwise, use stretch
firstHarmonyDistance = qMax(firstHarmonyDistance, harmonyCarryOver + minHarmonyDistance);
}
harmonyCarryOver = 0.0;
}
#endif
}
2012-05-26 14:26:10 +02:00
}
else {
// current segment (s) is not a ChordRest
2012-05-26 14:26:10 +02:00
Element* e = s->element(track);
2014-10-10 11:52:44 +02:00
if (segType == Segment::Type::StartRepeatBarLine && !e) {
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
Element* ee = s->element(staffIdx * VOICES);
if (ee) {
BarLine* bl = static_cast<BarLine*>(ee);
int strack = staffIdx * VOICES;
int etrack = (staffIdx + bl->span()) * VOICES;
if (track >= strack && track < etrack) {
e = ee;
break;
}
}
}
}
if ((segType == Segment::Type::Clef) && (pt != Segment::Type::ChordRest))
2014-05-26 15:31:36 +02:00
minDistance = score()->styleS(StyleIdx::clefLeftMargin).val() * _spatium;
2015-06-12 07:04:43 +02:00
else if (segType == Segment::Type::StartRepeatBarLine && pSeg)
2012-05-26 14:26:10 +02:00
minDistance = .5 * _spatium;
else if (segType == Segment::Type::TimeSig && pt == Segment::Type::Clef) {
// missing key signature, but allocate default margin anyhow
minDistance = score()->styleS(StyleIdx::keysigLeftMargin).val() * _spatium;
}
else if ((segType == Segment::Type::EndBarLine) && segmentIdx) {
if (pt == Segment::Type::Clef)
2014-05-26 15:31:36 +02:00
minDistance = score()->styleS(StyleIdx::clefBarlineDistance).val() * _spatium;
2012-05-26 14:26:10 +02:00
else
2014-05-26 15:31:36 +02:00
stretchDistance = score()->styleS(StyleIdx::noteBarDistance).val() * _spatium;
2012-05-26 14:26:10 +02:00
if (e == 0) {
// look for barline
for (int i = track - VOICES; i >= 0; i -= VOICES) {
e = s->element(i);
if (e)
break;
}
}
}
if (e) {
eFound = true;
if (!s->next()) // segType & Segment::Type::EndBarLine
spaceHarmony = true; // to space last Harmony to end of measure
2012-05-26 14:26:10 +02:00
space.max(e->space());
}
}
space += Space(elsp, etsp);
2014-10-10 11:15:48 +02:00
// if (isMMRest()) //?? minDistance needed for clef-repeatbarline distance
// minDistance = 0;
if (found || eFound) {
2015-03-31 16:15:40 +02:00
space.addL(clefWidth[staffIdx]);
qreal sp = minDistance + rest[staffIdx] + qMax(space.lw(), stretchDistance);
2012-05-26 14:26:10 +02:00
rest[staffIdx] = space.rw();
rest2[staffIdx] = false;
segmentWidth = qMax(segmentWidth, sp);
}
else
rest2[staffIdx] = true;
// space chord symbols separately from segments
if (hFound || spaceHarmony) {
qreal sp = 0.0;
// space chord symbols unless they miss each other vertically
2015-06-12 07:04:43 +02:00
if (hFound && hBbox.top() < hLastBbox[staffIdx].bottom() && hBbox.bottom() > hLastBbox[staffIdx].top())
sp = hRest[staffIdx] + minHarmonyDistance + hSpace.lw();
// barline: limit space to maxHarmonyBarDistance
else if (spaceHarmony)
2015-06-12 07:04:43 +02:00
sp = qMin(hRest[staffIdx], maxHarmonyBarDistance);
hLastBbox[staffIdx] = hBbox;
hRest[staffIdx] = hSpace.rw();
hRest2[staffIdx] = false;
harmonyWidth = qMax(harmonyWidth, sp);
}
else
hRest2[staffIdx] = true;
2012-05-26 14:26:10 +02:00
clefWidth[staffIdx] = 0.0;
}
// set previous seg width before adding in harmony, to allow stretching
2012-05-26 14:26:10 +02:00
if (segmentIdx) {
width[segmentIdx-1] = segmentWidth;
if (pSeg)
pSeg->setbbox(QRectF(0.0, 0.0, segmentWidth, _spatium * 5)); //??
2012-05-26 14:26:10 +02:00
}
// make room for harmony if needed
segmentWidth = qMax(segmentWidth, harmonyWidth);
x += segmentWidth;
xpos[segmentIdx] = x;
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
2012-05-26 14:26:10 +02:00
if (rest2[staffIdx])
rest[staffIdx] -= qMin(rest[staffIdx], segmentWidth);
if (hRest2[staffIdx])
hRest[staffIdx] -= qMin(hRest[staffIdx], segmentWidth);
2012-05-26 14:26:10 +02:00
}
if ((s->segmentType() == Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
const Segment* nseg = s;
for (;;) {
nseg = nseg->next();
if (nseg == 0 || nseg->segmentType() == Segment::Type::ChordRest)
2012-05-26 14:26:10 +02:00
break;
}
2013-05-06 14:20:31 +02:00
int nticks = (nseg ? nseg->rtick() : ntick) - s->rtick();
2012-05-26 14:26:10 +02:00
if (nticks == 0) {
// this happens for tremolo notes
qDebug("layoutX: empty segment(%p)%s: measure: tick %d ticks %d",
s, s->subTypeName(), tick(), ticks());
qDebug(" nticks==0 segmente %d, segmentIdx: %d, segTick: %d nsegTick(%p) %d",
size(), segmentIdx-1, s->tick(), nseg, ntick
);
}
else {
if (nticks < minTick)
minTick = nticks;
if (nticks < hMinTick)
hMinTick = nticks;
2012-05-26 14:26:10 +02:00
}
ticksList[segmentIdx] = nticks;
}
else
ticksList[segmentIdx] = 0;
// if we are on a chord symbol, stretch the notes below it if necessary
if (spaceHarmony) {
if (hLastIdx >= 0) {
computeStretch(hMinTick, 0.0, x-lastx, hLastIdx, segmentIdx, ticksList, xpos, width);
}
#if 0
else if (s->rtick() && firstHarmonyDistance > 0.0) {
// account for carryover from previous measure
qDebug("measure %d, initial %d segments: stretching to %f", _no, segmentIdx, firstHarmonyDistance);
computeStretch(0, 0.0, firstHarmonyDistance, 0, segmentIdx, ticksList, xpos, width);
firstHarmonyDistance = 0.0;
}
#endif
hMinTick = 10000;
lastx = x;
hLastIdx = segmentIdx;
}
//
// set pSeg only to used segments
//
for (int voice = 0; voice < nstaves * VOICES; ++voice) {
if (!score()->staff(voice/VOICES)->show()) {
voice += VOICES-1;
continue;
}
if (s->element(voice)) {
pSeg = s;
break;
}
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------
// TAB: compute distance above and below
//---------------------------------------------------
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
qreal distAbove = 0.0;
qreal distBelow = 0.0;
2012-05-26 14:26:10 +02:00
Staff * staff = _score->staff(staffIdx);
if (staff->isTabStaff()) {
2014-04-28 18:38:50 +02:00
StaffType* stt = staff->staffType();
if (stt->slashStyle()) // if no stems
distAbove = stt->genDurations() ? -stt->durationBoxY() : 0.0;
else { // if stems
Fix #69846, #69821, #63561 - multi-voice TAB's: stem and slur positions __Background__: In the original TAB implementation, the "Stems above / below" setting was followed when there was only one voice, while with multiple voices, voice 1 stems were always above and voice 2 stems always below. In issue https://musescore.org/en/node/63561 the OP reported that: - with multiple voices, stems in TAB did not follow the "Stems above / below" setting; - extra space was allocated for voice 2 stems. Both issues were more apparent than real, because the example provided casually had no stems in voice 2 (all whole notes). With the commit https://github.com/musescore/MuseScore/commit/33797cd6c55490cd67a3267e5e7e09f409e61dbf : - voice 1 stems are forced to be below or down according to the setting even for multi-voice cases and voice 2 stems on the opposite side; - additional distance is allocated above the TAB staff (but not below) if stems are above or there are multiple voices or below (but not above) if stems are below and there is only voice 1. __Issues__: 1a) The original stem directions were __by design__, assuming that with multiple voices notes of voice 1 tend to be above and notes of voice 2 tend to be below; then it is more sensible to put voice 1 stems above and voice 2 stems below, limiting the "Stems above / below" setting to the single voice case only. 1b) Stem direction controls slurs and tie placement and users are complaining that, with multiple voices, stems, slurs and ties are placed unexpectedly. See issue https://musescore.org/en/node/69846 and forum post https://musescore.org/en/node/69821 . 2) About additional staff distance allocation, with multiple voices it has to be allocated __both__ above and below, as stems may appear both above and below. __Fix__: 1) This patch fixes 1) by restoring the original stem direction computation (single voice: follow "Stems above / below" setting | multiple voices: voice 1 unconditionally above and voice 2 below) when tab is configured to have stems beside the staff and also when it is configured to have __no stems at all_ (so that slurs and ties occur in expected positions). 2) It corrects additional staff distance allocation for the multiple voice case. No resources are spent to check the odd case when one voice casually has no stems over the entire system (in which case, additional distance on that side could in theory be spared).
2015-07-26 20:55:02 +02:00
if (mstaff(staffIdx)->hasVoices) { // reserve space both above and below
distBelow = (STAFFTYPE_TAB_DEFAULTSTEMLEN_UP + STAFFTYPE_TAB_DEFAULTSTEMDIST_UP)*_spatium;
distAbove = (STAFFTYPE_TAB_DEFAULTSTEMLEN_DN + STAFFTYPE_TAB_DEFAULTSTEMDIST_DN)*_spatium;
Fix #69846, #69821, #63561 - multi-voice TAB's: stem and slur positions __Background__: In the original TAB implementation, the "Stems above / below" setting was followed when there was only one voice, while with multiple voices, voice 1 stems were always above and voice 2 stems always below. In issue https://musescore.org/en/node/63561 the OP reported that: - with multiple voices, stems in TAB did not follow the "Stems above / below" setting; - extra space was allocated for voice 2 stems. Both issues were more apparent than real, because the example provided casually had no stems in voice 2 (all whole notes). With the commit https://github.com/musescore/MuseScore/commit/33797cd6c55490cd67a3267e5e7e09f409e61dbf : - voice 1 stems are forced to be below or down according to the setting even for multi-voice cases and voice 2 stems on the opposite side; - additional distance is allocated above the TAB staff (but not below) if stems are above or there are multiple voices or below (but not above) if stems are below and there is only voice 1. __Issues__: 1a) The original stem directions were __by design__, assuming that with multiple voices notes of voice 1 tend to be above and notes of voice 2 tend to be below; then it is more sensible to put voice 1 stems above and voice 2 stems below, limiting the "Stems above / below" setting to the single voice case only. 1b) Stem direction controls slurs and tie placement and users are complaining that, with multiple voices, stems, slurs and ties are placed unexpectedly. See issue https://musescore.org/en/node/69846 and forum post https://musescore.org/en/node/69821 . 2) About additional staff distance allocation, with multiple voices it has to be allocated __both__ above and below, as stems may appear both above and below. __Fix__: 1) This patch fixes 1) by restoring the original stem direction computation (single voice: follow "Stems above / below" setting | multiple voices: voice 1 unconditionally above and voice 2 below) when tab is configured to have stems beside the staff and also when it is configured to have __no stems at all_ (so that slurs and ties occur in expected positions). 2) It corrects additional staff distance allocation for the multiple voice case. No resources are spent to check the odd case when one voice casually has no stems over the entire system (in which case, additional distance on that side could in theory be spared).
2015-07-26 20:55:02 +02:00
}
else { // if no voices, reserve space on stem side
if (stt->stemsDown())
distBelow = (STAFFTYPE_TAB_DEFAULTSTEMLEN_UP + STAFFTYPE_TAB_DEFAULTSTEMDIST_UP)*_spatium;
else
distAbove = (STAFFTYPE_TAB_DEFAULTSTEMLEN_DN + STAFFTYPE_TAB_DEFAULTSTEMDIST_DN)*_spatium;
}
}
2012-05-26 14:26:10 +02:00
if (distAbove > staves[staffIdx]->distanceUp)
staves[staffIdx]->distanceUp = distAbove;
if (distBelow > staves[staffIdx]->distanceDown)
staves[staffIdx]->distanceDown = distBelow;
2012-05-26 14:26:10 +02:00
}
}
2012-05-26 14:26:10 +02:00
qreal segmentWidth = 0.0;
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
2012-05-26 14:26:10 +02:00
segmentWidth = qMax(segmentWidth, rest[staffIdx]);
segmentWidth = qMax(segmentWidth, hRest[staffIdx]);
}
if (segmentIdx)
width[segmentIdx-1] = segmentWidth;
2012-05-26 14:26:10 +02:00
xpos[segmentIdx] = x + segmentWidth;
//---------------------------------------------------
// compute stretches for whole measure
2012-05-26 14:26:10 +02:00
//---------------------------------------------------
computeStretch(minTick, xpos[0], stretch, 0, segs, ticksList, xpos, width);
2012-05-26 14:26:10 +02:00
//---------------------------------------------------
// layout individual elements
//---------------------------------------------------
int seg = 0;
for (Segment* s = first(); s; s = s->next(), ++seg) {
if ((s->segmentType() == Segment::Type::Clef) && (s != first())) {
2013-07-18 15:54:05 +02:00
//
// clefs are not in xpos[] table
//
2012-05-26 14:26:10 +02:00
s->setPos(xpos[seg], 0.0);
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
if (!score()->staff(staffIdx)->show())
continue;
2012-05-26 14:26:10 +02:00
int track = staffIdx * VOICES;
Element* e = s->element(track);
if (e) {
qreal lm = 0.0;
if (s->next()) {
for (int track = staffIdx * VOICES; track < staffIdx*VOICES+VOICES; ++track) {
if (s->next()->element(track)) {
qreal clm = s->next()->element(track)->space().lw();
lm = qMax(lm, clm);
}
}
}
e->setPos(-e->width() - lm - _spatium*.5, 0.0);
e->adjustReadPos();
}
}
--seg;
continue;
}
s->setPos(xpos[seg], 0.0);
2013-07-18 15:54:05 +02:00
}
2012-05-26 14:26:10 +02:00
2013-07-18 15:54:05 +02:00
for (Segment* s = first(); s; s = s->next(), ++seg) {
qreal barLineWidth = 0.0;
2012-05-26 14:26:10 +02:00
for (int track = 0; track < tracks; ++track) {
if (!score()->staff(track/VOICES)->show()) {
track += VOICES-1;
continue;
}
2012-05-26 14:26:10 +02:00
Element* e = s->element(track);
if (e == 0)
continue;
Element::Type t = e->type();
Rest* rest = static_cast<Rest*>(e);
if (t == Element::Type::REPEAT_MEASURE || (t == Element::Type::REST && (isMMRest() || rest->isFullMeasureRest()))) {
//
// element has to be centered in free space
// x1 - left measure position of free space
// x2 - right measure position of free space
2013-09-10 15:11:30 +02:00
if (isMMRest()) {
//
// center multi measure rest
//
qreal x1 = 0.0, x2;
2013-09-19 17:17:22 +02:00
Segment* ss = first();
Segment* ps = nullptr;
for (; ss->segmentType() != Segment::Type::ChordRest; ss = ss->next()) {
if (!ss->isEmpty())
ps = ss;
}
if (ps) {
ss = ps;
2013-09-19 17:17:22 +02:00
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Element* e = ss->element(track);
if (e)
x1 = qMax(x1, ss->x() + e->x() + e->width());
}
}
Segment* ns = s->next();
x2 = this->width();
if (ns) {
x2 = ns->x();
if (ns->segmentType() != Segment::Type::EndBarLine) {
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Element* e = ns->element(track);
if (e)
x2 = qMin(x2, ns->x() + e->x());
}
}
}
2013-07-18 15:54:05 +02:00
2014-05-26 15:31:36 +02:00
qreal d = point(score()->styleS(StyleIdx::multiMeasureRestMargin));
qreal w = x2 - x1 - 2 * d;
rest->setMMWidth(w);
StaffLines* sl = staves[track/VOICES]->lines;
qreal x = x1 - s->pos().x() + d;
e->setPos(x, sl->staffHeight() * .5); // center vertically in measure
}
2013-09-19 16:27:29 +02:00
else { // if (rest->isFullMeasureRest()) {
//
// center full measure rest
//
qreal x1 = 0.0;
qreal x2 = this->width();
2013-09-19 15:49:39 +02:00
Segment* ss = first();
Segment* ps = nullptr;
for (; ss->segmentType() != Segment::Type::ChordRest; ss = ss->next()) {
if (!ss->isEmpty())
ps = ss;
}
if (ps) {
ss = ps;
2013-09-19 15:49:39 +02:00
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Element* e = ss->element(track);
if (e)
x1 = qMax(x1, ss->x() + e->x() + e->width());
}
2012-05-26 14:26:10 +02:00
}
Segment* ns = s->next();
while (ns && ns->segmentType() != Segment::Type::EndBarLine) {
ns = ns->next();
}
if (ns)
x2 = ns->x();
rest->rxpos() = (x2 - x1 - e->width()) * .5 + x1 - s->x() - e->bbox().x();
rest->adjustReadPos();
2012-05-26 14:26:10 +02:00
}
}
else if (t == Element::Type::REST)
2013-07-18 15:54:05 +02:00
e->rxpos() = 0;
else if (t == Element::Type::CHORD)
static_cast<Chord*>(e)->layout2();
else if (t == Element::Type::CLEF) {
2013-07-18 15:54:05 +02:00
if (s == first()) {
// clef at the beginning of measure
qreal gap = 0.0;
Segment* ps = s->prev();
if (ps)
gap = s->x() - (ps->x() + ps->width());
e->rxpos() = -gap * .5;
e->adjustReadPos();
}
2012-05-26 14:26:10 +02:00
}
else if (t == Element::Type::BAR_LINE) {
e->setPos(QPointF());
barLineWidth = qMax(barLineWidth, e->width());
e->adjustReadPos();
}
2012-05-26 14:26:10 +02:00
else {
if (t != Element::Type::AMBITUS)
e->setPos(-e->bbox().x(), 0.0);
2012-05-26 14:26:10 +02:00
e->adjustReadPos();
}
}
// align bar lines
if (s->segmentType() == Segment::Type::EndBarLine)
for (int track = 0; track < tracks; ++track)
if (s->element(track)) {
BarLine* bl = static_cast<BarLine*>(s->element(track));
qreal delta = barLineWidth - bl->width();
// right-align end bar lines, end-repeat bar lines and bar lines in last system measure
if (bl->barLineType() == BarLineType::END || bl->barLineType() == BarLineType::END_REPEAT
|| this == system()->lastMeasure())
bl->rxpos() = delta;
// centre-align all other bar lines except start-repeat
else if (bl->barLineType() != BarLineType::START_REPEAT)
bl->rxpos() = delta * 0.5;
// leave start-repeat left aligned
}
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// layoutStage1
2013-09-19 17:17:22 +02:00
// compute multi measure rest break
// call layoutCR0 for every chord/rest
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Measure::layoutStage1()
{
2012-08-24 18:00:12 +02:00
setDirty();
bool mmrests = score()->styleB(StyleIdx::createMultiMeasureRests);
setBreakMMRest(false);
2012-05-26 14:26:10 +02:00
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
2015-02-19 10:28:25 +01:00
AccidentalState as; // list of already set accidentals for this measure
Staff* staff = score()->staff(staffIdx);
as.init(staff->key(tick()));
if (mmrests) {
2014-07-24 12:37:08 +02:00
if (
(repeatFlags() & Repeat::START)
|| (prevMeasure() && (prevMeasure()->repeatFlags() & Repeat::END))
|| (prevMeasure() && (prevMeasure()->sectionBreak()))
)
2012-05-26 14:26:10 +02:00
setBreakMMRest(true);
2013-09-19 17:17:22 +02:00
else if (!breakMultiMeasureRest()) {
2012-05-26 14:26:10 +02:00
for (Segment* s = first(); s; s = s->next()) {
2013-10-15 15:39:01 +02:00
for (Element* e : s->annotations()) {
2014-03-03 14:54:53 +01:00
if (score()->staff(e->staffIdx())->show() || e->systemFlag()) {
2012-05-26 14:26:10 +02:00
setBreakMMRest(true);
break;
}
}
2013-09-19 17:17:22 +02:00
if (breakMultiMeasureRest()) // optimize
2012-05-26 14:26:10 +02:00
break;
}
}
}
2015-02-19 10:28:25 +01:00
int track = staffIdx * VOICES;
int endTrack = track + VOICES;
2012-05-26 14:26:10 +02:00
for (Segment* segment = first(); segment; segment = segment->next()) {
2014-03-03 14:54:53 +01:00
if (score()->staff(staffIdx)->show()) {
Element* e = segment->element(track);
if (e && !e->generated()) {
if (segment->segmentType() & (Segment::Type::StartRepeatBarLine))
2014-03-03 14:54:53 +01:00
setBreakMMRest(true);
if (segment->segmentType() & (Segment::Type::KeySig | Segment::Type::TimeSig) && tick())
2013-09-19 17:39:18 +02:00
setBreakMMRest(true);
else if (segment->segmentType() == Segment::Type::Clef) {
if (segment->tick() != endTick() && tick())
2014-03-03 14:54:53 +01:00
setBreakMMRest(true);
}
2013-09-19 17:39:18 +02:00
}
2012-05-26 14:26:10 +02:00
}
if (segment->segmentType() == Segment::Type::KeySig) {
KeySig* ks = static_cast<KeySig*>(segment->element(staffIdx * VOICES));
if (!ks)
continue;
as.init(staff->key(segment->tick()));
}
else if (segment->segmentType() == Segment::Type::ChordRest) {
2013-10-15 15:39:01 +02:00
Staff* staff = score()->staff(staffIdx);
qreal staffMag = staff->mag();
for (int t = track; t < endTrack; ++t) {
ChordRest* cr = static_cast<ChordRest*>(segment->element(t));
if (!cr)
continue;
2015-02-19 10:28:25 +01:00
layoutCR0(cr, staffMag, &as);
2013-10-15 15:39:01 +02:00
}
}
2012-05-26 14:26:10 +02:00
}
}
if (!mmrests || breakMultiMeasureRest())
2013-10-15 15:39:01 +02:00
return;
Measure* pm = prevMeasure();
if (pm) {
if (pm->endBarLineType() != BarLineType::NORMAL
&& pm->endBarLineType() != BarLineType::BROKEN
&& pm->endBarLineType() != BarLineType::DOTTED) {
setBreakMMRest(true);
return;
}
if (pm->findSegment(Segment::Type::Clef, tick()))
setBreakMMRest(true);
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// stretchedLen
//---------------------------------------------------------
Fraction Measure::stretchedLen(Staff* staff) const
{
2014-07-29 18:48:57 +02:00
return len() * staff->timeStretch(tick());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cloneMeasure
//---------------------------------------------------------
2013-07-05 11:23:52 +02:00
Measure* Measure::cloneMeasure(Score* sc, TieMap* tieMap)
2012-05-26 14:26:10 +02:00
{
Measure* m = new Measure(sc);
m->_timesig = _timesig;
m->_len = _len;
m->_repeatCount = _repeatCount;
m->_repeatFlags = _repeatFlags;
foreach(MStaff* ms, staves)
m->staves.append(new MStaff(*ms));
m->_no = _no;
m->_noOffset = _noOffset;
m->_userStretch = _userStretch;
m->_irregular = _irregular;
m->_breakMultiMeasureRest = _breakMultiMeasureRest;
m->_breakMMRest = _breakMMRest;
m->_endBarLineGenerated = _endBarLineGenerated;
m->_endBarLineVisible = _endBarLineVisible;
m->_endBarLineType = _endBarLineType;
m->_playbackCount = _playbackCount;
m->_endBarLineColor = _endBarLineColor;
m->_minWidth1 = _minWidth1;
m->_minWidth2 = _minWidth2;
2012-05-26 14:26:10 +02:00
m->setTick(tick());
m->setLineBreak(lineBreak());
m->setPageBreak(pageBreak());
m->setSectionBreak(sectionBreak() ? new LayoutBreak(*sectionBreak()) : 0);
int tracks = sc->nstaves() * VOICES;
TupletMap tupletMap;
for (Segment* oseg = first(); oseg; oseg = oseg->next()) {
Segment* s = new Segment(m);
s->setSegmentType(oseg->segmentType());
2012-05-26 14:26:10 +02:00
s->setRtick(oseg->rtick());
m->_segments.push_back(s);
for (int track = 0; track < tracks; ++track) {
Element* oe = oseg->element(track);
2014-08-13 17:45:53 +02:00
foreach (Element* e, oseg->annotations()) {
2012-05-26 14:26:10 +02:00
if (e->generated() || e->track() != track)
continue;
Element* ne = e->clone();
ne->setTrack(track);
ne->setUserOff(e->userOff());
2014-11-10 13:33:32 +01:00
ne->setScore(sc);
2012-05-26 14:26:10 +02:00
s->add(ne);
}
2014-08-13 17:45:53 +02:00
if (!oe)
continue;
Element* ne = oe->clone();
if (oe->isChordRest()) {
ChordRest* ocr = static_cast<ChordRest*>(oe);
ChordRest* ncr = static_cast<ChordRest*>(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);
tupletMap.add(ot, nt);
}
ncr->setTuplet(nt);
nt->add(ncr);
}
if (oe->type() == Element::Type::CHORD) {
Chord* och = static_cast<Chord*>(ocr);
Chord* nch = static_cast<Chord*>(ncr);
int n = och->notes().size();
for (int i = 0; i < n; ++i) {
Note* on = och->notes().at(i);
Note* nn = nch->notes().at(i);
if (on->tieFor()) {
2014-11-25 16:53:43 +01:00
Tie* tie = on->tieFor()->clone();
tie->setScore(sc);
2014-08-13 17:45:53 +02:00
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->setUserOff(oe->userOff());
2014-11-10 13:33:32 +01:00
ne->setScore(sc);
2014-08-13 17:45:53 +02:00
s->add(ne);
2012-05-26 14:26:10 +02:00
}
}
foreach(Element* e, el()) {
2012-05-26 14:26:10 +02:00
Element* ne = e->clone();
ne->setScore(sc);
ne->setUserOff(e->userOff());
2012-05-26 14:26:10 +02:00
m->add(ne);
}
return m;
}
//---------------------------------------------------------
// pos2sel
//---------------------------------------------------------
int Measure::snap(int 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
//---------------------------------------------------------
int Measure::snapNote(int /*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();
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Measure::getProperty(P_ID propertyId) const
{
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::TIMESIG_NOMINAL:
return QVariant::fromValue(_timesig);
2014-05-26 18:18:01 +02:00
case P_ID::TIMESIG_ACTUAL:
return QVariant::fromValue(_len);
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_FLAGS:
return int(repeatFlags());
2014-05-26 18:18:01 +02:00
case P_ID::MEASURE_NUMBER_MODE:
return int(measureNumberMode());
2014-05-26 18:18:01 +02:00
case P_ID::BREAK_MMR:
2015-03-30 12:38:30 +02:00
return getBreakMultiMeasureRest();
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_COUNT:
2014-05-20 17:26:26 +02:00
return repeatCount();
2014-05-26 18:18:01 +02:00
case P_ID::USER_STRETCH:
2014-05-20 17:26:26 +02:00
return userStretch();
2014-05-26 18:18:01 +02:00
case P_ID::NO_OFFSET:
2014-05-20 17:26:26 +02:00
return noOffset();
2014-05-26 18:18:01 +02:00
case P_ID::IRREGULAR:
2014-05-20 17:26:26 +02:00
return irregular();
2014-12-08 13:36:52 +01:00
case P_ID::SYSTEM_INITIAL_BARLINE_TYPE:
return int(systemInitialBarLineType());
default:
return MeasureBase::getProperty(propertyId);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Measure::setProperty(P_ID propertyId, const QVariant& value)
{
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::TIMESIG_NOMINAL:
_timesig = value.value<Fraction>();
break;
2014-05-26 18:18:01 +02:00
case P_ID::TIMESIG_ACTUAL:
_len = value.value<Fraction>();
break;
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_FLAGS:
setRepeatFlags(Repeat(value.toInt()));
break;
2014-05-26 18:18:01 +02:00
case P_ID::MEASURE_NUMBER_MODE:
setMeasureNumberMode(MeasureNumberMode(value.toInt()));
break;
2014-05-26 18:18:01 +02:00
case P_ID::BREAK_MMR:
2014-05-20 17:26:26 +02:00
setBreakMultiMeasureRest(value.toBool());
break;
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_COUNT:
2014-05-20 17:26:26 +02:00
setRepeatCount(value.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::USER_STRETCH:
2014-05-20 17:26:26 +02:00
setUserStretch(value.toDouble());
break;
2014-05-26 18:18:01 +02:00
case P_ID::NO_OFFSET:
2014-05-20 17:26:26 +02:00
setNoOffset(value.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::IRREGULAR:
2014-05-20 17:26:26 +02:00
setIrregular(value.toBool());
break;
2014-12-08 13:36:52 +01:00
case P_ID::SYSTEM_INITIAL_BARLINE_TYPE:
setSystemInitialBarLineType(BarLineType(value.toInt()));
break;
default:
return MeasureBase::setProperty(propertyId, value);
}
2014-05-20 17:26:26 +02:00
score()->setLayoutAll(true);
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Measure::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::TIMESIG_NOMINAL:
case P_ID::TIMESIG_ACTUAL:
return QVariant();
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_FLAGS:
return 0;
2014-05-26 18:18:01 +02:00
case P_ID::MEASURE_NUMBER_MODE:
return int(MeasureNumberMode::AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::BREAK_MMR:
2014-05-20 17:26:26 +02:00
return false;
2014-05-26 18:18:01 +02:00
case P_ID::REPEAT_COUNT:
2014-05-20 17:26:26 +02:00
return 2;
2014-05-26 18:18:01 +02:00
case P_ID::USER_STRETCH:
2014-05-20 17:26:26 +02:00
return 1.0;
2014-05-26 18:18:01 +02:00
case P_ID::NO_OFFSET:
2014-05-20 17:26:26 +02:00
return 0;
2014-05-26 18:18:01 +02:00
case P_ID::IRREGULAR:
2014-05-20 17:26:26 +02:00
return false;
2014-12-08 13:36:52 +01:00
case P_ID::SYSTEM_INITIAL_BARLINE_TYPE:
return int(BarLineType::NORMAL);
default:
break;
}
return MeasureBase::getProperty(propertyId);
}
2013-10-30 14:21:08 +01:00
//-------------------------------------------------------------------
// 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 static_cast<Measure*>(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 static_cast<Measure*>(next()->prev());
return score()->lastMeasure();
}
2014-04-24 10:42:42 +02:00
//---------------------------------------------------------
// mmRest1
// return the multi measure rest this measure is covered
// by
//---------------------------------------------------------
const Measure* Measure::mmRest1() const
2014-04-24 10:42:42 +02:00
{
if (_mmRest)
return _mmRest;
if (_mmRestCount != -1)
// return const_cast<Measure*>(this);
return this;
2014-04-24 10:42:42 +02:00
const Measure* m = this;
while (m && !m->_mmRest)
m = m->prevMeasure();
if (m)
return const_cast<Measure*>(m->_mmRest);
return 0;
}
2014-05-17 18:40:05 +02:00
//-------------------------------------------------------------------
// userStretch
//-------------------------------------------------------------------
2014-05-20 17:26:26 +02:00
2014-05-17 18:40:05 +02:00
qreal Measure::userStretch() const
{
return (score()->layoutMode() == LayoutMode::FLOAT ? 1.0 : _userStretch);
2014-05-17 18:40:05 +02:00
}
2014-04-24 10:42:42 +02:00
Element* Measure::nextElementStaff(int staff)
{
Segment* firstSeg = segments()->first();
if (firstSeg)
return firstSeg->firstElement(staff);
return score()->firstElement();
}
Element* Measure::prevElementStaff(int staff)
{
Measure* prevM = prevMeasureMM();
if (prevM) {
Segment* seg = prevM->last();
if (seg)
seg->lastElement(staff);
}
return score()->lastElement();
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString Measure::accessibleInfo()
{
return QString("%1: %2").arg(Element::accessibleInfo()).arg(QString::number(no() + 1));
}
2013-05-13 18:49:17 +02:00
}