2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
2016-01-04 14:48:58 +01:00
|
|
|
// Copyright (C) 2002-2016 Werner Schweer
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//=============================================================================
|
|
|
|
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "accidental.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "barline.h"
|
|
|
|
#include "beam.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "box.h"
|
|
|
|
#include "chord.h"
|
|
|
|
#include "clef.h"
|
|
|
|
#include "element.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "fingering.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "glissando.h"
|
|
|
|
#include "harmony.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "keysig.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "layoutbreak.h"
|
|
|
|
#include "layout.h"
|
2012-08-01 18:00:27 +02:00
|
|
|
#include "lyrics.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "marker.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "mscore.h"
|
2014-02-22 11:19:44 +01:00
|
|
|
#include "notedot.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "note.h"
|
|
|
|
#include "ottava.h"
|
|
|
|
#include "page.h"
|
|
|
|
#include "part.h"
|
|
|
|
#include "repeat.h"
|
|
|
|
#include "score.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "sig.h"
|
|
|
|
#include "slur.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "stem.h"
|
|
|
|
#include "style.h"
|
|
|
|
#include "sym.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "text.h"
|
|
|
|
#include "tie.h"
|
|
|
|
#include "timesig.h"
|
2014-05-15 13:42:03 +02:00
|
|
|
#include "tremolo.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
#include "tuplet.h"
|
|
|
|
#include "undo.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "volta.h"
|
2016-01-04 14:48:58 +01:00
|
|
|
#include "breath.h"
|
|
|
|
#include "tempotext.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2014-12-30 21:14:31 +01:00
|
|
|
// #define PAGE_DEBUG
|
|
|
|
|
2015-01-05 13:17:04 +01:00
|
|
|
#ifdef PAGE_DEBUG
|
|
|
|
#define PAGEDBG(...) qDebug(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define PAGEDBG(...) ;
|
|
|
|
#endif
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// LayoutContext
|
|
|
|
// temp values used during layout
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
struct LayoutContext {
|
|
|
|
bool startWithLongNames { true };
|
|
|
|
bool firstSystem { true };
|
|
|
|
int curPage { 0 }; // index in Score->_pages
|
|
|
|
int tick { 0 };
|
|
|
|
Fraction sig;
|
|
|
|
|
|
|
|
QList<System*> systemList; // reusable systems
|
|
|
|
System* curSystem { 0 };
|
|
|
|
|
|
|
|
MeasureBase* prevMeasure { 0 };
|
|
|
|
MeasureBase* curMeasure { 0 };
|
|
|
|
MeasureBase* nextMeasure { 0 };
|
|
|
|
int measureNo { 0 };
|
|
|
|
};
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// rebuildBspTree
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::rebuildBspTree()
|
|
|
|
{
|
2014-11-23 13:34:03 +01:00
|
|
|
for (Page* page : _pages)
|
|
|
|
page->rebuildBspTree();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchNote
|
|
|
|
// search for note or rest before or at tick position tick
|
|
|
|
// in staff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Score::searchNote(int tick, int track) const
|
|
|
|
{
|
|
|
|
ChordRest* ipe = 0;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
2015-02-14 01:18:41 +01:00
|
|
|
for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st)) {
|
2016-02-04 17:06:32 +01:00
|
|
|
ChordRest* cr = segment->cr(track);
|
2012-12-06 09:19:59 +01:00
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
if (cr->tick() == tick)
|
|
|
|
return cr;
|
|
|
|
if (cr->tick() > tick)
|
|
|
|
return ipe ? ipe : cr;
|
|
|
|
ipe = cr;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutChords1
|
2014-03-05 23:45:58 +01:00
|
|
|
// - layout upstem and downstem chords
|
|
|
|
// - offset as necessary to avoid conflict
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutChords1(Segment* segment, int staffIdx)
|
|
|
|
{
|
|
|
|
Staff* staff = Score::staff(staffIdx);
|
|
|
|
|
2013-05-23 15:39:14 +02:00
|
|
|
// if (staff->isDrumStaff() || staff->isTabStaff())
|
|
|
|
if (staff->isTabStaff())
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
|
2014-03-07 03:43:41 +01:00
|
|
|
int upVoices = 0, downVoices = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<Note*> upStemNotes, downStemNotes;
|
2014-04-08 23:18:15 +02:00
|
|
|
qreal nominalWidth = noteHeadWidth() * staff->mag();
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal maxUpWidth = 0.0;
|
|
|
|
qreal maxDownWidth = 0.0;
|
2014-04-08 23:18:15 +02:00
|
|
|
qreal maxUpMag = 0.0;
|
|
|
|
qreal maxDownMag = 0.0;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
2015-01-30 19:33:54 +01:00
|
|
|
// dots and hooks can affect layout of notes as well as vice versa
|
2014-03-08 07:43:24 +01:00
|
|
|
int upDots = 0;
|
|
|
|
int downDots = 0;
|
2015-01-30 19:33:54 +01:00
|
|
|
bool upHooks = false;
|
|
|
|
bool downHooks = false;
|
2015-02-13 19:03:44 +01:00
|
|
|
// also check for grace notes
|
|
|
|
bool upGrace = false;
|
|
|
|
bool downGrace = false;
|
2014-03-08 07:43:24 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
2016-02-04 17:06:32 +01:00
|
|
|
Chord* chord = segment->element(track)->castChord();
|
|
|
|
if (chord) {
|
2015-02-13 19:03:44 +01:00
|
|
|
bool hasGraceBefore = false;
|
2014-03-05 23:45:58 +01:00
|
|
|
for (Chord* c : chord->graceNotes()) {
|
2015-02-13 19:03:44 +01:00
|
|
|
if (c->isGraceBefore())
|
|
|
|
hasGraceBefore = true;
|
2016-02-04 17:06:32 +01:00
|
|
|
layoutChords2(c->notes(), c->up()); // layout grace note noteheads
|
|
|
|
layoutChords3(c->notes(), staff, 0); // layout grace note chords
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2014-03-07 03:43:41 +01:00
|
|
|
if (chord->up()) {
|
|
|
|
++upVoices;
|
2016-02-06 22:03:43 +01:00
|
|
|
upStemNotes.insert(upStemNotes.end(), chord->notes().begin(), chord->notes().end());
|
2016-02-04 17:06:32 +01:00
|
|
|
upDots = qMax(upDots, chord->dots());
|
2014-04-08 23:18:15 +02:00
|
|
|
maxUpMag = qMax(maxUpMag, chord->mag());
|
2015-01-30 19:33:54 +01:00
|
|
|
if (!upHooks)
|
|
|
|
upHooks = chord->hook();
|
2015-02-13 19:03:44 +01:00
|
|
|
if (hasGraceBefore)
|
|
|
|
upGrace = true;
|
2014-03-07 03:43:41 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
++downVoices;
|
2016-02-06 22:03:43 +01:00
|
|
|
downStemNotes.insert(downStemNotes.end(), chord->notes().begin(), chord->notes().end());
|
2014-03-08 07:43:24 +01:00
|
|
|
downDots = qMax(downDots, chord->dots());
|
2014-04-08 23:18:15 +02:00
|
|
|
maxDownMag = qMax(maxDownMag, chord->mag());
|
2015-01-30 19:33:54 +01:00
|
|
|
if (!downHooks)
|
|
|
|
downHooks = chord->hook();
|
2015-02-13 19:03:44 +01:00
|
|
|
if (hasGraceBefore)
|
|
|
|
downGrace = true;
|
2014-03-07 03:43:41 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (upVoices + downVoices) {
|
|
|
|
// TODO: use track as secondary sort criteria?
|
|
|
|
// otherwise there might be issues with unisons between voices
|
|
|
|
// in some corner cases
|
2014-03-05 23:45:58 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
maxUpWidth = nominalWidth * maxUpMag;
|
|
|
|
maxDownWidth = nominalWidth * maxDownMag;
|
2014-04-08 20:52:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// layout upstem noteheads
|
|
|
|
if (upVoices > 1) {
|
|
|
|
qSort(upStemNotes.begin(), upStemNotes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
2015-02-18 07:47:16 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (upVoices) {
|
|
|
|
qreal hw = layoutChords2(upStemNotes, true);
|
|
|
|
maxUpWidth = qMax(maxUpWidth, hw);
|
2015-02-18 07:47:16 +01:00
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// layout downstem noteheads
|
|
|
|
if (downVoices > 1) {
|
|
|
|
qSort(downStemNotes.begin(), downStemNotes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (downVoices) {
|
|
|
|
qreal hw = layoutChords2(downStemNotes, false);
|
|
|
|
maxDownWidth = qMax(maxDownWidth, hw);
|
|
|
|
}
|
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
qreal sp = staff->spatium();
|
|
|
|
qreal upOffset = 0.0; // offset to apply to upstem chords
|
|
|
|
qreal downOffset = 0.0; // offset to apply to downstem chords
|
|
|
|
qreal dotAdjust = 0.0; // additional chord offset to account for dots
|
|
|
|
qreal dotAdjustThreshold = 0.0; // if it exceeds this amount
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
// centering adjustments for whole note, breve, and small chords
|
|
|
|
qreal centerUp = 0.0; // offset to apply in order to center upstem chords
|
|
|
|
qreal oversizeUp = 0.0; // adjustment to oversized upstem chord needed if laid out to the right
|
|
|
|
qreal centerDown = 0.0; // offset to apply in order to center downstem chords
|
|
|
|
qreal centerAdjustUp = 0.0; // adjustment to upstem chord needed after centering donwstem chord
|
|
|
|
qreal centerAdjustDown = 0.0; // adjustment to downstem chord needed after centering upstem chord
|
|
|
|
|
|
|
|
// only center chords if they differ from nominal by at least this amount
|
|
|
|
// this avoids unnecessary centering on differences due only to floating point roundoff
|
|
|
|
// it also allows for the possibility of disabling centering
|
|
|
|
// for notes only "slightly" larger than nominal, like half notes
|
|
|
|
// but this will result in them not being aligned with each other between voices
|
|
|
|
// unless you change to left alignment as described in the comments below
|
|
|
|
qreal centerThreshold = 0.01 * sp;
|
|
|
|
|
|
|
|
// amount by which actual width exceeds nominal, adjusted for staff mag() only
|
|
|
|
qreal headDiff = maxUpWidth - nominalWidth;
|
|
|
|
// amount by which actual width exceeds nominal, adjusted for staff & chord/note mag()
|
|
|
|
qreal headDiff2 = maxUpWidth - nominalWidth * (maxUpMag / staff->mag());
|
|
|
|
if (headDiff > centerThreshold) {
|
|
|
|
// larger than nominal
|
|
|
|
centerUp = headDiff * -0.5;
|
|
|
|
// maxUpWidth is true width, but we no longer will care about that
|
|
|
|
// instead, we care only about portion to right of origin
|
|
|
|
maxUpWidth += centerUp;
|
|
|
|
// to left align rather than center, delete both of the above
|
|
|
|
if (headDiff2 > centerThreshold) {
|
|
|
|
// if max notehead is wider than nominal with chord/note mag() applied
|
|
|
|
// then noteheads extend to left of origin
|
|
|
|
// because stemPosX() is based on nominal width
|
|
|
|
// so we need to correct for that too
|
|
|
|
centerUp += headDiff2;
|
|
|
|
oversizeUp = headDiff2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (-headDiff > centerThreshold) {
|
|
|
|
// smaller than nominal
|
|
|
|
centerUp = -headDiff * 0.5;
|
|
|
|
if (headDiff2 > centerThreshold) {
|
|
|
|
// max notehead is wider than nominal with chord/note mag() applied
|
|
|
|
// perform same adjustment as above
|
|
|
|
centerUp += headDiff2;
|
|
|
|
oversizeUp = headDiff2;
|
|
|
|
}
|
|
|
|
centerAdjustDown = centerUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
headDiff = maxDownWidth - nominalWidth;
|
|
|
|
if (headDiff > centerThreshold) {
|
|
|
|
// larger than nominal
|
|
|
|
centerDown = headDiff * -0.5;
|
|
|
|
// to left align rather than center, change the above to
|
|
|
|
//centerAdjustUp = headDiff;
|
|
|
|
maxDownWidth = nominalWidth - centerDown;
|
|
|
|
}
|
|
|
|
else if (-headDiff > centerThreshold) {
|
|
|
|
// smaller than nominal
|
|
|
|
centerDown = -headDiff * 0.5;
|
|
|
|
centerAdjustUp = centerDown;
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle conflict between upstem and downstem chords
|
|
|
|
|
|
|
|
if (upVoices && downVoices) {
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
Note* bottomUpNote = upStemNotes.front();
|
|
|
|
Note* topDownNote = downStemNotes.back();
|
2016-01-04 14:48:58 +01:00
|
|
|
int separation;
|
|
|
|
if (bottomUpNote->chord()->staffMove() == topDownNote->chord()->staffMove())
|
|
|
|
separation = topDownNote->line() - bottomUpNote->line();
|
|
|
|
else
|
|
|
|
separation = 2; // no conflict
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<Note*> overlapNotes;
|
|
|
|
overlapNotes.reserve(8);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
if (separation == 1) {
|
|
|
|
// second
|
|
|
|
downOffset = maxUpWidth;
|
|
|
|
// align stems if present, leave extra room if not
|
|
|
|
if (topDownNote->chord()->stem() && bottomUpNote->chord()->stem())
|
|
|
|
downOffset -= topDownNote->chord()->stem()->lineWidth();
|
2014-03-05 23:45:58 +01:00
|
|
|
else
|
2016-01-04 14:48:58 +01:00
|
|
|
downOffset += 0.1 * sp;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (separation < 1) {
|
|
|
|
|
|
|
|
// overlap (possibly unison)
|
|
|
|
|
|
|
|
// build list of overlapping notes
|
|
|
|
for (int i = 0, n = upStemNotes.size(); i < n; ++i) {
|
|
|
|
if (upStemNotes[i]->line() >= topDownNote->line() - 1)
|
|
|
|
overlapNotes.append(upStemNotes[i]);
|
|
|
|
else
|
|
|
|
break;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
for (int i = downStemNotes.size() - 1; i >= 0; --i) {
|
|
|
|
if (downStemNotes[i]->line() <= bottomUpNote->line() + 1)
|
|
|
|
overlapNotes.append(downStemNotes[i]);
|
|
|
|
else
|
2014-03-05 23:45:58 +01:00
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
qSort(overlapNotes.begin(), overlapNotes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
|
|
|
|
// determine nature of overlap
|
|
|
|
bool shareHeads = true; // can all overlapping notes share heads?
|
|
|
|
bool matchPending = false; // looking for a unison match
|
|
|
|
bool conflictUnison = false; // unison found
|
|
|
|
bool conflictSecondUpHigher = false; // second found
|
|
|
|
bool conflictSecondDownHigher = false; // second found
|
|
|
|
int lastLine = 1000;
|
|
|
|
Note* p = overlapNotes[0];
|
|
|
|
for (int i = 0, count = overlapNotes.size(); i < count; ++i) {
|
|
|
|
Note* n = overlapNotes[i];
|
|
|
|
NoteHead::Type nHeadType;
|
|
|
|
NoteHead::Type pHeadType;
|
|
|
|
Chord* nchord = n->chord();
|
|
|
|
Chord* pchord = p->chord();
|
|
|
|
if (n->mirror()) {
|
2014-03-05 23:45:58 +01:00
|
|
|
if (separation < 0) {
|
2016-01-04 14:48:58 +01:00
|
|
|
// don't try to share heads if there is any mirroring
|
2014-03-05 23:45:58 +01:00
|
|
|
shareHeads = false;
|
2016-01-04 14:48:58 +01:00
|
|
|
// don't worry about conflicts involving mirrored notes
|
|
|
|
continue;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
int line = n->line();
|
|
|
|
int d = lastLine - line;
|
|
|
|
switch (d) {
|
|
|
|
case 0:
|
|
|
|
// unison
|
|
|
|
conflictUnison = true;
|
|
|
|
matchPending = false;
|
|
|
|
nHeadType = (n->headType() == NoteHead::Type::HEAD_AUTO) ? n->chord()->durationType().headType() : n->headType();
|
|
|
|
pHeadType = (p->headType() == NoteHead::Type::HEAD_AUTO) ? p->chord()->durationType().headType() : p->headType();
|
|
|
|
// the most important rules for sharing noteheads on unisons between voices are
|
|
|
|
// that notes must be one same line with same tpc
|
|
|
|
// noteheads must be unmirrored and of same group
|
|
|
|
// and chords must be same size (or else sharing code won't work)
|
|
|
|
if (n->headGroup() != p->headGroup() || n->tpc() != p->tpc() || n->mirror() || p->mirror() || nchord->small() != pchord->small()) {
|
|
|
|
shareHeads = false;
|
2014-04-22 04:04:43 +02:00
|
|
|
}
|
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
// noteheads are potentially shareable
|
|
|
|
// it is more subjective at this point
|
|
|
|
// current default is to require *either* of the following:
|
|
|
|
// 1) both chords have same number of dots, both have stems, and both noteheads are same type and are full size (automatic match)
|
|
|
|
// or 2) one or more of the noteheads is not of type AUTO, but is explicitly set to match the other (user-forced match)
|
|
|
|
// or 3) exactly one of the noteheads is invisible (user-forced match)
|
|
|
|
// thus user can force notes to be shared despite differing number of dots or either being stemless
|
|
|
|
// by setting one of the notehead types to match the other or by making one notehead invisible
|
|
|
|
// TODO: consider adding a style option, staff properties, or note property to control sharing
|
|
|
|
if ((nchord->dots() != pchord->dots() || !nchord->stem() || !pchord->stem() || nHeadType != pHeadType || n->small() || p->small()) &&
|
|
|
|
((n->headType() == NoteHead::Type::HEAD_AUTO && p->headType() == NoteHead::Type::HEAD_AUTO) || nHeadType != pHeadType) &&
|
|
|
|
(n->visible() == p->visible())) {
|
|
|
|
shareHeads = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// second
|
|
|
|
// trust that this won't be a problem for single unison
|
|
|
|
if (separation < 0) {
|
|
|
|
if (n->chord()->up())
|
|
|
|
conflictSecondUpHigher = true;
|
2014-04-22 04:04:43 +02:00
|
|
|
else
|
2016-01-04 14:48:58 +01:00
|
|
|
conflictSecondDownHigher = true;
|
|
|
|
shareHeads = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// no conflict
|
|
|
|
if (matchPending)
|
|
|
|
shareHeads = false;
|
|
|
|
matchPending = true;
|
|
|
|
}
|
|
|
|
p = n;
|
|
|
|
lastLine = line;
|
|
|
|
}
|
|
|
|
if (matchPending)
|
|
|
|
shareHeads = false;
|
|
|
|
|
|
|
|
// calculate offsets
|
|
|
|
if (shareHeads) {
|
|
|
|
for (int i = overlapNotes.size() - 1; i >= 1; i -= 2) {
|
|
|
|
Note* p = overlapNotes[i-1];
|
|
|
|
Note* n = overlapNotes[i];
|
|
|
|
if (!(p->chord()->isNudged() || n->chord()->isNudged())) {
|
|
|
|
if (p->chord()->dots() == n->chord()->dots()) {
|
|
|
|
// hide one set dots
|
|
|
|
bool onLine = !(p->line() & 1);
|
|
|
|
if (onLine) {
|
|
|
|
// hide dots for lower voice
|
|
|
|
if (p->voice() & 1)
|
|
|
|
p->setDotsHidden(true);
|
|
|
|
else
|
|
|
|
n->setDotsHidden(true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// hide dots for upper voice
|
|
|
|
if (!(p->voice() & 1))
|
|
|
|
p->setDotsHidden(true);
|
|
|
|
else
|
|
|
|
n->setDotsHidden(true);
|
|
|
|
}
|
2014-04-22 04:04:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
// formerly we hid noteheads in an effort to fix playback
|
|
|
|
// but this doesn't work for cases where noteheads cannot be shared
|
|
|
|
// so better to solve the problem elsewhere
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (conflictUnison && separation == 0 && (!downGrace || upGrace))
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
|
|
|
else if (conflictUnison)
|
|
|
|
upOffset = maxDownWidth + 0.3 * sp;
|
|
|
|
else if (conflictSecondUpHigher)
|
|
|
|
upOffset = maxDownWidth + 0.2 * sp;
|
|
|
|
else if ((downHooks && !upHooks) && !(upDots && !downDots))
|
2014-03-08 07:43:24 +01:00
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (conflictSecondDownHigher) {
|
|
|
|
if (downDots && !upDots)
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
|
|
|
else {
|
|
|
|
upOffset = maxDownWidth - 0.2 * sp;
|
|
|
|
if (downHooks)
|
|
|
|
upOffset += 0.3 * sp;
|
|
|
|
}
|
|
|
|
}
|
2015-01-30 19:33:54 +01:00
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
// no direct conflict, so parts can overlap (downstem on left)
|
|
|
|
// just be sure that stems clear opposing noteheads
|
|
|
|
qreal clearLeft = 0.0, clearRight = 0.0;
|
|
|
|
if (topDownNote->chord()->stem())
|
|
|
|
clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
|
|
|
|
if (bottomUpNote->chord()->stem())
|
|
|
|
clearRight = bottomUpNote->chord()->stem()->lineWidth() + qMax(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
|
|
|
|
else
|
|
|
|
downDots = 0; // no need to adjust for dots in this case
|
|
|
|
upOffset = qMax(clearLeft, clearRight);
|
|
|
|
if (downHooks) {
|
|
|
|
// we will need more space to avoid collision with hook
|
|
|
|
// but we won't need as much dot adjustment
|
|
|
|
upOffset = qMax(upOffset, maxDownWidth + 0.1 * sp);
|
|
|
|
dotAdjustThreshold = maxUpWidth - 0.3 * sp;
|
|
|
|
}
|
|
|
|
// if downstem chord is small, don't center
|
|
|
|
// and we might not need as much dot adjustment either
|
|
|
|
if (centerDown > 0.0) {
|
|
|
|
centerDown = 0.0;
|
|
|
|
centerAdjustUp = 0.0;
|
|
|
|
dotAdjustThreshold = (upOffset - maxDownWidth) + maxUpWidth - 0.3 * sp;
|
|
|
|
}
|
2015-01-30 19:33:54 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
2014-03-08 07:43:24 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
// adjust for dots
|
|
|
|
if ((upDots && !downDots) || (downDots && !upDots)) {
|
|
|
|
// only one sets of dots
|
|
|
|
// place between chords
|
|
|
|
int dots;
|
|
|
|
qreal mag;
|
|
|
|
if (upDots) {
|
|
|
|
dots = upDots;
|
|
|
|
mag = maxUpMag;
|
2015-01-30 19:33:54 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
dots = downDots;
|
|
|
|
mag = maxDownMag;
|
2014-08-11 06:57:11 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal dotWidth = segment->symWidth(SymId::augmentationDot);
|
|
|
|
// first dot
|
|
|
|
dotAdjust = point(styleS(StyleIdx::dotNoteDistance)) + dotWidth;
|
|
|
|
// additional dots
|
|
|
|
if (dots > 1)
|
|
|
|
dotAdjust += point(styleS(StyleIdx::dotDotDistance)) * (dots - 1);
|
|
|
|
dotAdjust *= mag;
|
|
|
|
// only by amount over threshold
|
|
|
|
dotAdjust = qMax(dotAdjust - dotAdjustThreshold, 0.0);
|
2014-03-07 03:43:41 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (separation == 1)
|
|
|
|
dotAdjust += 0.1 * sp;
|
2014-03-08 07:43:24 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// apply chord offsets
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
2016-02-04 17:06:32 +01:00
|
|
|
Chord* chord = segment->element(track)->castChord();
|
|
|
|
if (chord) {
|
2016-01-04 14:48:58 +01:00
|
|
|
if (chord->up()) {
|
|
|
|
if (upOffset != 0.0) {
|
|
|
|
chord->rxpos() += upOffset + centerAdjustUp + oversizeUp;
|
|
|
|
if (downDots && !upDots)
|
|
|
|
chord->rxpos() += dotAdjust;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
chord->rxpos() += centerUp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (downOffset != 0.0) {
|
|
|
|
chord->rxpos() += downOffset + centerAdjustDown;
|
|
|
|
if (upDots && !downDots)
|
|
|
|
chord->rxpos() += dotAdjust;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
chord->rxpos() += centerDown;
|
|
|
|
}
|
2014-04-08 23:18:15 +02:00
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// layout chords
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<Note*> notes;
|
2016-01-04 14:48:58 +01:00
|
|
|
if (upVoices)
|
2016-02-06 22:03:43 +01:00
|
|
|
notes.insert(notes.end(), upStemNotes.begin(), upStemNotes.end());
|
2016-01-04 14:48:58 +01:00
|
|
|
if (downVoices)
|
2016-02-06 22:03:43 +01:00
|
|
|
notes.insert(notes.end(), downStemNotes.begin(), downStemNotes.end());
|
2016-01-04 14:48:58 +01:00
|
|
|
if (upVoices + downVoices > 1)
|
|
|
|
qSort(notes.begin(), notes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
layoutChords3(notes, staff, segment);
|
2014-04-08 20:52:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
Element* e = segment->element(track);
|
2016-01-04 14:48:58 +01:00
|
|
|
if (e)
|
|
|
|
e->layout();
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2013-06-10 21:13:04 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutChords2
|
2014-03-09 07:09:40 +01:00
|
|
|
// - determine which notes need mirroring
|
2014-03-16 07:55:39 +01:00
|
|
|
// - this is called once for each stem direction
|
|
|
|
// eg, once for voices 1&3, once for 2&4
|
|
|
|
// with all notes combined and sorted to resemble one chord
|
2014-04-08 20:52:10 +02:00
|
|
|
// - return maximum non-mirrored notehead width
|
2014-03-05 23:45:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
qreal Score::layoutChords2(std::vector<Note*>& notes, bool up)
|
2013-06-10 21:13:04 +02:00
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
int startIdx, endIdx, incIdx;
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal maxWidth = 0.0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
// loop in correct direction so that first encountered notehead wins conflict
|
|
|
|
if (up) {
|
|
|
|
// loop bottom up
|
2012-05-26 14:26:10 +02:00
|
|
|
startIdx = 0;
|
2014-03-05 23:45:58 +01:00
|
|
|
endIdx = notes.size();
|
|
|
|
incIdx = 1;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-03-05 23:45:58 +01:00
|
|
|
// loop top down
|
2012-05-26 14:26:10 +02:00
|
|
|
startIdx = notes.size() - 1;
|
2014-03-05 23:45:58 +01:00
|
|
|
endIdx = -1;
|
|
|
|
incIdx = -1;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-07 05:56:46 +02:00
|
|
|
int ll = 1000; // line of previous note head
|
2014-03-05 23:45:58 +01:00
|
|
|
// hack: start high so first note won't show as conflict
|
2014-05-07 05:56:46 +02:00
|
|
|
bool lvisible = false; // was last note visible?
|
|
|
|
bool mirror = false; // should current note head be mirrored?
|
|
|
|
// value is retained and may be used on next iteration
|
|
|
|
// to track mirror status of previous note
|
2014-03-05 23:45:58 +01:00
|
|
|
bool isLeft = notes[startIdx]->chord()->up(); // is note head on left?
|
2014-05-07 05:56:46 +02:00
|
|
|
int lmove = notes[startIdx]->chord()->staffMove(); // staff offset of last note (for cross-staff beaming)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
for (int idx = startIdx; idx != endIdx; idx += incIdx) {
|
2014-03-05 23:45:58 +01:00
|
|
|
Note* note = notes[idx]; // current note
|
|
|
|
int line = note->line(); // line of current note
|
2012-05-26 14:26:10 +02:00
|
|
|
Chord* chord = note->chord();
|
2014-05-07 05:56:46 +02:00
|
|
|
int move = chord->staffMove(); // staff offset of current note
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
// there is a conflict
|
2014-05-07 05:56:46 +02:00
|
|
|
// if this is same or adjacent line as previous note (and chords are on same staff!)
|
|
|
|
// but no need to do anything about it if either note is invisible
|
|
|
|
bool conflict = (qAbs(ll - line) < 2) && (lmove == move) && note->visible() && lvisible;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
// this note is on opposite side of stem as previous note
|
|
|
|
// if there is a conflict
|
|
|
|
// or if this the first note *after* a conflict
|
|
|
|
if (conflict || (chord->up() != isLeft))
|
2012-05-26 14:26:10 +02:00
|
|
|
isLeft = !isLeft;
|
|
|
|
|
2014-05-07 05:56:46 +02:00
|
|
|
// determine if we would need to mirror current note
|
|
|
|
// to get it to the correct side
|
|
|
|
// this would be needed to get a note to left or downstem or right of upstem
|
|
|
|
// whether or not we actually do this is determined later (based on user mirror property)
|
|
|
|
bool nmirror = (chord->up() != isLeft);
|
|
|
|
|
2014-05-09 18:17:52 +02:00
|
|
|
// by default, notes and dots are not hidden
|
2014-05-07 05:56:46 +02:00
|
|
|
// this may be changed later to allow unisons to share note heads
|
2012-05-26 14:26:10 +02:00
|
|
|
note->setHidden(false);
|
2014-02-22 11:19:44 +01:00
|
|
|
note->setDotsHidden(false);
|
|
|
|
|
2014-05-07 05:56:46 +02:00
|
|
|
// be sure chord position is initialized
|
|
|
|
// chord may be moved to the right later
|
|
|
|
// if there are conflicts between voices
|
2012-05-26 14:26:10 +02:00
|
|
|
chord->rxpos() = 0.0;
|
|
|
|
|
2014-05-07 05:56:46 +02:00
|
|
|
// let user mirror property override the default we calculated
|
2014-06-26 10:53:57 +02:00
|
|
|
if (note->userMirror() == MScore::DirectionH::AUTO) {
|
2012-05-26 14:26:10 +02:00
|
|
|
mirror = nmirror;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mirror = note->chord()->up();
|
2014-06-26 10:53:57 +02:00
|
|
|
if (note->userMirror() == MScore::DirectionH::LEFT)
|
2012-05-26 14:26:10 +02:00
|
|
|
mirror = !mirror;
|
|
|
|
}
|
|
|
|
note->setMirror(mirror);
|
2014-05-07 05:56:46 +02:00
|
|
|
|
|
|
|
// accumulate return value
|
2014-04-08 20:52:10 +02:00
|
|
|
if (!mirror)
|
|
|
|
maxWidth = qMax(maxWidth, note->headWidth());
|
2014-05-07 05:56:46 +02:00
|
|
|
|
|
|
|
// prepare for next iteration
|
2016-02-06 22:03:43 +01:00
|
|
|
lvisible = note->visible();
|
|
|
|
lmove = move;
|
|
|
|
ll = line;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
return maxWidth;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
|
|
|
|
2014-03-12 22:52:59 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// AcEl
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
struct AcEl {
|
|
|
|
Note* note;
|
|
|
|
qreal x; // actual x position of this accidental relative to origin
|
|
|
|
qreal top; // top of accidental bbox relative to staff
|
|
|
|
qreal bottom; // bottom of accidental bbox relative to staff
|
|
|
|
int line; // line of note
|
2014-03-19 06:22:12 +01:00
|
|
|
int next; // index of next accidental of same pitch class (ascending list)
|
2014-03-14 01:19:36 +01:00
|
|
|
qreal width; // width of accidental
|
2014-03-16 07:55:39 +01:00
|
|
|
qreal ascent; // amount (in sp) vertical strokes extend above body
|
|
|
|
qreal descent; // amount (in sp) vertical strokes extend below body
|
|
|
|
qreal rightClear; // amount (in sp) to right of last vertical stroke above body
|
|
|
|
qreal leftClear; // amount (in sp) to left of last vertical stroke below body
|
2014-03-12 22:52:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// resolveAccidentals
|
2014-05-12 19:13:48 +02:00
|
|
|
// lx = calculated position of rightmost edge of left accidental relative to origin
|
2014-03-12 22:52:59 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-14 05:39:02 +02:00
|
|
|
static bool resolveAccidentals(AcEl* left, AcEl* right, qreal& lx, qreal pd, qreal sp)
|
2014-03-12 22:52:59 +01:00
|
|
|
{
|
|
|
|
AcEl* upper;
|
|
|
|
AcEl* lower;
|
|
|
|
if (left->line >= right->line) {
|
|
|
|
upper = right;
|
|
|
|
lower = left;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
upper = left;
|
|
|
|
lower = right;
|
|
|
|
}
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
qreal gap = lower->top - upper->bottom;
|
2014-03-12 22:52:59 +01:00
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
// no conflict at all if there is sufficient vertical gap between accidentals
|
2014-08-10 19:39:52 +02:00
|
|
|
// the arrangement of accidentals into columns assumes accidentals an octave apart *do* clear
|
|
|
|
if (gap >= pd || lower->line - upper->line >= 7)
|
2014-03-12 22:52:59 +01:00
|
|
|
return false;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
2014-05-12 05:00:54 +02:00
|
|
|
qreal allowableOverlap = qMax(upper->descent, lower->ascent) - pd;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
|
|
|
// accidentals that are "close" (small gap or even slight overlap)
|
2014-08-10 19:39:52 +02:00
|
|
|
if (qAbs(gap) <= 0.33 * sp) {
|
2014-03-16 07:55:39 +01:00
|
|
|
// acceptable with slight offset
|
|
|
|
// if one of the accidentals can subsume the overlap
|
|
|
|
// and both accidentals allow it
|
|
|
|
if (-gap <= allowableOverlap && qMin(upper->descent, lower->ascent) > 0.0) {
|
2014-05-12 19:13:48 +02:00
|
|
|
qreal align = qMin(left->width, right->width);
|
2014-03-16 07:55:39 +01:00
|
|
|
lx = qMin(lx, right->x + align - pd);
|
2014-05-12 19:13:48 +02:00
|
|
|
return true;
|
2014-03-16 07:55:39 +01:00
|
|
|
}
|
2014-03-12 22:52:59 +01:00
|
|
|
}
|
|
|
|
|
2014-05-12 19:13:48 +02:00
|
|
|
// amount by which overlapping accidentals will be separated
|
|
|
|
// for example, the vertical stems of two flat signs
|
|
|
|
// these need more space than we would need between non-overlapping accidentals
|
2014-05-14 05:39:02 +02:00
|
|
|
qreal overlapShift = pd * 1.41;
|
2014-05-12 19:13:48 +02:00
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
// accidentals with more significant overlap
|
|
|
|
// acceptable if one accidental can subsume overlap
|
|
|
|
if (left == lower && -gap <= allowableOverlap) {
|
|
|
|
qreal offset = qMax(left->rightClear, right->leftClear);
|
2014-05-12 19:13:48 +02:00
|
|
|
offset = qMin(offset, left->width) - overlapShift;
|
|
|
|
lx = qMin(lx, right->x + offset);
|
|
|
|
return true;
|
2014-03-12 22:52:59 +01:00
|
|
|
}
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
// accidentals with even more overlap
|
|
|
|
// can work if both accidentals can subsume overlap
|
2014-05-12 05:00:54 +02:00
|
|
|
if (left == lower && -gap <= upper->descent + lower->ascent - pd) {
|
2014-05-12 19:13:48 +02:00
|
|
|
qreal offset = qMin(left->rightClear, right->leftClear) - overlapShift;
|
2014-03-16 07:55:39 +01:00
|
|
|
if (offset > 0.0) {
|
2014-05-12 19:13:48 +02:00
|
|
|
lx = qMin(lx, right->x + offset);
|
|
|
|
return true;
|
2014-03-16 07:55:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, there is real conflict
|
2014-05-12 19:13:48 +02:00
|
|
|
lx = qMin(lx, right->x - pd);
|
2014-03-12 22:52:59 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-14 01:19:36 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutAccidental
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
static qreal layoutAccidental(AcEl* me, AcEl* above, AcEl* below, qreal colOffset, QVector<Note*>& leftNotes, qreal pnd, qreal pd, qreal sp)
|
2014-03-14 01:19:36 +01:00
|
|
|
{
|
2014-03-19 06:22:12 +01:00
|
|
|
qreal lx = colOffset;
|
2014-08-10 19:39:52 +02:00
|
|
|
Accidental* acc = me->note->accidental();
|
|
|
|
qreal mag = acc->mag();
|
|
|
|
pnd *= mag;
|
|
|
|
pd *= mag;
|
2014-03-14 01:19:36 +01:00
|
|
|
|
|
|
|
// extra space for ledger lines
|
2014-03-14 22:50:25 +01:00
|
|
|
if (me->line <= -2 || me->line >= me->note->staff()->lines() * 2)
|
2014-03-19 06:22:12 +01:00
|
|
|
lx = qMin(lx, -0.2 * sp);
|
2014-03-14 01:19:36 +01:00
|
|
|
|
|
|
|
// clear left notes
|
|
|
|
int lns = leftNotes.size();
|
|
|
|
for (int i = 0; i < lns; ++i) {
|
|
|
|
Note* ln = leftNotes[i];
|
|
|
|
int lnLine = ln->line();
|
2014-03-16 07:55:39 +01:00
|
|
|
qreal lnTop = (lnLine - 1) * 0.5 * sp;
|
2014-03-14 01:19:36 +01:00
|
|
|
qreal lnBottom = lnTop + sp;
|
2014-03-16 07:55:39 +01:00
|
|
|
if (me->top - lnBottom <= pnd && lnTop - me->bottom <= pnd) {
|
2014-03-14 01:19:36 +01:00
|
|
|
// undercut note above if possible
|
2014-05-12 05:00:54 +02:00
|
|
|
if (lnBottom - me->top <= me->ascent - pnd)
|
2014-08-10 19:39:52 +02:00
|
|
|
lx = qMin(lx, ln->x() + ln->chord()->x() + me->rightClear);
|
2014-03-14 01:19:36 +01:00
|
|
|
else
|
2014-08-10 19:39:52 +02:00
|
|
|
lx = qMin(lx, ln->x() + ln->chord()->x());
|
2014-03-14 01:19:36 +01:00
|
|
|
}
|
|
|
|
else if (lnTop > me->bottom)
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-03-14 01:19:36 +01:00
|
|
|
// clear other accidentals
|
|
|
|
bool conflictAbove = false;
|
|
|
|
bool conflictBelow = false;
|
|
|
|
|
|
|
|
if (above)
|
2014-05-14 05:39:02 +02:00
|
|
|
conflictAbove = resolveAccidentals(me, above, lx, pd, sp);
|
2014-03-14 01:19:36 +01:00
|
|
|
if (below)
|
2014-05-14 05:39:02 +02:00
|
|
|
conflictBelow = resolveAccidentals(me, below, lx, pd, sp);
|
2014-05-12 19:13:48 +02:00
|
|
|
if (conflictAbove || conflictBelow)
|
|
|
|
me->x = lx - acc->width() - acc->bbox().x();
|
|
|
|
else if (colOffset != 0.0)
|
2014-08-10 19:39:52 +02:00
|
|
|
me->x = lx - pd - acc->width() - acc->bbox().x();
|
2014-03-14 01:19:36 +01:00
|
|
|
else
|
2014-08-10 19:39:52 +02:00
|
|
|
me->x = lx - pnd - acc->width() - acc->bbox().x();
|
2014-03-19 06:22:12 +01:00
|
|
|
|
|
|
|
return me->x;
|
2014-03-14 01:19:36 +01:00
|
|
|
}
|
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutChords3
|
|
|
|
// - calculate positions of notes, accidentals, dots
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
void Score::layoutChords3(std::vector<Note*>& notes, Staff* staff, Segment* segment)
|
2014-03-05 23:45:58 +01:00
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------
|
|
|
|
// layout accidentals
|
|
|
|
// find column for dots
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<Note*> leftNotes; // notes to left of origin
|
|
|
|
leftNotes.reserve(8);
|
|
|
|
QVector<AcEl> aclist; // accidentals
|
|
|
|
aclist.reserve(8);
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
// track columns of octave-separated accidentals
|
|
|
|
int columnBottom[7] = { -1, -1, -1, -1, -1, -1, -1 };
|
2013-05-22 14:12:47 +02:00
|
|
|
|
2014-03-12 22:52:59 +01:00
|
|
|
qreal sp = staff->spatium();
|
2015-09-01 06:19:20 +02:00
|
|
|
qreal stepDistance = sp * staff->logicalLineDistance() * .5;
|
2013-05-29 12:21:42 +02:00
|
|
|
int stepOffset = staff->staffType()->stepOffset();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-03-08 07:43:24 +01:00
|
|
|
qreal lx = 10000.0; // leftmost note head position
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal upDotPosX = 0.0;
|
|
|
|
qreal downDotPosX = 0.0;
|
2013-06-27 15:00:08 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
int nNotes = notes.size();
|
2014-03-19 06:22:12 +01:00
|
|
|
int nAcc = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int i = nNotes-1; i >= 0; --i) {
|
|
|
|
Note* note = notes[i];
|
|
|
|
Accidental* ac = note->accidental();
|
2014-11-15 21:28:16 +01:00
|
|
|
if (ac && !note->fixed()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ac->layout();
|
|
|
|
AcEl acel;
|
2014-03-12 22:52:59 +01:00
|
|
|
acel.note = note;
|
2014-03-19 06:22:12 +01:00
|
|
|
int line = note->line();
|
|
|
|
acel.line = line;
|
2014-03-12 22:52:59 +01:00
|
|
|
acel.x = 0.0;
|
2014-03-19 06:22:12 +01:00
|
|
|
acel.top = line * 0.5 * sp + ac->bbox().top();
|
|
|
|
acel.bottom = line * 0.5 * sp + ac->bbox().bottom();
|
2014-03-16 07:55:39 +01:00
|
|
|
acel.width = ac->width();
|
2014-05-12 05:00:54 +02:00
|
|
|
QPointF bboxNE = ac->symBbox(ac->symbol()).topRight();
|
|
|
|
QPointF bboxSW = ac->symBbox(ac->symbol()).bottomLeft();
|
|
|
|
QPointF cutOutNE = ac->symCutOutNE(ac->symbol());
|
|
|
|
QPointF cutOutSW = ac->symCutOutSW(ac->symbol());
|
|
|
|
if (!cutOutNE.isNull()) {
|
|
|
|
acel.ascent = cutOutNE.y() - bboxNE.y();
|
|
|
|
acel.rightClear = bboxNE.x() - cutOutNE.x();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
acel.ascent = 0.0;
|
|
|
|
acel.rightClear = 0.0;
|
|
|
|
}
|
|
|
|
if (!cutOutSW.isNull()) {
|
|
|
|
acel.descent = bboxSW.y() - cutOutSW.y();
|
|
|
|
acel.leftClear = cutOutSW.x() - bboxSW.x();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
acel.descent = 0.0;
|
|
|
|
acel.leftClear = 0.0;
|
|
|
|
}
|
2014-03-19 06:22:12 +01:00
|
|
|
int pitchClass = (line + 700) % 7;
|
|
|
|
acel.next = columnBottom[pitchClass];
|
|
|
|
columnBottom[pitchClass] = nAcc;
|
|
|
|
aclist.append(acel);
|
|
|
|
++nAcc;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-05-22 14:12:47 +02:00
|
|
|
|
2014-09-08 08:38:06 +02:00
|
|
|
qreal hw = note->headWidth(); // actual head width, including note & chord mag
|
2013-05-22 14:12:47 +02:00
|
|
|
Chord* chord = note->chord();
|
2013-06-04 18:29:14 +02:00
|
|
|
bool _up = chord->up();
|
2014-09-08 08:38:06 +02:00
|
|
|
qreal stemX = chord->stemPosX(); // stem position for nominal notehead, but allowing for mag
|
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal overlapMirror;
|
2013-05-22 14:12:47 +02:00
|
|
|
if (chord->stem()) {
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal stemWidth = chord->stem()->lineWidth();
|
|
|
|
qreal stemWidth5 = stemWidth * 0.5;
|
2013-09-18 22:05:17 +02:00
|
|
|
chord->stem()->rxpos() = _up ? stemX - stemWidth5 : stemWidth5;
|
2014-04-08 20:52:10 +02:00
|
|
|
overlapMirror = stemWidth;
|
2013-05-22 14:12:47 +02:00
|
|
|
}
|
2014-06-25 22:01:26 +02:00
|
|
|
else if (chord->durationType().headType() == NoteHead::Type::HEAD_WHOLE)
|
2014-05-26 15:31:36 +02:00
|
|
|
overlapMirror = styleD(StyleIdx::stemWidth) * chord->mag() * sp;
|
2013-05-22 14:12:47 +02:00
|
|
|
else
|
2014-04-08 20:52:10 +02:00
|
|
|
overlapMirror = 0.0;
|
2013-05-22 14:12:47 +02:00
|
|
|
|
|
|
|
qreal x;
|
|
|
|
if (note->mirror()) {
|
|
|
|
if (_up)
|
2014-04-08 20:52:10 +02:00
|
|
|
x = stemX - overlapMirror;
|
2013-05-22 14:12:47 +02:00
|
|
|
else
|
2014-04-08 20:52:10 +02:00
|
|
|
x = stemX - hw + overlapMirror;
|
2013-05-22 14:12:47 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-04-08 20:52:10 +02:00
|
|
|
if (_up)
|
|
|
|
x = stemX - hw;
|
|
|
|
else
|
|
|
|
x = 0.0;
|
2013-05-22 14:12:47 +02:00
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
|
2013-05-22 14:12:47 +02:00
|
|
|
note->rypos() = (note->line() + stepOffset) * stepDistance;
|
|
|
|
note->rxpos() = x;
|
2014-05-21 00:43:28 +02:00
|
|
|
// we need to do this now
|
|
|
|
// or else note pos / readPos / userOff will be out of sync
|
|
|
|
// and we rely on note->x() throughout the layout process
|
|
|
|
note->adjustReadPos();
|
2014-03-12 22:52:59 +01:00
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
// find leftmost non-mirrored note to set as X origin for accidental layout
|
|
|
|
// a mirrored note that extends to left of segment X origin
|
|
|
|
// will displace accidentals only if there is conflict
|
2014-05-21 00:43:28 +02:00
|
|
|
qreal sx = x + chord->x(); // segment-relative X position of note
|
2014-05-19 21:08:50 +02:00
|
|
|
if (note->mirror() && !chord->up() && sx < 0.0)
|
2014-03-12 22:52:59 +01:00
|
|
|
leftNotes.append(note);
|
2014-04-08 20:52:10 +02:00
|
|
|
else if (sx < lx)
|
|
|
|
lx = sx;
|
2013-06-04 18:29:14 +02:00
|
|
|
|
2013-09-18 22:05:17 +02:00
|
|
|
//if (chord->stem())
|
|
|
|
// chord->stem()->rxpos() = _up ? x + hw - stemWidth5 : x + stemWidth5;
|
2013-09-10 01:17:39 +02:00
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal xx = x + hw + chord->pos().x();
|
2014-04-02 20:31:37 +02:00
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
if (chord->dots()) {
|
|
|
|
if (chord->up())
|
|
|
|
upDotPosX = qMax(upDotPosX, xx);
|
|
|
|
else
|
|
|
|
downDotPosX = qMax(downDotPosX, xx);
|
2014-06-26 10:53:57 +02:00
|
|
|
MScore::Direction dotPosition = note->userDotPosition();
|
2014-04-02 20:31:37 +02:00
|
|
|
|
2014-06-26 10:53:57 +02:00
|
|
|
if (dotPosition == MScore::Direction::AUTO && nNotes > 1 && note->visible() && !note->dotsHidden()) {
|
2014-04-02 20:31:37 +02:00
|
|
|
// resolve dot conflicts
|
|
|
|
int line = note->line();
|
|
|
|
Note* above = (i < nNotes - 1) ? notes[i+1] : 0;
|
2014-05-07 05:56:46 +02:00
|
|
|
if (above && (!above->visible() || above->dotsHidden()))
|
|
|
|
above = 0;
|
2014-04-02 20:31:37 +02:00
|
|
|
int intervalAbove = above ? line - above->line() : 1000;
|
|
|
|
Note* below = (i > 0) ? notes[i-1] : 0;
|
2014-05-07 05:56:46 +02:00
|
|
|
if (below && (!below->visible() || below->dotsHidden()))
|
|
|
|
below = 0;
|
2014-04-02 20:31:37 +02:00
|
|
|
int intervalBelow = below ? below->line() - line : 1000;
|
|
|
|
if ((line & 1) == 0) {
|
|
|
|
// line
|
|
|
|
if (intervalAbove == 1 && intervalBelow != 1)
|
2014-06-26 10:53:57 +02:00
|
|
|
dotPosition = MScore::Direction::DOWN;
|
2014-05-07 05:56:46 +02:00
|
|
|
else if (intervalBelow == 1 && intervalAbove != 1)
|
2014-06-26 10:53:57 +02:00
|
|
|
dotPosition = MScore::Direction::UP;
|
2014-04-02 20:31:37 +02:00
|
|
|
else if (intervalAbove == 0 && above->chord()->dots()) {
|
|
|
|
// unison
|
|
|
|
if (((above->voice() & 1) == (note->voice() & 1))) {
|
2014-06-26 10:53:57 +02:00
|
|
|
above->setDotY(MScore::Direction::UP);
|
|
|
|
dotPosition = MScore::Direction::DOWN;
|
2014-04-02 20:31:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// space
|
|
|
|
if (intervalAbove == 0 && above->chord()->dots()) {
|
|
|
|
// unison
|
|
|
|
if (!(note->voice() & 1))
|
2014-06-26 10:53:57 +02:00
|
|
|
dotPosition = MScore::Direction::UP;
|
2014-04-02 20:31:37 +02:00
|
|
|
else {
|
|
|
|
if (!(above->voice() & 1))
|
2014-06-26 10:53:57 +02:00
|
|
|
above->setDotY(MScore::Direction::UP);
|
2014-04-02 20:31:37 +02:00
|
|
|
else
|
2014-06-26 10:53:57 +02:00
|
|
|
dotPosition = MScore::Direction::DOWN;
|
2014-04-02 20:31:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
note->setDotY(dotPosition);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-05-22 14:12:47 +02:00
|
|
|
|
2014-03-08 07:43:24 +01:00
|
|
|
if (segment) {
|
2014-04-08 20:52:10 +02:00
|
|
|
// align all dots for segment/staff
|
|
|
|
// it would be possible to dots for up & down chords separately
|
|
|
|
// this would require space to have been allocated previously
|
|
|
|
// when calculating chord offsets
|
|
|
|
segment->setDotPosX(staff->idx(), qMax(upDotPosX, downDotPosX));
|
2014-03-08 07:43:24 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (nAcc == 0)
|
|
|
|
return;
|
2014-03-14 01:19:36 +01:00
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<int> umi;
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal pd = point(styleS(StyleIdx::accidentalDistance));
|
|
|
|
qreal pnd = point(styleS(StyleIdx::accidentalNoteDistance));
|
2014-03-19 06:22:12 +01:00
|
|
|
qreal colOffset = 0.0;
|
|
|
|
|
|
|
|
if (nAcc >= 2 && aclist[nAcc-1].line - aclist[0].line >= 7) {
|
|
|
|
|
|
|
|
// accidentals spread over an octave or more
|
|
|
|
// set up columns for accidentals with octave matches
|
|
|
|
// these will start at right and work to the left
|
|
|
|
// unmatched accidentals will use zig zag approach (see below)
|
|
|
|
// starting to the left of the octave columns
|
|
|
|
|
|
|
|
qreal minX = 0.0;
|
|
|
|
int columnTop[7] = { -1, -1, -1, -1, -1, -1, -1 };
|
|
|
|
|
|
|
|
// find columns of octaves
|
|
|
|
for (int pc = 0; pc < 7; ++pc) {
|
|
|
|
if (columnBottom[pc] == -1)
|
|
|
|
continue;
|
|
|
|
// calculate column height
|
2014-03-20 19:38:41 +01:00
|
|
|
for (int j = columnBottom[pc]; j != -1; j = aclist[j].next)
|
2014-03-19 06:22:12 +01:00
|
|
|
columnTop[pc] = j;
|
|
|
|
}
|
2013-06-27 15:00:08 +02:00
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
// compute reasonable column order
|
|
|
|
// use zig zag
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<int> column;
|
|
|
|
QVector<int> unmatched;
|
2014-03-19 06:22:12 +01:00
|
|
|
int n = nAcc - 1;
|
|
|
|
for (int i = 0; i <= n; ++i, --n) {
|
|
|
|
int pc = (aclist[i].line + 700) % 7;
|
2014-03-20 19:38:41 +01:00
|
|
|
if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
|
|
|
|
if (!column.contains(pc))
|
|
|
|
column.append(pc);
|
2014-03-19 06:22:12 +01:00
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
else
|
|
|
|
unmatched.append(i);
|
2014-03-19 06:22:12 +01:00
|
|
|
if (i == n)
|
|
|
|
break;
|
|
|
|
pc = (aclist[n].line + 700) % 7;
|
2014-03-20 19:38:41 +01:00
|
|
|
if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
|
|
|
|
if (!column.contains(pc))
|
|
|
|
column.append(pc);
|
2014-03-19 06:22:12 +01:00
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
else
|
|
|
|
unmatched.append(n);
|
2014-03-19 06:22:12 +01:00
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
int nColumns = column.size();
|
|
|
|
int nUnmatched = unmatched.size();
|
|
|
|
|
|
|
|
// handle unmatched accidentals
|
|
|
|
for (int i = 0; i < nUnmatched; ++i) {
|
|
|
|
// first try to slot it into an existing column
|
|
|
|
AcEl* me = &aclist[unmatched[i]];
|
|
|
|
// find column
|
|
|
|
bool found = false;
|
|
|
|
for (int j = 0; j < nColumns; ++j) {
|
|
|
|
int pc = column[j];
|
|
|
|
int above = -1;
|
|
|
|
int below = -1;
|
|
|
|
// find slot within column
|
|
|
|
for (int k = columnBottom[pc]; k != -1; k = aclist[k].next) {
|
|
|
|
if (aclist[k].line < me->line) {
|
|
|
|
above = k;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
below = k;
|
|
|
|
}
|
|
|
|
// check to see if accidental can fit in slot
|
2014-08-10 19:39:52 +02:00
|
|
|
qreal myPd = pd * me->note->accidental()->mag();
|
2014-03-20 19:38:41 +01:00
|
|
|
bool conflict = false;
|
|
|
|
if (above != -1 && me->top - aclist[above].bottom < myPd)
|
|
|
|
conflict = true;
|
|
|
|
else if (below != -1 && aclist[below].top - me->bottom < myPd)
|
|
|
|
conflict = true;
|
|
|
|
if (!conflict) {
|
|
|
|
// insert into column
|
|
|
|
found = true;
|
|
|
|
me->next = above;
|
|
|
|
if (above == -1)
|
|
|
|
columnTop[pc] = unmatched[i];
|
|
|
|
if (below != -1)
|
|
|
|
aclist[below].next = unmatched[i];
|
|
|
|
else
|
|
|
|
columnBottom[pc] = unmatched[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if no slot found, then add to list of unmatched accidental indices
|
|
|
|
if (!found)
|
2016-02-06 22:03:43 +01:00
|
|
|
umi.push_back(unmatched[i]);
|
2014-03-20 19:38:41 +01:00
|
|
|
}
|
|
|
|
nAcc = umi.size();
|
|
|
|
if (nAcc > 1)
|
|
|
|
qSort(umi);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
// lay out columns
|
|
|
|
for (int i = 0; i < nColumns; ++i) {
|
|
|
|
int pc = column[i];
|
|
|
|
AcEl* below = 0;
|
|
|
|
// lay out accidentals
|
|
|
|
for (int j = columnBottom[pc]; j != -1; j = aclist[j].next) {
|
|
|
|
qreal x = layoutAccidental(&aclist[j], 0, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
minX = qMin(minX, x);
|
|
|
|
below = &aclist[j];
|
|
|
|
}
|
|
|
|
// align within column
|
|
|
|
int next = -1;
|
|
|
|
for (int j = columnBottom[pc]; j != -1; j = next) {
|
|
|
|
next = aclist[j].next;
|
|
|
|
if (next != -1 && aclist[j].line == aclist[next].line)
|
|
|
|
continue;
|
|
|
|
aclist[j].x = minX;
|
|
|
|
}
|
|
|
|
// move to next column
|
|
|
|
colOffset = minX;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
for (int i = 0; i < nAcc; ++i)
|
2016-02-06 22:03:43 +01:00
|
|
|
umi.push_back(i);
|
2014-03-19 06:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nAcc) {
|
|
|
|
|
|
|
|
// for accidentals with no octave matches, use zig zag approach
|
|
|
|
// layout right to left in pairs, (next) highest then lowest
|
2014-03-13 07:52:59 +01:00
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
AcEl* me = &aclist[umi[0]];
|
|
|
|
AcEl* above = 0;
|
|
|
|
AcEl* below = 0;
|
|
|
|
|
|
|
|
// layout top accidental
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
|
|
|
// layout bottom accidental
|
|
|
|
int n = nAcc - 1;
|
|
|
|
if (n > 0) {
|
2014-03-13 07:52:59 +01:00
|
|
|
above = me;
|
2014-03-19 06:22:12 +01:00
|
|
|
me = &aclist[umi[n]];
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// layout middle accidentals
|
|
|
|
if (n > 1) {
|
|
|
|
for (int i = 1; i < n; ++i, --n) {
|
|
|
|
// next highest
|
|
|
|
below = me;
|
|
|
|
me = &aclist[umi[i]];
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
if (i == n - 1)
|
|
|
|
break;
|
|
|
|
// next lowest
|
|
|
|
above = me;
|
|
|
|
me = &aclist[umi[n-1]];
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-03-19 06:22:12 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-05-22 14:12:47 +02:00
|
|
|
for (const AcEl& e : aclist) {
|
2014-05-21 00:43:28 +02:00
|
|
|
// even though we initially calculate accidental position relative to segment
|
|
|
|
// we must record pos for accidental relative to note,
|
|
|
|
// since pos is always interpreted relative to parent
|
2013-05-22 14:12:47 +02:00
|
|
|
Note* note = e.note;
|
2014-03-09 07:09:40 +01:00
|
|
|
qreal x = e.x + lx - (note->x() + note->chord()->x());
|
2012-05-26 14:26:10 +02:00
|
|
|
note->accidental()->setPos(x, 0);
|
|
|
|
note->accidental()->adjustReadPos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-26 07:45:09 +02:00
|
|
|
#define beamModeMid(a) (a == Beam::Mode::MID || a == Beam::Mode::BEGIN32 || a == Beam::Mode::BEGIN64)
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// beamGraceNotes
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-05-15 13:42:03 +02:00
|
|
|
void Score::beamGraceNotes(Chord* mainNote, bool after)
|
2013-06-16 23:33:37 +02:00
|
|
|
{
|
2013-11-24 20:39:58 +01:00
|
|
|
ChordRest* a1 = 0; // start of (potential) beam
|
|
|
|
Beam* beam = 0; // current beam
|
2014-06-26 07:45:09 +02:00
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();
|
2015-02-19 10:28:25 +01:00
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
for (ChordRest* cr : graceNotes) {
|
2013-11-24 20:39:58 +01:00
|
|
|
bm = Groups::endBeam(cr);
|
2014-06-26 07:45:09 +02:00
|
|
|
if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
|
2013-11-24 20:39:58 +01:00
|
|
|
if (beam) {
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
if (a1) {
|
2016-01-04 14:48:58 +01:00
|
|
|
a1->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
a1 = 0;
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
cr->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (beam) {
|
2014-06-26 07:45:09 +02:00
|
|
|
bool beamEnd = bm == Beam::Mode::BEGIN;
|
2013-11-24 20:39:58 +01:00
|
|
|
if (!beamEnd) {
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
beam->add(cr);
|
|
|
|
cr = 0;
|
2014-06-26 07:45:09 +02:00
|
|
|
beamEnd = (bm == Beam::Mode::END);
|
2013-11-24 20:39:58 +01:00
|
|
|
}
|
|
|
|
if (beamEnd) {
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
if (a1 == 0)
|
|
|
|
a1 = cr;
|
|
|
|
else {
|
2014-06-26 07:45:09 +02:00
|
|
|
if (!beamModeMid(bm) && (bm == Beam::Mode::BEGIN)) {
|
2016-01-04 14:48:58 +01:00
|
|
|
a1->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
a1 = cr;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
beam = a1->beam();
|
|
|
|
if (beam == 0 || beam->elements().front() != a1) {
|
|
|
|
beam = new Beam(this);
|
|
|
|
beam->setGenerated(true);
|
|
|
|
beam->setTrack(mainNote->track());
|
|
|
|
a1->removeDeleteBeam(true);
|
|
|
|
beam->add(a1);
|
|
|
|
}
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
beam->add(cr);
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
}
|
2013-07-10 18:01:37 +02:00
|
|
|
}
|
2013-11-24 20:39:58 +01:00
|
|
|
if (beam)
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
else if (a1)
|
2016-01-04 14:48:58 +01:00
|
|
|
a1->removeDeleteBeam(false);
|
2013-06-16 23:33:37 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// layoutSpanner
|
|
|
|
// called after dragging a staff
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::layoutSpanner()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
int tracks = ntracks();
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = 0; track < tracks; ++track) {
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Segment* segment = firstSegment(); segment; segment = segment->next1()) {
|
|
|
|
if (track == tracks-1) {
|
|
|
|
int n = segment->annotations().size();
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
segment->annotations().at(i)->layout();
|
|
|
|
}
|
2016-02-04 17:06:32 +01:00
|
|
|
Chord* c = segment->element(track)->castChord();
|
|
|
|
if (c) {
|
2016-01-04 14:48:58 +01:00
|
|
|
c->layoutStem();
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rebuildBspTree();
|
|
|
|
}
|
2015-01-21 11:55:36 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// addSystemHeader
|
|
|
|
/// Add elements to make this measure suitable as the first measure
|
|
|
|
/// of a system.
|
|
|
|
// The system header can contain a starting BarLine, a Clef,
|
|
|
|
// a KeySig and a RepeatBarLine.
|
|
|
|
//-------------------------------------------------------------------
|
2015-01-21 11:55:36 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::addSystemHeader(Measure* m, bool isFirstSystem)
|
|
|
|
{
|
|
|
|
m->setHasSystemHeader(true);
|
|
|
|
const int tick = m->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
int nVisible = 0;
|
|
|
|
int staffIdx = 0;
|
2016-02-04 11:27:47 +01:00
|
|
|
|
|
|
|
// keep key sigs in TABs: TABs themselves should hide them
|
|
|
|
bool needKeysig = isFirstSystem || styleB(StyleIdx::genKeysig);
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Staff* staff : _staves) {
|
2014-11-15 17:23:20 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// At this time we don't know which staff is visible or not...
|
|
|
|
// but let's not create the key/clef if there were no visible before this layout
|
|
|
|
// sometimes we will be right, other time it will take another layout to be right...
|
2014-11-15 17:23:20 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!m->system()->staff(staffIdx)->show()) {
|
|
|
|
++staffIdx;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++nVisible;
|
2013-05-27 19:07:03 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
KeySig* keysig = 0;
|
|
|
|
Clef* clef = 0;
|
|
|
|
const int strack = staffIdx * VOICES;
|
2014-11-15 17:23:20 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// we assume that keysigs and clefs are only in the first
|
|
|
|
// track (voice 0) of a staff
|
|
|
|
|
|
|
|
KeySigEvent keyIdx = staff->keySigEvent(tick);
|
|
|
|
|
|
|
|
for (Segment* seg = m->first(); seg; seg = seg->next()) {
|
|
|
|
// search only up to the first ChordRest/StartRepeatBarLine
|
|
|
|
if (seg->segmentType() & (Segment::Type::ChordRest | Segment::Type::StartRepeatBarLine))
|
|
|
|
break;
|
|
|
|
Element* el = seg->element(strack);
|
|
|
|
if (!el)
|
|
|
|
continue;
|
|
|
|
switch (el->type()) {
|
|
|
|
case Element::Type::KEYSIG:
|
2016-02-04 17:06:32 +01:00
|
|
|
keysig = el->keySig();
|
2016-01-04 14:48:58 +01:00
|
|
|
break;
|
|
|
|
case Element::Type::CLEF:
|
2016-02-04 17:06:32 +01:00
|
|
|
clef = el->clef();
|
2016-01-04 14:48:58 +01:00
|
|
|
clef->setSmall(false);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2014-11-15 17:23:20 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
if (needKeysig && !keysig && ((keyIdx.key() != Key::C) || keyIdx.custom() || keyIdx.isAtonal())) {
|
|
|
|
//
|
|
|
|
// create missing key signature
|
|
|
|
//
|
|
|
|
keysig = new KeySig(this);
|
|
|
|
keysig->setKeySigEvent(keyIdx);
|
|
|
|
keysig->setTrack(strack);
|
|
|
|
keysig->setGenerated(true);
|
|
|
|
Segment* seg = m->undoGetSegment(Segment::Type::KeySig, tick);
|
|
|
|
keysig->setParent(seg);
|
|
|
|
keysig->layout();
|
|
|
|
undo(new AddElement(keysig));
|
|
|
|
seg->createShape(staffIdx);
|
|
|
|
}
|
|
|
|
else if (!needKeysig && keysig && keysig->generated())
|
|
|
|
undoRemoveElement(keysig);
|
|
|
|
else if (keysig && !(keysig->keySigEvent() == keyIdx))
|
|
|
|
undo(new ChangeKeySig(keysig, keyIdx, keysig->showCourtesy()));
|
2014-11-10 20:08:53 +01:00
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
if (isFirstSystem || styleB(StyleIdx::genClef) || staff->clef(tick) != staff->clef(tick-1)) {
|
|
|
|
ClefTypeList cl = staff->clefType(tick);
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!clef) {
|
|
|
|
//
|
|
|
|
// create missing clef
|
|
|
|
//
|
|
|
|
clef = new Clef(this);
|
|
|
|
clef->setTrack(strack);
|
|
|
|
clef->setSmall(false);
|
|
|
|
clef->setGenerated(true);
|
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
Segment* s = m->undoGetSegment(Segment::Type::Clef, tick);
|
2016-01-04 14:48:58 +01:00
|
|
|
clef->setParent(s);
|
2016-02-04 11:27:47 +01:00
|
|
|
clef->setClefType(cl);
|
2016-01-04 14:48:58 +01:00
|
|
|
undo(new AddElement(clef));
|
|
|
|
clef->layout();
|
|
|
|
s->createShape(staffIdx);
|
|
|
|
}
|
|
|
|
else if (clef->generated()) {
|
|
|
|
if (cl != clef->clefTypeList()) {
|
|
|
|
undo(new ChangeClefType(clef, cl._concertClef, cl._transposingClef));
|
|
|
|
clef->layout();
|
|
|
|
clef->segment()->createShape(staffIdx);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
if (clef && clef->generated()) {
|
|
|
|
undo(new RemoveElement(clef));
|
|
|
|
clef->segment()->createShape(staffIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++staffIdx;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
m->setStartRepeatBarLine();
|
|
|
|
|
|
|
|
// create system barline
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
BarLine* bl = 0;
|
2016-02-04 11:27:47 +01:00
|
|
|
Segment* s = m->first();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (s->segmentType() == Segment::Type::BeginBarLine)
|
2016-02-04 11:27:47 +01:00
|
|
|
bl = s->element(0)->barLine();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if ((nVisible > 1 && score()->styleB(StyleIdx::startBarlineMultiple)) || (nVisible <= 1 && score()->styleB(StyleIdx::startBarlineSingle))) {
|
|
|
|
if (!bl) {
|
|
|
|
bl = new BarLine(this);
|
|
|
|
bl->setTrack(0);
|
|
|
|
bl->setGenerated(true);
|
|
|
|
|
|
|
|
Segment* seg = m->undoGetSegment(Segment::Type::BeginBarLine, tick);
|
|
|
|
bl->setParent(seg);
|
|
|
|
bl->layout();
|
|
|
|
undo(new AddElement(bl));
|
2016-02-04 11:27:47 +01:00
|
|
|
seg->createShapes();
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (bl)
|
|
|
|
score()->undoRemoveElement(bl);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// cautionaryWidth
|
|
|
|
// Compute the width of required courtesy of time signature
|
|
|
|
// and key signature elements if m were the last measure
|
|
|
|
// in a staff.
|
|
|
|
// Return hasCourtesy == true if courtesy elements are
|
|
|
|
// already present. The value is undefined if no
|
|
|
|
// courtesy elements are required.
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal Score::cautionaryWidth(Measure* m, bool& hasCourtesy)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
hasCourtesy = false;
|
|
|
|
if (m == 0)
|
|
|
|
return 0.0;
|
|
|
|
Measure* nm = m->nextMeasure();
|
|
|
|
if (nm == 0 || (m->sectionBreak() && _layoutMode != LayoutMode::FLOAT))
|
|
|
|
return 0.0;
|
2014-08-27 14:35:39 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
int tick = m->endTick();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// locate a time sig. in the next measure and, if found,
|
|
|
|
// check if it has caut. sig. turned off
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Segment* ns = nm->findSegment(Segment::Type::TimeSig, tick);
|
|
|
|
bool showCourtesy = styleB(StyleIdx::genCourtesyTimesig);
|
2014-06-02 13:07:19 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal w = 0.0;
|
|
|
|
if (showCourtesy && ns) {
|
2016-02-04 17:06:32 +01:00
|
|
|
TimeSig* ts = ns->element(0)->timeSig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (ts && ts->showCourtesySig()) {
|
|
|
|
qreal leftMargin = point(styleS(StyleIdx::timesigLeftMargin));
|
|
|
|
Segment* s = m->findSegment(Segment::Type::TimeSigAnnounce, tick);
|
|
|
|
if (s && s->element(0)) {
|
2016-02-04 17:06:32 +01:00
|
|
|
w = s->element(0)->timeSig()->width() + leftMargin;
|
2016-01-04 14:48:58 +01:00
|
|
|
hasCourtesy = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ts->layout();
|
|
|
|
w = ts->width() + leftMargin;
|
|
|
|
hasCourtesy = false;
|
2015-02-17 20:22:24 +01:00
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// courtesy key signatures
|
2013-09-19 15:08:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
showCourtesy = styleB(StyleIdx::genCourtesyKeysig);
|
|
|
|
ns = nm->findSegment(Segment::Type::KeySig, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal wwMax = 0.0;
|
|
|
|
if (showCourtesy && ns) {
|
|
|
|
qreal leftMargin = point(styleS(StyleIdx::keysigLeftMargin));
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-04 17:06:32 +01:00
|
|
|
KeySig* nks = ns->element(track)->keySig();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (nks && nks->showCourtesy() && !nks->generated()) {
|
|
|
|
Segment* s = m->findSegment(Segment::Type::KeySigAnnounce, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (s && s->element(track)) {
|
|
|
|
wwMax = qMax(wwMax, s->element(track)->width() + leftMargin);
|
|
|
|
hasCourtesy = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nks->layout();
|
|
|
|
wwMax = qMax(wwMax, nks->width() + leftMargin);
|
|
|
|
//hasCourtesy = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-02 10:26:09 +02:00
|
|
|
}
|
2014-07-09 18:05:58 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
w += wwMax;
|
2014-07-09 18:05:58 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
return w; //* 1.5
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-07-23 18:49:26 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// hideEmptyStaves
|
2013-07-23 18:49:26 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::hideEmptyStaves(System* system, bool isFirstSystem)
|
2013-07-23 18:49:26 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// hide empty staves
|
|
|
|
//
|
|
|
|
int staves = _staves.size();
|
|
|
|
int staffIdx = 0;
|
|
|
|
bool systemIsEmpty = true;
|
2012-06-10 10:35:17 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach (Staff* staff, _staves) {
|
2016-01-04 14:48:58 +01:00
|
|
|
SysStaff* s = system->staff(staffIdx);
|
|
|
|
bool oldShow = s->show();
|
|
|
|
Staff::HideMode hideMode = staff->hideWhenEmpty();
|
|
|
|
if (hideMode == Staff::HideMode::ALWAYS
|
|
|
|
|| (styleB(StyleIdx::hideEmptyStaves)
|
|
|
|
&& (staves > 1)
|
|
|
|
&& !(isFirstSystem && styleB(StyleIdx::dontHideStavesInFirstSystem))
|
|
|
|
&& hideMode != Staff::HideMode::NEVER)) {
|
|
|
|
bool hideStaff = true;
|
|
|
|
foreach(MeasureBase* m, system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!m->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
2016-02-04 17:06:32 +01:00
|
|
|
Measure* measure = m->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!measure->isMeasureRest(staffIdx)) {
|
|
|
|
hideStaff = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2015-02-14 15:10:35 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
// check if notes moved into this staff
|
|
|
|
Part* part = staff->part();
|
|
|
|
int n = part->nstaves();
|
|
|
|
if (hideStaff && (n > 1)) {
|
|
|
|
int idx = part->staves()->front()->idx();
|
|
|
|
for (int i = 0; i < part->nstaves(); ++i) {
|
|
|
|
int st = idx + i;
|
2014-09-02 19:54:36 +02:00
|
|
|
|
2016-02-04 17:06:32 +01:00
|
|
|
foreach (MeasureBase* mb, system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!mb->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
2016-02-04 17:06:32 +01:00
|
|
|
Measure* m = mb->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
2016-02-04 17:06:32 +01:00
|
|
|
ChordRest* cr = s->cr(st * VOICES + voice);
|
2016-02-06 11:41:16 +01:00
|
|
|
if (cr == 0 || cr->isRest())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
|
|
|
int staffMove = cr->staffMove();
|
|
|
|
if (staffIdx == st + staffMove) {
|
|
|
|
hideStaff = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!hideStaff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!hideStaff)
|
|
|
|
break;
|
|
|
|
}
|
2014-09-02 19:54:36 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
s->setShow(hideStaff ? false : staff->show());
|
|
|
|
if (s->show()) {
|
|
|
|
systemIsEmpty = false;
|
2013-09-27 18:43:25 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
systemIsEmpty = false;
|
|
|
|
s->setShow(true);
|
|
|
|
}
|
2013-10-28 15:52:32 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (oldShow != s->show()) {
|
|
|
|
#if 0
|
|
|
|
foreach (MeasureBase* mb, system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!mb->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
|
|
|
static_cast<Measure*>(mb)->createEndBarLines();
|
2013-09-20 17:21:12 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
++staffIdx;
|
|
|
|
}
|
|
|
|
if (systemIsEmpty) {
|
|
|
|
foreach (Staff* staff, _staves) {
|
|
|
|
SysStaff* s = system->staff(staff->idx());
|
|
|
|
if (staff->showIfEmpty() && !s->show()) {
|
|
|
|
s->setShow(true);
|
2013-09-20 17:21:12 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-09-20 17:21:12 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addPage
|
|
|
|
//---------------------------------------------------------
|
2015-02-25 10:48:39 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Page* Score::addPage()
|
|
|
|
{
|
|
|
|
Page* page = new Page(this);
|
|
|
|
page->setNo(_pages.size());
|
|
|
|
_pages.push_back(page);
|
|
|
|
return page;
|
|
|
|
}
|
2014-04-24 16:06:38 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// connectTies
|
|
|
|
/// Rebuild tie connections.
|
|
|
|
//---------------------------------------------------------
|
2014-05-31 12:03:21 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::connectTies(bool silent)
|
|
|
|
{
|
|
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
Measure* m = firstMeasure();
|
|
|
|
if (!m)
|
|
|
|
return;
|
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
|
|
|
for (Segment* s = m->first(st); s; s = s->next1(st)) {
|
|
|
|
for (int i = 0; i < tracks; ++i) {
|
|
|
|
Chord* c = static_cast<Chord*>(s->element(i));
|
2016-02-06 11:41:16 +01:00
|
|
|
if (c == 0 || !c->isChord())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
2016-02-06 22:03:43 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
// connect a tie without end note
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie && !tie->endNote()) {
|
|
|
|
Note* nnote;
|
|
|
|
if (_mscVersion <= 114)
|
|
|
|
nnote = searchTieNote114(n);
|
|
|
|
else
|
|
|
|
nnote = searchTieNote(n);
|
|
|
|
if (nnote == 0) {
|
|
|
|
if (!silent) {
|
|
|
|
qDebug("next note at %d track %d for tie not found (version %d)", s->tick(), i, _mscVersion);
|
|
|
|
delete tie;
|
|
|
|
n->setTieFor(0);
|
2013-09-19 15:08:54 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
tie->setEndNote(nnote);
|
|
|
|
nnote->setTieBack(tie);
|
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
// connect a glissando without initial note (old glissando format)
|
|
|
|
for (Spanner* spanner : n->spannerBack()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (spanner->isGlissando() && !spanner->startElement()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
Note* initialNote = Glissando::guessInitialNote(n->chord());
|
|
|
|
n->removeSpannerBack(spanner);
|
|
|
|
if (initialNote != nullptr) {
|
|
|
|
spanner->setStartElement(initialNote);
|
|
|
|
spanner->setEndElement(n);
|
|
|
|
spanner->setTick(initialNote->chord()->tick());
|
|
|
|
spanner->setTick2(n->chord()->tick());
|
|
|
|
spanner->setTrack(n->track());
|
|
|
|
spanner->setTrack2(n->track());
|
|
|
|
spanner->setParent(initialNote);
|
|
|
|
initialNote->add(spanner);
|
2014-05-31 12:03:21 +02:00
|
|
|
}
|
2013-09-19 17:17:22 +02:00
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
delete spanner;
|
2013-09-19 17:17:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
// spanner with no end element can happen during copy/paste
|
|
|
|
for (Spanner* spanner : n->spannerFor()) {
|
|
|
|
if (spanner->endElement() == nullptr) {
|
|
|
|
n->removeSpannerFor(spanner);
|
|
|
|
delete spanner;
|
2013-10-30 14:21:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
// connect two note tremolos
|
|
|
|
Tremolo* tremolo = c->tremolo();
|
|
|
|
if (tremolo && tremolo->twoNotes() && !tremolo->chord2()) {
|
|
|
|
for (Segment* ls = s->next1(st); ls; ls = ls->next1(st)) {
|
|
|
|
Chord* nc = static_cast<Chord*>(ls->element(i));
|
|
|
|
if (nc == 0)
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!nc->isChord())
|
2016-01-04 14:48:58 +01:00
|
|
|
qDebug("cannot connect tremolo");
|
|
|
|
else {
|
|
|
|
nc->setTremolo(tremolo);
|
|
|
|
tremolo->setChords(c, nc);
|
|
|
|
// cross-measure tremolos are not supported
|
|
|
|
// but can accidentally result from copy & paste
|
|
|
|
// remove them now
|
|
|
|
if (c->measure() != nc->measure())
|
|
|
|
c->remove(tremolo);
|
2013-10-30 14:21:08 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
break;
|
2013-10-25 12:17:42 +02:00
|
|
|
}
|
|
|
|
}
|
2014-08-17 12:41:44 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-06 12:35:34 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// layoutFingering
|
|
|
|
// - place numbers above a note execpt for the last
|
|
|
|
// staff in a multi stave part (piano)
|
|
|
|
// - does not handle chords
|
2012-09-06 12:35:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::layoutFingering(Fingering* f)
|
2012-09-06 12:35:34 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
if (f == 0)
|
|
|
|
return;
|
|
|
|
TextStyleType tst = f->textStyleType();
|
|
|
|
if (tst != TextStyleType::FINGERING && tst != TextStyleType::RH_GUITAR_FINGERING && tst != TextStyleType::STRING_NUMBER)
|
|
|
|
return;
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Note* note = f->note();
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
Staff* staff = chord->staff();
|
|
|
|
Part* part = staff->part();
|
|
|
|
int n = part->nstaves();
|
|
|
|
bool voices = chord->measure()->hasVoices(staff->idx());
|
|
|
|
bool below = voices ? !chord->up() : (n > 1) && (staff->rstaff() == n-1);
|
|
|
|
bool tight = voices && !chord->beam();
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
f->layout();
|
|
|
|
qreal x = 0.0;
|
|
|
|
qreal y = 0.0;
|
|
|
|
qreal headWidth = note->headWidth();
|
|
|
|
qreal headHeight = note->headHeight();
|
|
|
|
qreal fh = headHeight; // TODO: fingering number height
|
2014-10-21 18:57:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (chord->notes().size() == 1) {
|
|
|
|
x = headWidth * .5;
|
|
|
|
if (below) {
|
|
|
|
// place fingering below note
|
|
|
|
y = fh + spatium() * .4;
|
|
|
|
if (tight) {
|
|
|
|
y += 0.5 * spatium();
|
|
|
|
if (chord->stem())
|
|
|
|
x += 0.5 * spatium();
|
2014-10-21 18:57:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (chord->stem() && !chord->up()) {
|
|
|
|
// on stem side
|
|
|
|
y += chord->stem()->height();
|
|
|
|
x -= spatium() * .4;
|
2014-10-21 18:57:43 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
// place fingering above note
|
|
|
|
y = -headHeight - spatium() * .4;
|
|
|
|
if (tight) {
|
|
|
|
y -= 0.5 * spatium();
|
|
|
|
if (chord->stem())
|
|
|
|
x -= 0.5 * spatium();
|
|
|
|
}
|
|
|
|
else if (chord->stem() && chord->up()) {
|
|
|
|
// on stem side
|
|
|
|
y -= chord->stem()->height();
|
|
|
|
x += spatium() * .4;
|
2014-10-21 18:57:43 +02:00
|
|
|
}
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
x -= spatium();
|
|
|
|
}
|
|
|
|
f->setUserOff(QPointF(x, y));
|
2012-09-06 12:35:34 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// layoutPages
|
|
|
|
// create list of pages
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::layoutPages(LayoutContext& lc)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
const qreal _spatium = spatium();
|
|
|
|
const qreal slb = styleS(StyleIdx::staffLowerBorder).val() * _spatium;
|
|
|
|
const qreal sub = styleS(StyleIdx::staffUpperBorder).val() * _spatium;
|
|
|
|
lc.curPage = 0;
|
|
|
|
Page* page = getEmptyPage(lc);
|
|
|
|
bool breakPages = layoutMode() != LayoutMode::SYSTEM;
|
|
|
|
qreal y = page->tm();
|
|
|
|
qreal ey = page->height() - page->bm();
|
|
|
|
System* s1 = 0; // previous system
|
|
|
|
System* s2 = lc.curSystem;
|
|
|
|
|
|
|
|
for (int i = 0;; ++i) {
|
|
|
|
//
|
|
|
|
// calculate distance to previous system
|
|
|
|
//
|
|
|
|
qreal distance;
|
|
|
|
if (s1)
|
|
|
|
distance = s1->minDistance(s2);
|
|
|
|
else {
|
|
|
|
// this is the first system on page
|
|
|
|
distance = s2->isVbox() ? s2->vbox()->topGap() : sub;
|
|
|
|
distance = qMax(distance, -s2->minTop());
|
|
|
|
}
|
|
|
|
y += distance;
|
|
|
|
s2->setPos(page->lm(), y);
|
|
|
|
page->appendSystem(s2);
|
|
|
|
y += s2->height();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// check for page break or if next system will fit on page
|
|
|
|
//
|
|
|
|
System* s3 = _systems.value(i+1); // next system
|
|
|
|
bool breakPage = !s3 || (breakPages && s2->pageBreak());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!breakPage) {
|
|
|
|
qreal dist = s2->minDistance(s3) + s3->height();
|
|
|
|
if (s3->isVbox())
|
|
|
|
dist += s2->vbox()->bottomGap();
|
|
|
|
else
|
|
|
|
dist += qMax(s3->minBottom(), slb);
|
|
|
|
breakPage = (y + dist) >= ey;
|
|
|
|
}
|
|
|
|
if (breakPage) {
|
|
|
|
qreal dist = s2->isVbox() ? s2->vbox()->bottomGap() : qMax(s2->minBottom(), slb);
|
|
|
|
layoutPage(page, ey - (y + dist));
|
|
|
|
if (!s3)
|
|
|
|
break;
|
|
|
|
page = getEmptyPage(lc);
|
|
|
|
y = page->tm();
|
|
|
|
s2 = 0;
|
|
|
|
}
|
|
|
|
s1 = s2; // current system becomes previous
|
|
|
|
s2 = s3; // next system becomes current
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
while (_pages.size() > lc.curPage) // Remove not needed pages. TODO: make undoable:
|
|
|
|
_pages.takeLast();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// doLayoutSystems
|
|
|
|
// layout staves in a system
|
|
|
|
// layout pages
|
|
|
|
//---------------------------------------------------------
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::doLayoutSystems()
|
|
|
|
{
|
|
|
|
LayoutContext lc;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (System* system : _systems)
|
|
|
|
system->layout2();
|
|
|
|
if (layoutMode() != LayoutMode::LINE)
|
|
|
|
layoutPages(lc);
|
|
|
|
rebuildBspTree();
|
|
|
|
_updateAll = true;
|
2013-12-31 12:33:30 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (MuseScoreView* v : viewer)
|
|
|
|
v->layoutChanged();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutPage
|
|
|
|
// gaps - number of gaps to stretch
|
|
|
|
//---------------------------------------------------------
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::layoutPage(Page* page, qreal restHeight)
|
|
|
|
{
|
|
|
|
int gaps = 0;
|
|
|
|
int nsystems = page->systems().size();
|
|
|
|
for (int i = 0; i < nsystems - 1; ++i) {
|
|
|
|
System* s1 = page->systems().at(i);
|
|
|
|
System* s2 = page->systems().at(i+1);
|
|
|
|
if (s1->isVbox() || s2->isVbox())
|
|
|
|
continue;
|
|
|
|
++gaps;
|
|
|
|
}
|
|
|
|
if (!gaps || MScore::layoutDebug || layoutMode() == LayoutMode::SYSTEM) {
|
|
|
|
if (_layoutMode == LayoutMode::FLOAT) {
|
|
|
|
qreal y = restHeight * .5;
|
|
|
|
for (System* system : page->systems())
|
|
|
|
system->move(QPointF(0.0, y));
|
|
|
|
}
|
|
|
|
// remove system dividers
|
|
|
|
for (System* s : page->systems()) {
|
|
|
|
for (MeasureBase* mb : s->measures()) {
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
continue;
|
|
|
|
for (Element* e : mb->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isSystemDivider())
|
2016-01-04 14:48:58 +01:00
|
|
|
mb->remove(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const qreal maxDistance = styleP(StyleIdx::maxSystemDistance);
|
|
|
|
qreal stretch = restHeight / gaps;
|
|
|
|
|
|
|
|
qreal yoff = 0;
|
|
|
|
for (int i = 0; i < nsystems - 1; ++i) {
|
|
|
|
System* s1 = page->systems().at(i);
|
|
|
|
System* s2 = page->systems().at(i+1);
|
|
|
|
if (!(s1->isVbox() || s2->isVbox())) {
|
|
|
|
qreal dist = s2->y() - (s1->y() + s1->height());
|
|
|
|
qreal offset = stretch;
|
|
|
|
if (dist + stretch > maxDistance) { // limit stretch
|
|
|
|
offset = maxDistance - dist;
|
|
|
|
if (offset < 0)
|
|
|
|
offset = 0;
|
2012-07-31 09:48:37 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
yoff += offset;
|
|
|
|
}
|
|
|
|
s2->rypos() += yoff;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
#if 0
|
|
|
|
for (System* system : page->systems()) {
|
|
|
|
system->move(QPointF(0.0, y));
|
|
|
|
if (system->addStretch())
|
|
|
|
y += system->stretchDistance();
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (system->isVbox())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// add / remove system dividers
|
|
|
|
bool divideLeft = styleB(StyleIdx::dividerLeft);
|
|
|
|
bool divideRight = styleB(StyleIdx::dividerRight);
|
|
|
|
if (system == page->systems().last()) {
|
|
|
|
// no dividers for last system of page
|
|
|
|
divideLeft = false;
|
|
|
|
divideRight = false;
|
|
|
|
}
|
|
|
|
MeasureBase* first = system->firstMeasure();
|
|
|
|
MeasureBase* last = system->lastMeasure();
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
continue;
|
|
|
|
SystemDivider* divider1 = nullptr;
|
|
|
|
SystemDivider* divider2 = nullptr;
|
|
|
|
for (Element* e : mb->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!e->isSystemDivider())
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
|
|
|
SystemDivider* sd = static_cast<SystemDivider*>(e);
|
|
|
|
if (sd->generated())
|
|
|
|
mb->remove(sd);
|
|
|
|
else if (mb == first && divideLeft && sd->dividerType() == SystemDivider::Type::LEFT)
|
|
|
|
divider1 = sd;
|
|
|
|
else if (mb == last && divideRight && sd->dividerType() == SystemDivider::Type::RIGHT)
|
|
|
|
divider2 = sd;
|
|
|
|
else // this was non-generated, but no longer applies
|
|
|
|
mb->remove(sd);
|
|
|
|
}
|
|
|
|
if (mb == first && divideLeft) {
|
|
|
|
if (!divider1) {
|
|
|
|
divider1 = new SystemDivider(this);
|
|
|
|
divider1->setGenerated(true);
|
|
|
|
divider1->setParent(mb);
|
|
|
|
addElement(divider1);
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
divider1->setDividerType(SystemDivider::Type::LEFT);
|
|
|
|
}
|
|
|
|
if (mb == last && divideRight) {
|
|
|
|
if (!divider2) {
|
|
|
|
divider2 = new SystemDivider(this);
|
|
|
|
divider2->setGenerated(true);
|
|
|
|
divider2->setParent(mb);
|
|
|
|
addElement(divider2);
|
2012-06-11 12:15:21 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
divider2->setDividerType(SystemDivider::Type::RIGHT);
|
2012-06-11 12:15:21 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2014-10-22 17:17:26 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// doLayoutPages
|
|
|
|
// small wrapper for layoutPages()
|
|
|
|
//---------------------------------------------------------
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::doLayoutPages()
|
|
|
|
{
|
|
|
|
LayoutContext lc;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
layoutPages(lc);
|
|
|
|
rebuildBspTree();
|
|
|
|
_updateAll = true;
|
|
|
|
foreach(MuseScoreView* v, viewer)
|
|
|
|
v->layoutChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// sff
|
|
|
|
// compute 1/Force for a given Extend
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal sff(qreal x, qreal xMin, const SpringMap& springs)
|
|
|
|
{
|
|
|
|
if (x <= xMin)
|
|
|
|
return 0.0;
|
|
|
|
auto i = springs.begin();
|
|
|
|
qreal c = i->second.stretch;
|
|
|
|
if (c == 0.0) //DEBUG
|
|
|
|
c = 1.1;
|
|
|
|
qreal f = 0.0;
|
|
|
|
for (; i != springs.end();) {
|
|
|
|
xMin -= i->second.fix;
|
|
|
|
f = (x - xMin) / c;
|
|
|
|
++i;
|
|
|
|
if (i == springs.end() || f <= i->first)
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
c += i->second.stretch;
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
2015-09-17 08:12:19 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// Spring2
|
|
|
|
//---------------------------------------------------------
|
2012-07-31 09:48:37 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
struct Spring2 {
|
|
|
|
int seg;
|
|
|
|
qreal stretch;
|
|
|
|
qreal fix;
|
|
|
|
Spring2(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
|
|
|
|
};
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
typedef std::multimap<qreal, Spring2, std::less<qreal> > SpringMap2;
|
2014-10-21 18:57:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// sff2
|
|
|
|
// compute 1/Force for a given Extend
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal sff2(qreal x, qreal xMin, const SpringMap2& springs)
|
|
|
|
{
|
|
|
|
if (x <= xMin)
|
|
|
|
return 0.0;
|
|
|
|
auto i = springs.begin();
|
|
|
|
qreal c = i->second.stretch;
|
|
|
|
if (c == 0.0) //DEBUG
|
|
|
|
c = 1.1;
|
|
|
|
qreal f = 0.0;
|
|
|
|
for (; i != springs.end();) {
|
|
|
|
xMin -= i->second.fix;
|
|
|
|
f = (x - xMin) / c;
|
|
|
|
++i;
|
|
|
|
if (i == springs.end() || f <= i->first)
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
c += i->second.stretch;
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// respace
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::respace(QList<ChordRest*>* /*elements*/)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
ChordRest* cr1 = elements->front();
|
|
|
|
ChordRest* cr2 = elements->back();
|
|
|
|
int n = elements->size();
|
|
|
|
qreal x1 = cr1->segment()->pos().x();
|
|
|
|
qreal x2 = cr2->segment()->pos().x();
|
|
|
|
|
|
|
|
qreal width[n-1];
|
|
|
|
int ticksList[n-1];
|
|
|
|
int minTick = 100000;
|
|
|
|
|
|
|
|
for (int i = 0; i < n-1; ++i) {
|
|
|
|
ChordRest* cr = (*elements)[i];
|
|
|
|
ChordRest* ncr = (*elements)[i+1];
|
|
|
|
Space space(cr->space());
|
|
|
|
Space nspace(ncr->space());
|
|
|
|
width[i] = space.rw() + nspace.lw();
|
|
|
|
ticksList[i] = ncr->segment()->tick() - cr->segment()->tick();
|
|
|
|
minTick = qMin(ticksList[i], minTick);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------
|
|
|
|
// compute stretches
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
|
|
SpringMap2 springs;
|
|
|
|
qreal minimum = 0.0;
|
|
|
|
for (int i = 0; i < n-1; ++i) {
|
|
|
|
qreal w = width[i];
|
|
|
|
int t = ticksList[i];
|
|
|
|
// qreal str = 1.0 + .6 * log(qreal(t) / qreal(minTick)) / log(2.0);
|
|
|
|
qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
|
|
|
|
qreal d = w / str;
|
2012-09-05 12:14:58 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
springs.insert(std::pair<qreal, Spring2>(d, Spring2(i, str, w)));
|
|
|
|
minimum += w;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------
|
|
|
|
// distribute stretch to elements
|
|
|
|
//---------------------------------------------------
|
2013-03-29 15:01:21 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal force = sff2(x2 - x1, minimum, springs);
|
|
|
|
for (auto 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 = x1;
|
|
|
|
for (int i = 1; i < n-1; ++i) {
|
|
|
|
x += width[i-1];
|
|
|
|
ChordRest* cr = (*elements)[i];
|
|
|
|
qreal dx = x - cr->segment()->pos().x();
|
|
|
|
cr->rxpos() += dx;
|
|
|
|
}
|
|
|
|
#endif
|
2013-03-29 15:01:21 +01:00
|
|
|
}
|
|
|
|
|
2014-07-31 18:46:41 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// computeMinWidth
|
|
|
|
// return the minimum width of segment list s
|
|
|
|
// set the width for all segments
|
|
|
|
// set the x position of first segment
|
2014-07-31 18:46:41 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal Score::computeMinWidth(Segment* s)
|
2013-03-29 15:01:21 +01:00
|
|
|
{
|
2016-02-04 11:27:47 +01:00
|
|
|
qreal x = s->minLeft();
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal _spatium = spatium();
|
|
|
|
qreal clefLeftMargin = styleS(StyleIdx::clefLeftMargin).val() * _spatium;
|
2016-02-04 11:27:47 +01:00
|
|
|
qreal keysigLeftMargin = styleS(StyleIdx::keysigLeftMargin).val() * _spatium;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
if (s->segmentType() == Segment::Type::ChordRest)
|
2016-02-04 11:27:47 +01:00
|
|
|
x = s->minLeft() + qMax(x, styleS(StyleIdx::barNoteDistance).val() * _spatium);
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (s->segmentType() == Segment::Type::Clef)
|
2016-02-04 11:27:47 +01:00
|
|
|
x = qMax(x, clefLeftMargin);
|
|
|
|
else if (s->segmentType() == Segment::Type::KeySig)
|
|
|
|
x = qMax(x, keysigLeftMargin);
|
|
|
|
x += s->extraLeadingSpace().val() * _spatium;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
2016-02-08 15:10:03 +01:00
|
|
|
for (Segment* ss = s;;) {
|
|
|
|
ss->rxpos() = x;
|
|
|
|
Segment* ns = ss->next();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!ns) {
|
2016-02-08 15:10:03 +01:00
|
|
|
qreal w = ss->segmentType() == Segment::Type::EndBarLine ? 0.0 : ss->minRight();
|
|
|
|
ss->setWidth(w);
|
2016-02-04 11:27:47 +01:00
|
|
|
x += w;
|
2016-01-04 14:48:58 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-02-08 15:10:03 +01:00
|
|
|
qreal w = ss->minHorizontalDistance(ns);
|
|
|
|
|
|
|
|
int n = 1;
|
|
|
|
for (Segment* ps = ss;;) {
|
|
|
|
if (ps == s)
|
|
|
|
break;
|
|
|
|
ps = ps->prev();
|
|
|
|
++n;
|
|
|
|
qreal ww = ps->minHorizontalDistance(ns) - (ss->x() - ps->x());
|
|
|
|
if (ww > w) {
|
|
|
|
// overlap !
|
|
|
|
// distribute extra space between segments ps - ss;
|
|
|
|
qreal d = (ww - w) / n;
|
|
|
|
for (Segment* s = ps; s != ss; s = s->next())
|
|
|
|
s->setWidth(s->width() + d);
|
|
|
|
w += d;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ss->setWidth(w);
|
2016-02-04 11:27:47 +01:00
|
|
|
x += w;
|
2016-02-08 15:10:03 +01:00
|
|
|
ss = ns;
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
return x;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-02 18:33:43 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// updateBarLineSpans
|
|
|
|
/// updates bar line span(s) when the number of lines of a staff changes
|
2012-08-02 18:33:43 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::updateBarLineSpans(int idx, int linesOld, int linesNew)
|
2012-08-02 18:33:43 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
int nStaves = nstaves();
|
|
|
|
Staff* _staff;
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// scan staves and check the destination staff of each bar line span
|
|
|
|
// barLineSpan is not changed; barLineFrom and barLineTo are changed if they occur in the bottom half of a staff
|
|
|
|
// in practice, a barLineFrom/To from/to the top half of the staff is linked to the staff top line,
|
|
|
|
// a barLineFrom/To from/to the bottom half of the staff is linked to staff bottom line;
|
|
|
|
// this ensures plainchant and mensurstrich special bar lines keep their relationships to the staff lines.
|
|
|
|
// 1-line staves are traited as a special case.
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for(int sIdx = 0; sIdx < nStaves; sIdx++) {
|
|
|
|
_staff = staff(sIdx);
|
|
|
|
// if this is the modified staff
|
|
|
|
if(sIdx == idx) {
|
|
|
|
// if it has no bar line, set barLineTo to a default value
|
|
|
|
if(_staff->barLineSpan() == 0)
|
|
|
|
_staff->setBarLineTo( (linesNew-1) * 2);
|
|
|
|
// if new line count is 1, set default From for 1-line staves
|
|
|
|
else if(linesNew == 1)
|
|
|
|
_staff->setBarLineFrom(BARLINE_SPAN_1LINESTAFF_FROM);
|
|
|
|
// if old line count was 1, set default From for normal staves
|
|
|
|
else if (linesOld == 1)
|
|
|
|
_staff->setBarLineFrom(0);
|
|
|
|
// if barLineFrom was below the staff middle position
|
|
|
|
// raise or lower it to account for new number of lines
|
|
|
|
else if(_staff->barLineFrom() > linesOld - 1)
|
|
|
|
_staff->setBarLineFrom(_staff->barLineFrom() + (linesNew - linesOld)*2);
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// if the modified staff is the destination of the current staff bar span:
|
|
|
|
if(sIdx + _staff->barLineSpan() - 1 == idx) {
|
|
|
|
// if new line count is 1, set default To for 1-line staves
|
|
|
|
if(linesNew == 1)
|
|
|
|
_staff->setBarLineTo(BARLINE_SPAN_1LINESTAFF_TO);
|
|
|
|
// if old line count was 1, set default To for normal staves
|
|
|
|
else if (linesOld == 1)
|
|
|
|
_staff->setBarLineTo((linesNew - 1) * 2);
|
|
|
|
// if barLineTo was below its middle position, raise or lower it
|
|
|
|
else if(_staff->barLineTo() > linesOld - 1)
|
|
|
|
_staff->setBarLineTo(_staff->barLineTo() + (linesNew - linesOld)*2);
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// getEmptyPage
|
|
|
|
//---------------------------------------------------------
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Page* Score::getEmptyPage(LayoutContext& lc)
|
|
|
|
{
|
|
|
|
Page* page = lc.curPage >= _pages.size() ? addPage() : _pages[lc.curPage];
|
|
|
|
page->setNo(lc.curPage);
|
|
|
|
page->layout();
|
|
|
|
qreal x, y;
|
|
|
|
if (MScore::verticalOrientation()) {
|
|
|
|
x = 0.0;
|
|
|
|
y = (lc.curPage == 0) ? 0.0 : _pages[lc.curPage - 1]->pos().y() + page->height() + MScore::verticalPageGap;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
y = 0.0;
|
|
|
|
x = (lc.curPage == 0) ? 0.0 : _pages[lc.curPage - 1]->pos().x()
|
|
|
|
+ page->width()
|
|
|
|
+ (((lc.curPage+_pageNumberOffset) & 1) ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven);
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
++lc.curPage;
|
|
|
|
page->setPos(x, y);
|
|
|
|
page->systems().clear();
|
|
|
|
return page;
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// getNextSystem
|
|
|
|
//---------------------------------------------------------
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
System* Score::getNextSystem(LayoutContext& lc)
|
|
|
|
{
|
2016-02-06 11:41:16 +01:00
|
|
|
bool isVBox = lc.curMeasure->isVBox();
|
2016-01-04 14:48:58 +01:00
|
|
|
System* system;
|
|
|
|
if (lc.systemList.empty()) {
|
|
|
|
system = new System(this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
system = lc.systemList.takeFirst();
|
|
|
|
system->clear(); // remove measures from system
|
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
_systems.append(system);
|
2016-01-04 14:48:58 +01:00
|
|
|
system->setVbox(isVBox);
|
|
|
|
if (!isVBox) {
|
|
|
|
int nstaves = Score::nstaves();
|
|
|
|
for (int i = system->staves()->size(); i < nstaves; ++i)
|
|
|
|
system->insertStaff(i);
|
|
|
|
int dn = system->staves()->size() - nstaves;
|
|
|
|
for (int i = 0; i < dn; ++i)
|
|
|
|
system->removeStaff(system->staves()->size()-1);
|
|
|
|
}
|
|
|
|
return system;
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// createMMRest
|
|
|
|
// create a multi measure rest from m to lm (inclusive)
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::createMMRest(Measure* m, Measure* lm, const Fraction& len)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
int n = 1;
|
|
|
|
for (Measure* mm = m->nextMeasure(); mm; mm = mm->nextMeasure()) {
|
|
|
|
++n;
|
|
|
|
mm->setMMRestCount(-1);
|
|
|
|
if (mm->mmRest())
|
|
|
|
undo(new ChangeMMRest(mm, 0));
|
|
|
|
if (mm == lm)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Measure* mmr = m->mmRest();
|
|
|
|
if (mmr) {
|
|
|
|
if (mmr->len() != len) {
|
|
|
|
Segment* s = mmr->findSegment(Segment::Type::EndBarLine, mmr->endTick());
|
|
|
|
mmr->setLen(len);
|
|
|
|
if (s)
|
|
|
|
s->setTick(mmr->endTick());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mmr = new Measure(this);
|
|
|
|
mmr->setLen(len);
|
|
|
|
mmr->setTick(m->tick());
|
|
|
|
undo(new ChangeMMRest(m, mmr));
|
|
|
|
}
|
|
|
|
mmr->setMMRestCount(n);
|
|
|
|
mmr->setNo(m->no());
|
|
|
|
mmr->setPageBreak(lm->pageBreak());
|
|
|
|
mmr->setLineBreak(lm->lineBreak());
|
|
|
|
|
|
|
|
// BarLineType t;
|
|
|
|
// End repeats do not set endBarLineGenerated to false because
|
|
|
|
// they can be generated from the repeatFlags. So we need to test it separately.
|
|
|
|
//TODO if (lm->endBarLineGenerated() && !(lm->repeatFlags() & Repeat::END))
|
|
|
|
// t = BarLineType::NORMAL;
|
|
|
|
// else
|
|
|
|
// t = lm->endBarLineType();
|
|
|
|
// mmr->setEndBarLineType(t, false, lm->endBarLineVisible(), lm->endBarLineColor());
|
2016-02-04 11:27:47 +01:00
|
|
|
|
|
|
|
//TODO mmr->setRepeatFlags(m->repeatFlags() | lm->repeatFlags());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
ElementList oldList = mmr->takeElements();
|
|
|
|
ElementList newList = lm->el();
|
|
|
|
|
|
|
|
for (Element* e : m->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isMarker())
|
2016-02-06 22:03:43 +01:00
|
|
|
newList.push_back(e);
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
for (Element* e : newList) {
|
|
|
|
bool found = false;
|
|
|
|
for (Element* ee : oldList) {
|
|
|
|
if (ee->type() == e->type()) {
|
|
|
|
mmr->add(ee);
|
2016-02-06 22:03:43 +01:00
|
|
|
auto i = std::find(oldList.begin(), oldList.end(), ee);
|
|
|
|
if (i != oldList.end())
|
|
|
|
oldList.erase(i);
|
2016-01-04 14:48:58 +01:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
mmr->add(e->clone());
|
|
|
|
}
|
|
|
|
for (Element* e : oldList)
|
|
|
|
delete e;
|
|
|
|
Segment* s = mmr->undoGetSegment(Segment::Type::ChordRest, mmr->tick());
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
if (s->element(track) == 0) {
|
|
|
|
Rest* r = new Rest(this);
|
|
|
|
r->setDurationType(TDuration::DurationType::V_MEASURE);
|
|
|
|
r->setDuration(mmr->len());
|
|
|
|
r->setTrack(track);
|
|
|
|
r->setParent(s);
|
|
|
|
undo(new AddElement(r));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check for clefs
|
|
|
|
//
|
|
|
|
Segment* cs = lm->findSegment(Segment::Type::Clef, lm->endTick());
|
|
|
|
Segment* ns = mmr->findSegment(Segment::Type::Clef, lm->endTick());
|
|
|
|
if (cs) {
|
|
|
|
if (ns == 0)
|
|
|
|
ns = mmr->undoGetSegment(Segment::Type::Clef, lm->endTick());
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
Clef* clef = static_cast<Clef*>(cs->element(track));
|
|
|
|
if (clef) {
|
|
|
|
if (ns->element(track) == 0)
|
|
|
|
ns->add(clef->clone());
|
|
|
|
else {
|
|
|
|
//TODO: check if same clef
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ns)
|
|
|
|
undo(new RemoveElement(ns));
|
|
|
|
//
|
|
|
|
// check for time signature
|
|
|
|
//
|
|
|
|
cs = m->findSegment(Segment::Type::TimeSig, m->tick());
|
|
|
|
ns = mmr->findSegment(Segment::Type::TimeSig, m->tick());
|
|
|
|
if (cs) {
|
|
|
|
if (ns == 0)
|
|
|
|
ns = mmr->undoGetSegment(Segment::Type::TimeSig, m->tick());
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
TimeSig* ts = static_cast<TimeSig*>(cs->element(track));
|
|
|
|
if (ts) {
|
|
|
|
TimeSig* nts = static_cast<TimeSig*>(ns->element(track));
|
|
|
|
if (!nts) {
|
|
|
|
nts = ts->clone();
|
|
|
|
nts->setParent(ns);
|
|
|
|
undo(new AddElement(nts));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nts->setSig(ts->sig(), ts->timeSigType());
|
|
|
|
nts->layout();
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ns)
|
|
|
|
undo(new RemoveElement(ns));
|
2015-04-09 13:48:32 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// check for key signature
|
|
|
|
//
|
|
|
|
cs = m->findSegment(Segment::Type::KeySig, m->tick());
|
|
|
|
ns = mmr->findSegment(Segment::Type::KeySig, m->tick());
|
|
|
|
if (cs) {
|
|
|
|
if (ns == 0)
|
|
|
|
ns = mmr->undoGetSegment(Segment::Type::KeySig, m->tick());
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
KeySig* ts = static_cast<KeySig*>(cs->element(track));
|
|
|
|
KeySig* nts = static_cast<KeySig*>(ns->element(track));
|
|
|
|
if (ts) {
|
|
|
|
if (!nts) {
|
|
|
|
KeySig* nks = ts->clone();
|
|
|
|
nks->setParent(ns);
|
|
|
|
undo(new AddElement(nks));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
if (!(nts->keySigEvent() == ts->keySigEvent())) {
|
|
|
|
undo(new ChangeKeySig(nts, ts->keySigEvent(), nts->showCourtesy()));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-06 22:03:43 +01:00
|
|
|
else if (ns && ns->empty())
|
2016-01-04 14:48:58 +01:00
|
|
|
undo(new RemoveElement(ns));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// check for rehearsal mark etc.
|
|
|
|
//
|
|
|
|
cs = m->findSegment(Segment::Type::ChordRest, m->tick());
|
|
|
|
if (cs) {
|
|
|
|
for (Element* e : cs->annotations()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->harmony() || e->isStaffText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
bool found = false;
|
|
|
|
for (Element* ee : s->annotations()) {
|
|
|
|
if (ee->type() == e->type() && ee->track() == e->track()) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
Element* ne = e->linkedClone();
|
|
|
|
ne->setParent(s);
|
|
|
|
undo(new AddElement(ne));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (Element* e : s->annotations()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->harmony() || e->isStaffText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
continue;
|
|
|
|
bool found = false;
|
|
|
|
for (Element* ee : cs->annotations()) {
|
|
|
|
if (ee->type() == e->type() && ee->track() == e->track()) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
undo(new RemoveElement(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
MeasureBase* nm = _showVBox ? lm->next() : lm->nextMeasure();
|
|
|
|
mmr->setNext(nm);
|
|
|
|
mmr->setPrev(m->prev());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// validMMRestMeasure
|
|
|
|
// return true if this might be a measure in a
|
|
|
|
// multi measure rest
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
static bool validMMRestMeasure(Measure* m)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
if (m->irregular())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
|
|
|
for (Element* e : s->annotations()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->harmony() || e->isStaffText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (s->segmentType() == Segment::Type::ChordRest) {
|
|
|
|
bool restFound = false;
|
|
|
|
int tracks = m->mstaves().size() * VOICES;
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
|
|
|
if ((track % VOICES) == 0 && !m->score()->staff(track/VOICES)->show()) {
|
|
|
|
track += VOICES-1;
|
|
|
|
continue;
|
2015-06-29 20:07:26 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (s->element(track)) {
|
|
|
|
if (s->element(track)->type() != Element::Type::REST)
|
|
|
|
return false;
|
|
|
|
restFound = true;
|
2014-05-15 13:42:03 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01: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
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// breakMultiMeasureRest
|
|
|
|
// return true if this measure should start a new
|
|
|
|
// multi measure rest
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
static bool breakMultiMeasureRest(Measure* m)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
if (m->breakMultiMeasureRest())
|
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
if (m->repeatStart()
|
|
|
|
|| (m->prevMeasure() && m->prevMeasure()->repeatEnd())
|
2016-01-04 14:48:58 +01:00
|
|
|
|| (m->prevMeasure() && (m->prevMeasure()->sectionBreak())))
|
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
auto sl = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
|
|
|
|
for (auto i : sl) {
|
|
|
|
Spanner* s = i.value;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (s->isVolta() && (s->tick() == m->tick() || s->tick2() == m->tick()))
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
}
|
2014-10-17 03:43:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// break for marker in this measure
|
|
|
|
for (Element* e : m->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isMarker()) {
|
|
|
|
Marker* mark = e->marker();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!(mark->textStyle().align() & AlignmentFlags::RIGHT))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// break for marker & jump in previous measure
|
|
|
|
Measure* pm = m->prevMeasure();
|
|
|
|
if (pm) {
|
|
|
|
for (Element* e : pm->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isJump())
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (e->isMarker()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
Marker* mark = static_cast<Marker*>(e);
|
|
|
|
if (mark->textStyle().align() & AlignmentFlags::RIGHT)
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// break for end of volta
|
|
|
|
auto l = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
|
|
|
|
for (auto isp : l) {
|
|
|
|
Spanner* s = isp.value;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (s->isVolta() && (s->tick2() == m->endTick()))
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
|
|
|
for (Element* e : s->annotations()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isRehearsalMark() ||
|
|
|
|
e->isTempoText() ||
|
|
|
|
((e->isHarmony() || e->isStaffText()) && (e->systemFlag() || m->score()->staff(e->staffIdx())->show())))
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
|
|
|
|
if (!m->score()->staff(staffIdx)->show())
|
|
|
|
continue;
|
|
|
|
Element* e = s->element(staffIdx * VOICES);
|
|
|
|
if (!e || e->generated())
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (s->isStartRepeatBarLine())
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
|
|
|
if (s->segmentType() & (Segment::Type::KeySig | Segment::Type::TimeSig) && m->tick())
|
|
|
|
return true;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (s->isClef()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
if (s->tick() != m->endTick() && m->tick())
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (pm) {
|
|
|
|
//TODO if (pm->endBarLineType() != BarLineType::NORMAL
|
|
|
|
// && pm->endBarLineType() != BarLineType::BROKEN
|
|
|
|
// && pm->endBarLineType() != BarLineType::DOTTED) {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
if (pm->findSegment(Segment::Type::Clef, m->tick()))
|
|
|
|
return true;
|
2014-10-17 03:43:54 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
return false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// getNextMeasure
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::getNextMeasure(LayoutContext& lc)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
lc.prevMeasure = lc.curMeasure;
|
|
|
|
lc.curMeasure = lc.nextMeasure;
|
|
|
|
if (!lc.curMeasure)
|
|
|
|
lc.nextMeasure = _showVBox ? first() : firstMeasure();
|
|
|
|
else
|
|
|
|
lc.nextMeasure = _showVBox ? lc.curMeasure->next() : lc.curMeasure->nextMeasure();
|
|
|
|
if (!lc.curMeasure)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lc.measureNo += lc.curMeasure->noOffset();
|
|
|
|
lc.curMeasure->setNo(lc.measureNo);
|
|
|
|
if (!lc.curMeasure->irregular()) // dont count measure
|
|
|
|
++lc.measureNo;
|
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
if (lc.curMeasure->isMeasure() && score()->styleB(StyleIdx::createMultiMeasureRests)) {
|
|
|
|
Measure* m = lc.curMeasure->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
Measure* nm = m;
|
|
|
|
Measure* lm = nm;
|
|
|
|
int n = 0;
|
|
|
|
Fraction len;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
while (validMMRestMeasure(nm)) {
|
|
|
|
MeasureBase* mb = _showVBox ? nm->next() : nm->nextMeasure();
|
|
|
|
if (breakMultiMeasureRest(nm) && n)
|
|
|
|
break;
|
|
|
|
++n;
|
|
|
|
len += nm->len();
|
|
|
|
lm = nm;
|
2016-02-06 11:41:16 +01:00
|
|
|
nm = mb->measure();
|
|
|
|
if (!nm || !nm->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (n >= styleI(StyleIdx::minEmptyMeasures)) {
|
|
|
|
createMMRest(m, lm, len);
|
|
|
|
lc.curMeasure = m->mmRest();
|
|
|
|
lc.nextMeasure = _showVBox ? lm->next() : lm->nextMeasure();
|
2015-06-14 07:06:28 +02:00
|
|
|
}
|
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
if (m->mmRest())
|
|
|
|
undo(new ChangeMMRest(m, 0));
|
|
|
|
m->setMMRestCount(0);
|
2015-06-14 07:06:28 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (lc.curMeasure->sectionBreak() && lc.curMeasure->sectionBreak()->startWithMeasureOne())
|
|
|
|
lc.measureNo = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
if (!lc.curMeasure->isMeasure()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
lc.curMeasure->setTick(lc.tick);
|
|
|
|
return;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//-----------------------------------------
|
|
|
|
// process one measure
|
|
|
|
//-----------------------------------------
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
Measure* measure = lc.curMeasure->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
measure->moveTicks(lc.tick - measure->tick());
|
|
|
|
if (!parentScore() && lc.prevMeasure) {
|
|
|
|
lc.sig = measure->len();
|
|
|
|
tempomap()->clear();
|
|
|
|
_sigmap->clear();
|
|
|
|
_sigmap->add(0, SigEvent(lc.sig, measure->timesig(), 0));
|
|
|
|
}
|
2014-09-16 13:20:15 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// implement section break rest
|
|
|
|
//
|
|
|
|
if (measure->sectionBreak() && measure->pause() != 0.0)
|
|
|
|
setPause(measure->tick() + measure->ticks(), measure->pause());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
|
|
|
|
AccidentalState as; // list of already set accidentals for this measure
|
|
|
|
Staff* staff = score()->staff(staffIdx);
|
|
|
|
as.init(staff->key(measure->tick()));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
int endTrack = track + VOICES;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (segment->isKeySig()) {
|
|
|
|
KeySig* ks = segment->element(staffIdx * VOICES)->keySig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!ks)
|
|
|
|
continue;
|
|
|
|
as.init(staff->key(segment->tick()));
|
|
|
|
ks->layout();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (segment->segmentType() == Segment::Type::ChordRest) {
|
|
|
|
Staff* staff = score()->staff(staffIdx);
|
|
|
|
qreal staffMag = staff->mag();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (int t = track; t < endTrack; ++t) {
|
2016-02-06 11:41:16 +01:00
|
|
|
ChordRest* cr = segment->cr(t);
|
2016-01-04 14:48:58 +01:00
|
|
|
if (cr)
|
|
|
|
measure->layoutCR0(cr, staffMag, &as);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
layoutChords1(segment, staffIdx);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (segment->segmentType() & (Segment::Type::Clef | Segment::Type::TimeSig)) {
|
|
|
|
Element* e = segment->element(staffIdx * VOICES);
|
|
|
|
if (e)
|
|
|
|
e->layout();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
for (Segment* s = measure->first(); s; s = s->next()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (s->isBreath()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal length = 0.0;
|
|
|
|
int tick = s->tick();
|
|
|
|
// find longest pause
|
|
|
|
for (int i = 0, n = ntracks(); i < n; ++i) {
|
2016-02-06 11:41:16 +01:00
|
|
|
Breath* b = s->element(i)->castBreath();
|
|
|
|
if (b)
|
2016-01-04 14:48:58 +01:00
|
|
|
length = qMax(length, b->pause());
|
2015-06-17 10:35:35 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (length != 0.0)
|
|
|
|
setPause(tick, length);
|
2015-06-17 10:35:35 +02:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (s->isTimeSig()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
2016-02-06 11:41:16 +01:00
|
|
|
TimeSig* ts = s->element(staffIdx * VOICES)->timeSig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (ts)
|
|
|
|
staff(staffIdx)->addTimeSig(ts);
|
|
|
|
}
|
2012-06-10 10:35:17 +02:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (!parentScore() && s->isChordRest()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Element* e : s->annotations()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
TempoText* tt = e->castTempoText();
|
|
|
|
if (tt)
|
2016-01-04 14:48:58 +01:00
|
|
|
setTempo(tt->segment(), tt->tempo());
|
2016-02-06 11:41:16 +01:00
|
|
|
e->layout();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal stretch = 0.0;
|
|
|
|
for (Element* e : s->elist()) {
|
|
|
|
if (!e)
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
ChordRest* cr = e->chordRest();
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Articulation* a : cr->articulations())
|
|
|
|
stretch = qMax(a->timeStretch(), stretch);
|
|
|
|
if (stretch != 0.0 && stretch != 1.0) {
|
|
|
|
qreal otempo = tempomap()->tempo(cr->tick());
|
|
|
|
qreal ntempo = otempo / stretch;
|
|
|
|
setTempo(cr->tick(), ntempo);
|
|
|
|
int etick = cr->tick() + cr->actualTicks() - 1;
|
|
|
|
auto e = tempomap()->find(etick);
|
|
|
|
if (e == tempomap()->end())
|
|
|
|
setTempo(etick, otempo);
|
|
|
|
break;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// update time signature map
|
|
|
|
// create event if measure len and time signature are different
|
|
|
|
// even if they are equivalent 4/4 vs 2/2
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!parentScore() && !measure->len().identical(lc.sig)) {
|
|
|
|
lc.sig = measure->len();
|
|
|
|
_sigmap->add(lc.tick, SigEvent(lc.sig, measure->timesig(), measure->no()));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2012-12-10 17:44:57 +01:00
|
|
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
bool crossMeasure = styleB(StyleIdx::crossMeasureValues);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (int track = 0; track < ntracks(); ++track) {
|
|
|
|
Staff* stf = staff(track2staff(track));
|
2013-09-14 02:30:40 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// dont compute beams for invisible staffs and tablature without stems
|
|
|
|
if (!stf->show() || (stf->isTabStaff() && stf->staffType()->slashStyle()))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
ChordRest* a1 = 0; // start of (potential) beam
|
|
|
|
Beam* beam = 0; // current beam
|
2013-09-16 18:32:25 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
|
|
|
ChordRest* prev = 0;
|
|
|
|
QHash<int, TDuration> beatSubdivision;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Segment* segment = measure->first(st); segment; segment = segment->next(st)) {
|
2016-02-06 11:41:16 +01:00
|
|
|
ChordRest* cr = segment->cr(track);
|
2016-01-04 14:48:58 +01:00
|
|
|
if (cr == 0)
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
cr->layoutArticulations();
|
|
|
|
for (Lyrics* l : cr->lyricsList()) {
|
|
|
|
if (l)
|
|
|
|
l->layout();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// handle grace notes and cross-measure beaming
|
2016-02-06 11:41:16 +01:00
|
|
|
if (cr->isChord()) {
|
|
|
|
Chord* chord = cr->chord();
|
2016-01-04 14:48:58 +01:00
|
|
|
beamGraceNotes(chord, false); // grace before
|
|
|
|
beamGraceNotes(chord, true); // grace after
|
|
|
|
// set up for cross-measure values as soon as possible
|
|
|
|
// to have all computations (stems, hooks, ...) consistent with it
|
|
|
|
if (!chord->isGrace())
|
|
|
|
chord->crossMeasureSetup(crossMeasure);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// get defaults from time signature properties
|
|
|
|
bm = Groups::endBeam(cr, prev);
|
|
|
|
prev = cr;
|
|
|
|
|
|
|
|
// if chord has hooks and is 2nd element of a cross-measure value
|
|
|
|
// set beam mode to NONE (do not combine with following chord beam/hook, if any)
|
|
|
|
if (cr->durationType().hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND)
|
|
|
|
bm = Beam::Mode::NONE;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
|
|
|
|
if (beam) {
|
|
|
|
beam->layout1();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
if (a1) {
|
|
|
|
a1->removeDeleteBeam(false);
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
cr->removeDeleteBeam(false);
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (beam) {
|
|
|
|
bool beamEnd = (bm == Beam::Mode::BEGIN);
|
|
|
|
if (!beamEnd) {
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
beam->add(cr);
|
|
|
|
cr = 0;
|
|
|
|
beamEnd = (bm == Beam::Mode::END);
|
|
|
|
}
|
|
|
|
if (beamEnd) {
|
|
|
|
beam->layout1();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (a1 == 0)
|
|
|
|
a1 = cr;
|
|
|
|
else {
|
|
|
|
if (!beamModeMid(bm)
|
|
|
|
&&
|
|
|
|
(bm == Beam::Mode::BEGIN
|
|
|
|
|| (a1->segment()->segmentType() != cr->segment()->segmentType())
|
|
|
|
|| (a1->tick() + a1->actualTicks() < cr->tick())
|
|
|
|
)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
a1->removeDeleteBeam(false);
|
|
|
|
a1 = cr;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
beam = a1->beam();
|
|
|
|
if (beam == 0 || beam->elements().front() != a1) {
|
|
|
|
beam = new Beam(this);
|
|
|
|
beam->setGenerated(true);
|
|
|
|
beam->setTrack(track);
|
|
|
|
a1->removeDeleteBeam(true);
|
|
|
|
beam->add(a1);
|
|
|
|
}
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
beam->add(cr);
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
}
|
2015-03-10 23:22:00 +01:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (beam)
|
|
|
|
beam->layout1();
|
|
|
|
else if (a1)
|
|
|
|
a1->removeDeleteBeam(false);
|
2015-03-10 23:22:00 +01:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Segment* s = measure->first(); s; s = s->next()) {
|
|
|
|
if (s->segmentType() == Segment::Type::EndBarLine)
|
|
|
|
continue;
|
|
|
|
s->createShapes();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
lc.tick += measure->ticks();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// collectSystem
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
System* Score::collectSystem(LayoutContext& lc)
|
|
|
|
{
|
|
|
|
if (!lc.curMeasure) {
|
|
|
|
lc.curSystem = 0;
|
|
|
|
return 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
bool raggedRight = MScore::layoutDebug;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
System* system = getNextSystem(lc);
|
|
|
|
lc.curSystem = system;
|
|
|
|
system->setInstrumentNames(lc.startWithLongNames);
|
2014-12-30 15:14:05 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal xo;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (lc.curMeasure->isHBox())
|
|
|
|
xo = point(lc.curMeasure->hBox()->boxWidth());
|
2016-01-04 14:48:58 +01:00
|
|
|
else
|
|
|
|
xo = 0;
|
|
|
|
system->layoutSystem(xo);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal minMeasureWidth = point(styleS(StyleIdx::minMeasureWidth));
|
|
|
|
qreal minWidth = system->leftMargin();
|
|
|
|
Measure* firstMeasure = 0;
|
|
|
|
qreal measureSpacing = styleD(StyleIdx::measureSpacing);
|
|
|
|
qreal systemWidth = pageFormat()->printableWidth() * DPI;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
while (lc.curMeasure) { // collect measure for system
|
|
|
|
System* oldSystem = lc.curMeasure->system();
|
|
|
|
lc.curMeasure->setSystem(system);
|
|
|
|
system->measures().push_back(lc.curMeasure);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal cautionaryW = 0.0;
|
|
|
|
qreal ww = 0.0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
if (lc.curMeasure->isHBox())
|
|
|
|
ww = point(lc.curMeasure->hBox()->boxWidth());
|
|
|
|
else if (lc.curMeasure->isMeasure()) {
|
|
|
|
Measure* m = lc.curMeasure->measure();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!firstMeasure) {
|
|
|
|
firstMeasure = m;
|
|
|
|
addSystemHeader(m, lc.firstSystem);
|
|
|
|
ww = computeMinWidth(m->first());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else
|
2016-01-04 14:48:58 +01:00
|
|
|
ww = m->minWidth1(); // without system header
|
|
|
|
m->setWidth(ww);
|
|
|
|
|
|
|
|
qreal stretch = m->userStretch() * measureSpacing;
|
|
|
|
if (stretch < 1.0)
|
|
|
|
stretch = 1.0;
|
|
|
|
ww *= stretch;
|
|
|
|
|
|
|
|
bool hasCourtesy;
|
|
|
|
cautionaryW = cautionaryWidth(m, hasCourtesy) * stretch;
|
|
|
|
|
|
|
|
// if measure does not already have courtesy elements,
|
|
|
|
// add in the amount of space that courtesy elements would take if needed
|
|
|
|
// (if measure *does* already have courtesy elements, these are included in width already)
|
|
|
|
|
|
|
|
if (!hasCourtesy)
|
|
|
|
ww += cautionaryW;
|
|
|
|
|
|
|
|
if (ww < minMeasureWidth)
|
|
|
|
ww = minMeasureWidth;
|
|
|
|
|
|
|
|
// if measure does not already have courtesy elements,
|
|
|
|
// add in the amount of space that courtesy elements would take if needed
|
|
|
|
// (if measure *does* already have courtesy elements, these are included in width already)
|
|
|
|
if (!hasCourtesy)
|
|
|
|
ww += cautionaryW;
|
|
|
|
|
|
|
|
if (ww < minMeasureWidth)
|
|
|
|
ww = minMeasureWidth;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// check if lc.curMeasure fits, remove if not
|
|
|
|
// collect at least one measure
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if ((system->measures().size() > 1) && (minWidth + ww > systemWidth)) {
|
|
|
|
system->measures().pop_back();
|
|
|
|
lc.curMeasure->setSystem(oldSystem);
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (lc.prevMeasure && lc.prevMeasure->isMeasure() && lc.prevMeasure->system() == system) {
|
|
|
|
qreal v = lc.prevMeasure->measure()->createEndBarLines(false);
|
|
|
|
lc.prevMeasure->setWidth(lc.prevMeasure->width() + v);
|
|
|
|
qreal stretch = lc.prevMeasure->measure()->userStretch() * measureSpacing;
|
|
|
|
if (stretch < 1.0)
|
|
|
|
stretch = 1.0;
|
|
|
|
ww += v * stretch;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
bool pbreak;
|
|
|
|
switch (_layoutMode) {
|
|
|
|
case LayoutMode::PAGE:
|
|
|
|
case LayoutMode::SYSTEM:
|
|
|
|
pbreak =
|
|
|
|
lc.curMeasure->pageBreak()
|
|
|
|
|| lc.curMeasure->lineBreak()
|
|
|
|
|| lc.curMeasure->sectionBreak()
|
2016-02-06 11:41:16 +01:00
|
|
|
|| lc.curMeasure->isVBox();
|
2016-01-04 14:48:58 +01:00
|
|
|
break;
|
|
|
|
case LayoutMode::FLOAT:
|
|
|
|
case LayoutMode::LINE:
|
|
|
|
pbreak = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Element::Type nt = lc.nextMeasure ? lc.nextMeasure->type() : Element::Type::INVALID;
|
|
|
|
if (pbreak || (nt == Element::Type::VBOX || nt == Element::Type::TBOX || nt == Element::Type::FBOX)) {
|
|
|
|
if (_layoutMode != LayoutMode::SYSTEM)
|
|
|
|
system->setPageBreak(lc.curMeasure->pageBreak());
|
|
|
|
minWidth += ww;
|
|
|
|
getNextMeasure(lc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
getNextMeasure(lc);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (minWidth + minMeasureWidth > systemWidth) // small optimization
|
|
|
|
break; // next measure will not fit
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
minWidth += ww;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// whether the measure actually has courtesy elements or whether we added space for hypothetical ones,
|
|
|
|
// we should remove the width of courtesy elements for this measure from the accumulated total
|
|
|
|
// since at this point we are assuming we may be able to fit another measure
|
|
|
|
minWidth -= cautionaryW;
|
|
|
|
} // end collect measures for system
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!system->isVbox()) {
|
|
|
|
if (lc.prevMeasure && lc.prevMeasure->isMeasure()) {
|
|
|
|
qreal v = lc.prevMeasure->measure()->createEndBarLines(true);
|
|
|
|
lc.prevMeasure->setWidth(lc.prevMeasure->width() + v);
|
2015-07-25 08:43:02 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
system->removeGeneratedElements();
|
|
|
|
hideEmptyStaves(system, lc.firstSystem);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// dont stretch last system row, if accumulated minWidth is <= lastSystemFillLimit
|
|
|
|
//
|
|
|
|
if (lc.curMeasure == 0 && ((minWidth / systemWidth) <= styleD(StyleIdx::lastSystemFillLimit)))
|
|
|
|
raggedRight = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//-------------------------------------------------------
|
|
|
|
// add cautionary time/key signatures if needed
|
|
|
|
//-------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* m = system->lastMeasure();
|
|
|
|
Measure* nm = m ? m->nextMeasure() : 0;
|
|
|
|
Segment* s;
|
|
|
|
|
|
|
|
if (m && nm) {
|
|
|
|
m->setHasSystemTrailer(false);
|
|
|
|
int tick = m->tick() + m->ticks();
|
|
|
|
bool isFinalMeasureOfSection = m->isFinalMeasureOfSection();
|
|
|
|
|
|
|
|
// locate a time sig. in the next measure and, if found,
|
|
|
|
// check if it has cout. sig. turned off
|
|
|
|
TimeSig* ts;
|
|
|
|
Segment* tss = nm->findSegment(Segment::Type::TimeSig, tick);
|
|
|
|
bool showCourtesySig = tss && styleB(StyleIdx::genCourtesyTimesig) && !(isFinalMeasureOfSection && _layoutMode != LayoutMode::FLOAT);
|
|
|
|
if (showCourtesySig) {
|
2016-02-06 11:41:16 +01:00
|
|
|
ts = tss->element(0)->timeSig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (ts && !ts->showCourtesySig())
|
|
|
|
showCourtesySig = false; // this key change has court. sig turned off
|
|
|
|
}
|
|
|
|
if (showCourtesySig) {
|
|
|
|
// if due, create a new courtesy time signature for each staff
|
|
|
|
m->setHasSystemTrailer(true);
|
|
|
|
s = m->undoGetSegment(Segment::Type::TimeSigAnnounce, tick);
|
|
|
|
int nstaves = Score::nstaves();
|
|
|
|
for (int track = 0; track < nstaves * VOICES; track += VOICES) {
|
2016-02-06 11:41:16 +01:00
|
|
|
TimeSig* nts = tss->element(track)->timeSig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!nts)
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
ts = s->element(track)->timeSig();
|
|
|
|
if (!ts) {
|
2016-01-04 14:48:58 +01:00
|
|
|
ts = new TimeSig(this);
|
|
|
|
ts->setTrack(track);
|
|
|
|
ts->setGenerated(true);
|
|
|
|
ts->setParent(s);
|
|
|
|
undoAddElement(ts);
|
|
|
|
ts->layout();
|
|
|
|
s->createShape(track / VOICES);
|
|
|
|
}
|
|
|
|
ts->setFrom(nts);
|
2015-03-10 23:22:00 +01:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else {
|
|
|
|
// remove any existing time signatures
|
|
|
|
Segment* tss = m->findSegment(Segment::Type::TimeSigAnnounce, tick);
|
|
|
|
if (tss)
|
|
|
|
undoRemoveElement(tss);
|
2015-03-10 23:22:00 +01:00
|
|
|
}
|
2015-03-09 22:07:13 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// courtesy key signatures
|
|
|
|
int n = _staves.size();
|
|
|
|
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
Staff* staff = _staves[staffIdx];
|
|
|
|
showCourtesySig = styleB(StyleIdx::genCourtesyKeysig) && !(isFinalMeasureOfSection && _layoutMode != LayoutMode::FLOAT);
|
|
|
|
|
|
|
|
KeySigEvent key1 = staff->keySigEvent(tick - 1);
|
|
|
|
KeySigEvent key2 = staff->keySigEvent(tick);
|
|
|
|
if (showCourtesySig && !(key1 == key2)) {
|
|
|
|
// locate a key sig. in next measure and, if found,
|
|
|
|
// check if it has court. sig turned off
|
|
|
|
s = nm->findSegment(Segment::Type::KeySig, tick);
|
|
|
|
showCourtesySig = true; // assume this key change has court. sig turned on
|
|
|
|
if (s) {
|
2016-02-06 11:41:16 +01:00
|
|
|
KeySig* ks = s->element(track)->keySig();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (ks && !ks->showCourtesy())
|
|
|
|
showCourtesySig = false; // this key change has court. sig turned off
|
|
|
|
}
|
|
|
|
|
|
|
|
if (showCourtesySig) {
|
|
|
|
m->setHasSystemTrailer(true);
|
|
|
|
s = m->undoGetSegment(Segment::Type::KeySigAnnounce, tick);
|
2016-02-06 11:41:16 +01:00
|
|
|
KeySig* ks = s->element(track)->keySig();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
if (!ks) {
|
|
|
|
ks = new KeySig(this);
|
|
|
|
ks->setKeySigEvent(key2);
|
|
|
|
ks->setTrack(track);
|
|
|
|
ks->setGenerated(true);
|
|
|
|
ks->setParent(s);
|
|
|
|
undoAddElement(ks);
|
|
|
|
ks->layout();
|
|
|
|
s->createShape(track / VOICES);
|
|
|
|
}
|
|
|
|
else if (!(ks->keySigEvent() == key2)) {
|
|
|
|
undo(new ChangeKeySig(ks, key2, ks->showCourtesy()));
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!showCourtesySig) {
|
|
|
|
// remove any existent courtesy key signature
|
|
|
|
Segment* s = m->findSegment(Segment::Type::KeySigAnnounce, tick);
|
|
|
|
if (s && s->element(track))
|
|
|
|
undoRemoveElement(s->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
//HACK to layout cautionary elements:
|
|
|
|
if (system->lastMeasure() && system->lastMeasure()->hasSystemTrailer())
|
|
|
|
computeMinWidth(system->lastMeasure()->first());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
minWidth = system->leftMargin();
|
|
|
|
qreal totalWeight = 0.0;
|
|
|
|
qreal totalWeight2 = 0.0;
|
2015-01-05 13:17:04 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (MeasureBase* mb : system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (mb->isHBox())
|
2016-01-04 14:48:58 +01:00
|
|
|
minWidth += point(((Box*)mb)->boxWidth());
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (mb->isMeasure()) {
|
|
|
|
Measure* m = mb->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal w = m->width();
|
|
|
|
minWidth += w;
|
|
|
|
totalWeight += m->ticks() * m->userStretch();
|
|
|
|
totalWeight2 += m->ticks();
|
|
|
|
}
|
|
|
|
}
|
2015-07-25 08:43:02 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
bool zeroStretch = totalWeight >= 0.01;
|
|
|
|
if (zeroStretch)
|
|
|
|
totalWeight = totalWeight2;
|
2015-07-25 08:43:02 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// stretch incomplete row
|
|
|
|
qreal rest;
|
|
|
|
if (MScore::layoutDebug)
|
|
|
|
rest = 0;
|
|
|
|
else {
|
|
|
|
rest = systemWidth - minWidth;
|
|
|
|
if (raggedRight) {
|
|
|
|
if (minWidth > rest)
|
|
|
|
rest = rest * .5;
|
|
|
|
else
|
|
|
|
rest = minWidth;
|
2015-07-25 08:43:02 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
rest /= totalWeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPointF pos;
|
|
|
|
bool firstM = true;
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
qreal ww = 0.0;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (mb->isMeasure()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
if (firstM) {
|
|
|
|
pos.rx() += system->leftMargin();
|
|
|
|
firstM = false;
|
2015-07-25 08:43:02 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
mb->setPos(pos);
|
2016-02-06 11:41:16 +01:00
|
|
|
Measure* m = mb->measure();
|
2016-01-04 14:48:58 +01:00
|
|
|
qreal stretch = zeroStretch ? m->userStretch() : 1.0;
|
|
|
|
if (stretch < 1.0)
|
|
|
|
stretch = 1.0;
|
|
|
|
ww = m->width() + rest * m->ticks() * stretch;
|
|
|
|
for (MStaff* ms : m->mstaves())
|
|
|
|
ms->lines->layout();
|
|
|
|
m->stretchMeasure(ww);
|
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (mb->isHBox()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
mb->setPos(pos);
|
2016-02-06 11:41:16 +01:00
|
|
|
ww = point(mb->hBox()->boxWidth());
|
2016-01-04 14:48:58 +01:00
|
|
|
mb->layout();
|
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (mb->isVBox()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
mb->setPos(pos);
|
2015-07-25 08:43:02 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
pos.rx() += ww;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
system->setWidth(systemWidth);
|
|
|
|
system->layout2();
|
|
|
|
|
|
|
|
Measure* lm = system->lastMeasure();
|
|
|
|
lc.firstSystem = lm && lm->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
|
|
|
|
lc.startWithLongNames = lc.firstSystem && lm->sectionBreak()->startWithLongNames();
|
|
|
|
return system;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// collectPage
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
bool Score::collectPage(LayoutContext& lc)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
if (!lc.curSystem)
|
|
|
|
return false;
|
2013-06-05 15:47:34 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
const qreal _spatium = spatium();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
const qreal slb = styleS(StyleIdx::staffLowerBorder).val() * _spatium;
|
|
|
|
const qreal sub = styleS(StyleIdx::staffUpperBorder).val() * _spatium;
|
|
|
|
bool breakPages = layoutMode() != LayoutMode::SYSTEM;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Page* page = getEmptyPage(lc);
|
|
|
|
qreal y = page->tm();
|
|
|
|
qreal ey = page->height() - page->bm();
|
|
|
|
System* s1 = 0; // previous system
|
|
|
|
System* s2 = lc.curSystem;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (;;) {
|
|
|
|
//
|
|
|
|
// calculate distance to previous system
|
|
|
|
//
|
|
|
|
qreal distance;
|
|
|
|
if (s1)
|
|
|
|
distance = s1->minDistance(s2);
|
|
|
|
else {
|
|
|
|
// this is the first system on page
|
|
|
|
distance = s2->isVbox() ? s2->vbox()->topGap() : sub;
|
|
|
|
distance = qMax(distance, -s2->minTop());
|
|
|
|
}
|
|
|
|
y += distance;
|
|
|
|
s2->setPos(page->lm(), y);
|
|
|
|
page->appendSystem(s2);
|
|
|
|
y += s2->height();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//
|
|
|
|
// check for page break or if next system will fit on page
|
|
|
|
//
|
|
|
|
collectSystem(lc);
|
|
|
|
System* s3 = lc.curSystem;
|
|
|
|
bool breakPage = !s3 || (breakPages && s2->pageBreak());
|
|
|
|
|
|
|
|
if (!breakPage) {
|
|
|
|
qreal dist = s2->minDistance(s3) + s3->height();
|
|
|
|
if (s3->isVbox())
|
|
|
|
dist += s3->vbox()->bottomGap();
|
|
|
|
else
|
|
|
|
dist += qMax(s3->minBottom(), slb);
|
|
|
|
breakPage = (y + dist) >= ey;
|
|
|
|
}
|
|
|
|
if (breakPage) {
|
|
|
|
qreal dist = s2->isVbox() ? s2->vbox()->bottomGap() : qMax(s2->minBottom(), slb);
|
|
|
|
layoutPage(page, ey - (y + dist));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
|
|
|
s1 = s2; // current system becomes previous
|
|
|
|
s2 = s3; // next system becomes current
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// doLayout
|
|
|
|
// input: list of measures
|
|
|
|
// output: list of systems
|
|
|
|
// list of pages
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
void Score::doLayout()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
LayoutContext lc;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
printf("====================doLayout\n");
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
if (_staves.empty() || first() == 0) {
|
2016-01-04 14:48:58 +01:00
|
|
|
// score is empty
|
|
|
|
_pages.clear();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
Page* page = addPage();
|
|
|
|
page->layout();
|
|
|
|
page->setNo(0);
|
|
|
|
page->setPos(0.0, 0.0);
|
|
|
|
page->rebuildBspTree();
|
|
|
|
qDebug("layout: empty score");
|
|
|
|
_layoutAll = false;
|
|
|
|
return;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
_scoreFont = ScoreFont::fontFactory(_style.value(StyleIdx::MusicalSymbolFont).toString());
|
|
|
|
_noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, spatium() / SPATIUM20);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (layoutFlags & LayoutFlag::FIX_PITCH_VELO)
|
|
|
|
updateVelo();
|
|
|
|
if (layoutFlags & LayoutFlag::PLAY_EVENTS)
|
|
|
|
createPlayEvents();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
// initialize layout context lc
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
_systems.swap(lc.systemList);
|
|
|
|
getNextMeasure(lc);
|
|
|
|
getNextMeasure(lc);
|
|
|
|
collectSystem(lc);
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------
|
|
|
|
// layout score
|
|
|
|
//---------------------------------------------------
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
while (collectPage(lc))
|
|
|
|
;
|
2013-03-26 16:25:07 +01:00
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
// TODO: remove remaining systems from lc.systemList
|
2016-01-04 14:48:58 +01:00
|
|
|
while (_pages.size() > lc.curPage) // Remove not needed pages. TODO: make undoable:
|
|
|
|
_pages.takeLast();
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
//---------------------------------------------------
|
|
|
|
// place Spanner & beams
|
|
|
|
//---------------------------------------------------
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
|
|
|
for (Segment* segment = firstSegmentMM(); segment; segment = segment->next1MM()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
// if (track == tracks-1) {
|
|
|
|
// for (Element* e : segment->annotations())
|
|
|
|
// e->layout();
|
|
|
|
// }
|
2016-01-04 14:48:58 +01:00
|
|
|
Element* e = segment->element(track);
|
|
|
|
if (!e)
|
2012-08-01 22:15:58 +02:00
|
|
|
continue;
|
2016-01-04 14:48:58 +01:00
|
|
|
if (e->isChordRest()) {
|
|
|
|
if (!staff(track2staff(track))->show())
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
ChordRest* cr = e->chordRest();
|
2016-01-04 14:48:58 +01:00
|
|
|
if (cr->beam() && cr->beam()->elements().front() == cr)
|
|
|
|
cr->beam()->layout();
|
2014-05-21 00:43:28 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
if (cr->isChord()) {
|
|
|
|
Chord* c = cr->chord();
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Chord* cc : c->graceNotes()) {
|
|
|
|
if (cc->beam() && cc->beam()->elements().front() == cc)
|
|
|
|
cc->beam()->layout();
|
|
|
|
for (Note* n : cc->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Element* e : cc->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
if (e->isSlur())
|
2016-01-04 14:48:58 +01:00
|
|
|
e->layout();
|
2014-10-10 11:52:44 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
cc->layoutArticulations();
|
2014-10-10 11:52:44 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
c->layoutArpeggio2();
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
cr->layoutArticulations();
|
2013-03-14 16:52:10 +01:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
else if (e->isBarLine())
|
2016-01-04 14:48:58 +01:00
|
|
|
e->layout();
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
2013-03-26 16:25:07 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
if (lastSegment())
|
|
|
|
checkSpanner(0, lastSegment()->tick());
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2016-02-06 11:41:16 +01:00
|
|
|
for (Staff* staff : _staves)
|
|
|
|
staff->pitchOffsets().clear();
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (auto s : _spanner.map()) {
|
|
|
|
Spanner* sp = s.second;
|
2016-02-06 11:41:16 +01:00
|
|
|
|
|
|
|
if (sp->isOttava() && sp->ticks() == 0) {
|
2016-01-04 14:48:58 +01:00
|
|
|
sp->setTick2(lastMeasure()->endTick());
|
|
|
|
sp->staff()->updateOttava();
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
sp->layout();
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Spanner* s : _unmanagedSpanner)
|
|
|
|
s->layout();
|
2012-10-23 22:16:31 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM())
|
|
|
|
m->layout2();
|
2012-10-26 11:42:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (auto s : _spanner.map()) {
|
|
|
|
Spanner* sp = s.second;
|
2016-02-06 11:41:16 +01:00
|
|
|
if (sp->isSlur())
|
2016-01-04 14:48:58 +01:00
|
|
|
sp->layout();
|
|
|
|
}
|
2012-10-26 11:42:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
rebuildBspTree();
|
|
|
|
for (MuseScoreView* v : viewer)
|
|
|
|
v->layoutChanged();
|
2012-10-26 11:42:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
_layoutAll = false;
|
|
|
|
layoutFlags = 0;
|
2012-10-26 11:42:54 +02:00
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
// _mscVersion is used during read and first layout
|
|
|
|
// but then it's used for drag and drop and should be set to new version
|
|
|
|
_mscVersion = MSCVERSION; // for later drag & drop usage
|
2012-10-26 11:42:54 +02:00
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
|
|
|
|
}
|