2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
|
|
|
// Copyright (C) 2002-2011 Werner Schweer
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License version 2
|
|
|
|
// as published by the Free Software Foundation and appearing in
|
|
|
|
// the file LICENCE.GPL
|
|
|
|
//=============================================================================
|
|
|
|
|
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"
|
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
|
|
|
|
|
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)) {
|
2012-12-06 09:19:59 +01:00
|
|
|
ChordRest* cr = static_cast<ChordRest*>(segment->element(track));
|
|
|
|
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;
|
2014-03-05 23:45:58 +01:00
|
|
|
QList<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) {
|
|
|
|
Element* e = segment->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && (e->type() == Element::Type::CHORD)) {
|
2013-06-10 21:13:04 +02:00
|
|
|
Chord* chord = static_cast<Chord*>(e);
|
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;
|
2014-03-05 23:45:58 +01:00
|
|
|
// layout grace note noteheads
|
|
|
|
layoutChords2(c->notes(), c->up());
|
|
|
|
// layout grace note chords
|
|
|
|
layoutChords3(c->notes(), staff, 0);
|
|
|
|
}
|
2014-03-07 03:43:41 +01:00
|
|
|
if (chord->up()) {
|
|
|
|
++upVoices;
|
2014-03-05 23:45:58 +01:00
|
|
|
upStemNotes.append(chord->notes());
|
2014-03-08 07:43:24 +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;
|
2014-03-05 23:45:58 +01:00
|
|
|
downStemNotes.append(chord->notes());
|
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
|
|
|
|
2014-03-10 04:24:54 +01:00
|
|
|
if (upVoices + downVoices == 0)
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
// TODO: use track as secondary sort criteria?
|
|
|
|
// otherwise there might be issues with unisons between voices
|
|
|
|
// in some corner cases
|
|
|
|
|
2014-04-08 23:18:15 +02:00
|
|
|
maxUpWidth = nominalWidth * maxUpMag;
|
|
|
|
maxDownWidth = nominalWidth * maxDownMag;
|
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
// layout upstem noteheads
|
2014-03-07 03:43:41 +01:00
|
|
|
if (upVoices > 1) {
|
2014-03-05 23:45:58 +01:00
|
|
|
qSort(upStemNotes.begin(), upStemNotes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
if (upVoices) {
|
|
|
|
qreal hw = layoutChords2(upStemNotes, true);
|
|
|
|
maxUpWidth = qMax(maxUpWidth, hw);
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
// layout downstem noteheads
|
2014-03-07 03:43:41 +01:00
|
|
|
if (downVoices > 1) {
|
2014-03-05 23:45:58 +01:00
|
|
|
qSort(downStemNotes.begin(), downStemNotes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
if (downVoices) {
|
|
|
|
qreal hw = layoutChords2(downStemNotes, false);
|
|
|
|
maxDownWidth = qMax(maxDownWidth, hw);
|
|
|
|
}
|
|
|
|
|
2014-08-11 06:57:11 +02: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
|
2014-04-08 20:52:10 +02:00
|
|
|
|
2014-04-08 23:18:15 +02:00
|
|
|
// centering adjustments for whole note, breve, and small chords
|
2014-08-11 06:57:11 +02:00
|
|
|
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
|
2014-08-29 07:03:18 +02:00
|
|
|
|
|
|
|
// 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;
|
2014-08-11 06:57:11 +02:00
|
|
|
|
2015-02-18 07:47:16 +01:00
|
|
|
// 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());
|
2014-04-08 20:52:10 +02:00
|
|
|
if (headDiff > centerThreshold) {
|
2014-04-08 23:18:15 +02:00
|
|
|
// larger than nominal
|
2015-02-18 07:47:16 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
}
|
2014-04-08 23:18:15 +02:00
|
|
|
else if (-headDiff > centerThreshold) {
|
|
|
|
// smaller than nominal
|
2015-02-18 07:47:16 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-08-11 06:57:11 +02:00
|
|
|
centerAdjustDown = centerUp;
|
2014-04-08 23:18:15 +02:00
|
|
|
}
|
2015-02-18 07:47:16 +01:00
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
headDiff = maxDownWidth - nominalWidth;
|
|
|
|
if (headDiff > centerThreshold) {
|
2014-08-29 07:03:18 +02:00
|
|
|
// larger than nominal
|
2014-04-08 20:52:10 +02:00
|
|
|
centerDown = headDiff * -0.5;
|
2014-08-29 07:03:18 +02:00
|
|
|
// to left align rather than center, change the above to
|
|
|
|
//centerAdjustUp = headDiff;
|
2014-04-08 20:52:10 +02:00
|
|
|
maxDownWidth = nominalWidth - centerDown;
|
|
|
|
}
|
2014-08-11 06:57:11 +02:00
|
|
|
else if (-headDiff > centerThreshold) {
|
2014-08-29 07:03:18 +02:00
|
|
|
// smaller than nominal
|
2015-02-18 07:47:16 +01:00
|
|
|
centerDown = -headDiff * 0.5;
|
2014-08-11 06:57:11 +02:00
|
|
|
centerAdjustUp = centerDown;
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
// handle conflict between upstem and downstem chords
|
|
|
|
|
2014-03-10 04:24:54 +01:00
|
|
|
if (upVoices && downVoices) {
|
2014-04-08 20:52:10 +02:00
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
Note* bottomUpNote = upStemNotes.first();
|
|
|
|
Note* topDownNote = downStemNotes.last();
|
2014-09-08 19:31:00 +02:00
|
|
|
int separation;
|
|
|
|
if (bottomUpNote->chord()->staffMove() == topDownNote->chord()->staffMove())
|
|
|
|
separation = topDownNote->line() - bottomUpNote->line();
|
|
|
|
else
|
|
|
|
separation = 2; // no conflict
|
2014-04-08 20:52:10 +02:00
|
|
|
QList<Note*> overlapNotes;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
|
|
if (separation == 1) {
|
|
|
|
// second
|
2014-04-08 20:52:10 +02:00
|
|
|
downOffset = maxUpWidth;
|
|
|
|
// align stems if present, leave extra room if not
|
|
|
|
if (topDownNote->chord()->stem() && bottomUpNote->chord()->stem())
|
2014-03-10 16:55:51 +01:00
|
|
|
downOffset -= topDownNote->chord()->stem()->lineWidth();
|
2014-03-05 23:45:58 +01:00
|
|
|
else
|
|
|
|
downOffset += 0.1 * sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
for (int i = downStemNotes.size() - 1; i >= 0; --i) {
|
|
|
|
if (downStemNotes[i]->line() <= bottomUpNote->line() + 1)
|
|
|
|
overlapNotes.append(downStemNotes[i]);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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
|
2014-03-07 03:43:41 +01:00
|
|
|
bool conflictSecondUpHigher = false; // second found
|
|
|
|
bool conflictSecondDownHigher = false; // second found
|
2014-03-05 23:45:58 +01:00
|
|
|
int lastLine = 1000;
|
|
|
|
Note* p = overlapNotes[0];
|
|
|
|
for (int i = 0, count = overlapNotes.size(); i < count; ++i) {
|
|
|
|
Note* n = overlapNotes[i];
|
2014-06-25 22:01:26 +02:00
|
|
|
NoteHead::Type nHeadType;
|
|
|
|
NoteHead::Type pHeadType;
|
2014-04-28 23:59:33 +02:00
|
|
|
Chord* nchord = n->chord();
|
|
|
|
Chord* pchord = p->chord();
|
2014-03-05 23:45:58 +01:00
|
|
|
if (n->mirror()) {
|
|
|
|
if (separation < 0) {
|
|
|
|
// don't try to share heads if there is any mirroring
|
|
|
|
shareHeads = false;
|
|
|
|
// don't worry about conflicts involving mirrored notes
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int line = n->line();
|
|
|
|
int d = lastLine - line;
|
|
|
|
switch (d) {
|
|
|
|
case 0:
|
|
|
|
// unison
|
|
|
|
conflictUnison = true;
|
|
|
|
matchPending = false;
|
2014-06-25 22:01:26 +02:00
|
|
|
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();
|
2014-04-22 04:04:43 +02:00
|
|
|
// the most important rules for sharing noteheads on unisons between voices are
|
|
|
|
// that notes must be one same line with same tpc
|
2014-05-07 05:56:46 +02:00
|
|
|
// 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()) {
|
2014-03-05 23:45:58 +01:00
|
|
|
shareHeads = false;
|
2014-04-22 04:04:43 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// noteheads are potentially shareable
|
|
|
|
// it is more subjective at this point
|
|
|
|
// current default is to require *either* of the following:
|
2014-05-07 05:56:46 +02:00
|
|
|
// 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)
|
2014-04-22 04:04:43 +02:00
|
|
|
// thus user can force notes to be shared despite differing number of dots or either being stemless
|
2014-05-07 05:56:46 +02:00
|
|
|
// by setting one of the notehead types to match the other or by making one notehead invisible
|
2014-04-22 04:04:43 +02:00
|
|
|
// TODO: consider adding a style option, staff properties, or note property to control sharing
|
2014-05-07 05:56:46 +02:00
|
|
|
if ((nchord->dots() != pchord->dots() || !nchord->stem() || !pchord->stem() || nHeadType != pHeadType || n->small() || p->small()) &&
|
2014-06-25 22:01:26 +02:00
|
|
|
((n->headType() == NoteHead::Type::HEAD_AUTO && p->headType() == NoteHead::Type::HEAD_AUTO) || nHeadType != pHeadType) &&
|
2014-05-07 05:56:46 +02:00
|
|
|
(n->visible() == p->visible())) {
|
2014-04-22 04:04:43 +02:00
|
|
|
shareHeads = false;
|
|
|
|
}
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
break;
|
|
|
|
case 1:
|
2014-03-07 03:43:41 +01:00
|
|
|
// second
|
|
|
|
// trust that this won't be a problem for single unison
|
2014-03-05 23:45:58 +01:00
|
|
|
if (separation < 0) {
|
2014-03-07 03:43:41 +01:00
|
|
|
if (n->chord()->up())
|
|
|
|
conflictSecondUpHigher = true;
|
|
|
|
else
|
|
|
|
conflictSecondDownHigher = true;
|
2014-03-05 23:45:58 +01:00
|
|
|
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())) {
|
2014-04-22 04:04:43 +02:00
|
|
|
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-03-05 23:45:58 +01:00
|
|
|
}
|
2014-04-02 20:31:37 +02: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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-13 19:03:44 +01:00
|
|
|
else if (conflictUnison && separation == 0 && (!downGrace || upGrace))
|
2014-03-05 23:45:58 +01:00
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
|
|
|
else if (conflictUnison)
|
|
|
|
upOffset = maxDownWidth + 0.3 * sp;
|
2014-03-07 03:43:41 +01:00
|
|
|
else if (conflictSecondUpHigher)
|
2014-03-05 23:45:58 +01:00
|
|
|
upOffset = maxDownWidth + 0.2 * sp;
|
2015-01-30 19:33:54 +01:00
|
|
|
else if ((downHooks && !upHooks) && !(upDots && !downDots))
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
2014-03-08 07:43:24 +01:00
|
|
|
else if (conflictSecondDownHigher) {
|
|
|
|
if (downDots && !upDots)
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
2015-01-30 19:33:54 +01:00
|
|
|
else {
|
2014-03-08 07:43:24 +01:00
|
|
|
upOffset = maxDownWidth - 0.2 * sp;
|
2015-01-30 19:33:54 +01:00
|
|
|
if (downHooks)
|
|
|
|
upOffset += 0.3 * sp;
|
|
|
|
}
|
2014-03-08 07:43:24 +01:00
|
|
|
}
|
2014-03-07 03:43:41 +01:00
|
|
|
else {
|
|
|
|
// 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())
|
2014-03-10 16:55:51 +01:00
|
|
|
clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
|
2014-03-07 03:43:41 +01:00
|
|
|
if (bottomUpNote->chord()->stem())
|
2014-04-08 20:52:10 +02:00
|
|
|
clearRight = bottomUpNote->chord()->stem()->lineWidth() + qMax(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
|
2014-03-08 07:43:24 +01:00
|
|
|
else
|
2014-03-08 17:13:06 +01:00
|
|
|
downDots = 0; // no need to adjust for dots in this case
|
2014-03-07 03:43:41 +01:00
|
|
|
upOffset = qMax(clearLeft, clearRight);
|
2015-01-30 19:33:54 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-08-11 06:57:11 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2014-03-07 03:43:41 +01:00
|
|
|
}
|
2014-03-08 07:43:24 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// adjust for dots
|
|
|
|
if ((upDots && !downDots) || (downDots && !upDots)) {
|
|
|
|
// only one sets of dots
|
|
|
|
// place between chords
|
2014-04-08 23:18:15 +02:00
|
|
|
int dots;
|
|
|
|
qreal mag;
|
|
|
|
if (upDots) {
|
|
|
|
dots = upDots;
|
|
|
|
mag = maxUpMag;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dots = downDots;
|
|
|
|
mag = maxDownMag;
|
|
|
|
}
|
2014-03-08 07:43:24 +01:00
|
|
|
qreal dotWidth = segment->symWidth(SymId::augmentationDot);
|
|
|
|
// first dot
|
2014-05-26 15:31:36 +02:00
|
|
|
dotAdjust = point(styleS(StyleIdx::dotNoteDistance)) + dotWidth;
|
2014-03-08 07:43:24 +01:00
|
|
|
// additional dots
|
2014-03-08 17:13:06 +01:00
|
|
|
if (dots > 1)
|
2014-05-26 15:31:36 +02:00
|
|
|
dotAdjust += point(styleS(StyleIdx::dotDotDistance)) * (dots - 1);
|
2014-04-08 23:18:15 +02:00
|
|
|
dotAdjust *= mag;
|
2014-08-11 06:57:11 +02:00
|
|
|
// only by amount over threshold
|
|
|
|
dotAdjust = qMax(dotAdjust - dotAdjustThreshold, 0.0);
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2014-03-08 17:13:06 +01:00
|
|
|
if (separation == 1)
|
|
|
|
dotAdjust += 0.1 * sp;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply chord offsets
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
Element* e = segment->element(track);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e && (e->type() == Element::Type::CHORD)) {
|
2014-04-08 20:52:10 +02:00
|
|
|
Chord* chord = static_cast<Chord*>(e);
|
|
|
|
if (chord->up()) {
|
|
|
|
if (upOffset != 0.0) {
|
2014-08-11 06:57:11 +02:00
|
|
|
chord->rxpos() += upOffset + centerAdjustUp + oversizeUp;
|
2014-03-08 07:43:24 +01:00
|
|
|
if (downDots && !upDots)
|
|
|
|
chord->rxpos() += dotAdjust;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
else
|
|
|
|
chord->rxpos() += centerUp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (downOffset != 0.0) {
|
2014-08-11 06:57:11 +02:00
|
|
|
chord->rxpos() += downOffset + centerAdjustDown;
|
2014-03-08 07:43:24 +01:00
|
|
|
if (upDots && !downDots)
|
|
|
|
chord->rxpos() += dotAdjust;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
2014-04-08 20:52:10 +02:00
|
|
|
else
|
|
|
|
chord->rxpos() += centerDown;
|
2014-03-05 23:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// layout chords
|
|
|
|
QList<Note*> notes;
|
2014-03-10 04:24:54 +01:00
|
|
|
if (upVoices)
|
|
|
|
notes.append(upStemNotes);
|
|
|
|
if (downVoices)
|
|
|
|
notes.append(downStemNotes);
|
2014-03-09 07:09:40 +01:00
|
|
|
if (upVoices + downVoices > 1)
|
2014-03-05 23:45:58 +01:00
|
|
|
qSort(notes.begin(), notes.end(),
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
layoutChords3(notes, staff, segment);
|
|
|
|
|
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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
qreal Score::layoutChords2(QList<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
|
|
|
|
lvisible = note->visible();
|
|
|
|
lmove = move;
|
2012-05-26 14:26:10 +02:00
|
|
|
ll = line;
|
2014-05-07 05:56:46 +02:00
|
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
static qreal layoutAccidental(AcEl* me, AcEl* above, AcEl* below, qreal colOffset, QList<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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutChords3(QList<Note*>& notes, Staff* staff, Segment* segment)
|
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------
|
|
|
|
// layout accidentals
|
|
|
|
// find column for dots
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
QList<Note*> leftNotes; // notes to left of origin
|
|
|
|
QList<AcEl> aclist; // accidentals
|
|
|
|
// 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();
|
|
|
|
qreal stepDistance = sp * .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
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
QList<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
|
2014-03-20 19:38:41 +01:00
|
|
|
QList<int> column;
|
|
|
|
QList<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)
|
|
|
|
umi.append(unmatched[i]);
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
umi.append(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
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-03-05 23:45:58 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
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;
|
2015-02-19 10:28:25 +01:00
|
|
|
QList<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();
|
|
|
|
|
2014-04-23 18:07:38 +02:00
|
|
|
foreach (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) {
|
|
|
|
a1->removeDeleteBeam();
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
cr->removeDeleteBeam();
|
|
|
|
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)) {
|
2013-11-24 20:39:58 +01:00
|
|
|
a1->removeDeleteBeam();
|
|
|
|
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)
|
|
|
|
a1->removeDeleteBeam();
|
2013-06-16 23:33:37 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutStage2
|
|
|
|
// auto - beamer
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutStage2()
|
|
|
|
{
|
|
|
|
int tracks = nstaves() * VOICES;
|
2014-05-26 15:31:36 +02:00
|
|
|
bool crossMeasure = styleB(StyleIdx::crossMeasureValues);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
2015-01-21 11:55:36 +01:00
|
|
|
Staff* stf = staff(track2staff(track));
|
|
|
|
|
2015-03-19 01:04:51 +01:00
|
|
|
// dont compute beams for invisible staffs and tablature without stems
|
2015-01-21 11:55:36 +01:00
|
|
|
if (!stf->show() || (stf->isTabStaff() && stf->staffType()->slashStyle()))
|
2014-03-10 10:36:36 +01:00
|
|
|
continue;
|
2015-01-21 11:55:36 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* a1 = 0; // start of (potential) beam
|
|
|
|
Beam* beam = 0; // current beam
|
|
|
|
Measure* measure = 0;
|
|
|
|
|
2014-11-15 17:23:20 +01:00
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
2014-11-15 17:23:20 +01:00
|
|
|
ChordRest* prev = 0;
|
|
|
|
bool checkBeats = false;
|
|
|
|
Fraction stretch = 1;
|
|
|
|
QHash<int, TDuration> beatSubdivision;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st)) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(segment->element(track));
|
|
|
|
if (cr == 0)
|
|
|
|
continue;
|
2014-11-15 17:23:20 +01:00
|
|
|
|
|
|
|
// handle grace notes and cross-measure beaming
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2013-07-11 12:25:25 +02:00
|
|
|
Chord* chord = static_cast<Chord*>(cr);
|
2014-11-15 17:23:20 +01:00
|
|
|
beamGraceNotes(chord, false); // grace before
|
|
|
|
beamGraceNotes(chord, true); // grace after
|
2013-07-11 12:25:25 +02:00
|
|
|
// 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);
|
|
|
|
}
|
2013-05-27 19:07:03 +02:00
|
|
|
|
2014-11-15 17:23:20 +01:00
|
|
|
// if this is a new measure, and it's simple meter (actually X/4),
|
|
|
|
// then perform a prepass to determine the subdivision of each beat
|
|
|
|
if (segment->measure() != measure) {
|
|
|
|
Measure* m = segment->measure();
|
|
|
|
TimeSig* ts = cr->staff()->timeSig(m->tick());
|
|
|
|
beatSubdivision.clear();
|
|
|
|
checkBeats = false;
|
|
|
|
stretch = ts ? ts->stretch() : 1;
|
|
|
|
if (ts && ts->denominator() == 4) {
|
|
|
|
checkBeats = true;
|
|
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
|
|
ChordRest* mcr = static_cast<ChordRest*>(s->element(track));
|
|
|
|
if (mcr == 0)
|
|
|
|
continue;
|
|
|
|
int beat = ((mcr->rtick() * stretch.numerator()) / stretch.denominator()) / MScore::division;
|
|
|
|
if (beatSubdivision.contains(beat))
|
|
|
|
beatSubdivision[beat] = qMin(beatSubdivision[beat], mcr->durationType());
|
|
|
|
else
|
|
|
|
beatSubdivision[beat] = mcr->durationType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get defaults from time signature properties
|
|
|
|
bm = Groups::endBeam(cr, prev);
|
|
|
|
|
|
|
|
// perform additional context-dependent checks
|
|
|
|
if (bm == Beam::Mode::AUTO) {
|
|
|
|
// check if we need to break beams according to minimum duration in current / previous beat
|
|
|
|
if (checkBeats && cr->rtick()) {
|
|
|
|
int tick = (cr->rtick() * stretch.numerator()) / stretch.denominator();
|
|
|
|
// check if on the beat
|
|
|
|
if (tick % MScore::division == 0) {
|
|
|
|
int beat = tick / MScore::division;
|
|
|
|
// get minimum duration for this & previous beat
|
|
|
|
TDuration minDuration = qMin(beatSubdivision[beat], beatSubdivision[beat - 1]);
|
|
|
|
// re-calculate beam as if this were the duration of current chordrest
|
2015-03-19 01:04:51 +01:00
|
|
|
TDuration saveDuration = cr->actualDurationType();
|
|
|
|
TDuration saveCMDuration = cr->crossMeasureDurationType();
|
|
|
|
CrossMeasure saveCrossMeasVal = cr->crossMeasure();
|
2014-11-15 17:23:20 +01:00
|
|
|
cr->setDurationType(minDuration);
|
|
|
|
bm = Groups::endBeam(cr, prev);
|
|
|
|
cr->setDurationType(saveDuration);
|
2015-03-19 01:04:51 +01:00
|
|
|
cr->setCrossMeasure(saveCrossMeasVal);
|
|
|
|
cr->setCrossMeasureDurationType(saveCMDuration);
|
2014-11-15 17:23:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 20:08:53 +01:00
|
|
|
prev = cr;
|
|
|
|
|
2012-11-26 00:28:25 +01:00
|
|
|
// 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)
|
2014-05-21 15:41:23 +02:00
|
|
|
if (cr->durationType().hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND)
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::NONE;
|
2015-03-19 01:04:51 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (cr->measure() != measure) {
|
|
|
|
if (measure && !beamModeMid(bm)) {
|
|
|
|
if (beam) {
|
|
|
|
beam->layout1();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
else if (a1) {
|
|
|
|
a1->removeDeleteBeam();
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
measure = cr->measure();
|
|
|
|
if (!beamModeMid(bm)) {
|
|
|
|
a1 = 0;
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
}
|
2014-06-26 07:45:09 +02:00
|
|
|
if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (beam) {
|
|
|
|
beam->layout1();
|
|
|
|
beam = 0;
|
|
|
|
}
|
|
|
|
if (a1) {
|
|
|
|
a1->removeDeleteBeam();
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
cr->removeDeleteBeam();
|
|
|
|
continue;
|
|
|
|
}
|
2013-05-27 19:07:03 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (beam) {
|
2014-11-10 20:08:53 +01:00
|
|
|
bool beamEnd = (bm == Beam::Mode::BEGIN);
|
2013-05-27 19:07:03 +02:00
|
|
|
if (!beamEnd) {
|
2013-07-11 12:25:25 +02:00
|
|
|
cr->removeDeleteBeam(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
beam->add(cr);
|
|
|
|
cr = 0;
|
2014-06-26 07:45:09 +02:00
|
|
|
beamEnd = (bm == Beam::Mode::END);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-05-27 19:07:03 +02:00
|
|
|
if (beamEnd) {
|
2012-05-26 14:26:10 +02:00
|
|
|
beam->layout1();
|
|
|
|
beam = 0;
|
|
|
|
}
|
2013-05-27 19:07:03 +02:00
|
|
|
}
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (a1 == 0)
|
|
|
|
a1 = cr;
|
|
|
|
else {
|
|
|
|
if (!beamModeMid(bm)
|
|
|
|
&&
|
2014-06-26 07:45:09 +02:00
|
|
|
(bm == Beam::Mode::BEGIN
|
2013-05-27 19:07:03 +02:00
|
|
|
|| (a1->segment()->segmentType() != cr->segment()->segmentType())
|
|
|
|
|| (a1->tick() + a1->actualTicks() < cr->tick())
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
a1->removeDeleteBeam();
|
|
|
|
a1 = cr;
|
|
|
|
}
|
|
|
|
else {
|
2012-05-26 14:26:10 +02:00
|
|
|
beam = a1->beam();
|
|
|
|
if (beam == 0 || beam->elements().front() != a1) {
|
|
|
|
beam = new Beam(this);
|
|
|
|
beam->setGenerated(true);
|
2013-05-27 19:07:03 +02:00
|
|
|
beam->setTrack(track);
|
2013-07-11 12:25:25 +02:00
|
|
|
a1->removeDeleteBeam(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
beam->add(a1);
|
|
|
|
}
|
2013-07-11 12:25:25 +02:00
|
|
|
cr->removeDeleteBeam(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
beam->add(cr);
|
|
|
|
a1 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (beam)
|
|
|
|
beam->layout1();
|
2013-07-11 12:25:25 +02:00
|
|
|
else if (a1)
|
2012-05-26 14:26:10 +02:00
|
|
|
a1->removeDeleteBeam();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutStage3
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutStage3()
|
|
|
|
{
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
2014-03-10 10:36:36 +01:00
|
|
|
if (!staff(staffIdx)->show())
|
|
|
|
continue;
|
2015-02-17 20:22:24 +01:00
|
|
|
for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st))
|
2012-05-26 14:26:10 +02:00
|
|
|
layoutChords1(segment, staffIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layout
|
|
|
|
// - measures are akkumulated into systems
|
|
|
|
// - systems are akkumulated into pages
|
|
|
|
// already existent systems and pages are reused
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::doLayout()
|
|
|
|
{
|
2014-08-27 14:35:39 +02:00
|
|
|
// printf("doLayout %p cmd %d undo empty %d\n", this, undo()->active(), undo()->isEmpty());
|
2014-12-08 18:02:17 +01:00
|
|
|
|
2014-08-27 14:35:39 +02:00
|
|
|
if (!undo()->active() && !undo()->isEmpty() && !undoRedo()) {
|
|
|
|
qDebug("layout outside cmd and dirty undo");
|
|
|
|
// _layoutAll = false;
|
|
|
|
// abort();
|
|
|
|
// return;
|
|
|
|
}
|
2015-02-17 20:22:24 +01:00
|
|
|
if (_staves.isEmpty() || first() == 0) {
|
|
|
|
// score is empty
|
|
|
|
// qDeleteAll(_pages);
|
|
|
|
_pages.clear();
|
|
|
|
|
|
|
|
Page* page = addPage();
|
|
|
|
page->layout();
|
|
|
|
page->setNo(0);
|
|
|
|
page->setPos(0.0, 0.0);
|
|
|
|
page->rebuildBspTree();
|
|
|
|
qDebug("layout: empty score");
|
|
|
|
_layoutAll = false;
|
|
|
|
return;
|
|
|
|
}
|
2014-08-27 14:35:39 +02:00
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
_scoreFont = ScoreFont::fontFactory(_style.value(StyleIdx::MusicalSymbolFont).toString());
|
2013-11-11 15:11:28 +01:00
|
|
|
_noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, spatium() / (MScore::DPI * SPATIUM20));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-30 10:16:38 +02:00
|
|
|
if (layoutFlags & LayoutFlag::FIX_TICKS)
|
2012-05-26 14:26:10 +02:00
|
|
|
fixTicks();
|
2014-05-30 10:16:38 +02:00
|
|
|
if (layoutFlags & LayoutFlag::FIX_PITCH_VELO)
|
2012-05-26 14:26:10 +02:00
|
|
|
updateVelo();
|
2014-05-30 10:16:38 +02:00
|
|
|
if (layoutFlags & LayoutFlag::PLAY_EVENTS)
|
2012-11-21 13:49:08 +01:00
|
|
|
createPlayEvents();
|
2012-05-26 14:26:10 +02:00
|
|
|
layoutFlags = 0;
|
|
|
|
|
2015-02-17 20:22:24 +01:00
|
|
|
int measureNo = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
int nstaves = _staves.size();
|
2015-02-17 20:22:24 +01:00
|
|
|
for (MeasureBase* m = first(); m; m = m->next()) { // set layout break
|
|
|
|
m->setPageBreak(false);
|
|
|
|
m->setLineBreak(false);
|
|
|
|
m->setSectionBreak(0);
|
2014-06-02 13:07:19 +02:00
|
|
|
|
2015-02-17 20:22:24 +01:00
|
|
|
for (Element* e : m->el()) {
|
|
|
|
if (!tagIsValid(e->tag()) || (e->type() != Element::Type::LAYOUT_BREAK))
|
|
|
|
continue;
|
|
|
|
LayoutBreak* lb = static_cast<LayoutBreak*>(e);
|
|
|
|
switch (lb->layoutBreakType()) {
|
|
|
|
case LayoutBreak::Type::PAGE:
|
|
|
|
m->setPageBreak(true);
|
|
|
|
break;
|
|
|
|
case LayoutBreak::Type::LINE:
|
|
|
|
m->setLineBreak(true);
|
|
|
|
break;
|
|
|
|
case LayoutBreak::Type::SECTION:
|
|
|
|
m->setSectionBreak(lb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m->type() == Element::Type::MEASURE) {
|
|
|
|
Measure* measure = static_cast<Measure*>(m);
|
|
|
|
measureNo += measure->noOffset();
|
|
|
|
measure->setNo(measureNo);
|
|
|
|
if (measure->sectionBreak() && measure->sectionBreak()->startWithMeasureOne())
|
|
|
|
measureNo = 0;
|
|
|
|
else if (!measure->irregular()) // dont count measure
|
|
|
|
++measureNo;
|
2015-02-17 22:17:22 +01:00
|
|
|
measure->layoutStage1();
|
2015-02-17 20:22:24 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
if (styleB(StyleIdx::createMultiMeasureRests))
|
2013-09-19 15:08:54 +02:00
|
|
|
createMMRests();
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
layoutStage2(); // beam notes, finally decide if chord is up/down
|
|
|
|
layoutStage3(); // compute note head horizontal positions
|
|
|
|
|
2014-05-30 10:15:36 +02:00
|
|
|
if (layoutMode() == LayoutMode::LINE)
|
2012-05-26 14:26:10 +02:00
|
|
|
layoutLinear();
|
2012-12-10 17:44:57 +01:00
|
|
|
else
|
2012-05-26 14:26:10 +02:00
|
|
|
layoutSystems(); // create list of systems
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
// place Spanner & beams
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
|
|
int tracks = nstaves * VOICES;
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
2013-10-02 10:26:09 +02:00
|
|
|
for (Segment* segment = firstSegmentMM(); segment; segment = segment->next1MM()) {
|
2012-12-06 12:46:05 +01:00
|
|
|
if (track == tracks-1) {
|
2013-09-03 14:00:07 +02:00
|
|
|
for (Element* e : segment->annotations())
|
|
|
|
e->layout();
|
2012-12-06 12:46:05 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
Element* e = segment->element(track);
|
2012-12-06 12:46:05 +01:00
|
|
|
if (!e)
|
|
|
|
continue;
|
|
|
|
if (e->isChordRest()) {
|
2014-03-10 10:36:36 +01:00
|
|
|
if (!staff(track2staff(track))->show())
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
|
|
|
if (cr->beam() && cr->beam()->elements().front() == cr)
|
|
|
|
cr->beam()->layout();
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Chord* c = static_cast<Chord*>(cr);
|
2013-06-16 23:33:37 +02:00
|
|
|
for (Chord* cc : c->graceNotes()) {
|
2013-06-20 10:19:04 +02:00
|
|
|
if (cc->beam() && cc->beam()->elements().front() == cc)
|
2013-06-16 23:33:37 +02:00
|
|
|
cc->beam()->layout();
|
2014-11-29 08:27:07 +01:00
|
|
|
for (Note* n : cc->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
|
|
|
}
|
2013-06-20 17:23:24 +02:00
|
|
|
for (Element* e : cc->el()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::SLUR)
|
2013-06-20 17:23:24 +02:00
|
|
|
e->layout();
|
|
|
|
}
|
2014-11-29 08:27:07 +01:00
|
|
|
cc->layoutArticulations();
|
2013-06-16 23:33:37 +02:00
|
|
|
}
|
2013-06-20 10:19:04 +02:00
|
|
|
c->layoutStem();
|
2012-05-26 14:26:10 +02:00
|
|
|
c->layoutArpeggio2();
|
2013-07-04 13:40:25 +02:00
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
2012-05-26 14:26:10 +02:00
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
2013-07-04 13:40:25 +02:00
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cr->layoutArticulations();
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (e->type() == Element::Type::BAR_LINE)
|
2012-05-26 14:26:10 +02:00
|
|
|
e->layout();
|
|
|
|
}
|
|
|
|
}
|
2014-08-23 11:58:50 +02:00
|
|
|
|
2014-08-23 16:22:49 +02:00
|
|
|
if (lastSegment())
|
|
|
|
checkSpanner(0, lastSegment()->tick());
|
2014-08-23 11:58:50 +02:00
|
|
|
|
2014-06-24 13:18:37 +02:00
|
|
|
for (auto s : _spanner.map()) {
|
2013-07-23 19:08:24 +02:00
|
|
|
Spanner* sp = s.second;
|
2014-08-13 15:42:40 +02:00
|
|
|
if (sp->type() == Element::Type::OTTAVA && sp->ticks() == 0) {
|
2013-07-23 19:08:24 +02:00
|
|
|
sp->setTick2(lastMeasure()->endTick());
|
2014-07-15 12:49:51 +02:00
|
|
|
sp->staff()->updateOttava();
|
2013-07-23 19:08:24 +02:00
|
|
|
}
|
2014-08-23 11:58:50 +02:00
|
|
|
// 1.3 scores can have ties in this list (ws: should not happen anymore)
|
2014-06-24 18:36:02 +02:00
|
|
|
if (sp->type() != Element::Type::TIE) {
|
2013-10-02 10:26:09 +02:00
|
|
|
if (sp->tick() == -1) {
|
2014-07-21 13:24:21 +02:00
|
|
|
qDebug("bad spanner %s %d - %d", sp->name(), sp->tick(), sp->tick2());
|
2013-10-02 10:26:09 +02:00
|
|
|
}
|
2014-02-24 13:53:43 +01:00
|
|
|
else
|
2013-10-02 10:26:09 +02:00
|
|
|
sp->layout();
|
2013-07-23 19:08:24 +02:00
|
|
|
}
|
|
|
|
}
|
Lyrics multi-system melisma and dashes
Implements melisma and dash lines for lyrics spanning several systems.
The melisma and dash line is based on the `SLine` class and its segments on the `LineSegment` class. Both the whole line and its segments are not selectable, marked as generated and not saved in the score file, which is not changed in any way.
For very wide dash segments, several dashes are drawn; the distance between the dashes is not configurable.
Lyrics layout code in `Measure` class and in `layout.cpp` file has been commented out as the lyrics line layout is all contained in the lyrics.cpp file
The line is registered with the `Score` (to have its layout delayed until all elements are positioned) with a mechanism similar to other `Spanner`'s, but in a different container (`_unmanagedSpanner`), as the owning `Lyrics` should decide when create, register, unregister and delete its line.
The line segments are registered with the `System` they belong to (to have them drawn), in the same way as other `Spanner`'s.
There is code for using the dash metrics of the lyrics font, but it is turned off via a conditional directive, as there does not seem to be a reliable way to determine the dash metrics; conventional values (determined by trials and errors and based on my taste!) are used when the conditional directive is off.
2015-01-11 10:11:44 +01:00
|
|
|
for (Spanner* s : _unmanagedSpanner)
|
|
|
|
s->layout();
|
|
|
|
|
2014-05-30 10:15:36 +02:00
|
|
|
if (layoutMode() != LayoutMode::LINE) {
|
2012-12-10 17:44:57 +01:00
|
|
|
layoutSystems2();
|
|
|
|
layoutPages(); // create list of pages
|
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM())
|
2012-05-26 14:26:10 +02:00
|
|
|
m->layout2();
|
|
|
|
|
2014-07-09 18:05:58 +02:00
|
|
|
for (auto s : _spanner.map()) { // DEBUG
|
|
|
|
Spanner* sp = s.second;
|
|
|
|
if (sp->type() == Element::Type::SLUR) {
|
|
|
|
sp->layout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-23 19:08:24 +02:00
|
|
|
rebuildBspTree();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-11-20 14:34:47 +01:00
|
|
|
for (MuseScoreView* v : viewer)
|
2014-07-02 09:55:50 +02:00
|
|
|
v->layoutChanged();
|
2013-08-23 03:32:16 +02:00
|
|
|
|
2013-06-27 11:02:42 +02:00
|
|
|
_layoutAll = false;
|
2014-07-21 13:24:21 +02:00
|
|
|
|
2014-07-19 12:29:20 +02: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
|
|
|
|
if (_mscVersion <= 114)
|
|
|
|
_mscVersion = MSCVERSION; // for later drag & drop usage
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-07-23 18:49:26 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSpanner
|
2013-07-23 19:08:24 +02:00
|
|
|
// called after dragging a staff
|
2013-07-23 18:49:26 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutSpanner()
|
|
|
|
{
|
2013-07-23 19:08:24 +02:00
|
|
|
int tracks = ntracks();
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
|
|
|
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();
|
|
|
|
}
|
2014-07-02 09:55:50 +02:00
|
|
|
Chord* c = static_cast<Chord*>(segment->element(track));
|
|
|
|
if (c && c->type() == Element::Type::CHORD) {
|
|
|
|
c->layoutStem();
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
if (tie)
|
|
|
|
tie->layout();
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
sp->layout();
|
|
|
|
}
|
2013-07-23 19:08:24 +02:00
|
|
|
}
|
2013-07-23 18:49:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rebuildBspTree();
|
|
|
|
}
|
|
|
|
|
2012-08-02 18:33:43 +02:00
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// addSystemHeader
|
|
|
|
/// Add elements to make this measure suitable as the first measure
|
|
|
|
/// of a system.
|
|
|
|
// The system header can contain a Clef, a KeySig and a
|
|
|
|
// RepeatBarLine.
|
|
|
|
//-------------------------------------------------------------------
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-08-02 18:33:43 +02:00
|
|
|
void Score::addSystemHeader(Measure* m, bool isFirstSystem)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2012-08-02 18:33:43 +02:00
|
|
|
if (undoRedo()) // no change possible in this state
|
2012-06-10 10:35:17 +02:00
|
|
|
return;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
int tick = m->tick();
|
2012-07-31 09:48:37 +02:00
|
|
|
int i = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach (Staff* staff, _staves) {
|
2015-03-12 15:21:31 +01:00
|
|
|
// At this time we don't know which staff is visible or not...
|
2015-03-12 15:53:01 +01:00
|
|
|
// 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...
|
|
|
|
if (!m->system()->staff(i)->show()) {
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
KeySig* keysig = 0;
|
|
|
|
Clef* clef = 0;
|
|
|
|
int strack = i * VOICES;
|
|
|
|
|
|
|
|
// we assume that keysigs and clefs are only in the first
|
|
|
|
// track (voice 0) of a staff
|
|
|
|
|
2014-12-08 18:02:17 +01:00
|
|
|
KeySigEvent keyIdx = staff->keySigEvent(tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
for (Segment* seg = m->first(); seg; seg = seg->next()) {
|
2015-02-14 15:10:35 +01:00
|
|
|
// search only up to the first ChordRest/StartRepeatBarLine
|
|
|
|
if (seg->segmentType() & (Segment::Type::ChordRest | Segment::Type::StartRepeatBarLine))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
Element* el = seg->element(strack);
|
|
|
|
if (!el)
|
|
|
|
continue;
|
|
|
|
switch (el->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::KEYSIG:
|
2012-05-26 14:26:10 +02:00
|
|
|
keysig = static_cast<KeySig*>(el);
|
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CLEF:
|
2012-05-26 14:26:10 +02:00
|
|
|
clef = static_cast<Clef*>(el);
|
|
|
|
clef->setSmall(false);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-06-05 11:37:21 +02:00
|
|
|
bool needKeysig = // keep key sigs in TABs: TABs themselves should hide them
|
|
|
|
isFirstSystem || styleB(StyleIdx::genKeysig);
|
2012-07-31 09:48:37 +02:00
|
|
|
|
2015-06-05 01:53:47 +02:00
|
|
|
if (needKeysig && !keysig && ((keyIdx.key() != Key::C) || keyIdx.custom() || keyIdx.isAtonal())) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// create missing key signature
|
|
|
|
//
|
2014-12-04 14:40:26 +01:00
|
|
|
keysig = new KeySig(this);
|
|
|
|
keysig->setKeySigEvent(keyIdx);
|
|
|
|
keysig->setTrack(i * VOICES);
|
|
|
|
keysig->setGenerated(true);
|
|
|
|
Segment* seg = m->undoGetSegment(Segment::Type::KeySig, tick);
|
|
|
|
keysig->setParent(seg);
|
|
|
|
keysig->layout();
|
2015-01-08 10:12:34 +01:00
|
|
|
undo(new AddElement(keysig));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else if (!needKeysig && keysig)
|
|
|
|
undoRemoveElement(keysig);
|
2014-12-08 18:02:17 +01:00
|
|
|
else if (keysig && !(keysig->keySigEvent() == keyIdx))
|
2014-06-02 13:07:19 +02:00
|
|
|
undo(new ChangeKeySig(keysig, keyIdx, keysig->showCourtesy()));
|
2012-07-31 09:48:37 +02:00
|
|
|
|
2015-02-16 15:26:36 +01:00
|
|
|
bool needClef = isFirstSystem
|
|
|
|
|| styleB(StyleIdx::genClef)
|
|
|
|
// real clef change?:
|
|
|
|
|| staff->clef(m->tick()) != staff->clef(m->tick()-1);
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (needClef) {
|
|
|
|
if (!clef) {
|
|
|
|
//
|
|
|
|
// create missing clef
|
|
|
|
//
|
|
|
|
clef = new Clef(this);
|
|
|
|
clef->setTrack(i * VOICES);
|
|
|
|
clef->setSmall(false);
|
2014-08-06 10:44:27 +02:00
|
|
|
clef->setGenerated(true);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-02-14 15:10:35 +01:00
|
|
|
Segment* ns = m->findSegment(Segment::Type::Clef, tick);
|
|
|
|
Segment* s;
|
|
|
|
if (ns && !ns->element(clef->track())) {
|
|
|
|
s = ns;
|
|
|
|
ns = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s = new Segment(m, Segment::Type::Clef, tick);
|
|
|
|
undoAddElement(s);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
clef->setParent(s);
|
2015-02-14 15:10:35 +01:00
|
|
|
|
|
|
|
// if there is already a clef at the same tick position,
|
|
|
|
// then this is the real clef change and we have to
|
|
|
|
// show the previous clef type at tick-1
|
|
|
|
|
|
|
|
ClefTypeList clefType = staff->clefType(ns ? tick - 1 : tick);
|
2012-07-31 09:48:37 +02:00
|
|
|
clef->layout();
|
2015-02-14 15:10:35 +01:00
|
|
|
clef->setClefType(clefType); // set before add !
|
2014-08-27 14:35:39 +02:00
|
|
|
undo(new AddElement(clef));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-09-04 17:41:12 +02:00
|
|
|
else if (clef->generated()) {
|
2014-08-16 11:21:56 +02:00
|
|
|
ClefTypeList cl = staff->clefType(tick);
|
2013-09-04 17:41:12 +02:00
|
|
|
if (cl != clef->clefTypeList())
|
|
|
|
undo(new ChangeClefType(clef, cl._concertClef, cl._transposingClef));
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (clef && clef->generated())
|
2014-08-27 14:35:39 +02:00
|
|
|
undo(new RemoveElement(clef));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
2014-05-23 12:10:15 +02:00
|
|
|
m->setStartRepeatBarLine(m->repeatFlags() & Repeat::START);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getNextSystem
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
System* Score::getNextSystem(bool isFirstSystem, bool isVbox)
|
|
|
|
{
|
|
|
|
System* system;
|
|
|
|
if (curSystem >= _systems.size()) {
|
|
|
|
system = new System(this);
|
|
|
|
_systems.append(system);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
system = _systems[curSystem];
|
|
|
|
system->clear(); // remove measures from system
|
|
|
|
}
|
|
|
|
system->setFirstSystem(isFirstSystem);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-10-15 15:39:01 +02:00
|
|
|
//---------------------------------------------------------
|
2013-10-15 16:42:22 +02:00
|
|
|
// validMMRestMeasure
|
|
|
|
// return true if this might be a measure in a
|
|
|
|
// multi measure rest
|
2013-10-15 15:39:01 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-10-15 16:42:22 +02:00
|
|
|
static bool validMMRestMeasure(Measure* m)
|
2013-10-15 15:39:01 +02:00
|
|
|
{
|
2013-10-15 16:42:22 +02:00
|
|
|
if (!m->isEmpty())
|
|
|
|
return false;
|
2014-02-24 13:53:43 +01:00
|
|
|
|
2014-04-24 10:42:42 +02:00
|
|
|
#if 0
|
2014-02-24 13:53:43 +01:00
|
|
|
auto l = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
|
|
|
|
for (::Interval<Spanner*> isp : l) {
|
|
|
|
Spanner* s = isp.value;
|
2015-01-19 12:37:17 +01:00
|
|
|
if (s->type() == Element::Type::VOLTA && (s->tick() == m->tick() || s->tick2() == m->endTick()))
|
2014-02-24 13:53:43 +01:00
|
|
|
return false;
|
|
|
|
}
|
2014-04-24 10:42:42 +02:00
|
|
|
#endif
|
|
|
|
|
2013-10-15 16:42:22 +02:00
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
2013-10-25 12:17:42 +02:00
|
|
|
for (Element* e : s->annotations()) {
|
2014-09-02 18:41:31 +02:00
|
|
|
if (e->type() != Element::Type::REHEARSAL_MARK &&
|
|
|
|
e->type() != Element::Type::TEMPO_TEXT &&
|
|
|
|
e->type() != Element::Type::HARMONY &&
|
|
|
|
e->type() != Element::Type::STAFF_TEXT)
|
2013-10-25 12:17:42 +02:00
|
|
|
return false;
|
|
|
|
}
|
2013-10-15 16:42:22 +02:00
|
|
|
}
|
|
|
|
return true;
|
2013-10-15 15:39:01 +02:00
|
|
|
}
|
|
|
|
|
2013-10-25 12:17:42 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// breakMultiMeasureRest
|
|
|
|
// return true if this measure should start a new
|
|
|
|
// multi measure rest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool breakMultiMeasureRest(Measure* m)
|
|
|
|
{
|
|
|
|
if (m->breakMultiMeasureRest())
|
|
|
|
return true;
|
2013-10-30 09:42:08 +01:00
|
|
|
auto sl = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
|
2014-02-12 10:58:00 +01:00
|
|
|
foreach (auto i, sl) {
|
|
|
|
Spanner* s = i.value;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (s->type() == Element::Type::VOLTA && (s->tick() == m->tick() || s->tick2() == m->tick()))
|
2014-04-24 10:42:42 +02:00
|
|
|
return true;
|
2014-02-12 10:58:00 +01:00
|
|
|
}
|
|
|
|
|
2014-09-02 19:54:36 +02:00
|
|
|
// break for marker in this measure
|
2015-02-17 20:22:24 +01:00
|
|
|
for (Element* e : m->el()) {
|
2014-09-02 19:54:36 +02:00
|
|
|
if (e->type() == Element::Type::MARKER) {
|
|
|
|
Marker* mark = static_cast<Marker*>(e);
|
|
|
|
if (!(mark->textStyle().align() & AlignmentFlags::RIGHT))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// break for marker & jump in previous measure
|
|
|
|
Measure* pm = m->prevMeasure();
|
|
|
|
if (pm) {
|
2015-02-17 20:22:24 +01:00
|
|
|
for (Element* e : pm->el()) {
|
2014-09-02 19:54:36 +02:00
|
|
|
if (e->type() == Element::Type::JUMP) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (e->type() == Element::Type::MARKER) {
|
|
|
|
Marker* mark = static_cast<Marker*>(e);
|
|
|
|
if (mark->textStyle().align() & AlignmentFlags::RIGHT)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
// break for end of volta
|
|
|
|
auto l = m->score()->spannerMap().findOverlapping(m->tick(), m->endTick());
|
|
|
|
for (::Interval<Spanner*> isp : l) {
|
|
|
|
Spanner* s = isp.value;
|
|
|
|
if (s->type() == Element::Type::VOLTA && (s->tick2() == m->endTick()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-25 12:17:42 +02:00
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
|
|
|
for (Element* e : s->annotations()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::REHEARSAL_MARK ||
|
|
|
|
e->type() == Element::Type::TEMPO_TEXT ||
|
2014-09-02 18:41:31 +02:00
|
|
|
((e->type() == Element::Type::HARMONY || e->type() == Element::Type::STAFF_TEXT) && (e->systemFlag() || m->score()->staff(e->staffIdx())->show())))
|
2013-10-25 12:17:42 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2013-09-19 15:08:54 +02:00
|
|
|
// createMMRests
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-09-19 15:08:54 +02:00
|
|
|
void Score::createMMRests()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-09-19 15:08:54 +02:00
|
|
|
//
|
|
|
|
// create mm rest measures
|
|
|
|
//
|
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
|
|
|
Measure* nm = m;
|
|
|
|
Measure* lm = nm;
|
|
|
|
int n = 0;
|
|
|
|
Fraction len;
|
2013-10-15 16:42:22 +02:00
|
|
|
while (validMMRestMeasure(nm)) {
|
2013-09-27 18:43:25 +02:00
|
|
|
m->setMMRestCount(0);
|
2013-09-19 15:08:54 +02:00
|
|
|
MeasureBase* mb = _showVBox ? nm->next() : nm->nextMeasure();
|
2013-10-25 12:17:42 +02:00
|
|
|
if (breakMultiMeasureRest(nm) && n)
|
2013-09-19 15:08:54 +02:00
|
|
|
break;
|
|
|
|
++n;
|
|
|
|
len += nm->len();
|
|
|
|
lm = nm;
|
|
|
|
nm = static_cast<Measure*>(mb);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (!nm || (nm->type() != Element::Type::MEASURE))
|
2013-09-19 15:08:54 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
if (n >= styleI(StyleIdx::minEmptyMeasures)) {
|
2013-09-19 15:08:54 +02:00
|
|
|
//
|
|
|
|
// create a multi measure rest from m to lm (inclusive)
|
|
|
|
// attach the measure to m
|
|
|
|
//
|
2013-10-28 15:52:32 +01:00
|
|
|
|
2013-09-27 18:43:25 +02:00
|
|
|
for (Measure* mm = m->nextMeasure(); mm; mm = mm->nextMeasure()) {
|
|
|
|
mm->setMMRestCount(-1);
|
2014-04-25 10:37:34 +02:00
|
|
|
if (mm->mmRest())
|
|
|
|
undo(new ChangeMMRest(mm, 0));
|
2013-09-27 18:43:25 +02:00
|
|
|
if (mm == lm)
|
|
|
|
break;
|
|
|
|
}
|
2013-10-28 15:52:32 +01:00
|
|
|
|
2015-04-09 13:48:32 +02:00
|
|
|
Measure* mmr = m->mmRest();
|
|
|
|
if (mmr) {
|
2013-10-28 15:52:32 +01:00
|
|
|
if (mmr->len() != len) {
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* s = mmr->findSegment(Segment::Type::EndBarLine, mmr->endTick());
|
2013-09-20 17:21:12 +02:00
|
|
|
mmr->setLen(len);
|
2013-09-23 12:26:16 +02:00
|
|
|
if (s)
|
|
|
|
s->setTick(mmr->endTick());
|
2013-09-20 17:21:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mmr = new Measure(this);
|
|
|
|
mmr->setLen(len);
|
2014-05-31 12:03:21 +02:00
|
|
|
mmr->setTick(m->tick());
|
2013-10-30 14:21:08 +01:00
|
|
|
undo(new ChangeMMRest(m, mmr));
|
2013-09-20 17:21:12 +02:00
|
|
|
}
|
|
|
|
|
2013-09-19 15:08:54 +02:00
|
|
|
mmr->setMMRestCount(n);
|
|
|
|
mmr->setNo(m->no());
|
2013-09-27 18:43:25 +02:00
|
|
|
mmr->setPageBreak(lm->pageBreak());
|
|
|
|
mmr->setLineBreak(lm->lineBreak());
|
2014-07-17 09:32:30 +02:00
|
|
|
|
2014-09-16 00:40:58 +02:00
|
|
|
BarLineType t;
|
2014-09-20 22:32:31 +02:00
|
|
|
// End repeats do not set endBarLineGenerated to false because
|
|
|
|
// they can be generated from the repeatFlags. So we need to test it separately.
|
2014-09-16 00:40:58 +02:00
|
|
|
if (lm->endBarLineGenerated() && !(lm->repeatFlags() & Repeat::END))
|
|
|
|
t = BarLineType::NORMAL;
|
|
|
|
else
|
|
|
|
t = lm->endBarLineType();
|
2014-07-17 09:32:30 +02:00
|
|
|
mmr->setEndBarLineType(t, false, lm->endBarLineVisible(), lm->endBarLineColor());
|
2014-09-16 00:40:58 +02:00
|
|
|
mmr->setRepeatFlags(m->repeatFlags() | lm->repeatFlags());
|
2015-02-25 10:48:39 +01:00
|
|
|
|
|
|
|
ElementList oldList = mmr->takeElements();
|
|
|
|
ElementList newList = lm->el();
|
2014-04-24 16:06:38 +02:00
|
|
|
|
2015-02-17 20:22:24 +01:00
|
|
|
for (Element* e : m->el()) {
|
2014-09-02 19:54:36 +02:00
|
|
|
if (e->type() == Element::Type::MARKER)
|
2015-02-25 10:48:39 +01:00
|
|
|
newList.append(e);
|
|
|
|
}
|
|
|
|
for (Element* e : newList) {
|
|
|
|
bool found = false;
|
|
|
|
for (Element* ee : oldList) {
|
|
|
|
if (ee->type() == e->type()) {
|
|
|
|
mmr->add(ee);
|
|
|
|
oldList.removeOne(ee);
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
2014-09-02 19:54:36 +02:00
|
|
|
mmr->add(e->clone());
|
|
|
|
}
|
2015-02-25 10:48:39 +01:00
|
|
|
for (Element* e : oldList)
|
|
|
|
delete e;
|
2015-04-09 13:48:32 +02:00
|
|
|
Segment* s = mmr->undoGetSegment(Segment::Type::ChordRest, mmr->tick());
|
2013-09-19 15:08:54 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
if (s->element(track) == 0) {
|
|
|
|
Rest* r = new Rest(this);
|
2014-05-21 15:43:19 +02:00
|
|
|
r->setDurationType(TDuration::DurationType::V_MEASURE);
|
2015-04-09 13:48:32 +02:00
|
|
|
r->setDuration(mmr->len());
|
2013-09-19 15:08:54 +02:00
|
|
|
r->setTrack(track);
|
2013-10-30 14:21:08 +01:00
|
|
|
r->setParent(s);
|
|
|
|
undo(new AddElement(r));
|
2013-09-19 15:08:54 +02:00
|
|
|
}
|
|
|
|
}
|
2014-05-31 12:03:21 +02:00
|
|
|
|
2013-09-19 15:08:54 +02:00
|
|
|
//
|
|
|
|
// check for clefs
|
|
|
|
//
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* cs = lm->findSegment(Segment::Type::Clef, lm->endTick());
|
|
|
|
Segment* ns = mmr->findSegment(Segment::Type::Clef, lm->endTick());
|
2013-09-19 15:08:54 +02:00
|
|
|
if (cs) {
|
2013-10-30 14:21:08 +01:00
|
|
|
if (ns == 0)
|
2014-06-25 11:46:10 +02:00
|
|
|
ns = mmr->undoGetSegment(Segment::Type::Clef, lm->endTick());
|
2013-09-19 15:08:54 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-30 14:21:08 +01:00
|
|
|
else if (ns)
|
|
|
|
undo(new RemoveElement(ns));
|
2013-09-19 15:08:54 +02:00
|
|
|
//
|
|
|
|
// check for time signature
|
|
|
|
//
|
2014-06-25 11:46:10 +02:00
|
|
|
cs = m->findSegment(Segment::Type::TimeSig, m->tick());
|
|
|
|
ns = mmr->findSegment(Segment::Type::TimeSig, m->tick());
|
2013-09-19 15:08:54 +02:00
|
|
|
if (cs) {
|
2013-10-30 14:21:08 +01:00
|
|
|
if (ns == 0)
|
2014-06-25 11:46:10 +02:00
|
|
|
ns = mmr->undoGetSegment(Segment::Type::TimeSig, m->tick());
|
2013-09-19 15:08:54 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
TimeSig* ts = static_cast<TimeSig*>(cs->element(track));
|
|
|
|
if (ts) {
|
2014-10-07 11:32:09 +02:00
|
|
|
TimeSig* nts = static_cast<TimeSig*>(ns->element(track));
|
|
|
|
if (!nts) {
|
|
|
|
nts = ts->clone();
|
2013-10-30 14:21:08 +01:00
|
|
|
nts->setParent(ns);
|
|
|
|
undo(new AddElement(nts));
|
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
else {
|
2014-10-07 11:32:09 +02:00
|
|
|
nts->setSig(ts->sig(), ts->timeSigType());
|
|
|
|
nts->layout();
|
2013-09-19 15:08:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-31 13:23:27 +02:00
|
|
|
else if (ns)
|
2013-10-30 14:21:08 +01:00
|
|
|
undo(new RemoveElement(ns));
|
|
|
|
|
2013-09-19 17:17:22 +02:00
|
|
|
//
|
|
|
|
// check for key signature
|
|
|
|
//
|
2014-06-25 11:46:10 +02:00
|
|
|
cs = m->findSegment(Segment::Type::KeySig, m->tick());
|
|
|
|
ns = mmr->findSegment(Segment::Type::KeySig, m->tick());
|
2013-09-19 17:17:22 +02:00
|
|
|
if (cs) {
|
2013-10-30 14:21:08 +01:00
|
|
|
if (ns == 0)
|
2014-06-25 11:46:10 +02:00
|
|
|
ns = mmr->undoGetSegment(Segment::Type::KeySig, m->tick());
|
2013-09-19 17:17:22 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
2014-08-21 15:01:54 +02:00
|
|
|
KeySig* ts = static_cast<KeySig*>(cs->element(track));
|
|
|
|
KeySig* nts = static_cast<KeySig*>(ns->element(track));
|
2013-09-19 17:17:22 +02:00
|
|
|
if (ts) {
|
2014-08-21 15:01:54 +02:00
|
|
|
if (!nts) {
|
2014-05-31 12:03:21 +02:00
|
|
|
KeySig* nks = ts->clone();
|
|
|
|
nks->setParent(ns);
|
|
|
|
undo(new AddElement(nks));
|
|
|
|
}
|
2013-09-19 17:17:22 +02:00
|
|
|
else {
|
2014-12-04 14:40:26 +01:00
|
|
|
if (!(nts->keySigEvent() == ts->keySigEvent())) {
|
2014-08-21 15:01:54 +02:00
|
|
|
undo(new ChangeKeySig(nts, ts->keySigEvent(), nts->showCourtesy()));
|
|
|
|
}
|
2013-09-19 17:17:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-31 12:03:21 +02:00
|
|
|
else if (ns && ns->isEmpty())
|
2013-10-30 14:21:08 +01:00
|
|
|
undo(new RemoveElement(ns));
|
|
|
|
|
2013-10-25 12:17:42 +02:00
|
|
|
//
|
2014-09-02 18:41:31 +02:00
|
|
|
// check for rehearsal mark etc.
|
2013-10-25 12:17:42 +02:00
|
|
|
//
|
2014-06-25 11:46:10 +02:00
|
|
|
cs = m->findSegment(Segment::Type::ChordRest, m->tick());
|
2014-05-29 14:04:14 +02:00
|
|
|
if (cs) {
|
|
|
|
for (Element* e : cs->annotations()) {
|
2014-09-02 18:41:31 +02:00
|
|
|
if (e->type() != Element::Type::REHEARSAL_MARK &&
|
|
|
|
e->type() != Element::Type::TEMPO_TEXT &&
|
|
|
|
e->type() != Element::Type::HARMONY &&
|
|
|
|
e->type() != Element::Type::STAFF_TEXT)
|
2014-05-29 14:04:14 +02:00
|
|
|
continue;
|
2013-10-30 14:21:08 +01:00
|
|
|
|
2014-05-29 14:04:14 +02: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));
|
2013-10-30 14:21:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (Element* e : s->annotations()) {
|
2014-09-02 18:41:31 +02:00
|
|
|
if (e->type() != Element::Type::REHEARSAL_MARK &&
|
|
|
|
e->type() != Element::Type::TEMPO_TEXT &&
|
|
|
|
e->type() != Element::Type::HARMONY &&
|
|
|
|
e->type() != Element::Type::STAFF_TEXT)
|
2013-10-30 14:21:08 +01:00
|
|
|
continue;
|
|
|
|
bool found = false;
|
|
|
|
for (Element* ee : cs->annotations()) {
|
|
|
|
if (ee->type() == e->type() && ee->track() == e->track()) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-25 12:17:42 +02:00
|
|
|
}
|
2013-10-30 14:21:08 +01:00
|
|
|
if (!found)
|
|
|
|
undo(new RemoveElement(e));
|
2013-10-25 12:17:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-19 15:08:54 +02:00
|
|
|
mmr->setNext(nm);
|
|
|
|
mmr->setPrev(m->prev());
|
|
|
|
m = lm;
|
|
|
|
}
|
2014-08-17 12:41:44 +02:00
|
|
|
else {
|
|
|
|
if (m->mmRest())
|
|
|
|
undo(new ChangeMMRest(m, 0));
|
|
|
|
m->setMMRestCount(0);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-06 12:35:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cautionaryWidth
|
2014-10-21 18:57:43 +02:00
|
|
|
// 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-09-06 12:35:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
qreal Score::cautionaryWidth(Measure* m, bool& hasCourtesy)
|
2012-09-06 12:35:34 +02:00
|
|
|
{
|
2014-10-27 05:53:50 +01:00
|
|
|
hasCourtesy = false;
|
2012-09-06 12:35:34 +02:00
|
|
|
if (m == 0)
|
2014-10-21 18:57:43 +02:00
|
|
|
return 0.0;
|
|
|
|
Measure* nm = m->nextMeasure();
|
2014-05-30 10:15:36 +02:00
|
|
|
if (nm == 0 || (m->sectionBreak() && _layoutMode != LayoutMode::FLOAT))
|
2014-10-21 18:57:43 +02:00
|
|
|
return 0.0;
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
int tick = m->endTick();
|
2012-09-06 12:35:34 +02:00
|
|
|
|
|
|
|
// locate a time sig. in the next measure and, if found,
|
|
|
|
// check if it has caut. sig. turned off
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* ns = nm->findSegment(Segment::Type::TimeSig, tick);
|
2014-10-21 18:57:43 +02:00
|
|
|
bool showCourtesy = styleB(StyleIdx::genCourtesyTimesig);
|
|
|
|
|
2014-12-18 20:41:15 +01:00
|
|
|
qreal w = 0.0;
|
2014-10-21 18:57:43 +02:00
|
|
|
if (showCourtesy && ns) {
|
|
|
|
TimeSig* ts = static_cast<TimeSig*>(ns->element(0));
|
|
|
|
if (ts && ts->showCourtesySig()) {
|
2014-10-27 05:53:50 +01:00
|
|
|
qreal leftMargin = point(styleS(StyleIdx::timesigLeftMargin));
|
2014-10-21 18:57:43 +02:00
|
|
|
Segment* s = m->findSegment(Segment::Type::TimeSigAnnounce, tick);
|
|
|
|
if (s && s->element(0)) {
|
2014-10-27 05:53:50 +01:00
|
|
|
w = static_cast<TimeSig*>(s->element(0))->width() + leftMargin;
|
2014-10-21 18:57:43 +02:00
|
|
|
hasCourtesy = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ts->layout();
|
2014-10-27 05:53:50 +01:00
|
|
|
w = ts->width() + leftMargin;
|
2014-10-21 18:57:43 +02:00
|
|
|
hasCourtesy = false;
|
|
|
|
}
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// courtesy key signatures
|
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
showCourtesy = styleB(StyleIdx::genCourtesyKeysig);
|
|
|
|
ns = nm->findSegment(Segment::Type::KeySig, tick);
|
|
|
|
|
|
|
|
qreal wwMax = 0.0;
|
|
|
|
if (showCourtesy && ns) {
|
2014-10-27 05:53:50 +01:00
|
|
|
qreal leftMargin = point(styleS(StyleIdx::keysigLeftMargin));
|
2014-10-21 18:57:43 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
|
|
|
|
KeySig* nks = static_cast<KeySig*>(ns->element(track));
|
|
|
|
|
|
|
|
if (nks && nks->showCourtesy() && !nks->generated()) {
|
|
|
|
Segment* s = m->findSegment(Segment::Type::KeySigAnnounce, tick);
|
|
|
|
|
|
|
|
if (s && s->element(track)) {
|
2014-10-27 05:53:50 +01:00
|
|
|
wwMax = qMax(wwMax, s->element(track)->width()) + leftMargin;
|
2014-10-21 18:57:43 +02:00
|
|
|
hasCourtesy = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nks->layout();
|
2014-10-27 05:53:50 +01:00
|
|
|
wwMax = qMax(wwMax, nks->width()) + leftMargin;
|
|
|
|
//hasCourtesy = false;
|
2014-10-21 18:57:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
}
|
2014-10-21 18:57:43 +02:00
|
|
|
w += wwMax;
|
2014-10-22 17:17:26 +02:00
|
|
|
|
2014-10-27 05:53:50 +01:00
|
|
|
return w; //* 1.5
|
2012-09-06 12:35:34 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSystem
|
|
|
|
// return true if line continues
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
bool Score::layoutSystem(qreal& minWidth, qreal systemWidth, bool isFirstSystem, bool longName)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2012-08-02 18:33:43 +02:00
|
|
|
if (undoRedo()) // no change possible in this state
|
|
|
|
return layoutSystem1(minWidth, isFirstSystem, longName);
|
2012-05-26 14:26:10 +02:00
|
|
|
System* system = getNextSystem(isFirstSystem, false);
|
|
|
|
|
|
|
|
qreal xo = 0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::HBOX)
|
2012-05-26 14:26:10 +02:00
|
|
|
xo = point(static_cast<Box*>(curMeasure)->boxWidth());
|
|
|
|
|
|
|
|
system->setInstrumentNames(longName);
|
2015-05-05 18:28:39 +02:00
|
|
|
system->layoutSystem(xo);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal minMeasureWidth = point(styleS(StyleIdx::minMeasureWidth));
|
2012-05-26 14:26:10 +02:00
|
|
|
minWidth = system->leftMargin();
|
|
|
|
bool continueFlag = false;
|
|
|
|
bool isFirstMeasure = true;
|
|
|
|
Measure* firstMeasure = 0;
|
|
|
|
Measure* lastMeasure = 0;
|
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal measureSpacing = styleD(StyleIdx::measureSpacing);
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
for (; curMeasure;) {
|
|
|
|
MeasureBase* nextMeasure;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::MEASURE && !_showVBox)
|
2013-09-19 15:08:54 +02:00
|
|
|
nextMeasure = curMeasure->nextMeasureMM();
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2013-09-19 15:08:54 +02:00
|
|
|
nextMeasure = curMeasure->nextMM();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-12-31 12:33:30 +01:00
|
|
|
Q_ASSERT(nextMeasure != curMeasure);
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
System* oldSystem = curMeasure->system();
|
|
|
|
curMeasure->setSystem(system);
|
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
bool hasCourtesy;
|
2012-09-06 12:35:34 +02:00
|
|
|
qreal cautionaryW = 0.0;
|
2014-10-21 18:57:43 +02:00
|
|
|
qreal ww = 0.0;
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::HBOX) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ww = point(static_cast<Box*>(curMeasure)->boxWidth());
|
|
|
|
if (!isFirstMeasure) {
|
|
|
|
// try to put another system on current row
|
|
|
|
// if not a line break
|
|
|
|
switch(_layoutMode) {
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::FLOAT:
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::LINE:
|
|
|
|
case LayoutMode::PAGE:
|
|
|
|
case LayoutMode::SYSTEM:
|
2015-02-25 11:14:27 +01:00
|
|
|
continueFlag = !(curMeasure->lineBreak()
|
|
|
|
|| curMeasure->sectionBreak()
|
|
|
|
|| curMeasure->pageBreak());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (curMeasure->type() == Element::Type::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = static_cast<Measure*>(curMeasure);
|
2012-08-01 22:15:58 +02:00
|
|
|
m->createEndBarLines(); // TODO: type not set right here
|
|
|
|
if (isFirstMeasure) {
|
2012-05-26 14:26:10 +02:00
|
|
|
firstMeasure = m;
|
2013-01-28 18:05:38 +01:00
|
|
|
addSystemHeader(m, isFirstSystem);
|
2012-08-01 22:15:58 +02:00
|
|
|
ww = m->minWidth2();
|
2012-07-31 09:48:37 +02:00
|
|
|
}
|
2012-08-01 22:15:58 +02:00
|
|
|
else
|
|
|
|
ww = m->minWidth1();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-08-02 18:33:43 +02:00
|
|
|
Segment* s = m->last();
|
2014-06-25 11:46:10 +02:00
|
|
|
if ((s->segmentType() == Segment::Type::EndBarLine) && s->element(0)) {
|
2012-08-02 18:33:43 +02:00
|
|
|
BarLine* bl = static_cast<BarLine*>(s->element(0));
|
2013-03-05 20:23:59 +01:00
|
|
|
BarLineType ot = bl->barLineType();
|
2012-08-10 10:14:17 +02:00
|
|
|
BarLineType nt = m->endBarLineType();
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2014-05-23 12:10:15 +02:00
|
|
|
if (m->repeatFlags() & Repeat::END)
|
2014-05-30 10:14:09 +02:00
|
|
|
nt = BarLineType::END_REPEAT;
|
2012-08-02 18:33:43 +02:00
|
|
|
else {
|
2013-09-19 15:08:54 +02:00
|
|
|
Measure* nm = m->nextMeasureMM();
|
2014-05-23 12:10:15 +02:00
|
|
|
if (nm && (nm->repeatFlags() & Repeat::START))
|
2014-05-30 10:14:09 +02:00
|
|
|
nt = BarLineType::START_REPEAT;
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
|
|
|
if (ot != nt) {
|
2013-05-29 11:49:58 +02:00
|
|
|
qreal mag = bl->magS();
|
|
|
|
ww += BarLine::layoutWidth(this, nt, mag)
|
|
|
|
- BarLine::layoutWidth(this, ot, mag);
|
2012-06-11 12:15:21 +02:00
|
|
|
}
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
qreal stretch = m->userStretch() * measureSpacing;
|
2014-10-21 18:57:43 +02:00
|
|
|
ww *= stretch;
|
2014-10-22 17:17:26 +02:00
|
|
|
cautionaryW = cautionaryWidth(m, hasCourtesy) * stretch;
|
|
|
|
|
2014-10-27 05:53:50 +01:00
|
|
|
// 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)
|
2014-10-21 18:57:43 +02:00
|
|
|
if (!hasCourtesy)
|
|
|
|
ww += cautionaryW;
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2012-07-31 09:48:37 +02:00
|
|
|
if (ww < minMeasureWidth)
|
|
|
|
ww = minMeasureWidth;
|
2012-05-26 14:26:10 +02:00
|
|
|
isFirstMeasure = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect at least one measure
|
2012-09-06 12:35:34 +02:00
|
|
|
bool empty = system->measures().isEmpty();
|
2014-10-28 11:47:04 +01:00
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
if (!empty && (minWidth + ww > systemWidth)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
curMeasure->setSystem(oldSystem);
|
2015-03-10 03:16:46 +01:00
|
|
|
continueFlag = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
lastMeasure = static_cast<Measure*>(curMeasure);
|
2012-07-31 09:48:37 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
system->measures().append(curMeasure);
|
2012-09-06 12:35:34 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type nt;
|
2012-09-29 16:46:45 +02:00
|
|
|
if (_showVBox)
|
2014-06-24 18:36:02 +02:00
|
|
|
nt = curMeasure->nextMM() ? curMeasure->nextMM()->type() : Element::Type::INVALID;
|
2012-09-29 16:46:45 +02:00
|
|
|
else
|
2014-06-24 18:36:02 +02:00
|
|
|
nt = curMeasure->nextMeasureMM() ? curMeasure->nextMeasureMM()->type() : Element::Type::INVALID;
|
2014-10-21 18:57:43 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
bool pbreak;
|
|
|
|
switch (_layoutMode) {
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::PAGE:
|
|
|
|
case LayoutMode::SYSTEM:
|
2015-02-25 11:14:27 +01:00
|
|
|
pbreak = curMeasure->pageBreak() || curMeasure->lineBreak() || curMeasure->sectionBreak();
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::FLOAT:
|
|
|
|
case LayoutMode::LINE:
|
2012-05-26 14:26:10 +02:00
|
|
|
pbreak = false;
|
|
|
|
break;
|
|
|
|
}
|
2015-02-19 15:42:24 +01:00
|
|
|
if (continueFlag
|
2012-09-13 18:01:34 +02:00
|
|
|
|| pbreak
|
2014-06-24 18:36:02 +02:00
|
|
|
|| (nt == Element::Type::VBOX || nt == Element::Type::TBOX || nt == Element::Type::FBOX)
|
2012-09-13 18:01:34 +02:00
|
|
|
) {
|
2014-05-30 10:15:36 +02:00
|
|
|
if (_layoutMode != LayoutMode::SYSTEM)
|
2012-12-04 15:28:26 +01:00
|
|
|
system->setPageBreak(curMeasure->pageBreak());
|
2015-03-10 03:16:46 +01:00
|
|
|
minWidth += ww;
|
2012-05-26 14:26:10 +02:00
|
|
|
curMeasure = nextMeasure;
|
|
|
|
break;
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
curMeasure = nextMeasure;
|
2014-10-21 18:57:43 +02:00
|
|
|
if (minWidth + minMeasureWidth > systemWidth) // small optimization
|
2012-09-06 12:35:34 +02:00
|
|
|
break; // next measure will not fit
|
|
|
|
|
|
|
|
minWidth += ww;
|
2014-10-27 05:53:50 +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
|
2014-10-21 18:57:43 +02:00
|
|
|
minWidth -= cautionaryW;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-05 12:14:58 +02:00
|
|
|
//
|
|
|
|
// remember line breaks in list of measures
|
|
|
|
//
|
2014-05-22 09:04:08 +02:00
|
|
|
int n = system->measures().size() - 1;
|
|
|
|
if (n >= 0) {
|
|
|
|
for (int i = 0; i < n; ++i)
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(system->measure(i), P_ID::BREAK_HINT, false);
|
|
|
|
undoChangeProperty(system->measures().last(), P_ID::BREAK_HINT, true);
|
2014-05-22 09:04:08 +02:00
|
|
|
}
|
2012-09-05 12:14:58 +02:00
|
|
|
|
2014-10-21 18:57:43 +02:00
|
|
|
if (firstMeasure && lastMeasure && firstMeasure != lastMeasure)
|
2012-08-01 18:00:27 +02:00
|
|
|
removeGeneratedElements(firstMeasure, lastMeasure);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-03-29 15:01:21 +01:00
|
|
|
hideEmptyStaves(system, isFirstSystem);
|
|
|
|
|
|
|
|
return continueFlag && curMeasure;
|
|
|
|
}
|
|
|
|
|
2014-07-31 18:46:41 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// hideEmptyStaves
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-03-29 15:01:21 +01:00
|
|
|
void Score::hideEmptyStaves(System* system, bool isFirstSystem)
|
|
|
|
{
|
|
|
|
//
|
2012-05-26 14:26:10 +02:00
|
|
|
// hide empty staves
|
|
|
|
//
|
|
|
|
int staves = _staves.size();
|
|
|
|
int staffIdx = 0;
|
2014-08-06 16:37:41 +02:00
|
|
|
bool systemIsEmpty = true;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach (Staff* staff, _staves) {
|
|
|
|
SysStaff* s = system->staff(staffIdx);
|
|
|
|
bool oldShow = s->show();
|
2014-05-26 15:31:36 +02:00
|
|
|
if (styleB(StyleIdx::hideEmptyStaves)
|
2012-05-26 14:26:10 +02:00
|
|
|
&& (staves > 1)
|
2014-05-26 15:31:36 +02:00
|
|
|
&& !(isFirstSystem && styleB(StyleIdx::dontHideStavesInFirstSystem))
|
2014-07-31 18:46:41 +02:00
|
|
|
&& !staff->neverHide()
|
2012-05-26 14:26:10 +02:00
|
|
|
) {
|
|
|
|
bool hideStaff = true;
|
|
|
|
foreach(MeasureBase* m, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (m->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
Measure* measure = static_cast<Measure*>(m);
|
|
|
|
if (!measure->isMeasureRest(staffIdx)) {
|
|
|
|
hideStaff = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
foreach(MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(st * VOICES + voice));
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr == 0 || cr->type() == Element::Type::REST)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
int staffMove = cr->staffMove();
|
|
|
|
if (staffIdx == st + staffMove) {
|
|
|
|
hideStaff = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!hideStaff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!hideStaff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->setShow(hideStaff ? false : staff->show());
|
2014-08-06 16:37:41 +02:00
|
|
|
if (s->show()) {
|
|
|
|
systemIsEmpty = false;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-08-06 16:37:41 +02:00
|
|
|
systemIsEmpty = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
s->setShow(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldShow != s->show()) {
|
|
|
|
foreach (MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() != Element::Type::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
static_cast<Measure*>(mb)->createEndBarLines();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++staffIdx;
|
|
|
|
}
|
2014-08-06 16:37:41 +02:00
|
|
|
if (systemIsEmpty) {
|
|
|
|
foreach (Staff* staff, _staves) {
|
|
|
|
SysStaff* s = system->staff(staff->idx());
|
|
|
|
if (staff->showIfEmpty() && !s->show()) {
|
|
|
|
s->setShow(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-08-02 18:33:43 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSystem1
|
|
|
|
// used in undoRedo state
|
|
|
|
// return true if line continues
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::layoutSystem1(qreal& minWidth, bool isFirstSystem, bool longName)
|
|
|
|
{
|
|
|
|
System* system = getNextSystem(isFirstSystem, false);
|
|
|
|
|
|
|
|
qreal xo = 0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::HBOX)
|
2012-08-02 18:33:43 +02:00
|
|
|
xo = point(static_cast<Box*>(curMeasure)->boxWidth());
|
|
|
|
|
|
|
|
system->setInstrumentNames(longName);
|
2015-05-05 18:28:39 +02:00
|
|
|
system->layoutSystem(xo);
|
2012-08-02 18:33:43 +02:00
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal minMeasureWidth = point(styleS(StyleIdx::minMeasureWidth));
|
2012-08-02 18:33:43 +02:00
|
|
|
minWidth = system->leftMargin();
|
|
|
|
bool continueFlag = false;
|
|
|
|
bool isFirstMeasure = true;
|
|
|
|
|
|
|
|
for (; curMeasure;) {
|
|
|
|
MeasureBase* nextMeasure;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::MEASURE && !_showVBox)
|
2013-09-28 14:50:01 +02:00
|
|
|
nextMeasure = curMeasure->nextMeasureMM();
|
2012-08-02 18:33:43 +02:00
|
|
|
else
|
2013-09-28 14:50:01 +02:00
|
|
|
nextMeasure = curMeasure->nextMM();
|
2012-08-02 18:33:43 +02:00
|
|
|
|
|
|
|
// System* oldSystem = curMeasure->system();
|
|
|
|
curMeasure->setSystem(system);
|
|
|
|
qreal ww = 0.0;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (curMeasure->type() == Element::Type::HBOX) {
|
2012-08-02 18:33:43 +02:00
|
|
|
ww = point(static_cast<Box*>(curMeasure)->boxWidth());
|
|
|
|
if (!isFirstMeasure) {
|
|
|
|
// try to put another system on current row
|
|
|
|
// if not a line break
|
|
|
|
switch(_layoutMode) {
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::FLOAT:
|
2012-08-02 18:33:43 +02:00
|
|
|
break;
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::LINE:
|
|
|
|
case LayoutMode::PAGE:
|
|
|
|
case LayoutMode::SYSTEM:
|
2015-02-25 11:14:27 +01:00
|
|
|
continueFlag = !(curMeasure->lineBreak()
|
|
|
|
|| curMeasure->sectionBreak()
|
|
|
|
|| curMeasure->pageBreak());
|
2012-08-02 18:33:43 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (curMeasure->type() == Element::Type::MEASURE) {
|
2012-08-02 18:33:43 +02:00
|
|
|
Measure* m = static_cast<Measure*>(curMeasure);
|
|
|
|
m->createEndBarLines(); // TODO: type not set right here
|
|
|
|
if (isFirstMeasure) {
|
2013-01-28 18:05:38 +01:00
|
|
|
addSystemHeader(m, isFirstSystem);
|
2012-08-02 18:33:43 +02:00
|
|
|
ww = m->minWidth2();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ww = m->minWidth1();
|
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
ww *= m->userStretch() * styleD(StyleIdx::measureSpacing);
|
2012-08-02 18:33:43 +02:00
|
|
|
if (ww < minMeasureWidth)
|
|
|
|
ww = minMeasureWidth;
|
|
|
|
isFirstMeasure = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
minWidth += ww;
|
|
|
|
|
|
|
|
system->measures().append(curMeasure);
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type nt = curMeasure->next() ? curMeasure->next()->type() : Element::Type::INVALID;
|
2012-08-02 18:33:43 +02:00
|
|
|
bool pbreak;
|
|
|
|
switch (_layoutMode) {
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::PAGE:
|
|
|
|
case LayoutMode::SYSTEM:
|
2015-02-25 11:14:27 +01:00
|
|
|
pbreak = curMeasure->pageBreak() || curMeasure->lineBreak() || curMeasure->sectionBreak();
|
2012-08-02 18:33:43 +02:00
|
|
|
break;
|
2014-05-30 10:15:36 +02:00
|
|
|
case LayoutMode::FLOAT:
|
|
|
|
case LayoutMode::LINE:
|
2012-08-02 18:33:43 +02:00
|
|
|
pbreak = false;
|
|
|
|
break;
|
|
|
|
}
|
2015-02-19 15:42:24 +01:00
|
|
|
if (continueFlag || pbreak || (nt == Element::Type::VBOX || nt == Element::Type::TBOX || nt == Element::Type::FBOX)) {
|
2014-05-30 10:15:36 +02:00
|
|
|
if (_layoutMode != LayoutMode::SYSTEM)
|
2012-12-04 15:28:26 +01:00
|
|
|
system->setPageBreak(curMeasure->pageBreak());
|
2012-08-02 18:33:43 +02:00
|
|
|
curMeasure = nextMeasure;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// do not change line break
|
|
|
|
if (curMeasure->breakHint()) {
|
|
|
|
curMeasure = nextMeasure;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
curMeasure = nextMeasure;
|
|
|
|
}
|
|
|
|
|
2013-03-29 15:01:21 +01:00
|
|
|
hideEmptyStaves(system,isFirstSystem);
|
2012-08-02 18:33:43 +02:00
|
|
|
|
|
|
|
return continueFlag && curMeasure;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2012-08-01 18:00:27 +02:00
|
|
|
// removeGeneratedElements (System Header + TimeSig Announce)
|
2012-05-26 14:26:10 +02:00
|
|
|
// helper function
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
void Score::removeGeneratedElements(Measure* sm, Measure* em)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-05-12 18:24:35 +02:00
|
|
|
Measure* sectionStart = sm;
|
2013-09-20 12:59:31 +02:00
|
|
|
for (Measure* m = sm; m; m = m->nextMeasureMM()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// remove generated elements from all measures in [sm;em]
|
|
|
|
// assume: generated elements are only living in voice 0
|
|
|
|
// - do not remove end bar lines
|
|
|
|
// - set size of clefs to small
|
|
|
|
//
|
2015-05-12 18:24:35 +02:00
|
|
|
if (m->sectionBreak() && m->nextMeasureMM())
|
|
|
|
sectionStart = m->nextMeasureMM();
|
2012-05-26 14:26:10 +02:00
|
|
|
for (Segment* seg = m->first(); seg; seg = seg->next()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = seg->segmentType();
|
|
|
|
if (st == Segment::Type::EndBarLine)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2014-06-25 11:46:10 +02:00
|
|
|
if (st == Segment::Type::StartRepeatBarLine && m != sm) {
|
2013-10-25 12:17:42 +02:00
|
|
|
if (!undoRedo())
|
|
|
|
undoRemoveElement(seg);
|
|
|
|
else
|
|
|
|
qDebug("remove repeat segment in undo/redo");
|
|
|
|
continue;
|
2012-08-02 18:33:43 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
Element* el = seg->element(staffIdx * VOICES);
|
|
|
|
if (el == 0)
|
|
|
|
continue;
|
2015-04-09 13:48:32 +02:00
|
|
|
|
Fix #14576 : hiding courtesy time sig corrupts score
See http://musescore.org/en/node/14576 for discussion and examples.
1) Edited courtesy time sig (in any parameter: visibility, colour, text, ...) are saved to the score
2) When read back from file, such a time sig is not recognized as courtesy time sig
3) If the edited courtesy time sig no longer is at the end of a system, it is not removed.
The straight approach could have been to disable any editing of courtesy time sig. Rather than loosing this flexibility, I tried to improve the management of courtesy sigs:
1) not changed
2) when reading a time sig, if not at the beginning of a measure, now it is turned into a courtesy time sig
3) courtesy time sig removal now depends on its position in the system and not on the generated status.
This patch also do the same changes, in the same code areas, for courtesy key signs, as they behave in a very similar way.
2014-04-16 00:55:11 +02:00
|
|
|
// courtesy time sigs and key sigs: remove if not in last measure (generated or not!)
|
2015-05-12 18:24:35 +02:00
|
|
|
// clefs & keysig: remove if generated and not at beginning of first measure of a section
|
2014-06-25 11:46:10 +02:00
|
|
|
if ( ((st == Segment::Type::TimeSigAnnounce || st == Segment::Type::KeySigAnnounce) && m != em)
|
2015-05-12 18:24:35 +02:00
|
|
|
|| ((el->type() == Element::Type::CLEF || el->type() == Element::Type::KEYSIG) && el->generated() && seg->tick() != sectionStart->tick())
|
Fix #14576 : hiding courtesy time sig corrupts score
See http://musescore.org/en/node/14576 for discussion and examples.
1) Edited courtesy time sig (in any parameter: visibility, colour, text, ...) are saved to the score
2) When read back from file, such a time sig is not recognized as courtesy time sig
3) If the edited courtesy time sig no longer is at the end of a system, it is not removed.
The straight approach could have been to disable any editing of courtesy time sig. Rather than loosing this flexibility, I tried to improve the management of courtesy sigs:
1) not changed
2) when reading a time sig, if not at the beginning of a measure, now it is turned into a courtesy time sig
3) courtesy time sig removal now depends on its position in the system and not on the generated status.
This patch also do the same changes, in the same code areas, for courtesy key signs, as they behave in a very similar way.
2014-04-16 00:55:11 +02:00
|
|
|
)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2012-08-01 22:15:58 +02:00
|
|
|
undoRemoveElement(el);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (el->type() == Element::Type::CLEF) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Clef* clef = static_cast<Clef*>(el);
|
|
|
|
System* s = m->system();
|
|
|
|
bool small = seg != m->first() || s->firstMeasure() != m;
|
|
|
|
if (clef->small() != small) {
|
|
|
|
clef->setSmall(small);
|
2012-08-01 18:00:27 +02:00
|
|
|
m->setDirty();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m == em)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addPage
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Page* Score::addPage()
|
|
|
|
{
|
|
|
|
Page* page = new Page(this);
|
|
|
|
page->setNo(_pages.size());
|
|
|
|
_pages.push_back(page);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// connectTies
|
|
|
|
/// Rebuild tie connections.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-08-11 21:17:55 +02:00
|
|
|
void Score::connectTies(bool silent)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
Measure* m = firstMeasure();
|
|
|
|
if (!m)
|
|
|
|
return;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment::Type st = Segment::Type::ChordRest;
|
2012-12-06 12:46:05 +01:00
|
|
|
for (Segment* s = m->first(st); s; s = s->next1(st)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int i = 0; i < tracks; ++i) {
|
2012-12-06 12:46:05 +01:00
|
|
|
Chord* c = static_cast<Chord*>(s->element(i));
|
2014-06-24 18:36:02 +02:00
|
|
|
if (c == 0 || c->type() != Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2015-03-19 20:40:30 +01:00
|
|
|
// connect grace note tie to main note in 1.3 scores
|
|
|
|
if (_mscVersion <= 114) {
|
|
|
|
for (Chord* gc : c->graceNotes()) {
|
|
|
|
for (Note* gn : gc->notes()) {
|
|
|
|
Tie* tie = gn->tieFor();
|
|
|
|
if (tie && !tie->endNote()) {
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
if (n->pitch() == gn->pitch()) {
|
|
|
|
tie->setEndNote(n);
|
|
|
|
n->setTieBack(tie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-28 10:55:25 +02:00
|
|
|
for (Note* n : c->notes()) {
|
2014-12-13 17:40:39 +01:00
|
|
|
// connect a tie without end note
|
2012-05-26 14:26:10 +02:00
|
|
|
Tie* tie = n->tieFor();
|
2014-12-13 17:40:39 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tie->setEndNote(nnote);
|
|
|
|
nnote->setTieBack(tie);
|
2014-08-11 21:17:55 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-12-13 17:40:39 +01:00
|
|
|
// connect a glissando without initial note (old glissando format)
|
|
|
|
for (Spanner* spanner : n->spannerBack())
|
|
|
|
if (spanner->type() == Element::Type::GLISSANDO
|
|
|
|
&& spanner->startElement() == nullptr) {
|
|
|
|
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);
|
2015-02-25 19:53:10 +01:00
|
|
|
initialNote->add(spanner);
|
2014-12-13 17:40:39 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
delete spanner;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-15 13:42:03 +02: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;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (nc->type() != Element::Type::CHORD)
|
2014-05-15 13:42:03 +02:00
|
|
|
qDebug("cannot connect tremolo");
|
|
|
|
else {
|
|
|
|
nc->setTremolo(tremolo);
|
|
|
|
tremolo->setChords(c, nc);
|
2015-05-27 04:03:47 +02:00
|
|
|
// cross-measure tremolos are not supported
|
|
|
|
// but can accidentally result from copy & paste
|
|
|
|
// remove them now
|
|
|
|
if (c->measure() != nc->measure())
|
|
|
|
c->remove(tremolo);
|
2014-05-15 13:42:03 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutFingering
|
|
|
|
// - place numbers above a note execpt for the last
|
|
|
|
// staff in a multi stave part (piano)
|
|
|
|
// - does not handle chords
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutFingering(Fingering* f)
|
|
|
|
{
|
|
|
|
if (f == 0)
|
|
|
|
return;
|
2014-10-17 03:43:54 +02:00
|
|
|
TextStyleType tst = f->textStyleType();
|
|
|
|
if (tst != TextStyleType::FINGERING && tst != TextStyleType::RH_GUITAR_FINGERING && tst != TextStyleType::STRING_NUMBER)
|
|
|
|
return;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Note* note = f->note();
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
Staff* staff = chord->staff();
|
|
|
|
Part* part = staff->part();
|
|
|
|
int n = part->nstaves();
|
2014-10-17 03:43:54 +02:00
|
|
|
bool voices = chord->measure()->hasVoices(staff->idx());
|
|
|
|
bool below = voices ? !chord->up() : (n > 1) && (staff->rstaff() == n-1);
|
|
|
|
bool tight = voices && !chord->beam();
|
2012-05-26 14:26:10 +02: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
|
|
|
|
|
|
|
|
if (chord->notes().size() == 1) {
|
|
|
|
x = headWidth * .5;
|
|
|
|
if (below) {
|
|
|
|
// place fingering below note
|
|
|
|
y = fh + spatium() * .4;
|
2014-10-17 03:43:54 +02:00
|
|
|
if (tight) {
|
|
|
|
y += 0.5 * spatium();
|
|
|
|
if (chord->stem())
|
|
|
|
x += 0.5 * spatium();
|
|
|
|
}
|
|
|
|
else if (chord->stem() && !chord->up()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// on stem side
|
|
|
|
y += chord->stem()->height();
|
|
|
|
x -= spatium() * .4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// place fingering above note
|
|
|
|
y = -headHeight - spatium() * .4;
|
2014-10-17 03:43:54 +02:00
|
|
|
if (tight) {
|
|
|
|
y -= 0.5 * spatium();
|
|
|
|
if (chord->stem())
|
|
|
|
x -= 0.5 * spatium();
|
|
|
|
}
|
|
|
|
else if (chord->stem() && chord->up()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// on stem side
|
|
|
|
y -= chord->stem()->height();
|
|
|
|
x += spatium() * .4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-17 03:43:54 +02:00
|
|
|
else {
|
|
|
|
x -= spatium();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
f->setUserOff(QPointF(x, y));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSystemRow
|
2012-10-08 08:28:19 +02:00
|
|
|
// return height in h
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QList<System*> Score::layoutSystemRow(qreal rowWidth, bool isFirstSystem, bool useLongName)
|
|
|
|
{
|
|
|
|
bool raggedRight = MScore::layoutDebug;
|
|
|
|
|
|
|
|
QList<System*> sl;
|
|
|
|
|
|
|
|
qreal ww = rowWidth;
|
|
|
|
qreal minWidth;
|
|
|
|
for (bool a = true; a;) {
|
|
|
|
a = layoutSystem(minWidth, ww, isFirstSystem, useLongName);
|
|
|
|
sl.append(_systems[curSystem]);
|
|
|
|
++curSystem;
|
|
|
|
ww -= minWidth;
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// dont stretch last system row, if minWidth is <= lastSystemFillLimit
|
|
|
|
//
|
2014-05-26 15:31:36 +02:00
|
|
|
if (curMeasure == 0 && ((minWidth / rowWidth) <= styleD(StyleIdx::lastSystemFillLimit)))
|
2012-05-26 14:26:10 +02:00
|
|
|
raggedRight = true;
|
|
|
|
|
|
|
|
//-------------------------------------------------------
|
|
|
|
// Round II
|
|
|
|
// stretch measures
|
|
|
|
// "nm" measures fit on this line of score
|
|
|
|
//-------------------------------------------------------
|
|
|
|
|
|
|
|
bool needRelayout = false;
|
|
|
|
|
|
|
|
foreach (System* system, sl) {
|
2014-09-16 13:20:15 +02:00
|
|
|
// set system initial bar line type here, as in System::layout...() methods
|
|
|
|
// it is either too early (in System::layout() measures are not added to the system yet)
|
|
|
|
// or too late (in System::layout2(), horizontal spacing has already been done
|
|
|
|
// and bar line width will be ignored).
|
|
|
|
// We only set bar line type here; track and bar line span values are set in System::layout2()
|
|
|
|
if (system->barLine() && system->firstMeasure())
|
|
|
|
system->barLine()->setBarLineType(system->firstMeasure()->systemInitialBarLineType());
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// add cautionary time/key signatures if needed
|
|
|
|
//
|
|
|
|
|
|
|
|
if (system->measures().isEmpty()) {
|
2014-06-05 20:58:05 +02:00
|
|
|
qFatal("System %p is empty", system);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
Measure* m = system->lastMeasure();
|
|
|
|
bool hasCourtesyKeysig = false;
|
|
|
|
Measure* nm = m ? m->nextMeasure() : 0;
|
|
|
|
Segment* s;
|
|
|
|
|
2015-05-01 18:36:08 +02:00
|
|
|
if (m && nm) {
|
2012-05-26 14:26:10 +02:00
|
|
|
int tick = m->tick() + m->ticks();
|
|
|
|
|
|
|
|
// locate a time sig. in the next measure and, if found,
|
|
|
|
// check if it has cout. sig. turned off
|
|
|
|
TimeSig* ts;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* tss = nm->findSegment(Segment::Type::TimeSig, tick);
|
2015-05-01 18:36:08 +02:00
|
|
|
bool showCourtesySig = tss && styleB(StyleIdx::genCourtesyTimesig) && !(m->sectionBreak() && _layoutMode != LayoutMode::FLOAT);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (showCourtesySig) {
|
|
|
|
ts = static_cast<TimeSig*>(tss->element(0));
|
|
|
|
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
|
2014-06-25 11:46:10 +02:00
|
|
|
s = m->undoGetSegment(Segment::Type::TimeSigAnnounce, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
int nstaves = Score::nstaves();
|
|
|
|
for (int track = 0; track < nstaves * VOICES; track += VOICES) {
|
|
|
|
TimeSig* nts = static_cast<TimeSig*>(tss->element(track));
|
|
|
|
if (!nts)
|
|
|
|
continue;
|
|
|
|
ts = static_cast<TimeSig*>(s->element(track));
|
|
|
|
if (ts == 0) {
|
|
|
|
ts = new TimeSig(this);
|
|
|
|
ts->setTrack(track);
|
|
|
|
ts->setGenerated(true);
|
|
|
|
ts->setParent(s);
|
|
|
|
undoAddElement(ts);
|
|
|
|
}
|
|
|
|
ts->setFrom(nts);
|
2012-07-31 09:48:37 +02:00
|
|
|
m->setDirty();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2012-07-27 18:01:15 +02:00
|
|
|
else {
|
|
|
|
// remove any existing time signatures
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* tss = m->findSegment(Segment::Type::TimeSigAnnounce, tick);
|
2012-07-27 18:01:15 +02:00
|
|
|
if (tss) {
|
|
|
|
undoRemoveElement(tss);
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
// courtesy key signatures
|
|
|
|
int n = _staves.size();
|
|
|
|
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
Staff* staff = _staves[staffIdx];
|
2015-05-01 18:36:08 +02:00
|
|
|
showCourtesySig = styleB(StyleIdx::genCourtesyKeysig) && !(m->sectionBreak() && _layoutMode != LayoutMode::FLOAT);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-12-08 18:02:17 +01:00
|
|
|
KeySigEvent key1 = staff->keySigEvent(tick - 1);
|
|
|
|
KeySigEvent key2 = staff->keySigEvent(tick);
|
2015-05-01 18:36:08 +02:00
|
|
|
if (showCourtesySig && !(key1 == key2)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// locate a key sig. in next measure and, if found,
|
|
|
|
// check if it has court. sig turned off
|
2014-06-25 11:46:10 +02:00
|
|
|
s = nm->findSegment(Segment::Type::KeySig, tick);
|
2014-05-30 13:35:44 +02:00
|
|
|
showCourtesySig = true; // assume this key change has court. sig turned on
|
2012-05-26 14:26:10 +02:00
|
|
|
if (s) {
|
|
|
|
KeySig* ks = static_cast<KeySig*>(s->element(track));
|
2012-07-27 18:01:15 +02:00
|
|
|
if (ks && !ks->showCourtesy())
|
2012-05-26 14:26:10 +02:00
|
|
|
showCourtesySig = false; // this key change has court. sig turned off
|
|
|
|
}
|
|
|
|
|
|
|
|
if (showCourtesySig) {
|
|
|
|
hasCourtesyKeysig = true;
|
2014-06-25 11:46:10 +02:00
|
|
|
s = m->undoGetSegment(Segment::Type::KeySigAnnounce, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
KeySig* ks = static_cast<KeySig*>(s->element(track));
|
|
|
|
|
|
|
|
if (!ks) {
|
|
|
|
ks = new KeySig(this);
|
2014-12-08 18:02:17 +01:00
|
|
|
ks->setKeySigEvent(key2);
|
2012-05-26 14:26:10 +02:00
|
|
|
ks->setTrack(track);
|
|
|
|
ks->setGenerated(true);
|
|
|
|
ks->setParent(s);
|
|
|
|
undoAddElement(ks);
|
|
|
|
}
|
2014-12-08 18:02:17 +01:00
|
|
|
else if (!(ks->keySigEvent() == key2)) {
|
|
|
|
undo(new ChangeKeySig(ks, key2, ks->showCourtesy()));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
// change bar line to qreal bar line
|
2014-05-30 10:14:09 +02:00
|
|
|
// m->setEndBarLineType(BarLineType::DOUBLE, true); // this caused issue #12918
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!showCourtesySig) {
|
|
|
|
// remove any existent courtesy key signature
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* s = m->findSegment(Segment::Type::KeySigAnnounce, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (s && s->element(track))
|
|
|
|
undoRemoveElement(s->element(track));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-24 20:11:51 +01:00
|
|
|
// courtesy clefs: show/hide of courtesy clefs moved to Clef::layout()
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// compute repeat bar lines
|
|
|
|
//
|
2013-10-25 12:17:42 +02:00
|
|
|
bool firstMeasure = true;
|
|
|
|
const QList<MeasureBase*>& ml = system->measures();
|
|
|
|
MeasureBase* lmb = ml.back();
|
2012-08-01 22:15:58 +02:00
|
|
|
|
2013-10-25 12:17:42 +02:00
|
|
|
for (MeasureBase* mb : ml) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() != Element::Type::MEASURE)
|
2013-10-25 12:17:42 +02:00
|
|
|
continue;
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
|
|
|
// first measure repeat?
|
2014-05-23 12:10:15 +02:00
|
|
|
bool fmr = firstMeasure && (m->repeatFlags() & Repeat::START);
|
2013-10-25 12:17:42 +02:00
|
|
|
|
|
|
|
if (mb == lmb) { // last measure in system?
|
|
|
|
//
|
|
|
|
// if last bar has a courtesy key signature,
|
|
|
|
// create a double bar line as end bar line
|
|
|
|
//
|
2014-05-30 10:14:09 +02:00
|
|
|
BarLineType bl = hasCourtesyKeysig ? BarLineType::DOUBLE : BarLineType::NORMAL;
|
2014-05-23 12:10:15 +02:00
|
|
|
if (m->repeatFlags() & Repeat::END)
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::END_REPEAT, m->endBarLineGenerated());
|
2013-10-25 12:17:42 +02:00
|
|
|
else if (m->endBarLineGenerated())
|
|
|
|
m->setEndBarLineType(bl, true);
|
|
|
|
if (m->setStartRepeatBarLine(fmr))
|
2012-08-01 22:15:58 +02:00
|
|
|
m->setDirty();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-25 12:17:42 +02:00
|
|
|
else {
|
|
|
|
MeasureBase* mb = m->next();
|
2014-06-24 18:36:02 +02:00
|
|
|
while (mb && mb->type() != Element::Type::MEASURE && (mb != lmb))
|
2013-10-25 12:17:42 +02:00
|
|
|
mb = mb->next();
|
|
|
|
|
|
|
|
Measure* nm = 0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb && mb->type() == Element::Type::MEASURE)
|
2013-10-25 12:17:42 +02:00
|
|
|
nm = static_cast<Measure*>(mb);
|
|
|
|
|
|
|
|
needRelayout |= m->setStartRepeatBarLine(fmr);
|
2014-05-23 12:10:15 +02:00
|
|
|
if (m->repeatFlags() & Repeat::END) {
|
|
|
|
if (nm && (nm->repeatFlags() & Repeat::START))
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::END_START_REPEAT, m->endBarLineGenerated());
|
2013-10-25 12:17:42 +02:00
|
|
|
else
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::END_REPEAT, m->endBarLineGenerated());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-05-23 12:10:15 +02:00
|
|
|
else if (nm && (nm->repeatFlags() & Repeat::START))
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::START_REPEAT, m->endBarLineGenerated());
|
2013-10-25 12:17:42 +02:00
|
|
|
else if (m->endBarLineGenerated())
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::NORMAL, m->endBarLineGenerated());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-25 12:17:42 +02:00
|
|
|
if (m->createEndBarLines())
|
|
|
|
m->setDirty();
|
|
|
|
firstMeasure = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-25 12:17:42 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
minWidth = 0.0;
|
|
|
|
qreal totalWeight = 0.0;
|
|
|
|
|
|
|
|
foreach(System* system, sl) {
|
|
|
|
foreach (MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::HBOX)
|
2012-05-26 14:26:10 +02:00
|
|
|
minWidth += point(((Box*)mb)->boxWidth());
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (mb->type() == Element::Type::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = (Measure*)mb;
|
2012-07-31 09:48:37 +02:00
|
|
|
if (needRelayout)
|
|
|
|
m->setDirty();
|
2012-08-01 22:15:58 +02:00
|
|
|
minWidth += m->minWidth2();
|
2012-05-26 14:26:10 +02:00
|
|
|
totalWeight += m->ticks() * m->userStretch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
minWidth += system->leftMargin();
|
|
|
|
}
|
|
|
|
|
|
|
|
// stretch incomplete row
|
2012-06-10 10:35:17 +02:00
|
|
|
qreal rest;
|
|
|
|
if (MScore::layoutDebug)
|
|
|
|
rest = 0;
|
|
|
|
else {
|
|
|
|
rest = rowWidth - minWidth;
|
|
|
|
if (raggedRight) {
|
|
|
|
if (minWidth > rest)
|
|
|
|
rest = rest * .5;
|
|
|
|
else
|
|
|
|
rest = minWidth;
|
|
|
|
}
|
|
|
|
rest /= totalWeight;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
qreal xx = 0.0;
|
|
|
|
qreal y = 0.0;
|
|
|
|
|
|
|
|
foreach(System* system, sl) {
|
|
|
|
QPointF pos;
|
|
|
|
|
|
|
|
bool firstMeasure = true;
|
|
|
|
foreach(MeasureBase* mb, system->measures()) {
|
|
|
|
qreal ww = 0.0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (firstMeasure) {
|
|
|
|
pos.rx() += system->leftMargin();
|
|
|
|
firstMeasure = false;
|
|
|
|
}
|
|
|
|
mb->setPos(pos);
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
2015-02-19 15:42:24 +01:00
|
|
|
qreal weight = m->ticks() * m->userStretch();
|
|
|
|
ww = m->minWidth2() + rest * weight;
|
2015-05-05 18:28:39 +02:00
|
|
|
m->layoutWidth(ww);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (mb->type() == Element::Type::HBOX) {
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setPos(pos);
|
|
|
|
ww = point(static_cast<Box*>(mb)->boxWidth());
|
|
|
|
mb->layout();
|
|
|
|
}
|
|
|
|
pos.rx() += ww;
|
|
|
|
}
|
|
|
|
system->setPos(xx, y);
|
|
|
|
qreal w = pos.x();
|
|
|
|
system->setWidth(w);
|
|
|
|
system->layout2();
|
|
|
|
foreach(MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::HBOX) {
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setHeight(system->height());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xx += w;
|
|
|
|
}
|
|
|
|
return sl;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSystems
|
|
|
|
// create list of systems
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutSystems()
|
|
|
|
{
|
2013-09-20 12:59:31 +02:00
|
|
|
curMeasure = _showVBox ? firstMM() : firstMeasureMM();
|
2012-05-26 14:26:10 +02:00
|
|
|
curSystem = 0;
|
|
|
|
bool firstSystem = true;
|
|
|
|
bool startWithLongNames = true;
|
|
|
|
|
|
|
|
qreal w = pageFormat()->printableWidth() * MScore::DPI;
|
|
|
|
|
|
|
|
while (curMeasure) {
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type t = curMeasure->type();
|
|
|
|
if (t == Element::Type::VBOX || t == Element::Type::TBOX || t == Element::Type::FBOX) {
|
2012-05-26 14:26:10 +02:00
|
|
|
System* system = getNextSystem(false, true);
|
2014-10-22 19:29:07 +02:00
|
|
|
system->setSameLine(false);
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(SysStaff* ss, *system->staves())
|
|
|
|
delete ss;
|
|
|
|
system->staves()->clear();
|
2013-06-28 17:46:24 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
system->setWidth(w);
|
|
|
|
VBox* vbox = static_cast<VBox*>(curMeasure);
|
|
|
|
vbox->setParent(system);
|
|
|
|
vbox->layout();
|
|
|
|
system->setHeight(vbox->height());
|
|
|
|
system->rxpos() = 0.0;
|
|
|
|
system->setPageBreak(vbox->pageBreak());
|
|
|
|
system->measures().push_back(vbox);
|
2013-09-20 12:59:31 +02:00
|
|
|
curMeasure = curMeasure->nextMM();
|
2012-05-26 14:26:10 +02:00
|
|
|
++curSystem;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
QList<System*> sl = layoutSystemRow(w, firstSystem, startWithLongNames);
|
|
|
|
for (int i = 0; i < sl.size(); ++i)
|
|
|
|
sl[i]->setSameLine(i != 0);
|
|
|
|
firstSystem = false;
|
|
|
|
startWithLongNames = false;
|
|
|
|
if (!sl.isEmpty()) {
|
|
|
|
Measure* lm = sl.back()->lastMeasure();
|
2014-05-30 10:15:36 +02:00
|
|
|
firstSystem = lm && lm->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
|
2012-05-26 14:26:10 +02:00
|
|
|
startWithLongNames = firstSystem && lm->sectionBreak()->startWithLongNames();
|
|
|
|
}
|
|
|
|
else
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("empty system!");
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: make undoable:
|
|
|
|
while (_systems.size() > curSystem)
|
|
|
|
_systems.takeLast();
|
|
|
|
}
|
|
|
|
|
2012-12-10 17:44:57 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutSystems2
|
|
|
|
// update distanceUp, distanceDown
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutSystems2()
|
|
|
|
{
|
|
|
|
int n = _systems.size();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
System* system = _systems.at(i);
|
|
|
|
if (!system->isVbox()) {
|
|
|
|
system->layout2();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutLinear
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutLinear()
|
|
|
|
{
|
|
|
|
curMeasure = first();
|
2013-04-29 16:34:05 +02:00
|
|
|
curSystem = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
System* system = getNextSystem(true, false);
|
|
|
|
system->setInstrumentNames(true);
|
2013-09-14 02:30:40 +02:00
|
|
|
qreal xo = 0;
|
|
|
|
|
|
|
|
Measure* fm = firstMeasure();
|
2013-10-07 18:39:14 +02:00
|
|
|
for (MeasureBase* m = first(); m != fm ; m = m->next()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (m->type() == Element::Type::HBOX)
|
2013-09-14 02:30:40 +02:00
|
|
|
xo += point(static_cast<Box*>(m)->boxWidth());
|
|
|
|
}
|
|
|
|
|
2015-05-05 18:28:39 +02:00
|
|
|
system->layoutSystem(xo);
|
2012-05-26 14:26:10 +02:00
|
|
|
system->setPos(0.0, spatium() * 10.0);
|
|
|
|
curPage = 0;
|
|
|
|
Page* page = getEmptyPage();
|
|
|
|
page->appendSystem(system);
|
|
|
|
|
2015-01-24 12:01:56 +01:00
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
2015-05-12 18:24:35 +02:00
|
|
|
Element::Type t = mb->type();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (t == Element::Type::VBOX || t == Element::Type::TBOX || t == Element::Type::FBOX) {
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
2015-01-24 12:01:56 +01:00
|
|
|
if (styleB(StyleIdx::createMultiMeasureRests) && mb->type() == Element::Type::MEASURE) {
|
2013-10-07 18:39:14 +02:00
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
|
|
|
if (m->hasMMRest())
|
|
|
|
mb = m->mmRest();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setSystem(system);
|
|
|
|
system->measures().append(mb);
|
|
|
|
}
|
|
|
|
if (system->measures().isEmpty())
|
|
|
|
return;
|
2013-10-07 18:39:14 +02:00
|
|
|
addSystemHeader(firstMeasureMM(), true);
|
2015-05-13 14:15:57 +02:00
|
|
|
// also add a system header after a section break
|
|
|
|
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM()) {
|
|
|
|
if (m->sectionBreak() && m->nextMeasureMM())
|
|
|
|
addSystemHeader(m->nextMeasureMM(), true);
|
|
|
|
}
|
2013-10-07 18:39:14 +02:00
|
|
|
removeGeneratedElements(firstMeasureMM(), lastMeasureMM());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-09-14 02:30:40 +02:00
|
|
|
QPointF pos(0.0, 0.0);
|
|
|
|
bool isFirstMeasure = true;
|
2013-10-07 18:39:14 +02:00
|
|
|
foreach (MeasureBase* mb, system->measures()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal w = 0.0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::MEASURE) {
|
2014-12-14 01:43:17 +01:00
|
|
|
if (isFirstMeasure)
|
2013-09-14 02:30:40 +02:00
|
|
|
pos.rx() += system->leftMargin();
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
2013-09-16 18:32:25 +02:00
|
|
|
Measure* nm = m->nextMeasure();
|
2014-11-28 19:56:09 +01:00
|
|
|
if (m->repeatFlags() & Repeat::END) {
|
2014-05-23 12:10:15 +02:00
|
|
|
if (nm && (nm->repeatFlags() & Repeat::START))
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::END_START_REPEAT, m->endBarLineGenerated());
|
2013-09-16 18:32:25 +02:00
|
|
|
else
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::END_REPEAT, m->endBarLineGenerated());
|
2013-09-16 18:32:25 +02:00
|
|
|
}
|
2014-05-23 12:10:15 +02:00
|
|
|
else if (nm && (nm->repeatFlags() & Repeat::START))
|
2014-05-30 10:14:09 +02:00
|
|
|
m->setEndBarLineType(BarLineType::START_REPEAT, m->endBarLineGenerated());
|
2013-09-16 18:32:25 +02:00
|
|
|
m->createEndBarLines();
|
2014-12-14 01:43:17 +01:00
|
|
|
if (isFirstMeasure) {
|
|
|
|
// width with header
|
|
|
|
qreal w2 = m->minWidth2();
|
|
|
|
// width *completely* excluding header
|
|
|
|
// minWidth1() includes the initial key / time signatures since they are considered non-generated
|
|
|
|
Segment* s = m->first();
|
|
|
|
while (s && s->segmentType() != Segment::Type::ChordRest)
|
|
|
|
s = s->next();
|
|
|
|
qreal w1 = s ? computeMinWidth(s, true) : m->minWidth1();
|
|
|
|
w = (w2 - w1) + w1 * styleD(StyleIdx::linearStretch);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
w = m->minWidth1() * styleD(StyleIdx::linearStretch);
|
|
|
|
}
|
2014-07-26 09:08:44 +02:00
|
|
|
qreal minMeasureWidth = point(styleS(StyleIdx::minMeasureWidth));
|
|
|
|
if (w < minMeasureWidth)
|
|
|
|
w = minMeasureWidth;
|
2015-05-05 18:28:39 +02:00
|
|
|
m->layoutWidth(w);
|
2014-12-14 01:43:17 +01:00
|
|
|
isFirstMeasure = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-09-16 18:32:25 +02:00
|
|
|
else {
|
|
|
|
mb->layout();
|
2012-05-26 14:26:10 +02:00
|
|
|
w = mb->width();
|
2013-09-16 18:32:25 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setPos(pos);
|
|
|
|
pos.rx() += w;
|
|
|
|
}
|
|
|
|
system->setWidth(pos.x());
|
|
|
|
page->setWidth(pos.x());
|
|
|
|
system->layout2();
|
2015-05-23 21:24:20 +02:00
|
|
|
page->setHeight(system->height() + 20 * spatium());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
while (_pages.size() > 1)
|
|
|
|
_pages.takeLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getEmptyPage
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Page* Score::getEmptyPage()
|
|
|
|
{
|
|
|
|
Page* page = curPage >= _pages.size() ? addPage() : _pages[curPage];
|
|
|
|
page->setNo(curPage);
|
|
|
|
page->layout();
|
|
|
|
qreal x = (curPage == 0) ? 0.0 : _pages[curPage - 1]->pos().x()
|
2014-06-24 23:16:34 +02:00
|
|
|
+ page->width() + (((curPage+_pageNumberOffset) & 1) ? 50.0 : 1.0);
|
2012-05-26 14:26:10 +02:00
|
|
|
++curPage;
|
|
|
|
page->setPos(x, 0.0);
|
|
|
|
page->systems()->clear();
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// SystemRow
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
struct SystemRow {
|
|
|
|
QList<System*> systems;
|
|
|
|
|
|
|
|
qreal height() const {
|
|
|
|
qreal h = 0.0;
|
|
|
|
foreach(System* s, systems) {
|
|
|
|
if (s->height() > h)
|
|
|
|
h = s->height();
|
|
|
|
}
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
bool isVbox() const {
|
2013-05-06 21:35:08 +02:00
|
|
|
return (systems.size() > 0) ? systems.back()->isVbox() : false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
VBox* vbox() const {
|
2013-05-06 21:35:08 +02:00
|
|
|
return (systems.size() > 0) ? systems.back()->vbox() : 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
bool pageBreak() const {
|
2013-05-06 21:35:08 +02:00
|
|
|
return (systems.size() > 0) ? systems.back()->pageBreak() : false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
qreal tm() const {
|
|
|
|
qreal v = 0.0;
|
|
|
|
foreach(System* s, systems) {
|
|
|
|
if (!s->staves()->isEmpty())
|
|
|
|
v = qMax(s->distanceUp(0), v);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
qreal bm() const {
|
|
|
|
qreal v = 0.0;
|
|
|
|
foreach(System* s, systems) {
|
|
|
|
int staffIdx = s->staves()->size() - 1;
|
|
|
|
if (staffIdx >= 0)
|
|
|
|
v = qMax(s->distanceDown(staffIdx), v);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
2015-03-10 23:22:00 +01:00
|
|
|
qreal extraDistance(int i) const {
|
|
|
|
qreal v = 0.0;
|
|
|
|
foreach(System* s, systems) {
|
|
|
|
foreach(MeasureBase* mb, s->measures())
|
|
|
|
v = qMax(mb->distanceDown(i), v);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
void clear() {
|
|
|
|
systems.clear();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// PageContext
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
struct PageContext {
|
|
|
|
qreal _spatium;
|
|
|
|
qreal slb;
|
|
|
|
Score* score;
|
|
|
|
Page* page;
|
|
|
|
qreal ey;
|
|
|
|
qreal y;
|
|
|
|
int gaps;
|
|
|
|
SystemRow sr;
|
|
|
|
|
|
|
|
System* lastSystem;
|
|
|
|
qreal prevDist;
|
|
|
|
|
|
|
|
PageContext(Score* s) : score(s) {
|
|
|
|
_spatium = score->spatium();
|
2014-05-26 15:31:36 +02:00
|
|
|
slb = score->styleS(StyleIdx::staffLowerBorder).val() * _spatium;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
void newPage() {
|
|
|
|
page = score->getEmptyPage();
|
|
|
|
ey = page->height() - page->bm();
|
|
|
|
y = page->tm();
|
|
|
|
gaps = 0;
|
|
|
|
lastSystem = 0;
|
|
|
|
prevDist = 0.0;
|
|
|
|
}
|
|
|
|
void layoutPage() {
|
|
|
|
qreal d = sr.isVbox() ? sr.vbox()->bottomGap() : slb;
|
|
|
|
score->layoutPage(*this, d);
|
|
|
|
}
|
|
|
|
qreal bm() const { return sr.bm(); }
|
|
|
|
qreal tm() const { return sr.tm(); }
|
|
|
|
};
|
|
|
|
|
2014-12-30 15:14:05 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutPages
|
|
|
|
// create list of pages
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutPages()
|
|
|
|
{
|
|
|
|
const qreal _spatium = spatium();
|
2014-05-26 15:31:36 +02:00
|
|
|
const qreal slb = styleS(StyleIdx::staffLowerBorder).val() * _spatium;
|
|
|
|
const qreal sub = styleS(StyleIdx::staffUpperBorder).val() * _spatium;
|
|
|
|
const qreal systemDist = styleS(StyleIdx::minSystemDistance).val() * _spatium;
|
|
|
|
const qreal systemFrameDistance = styleS(StyleIdx::systemFrameDistance).val() * _spatium;
|
|
|
|
const qreal frameSystemDistance = styleS(StyleIdx::frameSystemDistance).val() * _spatium;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
curPage = 0;
|
|
|
|
|
|
|
|
PageContext pC(this);
|
|
|
|
pC.newPage();
|
|
|
|
|
|
|
|
int nSystems = _systems.size();
|
|
|
|
|
2014-12-30 15:14:05 +01:00
|
|
|
PAGEDBG("layoutPages");
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int i = 0; i < nSystems; ++i) {
|
2015-01-05 13:17:04 +01:00
|
|
|
PAGEDBG(" system %d", i);
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// collect system row
|
|
|
|
//
|
|
|
|
pC.sr.clear();
|
|
|
|
for (;;) {
|
|
|
|
System* system = _systems[i];
|
|
|
|
pC.sr.systems.append(system);
|
|
|
|
if (i+1 == nSystems)
|
|
|
|
break;
|
|
|
|
if (!_systems[i+1]->sameLine())
|
|
|
|
break;
|
|
|
|
++i;
|
|
|
|
}
|
2015-01-05 13:17:04 +01:00
|
|
|
qreal tmargin; // top system margin
|
|
|
|
qreal bmargin; // bottom system margin
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (pC.sr.isVbox()) {
|
|
|
|
VBox* vbox = pC.sr.vbox();
|
|
|
|
bmargin = vbox->bottomGap();
|
2014-12-30 21:14:31 +01:00
|
|
|
tmargin = vbox->topGap();
|
2012-05-26 14:26:10 +02:00
|
|
|
if (pC.lastSystem) {
|
2015-03-09 22:07:13 +01:00
|
|
|
if (pC.lastSystem->isVbox())
|
2012-05-26 14:26:10 +02:00
|
|
|
tmargin += pC.lastSystem->vbox()->bottomGap();
|
|
|
|
else
|
|
|
|
tmargin += systemFrameDistance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (pC.lastSystem) {
|
|
|
|
if (pC.lastSystem->isVbox())
|
|
|
|
tmargin = pC.lastSystem->vbox()->bottomGap() + frameSystemDistance;
|
|
|
|
else
|
|
|
|
tmargin = qMax(pC.tm(), systemDist);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tmargin = qMax(pC.tm(), sub);
|
2012-12-10 17:44:57 +01:00
|
|
|
bmargin = pC.bm();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-10-22 19:29:07 +02:00
|
|
|
tmargin = qMax(tmargin, pC.prevDist);
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal h = pC.sr.height();
|
2015-01-05 13:17:04 +01:00
|
|
|
|
|
|
|
PAGEDBG(" y:%f + h:%f + tm:%f + qMax(bm:%f,%f)[=%f] > %f",
|
|
|
|
pC.y / MScore::DPMM,
|
|
|
|
h / MScore::DPMM,
|
2014-12-30 18:44:27 +01:00
|
|
|
tmargin / MScore::DPMM,
|
|
|
|
bmargin / MScore::DPMM,
|
2015-01-05 13:17:04 +01:00
|
|
|
slb / MScore::DPMM,
|
2014-12-30 18:44:27 +01:00
|
|
|
(pC.y + h + tmargin + qMax(bmargin, slb)) / MScore::DPMM,
|
2015-01-05 13:17:04 +01:00
|
|
|
pC.ey / MScore::DPMM
|
2014-12-30 18:44:27 +01:00
|
|
|
);
|
|
|
|
|
2012-12-10 17:44:57 +01:00
|
|
|
if (pC.lastSystem && (pC.y + h + tmargin + qMax(bmargin, slb) > pC.ey)) {
|
2014-12-30 15:14:05 +01:00
|
|
|
PAGEDBG(" == page break");
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// prepare next page
|
|
|
|
//
|
|
|
|
qreal d;
|
|
|
|
if (pC.lastSystem->isVbox())
|
|
|
|
d = pC.lastSystem->vbox()->bottomGap();
|
|
|
|
else
|
|
|
|
d = slb;
|
|
|
|
layoutPage(pC, d);
|
|
|
|
pC.newPage();
|
|
|
|
if (pC.sr.isVbox())
|
|
|
|
tmargin = pC.sr.vbox()->topGap();
|
|
|
|
else
|
|
|
|
tmargin = qMax(pC.sr.tm(), sub);
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal x = pC.page->lm();
|
|
|
|
pC.y += tmargin;
|
2014-07-22 18:31:32 +02:00
|
|
|
pC.prevDist = bmargin;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-01-05 13:17:04 +01:00
|
|
|
for (System* system : pC.sr.systems) {
|
2012-05-26 14:26:10 +02:00
|
|
|
system->setPos(x, pC.y);
|
|
|
|
x += system->width();
|
|
|
|
pC.page->appendSystem(system);
|
|
|
|
system->setAddStretch(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pC.lastSystem) {
|
|
|
|
bool addStretch = !pC.lastSystem->isVbox() && !pC.sr.isVbox();
|
|
|
|
pC.lastSystem->setAddStretch(addStretch);
|
|
|
|
if (addStretch)
|
|
|
|
++pC.gaps;
|
|
|
|
}
|
|
|
|
|
|
|
|
pC.y += h;
|
2014-05-30 10:15:36 +02:00
|
|
|
if (pC.sr.pageBreak() && (_layoutMode == LayoutMode::PAGE)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if ((i + 1) == nSystems)
|
|
|
|
break;
|
|
|
|
pC.layoutPage();
|
|
|
|
pC.newPage();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pC.lastSystem = pC.sr.systems.back();
|
|
|
|
}
|
|
|
|
if (pC.page)
|
|
|
|
pC.layoutPage();
|
|
|
|
|
|
|
|
// Remove not needed pages. TODO: make undoable:
|
|
|
|
while (_pages.size() > curPage)
|
|
|
|
_pages.takeLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// systemDistCompare
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool systemDistCompare(System* s1, System* s2)
|
|
|
|
{
|
|
|
|
return s1->distance() < s2->distance();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutPage
|
|
|
|
// increase system distance upto maxSystemDistance
|
|
|
|
// to fill page
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::layoutPage(const PageContext& pC, qreal d)
|
|
|
|
{
|
|
|
|
Page* page = pC.page;
|
|
|
|
int gaps = pC.gaps;
|
|
|
|
qreal restHeight = pC.ey - pC.y - d;
|
|
|
|
|
|
|
|
if (!gaps || MScore::layoutDebug) {
|
2014-05-30 10:15:36 +02:00
|
|
|
if (_layoutMode == LayoutMode::FLOAT) {
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal y = restHeight * .5;
|
|
|
|
int n = page->systems()->size();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
System* system = page->systems()->at(i);
|
2015-05-08 08:57:24 +02:00
|
|
|
system->move(QPointF(0.0, y));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-10 23:22:00 +01:00
|
|
|
// allow room for lyrics (and/or spacer) on last visible staff of last system
|
2015-03-09 22:07:13 +01:00
|
|
|
// these are not included in system height or in previous margin calculations
|
2015-03-11 15:38:32 +01:00
|
|
|
// TODO: why *not* include this space in system height?
|
2015-03-10 23:22:00 +01:00
|
|
|
System* lastSystem = page->systems()->last();
|
|
|
|
int lastStaff = nstaves() - 1;
|
|
|
|
if (!lastSystem->isVbox()) {
|
|
|
|
int lastVisible = -1;
|
|
|
|
for (int i = lastStaff; i >= 0; --i) {
|
|
|
|
if (staff(i)->show() && lastSystem->staff(i)->show()) {
|
|
|
|
lastVisible = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lastVisible >= 0) {
|
2015-03-11 15:38:32 +01:00
|
|
|
// allow lyrics to extend into the "music bottom margin" (as opposed to page margin)
|
|
|
|
// on the assumption that this is consistent with its main purpose
|
|
|
|
// the extra distance includes the "lyrics bottom margin"
|
|
|
|
// we could strip this off too, but then the lyrics might crowd the page margin too much
|
|
|
|
// TODO: consider another style parameter here
|
|
|
|
qreal allowableMargin = d; // + styleP(StyleIdx::lyricsMinBottomDistance);
|
|
|
|
qreal extra = qMax(pC.sr.extraDistance(lastVisible) - allowableMargin, 0.0);
|
2015-03-10 23:22:00 +01:00
|
|
|
// for last staff of system, this distance has not been accounted for at all
|
|
|
|
// for interior staves (with last staves hidden), this is only partially accounted for
|
|
|
|
if (lastVisible == lastStaff)
|
|
|
|
restHeight -= extra;
|
|
|
|
else
|
|
|
|
restHeight -= qMin(extra, styleP(StyleIdx::staffDistance));
|
|
|
|
}
|
2015-03-09 22:07:13 +01:00
|
|
|
}
|
|
|
|
|
2014-05-26 15:31:36 +02:00
|
|
|
const qreal maxStretch = styleP(StyleIdx::maxSystemDistance) - styleP(StyleIdx::minSystemDistance);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
QList<System*> slist;
|
|
|
|
int n = page->systems()->size();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
System* system = page->systems()->at(i);
|
|
|
|
qreal lastY1 = system->pos().y() + system->height();
|
|
|
|
|
|
|
|
if (system->addStretch()) {
|
|
|
|
System* ns = page->systems()->at(i + 1);
|
|
|
|
qreal dist = ns->pos().y() - lastY1;
|
|
|
|
system->setDistance(dist);
|
|
|
|
slist.append(system);
|
|
|
|
system->setStretchDistance(0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qSort(slist.begin(), slist.end(), systemDistCompare);
|
|
|
|
|
|
|
|
n = slist.size();
|
|
|
|
for (int i = 0; i < (n-1); ++i) {
|
|
|
|
System* s1 = slist.at(i);
|
|
|
|
System* s2 = slist.at(i + 1);
|
|
|
|
qreal td = s2->distance() - s1->distance();
|
|
|
|
if (td > 0.001) {
|
|
|
|
int nn = i + 1;
|
|
|
|
qreal tdd = td * nn;
|
|
|
|
if (tdd > restHeight) {
|
|
|
|
tdd = restHeight;
|
|
|
|
td = tdd / nn;
|
|
|
|
}
|
2015-01-05 13:17:04 +01:00
|
|
|
if ((s1->stretchDistance() + td) > maxStretch) {
|
2012-05-26 14:26:10 +02:00
|
|
|
td = maxStretch - s1->stretchDistance();
|
|
|
|
tdd = td * nn;
|
|
|
|
}
|
|
|
|
for (int k = 0; k <= i; ++k)
|
|
|
|
slist.at(k)->addStretchDistance(td);
|
|
|
|
restHeight -= tdd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qreal td = restHeight / n;
|
|
|
|
if (td > 0.001) {
|
|
|
|
qreal sd = slist.at(0)->stretchDistance();
|
|
|
|
if (sd + td > maxStretch)
|
|
|
|
td = maxStretch - sd;
|
|
|
|
for (int k = 0; k < n; ++k)
|
|
|
|
slist.at(k)->addStretchDistance(td);
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal y = 0.0;
|
2015-01-05 13:17:04 +01:00
|
|
|
|
|
|
|
for (System* system : *page->systems()) {
|
2015-05-08 08:57:24 +02:00
|
|
|
system->move(QPointF(0.0, y));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (system->addStretch())
|
|
|
|
y += system->stretchDistance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// doLayoutSystems
|
|
|
|
// layout staves in a system
|
|
|
|
// layout pages
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::doLayoutSystems()
|
|
|
|
{
|
2013-06-05 15:47:34 +02:00
|
|
|
foreach(System* system, _systems)
|
|
|
|
system->layout2();
|
2014-05-30 10:15:36 +02:00
|
|
|
if (layoutMode() != LayoutMode::LINE)
|
2014-05-11 06:07:41 +02:00
|
|
|
layoutPages();
|
2013-06-05 15:47:34 +02:00
|
|
|
rebuildBspTree();
|
|
|
|
_updateAll = true;
|
|
|
|
|
2014-07-02 09:55:50 +02:00
|
|
|
for (MuseScoreView* v : viewer)
|
2012-05-26 14:26:10 +02:00
|
|
|
v->layoutChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// doLayoutPages
|
|
|
|
// small wrapper for layoutPages()
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::doLayoutPages()
|
|
|
|
{
|
2013-06-05 15:47:34 +02:00
|
|
|
layoutPages();
|
|
|
|
rebuildBspTree();
|
|
|
|
_updateAll = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(MuseScoreView* v, viewer)
|
|
|
|
v->layoutChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// sff
|
|
|
|
// compute 1/Force for a given Extend
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
qreal sff(qreal x, qreal xMin, const SpringMap& springs)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
if (x <= xMin)
|
|
|
|
return 0.0;
|
|
|
|
iSpring 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)
|
|
|
|
break;
|
|
|
|
c += i->second.stretch;
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// respace
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::respace(QList<ChordRest*>* elements)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
// compute stretches
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
|
|
SpringMap springs;
|
|
|
|
qreal minimum = 0.0;
|
|
|
|
for (int i = 0; i < n-1; ++i) {
|
|
|
|
qreal w = width[i];
|
|
|
|
int t = ticksList[i];
|
2012-12-06 12:46:05 +01:00
|
|
|
// qreal str = 1.0 + .6 * log(qreal(t) / qreal(minTick)) / log(2.0);
|
|
|
|
qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal d = w / str;
|
|
|
|
|
|
|
|
springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
|
|
|
|
minimum += w;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
// distribute stretch to elements
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
|
|
qreal force = sff(x2 - x1, minimum, springs);
|
|
|
|
for (iSpring i = springs.begin(); i != springs.end(); ++i) {
|
|
|
|
qreal stretch = force * i->second.stretch;
|
|
|
|
if (stretch < i->second.fix)
|
|
|
|
stretch = i->second.fix;
|
|
|
|
width[i->second.seg] = stretch;
|
|
|
|
}
|
|
|
|
qreal x = 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// computeMinWidth
|
|
|
|
/// compute the minimum width of a measure with
|
|
|
|
/// segment list fs
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-10-28 15:22:39 +01:00
|
|
|
qreal Score::computeMinWidth(Segment* fs, bool firstMeasureInSystem)
|
2012-08-01 18:00:27 +02:00
|
|
|
{
|
|
|
|
int _nstaves = nstaves();
|
2012-10-08 08:28:19 +02:00
|
|
|
if (_nstaves == 0)
|
2012-08-01 18:00:27 +02:00
|
|
|
return 1.0;
|
|
|
|
|
2014-08-04 05:02:37 +02:00
|
|
|
qreal _spatium = spatium();
|
|
|
|
qreal clefKeyRightMargin = styleS(StyleIdx::clefKeyRightMargin).val() * _spatium;
|
|
|
|
qreal minNoteDistance = styleS(StyleIdx::minNoteDistance).val() * _spatium;
|
|
|
|
qreal minHarmonyDistance = styleS(StyleIdx::minHarmonyDistance).val() * _spatium;
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal maxHarmonyBarDistance = styleS(StyleIdx::maxHarmonyBarDistance).val() * _spatium;
|
2012-08-01 18:00:27 +02:00
|
|
|
|
2014-08-04 05:02:37 +02:00
|
|
|
qreal rest[_nstaves]; // fixed space needed from previous segment
|
2012-08-01 18:00:27 +02:00
|
|
|
memset(rest, 0, _nstaves * sizeof(qreal));
|
|
|
|
|
2014-08-04 05:02:37 +02:00
|
|
|
qreal hRest[_nstaves]; // fixed space needed from previous harmony
|
2013-03-14 16:52:10 +01:00
|
|
|
memset(hRest, 0, _nstaves * sizeof(qreal));
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
qreal clefWidth[_nstaves];
|
|
|
|
memset(clefWidth, 0, _nstaves * sizeof(qreal));
|
|
|
|
|
2013-04-08 18:26:53 +02:00
|
|
|
std::vector<QRectF> hLastBbox(_nstaves); // bbox of previous harmony to test vertical separation
|
2013-03-26 16:25:07 +01:00
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
int segmentIdx = 0;
|
|
|
|
qreal x = 0.0;
|
|
|
|
|
2012-10-23 22:16:31 +02:00
|
|
|
const Segment* pSeg = 0;
|
2012-08-01 18:00:27 +02:00
|
|
|
for (Segment* s = fs; s; s = s->next(), ++segmentIdx) {
|
|
|
|
qreal elsp = s->extraLeadingSpace().val() * _spatium;
|
|
|
|
qreal etsp = s->extraTrailingSpace().val() * _spatium;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
if ((s->segmentType() == Segment::Type::Clef) && (s != fs)) {
|
2012-08-01 18:00:27 +02:00
|
|
|
--segmentIdx;
|
|
|
|
for (int staffIdx = 0; staffIdx < _nstaves; ++staffIdx) {
|
2012-10-23 20:22:49 +02:00
|
|
|
if (!staff(staffIdx)->show())
|
|
|
|
continue;
|
2014-10-28 11:47:04 +01:00
|
|
|
Element* e = s->element(staffIdx * VOICES);
|
2012-08-01 18:00:27 +02:00
|
|
|
if (e) {
|
|
|
|
e->layout();
|
|
|
|
clefWidth[staffIdx] = e->width() + _spatium + elsp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2013-03-14 16:52:10 +01:00
|
|
|
bool rest2[_nstaves];
|
|
|
|
bool hRest2[_nstaves];
|
2014-06-01 20:24:29 +02:00
|
|
|
bool spaceHarmony = false;
|
2014-08-04 05:02:37 +02:00
|
|
|
Segment::Type segType = s->segmentType();
|
2014-06-01 20:24:29 +02:00
|
|
|
qreal segmentWidth = 0.0;
|
|
|
|
qreal harmonyWidth = 0.0;
|
|
|
|
qreal stretchDistance = 0.0;
|
2014-08-04 05:02:37 +02:00
|
|
|
Segment::Type pt = pSeg ? pSeg->segmentType() : Segment::Type::BarLine;
|
2012-08-01 18:00:27 +02:00
|
|
|
|
|
|
|
for (int staffIdx = 0; staffIdx < _nstaves; ++staffIdx) {
|
2012-08-01 22:15:58 +02:00
|
|
|
if (!staff(staffIdx)->show())
|
|
|
|
continue;
|
2012-08-01 18:00:27 +02:00
|
|
|
qreal minDistance = 0.0;
|
|
|
|
Space space;
|
2013-03-14 16:52:10 +01:00
|
|
|
Space hSpace;
|
2014-08-04 05:02:37 +02:00
|
|
|
Space naSpace; // space needed for full measure rests (and potentially other non-aligned elements)
|
2013-03-26 16:25:07 +01:00
|
|
|
QRectF hBbox;
|
2012-08-01 18:00:27 +02:00
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
bool found = false;
|
2013-03-14 16:52:10 +01:00
|
|
|
bool hFound = false;
|
2013-03-26 16:25:07 +01:00
|
|
|
bool eFound = false;
|
2014-04-27 03:29:13 +02:00
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
if (segType & (Segment::Type::ChordRest)) {
|
2012-08-01 18:00:27 +02:00
|
|
|
qreal llw = 0.0;
|
|
|
|
qreal rrw = 0.0;
|
|
|
|
Lyrics* lyrics = 0;
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(track+voice));
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
found = true;
|
2014-06-25 11:46:10 +02:00
|
|
|
if (pt & (Segment::Type::StartRepeatBarLine | Segment::Type::BarLine | Segment::Type::TimeSig)) {
|
2013-07-15 11:57:07 +02:00
|
|
|
// check for accidentals in chord
|
|
|
|
bool accidental = false;
|
2014-04-14 01:16:14 +02:00
|
|
|
bool grace = false;
|
2014-04-14 07:15:16 +02:00
|
|
|
qreal accidentalX = 0.0;
|
|
|
|
qreal noteX = 0.0;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2013-07-15 11:57:07 +02:00
|
|
|
Chord* c = static_cast<Chord*>(cr);
|
2015-02-19 10:28:25 +01:00
|
|
|
if (c->graceNotesBefore().size())
|
2014-04-14 01:16:14 +02:00
|
|
|
grace = true;
|
2013-07-15 11:57:07 +02:00
|
|
|
else {
|
|
|
|
for (Note* note : c->notes()) {
|
2014-11-15 21:28:16 +01:00
|
|
|
if (note->accidental() && !note->fixed()) {
|
2013-07-15 11:57:07 +02:00
|
|
|
accidental = true;
|
2014-05-21 00:43:28 +02:00
|
|
|
// segment-relative
|
|
|
|
accidentalX = qMin(accidentalX, note->accidental()->x() + note->x() + c->x());
|
2013-07-15 11:57:07 +02:00
|
|
|
}
|
2014-04-14 01:16:14 +02:00
|
|
|
else
|
2014-05-21 00:43:28 +02:00
|
|
|
// segment-relative
|
|
|
|
noteX = qMin(noteX, note->x() + c->x());
|
2013-07-15 11:57:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-14 01:16:14 +02:00
|
|
|
qreal sp;
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal bnd = styleS(StyleIdx::barNoteDistance).val() * _spatium;
|
2014-04-14 01:16:14 +02:00
|
|
|
if (accidental) {
|
2014-05-26 15:31:36 +02:00
|
|
|
qreal bad = styleS(StyleIdx::barAccidentalDistance).val() * _spatium;
|
2014-04-14 01:16:14 +02:00
|
|
|
qreal diff = qMax(noteX - accidentalX, 0.0);
|
|
|
|
sp = qMax(bad, bnd - diff);
|
|
|
|
}
|
|
|
|
else if (grace)
|
2015-02-05 08:20:57 +01:00
|
|
|
sp = styleS(StyleIdx::barGraceDistance).val() * _spatium;
|
2014-04-14 01:16:14 +02:00
|
|
|
else
|
2014-04-14 07:15:16 +02:00
|
|
|
sp = bnd;
|
2014-06-25 11:46:10 +02:00
|
|
|
if (pt & Segment::Type::TimeSig)
|
2014-04-14 07:15:16 +02:00
|
|
|
sp += clefKeyRightMargin - bnd;
|
2013-07-15 11:57:07 +02:00
|
|
|
minDistance = qMax(minDistance, sp);
|
2014-06-25 11:46:10 +02:00
|
|
|
if (!(pt & Segment::Type::TimeSig))
|
2014-04-15 05:41:05 +02:00
|
|
|
stretchDistance = sp * .7;
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2014-06-25 11:46:10 +02:00
|
|
|
else if (pt & Segment::Type::ChordRest) {
|
2012-08-01 18:00:27 +02:00
|
|
|
minDistance = qMax(minDistance, minNoteDistance);
|
|
|
|
}
|
|
|
|
else {
|
2014-06-25 11:46:10 +02:00
|
|
|
// if (pt & (Segment::Type::KeySig | Segment::Type::Clef))
|
2014-09-07 23:40:23 +02:00
|
|
|
bool firstClef = (pt == Segment::Type::Clef) && (pSeg && pSeg->rtick() == 0);
|
2014-06-25 11:46:10 +02:00
|
|
|
if ((pt & Segment::Type::KeySig) || firstClef)
|
2012-08-01 18:00:27 +02:00
|
|
|
minDistance = qMax(minDistance, clefKeyRightMargin);
|
|
|
|
}
|
|
|
|
cr->layout();
|
2014-05-21 00:43:28 +02:00
|
|
|
|
2014-10-28 15:22:39 +01:00
|
|
|
// special case:
|
2014-11-07 21:23:07 +01:00
|
|
|
// make extra space for ties or glissandi continued from previous system
|
2014-10-28 15:22:39 +01:00
|
|
|
|
|
|
|
if (firstMeasureInSystem && cr->type() == Element::Type::CHORD && cr->tick() == cr->measure()->tick()) {
|
|
|
|
Chord* c = static_cast<Chord*>(cr);
|
2014-12-13 17:40:39 +01:00
|
|
|
if (c->endsGlissando()) {
|
|
|
|
minDistance = qMax(minDistance, _spatium * GLISS_STARTOFSYSTEM_WIDTH);
|
2014-11-07 21:23:07 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (Note* note : c->notes()) {
|
|
|
|
if (note->tieBack()) {
|
|
|
|
minDistance = qMax(minDistance, _spatium * 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-10-28 15:22:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-21 00:43:28 +02:00
|
|
|
// calculate space needed for segment
|
|
|
|
// take cr position into account
|
|
|
|
// by converting to segment-relative space
|
2014-05-21 23:37:45 +02:00
|
|
|
// chord space itself already has ipos offset built in
|
|
|
|
// but lyrics do not
|
|
|
|
// and neither have user offsets
|
|
|
|
qreal cx = cr->ipos().x();
|
|
|
|
qreal cxu = cr->userOff().x();
|
|
|
|
qreal lx = qMax(cxu, 0.0); // nudge left shouldn't require more leading space
|
|
|
|
qreal rx = qMin(cxu, 0.0); // nudge right shouldn't require more trailing space
|
2014-05-21 00:43:28 +02:00
|
|
|
Space crSpace = cr->space();
|
|
|
|
Space segRelSpace(crSpace.lw()-lx, crSpace.rw()+rx);
|
2014-08-04 05:02:37 +02:00
|
|
|
// always allocate sufficient space
|
|
|
|
// but in Measure::layoutX() we will ignore full measure rests
|
|
|
|
// since they do not need to affect spacing in other voices
|
|
|
|
if (cr->durationType() != TDuration::DurationType::V_MEASURE)
|
|
|
|
space.max(segRelSpace);
|
|
|
|
else
|
|
|
|
naSpace.max(segRelSpace);
|
2014-05-21 00:43:28 +02:00
|
|
|
|
|
|
|
// lyrics
|
|
|
|
foreach (Lyrics* l, cr->lyricsList()) {
|
2012-08-01 18:00:27 +02:00
|
|
|
if (!l)
|
|
|
|
continue;
|
2015-04-29 19:26:45 +02:00
|
|
|
l->layout(); // need to create layout even if empty
|
2012-08-01 18:00:27 +02:00
|
|
|
if (!l->isEmpty()) {
|
|
|
|
lyrics = l;
|
2014-09-17 04:44:26 +02:00
|
|
|
QRectF b(l->bbox().translated(l->pos()));
|
|
|
|
llw = qMax(llw, -(b.left()+lx+cx));
|
|
|
|
rrw = qMax(rrw, b.right()+rx+cx);
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-04 05:02:37 +02:00
|
|
|
|
|
|
|
space.max(naSpace);
|
2012-08-01 18:00:27 +02:00
|
|
|
if (lyrics)
|
|
|
|
space.max(Space(llw, rrw));
|
2013-03-13 11:13:33 +01:00
|
|
|
|
2013-03-14 16:52:10 +01:00
|
|
|
// add spacing for chord symbols
|
2013-03-13 11:13:33 +01:00
|
|
|
foreach (Element* e, s->annotations()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() != Element::Type::HARMONY || e->track() < track || e->track() >= track+VOICES)
|
2013-03-13 11:13:33 +01:00
|
|
|
continue;
|
|
|
|
Harmony* h = static_cast<Harmony*>(e);
|
2014-06-08 04:23:50 +02:00
|
|
|
// call full layout here
|
|
|
|
// which also triggers layout of associated fret diagram if present
|
|
|
|
// otherwise the vertical position of the chord symbols cannot be known
|
|
|
|
h->layout(); // h->calculateBoundingRect();
|
2013-04-03 19:16:28 +02:00
|
|
|
QRectF b(h->bboxtight().translated(h->pos()));
|
2013-03-26 16:25:07 +01:00
|
|
|
if (hFound)
|
|
|
|
hBbox |= b;
|
|
|
|
else
|
|
|
|
hBbox = b;
|
|
|
|
hFound = true;
|
2014-04-27 03:29:13 +02:00
|
|
|
spaceHarmony = true;
|
|
|
|
// allow chord to be dragged
|
|
|
|
qreal xoff = h->pos().x();
|
|
|
|
qreal bl = -b.left() + qMin(xoff, 0.0);
|
|
|
|
qreal br = b.right() - qMax(xoff, 0.0);
|
|
|
|
hSpace.max(Space(bl, br));
|
2013-03-13 11:13:33 +01:00
|
|
|
}
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-04-27 03:29:13 +02:00
|
|
|
// current segment (s) is not a ChordRest
|
2012-08-01 18:00:27 +02:00
|
|
|
Element* e = s->element(track);
|
2014-10-10 11:52:44 +02:00
|
|
|
if (segType == Segment::Type::StartRepeatBarLine && !e) {
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
Element* ee = s->element(staffIdx * VOICES);
|
|
|
|
if (ee) {
|
|
|
|
BarLine* bl = static_cast<BarLine*>(ee);
|
|
|
|
int strack = staffIdx * VOICES;
|
|
|
|
int etrack = (staffIdx + bl->span()) * VOICES;
|
|
|
|
if (track >= strack && track < etrack) {
|
|
|
|
e = ee;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 11:46:10 +02:00
|
|
|
if ((segType == Segment::Type::Clef) && (pt != Segment::Type::ChordRest))
|
2014-05-26 15:31:36 +02:00
|
|
|
minDistance = styleP(StyleIdx::clefLeftMargin);
|
2015-06-12 07:04:43 +02:00
|
|
|
else if (segType == Segment::Type::StartRepeatBarLine && pSeg)
|
|
|
|
minDistance = .5 * _spatium; // TODO: make style parameter
|
2014-09-07 23:40:23 +02:00
|
|
|
else if (segType == Segment::Type::TimeSig && pt == Segment::Type::Clef) {
|
|
|
|
// missing key signature, but allocate default margin anyhow
|
|
|
|
minDistance = styleP(StyleIdx::keysigLeftMargin);
|
|
|
|
}
|
2014-06-25 11:46:10 +02:00
|
|
|
else if ((segType == Segment::Type::EndBarLine) && segmentIdx) {
|
|
|
|
if (pt == Segment::Type::Clef)
|
2014-05-26 15:31:36 +02:00
|
|
|
minDistance = styleP(StyleIdx::clefBarlineDistance);
|
2012-08-01 18:00:27 +02:00
|
|
|
else
|
2014-05-26 15:31:36 +02:00
|
|
|
stretchDistance = styleP(StyleIdx::noteBarDistance);
|
2012-08-01 18:00:27 +02:00
|
|
|
if (e == 0) {
|
|
|
|
// look for barline
|
|
|
|
for (int i = track - VOICES; i >= 0; i -= VOICES) {
|
|
|
|
e = s->element(i);
|
|
|
|
if (e)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (e) {
|
2013-03-26 16:25:07 +01:00
|
|
|
eFound = true;
|
2014-06-25 11:46:10 +02:00
|
|
|
if (!s->next()) // segType & Segment::Type::EndBarLine
|
2014-04-27 18:02:58 +02:00
|
|
|
spaceHarmony = true; // to space last Harmony to end of measure
|
2012-08-01 18:00:27 +02:00
|
|
|
e->layout();
|
|
|
|
space.max(e->space());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
space += Space(elsp, etsp);
|
2013-03-14 16:52:10 +01:00
|
|
|
|
2013-03-26 16:25:07 +01:00
|
|
|
if (found || eFound) {
|
2015-03-31 16:15:40 +02:00
|
|
|
space.addL(clefWidth[staffIdx]);
|
2014-10-28 11:47:04 +01:00
|
|
|
qreal sp = minDistance + rest[staffIdx] + qMax(space.lw(), stretchDistance);
|
2012-08-01 18:00:27 +02:00
|
|
|
rest[staffIdx] = space.rw();
|
|
|
|
rest2[staffIdx] = false;
|
|
|
|
segmentWidth = qMax(segmentWidth, sp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rest2[staffIdx] = true;
|
2013-03-14 16:52:10 +01:00
|
|
|
|
|
|
|
// space chord symbols separately from segments
|
2013-03-26 16:25:07 +01:00
|
|
|
if (hFound || eFound) {
|
|
|
|
qreal sp = 0.0;
|
|
|
|
|
|
|
|
// space chord symbols unless they miss each other vertically
|
2015-06-12 07:04:43 +02:00
|
|
|
if (hFound && hBbox.top() < hLastBbox[staffIdx].bottom() && hBbox.bottom() > hLastBbox[staffIdx].top())
|
2013-03-26 16:25:07 +01:00
|
|
|
sp = hRest[staffIdx] + minHarmonyDistance + hSpace.lw();
|
|
|
|
|
2014-04-28 03:50:21 +02:00
|
|
|
// barline: limit space to maxHarmonyBarDistance
|
2015-06-12 07:04:43 +02:00
|
|
|
else if (eFound && !hFound && spaceHarmony)
|
|
|
|
sp = qMin(hRest[staffIdx], maxHarmonyBarDistance);
|
2014-04-28 03:50:21 +02:00
|
|
|
|
2014-04-27 03:29:13 +02:00
|
|
|
hLastBbox[staffIdx] = hBbox;
|
2013-03-14 16:52:10 +01:00
|
|
|
hRest[staffIdx] = hSpace.rw();
|
|
|
|
hRest2[staffIdx] = false;
|
2014-04-27 03:29:13 +02:00
|
|
|
harmonyWidth = qMax(harmonyWidth, sp);
|
2013-03-14 16:52:10 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
hRest2[staffIdx] = true;
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
clefWidth[staffIdx] = 0.0;
|
|
|
|
}
|
2013-03-26 16:25:07 +01:00
|
|
|
|
2014-04-27 03:29:13 +02:00
|
|
|
// make room for harmony if needed
|
|
|
|
segmentWidth = qMax(segmentWidth, harmonyWidth);
|
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
x += segmentWidth;
|
|
|
|
|
2012-10-23 22:16:31 +02:00
|
|
|
// if (segmentIdx && pSeg)
|
|
|
|
// pSeg->setbbox(QRectF(0.0, 0.0, segmentWidth, _spatium * 5)); //??
|
2012-08-01 18:00:27 +02:00
|
|
|
|
|
|
|
for (int staffIdx = 0; staffIdx < _nstaves; ++staffIdx) {
|
2012-08-01 22:15:58 +02:00
|
|
|
if (!staff(staffIdx)->show())
|
|
|
|
continue;
|
2012-08-01 18:00:27 +02:00
|
|
|
if (rest2[staffIdx])
|
2014-04-27 03:29:13 +02:00
|
|
|
rest[staffIdx] -= qMin(rest[staffIdx], segmentWidth);
|
2013-03-14 16:52:10 +01:00
|
|
|
if (hRest2[staffIdx])
|
2014-04-27 03:29:13 +02:00
|
|
|
hRest[staffIdx] -= qMin(hRest[staffIdx], segmentWidth);
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2014-04-27 03:29:13 +02:00
|
|
|
|
2012-10-23 22:16:31 +02:00
|
|
|
//
|
|
|
|
// set pSeg only to used segments
|
|
|
|
//
|
|
|
|
for (int voice = 0; voice < _nstaves * VOICES; ++voice) {
|
|
|
|
if (!staff(voice/VOICES)->show()) {
|
|
|
|
voice += VOICES-1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s->element(voice)) {
|
|
|
|
pSeg = s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-08-01 18:00:27 +02:00
|
|
|
}
|
2012-10-23 22:16:31 +02:00
|
|
|
|
2012-08-01 18:00:27 +02:00
|
|
|
qreal segmentWidth = 0.0;
|
2012-08-01 22:15:58 +02:00
|
|
|
for (int staffIdx = 0; staffIdx < _nstaves; ++staffIdx) {
|
|
|
|
if (!staff(staffIdx)->show())
|
|
|
|
continue;
|
2012-08-01 18:00:27 +02:00
|
|
|
segmentWidth = qMax(segmentWidth, rest[staffIdx]);
|
2013-03-14 16:52:10 +01:00
|
|
|
segmentWidth = qMax(segmentWidth, hRest[staffIdx]);
|
2012-08-01 22:15:58 +02:00
|
|
|
}
|
2012-08-01 18:00:27 +02:00
|
|
|
x += segmentWidth;
|
|
|
|
return x;
|
|
|
|
}
|
2012-10-26 11:42:54 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateBarLineSpans
|
|
|
|
/// updates bar line span(s) when the number of lines of a staff changes
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-11-05 20:31:57 +01:00
|
|
|
void Score::updateBarLineSpans(int idx, int linesOld, int linesNew)
|
2012-10-26 11:42:54 +02:00
|
|
|
{
|
|
|
|
int nStaves = nstaves();
|
|
|
|
Staff* _staff;
|
|
|
|
|
|
|
|
// 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;
|
2012-11-11 01:50:57 +01:00
|
|
|
// this ensures plainchant and mensurstrich special bar lines keep their relationships to the staff lines.
|
2012-12-21 01:09:29 +01:00
|
|
|
// 1-line staves are traited as a special case.
|
2012-10-26 11:42:54 +02: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);
|
2015-04-10 04:39:14 +02:00
|
|
|
// if new line count is 1, set default From for 1-line staves
|
2012-12-21 01:09:29 +01:00
|
|
|
else if(linesNew == 1)
|
|
|
|
_staff->setBarLineFrom(BARLINE_SPAN_1LINESTAFF_FROM);
|
2015-04-10 04:39:14 +02:00
|
|
|
// if old line count was 1, set default From for normal staves
|
|
|
|
else if (linesOld == 1)
|
|
|
|
_staff->setBarLineFrom(0);
|
2012-10-26 11:42:54 +02:00
|
|
|
// 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-12-21 01:09:29 +01:00
|
|
|
// if the modified staff is the destination of the current staff bar span:
|
|
|
|
if(sIdx + _staff->barLineSpan() - 1 == idx) {
|
2015-04-10 04:39:14 +02:00
|
|
|
// if new line count is 1, set default To for 1-line staves
|
2012-12-21 01:09:29 +01:00
|
|
|
if(linesNew == 1)
|
|
|
|
_staff->setBarLineTo(BARLINE_SPAN_1LINESTAFF_TO);
|
2015-04-10 04:39:14 +02:00
|
|
|
// if old line count was 1, set default To for normal staves
|
|
|
|
else if (linesOld == 1)
|
|
|
|
_staff->setBarLineTo((linesNew - 1) * 2);
|
2012-12-21 01:09:29 +01:00
|
|
|
// 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-10-26 11:42:54 +02:00
|
|
|
}
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|