2012-05-26 14:26:10 +02:00
|
|
|
|
//=============================================================================
|
|
|
|
|
// MuseScore
|
|
|
|
|
// Music Composition & Notation
|
|
|
|
|
//
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// Copyright (C) 2002-2016 Werner Schweer
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//
|
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License version 2
|
|
|
|
|
// as published by the Free Software Foundation and appearing in
|
|
|
|
|
// the file LICENCE.GPL
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "accidental.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
#include "barline.h"
|
|
|
|
|
#include "beam.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "box.h"
|
|
|
|
|
#include "chord.h"
|
|
|
|
|
#include "clef.h"
|
|
|
|
|
#include "element.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
#include "fingering.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "glissando.h"
|
|
|
|
|
#include "harmony.h"
|
|
|
|
|
#include "key.h"
|
|
|
|
|
#include "keysig.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
#include "layoutbreak.h"
|
|
|
|
|
#include "layout.h"
|
2012-08-01 18:00:27 +02:00
|
|
|
|
#include "lyrics.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "marker.h"
|
|
|
|
|
#include "measure.h"
|
|
|
|
|
#include "mscore.h"
|
2014-02-22 11:19:44 +01:00
|
|
|
|
#include "notedot.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "note.h"
|
|
|
|
|
#include "ottava.h"
|
|
|
|
|
#include "page.h"
|
|
|
|
|
#include "part.h"
|
|
|
|
|
#include "repeat.h"
|
|
|
|
|
#include "score.h"
|
|
|
|
|
#include "segment.h"
|
|
|
|
|
#include "sig.h"
|
|
|
|
|
#include "slur.h"
|
|
|
|
|
#include "staff.h"
|
|
|
|
|
#include "stem.h"
|
|
|
|
|
#include "style.h"
|
|
|
|
|
#include "sym.h"
|
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "text.h"
|
|
|
|
|
#include "tie.h"
|
|
|
|
|
#include "timesig.h"
|
2014-05-15 13:42:03 +02:00
|
|
|
|
#include "tremolo.h"
|
2014-12-13 17:40:39 +01:00
|
|
|
|
#include "tuplet.h"
|
|
|
|
|
#include "undo.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "volta.h"
|
2016-01-04 14:48:58 +01:00
|
|
|
|
#include "breath.h"
|
|
|
|
|
#include "tempotext.h"
|
2016-02-10 13:40:34 +01:00
|
|
|
|
#include "systemdivider.h"
|
2016-05-19 13:15:34 +02:00
|
|
|
|
#include "hook.h"
|
2016-05-25 12:10:52 +02:00
|
|
|
|
#include "ambitus.h"
|
2016-07-19 20:35:06 +02:00
|
|
|
|
#include "hairpin.h"
|
2016-12-23 12:05:18 +01:00
|
|
|
|
#include "stafflines.h"
|
2017-01-05 11:23:47 +01:00
|
|
|
|
#include "articulation.h"
|
2017-03-31 13:03:15 +02:00
|
|
|
|
#include "bracket.h"
|
2017-10-27 13:06:22 +02:00
|
|
|
|
#include "spacer.h"
|
2018-01-17 13:25:23 +01:00
|
|
|
|
#include "fermata.h"
|
2018-12-09 15:11:26 +01:00
|
|
|
|
#include "measurenumber.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()
|
|
|
|
|
{
|
2017-01-05 11:23:47 +01:00
|
|
|
|
for (Page* page : pages())
|
2014-11-23 13:34:03 +01:00
|
|
|
|
page->rebuildBspTree();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-29 10:38:10 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutSegmentElements
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void layoutSegmentElements(Segment* segment, int startTrack, int endTrack)
|
|
|
|
|
{
|
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
|
|
|
|
if (Element* e = segment->element(track))
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// 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)
|
|
|
|
|
{
|
2018-11-08 15:45:14 +01:00
|
|
|
|
const Staff* staff = Score::staff(staffIdx);
|
2018-11-29 10:38:10 +01:00
|
|
|
|
const int startTrack = staffIdx * VOICES;
|
|
|
|
|
const int endTrack = startTrack + VOICES;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2018-11-29 10:38:10 +01:00
|
|
|
|
if (staff->isTabStaff(segment->tick())) {
|
|
|
|
|
layoutSegmentElements(segment, startTrack, endTrack);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
return;
|
2018-11-29 10:38:10 +01:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-04-27 16:33:30 +02:00
|
|
|
|
std::vector<Note*> upStemNotes;
|
|
|
|
|
std::vector<Note*> downStemNotes;
|
|
|
|
|
int upVoices = 0;
|
|
|
|
|
int downVoices = 0;
|
2016-12-23 12:05:18 +01:00
|
|
|
|
qreal nominalWidth = noteHeadWidth() * staff->mag(segment->tick());
|
2016-04-27 16:33:30 +02:00
|
|
|
|
qreal maxUpWidth = 0.0;
|
2014-04-08 20:52:10 +02:00
|
|
|
|
qreal maxDownWidth = 0.0;
|
2016-04-27 16:33:30 +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
|
2016-04-27 16:33:30 +02:00
|
|
|
|
int upDots = 0;
|
|
|
|
|
int downDots = 0;
|
|
|
|
|
bool upHooks = false;
|
|
|
|
|
bool downHooks = false;
|
|
|
|
|
|
2015-02-13 19:03:44 +01:00
|
|
|
|
// also check for grace notes
|
2016-04-27 16:33:30 +02:00
|
|
|
|
bool upGrace = false;
|
|
|
|
|
bool downGrace = false;
|
2014-03-08 07:43:24 +01:00
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
2016-02-10 18:35:35 +01:00
|
|
|
|
Element* e = segment->element(track);
|
|
|
|
|
if (e && e->isChord()) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Chord* chord = toChord(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;
|
2016-02-04 17:06:32 +01:00
|
|
|
|
layoutChords2(c->notes(), c->up()); // layout grace note noteheads
|
|
|
|
|
layoutChords3(c->notes(), staff, 0); // layout grace note chords
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
2014-03-07 03:43:41 +01:00
|
|
|
|
if (chord->up()) {
|
|
|
|
|
++upVoices;
|
2016-02-06 22:03:43 +01:00
|
|
|
|
upStemNotes.insert(upStemNotes.end(), chord->notes().begin(), chord->notes().end());
|
2016-02-04 17:06:32 +01:00
|
|
|
|
upDots = qMax(upDots, chord->dots());
|
2014-04-08 23:18:15 +02:00
|
|
|
|
maxUpMag = qMax(maxUpMag, chord->mag());
|
2015-01-30 19:33:54 +01:00
|
|
|
|
if (!upHooks)
|
|
|
|
|
upHooks = chord->hook();
|
2015-02-13 19:03:44 +01:00
|
|
|
|
if (hasGraceBefore)
|
|
|
|
|
upGrace = true;
|
2014-03-07 03:43:41 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
++downVoices;
|
2016-02-06 22:03:43 +01:00
|
|
|
|
downStemNotes.insert(downStemNotes.end(), chord->notes().begin(), chord->notes().end());
|
2014-03-08 07:43:24 +01:00
|
|
|
|
downDots = qMax(downDots, chord->dots());
|
2014-04-08 23:18:15 +02:00
|
|
|
|
maxDownMag = qMax(maxDownMag, chord->mag());
|
2015-01-30 19:33:54 +01:00
|
|
|
|
if (!downHooks)
|
|
|
|
|
downHooks = chord->hook();
|
2015-02-13 19:03:44 +01:00
|
|
|
|
if (hasGraceBefore)
|
|
|
|
|
downGrace = true;
|
2014-03-07 03:43:41 +01:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (upVoices + downVoices) {
|
|
|
|
|
// TODO: use track as secondary sort criteria?
|
|
|
|
|
// otherwise there might be issues with unisons between voices
|
|
|
|
|
// in some corner cases
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
2016-04-27 16:33:30 +02:00
|
|
|
|
maxUpWidth = nominalWidth * maxUpMag;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
maxDownWidth = nominalWidth * maxDownMag;
|
2014-04-08 20:52:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// layout upstem noteheads
|
|
|
|
|
if (upVoices > 1) {
|
|
|
|
|
qSort(upStemNotes.begin(), upStemNotes.end(),
|
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
2015-02-18 07:47:16 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (upVoices) {
|
|
|
|
|
qreal hw = layoutChords2(upStemNotes, true);
|
|
|
|
|
maxUpWidth = qMax(maxUpWidth, hw);
|
2015-02-18 07:47:16 +01:00
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// layout downstem noteheads
|
|
|
|
|
if (downVoices > 1) {
|
|
|
|
|
qSort(downStemNotes.begin(), downStemNotes.end(),
|
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (downVoices) {
|
|
|
|
|
qreal hw = layoutChords2(downStemNotes, false);
|
|
|
|
|
maxDownWidth = qMax(maxDownWidth, hw);
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-23 12:05:18 +01:00
|
|
|
|
qreal sp = staff->spatium(segment->tick());
|
2016-02-06 11:41:16 +01:00
|
|
|
|
qreal upOffset = 0.0; // offset to apply to upstem chords
|
|
|
|
|
qreal downOffset = 0.0; // offset to apply to downstem chords
|
|
|
|
|
qreal dotAdjust = 0.0; // additional chord offset to account for dots
|
|
|
|
|
qreal dotAdjustThreshold = 0.0; // if it exceeds this amount
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
|
|
// centering adjustments for whole note, breve, and small chords
|
|
|
|
|
qreal centerUp = 0.0; // offset to apply in order to center upstem chords
|
|
|
|
|
qreal oversizeUp = 0.0; // adjustment to oversized upstem chord needed if laid out to the right
|
|
|
|
|
qreal centerDown = 0.0; // offset to apply in order to center downstem chords
|
|
|
|
|
qreal centerAdjustUp = 0.0; // adjustment to upstem chord needed after centering donwstem chord
|
|
|
|
|
qreal centerAdjustDown = 0.0; // adjustment to downstem chord needed after centering upstem chord
|
|
|
|
|
|
|
|
|
|
// only center chords if they differ from nominal by at least this amount
|
|
|
|
|
// this avoids unnecessary centering on differences due only to floating point roundoff
|
|
|
|
|
// it also allows for the possibility of disabling centering
|
|
|
|
|
// for notes only "slightly" larger than nominal, like half notes
|
|
|
|
|
// but this will result in them not being aligned with each other between voices
|
|
|
|
|
// unless you change to left alignment as described in the comments below
|
|
|
|
|
qreal centerThreshold = 0.01 * sp;
|
|
|
|
|
|
|
|
|
|
// amount by which actual width exceeds nominal, adjusted for staff mag() only
|
|
|
|
|
qreal headDiff = maxUpWidth - nominalWidth;
|
|
|
|
|
// amount by which actual width exceeds nominal, adjusted for staff & chord/note mag()
|
2016-12-23 12:05:18 +01:00
|
|
|
|
qreal headDiff2 = maxUpWidth - nominalWidth * (maxUpMag / staff->mag(segment->tick()));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (headDiff > centerThreshold) {
|
|
|
|
|
// larger than nominal
|
|
|
|
|
centerUp = headDiff * -0.5;
|
|
|
|
|
// maxUpWidth is true width, but we no longer will care about that
|
|
|
|
|
// instead, we care only about portion to right of origin
|
|
|
|
|
maxUpWidth += centerUp;
|
|
|
|
|
// to left align rather than center, delete both of the above
|
|
|
|
|
if (headDiff2 > centerThreshold) {
|
|
|
|
|
// if max notehead is wider than nominal with chord/note mag() applied
|
|
|
|
|
// then noteheads extend to left of origin
|
|
|
|
|
// because stemPosX() is based on nominal width
|
|
|
|
|
// so we need to correct for that too
|
|
|
|
|
centerUp += headDiff2;
|
|
|
|
|
oversizeUp = headDiff2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (-headDiff > centerThreshold) {
|
|
|
|
|
// smaller than nominal
|
|
|
|
|
centerUp = -headDiff * 0.5;
|
|
|
|
|
if (headDiff2 > centerThreshold) {
|
|
|
|
|
// max notehead is wider than nominal with chord/note mag() applied
|
|
|
|
|
// perform same adjustment as above
|
|
|
|
|
centerUp += headDiff2;
|
|
|
|
|
oversizeUp = headDiff2;
|
|
|
|
|
}
|
|
|
|
|
centerAdjustDown = centerUp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headDiff = maxDownWidth - nominalWidth;
|
|
|
|
|
if (headDiff > centerThreshold) {
|
|
|
|
|
// larger than nominal
|
|
|
|
|
centerDown = headDiff * -0.5;
|
|
|
|
|
// to left align rather than center, change the above to
|
|
|
|
|
//centerAdjustUp = headDiff;
|
|
|
|
|
maxDownWidth = nominalWidth - centerDown;
|
|
|
|
|
}
|
|
|
|
|
else if (-headDiff > centerThreshold) {
|
|
|
|
|
// smaller than nominal
|
|
|
|
|
centerDown = -headDiff * 0.5;
|
|
|
|
|
centerAdjustUp = centerDown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handle conflict between upstem and downstem chords
|
|
|
|
|
|
|
|
|
|
if (upVoices && downVoices) {
|
2016-02-06 22:03:43 +01:00
|
|
|
|
Note* bottomUpNote = upStemNotes.front();
|
2016-04-27 16:33:30 +02:00
|
|
|
|
Note* topDownNote = downStemNotes.back();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int separation;
|
|
|
|
|
if (bottomUpNote->chord()->staffMove() == topDownNote->chord()->staffMove())
|
|
|
|
|
separation = topDownNote->line() - bottomUpNote->line();
|
|
|
|
|
else
|
|
|
|
|
separation = 2; // no conflict
|
2016-02-06 22:03:43 +01:00
|
|
|
|
QVector<Note*> overlapNotes;
|
|
|
|
|
overlapNotes.reserve(8);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
|
|
if (separation == 1) {
|
|
|
|
|
// second
|
|
|
|
|
downOffset = maxUpWidth;
|
|
|
|
|
// align stems if present, leave extra room if not
|
|
|
|
|
if (topDownNote->chord()->stem() && bottomUpNote->chord()->stem())
|
|
|
|
|
downOffset -= topDownNote->chord()->stem()->lineWidth();
|
2014-03-05 23:45:58 +01:00
|
|
|
|
else
|
2016-01-04 14:48:58 +01:00
|
|
|
|
downOffset += 0.1 * sp;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else if (separation < 1) {
|
|
|
|
|
|
|
|
|
|
// overlap (possibly unison)
|
|
|
|
|
|
|
|
|
|
// build list of overlapping notes
|
2018-09-11 16:56:50 +02:00
|
|
|
|
for (size_t i = 0, n = upStemNotes.size(); i < n; ++i) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (upStemNotes[i]->line() >= topDownNote->line() - 1)
|
|
|
|
|
overlapNotes.append(upStemNotes[i]);
|
|
|
|
|
else
|
|
|
|
|
break;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
2018-09-11 16:56:50 +02:00
|
|
|
|
for (int i = int(downStemNotes.size()) - 1; i >= 0; --i) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (downStemNotes[i]->line() <= bottomUpNote->line() + 1)
|
|
|
|
|
overlapNotes.append(downStemNotes[i]);
|
|
|
|
|
else
|
2014-03-05 23:45:58 +01:00
|
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
qSort(overlapNotes.begin(), overlapNotes.end(),
|
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
|
|
|
|
|
|
// determine nature of overlap
|
|
|
|
|
bool shareHeads = true; // can all overlapping notes share heads?
|
|
|
|
|
bool matchPending = false; // looking for a unison match
|
|
|
|
|
bool conflictUnison = false; // unison found
|
|
|
|
|
bool conflictSecondUpHigher = false; // second found
|
|
|
|
|
bool conflictSecondDownHigher = false; // second found
|
|
|
|
|
int lastLine = 1000;
|
|
|
|
|
Note* p = overlapNotes[0];
|
|
|
|
|
for (int i = 0, count = overlapNotes.size(); i < count; ++i) {
|
|
|
|
|
Note* n = overlapNotes[i];
|
|
|
|
|
NoteHead::Type nHeadType;
|
|
|
|
|
NoteHead::Type pHeadType;
|
|
|
|
|
Chord* nchord = n->chord();
|
|
|
|
|
Chord* pchord = p->chord();
|
|
|
|
|
if (n->mirror()) {
|
2014-03-05 23:45:58 +01:00
|
|
|
|
if (separation < 0) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// don't try to share heads if there is any mirroring
|
2014-03-05 23:45:58 +01:00
|
|
|
|
shareHeads = false;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// don't worry about conflicts involving mirrored notes
|
|
|
|
|
continue;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
int line = n->line();
|
|
|
|
|
int d = lastLine - line;
|
|
|
|
|
switch (d) {
|
|
|
|
|
case 0:
|
|
|
|
|
// unison
|
|
|
|
|
conflictUnison = true;
|
|
|
|
|
matchPending = false;
|
|
|
|
|
nHeadType = (n->headType() == NoteHead::Type::HEAD_AUTO) ? n->chord()->durationType().headType() : n->headType();
|
|
|
|
|
pHeadType = (p->headType() == NoteHead::Type::HEAD_AUTO) ? p->chord()->durationType().headType() : p->headType();
|
|
|
|
|
// the most important rules for sharing noteheads on unisons between voices are
|
|
|
|
|
// that notes must be one same line with same tpc
|
|
|
|
|
// noteheads must be unmirrored and of same group
|
|
|
|
|
// and chords must be same size (or else sharing code won't work)
|
|
|
|
|
if (n->headGroup() != p->headGroup() || n->tpc() != p->tpc() || n->mirror() || p->mirror() || nchord->small() != pchord->small()) {
|
|
|
|
|
shareHeads = false;
|
2014-04-22 04:04:43 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// noteheads are potentially shareable
|
|
|
|
|
// it is more subjective at this point
|
|
|
|
|
// current default is to require *either* of the following:
|
|
|
|
|
// 1) both chords have same number of dots, both have stems, and both noteheads are same type and are full size (automatic match)
|
|
|
|
|
// or 2) one or more of the noteheads is not of type AUTO, but is explicitly set to match the other (user-forced match)
|
|
|
|
|
// or 3) exactly one of the noteheads is invisible (user-forced match)
|
|
|
|
|
// thus user can force notes to be shared despite differing number of dots or either being stemless
|
|
|
|
|
// by setting one of the notehead types to match the other or by making one notehead invisible
|
|
|
|
|
// TODO: consider adding a style option, staff properties, or note property to control sharing
|
|
|
|
|
if ((nchord->dots() != pchord->dots() || !nchord->stem() || !pchord->stem() || nHeadType != pHeadType || n->small() || p->small()) &&
|
|
|
|
|
((n->headType() == NoteHead::Type::HEAD_AUTO && p->headType() == NoteHead::Type::HEAD_AUTO) || nHeadType != pHeadType) &&
|
|
|
|
|
(n->visible() == p->visible())) {
|
|
|
|
|
shareHeads = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
// second
|
|
|
|
|
// trust that this won't be a problem for single unison
|
|
|
|
|
if (separation < 0) {
|
|
|
|
|
if (n->chord()->up())
|
|
|
|
|
conflictSecondUpHigher = true;
|
2014-04-22 04:04:43 +02:00
|
|
|
|
else
|
2016-01-04 14:48:58 +01:00
|
|
|
|
conflictSecondDownHigher = true;
|
|
|
|
|
shareHeads = false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// no conflict
|
|
|
|
|
if (matchPending)
|
|
|
|
|
shareHeads = false;
|
|
|
|
|
matchPending = true;
|
|
|
|
|
}
|
|
|
|
|
p = n;
|
|
|
|
|
lastLine = line;
|
|
|
|
|
}
|
|
|
|
|
if (matchPending)
|
|
|
|
|
shareHeads = false;
|
|
|
|
|
|
|
|
|
|
// calculate offsets
|
|
|
|
|
if (shareHeads) {
|
|
|
|
|
for (int i = overlapNotes.size() - 1; i >= 1; i -= 2) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Note* previousNote = overlapNotes[i-1];
|
2016-01-04 14:48:58 +01:00
|
|
|
|
Note* n = overlapNotes[i];
|
2018-07-10 14:57:05 +02:00
|
|
|
|
if (!(previousNote->chord()->isNudged() || n->chord()->isNudged())) {
|
|
|
|
|
if (previousNote->chord()->dots() == n->chord()->dots()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// hide one set dots
|
2018-07-10 14:57:05 +02:00
|
|
|
|
bool onLine = !(previousNote->line() & 1);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (onLine) {
|
|
|
|
|
// hide dots for lower voice
|
2018-07-10 14:57:05 +02:00
|
|
|
|
if (previousNote->voice() & 1)
|
|
|
|
|
previousNote->setDotsHidden(true);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else
|
|
|
|
|
n->setDotsHidden(true);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// hide dots for upper voice
|
2018-07-10 14:57:05 +02:00
|
|
|
|
if (!(previousNote->voice() & 1))
|
|
|
|
|
previousNote->setDotsHidden(true);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else
|
|
|
|
|
n->setDotsHidden(true);
|
|
|
|
|
}
|
2014-04-22 04:04:43 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// formerly we hid noteheads in an effort to fix playback
|
|
|
|
|
// but this doesn't work for cases where noteheads cannot be shared
|
|
|
|
|
// so better to solve the problem elsewhere
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else if (conflictUnison && separation == 0 && (!downGrace || upGrace))
|
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
|
|
|
|
else if (conflictUnison)
|
|
|
|
|
upOffset = maxDownWidth + 0.3 * sp;
|
|
|
|
|
else if (conflictSecondUpHigher)
|
|
|
|
|
upOffset = maxDownWidth + 0.2 * sp;
|
|
|
|
|
else if ((downHooks && !upHooks) && !(upDots && !downDots))
|
2014-03-08 07:43:24 +01:00
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else if (conflictSecondDownHigher) {
|
|
|
|
|
if (downDots && !upDots)
|
|
|
|
|
downOffset = maxUpWidth + 0.3 * sp;
|
|
|
|
|
else {
|
|
|
|
|
upOffset = maxDownWidth - 0.2 * sp;
|
|
|
|
|
if (downHooks)
|
|
|
|
|
upOffset += 0.3 * sp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-01-30 19:33:54 +01:00
|
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// no direct conflict, so parts can overlap (downstem on left)
|
|
|
|
|
// just be sure that stems clear opposing noteheads
|
|
|
|
|
qreal clearLeft = 0.0, clearRight = 0.0;
|
|
|
|
|
if (topDownNote->chord()->stem())
|
|
|
|
|
clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
|
|
|
|
|
if (bottomUpNote->chord()->stem())
|
|
|
|
|
clearRight = bottomUpNote->chord()->stem()->lineWidth() + qMax(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
|
|
|
|
|
else
|
|
|
|
|
downDots = 0; // no need to adjust for dots in this case
|
|
|
|
|
upOffset = qMax(clearLeft, clearRight);
|
|
|
|
|
if (downHooks) {
|
|
|
|
|
// we will need more space to avoid collision with hook
|
|
|
|
|
// but we won't need as much dot adjustment
|
|
|
|
|
upOffset = qMax(upOffset, maxDownWidth + 0.1 * sp);
|
|
|
|
|
dotAdjustThreshold = maxUpWidth - 0.3 * sp;
|
|
|
|
|
}
|
|
|
|
|
// if downstem chord is small, don't center
|
|
|
|
|
// and we might not need as much dot adjustment either
|
|
|
|
|
if (centerDown > 0.0) {
|
|
|
|
|
centerDown = 0.0;
|
|
|
|
|
centerAdjustUp = 0.0;
|
|
|
|
|
dotAdjustThreshold = (upOffset - maxDownWidth) + maxUpWidth - 0.3 * sp;
|
|
|
|
|
}
|
2015-01-30 19:33:54 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
2014-03-08 07:43:24 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
|
|
// adjust for dots
|
|
|
|
|
if ((upDots && !downDots) || (downDots && !upDots)) {
|
|
|
|
|
// only one sets of dots
|
|
|
|
|
// place between chords
|
|
|
|
|
int dots;
|
|
|
|
|
qreal mag;
|
|
|
|
|
if (upDots) {
|
|
|
|
|
dots = upDots;
|
|
|
|
|
mag = maxUpMag;
|
2015-01-30 19:33:54 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
dots = downDots;
|
|
|
|
|
mag = maxDownMag;
|
2014-08-11 06:57:11 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal dotWidth = segment->symWidth(SymId::augmentationDot);
|
|
|
|
|
// first dot
|
2018-03-27 15:36:00 +02:00
|
|
|
|
dotAdjust = styleP(Sid::dotNoteDistance) + dotWidth;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// additional dots
|
|
|
|
|
if (dots > 1)
|
2018-03-27 15:36:00 +02:00
|
|
|
|
dotAdjust += styleP(Sid::dotDotDistance) * (dots - 1);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
dotAdjust *= mag;
|
|
|
|
|
// only by amount over threshold
|
|
|
|
|
dotAdjust = qMax(dotAdjust - dotAdjustThreshold, 0.0);
|
2014-03-07 03:43:41 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (separation == 1)
|
|
|
|
|
dotAdjust += 0.1 * sp;
|
2014-03-08 07:43:24 +01:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// apply chord offsets
|
|
|
|
|
for (int track = startTrack; track < endTrack; ++track) {
|
2016-02-10 18:35:35 +01:00
|
|
|
|
Element* e = segment->element(track);
|
|
|
|
|
if (e && e->isChord()) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Chord* chord = toChord(e);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (chord->up()) {
|
|
|
|
|
if (upOffset != 0.0) {
|
|
|
|
|
chord->rxpos() += upOffset + centerAdjustUp + oversizeUp;
|
|
|
|
|
if (downDots && !upDots)
|
|
|
|
|
chord->rxpos() += dotAdjust;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
chord->rxpos() += centerUp;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (downOffset != 0.0) {
|
|
|
|
|
chord->rxpos() += downOffset + centerAdjustDown;
|
|
|
|
|
if (upDots && !downDots)
|
|
|
|
|
chord->rxpos() += dotAdjust;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
chord->rxpos() += centerDown;
|
|
|
|
|
}
|
2014-04-08 23:18:15 +02:00
|
|
|
|
}
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// layout chords
|
2016-02-06 22:03:43 +01:00
|
|
|
|
std::vector<Note*> notes;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (upVoices)
|
2016-02-06 22:03:43 +01:00
|
|
|
|
notes.insert(notes.end(), upStemNotes.begin(), upStemNotes.end());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (downVoices)
|
2016-02-06 22:03:43 +01:00
|
|
|
|
notes.insert(notes.end(), downStemNotes.begin(), downStemNotes.end());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (upVoices + downVoices > 1)
|
|
|
|
|
qSort(notes.begin(), notes.end(),
|
|
|
|
|
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
|
|
|
|
|
layoutChords3(notes, staff, segment);
|
2014-04-08 20:52:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-29 10:38:10 +01:00
|
|
|
|
layoutSegmentElements(segment, startTrack, endTrack);
|
2013-06-10 21:13:04 +02:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutChords2
|
2014-03-09 07:09:40 +01:00
|
|
|
|
// - determine which notes need mirroring
|
2014-03-16 07:55:39 +01:00
|
|
|
|
// - this is called once for each stem direction
|
|
|
|
|
// eg, once for voices 1&3, once for 2&4
|
|
|
|
|
// with all notes combined and sorted to resemble one chord
|
2014-04-08 20:52:10 +02:00
|
|
|
|
// - return maximum non-mirrored notehead width
|
2014-03-05 23:45:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
|
qreal Score::layoutChords2(std::vector<Note*>& notes, bool up)
|
2013-06-10 21:13:04 +02:00
|
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
|
int startIdx, endIdx, incIdx;
|
2014-04-08 20:52:10 +02:00
|
|
|
|
qreal maxWidth = 0.0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2014-03-05 23:45:58 +01:00
|
|
|
|
// loop in correct direction so that first encountered notehead wins conflict
|
|
|
|
|
if (up) {
|
|
|
|
|
// loop bottom up
|
2012-05-26 14:26:10 +02:00
|
|
|
|
startIdx = 0;
|
2018-09-11 16:56:50 +02:00
|
|
|
|
endIdx = int(notes.size());
|
2014-03-05 23:45:58 +01:00
|
|
|
|
incIdx = 1;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2014-03-05 23:45:58 +01:00
|
|
|
|
// loop top down
|
2018-09-11 16:56:50 +02:00
|
|
|
|
startIdx = int(notes.size()) - 1;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
endIdx = -1;
|
|
|
|
|
incIdx = -1;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 01:21:42 +02:00
|
|
|
|
int ll = 1000; // line of previous notehead
|
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?
|
2016-05-03 01:21:42 +02:00
|
|
|
|
bool mirror = false; // should current notehead be mirrored?
|
2014-05-07 05:56:46 +02:00
|
|
|
|
// value is retained and may be used on next iteration
|
|
|
|
|
// to track mirror status of previous note
|
2016-05-03 01:21:42 +02:00
|
|
|
|
bool isLeft = notes[startIdx]->chord()->up(); // is notehead 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
|
2016-05-03 01:21:42 +02:00
|
|
|
|
// this may be changed later to allow unisons to share noteheads
|
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)
|
2018-04-28 15:12:29 +02:00
|
|
|
|
maxWidth = qMax(maxWidth, note->bboxRightPos());
|
2014-05-07 05:56:46 +02:00
|
|
|
|
|
|
|
|
|
// prepare for next iteration
|
2016-02-06 22:03:43 +01:00
|
|
|
|
lvisible = note->visible();
|
|
|
|
|
lmove = move;
|
|
|
|
|
ll = line;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
|
return maxWidth;
|
2014-03-05 23:45:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-12 22:52:59 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// AcEl
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
struct AcEl {
|
|
|
|
|
Note* note;
|
|
|
|
|
qreal x; // actual x position of this accidental relative to origin
|
|
|
|
|
qreal top; // top of accidental bbox relative to staff
|
|
|
|
|
qreal bottom; // bottom of accidental bbox relative to staff
|
|
|
|
|
int line; // line of note
|
2014-03-19 06:22:12 +01:00
|
|
|
|
int next; // index of next accidental of same pitch class (ascending list)
|
2014-03-14 01:19:36 +01:00
|
|
|
|
qreal width; // width of accidental
|
2014-03-16 07:55:39 +01:00
|
|
|
|
qreal ascent; // amount (in sp) vertical strokes extend above body
|
|
|
|
|
qreal descent; // amount (in sp) vertical strokes extend below body
|
|
|
|
|
qreal rightClear; // amount (in sp) to right of last vertical stroke above body
|
|
|
|
|
qreal leftClear; // amount (in sp) to left of last vertical stroke below body
|
2014-03-12 22:52:59 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// resolveAccidentals
|
2014-05-12 19:13:48 +02:00
|
|
|
|
// lx = calculated position of rightmost edge of left accidental relative to origin
|
2014-03-12 22:52:59 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2014-05-14 05:39:02 +02:00
|
|
|
|
static bool resolveAccidentals(AcEl* left, AcEl* right, qreal& lx, qreal pd, qreal sp)
|
2014-03-12 22:52:59 +01:00
|
|
|
|
{
|
|
|
|
|
AcEl* upper;
|
|
|
|
|
AcEl* lower;
|
|
|
|
|
if (left->line >= right->line) {
|
|
|
|
|
upper = right;
|
|
|
|
|
lower = left;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
upper = left;
|
|
|
|
|
lower = right;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
|
qreal gap = lower->top - upper->bottom;
|
2014-03-12 22:52:59 +01:00
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
|
// no conflict at all if there is sufficient vertical gap between accidentals
|
2014-08-10 19:39:52 +02:00
|
|
|
|
// the arrangement of accidentals into columns assumes accidentals an octave apart *do* clear
|
|
|
|
|
if (gap >= pd || lower->line - upper->line >= 7)
|
2014-03-12 22:52:59 +01:00
|
|
|
|
return false;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
|
2014-05-12 05:00:54 +02:00
|
|
|
|
qreal allowableOverlap = qMax(upper->descent, lower->ascent) - pd;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
|
|
|
|
|
// accidentals that are "close" (small gap or even slight overlap)
|
2014-08-10 19:39:52 +02:00
|
|
|
|
if (qAbs(gap) <= 0.33 * sp) {
|
2014-03-16 07:55:39 +01:00
|
|
|
|
// acceptable with slight offset
|
|
|
|
|
// if one of the accidentals can subsume the overlap
|
|
|
|
|
// and both accidentals allow it
|
|
|
|
|
if (-gap <= allowableOverlap && qMin(upper->descent, lower->ascent) > 0.0) {
|
2014-05-12 19:13:48 +02:00
|
|
|
|
qreal align = qMin(left->width, right->width);
|
2014-03-16 07:55:39 +01:00
|
|
|
|
lx = qMin(lx, right->x + align - pd);
|
2014-05-12 19:13:48 +02:00
|
|
|
|
return true;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
}
|
2014-03-12 22:52:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-12 19:13:48 +02:00
|
|
|
|
// amount by which overlapping accidentals will be separated
|
|
|
|
|
// for example, the vertical stems of two flat signs
|
|
|
|
|
// these need more space than we would need between non-overlapping accidentals
|
2014-05-14 05:39:02 +02:00
|
|
|
|
qreal overlapShift = pd * 1.41;
|
2014-05-12 19:13:48 +02:00
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
|
// accidentals with more significant overlap
|
|
|
|
|
// acceptable if one accidental can subsume overlap
|
|
|
|
|
if (left == lower && -gap <= allowableOverlap) {
|
|
|
|
|
qreal offset = qMax(left->rightClear, right->leftClear);
|
2014-05-12 19:13:48 +02:00
|
|
|
|
offset = qMin(offset, left->width) - overlapShift;
|
|
|
|
|
lx = qMin(lx, right->x + offset);
|
|
|
|
|
return true;
|
2014-03-12 22:52:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-16 07:55:39 +01:00
|
|
|
|
// accidentals with even more overlap
|
|
|
|
|
// can work if both accidentals can subsume overlap
|
2014-05-12 05:00:54 +02:00
|
|
|
|
if (left == lower && -gap <= upper->descent + lower->ascent - pd) {
|
2014-05-12 19:13:48 +02:00
|
|
|
|
qreal offset = qMin(left->rightClear, right->leftClear) - overlapShift;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
if (offset > 0.0) {
|
2014-05-12 19:13:48 +02:00
|
|
|
|
lx = qMin(lx, right->x + offset);
|
|
|
|
|
return true;
|
2014-03-16 07:55:39 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// otherwise, there is real conflict
|
2014-05-12 19:13:48 +02:00
|
|
|
|
lx = qMin(lx, right->x - pd);
|
2014-03-12 22:52:59 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 01:19:36 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutAccidental
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
|
static qreal layoutAccidental(AcEl* me, AcEl* above, AcEl* below, qreal colOffset, QVector<Note*>& leftNotes, qreal pnd, qreal pd, qreal sp)
|
2014-03-14 01:19:36 +01:00
|
|
|
|
{
|
2014-03-19 06:22:12 +01:00
|
|
|
|
qreal lx = colOffset;
|
2014-08-10 19:39:52 +02:00
|
|
|
|
Accidental* acc = me->note->accidental();
|
|
|
|
|
qreal mag = acc->mag();
|
|
|
|
|
pnd *= mag;
|
|
|
|
|
pd *= mag;
|
2014-03-14 01:19:36 +01:00
|
|
|
|
|
|
|
|
|
// extra space for ledger lines
|
2016-12-13 13:16:17 +01:00
|
|
|
|
if (me->line <= -2 || me->line >= me->note->staff()->lines(me->note->chord()->tick()) * 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
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2018-11-08 15:45:14 +01:00
|
|
|
|
void Score::layoutChords3(std::vector<Note*>& notes, const Staff* staff, Segment* segment)
|
2014-03-05 23:45:58 +01:00
|
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// layout accidentals
|
|
|
|
|
// find column for dots
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
|
QVector<Note*> leftNotes; // notes to left of origin
|
|
|
|
|
leftNotes.reserve(8);
|
|
|
|
|
QVector<AcEl> aclist; // accidentals
|
|
|
|
|
aclist.reserve(8);
|
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
|
// track columns of octave-separated accidentals
|
|
|
|
|
int columnBottom[7] = { -1, -1, -1, -1, -1, -1, -1 };
|
2013-05-22 14:12:47 +02:00
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction tick = notes.front()->chord()->segment()->tick();
|
2016-12-23 12:05:18 +01:00
|
|
|
|
qreal sp = staff->spatium(tick);
|
|
|
|
|
qreal stepDistance = sp * staff->lineDistance(tick) * .5;
|
2016-12-13 13:16:17 +01:00
|
|
|
|
int stepOffset = staff->staffType(tick)->stepOffset();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-12-13 13:16:17 +01:00
|
|
|
|
qreal lx = 10000.0; // leftmost notehead position
|
|
|
|
|
qreal upDotPosX = 0.0;
|
|
|
|
|
qreal downDotPosX = 0.0;
|
2013-06-27 15:00:08 +02:00
|
|
|
|
|
2018-09-11 16:56:50 +02:00
|
|
|
|
int nNotes = int(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
|
|
|
|
|
|
|
|
|
Chord* chord = note->chord();
|
2013-06-04 18:29:14 +02:00
|
|
|
|
bool _up = chord->up();
|
2014-09-08 08:38:06 +02:00
|
|
|
|
|
2014-04-08 20:52:10 +02:00
|
|
|
|
qreal overlapMirror;
|
2016-04-27 16:33:30 +02:00
|
|
|
|
Stem* stem = chord->stem();
|
|
|
|
|
if (stem)
|
|
|
|
|
overlapMirror = stem->lineWidth();
|
2014-06-25 22:01:26 +02:00
|
|
|
|
else if (chord->durationType().headType() == NoteHead::Type::HEAD_WHOLE)
|
2018-03-27 15:36:00 +02:00
|
|
|
|
overlapMirror = styleP(Sid::stemWidth) * chord->mag();
|
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
|
|
|
|
|
2018-04-28 15:12:29 +02:00
|
|
|
|
qreal x = 0.0;
|
|
|
|
|
if (note->mirror())
|
2013-05-22 14:12:47 +02:00
|
|
|
|
if (_up)
|
2018-04-28 15:12:29 +02:00
|
|
|
|
x = chord->stemPosX() - overlapMirror;
|
2013-05-22 14:12:47 +02:00
|
|
|
|
else
|
2018-04-28 15:12:29 +02:00
|
|
|
|
x = -note->headBodyWidth() + overlapMirror;
|
|
|
|
|
else if (_up)
|
|
|
|
|
x = chord->stemPosX() - note->headBodyWidth();
|
2014-04-08 20:52:10 +02:00
|
|
|
|
|
2018-11-12 15:19:21 +01:00
|
|
|
|
qreal ny = (note->line() + stepOffset) * stepDistance;
|
|
|
|
|
if (note->rypos() != ny) {
|
|
|
|
|
note->rypos() = ny;
|
2018-12-20 12:38:47 +01:00
|
|
|
|
if (chord->stem()) {
|
2018-11-12 15:19:21 +01:00
|
|
|
|
chord->stem()->layout();
|
2018-12-20 12:38:47 +01:00
|
|
|
|
if (chord->hook())
|
|
|
|
|
chord->hook()->rypos() = chord->stem()->hookPos().y();
|
|
|
|
|
}
|
2018-11-12 15:19:21 +01:00
|
|
|
|
}
|
2013-05-22 14:12:47 +02:00
|
|
|
|
note->rxpos() = x;
|
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
|
|
|
|
|
2018-07-03 17:01:54 +02:00
|
|
|
|
qreal xx = x + note->headBodyWidth() + chord->pos().x();
|
2014-04-02 20:31:37 +02:00
|
|
|
|
|
2016-08-18 10:58:59 +02:00
|
|
|
|
Direction dotPosition = note->userDotPosition();
|
2014-04-08 20:52:10 +02:00
|
|
|
|
if (chord->dots()) {
|
|
|
|
|
if (chord->up())
|
|
|
|
|
upDotPosX = qMax(upDotPosX, xx);
|
2018-04-28 15:12:29 +02:00
|
|
|
|
else {
|
2018-07-03 17:01:54 +02:00
|
|
|
|
downDotPosX = qMax(downDotPosX, xx);
|
2018-04-28 15:12:29 +02:00
|
|
|
|
}
|
2014-04-02 20:31:37 +02:00
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (dotPosition == 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)
|
2016-03-02 13:20:19 +01:00
|
|
|
|
dotPosition = Direction::DOWN;
|
2014-05-07 05:56:46 +02:00
|
|
|
|
else if (intervalBelow == 1 && intervalAbove != 1)
|
2016-03-02 13:20:19 +01:00
|
|
|
|
dotPosition = 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))) {
|
2016-03-02 13:20:19 +01:00
|
|
|
|
above->setDotY(Direction::UP);
|
|
|
|
|
dotPosition = Direction::DOWN;
|
2014-04-02 20:31:37 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// space
|
|
|
|
|
if (intervalAbove == 0 && above->chord()->dots()) {
|
|
|
|
|
// unison
|
|
|
|
|
if (!(note->voice() & 1))
|
2016-03-02 13:20:19 +01:00
|
|
|
|
dotPosition = Direction::UP;
|
2014-04-02 20:31:37 +02:00
|
|
|
|
else {
|
|
|
|
|
if (!(above->voice() & 1))
|
2016-03-02 13:20:19 +01:00
|
|
|
|
above->setDotY(Direction::UP);
|
2014-04-02 20:31:37 +02:00
|
|
|
|
else
|
2016-03-02 13:20:19 +01:00
|
|
|
|
dotPosition = Direction::DOWN;
|
2014-04-02 20:31:37 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-18 10:58:59 +02:00
|
|
|
|
note->setDotY(dotPosition); // also removes invalid dots
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2013-05-22 14:12:47 +02:00
|
|
|
|
|
2018-05-08 15:43:07 +02:00
|
|
|
|
// if there are no non-mirrored notes in a downstem chord,
|
|
|
|
|
// then use the stem X position as X origin for accidental layout
|
|
|
|
|
if (nNotes && leftNotes.size() == nNotes)
|
|
|
|
|
lx = notes.front()->chord()->stemPosX();
|
|
|
|
|
|
2014-03-08 07:43:24 +01:00
|
|
|
|
if (segment) {
|
2014-04-08 20:52:10 +02:00
|
|
|
|
// align all dots for segment/staff
|
|
|
|
|
// it would be possible to dots for up & down chords separately
|
|
|
|
|
// this would require space to have been allocated previously
|
|
|
|
|
// when calculating chord offsets
|
|
|
|
|
segment->setDotPosX(staff->idx(), qMax(upDotPosX, downDotPosX));
|
2014-03-08 07:43:24 +01:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
|
|
if (nAcc == 0)
|
|
|
|
|
return;
|
2014-03-14 01:19:36 +01:00
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
|
QVector<int> umi;
|
2018-03-27 15:36:00 +02:00
|
|
|
|
qreal pd = styleP(Sid::accidentalDistance);
|
|
|
|
|
qreal pnd = styleP(Sid::accidentalNoteDistance);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
qreal colOffset = 0.0;
|
|
|
|
|
|
|
|
|
|
if (nAcc >= 2 && aclist[nAcc-1].line - aclist[0].line >= 7) {
|
|
|
|
|
|
|
|
|
|
// accidentals spread over an octave or more
|
|
|
|
|
// set up columns for accidentals with octave matches
|
|
|
|
|
// these will start at right and work to the left
|
|
|
|
|
// unmatched accidentals will use zig zag approach (see below)
|
|
|
|
|
// starting to the left of the octave columns
|
|
|
|
|
|
|
|
|
|
qreal minX = 0.0;
|
|
|
|
|
int columnTop[7] = { -1, -1, -1, -1, -1, -1, -1 };
|
|
|
|
|
|
|
|
|
|
// find columns of octaves
|
|
|
|
|
for (int pc = 0; pc < 7; ++pc) {
|
|
|
|
|
if (columnBottom[pc] == -1)
|
|
|
|
|
continue;
|
|
|
|
|
// calculate column height
|
2014-03-20 19:38:41 +01:00
|
|
|
|
for (int j = columnBottom[pc]; j != -1; j = aclist[j].next)
|
2014-03-19 06:22:12 +01:00
|
|
|
|
columnTop[pc] = j;
|
|
|
|
|
}
|
2013-06-27 15:00:08 +02:00
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
|
// compute reasonable column order
|
|
|
|
|
// use zig zag
|
2016-02-06 22:03:43 +01:00
|
|
|
|
QVector<int> column;
|
|
|
|
|
QVector<int> unmatched;
|
2014-03-19 06:22:12 +01:00
|
|
|
|
int n = nAcc - 1;
|
|
|
|
|
for (int i = 0; i <= n; ++i, --n) {
|
|
|
|
|
int pc = (aclist[i].line + 700) % 7;
|
2014-03-20 19:38:41 +01:00
|
|
|
|
if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
|
|
|
|
|
if (!column.contains(pc))
|
|
|
|
|
column.append(pc);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
|
else
|
|
|
|
|
unmatched.append(i);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
if (i == n)
|
|
|
|
|
break;
|
|
|
|
|
pc = (aclist[n].line + 700) % 7;
|
2014-03-20 19:38:41 +01:00
|
|
|
|
if (aclist[columnTop[pc]].line != aclist[columnBottom[pc]].line) {
|
|
|
|
|
if (!column.contains(pc))
|
|
|
|
|
column.append(pc);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
|
else
|
|
|
|
|
unmatched.append(n);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
}
|
2014-03-20 19:38:41 +01:00
|
|
|
|
int nColumns = column.size();
|
|
|
|
|
int nUnmatched = unmatched.size();
|
|
|
|
|
|
|
|
|
|
// handle unmatched accidentals
|
|
|
|
|
for (int i = 0; i < nUnmatched; ++i) {
|
|
|
|
|
// first try to slot it into an existing column
|
|
|
|
|
AcEl* me = &aclist[unmatched[i]];
|
|
|
|
|
// find column
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (int j = 0; j < nColumns; ++j) {
|
|
|
|
|
int pc = column[j];
|
|
|
|
|
int above = -1;
|
|
|
|
|
int below = -1;
|
|
|
|
|
// find slot within column
|
|
|
|
|
for (int k = columnBottom[pc]; k != -1; k = aclist[k].next) {
|
|
|
|
|
if (aclist[k].line < me->line) {
|
|
|
|
|
above = k;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
below = k;
|
|
|
|
|
}
|
|
|
|
|
// check to see if accidental can fit in slot
|
2014-08-10 19:39:52 +02:00
|
|
|
|
qreal myPd = pd * me->note->accidental()->mag();
|
2014-03-20 19:38:41 +01:00
|
|
|
|
bool conflict = false;
|
|
|
|
|
if (above != -1 && me->top - aclist[above].bottom < myPd)
|
|
|
|
|
conflict = true;
|
|
|
|
|
else if (below != -1 && aclist[below].top - me->bottom < myPd)
|
|
|
|
|
conflict = true;
|
|
|
|
|
if (!conflict) {
|
|
|
|
|
// insert into column
|
|
|
|
|
found = true;
|
|
|
|
|
me->next = above;
|
|
|
|
|
if (above == -1)
|
|
|
|
|
columnTop[pc] = unmatched[i];
|
|
|
|
|
if (below != -1)
|
|
|
|
|
aclist[below].next = unmatched[i];
|
|
|
|
|
else
|
|
|
|
|
columnBottom[pc] = unmatched[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if no slot found, then add to list of unmatched accidental indices
|
|
|
|
|
if (!found)
|
2016-02-06 22:03:43 +01:00
|
|
|
|
umi.push_back(unmatched[i]);
|
2014-03-20 19:38:41 +01:00
|
|
|
|
}
|
|
|
|
|
nAcc = umi.size();
|
|
|
|
|
if (nAcc > 1)
|
|
|
|
|
qSort(umi);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
|
// lay out columns
|
|
|
|
|
for (int i = 0; i < nColumns; ++i) {
|
|
|
|
|
int pc = column[i];
|
|
|
|
|
AcEl* below = 0;
|
|
|
|
|
// lay out accidentals
|
|
|
|
|
for (int j = columnBottom[pc]; j != -1; j = aclist[j].next) {
|
|
|
|
|
qreal x = layoutAccidental(&aclist[j], 0, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
minX = qMin(minX, x);
|
|
|
|
|
below = &aclist[j];
|
|
|
|
|
}
|
|
|
|
|
// align within column
|
|
|
|
|
int next = -1;
|
|
|
|
|
for (int j = columnBottom[pc]; j != -1; j = next) {
|
|
|
|
|
next = aclist[j].next;
|
|
|
|
|
if (next != -1 && aclist[j].line == aclist[next].line)
|
|
|
|
|
continue;
|
|
|
|
|
aclist[j].x = minX;
|
|
|
|
|
}
|
|
|
|
|
// move to next column
|
|
|
|
|
colOffset = minX;
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
for (int i = 0; i < nAcc; ++i)
|
2016-02-06 22:03:43 +01:00
|
|
|
|
umi.push_back(i);
|
2014-03-19 06:22:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nAcc) {
|
|
|
|
|
|
|
|
|
|
// for accidentals with no octave matches, use zig zag approach
|
|
|
|
|
// layout right to left in pairs, (next) highest then lowest
|
2014-03-13 07:52:59 +01:00
|
|
|
|
|
2014-03-19 06:22:12 +01:00
|
|
|
|
AcEl* me = &aclist[umi[0]];
|
|
|
|
|
AcEl* above = 0;
|
|
|
|
|
AcEl* below = 0;
|
|
|
|
|
|
|
|
|
|
// layout top accidental
|
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
|
|
|
|
|
// layout bottom accidental
|
|
|
|
|
int n = nAcc - 1;
|
|
|
|
|
if (n > 0) {
|
2014-03-13 07:52:59 +01:00
|
|
|
|
above = me;
|
2014-03-19 06:22:12 +01:00
|
|
|
|
me = &aclist[umi[n]];
|
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// layout middle accidentals
|
|
|
|
|
if (n > 1) {
|
|
|
|
|
for (int i = 1; i < n; ++i, --n) {
|
|
|
|
|
// next highest
|
|
|
|
|
below = me;
|
|
|
|
|
me = &aclist[umi[i]];
|
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
if (i == n - 1)
|
|
|
|
|
break;
|
|
|
|
|
// next lowest
|
|
|
|
|
above = me;
|
|
|
|
|
me = &aclist[umi[n-1]];
|
|
|
|
|
layoutAccidental(me, above, below, colOffset, leftNotes, pnd, pd, sp);
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2014-03-19 06:22:12 +01:00
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-22 14:12:47 +02:00
|
|
|
|
for (const AcEl& e : aclist) {
|
2014-05-21 00:43:28 +02:00
|
|
|
|
// even though we initially calculate accidental position relative to segment
|
|
|
|
|
// we must record pos for accidental relative to note,
|
|
|
|
|
// since pos is always interpreted relative to parent
|
2013-05-22 14:12:47 +02:00
|
|
|
|
Note* note = e.note;
|
2014-03-09 07:09:40 +01:00
|
|
|
|
qreal x = e.x + lx - (note->x() + note->chord()->x());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
note->accidental()->setPos(x, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-26 07:45:09 +02:00
|
|
|
|
#define beamModeMid(a) (a == Beam::Mode::MID || a == Beam::Mode::BEGIN32 || a == Beam::Mode::BEGIN64)
|
|
|
|
|
|
2018-12-22 14:55:01 +01:00
|
|
|
|
bool beamNoContinue(Beam::Mode mode)
|
|
|
|
|
{
|
|
|
|
|
return mode == Beam::Mode::END || mode == Beam::Mode::NONE || mode == Beam::Mode::INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// beamGraceNotes
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2014-05-15 13:42:03 +02:00
|
|
|
|
void Score::beamGraceNotes(Chord* mainNote, bool after)
|
2013-06-16 23:33:37 +02:00
|
|
|
|
{
|
2013-11-24 20:39:58 +01:00
|
|
|
|
ChordRest* a1 = 0; // start of (potential) beam
|
|
|
|
|
Beam* beam = 0; // current beam
|
2014-06-26 07:45:09 +02:00
|
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
2016-02-06 22:03:43 +01:00
|
|
|
|
QVector<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();
|
2015-02-19 10:28:25 +01:00
|
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
|
for (ChordRest* cr : graceNotes) {
|
2013-11-24 20:39:58 +01:00
|
|
|
|
bm = Groups::endBeam(cr);
|
2014-06-26 07:45:09 +02:00
|
|
|
|
if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
|
2013-11-24 20:39:58 +01:00
|
|
|
|
if (beam) {
|
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
|
beam = 0;
|
|
|
|
|
}
|
|
|
|
|
if (a1) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
a1->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
|
a1 = 0;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
cr->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (beam) {
|
2014-06-26 07:45:09 +02:00
|
|
|
|
bool beamEnd = bm == Beam::Mode::BEGIN;
|
2013-11-24 20:39:58 +01:00
|
|
|
|
if (!beamEnd) {
|
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
|
beam->add(cr);
|
|
|
|
|
cr = 0;
|
2014-06-26 07:45:09 +02:00
|
|
|
|
beamEnd = (bm == Beam::Mode::END);
|
2013-11-24 20:39:58 +01:00
|
|
|
|
}
|
|
|
|
|
if (beamEnd) {
|
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
|
beam = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!cr)
|
|
|
|
|
continue;
|
|
|
|
|
if (a1 == 0)
|
|
|
|
|
a1 = cr;
|
|
|
|
|
else {
|
2014-06-26 07:45:09 +02:00
|
|
|
|
if (!beamModeMid(bm) && (bm == Beam::Mode::BEGIN)) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
a1->removeDeleteBeam(false);
|
2013-11-24 20:39:58 +01:00
|
|
|
|
a1 = cr;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
beam = a1->beam();
|
|
|
|
|
if (beam == 0 || beam->elements().front() != a1) {
|
|
|
|
|
beam = new Beam(this);
|
|
|
|
|
beam->setGenerated(true);
|
|
|
|
|
beam->setTrack(mainNote->track());
|
|
|
|
|
a1->removeDeleteBeam(true);
|
|
|
|
|
beam->add(a1);
|
|
|
|
|
}
|
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
|
beam->add(cr);
|
|
|
|
|
a1 = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-10 18:01:37 +02:00
|
|
|
|
}
|
2013-11-24 20:39:58 +01:00
|
|
|
|
if (beam)
|
|
|
|
|
beam->layoutGraceNotes();
|
|
|
|
|
else if (a1)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
a1->removeDeleteBeam(false);
|
2013-06-16 23:33:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 23:35:00 +01:00
|
|
|
|
#if 0 // unused
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// layoutSpanner
|
|
|
|
|
// called after dragging a staff
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
void Score::layoutSpanner()
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int tracks = ntracks();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
2017-03-08 13:12:26 +01:00
|
|
|
|
for (Segment* segment = firstSegment(SegmentType::All); segment; segment = segment->next1()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (track == tracks-1) {
|
2018-09-11 16:56:50 +02:00
|
|
|
|
size_t n = segment->annotations().size();
|
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
segment->annotations().at(i)->layout();
|
|
|
|
|
}
|
2016-08-23 16:39:27 +02:00
|
|
|
|
Element* e = segment->element(track);
|
|
|
|
|
if (e && e->isChord()) {
|
|
|
|
|
Chord* c = toChord(segment->element(track));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
c->layoutStem();
|
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
|
Tie* tie = n->tieFor();
|
2018-07-12 10:15:56 +02:00
|
|
|
|
if (tie)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
tie->layout();
|
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
|
sp->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rebuildBspTree();
|
|
|
|
|
}
|
2018-11-25 23:35:00 +01:00
|
|
|
|
#endif
|
2015-01-21 11:55:36 +01:00
|
|
|
|
|
2013-07-23 18:49:26 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// hideEmptyStaves
|
2013-07-23 18:49:26 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
void Score::hideEmptyStaves(System* system, bool isFirstSystem)
|
2013-07-23 18:49:26 +02:00
|
|
|
|
{
|
2016-04-19 16:56:28 +02:00
|
|
|
|
int staves = _staves.size();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int staffIdx = 0;
|
|
|
|
|
bool systemIsEmpty = true;
|
2012-06-10 10:35:17 +02:00
|
|
|
|
|
2016-04-19 16:56:28 +02:00
|
|
|
|
for (Staff* staff : _staves) {
|
|
|
|
|
SysStaff* ss = system->staff(staffIdx);
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
Staff::HideMode hideMode = staff->hideWhenEmpty();
|
2016-04-19 16:56:28 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (hideMode == Staff::HideMode::ALWAYS
|
2018-03-27 15:36:00 +02:00
|
|
|
|
|| (styleB(Sid::hideEmptyStaves)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
&& (staves > 1)
|
2018-03-27 15:36:00 +02:00
|
|
|
|
&& !(isFirstSystem && styleB(Sid::dontHideStavesInFirstSystem))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
&& hideMode != Staff::HideMode::NEVER)) {
|
|
|
|
|
bool hideStaff = true;
|
2016-04-19 16:56:28 +02:00
|
|
|
|
for (MeasureBase* m : system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (!m->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Measure* measure = toMeasure(m);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (!measure->isMeasureRest(staffIdx)) {
|
|
|
|
|
hideStaff = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
break;
|
2015-02-14 15:10:35 +01:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// check if notes moved into this staff
|
|
|
|
|
Part* part = staff->part();
|
|
|
|
|
int n = part->nstaves();
|
|
|
|
|
if (hideStaff && (n > 1)) {
|
|
|
|
|
int idx = part->staves()->front()->idx();
|
|
|
|
|
for (int i = 0; i < part->nstaves(); ++i) {
|
|
|
|
|
int st = idx + i;
|
2014-09-02 19:54:36 +02:00
|
|
|
|
|
2018-12-07 07:25:09 +01:00
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (!mb->isMeasure())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Measure* m = toMeasure(mb);
|
2018-12-07 07:25:09 +01:00
|
|
|
|
if (staff->hideWhenEmpty() == Staff::HideMode::INSTRUMENT && !m->isMeasureRest(st)) {
|
|
|
|
|
hideStaff = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-08 13:12:26 +01:00
|
|
|
|
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
2016-02-04 17:06:32 +01:00
|
|
|
|
ChordRest* cr = s->cr(st * VOICES + voice);
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (cr == 0 || cr->isRest())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
|
|
|
|
int staffMove = cr->staffMove();
|
|
|
|
|
if (staffIdx == st + staffMove) {
|
|
|
|
|
hideStaff = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hideStaff)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!hideStaff)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-09-02 19:54:36 +02:00
|
|
|
|
}
|
2016-04-19 16:56:28 +02:00
|
|
|
|
ss->setShow(hideStaff ? false : staff->show());
|
|
|
|
|
if (ss->show())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
systemIsEmpty = false;
|
|
|
|
|
}
|
2019-02-23 03:56:56 +01:00
|
|
|
|
else if (!staff->show()) {
|
|
|
|
|
// TODO: OK to check this first and not bother with checking if empty?
|
|
|
|
|
ss->setShow(false);
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
systemIsEmpty = false;
|
2016-04-19 16:56:28 +02:00
|
|
|
|
ss->setShow(true);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2013-10-28 15:52:32 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
++staffIdx;
|
|
|
|
|
}
|
2018-12-15 04:18:48 +01:00
|
|
|
|
Staff* firstVisible = nullptr;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (systemIsEmpty) {
|
2016-04-19 16:56:28 +02:00
|
|
|
|
for (Staff* staff : _staves) {
|
|
|
|
|
SysStaff* ss = system->staff(staff->idx());
|
|
|
|
|
if (staff->showIfEmpty() && !ss->show()) {
|
|
|
|
|
ss->setShow(true);
|
|
|
|
|
systemIsEmpty = false;
|
2013-09-20 17:21:12 +02:00
|
|
|
|
}
|
2018-12-15 04:18:48 +01:00
|
|
|
|
else if (!firstVisible && staff->show()) {
|
|
|
|
|
firstVisible = staff;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-27 22:49:18 +01:00
|
|
|
|
// don’t allow a complete empty system
|
2016-04-19 16:56:28 +02:00
|
|
|
|
if (systemIsEmpty) {
|
2018-12-15 04:18:48 +01:00
|
|
|
|
Staff* staff = firstVisible ? firstVisible : _staves.front();
|
2016-04-19 16:56:28 +02:00
|
|
|
|
SysStaff* ss = system->staff(staff->idx());
|
|
|
|
|
ss->setShow(true);
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2013-09-20 17:21:12 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// connectTies
|
|
|
|
|
/// Rebuild tie connections.
|
|
|
|
|
//---------------------------------------------------------
|
2014-05-31 12:03:21 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
void Score::connectTies(bool silent)
|
|
|
|
|
{
|
|
|
|
|
int tracks = nstaves() * VOICES;
|
|
|
|
|
Measure* m = firstMeasure();
|
|
|
|
|
if (!m)
|
|
|
|
|
return;
|
2018-12-12 13:40:19 +01:00
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
|
SegmentType st = SegmentType::ChordRest;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (Segment* s = m->first(st); s; s = s->next1(st)) {
|
|
|
|
|
for (int i = 0; i < tracks; ++i) {
|
2016-09-20 10:24:51 +02:00
|
|
|
|
Element* e = s->element(i);
|
|
|
|
|
if (e == 0 || !e->isChord())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2016-09-20 10:24:51 +02:00
|
|
|
|
Chord* c = toChord(e);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
|
// connect a tie without end note
|
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
|
if (tie && !tie->endNote()) {
|
|
|
|
|
Note* nnote;
|
|
|
|
|
if (_mscVersion <= 114)
|
|
|
|
|
nnote = searchTieNote114(n);
|
|
|
|
|
else
|
|
|
|
|
nnote = searchTieNote(n);
|
|
|
|
|
if (nnote == 0) {
|
|
|
|
|
if (!silent) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
qDebug("next note at %d track %d for tie not found (version %d)", s->tick().ticks(), i, _mscVersion);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
delete tie;
|
|
|
|
|
n->setTieFor(0);
|
2013-09-19 15:08:54 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
tie->setEndNote(nnote);
|
|
|
|
|
nnote->setTieBack(tie);
|
|
|
|
|
}
|
2013-09-19 15:08:54 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// connect a glissando without initial note (old glissando format)
|
|
|
|
|
for (Spanner* spanner : n->spannerBack()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (spanner->isGlissando() && !spanner->startElement()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
Note* initialNote = Glissando::guessInitialNote(n->chord());
|
|
|
|
|
n->removeSpannerBack(spanner);
|
2016-02-15 12:23:28 +01:00
|
|
|
|
if (initialNote) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
spanner->setStartElement(initialNote);
|
|
|
|
|
spanner->setEndElement(n);
|
|
|
|
|
spanner->setTick(initialNote->chord()->tick());
|
|
|
|
|
spanner->setTick2(n->chord()->tick());
|
|
|
|
|
spanner->setTrack(n->track());
|
|
|
|
|
spanner->setTrack2(n->track());
|
|
|
|
|
spanner->setParent(initialNote);
|
|
|
|
|
initialNote->add(spanner);
|
2014-05-31 12:03:21 +02:00
|
|
|
|
}
|
2013-09-19 17:17:22 +02:00
|
|
|
|
else {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
delete spanner;
|
2013-09-19 17:17:22 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// spanner with no end element can happen during copy/paste
|
|
|
|
|
for (Spanner* spanner : n->spannerFor()) {
|
|
|
|
|
if (spanner->endElement() == nullptr) {
|
|
|
|
|
n->removeSpannerFor(spanner);
|
|
|
|
|
delete spanner;
|
2013-10-30 14:21:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-12 13:40:19 +01:00
|
|
|
|
#if 0 // chords are set in tremolo->layout()
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// connect two note tremolos
|
|
|
|
|
Tremolo* tremolo = c->tremolo();
|
|
|
|
|
if (tremolo && tremolo->twoNotes() && !tremolo->chord2()) {
|
|
|
|
|
for (Segment* ls = s->next1(st); ls; ls = ls->next1(st)) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Element* element = ls->element(i);
|
|
|
|
|
if (!element)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2018-07-10 14:57:05 +02:00
|
|
|
|
if (!element->isChord())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qDebug("cannot connect tremolo");
|
|
|
|
|
else {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Chord* nc = toChord(element);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
nc->setTremolo(tremolo);
|
|
|
|
|
tremolo->setChords(c, nc);
|
|
|
|
|
// cross-measure tremolos are not supported
|
|
|
|
|
// but can accidentally result from copy & paste
|
|
|
|
|
// remove them now
|
|
|
|
|
if (c->measure() != nc->measure())
|
|
|
|
|
c->remove(tremolo);
|
2013-10-30 14:21:08 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
break;
|
2013-10-25 12:17:42 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-12 13:40:19 +01:00
|
|
|
|
#endif
|
2014-08-17 12:41:44 +02:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-10 13:40:34 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// checkDivider
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2017-07-18 10:46:15 +02:00
|
|
|
|
static void checkDivider(bool left, System* s, qreal yOffset)
|
2016-02-10 13:40:34 +01:00
|
|
|
|
{
|
|
|
|
|
SystemDivider* divider = left ? s->systemDividerLeft() : s->systemDividerRight();
|
2018-03-27 15:36:00 +02:00
|
|
|
|
if (s->score()->styleB(left ? Sid::dividerLeft : Sid::dividerRight)) {
|
2016-02-10 13:40:34 +01:00
|
|
|
|
if (!divider) {
|
|
|
|
|
divider = new SystemDivider(s->score());
|
|
|
|
|
divider->setDividerType(left ? SystemDivider::Type::LEFT : SystemDivider::Type::RIGHT);
|
|
|
|
|
divider->setGenerated(true);
|
|
|
|
|
s->add(divider);
|
|
|
|
|
}
|
|
|
|
|
divider->layout();
|
2017-07-18 10:46:15 +02:00
|
|
|
|
divider->rypos() = divider->height() * .5 + yOffset;
|
2016-12-16 17:17:12 +01:00
|
|
|
|
if (left) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
divider->rypos() += s->score()->styleD(Sid::dividerLeftY) * SPATIUM20;
|
|
|
|
|
divider->rxpos() = s->score()->styleD(Sid::dividerLeftX) * SPATIUM20;
|
2016-12-16 17:17:12 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
divider->rypos() += s->score()->styleD(Sid::dividerRightY) * SPATIUM20;
|
|
|
|
|
divider->rxpos() = s->score()->styleD(Sid::pagePrintableWidth) * DPI - divider->width();
|
|
|
|
|
divider->rxpos() += s->score()->styleD(Sid::dividerRightX) * SPATIUM20;
|
2016-12-16 17:17:12 +01:00
|
|
|
|
}
|
2016-02-10 13:40:34 +01:00
|
|
|
|
}
|
|
|
|
|
else if (divider) {
|
2016-05-25 15:30:10 +02:00
|
|
|
|
if (divider->generated()) {
|
2016-02-10 13:40:34 +01:00
|
|
|
|
s->remove(divider);
|
|
|
|
|
delete divider;
|
|
|
|
|
}
|
2016-05-25 15:30:10 +02:00
|
|
|
|
else
|
|
|
|
|
s->score()->undoRemoveElement(divider);
|
2016-02-10 13:40:34 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutPage
|
2016-03-24 12:39:18 +01:00
|
|
|
|
// restHeight - vertical space which has to be distributed
|
|
|
|
|
// between systems
|
2017-07-17 15:09:37 +02:00
|
|
|
|
// The algorithm tries to produce most equally spaced
|
|
|
|
|
// systems.
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
2012-09-06 12:35:34 +02:00
|
|
|
|
|
2016-03-24 12:39:18 +01:00
|
|
|
|
static void layoutPage(Page* page, qreal restHeight)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
{
|
2018-11-29 18:28:18 +01:00
|
|
|
|
if (restHeight < 0.0) {
|
|
|
|
|
qDebug("restHeight < 0.0: %f\n", restHeight);
|
|
|
|
|
restHeight = 0;
|
|
|
|
|
}
|
2018-11-02 11:59:15 +01:00
|
|
|
|
|
|
|
|
|
Score* score = page->score();
|
|
|
|
|
int gaps = page->systems().size() - 1;
|
2017-07-17 15:09:37 +02:00
|
|
|
|
|
|
|
|
|
QList<System*> sList;
|
|
|
|
|
|
2018-11-02 11:59:15 +01:00
|
|
|
|
for (int i = 0; i < gaps; ++i) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
System* s1 = page->systems().at(i);
|
|
|
|
|
System* s2 = page->systems().at(i+1);
|
2017-07-17 15:09:37 +02:00
|
|
|
|
s1->setDistance(s2->y() - s1->y());
|
2016-10-12 20:06:46 +02:00
|
|
|
|
if (s1->vbox() || s2->vbox() || s1->hasFixedDownDistance())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2017-07-17 15:09:37 +02:00
|
|
|
|
sList.push_back(s1);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2016-03-31 09:57:52 +02:00
|
|
|
|
|
2017-08-10 14:55:04 +02:00
|
|
|
|
if (sList.empty() || MScore::noVerticalStretch || score->layoutMode() == LayoutMode::SYSTEM) {
|
2016-03-24 12:39:18 +01:00
|
|
|
|
if (score->layoutMode() == LayoutMode::FLOAT) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal y = restHeight * .5;
|
|
|
|
|
for (System* system : page->systems())
|
|
|
|
|
system->move(QPointF(0.0, y));
|
|
|
|
|
}
|
|
|
|
|
// remove system dividers
|
|
|
|
|
for (System* s : page->systems()) {
|
2016-02-10 13:40:34 +01:00
|
|
|
|
SystemDivider* sd = s->systemDividerLeft();
|
|
|
|
|
if (sd) {
|
|
|
|
|
s->remove(sd);
|
|
|
|
|
delete sd;
|
|
|
|
|
}
|
|
|
|
|
sd = s->systemDividerRight();
|
|
|
|
|
if (sd) {
|
|
|
|
|
s->remove(sd);
|
|
|
|
|
delete sd;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-17 15:09:37 +02:00
|
|
|
|
std::sort(sList.begin(), sList.end(), [](System* a, System* b) { return a->distance() < b->distance(); });
|
2018-03-27 15:36:00 +02:00
|
|
|
|
qreal maxDist = score->styleP(Sid::maxSystemDistance);
|
2017-08-11 10:41:30 +02:00
|
|
|
|
|
2017-07-17 15:09:37 +02:00
|
|
|
|
qreal dist = sList[0]->distance();
|
2017-08-10 14:55:04 +02:00
|
|
|
|
for (int i = 1; i < sList.size(); ++i) {
|
2017-07-17 15:09:37 +02:00
|
|
|
|
qreal ndist = sList[i]->distance();
|
2018-11-02 11:59:15 +01:00
|
|
|
|
qreal fill = ndist - dist;
|
2017-07-17 15:09:37 +02:00
|
|
|
|
if (fill > 0.0) {
|
|
|
|
|
qreal totalFill = fill * i;
|
|
|
|
|
if (totalFill > restHeight) {
|
|
|
|
|
totalFill = restHeight;
|
|
|
|
|
fill = restHeight / i;
|
2012-07-31 09:48:37 +02:00
|
|
|
|
}
|
2017-07-17 15:09:37 +02:00
|
|
|
|
for (int k = 0; k < i; ++k) {
|
|
|
|
|
System* s = sList[k];
|
2017-08-11 10:41:30 +02:00
|
|
|
|
qreal d = s->distance() + fill;
|
|
|
|
|
if ((d - s->height()) > maxDist)
|
|
|
|
|
d = qMax(maxDist + s->height(), s->distance());
|
|
|
|
|
s->setDistance(d);
|
2017-07-17 15:09:37 +02:00
|
|
|
|
}
|
|
|
|
|
restHeight -= totalFill;
|
2017-08-10 14:55:04 +02:00
|
|
|
|
if (restHeight <= 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-11-02 11:59:15 +01:00
|
|
|
|
dist = ndist;
|
2017-08-10 14:55:04 +02:00
|
|
|
|
}
|
2018-11-02 11:59:15 +01:00
|
|
|
|
|
2017-08-10 14:55:04 +02:00
|
|
|
|
if (restHeight > 0.0) {
|
|
|
|
|
qreal fill = restHeight / sList.size();
|
2018-11-02 11:59:15 +01:00
|
|
|
|
for (System* s : sList) {
|
2017-08-11 10:41:30 +02:00
|
|
|
|
qreal d = s->distance() + fill;
|
|
|
|
|
if ((d - s->height()) > maxDist)
|
|
|
|
|
d = qMax(maxDist + s->height(), s->distance());
|
|
|
|
|
s->setDistance(d);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2017-07-17 15:09:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qreal y = page->systems().at(0)->y();
|
2018-11-02 11:59:15 +01:00
|
|
|
|
for (int i = 0; i < gaps; ++i) {
|
2017-07-18 10:46:15 +02:00
|
|
|
|
System* s1 = page->systems().at(i);
|
|
|
|
|
System* s2 = page->systems().at(i+1);
|
|
|
|
|
s1->rypos() = y;
|
|
|
|
|
y += s1->distance();
|
|
|
|
|
|
|
|
|
|
if (!(s1->vbox() || s2->vbox() || s1->hasFixedDownDistance())) {
|
|
|
|
|
qreal yOffset = s1->height() + (s1->distance()-s1->height()) * .5;
|
|
|
|
|
checkDivider(true, s1, yOffset);
|
|
|
|
|
checkDivider(false, s1, yOffset);
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2017-07-18 10:46:15 +02:00
|
|
|
|
page->systems().back()->rypos() = y;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2014-10-22 17:17:26 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
2016-05-02 13:41:41 +02:00
|
|
|
|
// Spring
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-05-02 13:41:41 +02:00
|
|
|
|
struct Spring {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int seg;
|
|
|
|
|
qreal stretch;
|
|
|
|
|
qreal fix;
|
2016-05-02 13:41:41 +02:00
|
|
|
|
Spring(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
};
|
2012-09-06 12:35:34 +02:00
|
|
|
|
|
2016-05-02 13:41:41 +02:00
|
|
|
|
typedef std::multimap<qreal, Spring, std::less<qreal> > SpringMap;
|
2014-10-21 18:57:43 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// sff2
|
|
|
|
|
// compute 1/Force for a given Extend
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-05-02 13:41:41 +02:00
|
|
|
|
static qreal sff2(qreal width, qreal xMin, const SpringMap& springs)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
{
|
2016-05-02 13:41:41 +02:00
|
|
|
|
if (width <= xMin)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return 0.0;
|
|
|
|
|
auto i = springs.begin();
|
|
|
|
|
qreal c = i->second.stretch;
|
|
|
|
|
if (c == 0.0) //DEBUG
|
|
|
|
|
c = 1.1;
|
|
|
|
|
qreal f = 0.0;
|
|
|
|
|
for (; i != springs.end();) {
|
|
|
|
|
xMin -= i->second.fix;
|
2016-05-02 13:41:41 +02:00
|
|
|
|
f = (width - xMin) / c;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
++i;
|
|
|
|
|
if (i == springs.end() || f <= i->first)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
c += i->second.stretch;
|
|
|
|
|
}
|
|
|
|
|
return f;
|
|
|
|
|
}
|
2012-09-06 12:35:34 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// respace
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-04-07 11:18:34 +02:00
|
|
|
|
void Score::respace(std::vector<ChordRest*>* elements)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
{
|
|
|
|
|
ChordRest* cr1 = elements->front();
|
|
|
|
|
ChordRest* cr2 = elements->back();
|
2018-09-11 16:56:50 +02:00
|
|
|
|
int n = int(elements->size());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal x1 = cr1->segment()->pos().x();
|
|
|
|
|
qreal x2 = cr2->segment()->pos().x();
|
|
|
|
|
|
Solved all compilation errors on MSVC
This commit contains changes required for MuseScore to compile under MSVC with no errors.
There are several general categories of problems that resulted in errors with the compilation. Main issues:
- Variable Length Arrays (VLA). This is a non-standard extension to C++, supported by clang toolchains, but not by MSVC. The initial workaround is to use std::vector<> instead of VLAs, eventually (if needed) with the original variable pointing to the beginning of the allocated array. More efficient alternatives are possible if profiling shows any code using VLAs to be timing-critical.
- Floating-point constants not suffixed with "f" are doubles by default; in some instances, this leads to narrowing conversion errors (in other instances, just warnings).
- MSVC does not support "or"/"and"/"not" in lieu of "||"/"&&"/"!". Changed unconditionally to use standard C++ symbols.
- MSVC does not support the "__builtin_unreachable()" compiler hint. A similar, albeit not exactly equal, alternative is "__assume(0)", which is MSVC-specific.
- MSVC does not support ranges in case statements. Replaced with list of cases instead of range (non-conditionally)
Detailed changes, with per-file comments:
- all.h: opt-in to deprecated features, include <io.h> and <process.h> instead of POSIX <unistd.h>, undefine "STRING_NONE" and "small", which Microsoft defines as macros, which result in compilation errors.
- effects/compressor/zita.cpp: eliminated narrowing conversion error by appending "f" to float constants.
- fluid/voice.cpp: appended "f" to float constants to eliminate narrowing conversion errors/warnings
- libmscore/beam.cpp: conditionally replaced VLA
- libmscore/edit.cpp: conditionally replaced VLA
- libmscore/element.cpp: appended "f" to float constant
- libmscore/fret.cpp: changed or -> ||
- libmscore/layout.cpp: conditionally replaced VLA
- libmscore/mscore.cpp: conditionally replaced "__builtin_unreachable()" with "__assume(0)" for MSVC
- libmscore/scorefile.coo: use correct char representation conversion for MSVC
- libmscore/stringdata.cpp: conditionally replaced VLA
- libmscore/system.cpp: conditionally replaced VLA
- libmscroe/text.cpp: replaced range in case statement.
- manual/genManual.cpp: use getopt() replacement.
- midi/midifile.cpp: conditionally replaced VLA. This does not use the default replacement.
- mscore/bb.cpp: replaced range in case statement.
- mscore/capella.cpp: conditionally replaced VLA. Changed and -> &&
- mscore/driver:cpp: preclude errors due to macro redefinitions.
- mscore/editstyle.cpp: conditionally replaced "__builtin_unreachable()" with "__assume(0)" for MSVC
- mscore/importgtp-gp6.cpp: conditionally replaced VLA.
- mscore/instrwidget.cpp: conditionally replaced VLA.
- mscore/jackaudio.cpp: conditionally replaced VLA. Preclude errors due to macro redefinitions.
- mscore/jackweakapi.cpp: Preclude errors due to macro redefinitions. Replacement for __atribute__((constructor)) through static object construction for MSVC. Force use of LoadLibraryA instead of LoadLibrary.
- mscore/mididriver.h: Changed not -> !
- mscore/musescore.cpp: Changed not -> !. Conditionally replaced VLA.
- mscore/resourceManager.cpp: conditionally replaced VLA.
- mscore/timeline.cpp: conditionally replaced VLA.
- omr/omrpage.cpp: conditionally replaced VLA.
- synthesizer/msynthesizer.cpp: replaced UNIX sleep(1) method with MSVC Sleep(1000) (equivalent, but in ms instead of seconds)
- synthesizer/msynthsizer.h: appended "f" to float constant
- thirdparty/poppler/config.h: set defines for MSVC to preclude the use of inexistent libraries.
- thirdparty/poppler/poppler/poppler-config.h: set defines for MSVC to preclude the use of inexistent libraries. Eliminated #defines for fmin and fmax which where causing problems.
- thirdparty/poppler/poppler/PSOutputDev.cc: added #include <algorithm> for std::min() and std::max(). Note this is required per-C++ standard.
- thirdparty/portmidi/pm_win/pmwinmm.c: undefined UNICODE to use char-based library functions.
- thirdparty/qzip/qzip.cpp: changed or -> ||
- thirdparty/rtf2html/rtf_keyword.h: file format changed from Apple to UNIX, as MSVC does not supported the former.
(NOT error related, just improvement on previous commit; manual/getopt/README.md: added link to source article)
2018-05-01 19:14:44 +02:00
|
|
|
|
#if (!defined (_MSCVER) && !defined (_MSC_VER))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal width[n-1];
|
|
|
|
|
int ticksList[n-1];
|
Solved all compilation errors on MSVC
This commit contains changes required for MuseScore to compile under MSVC with no errors.
There are several general categories of problems that resulted in errors with the compilation. Main issues:
- Variable Length Arrays (VLA). This is a non-standard extension to C++, supported by clang toolchains, but not by MSVC. The initial workaround is to use std::vector<> instead of VLAs, eventually (if needed) with the original variable pointing to the beginning of the allocated array. More efficient alternatives are possible if profiling shows any code using VLAs to be timing-critical.
- Floating-point constants not suffixed with "f" are doubles by default; in some instances, this leads to narrowing conversion errors (in other instances, just warnings).
- MSVC does not support "or"/"and"/"not" in lieu of "||"/"&&"/"!". Changed unconditionally to use standard C++ symbols.
- MSVC does not support the "__builtin_unreachable()" compiler hint. A similar, albeit not exactly equal, alternative is "__assume(0)", which is MSVC-specific.
- MSVC does not support ranges in case statements. Replaced with list of cases instead of range (non-conditionally)
Detailed changes, with per-file comments:
- all.h: opt-in to deprecated features, include <io.h> and <process.h> instead of POSIX <unistd.h>, undefine "STRING_NONE" and "small", which Microsoft defines as macros, which result in compilation errors.
- effects/compressor/zita.cpp: eliminated narrowing conversion error by appending "f" to float constants.
- fluid/voice.cpp: appended "f" to float constants to eliminate narrowing conversion errors/warnings
- libmscore/beam.cpp: conditionally replaced VLA
- libmscore/edit.cpp: conditionally replaced VLA
- libmscore/element.cpp: appended "f" to float constant
- libmscore/fret.cpp: changed or -> ||
- libmscore/layout.cpp: conditionally replaced VLA
- libmscore/mscore.cpp: conditionally replaced "__builtin_unreachable()" with "__assume(0)" for MSVC
- libmscore/scorefile.coo: use correct char representation conversion for MSVC
- libmscore/stringdata.cpp: conditionally replaced VLA
- libmscore/system.cpp: conditionally replaced VLA
- libmscroe/text.cpp: replaced range in case statement.
- manual/genManual.cpp: use getopt() replacement.
- midi/midifile.cpp: conditionally replaced VLA. This does not use the default replacement.
- mscore/bb.cpp: replaced range in case statement.
- mscore/capella.cpp: conditionally replaced VLA. Changed and -> &&
- mscore/driver:cpp: preclude errors due to macro redefinitions.
- mscore/editstyle.cpp: conditionally replaced "__builtin_unreachable()" with "__assume(0)" for MSVC
- mscore/importgtp-gp6.cpp: conditionally replaced VLA.
- mscore/instrwidget.cpp: conditionally replaced VLA.
- mscore/jackaudio.cpp: conditionally replaced VLA. Preclude errors due to macro redefinitions.
- mscore/jackweakapi.cpp: Preclude errors due to macro redefinitions. Replacement for __atribute__((constructor)) through static object construction for MSVC. Force use of LoadLibraryA instead of LoadLibrary.
- mscore/mididriver.h: Changed not -> !
- mscore/musescore.cpp: Changed not -> !. Conditionally replaced VLA.
- mscore/resourceManager.cpp: conditionally replaced VLA.
- mscore/timeline.cpp: conditionally replaced VLA.
- omr/omrpage.cpp: conditionally replaced VLA.
- synthesizer/msynthesizer.cpp: replaced UNIX sleep(1) method with MSVC Sleep(1000) (equivalent, but in ms instead of seconds)
- synthesizer/msynthsizer.h: appended "f" to float constant
- thirdparty/poppler/config.h: set defines for MSVC to preclude the use of inexistent libraries.
- thirdparty/poppler/poppler/poppler-config.h: set defines for MSVC to preclude the use of inexistent libraries. Eliminated #defines for fmin and fmax which where causing problems.
- thirdparty/poppler/poppler/PSOutputDev.cc: added #include <algorithm> for std::min() and std::max(). Note this is required per-C++ standard.
- thirdparty/portmidi/pm_win/pmwinmm.c: undefined UNICODE to use char-based library functions.
- thirdparty/qzip/qzip.cpp: changed or -> ||
- thirdparty/rtf2html/rtf_keyword.h: file format changed from Apple to UNIX, as MSVC does not supported the former.
(NOT error related, just improvement on previous commit; manual/getopt/README.md: added link to source article)
2018-05-01 19:14:44 +02:00
|
|
|
|
#else
|
|
|
|
|
// MSVC does not support VLA. Replace with std::vector. If profiling determines that the
|
|
|
|
|
// heap allocation is slow, an optimization might be used.
|
|
|
|
|
std::vector<qreal> width(n-1);
|
|
|
|
|
std::vector<int> ticksList(n-1);
|
|
|
|
|
#endif
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int minTick = 100000;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n-1; ++i) {
|
|
|
|
|
ChordRest* cr = (*elements)[i];
|
2016-04-07 11:18:34 +02:00
|
|
|
|
ChordRest* ncr = (*elements)[i+1];
|
|
|
|
|
width[i] = cr->shape().minHorizontalDistance(ncr->shape());
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ticksList[i] = cr->ticks().ticks();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
minTick = qMin(ticksList[i], minTick);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// compute stretches
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
|
2016-05-02 13:41:41 +02:00
|
|
|
|
SpringMap springs;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal minimum = 0.0;
|
|
|
|
|
for (int i = 0; i < n-1; ++i) {
|
|
|
|
|
qreal w = width[i];
|
|
|
|
|
int t = ticksList[i];
|
|
|
|
|
qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
|
|
|
|
|
qreal d = w / str;
|
2012-09-05 12:14:58 +02:00
|
|
|
|
|
2016-05-02 13:41:41 +02:00
|
|
|
|
springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
minimum += w;
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// distribute stretch to elements
|
|
|
|
|
//---------------------------------------------------
|
2013-03-29 15:01:21 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
qreal force = sff2(x2 - x1, minimum, springs);
|
|
|
|
|
for (auto i = springs.begin(); i != springs.end(); ++i) {
|
|
|
|
|
qreal stretch = force * i->second.stretch;
|
|
|
|
|
if (stretch < i->second.fix)
|
|
|
|
|
stretch = i->second.fix;
|
|
|
|
|
width[i->second.seg] = stretch;
|
|
|
|
|
}
|
|
|
|
|
qreal x = x1;
|
|
|
|
|
for (int i = 1; i < n-1; ++i) {
|
|
|
|
|
x += width[i-1];
|
|
|
|
|
ChordRest* cr = (*elements)[i];
|
|
|
|
|
qreal dx = x - cr->segment()->pos().x();
|
|
|
|
|
cr->rxpos() += dx;
|
|
|
|
|
}
|
2013-03-29 15:01:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
2018-10-15 16:42:04 +02:00
|
|
|
|
// getNextPage
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
2012-08-02 18:33:43 +02:00
|
|
|
|
|
2018-10-15 16:42:04 +02:00
|
|
|
|
void LayoutContext::getNextPage()
|
2016-01-04 14:48:58 +01:00
|
|
|
|
{
|
2018-10-15 16:42:04 +02:00
|
|
|
|
if (!page || curPage >= score->npages()) {
|
|
|
|
|
page = new Page(score);
|
|
|
|
|
score->pages().push_back(page);
|
|
|
|
|
prevSystem = nullptr;
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2017-01-05 11:23:47 +01:00
|
|
|
|
page = score->pages()[curPage];
|
2018-10-15 16:42:04 +02:00
|
|
|
|
QList<System*>& systems = page->systems();
|
|
|
|
|
const int i = systems.indexOf(curSystem);
|
2018-12-04 16:36:13 +01:00
|
|
|
|
if (i > 0 && systems[i-1]->page() == page) {
|
|
|
|
|
// Current and previous systems are on the current page.
|
|
|
|
|
// Erase only the current and the following systems
|
|
|
|
|
// as the previous one will not participate in layout.
|
2018-10-15 16:42:04 +02:00
|
|
|
|
systems.erase(systems.begin() + i, systems.end());
|
|
|
|
|
}
|
2018-12-04 16:36:13 +01:00
|
|
|
|
else // system is not on the current page (or will be the first one)
|
|
|
|
|
systems.clear();
|
2018-10-15 16:42:04 +02:00
|
|
|
|
prevSystem = systems.empty() ? nullptr : systems.back();
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
2018-10-15 16:42:04 +02:00
|
|
|
|
page->bbox().setRect(0.0, 0.0, score->loWidth(), score->loHeight());
|
2017-01-05 11:23:47 +01:00
|
|
|
|
page->setNo(curPage);
|
2018-10-15 16:42:04 +02:00
|
|
|
|
qreal x = 0.0;
|
|
|
|
|
qreal y = 0.0;
|
|
|
|
|
if (curPage) {
|
|
|
|
|
Page* prevPage = score->pages()[curPage - 1];
|
|
|
|
|
if (MScore::verticalOrientation())
|
|
|
|
|
y = prevPage->pos().y() + page->height() + MScore::verticalPageGap;
|
|
|
|
|
else {
|
|
|
|
|
qreal gap = (curPage + score->pageNumberOffset()) & 1 ? MScore::horizontalPageGapOdd : MScore::horizontalPageGapEven;
|
|
|
|
|
x = prevPage->pos().x() + page->width() + gap;
|
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
}
|
2017-01-05 11:23:47 +01:00
|
|
|
|
++curPage;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
page->setPos(x, y);
|
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// getNextSystem
|
|
|
|
|
//---------------------------------------------------------
|
2012-08-02 18:33:43 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
System* Score::getNextSystem(LayoutContext& lc)
|
|
|
|
|
{
|
2016-02-06 11:41:16 +01:00
|
|
|
|
bool isVBox = lc.curMeasure->isVBox();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
System* system;
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (lc.systemList.empty()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
system = new System(this);
|
2016-03-02 13:20:19 +01:00
|
|
|
|
lc.systemOldMeasure = 0;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
system = lc.systemList.takeFirst();
|
2016-03-02 13:20:19 +01:00
|
|
|
|
lc.systemOldMeasure = system->measures().empty() ? 0 : system->measures().back();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
system->clear(); // remove measures from system
|
|
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
|
_systems.append(system);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (!isVBox) {
|
|
|
|
|
int nstaves = Score::nstaves();
|
|
|
|
|
for (int i = system->staves()->size(); i < nstaves; ++i)
|
|
|
|
|
system->insertStaff(i);
|
|
|
|
|
int dn = system->staves()->size() - nstaves;
|
|
|
|
|
for (int i = 0; i < dn; ++i)
|
|
|
|
|
system->removeStaff(system->staves()->size()-1);
|
|
|
|
|
}
|
|
|
|
|
return system;
|
2012-08-02 18:33:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// createMMRest
|
|
|
|
|
// create a multi measure rest from m to lm (inclusive)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
void Score::createMMRest(Measure* m, Measure* lm, const Fraction& len)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
|
int n = 1;
|
2018-04-13 19:28:38 +02:00
|
|
|
|
if (m != lm) {
|
|
|
|
|
for (Measure* mm = m->nextMeasure(); mm; mm = mm->nextMeasure()) {
|
|
|
|
|
++n;
|
|
|
|
|
mm->setMMRestCount(-1);
|
|
|
|
|
if (mm->mmRest())
|
|
|
|
|
undo(new ChangeMMRest(mm, 0));
|
|
|
|
|
if (mm == lm)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2018-04-13 19:28:38 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
Measure* mmr = m->mmRest();
|
|
|
|
|
if (mmr) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (mmr->ticks() != len) {
|
2017-03-08 13:12:26 +01:00
|
|
|
|
Segment* s = mmr->findSegmentR(SegmentType::EndBarLine, mmr->ticks());
|
2019-01-30 15:13:54 +01:00
|
|
|
|
mmr->setTicks(len);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (s)
|
2019-01-30 15:13:54 +01:00
|
|
|
|
s->setRtick(mmr->ticks());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
mmr = new Measure(this);
|
2019-01-30 15:13:54 +01:00
|
|
|
|
mmr->setTicks(len);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
mmr->setTick(m->tick());
|
2016-02-11 12:16:32 +01:00
|
|
|
|
mmr->setPageBreak(lm->pageBreak());
|
|
|
|
|
mmr->setLineBreak(lm->lineBreak());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
undo(new ChangeMMRest(m, mmr));
|
|
|
|
|
}
|
|
|
|
|
mmr->setMMRestCount(n);
|
|
|
|
|
mmr->setNo(m->no());
|
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
|
Segment* ss = lm->findSegmentR(SegmentType::EndBarLine, lm->ticks());
|
2016-02-11 12:16:32 +01:00
|
|
|
|
if (ss) {
|
2017-03-08 13:12:26 +01:00
|
|
|
|
Segment* ds = mmr->undoGetSegment(SegmentType::EndBarLine, lm->endTick());
|
2016-02-11 12:16:32 +01:00
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
|
Element* e = ss->element(staffIdx * VOICES);
|
|
|
|
|
if (e) {
|
|
|
|
|
if (!ds->element(staffIdx * VOICES)) {
|
|
|
|
|
Element* ee = e->clone();
|
|
|
|
|
ee->setParent(ds);
|
|
|
|
|
undoAddElement(ee);
|
|
|
|
|
}
|
2016-08-03 15:39:30 +02:00
|
|
|
|
else {
|
|
|
|
|
BarLine* bd = toBarLine(ds->element(staffIdx * VOICES));
|
2016-08-11 10:21:28 +02:00
|
|
|
|
BarLine* bs = toBarLine(e);
|
2016-08-03 15:39:30 +02:00
|
|
|
|
if (bd->barLineType() != bs->barLineType()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
bd->undoChangeProperty(Pid::BARLINE_TYPE, QVariant::fromValue(bs->barLineType()));
|
|
|
|
|
bd->undoChangeProperty(Pid::GENERATED, true);
|
2016-08-03 15:39:30 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-11 12:16:32 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
|
|
2018-12-21 10:02:22 +01:00
|
|
|
|
Segment* clefSeg = lm->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef, lm->ticks());
|
|
|
|
|
if (clefSeg) {
|
|
|
|
|
Segment* mmrClefSeg = mmr->undoGetSegment(clefSeg->segmentType(), lm->endTick());
|
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
|
const int track = staff2track(staffIdx);
|
|
|
|
|
Element* e = clefSeg->element(track);
|
|
|
|
|
if (e && e->isClef()) {
|
|
|
|
|
Clef* clef = toClef(e);
|
|
|
|
|
if (!mmrClefSeg->element(track)) {
|
|
|
|
|
Clef* mmrClef = clef->clone();
|
|
|
|
|
mmrClef->setParent(mmrClefSeg);
|
|
|
|
|
undoAddElement(mmrClef);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Clef* mmrClef = toClef(mmrClefSeg->element(track));
|
|
|
|
|
mmrClef->setClefType(clef->clefType());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-11 12:16:32 +01:00
|
|
|
|
mmr->setRepeatStart(m->repeatStart() || lm->repeatStart());
|
|
|
|
|
mmr->setRepeatEnd(m->repeatEnd() || lm->repeatEnd());
|
2019-02-15 07:37:28 +01:00
|
|
|
|
mmr->setSectionBreak(lm->sectionBreak());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
|
|
ElementList oldList = mmr->takeElements();
|
|
|
|
|
ElementList newList = lm->el();
|
|
|
|
|
|
|
|
|
|
for (Element* e : m->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (e->isMarker())
|
2016-02-06 22:03:43 +01:00
|
|
|
|
newList.push_back(e);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
for (Element* e : newList) {
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (Element* ee : oldList) {
|
|
|
|
|
if (ee->type() == e->type()) {
|
|
|
|
|
mmr->add(ee);
|
2016-02-06 22:03:43 +01:00
|
|
|
|
auto i = std::find(oldList.begin(), oldList.end(), ee);
|
|
|
|
|
if (i != oldList.end())
|
|
|
|
|
oldList.erase(i);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-01 20:20:59 +01:00
|
|
|
|
if (!found) {
|
|
|
|
|
Element* e1 = e->clone();
|
|
|
|
|
e1->setParent(mmr);
|
|
|
|
|
undo(new AddElement(e1));
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
for (Element* e : oldList)
|
|
|
|
|
delete e;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Segment* s = mmr->undoGetSegmentR(SegmentType::ChordRest, Fraction(0,1));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
|
if (s->element(track) == 0) {
|
|
|
|
|
Rest* r = new Rest(this);
|
|
|
|
|
r->setDurationType(TDuration::DurationType::V_MEASURE);
|
2019-01-30 15:13:54 +01:00
|
|
|
|
r->setTicks(mmr->ticks());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
r->setTrack(track);
|
|
|
|
|
r->setParent(s);
|
|
|
|
|
undo(new AddElement(r));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// check for clefs
|
|
|
|
|
//
|
2017-03-08 13:12:26 +01:00
|
|
|
|
Segment* cs = lm->findSegmentR(SegmentType::Clef, lm->ticks());
|
|
|
|
|
Segment* ns = mmr->findSegment(SegmentType::Clef, lm->endTick());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cs) {
|
|
|
|
|
if (ns == 0)
|
2017-03-08 13:12:26 +01:00
|
|
|
|
ns = mmr->undoGetSegmentR(SegmentType::Clef, lm->ticks());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
int track = staffIdx * VOICES;
|
2016-10-20 11:32:07 +02:00
|
|
|
|
Clef* clef = toClef(cs->element(track));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (clef) {
|
|
|
|
|
if (ns->element(track) == 0)
|
|
|
|
|
ns->add(clef->clone());
|
|
|
|
|
else {
|
|
|
|
|
//TODO: check if same clef
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ns)
|
|
|
|
|
undo(new RemoveElement(ns));
|
2016-02-11 12:16:32 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
|
|
|
|
// check for time signature
|
|
|
|
|
//
|
2019-01-30 15:13:54 +01:00
|
|
|
|
cs = m->findSegmentR(SegmentType::TimeSig, Fraction(0,1));
|
2017-03-08 13:12:26 +01:00
|
|
|
|
ns = mmr->findSegment(SegmentType::TimeSig, m->tick());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cs) {
|
|
|
|
|
if (ns == 0)
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ns = mmr->undoGetSegmentR(SegmentType::TimeSig, Fraction(0,1));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
int track = staffIdx * VOICES;
|
2016-10-20 11:32:07 +02:00
|
|
|
|
TimeSig* ts = toTimeSig(cs->element(track));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (ts) {
|
2016-10-20 11:32:07 +02:00
|
|
|
|
TimeSig* nts = toTimeSig(ns->element(track));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (!nts) {
|
|
|
|
|
nts = ts->clone();
|
|
|
|
|
nts->setParent(ns);
|
|
|
|
|
undo(new AddElement(nts));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nts->setSig(ts->sig(), ts->timeSigType());
|
|
|
|
|
nts->layout();
|
|
|
|
|
}
|
2012-08-02 18:33:43 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ns)
|
|
|
|
|
undo(new RemoveElement(ns));
|
2015-04-09 13:48:32 +02:00
|
|
|
|
|
2016-05-25 12:10:52 +02:00
|
|
|
|
//
|
|
|
|
|
// check for ambitus
|
|
|
|
|
//
|
2019-01-30 15:13:54 +01:00
|
|
|
|
cs = m->findSegmentR(SegmentType::Ambitus, Fraction(0,1));
|
2017-03-08 13:12:26 +01:00
|
|
|
|
ns = mmr->findSegment(SegmentType::Ambitus, m->tick());
|
2016-05-25 12:10:52 +02:00
|
|
|
|
if (cs) {
|
|
|
|
|
if (ns == 0)
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ns = mmr->undoGetSegmentR(SegmentType::Ambitus, Fraction(0,1));
|
2016-05-25 12:10:52 +02:00
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
|
Ambitus* a = toAmbitus(cs->element(track));
|
|
|
|
|
if (a) {
|
|
|
|
|
Ambitus* na = toAmbitus(ns->element(track));
|
|
|
|
|
if (!na) {
|
|
|
|
|
na = a->clone();
|
|
|
|
|
na->setParent(ns);
|
|
|
|
|
undo(new AddElement(na));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
na->initFrom(a);
|
|
|
|
|
na->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ns)
|
|
|
|
|
undo(new RemoveElement(ns));
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
|
|
|
|
// check for key signature
|
|
|
|
|
//
|
2019-01-30 15:13:54 +01:00
|
|
|
|
cs = m->findSegmentR(SegmentType::KeySig, Fraction(0,1));
|
|
|
|
|
ns = mmr->findSegmentR(SegmentType::KeySig, Fraction(0,1));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cs) {
|
|
|
|
|
if (ns == 0)
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ns = mmr->undoGetSegmentR(SegmentType::KeySig, Fraction(0,1));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
int track = staffIdx * VOICES;
|
2016-10-20 11:32:07 +02:00
|
|
|
|
KeySig* ts = toKeySig(cs->element(track));
|
|
|
|
|
KeySig* nts = toKeySig(ns->element(track));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (ts) {
|
|
|
|
|
if (!nts) {
|
|
|
|
|
KeySig* nks = ts->clone();
|
|
|
|
|
nks->setParent(ns);
|
|
|
|
|
undo(new AddElement(nks));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
if (!(nts->keySigEvent() == ts->keySigEvent())) {
|
2019-02-13 01:05:42 +01:00
|
|
|
|
bool addKey = ts->isChange();
|
|
|
|
|
undo(new ChangeKeySig(nts, ts->keySigEvent(), nts->showCourtesy(), addKey));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-06 22:03:43 +01:00
|
|
|
|
else if (ns && ns->empty())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
undo(new RemoveElement(ns));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
|
|
|
|
// check for rehearsal mark etc.
|
|
|
|
|
//
|
2019-01-30 15:13:54 +01:00
|
|
|
|
cs = m->findSegmentR(SegmentType::ChordRest, Fraction(0,1));
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cs) {
|
|
|
|
|
for (Element* e : cs->annotations()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
bool found = false;
|
|
|
|
|
for (Element* ee : s->annotations()) {
|
|
|
|
|
if (ee->type() == e->type() && ee->track() == e->track()) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
Element* ne = e->linkedClone();
|
|
|
|
|
ne->setParent(s);
|
|
|
|
|
undo(new AddElement(ne));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-11 12:16:32 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (Element* ee : cs->annotations()) {
|
|
|
|
|
if (ee->type() == e->type() && ee->track() == e->track()) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found)
|
|
|
|
|
undo(new RemoveElement(e));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MeasureBase* nm = _showVBox ? lm->next() : lm->nextMeasure();
|
|
|
|
|
mmr->setNext(nm);
|
|
|
|
|
mmr->setPrev(m->prev());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// validMMRestMeasure
|
|
|
|
|
// return true if this might be a measure in a
|
|
|
|
|
// multi measure rest
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
static bool validMMRestMeasure(Measure* m)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (m->irregular())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (s->isChordRestType()) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
bool restFound = false;
|
2016-12-12 14:55:35 +01:00
|
|
|
|
int tracks = m->score()->ntracks();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int track = 0; track < tracks; ++track) {
|
|
|
|
|
if ((track % VOICES) == 0 && !m->score()->staff(track/VOICES)->show()) {
|
|
|
|
|
track += VOICES-1;
|
|
|
|
|
continue;
|
2015-06-29 20:07:26 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (s->element(track)) {
|
2016-12-12 14:55:35 +01:00
|
|
|
|
if (!s->element(track)->isRest())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return false;
|
|
|
|
|
restFound = true;
|
2014-05-15 13:42:03 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-17 13:25:23 +01:00
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
|
if (e->isFermata())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (restFound)
|
|
|
|
|
++n;
|
|
|
|
|
// measure is not empty if there is more than one rest
|
|
|
|
|
if (n > 1)
|
|
|
|
|
return false;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// breakMultiMeasureRest
|
|
|
|
|
// return true if this measure should start a new
|
|
|
|
|
// multi measure rest
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
static bool breakMultiMeasureRest(Measure* m)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (m->breakMultiMeasureRest())
|
|
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
|
if (m->repeatStart()
|
|
|
|
|
|| (m->prevMeasure() && m->prevMeasure()->repeatEnd())
|
2018-11-24 21:56:53 +01:00
|
|
|
|
|| (m->isIrregular())
|
|
|
|
|
|| (m->prevMeasure() && m->prevMeasure()->isIrregular())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|| (m->prevMeasure() && (m->prevMeasure()->sectionBreak())))
|
|
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
auto sl = m->score()->spannerMap().findOverlapping(m->tick().ticks(), m->endTick().ticks());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (auto i : sl) {
|
|
|
|
|
Spanner* s = i.value;
|
2019-02-22 20:21:56 +01:00
|
|
|
|
// break for first measure of volta and first measure *after* volta
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (s->isVolta() && (s->tick() == m->tick() || s->tick2() == m->tick()))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2015-12-18 01:34:32 +01:00
|
|
|
|
}
|
2014-10-17 03:43:54 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// break for marker in this measure
|
|
|
|
|
for (Element* e : m->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (e->isMarker()) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Marker* mark = toMarker(e);
|
2017-01-16 20:51:12 +01:00
|
|
|
|
if (!(mark->align() & Align::RIGHT))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// break for marker & jump in previous measure
|
|
|
|
|
Measure* pm = m->prevMeasure();
|
|
|
|
|
if (pm) {
|
|
|
|
|
for (Element* e : pm->el()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (e->isJump())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2016-02-06 11:41:16 +01:00
|
|
|
|
else if (e->isMarker()) {
|
2016-10-20 11:32:07 +02:00
|
|
|
|
Marker* mark = toMarker(e);
|
2017-01-16 20:51:12 +01:00
|
|
|
|
if (mark->align() & Align::RIGHT)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-12-02 07:34:44 +01:00
|
|
|
|
if (!e->visible())
|
|
|
|
|
continue;
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (e->isRehearsalMark() ||
|
|
|
|
|
e->isTempoText() ||
|
2018-10-18 11:53:01 +02:00
|
|
|
|
((e->isHarmony() || e->isStaffText() || e->isSystemText()) && (e->systemFlag() || m->score()->staff(e->staffIdx())->show())))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
|
|
|
|
|
if (!m->score()->staff(staffIdx)->show())
|
|
|
|
|
continue;
|
|
|
|
|
Element* e = s->element(staffIdx * VOICES);
|
|
|
|
|
if (!e || e->generated())
|
|
|
|
|
continue;
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (s->isStartRepeatBarLineType())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && m->tick().isNotZero())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (s->isClefType()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (s->tick() != m->endTick() && m->tick().isNotZero())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (pm) {
|
2017-03-08 13:12:26 +01:00
|
|
|
|
Segment* s = pm->findSegmentR(SegmentType::EndBarLine, pm->ticks());
|
2016-02-11 12:16:32 +01:00
|
|
|
|
if (s) {
|
|
|
|
|
for (int staffIdx = 0; staffIdx < s->score()->nstaves(); ++staffIdx) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
BarLine* bl = toBarLine(s->element(staffIdx * VOICES));
|
2016-02-11 12:16:32 +01:00
|
|
|
|
if (bl) {
|
|
|
|
|
BarLineType t = bl->barLineType();
|
2016-08-03 15:39:30 +02:00
|
|
|
|
if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated())
|
2016-02-11 12:16:32 +01:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-08 13:12:26 +01:00
|
|
|
|
if (pm->findSegment(SegmentType::Clef, m->tick()))
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return true;
|
2014-10-17 03:43:54 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return false;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-01 09:28:40 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// adjustMeasureNo
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
int LayoutContext::adjustMeasureNo(MeasureBase* m)
|
|
|
|
|
{
|
|
|
|
|
measureNo += m->noOffset();
|
|
|
|
|
m->setNo(measureNo);
|
2018-11-27 22:49:18 +01:00
|
|
|
|
if (!m->irregular()) // don’t count measure
|
2016-04-01 09:28:40 +02:00
|
|
|
|
++measureNo;
|
|
|
|
|
if (m->sectionBreak())
|
|
|
|
|
measureNo = 0;
|
|
|
|
|
return measureNo;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-05-19 13:15:34 +02:00
|
|
|
|
// createBeams
|
|
|
|
|
// helper function
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
|
void Score::createBeams(Measure* measure)
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2018-03-27 15:36:00 +02:00
|
|
|
|
bool crossMeasure = styleB(Sid::crossMeasureValues);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (int track = 0; track < ntracks(); ++track) {
|
|
|
|
|
Staff* stf = staff(track2staff(track));
|
2013-09-14 02:30:40 +02:00
|
|
|
|
|
2018-11-27 22:49:18 +01:00
|
|
|
|
// don’t compute beams for invisible staffs and tablature without stems
|
2016-12-13 13:16:17 +01:00
|
|
|
|
if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->slashStyle()))
|
2012-05-26 14:26:10 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
ChordRest* a1 = 0; // start of (potential) beam
|
2018-12-22 14:55:01 +01:00
|
|
|
|
bool firstCR = true;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
Beam* beam = 0; // current beam
|
|
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
|
|
|
|
ChordRest* prev = 0;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
bool checkBeats = false;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction stretch = Fraction(1,1);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
QHash<int, TDuration> beatSubdivision;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
|
|
|
|
|
// if this measure is simple meter (actually X/4),
|
|
|
|
|
// then perform a prepass to determine the subdivision of each beat
|
|
|
|
|
|
|
|
|
|
beatSubdivision.clear();
|
|
|
|
|
TimeSig* ts = stf->timeSig(measure->tick());
|
|
|
|
|
checkBeats = false;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
stretch = ts ? ts->stretch() : Fraction(1,1);
|
2016-02-11 18:20:16 +01:00
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
|
const SegmentType st = SegmentType::ChordRest;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
if (ts && ts->denominator() == 4) {
|
|
|
|
|
checkBeats = true;
|
|
|
|
|
for (Segment* s = measure->first(st); s; s = s->next(st)) {
|
2016-10-20 11:32:07 +02:00
|
|
|
|
ChordRest* mcr = toChordRest(s->element(track));
|
2016-02-11 18:20:16 +01:00
|
|
|
|
if (mcr == 0)
|
|
|
|
|
continue;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
int beat = (mcr->rtick() * stretch).ticks() / MScore::division;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
if (beatSubdivision.contains(beat))
|
|
|
|
|
beatSubdivision[beat] = qMin(beatSubdivision[beat], mcr->durationType());
|
|
|
|
|
else
|
|
|
|
|
beatSubdivision[beat] = mcr->durationType();
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (Segment* segment = measure->first(st); segment; segment = segment->next(st)) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
ChordRest* cr = segment->cr(track);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cr == 0)
|
|
|
|
|
continue;
|
2018-12-22 14:55:01 +01:00
|
|
|
|
|
|
|
|
|
if (firstCR) {
|
|
|
|
|
firstCR = false;
|
|
|
|
|
// Handle cross-measure beams
|
|
|
|
|
Beam::Mode mode = cr->beamMode();
|
|
|
|
|
if (mode == Beam::Mode::MID || mode == Beam::Mode::END) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ChordRest* prevCR = findCR(measure->tick() - Fraction::fromTicks(1), track);
|
2019-01-04 09:40:35 +01:00
|
|
|
|
if (prevCR) {
|
|
|
|
|
const Measure* pm = prevCR->measure();
|
|
|
|
|
if (!beamNoContinue(prevCR->beamMode())
|
|
|
|
|
&& !pm->lineBreak() && !pm->pageBreak() && !pm->sectionBreak()) {
|
|
|
|
|
beam = prevCR->beam();
|
2019-02-28 10:13:10 +01:00
|
|
|
|
//a1 = beam ? beam->elements().front() : prevCR;
|
|
|
|
|
a1 = beam ? nullptr : prevCR; // when beam is found, a1 is no longer required.
|
2019-01-04 09:40:35 +01:00
|
|
|
|
}
|
2018-12-22 14:55:01 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-19 13:03:25 +02:00
|
|
|
|
#if 0
|
2016-08-17 12:52:35 +02:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (l)
|
|
|
|
|
l->layout();
|
|
|
|
|
}
|
2018-07-19 13:03:25 +02:00
|
|
|
|
#endif
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// handle grace notes and cross-measure beaming
|
2018-12-22 14:55:01 +01:00
|
|
|
|
// (tied chords?)
|
2016-02-06 11:41:16 +01:00
|
|
|
|
if (cr->isChord()) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Chord* chord = toChord(cr);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
beamGraceNotes(chord, false); // grace before
|
|
|
|
|
beamGraceNotes(chord, true); // grace after
|
|
|
|
|
// set up for cross-measure values as soon as possible
|
|
|
|
|
// to have all computations (stems, hooks, ...) consistent with it
|
|
|
|
|
if (!chord->isGrace())
|
|
|
|
|
chord->crossMeasureSetup(crossMeasure);
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// get defaults from time signature properties
|
2016-02-11 18:20:16 +01:00
|
|
|
|
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
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (checkBeats && cr->rtick().isNotZero()) {
|
|
|
|
|
Fraction tick = cr->rtick() * stretch;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
// check if on the beat
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if ((tick.ticks() % MScore::division) == 0) {
|
|
|
|
|
int beat = tick.ticks() / MScore::division;
|
2016-02-11 18:20:16 +01:00
|
|
|
|
// 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
|
|
|
|
|
TDuration saveDuration = cr->actualDurationType();
|
|
|
|
|
TDuration saveCMDuration = cr->crossMeasureDurationType();
|
|
|
|
|
CrossMeasure saveCrossMeasVal = cr->crossMeasure();
|
|
|
|
|
cr->setDurationType(minDuration);
|
|
|
|
|
bm = Groups::endBeam(cr, prev);
|
|
|
|
|
cr->setDurationType(saveDuration);
|
|
|
|
|
cr->setCrossMeasure(saveCrossMeasVal);
|
|
|
|
|
cr->setCrossMeasureDurationType(saveCMDuration);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
prev = cr;
|
|
|
|
|
|
|
|
|
|
// if chord has hooks and is 2nd element of a cross-measure value
|
|
|
|
|
// set beam mode to NONE (do not combine with following chord beam/hook, if any)
|
2016-02-11 18:20:16 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (cr->durationType().hooks() > 0 && cr->crossMeasure() == CrossMeasure::SECOND)
|
|
|
|
|
bm = Beam::Mode::NONE;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) {
|
2018-12-22 14:55:01 +01:00
|
|
|
|
bool removeBeam = true;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (beam) {
|
|
|
|
|
beam->layout1();
|
2018-12-22 14:55:01 +01:00
|
|
|
|
removeBeam = (beam->elements().size() <= 1);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
beam = 0;
|
|
|
|
|
}
|
|
|
|
|
if (a1) {
|
2018-12-22 14:55:01 +01:00
|
|
|
|
if (removeBeam)
|
|
|
|
|
a1->removeDeleteBeam(false);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
a1 = 0;
|
|
|
|
|
}
|
|
|
|
|
cr->removeDeleteBeam(false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (beam) {
|
|
|
|
|
bool beamEnd = (bm == Beam::Mode::BEGIN);
|
|
|
|
|
if (!beamEnd) {
|
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
|
beam->add(cr);
|
|
|
|
|
cr = 0;
|
|
|
|
|
beamEnd = (bm == Beam::Mode::END);
|
|
|
|
|
}
|
|
|
|
|
if (beamEnd) {
|
|
|
|
|
beam->layout1();
|
|
|
|
|
beam = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!cr)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (a1 == 0)
|
|
|
|
|
a1 = cr;
|
|
|
|
|
else {
|
|
|
|
|
if (!beamModeMid(bm)
|
|
|
|
|
&&
|
|
|
|
|
(bm == Beam::Mode::BEGIN
|
|
|
|
|
|| (a1->segment()->segmentType() != cr->segment()->segmentType())
|
|
|
|
|
|| (a1->tick() + a1->actualTicks() < cr->tick())
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
a1->removeDeleteBeam(false);
|
|
|
|
|
a1 = cr;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
beam = a1->beam();
|
|
|
|
|
if (beam == 0 || beam->elements().front() != a1) {
|
|
|
|
|
beam = new Beam(this);
|
|
|
|
|
beam->setGenerated(true);
|
|
|
|
|
beam->setTrack(track);
|
|
|
|
|
a1->removeDeleteBeam(true);
|
|
|
|
|
beam->add(a1);
|
|
|
|
|
}
|
|
|
|
|
cr->removeDeleteBeam(true);
|
|
|
|
|
beam->add(cr);
|
|
|
|
|
a1 = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-10 23:22:00 +01:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (beam)
|
|
|
|
|
beam->layout1();
|
2019-02-28 10:13:10 +01:00
|
|
|
|
else if (a1) {
|
|
|
|
|
// if a1 is the last chord/rest in current measure
|
|
|
|
|
if (a1->segment()->next(SegmentType::ChordRest) == nullptr) {
|
|
|
|
|
const auto b = a1->beam();
|
|
|
|
|
// if the second chord/rest in a1's beam (it must be in next measure) has forced MID beam mode
|
|
|
|
|
if (b && b->elements().startsWith(a1) && b->elements().size()>=2 && b->elements()[1]->beamMode() == Beam::Mode::MID)
|
|
|
|
|
// do not delete the origin beam
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
a1->removeDeleteBeam(false);
|
2019-02-28 10:13:10 +01:00
|
|
|
|
}
|
2015-03-10 23:22:00 +01:00
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-22 14:55:01 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// breakCrossMeasureBeams
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void breakCrossMeasureBeams(Measure* measure)
|
|
|
|
|
{
|
|
|
|
|
MeasureBase* mbNext = measure->next();
|
|
|
|
|
if (!mbNext || !mbNext->isMeasure())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Measure* next = toMeasure(mbNext);
|
|
|
|
|
Score* score = measure->score();
|
|
|
|
|
const int ntracks = score->ntracks();
|
|
|
|
|
Segment* fstSeg = next->first(SegmentType::ChordRest);
|
|
|
|
|
|
|
|
|
|
for (int track = 0; track < ntracks; ++track) {
|
|
|
|
|
Staff* stf = score->staff(track2staff(track));
|
|
|
|
|
|
|
|
|
|
// don’t compute beams for invisible staffs and tablature without stems
|
|
|
|
|
if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->slashStyle()))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Element* e = fstSeg->element(track);
|
|
|
|
|
if (!e || !e->isChordRest())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ChordRest* cr = toChordRest(e);
|
|
|
|
|
Beam* beam = cr->beam();
|
|
|
|
|
if (!beam || beam->elements().front()->measure() == next) // no beam or not cross-measure beam
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
std::vector<ChordRest*> mElements;
|
|
|
|
|
std::vector<ChordRest*> nextElements;
|
|
|
|
|
|
|
|
|
|
for (ChordRest* beamCR : beam->elements()) {
|
|
|
|
|
if (beamCR->measure() == measure)
|
|
|
|
|
mElements.push_back(beamCR);
|
|
|
|
|
else
|
|
|
|
|
nextElements.push_back(beamCR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mElements.size() == 1)
|
|
|
|
|
mElements[0]->removeDeleteBeam(false);
|
|
|
|
|
|
|
|
|
|
Beam* newBeam = nullptr;
|
|
|
|
|
if (nextElements.size() > 1) {
|
|
|
|
|
newBeam = new Beam(score);
|
|
|
|
|
newBeam->setGenerated(true);
|
|
|
|
|
newBeam->setTrack(track);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool nextBeamed = bool(newBeam);
|
|
|
|
|
for (ChordRest* nextCR : nextElements) {
|
|
|
|
|
nextCR->removeDeleteBeam(nextBeamed);
|
|
|
|
|
if (newBeam)
|
|
|
|
|
newBeam->add(nextCR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newBeam)
|
|
|
|
|
newBeam->layout1();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 10:49:54 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutDrumsetChord
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2018-11-08 15:45:14 +01:00
|
|
|
|
void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium)
|
2017-12-20 10:49:54 +01:00
|
|
|
|
{
|
|
|
|
|
for (Note* note : c->notes()) {
|
|
|
|
|
int pitch = note->pitch();
|
|
|
|
|
if (!drumset->isValid(pitch)) {
|
|
|
|
|
// qDebug("unmapped drum note %d", pitch);
|
|
|
|
|
}
|
|
|
|
|
else if (!note->fixed()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
note->undoChangeProperty(Pid::HEAD_GROUP, int(drumset->noteHead(pitch)));
|
2017-12-20 10:49:54 +01:00
|
|
|
|
int line = drumset->line(pitch);
|
|
|
|
|
note->setLine(line);
|
|
|
|
|
|
|
|
|
|
int off = st->stepOffset();
|
|
|
|
|
qreal ld = st->lineDistance().val();
|
|
|
|
|
note->rypos() = (line + off * 2.0) * spatium * .5 * ld;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-19 14:36:02 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// connectTremolo
|
|
|
|
|
// Connect two-notes tremolo and update duration types
|
|
|
|
|
// for the involved chords.
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void connectTremolo(Measure* m)
|
|
|
|
|
{
|
|
|
|
|
const int ntracks = m->score()->ntracks();
|
|
|
|
|
constexpr SegmentType st = SegmentType::ChordRest;
|
|
|
|
|
for (Segment* s = m->first(st); s; s = s->next(st)) {
|
|
|
|
|
for (int i = 0; i < ntracks; ++i) {
|
|
|
|
|
Element* e = s->element(i);
|
|
|
|
|
if (!e || !e->isChord())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Chord* c = toChord(e);
|
|
|
|
|
Tremolo* tremolo = c->tremolo();
|
|
|
|
|
if (tremolo && tremolo->twoNotes()) {
|
|
|
|
|
// Ensure correct duration type for chord
|
|
|
|
|
c->setDurationType(tremolo->durationType());
|
|
|
|
|
|
|
|
|
|
// If it is the first tremolo's chord, find the second
|
|
|
|
|
// chord for tremolo, if needed.
|
|
|
|
|
if (!tremolo->chord1())
|
|
|
|
|
tremolo->setChords(c, tremolo->chord2());
|
|
|
|
|
else if (tremolo->chord1() != c || tremolo->chord2())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (Segment* ls = s->next(st); ls; ls = ls->next(st)) {
|
|
|
|
|
if (Element* element = ls->element(i)) {
|
2019-01-19 10:22:45 +01:00
|
|
|
|
if (!element->isChord()) {
|
2018-12-19 14:36:02 +01:00
|
|
|
|
qDebug("cannot connect tremolo");
|
2019-01-19 10:22:45 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-12-19 14:36:02 +01:00
|
|
|
|
Chord* nc = toChord(element);
|
|
|
|
|
tremolo->setChords(c, nc);
|
|
|
|
|
nc->setTremolo(tremolo);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// getNextMeasure
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Score::getNextMeasure(LayoutContext& lc)
|
|
|
|
|
{
|
|
|
|
|
lc.prevMeasure = lc.curMeasure;
|
|
|
|
|
lc.curMeasure = lc.nextMeasure;
|
|
|
|
|
if (!lc.curMeasure)
|
|
|
|
|
lc.nextMeasure = _showVBox ? first() : firstMeasure();
|
|
|
|
|
else
|
|
|
|
|
lc.nextMeasure = _showVBox ? lc.curMeasure->next() : lc.curMeasure->nextMeasure();
|
|
|
|
|
if (!lc.curMeasure)
|
|
|
|
|
return;
|
|
|
|
|
|
2016-10-20 11:32:07 +02:00
|
|
|
|
int mno = lc.adjustMeasureNo(lc.curMeasure);
|
2016-05-19 13:15:34 +02:00
|
|
|
|
|
2018-08-16 16:42:55 +02:00
|
|
|
|
if (lc.curMeasure->isMeasure()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
if (score()->styleB(Sid::createMultiMeasureRests)) {
|
2016-10-20 11:32:07 +02:00
|
|
|
|
Measure* m = toMeasure(lc.curMeasure);
|
|
|
|
|
Measure* nm = m;
|
|
|
|
|
Measure* lm = nm;
|
|
|
|
|
int n = 0;
|
|
|
|
|
Fraction len;
|
2016-05-19 13:15:34 +02:00
|
|
|
|
|
2016-10-20 11:32:07 +02:00
|
|
|
|
while (validMMRestMeasure(nm)) {
|
|
|
|
|
MeasureBase* mb = _showVBox ? nm->next() : nm->nextMeasure();
|
|
|
|
|
if (breakMultiMeasureRest(nm) && n)
|
|
|
|
|
break;
|
2019-02-14 23:37:06 +01:00
|
|
|
|
if (nm != m)
|
|
|
|
|
lc.adjustMeasureNo(nm);
|
2016-10-20 11:32:07 +02:00
|
|
|
|
++n;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
len += nm->ticks();
|
2016-10-20 11:32:07 +02:00
|
|
|
|
lm = nm;
|
|
|
|
|
if (!(mb && mb->isMeasure()))
|
|
|
|
|
break;
|
|
|
|
|
nm = toMeasure(mb);
|
|
|
|
|
}
|
2018-03-27 15:36:00 +02:00
|
|
|
|
if (n >= styleI(Sid::minEmptyMeasures)) {
|
2016-10-20 11:32:07 +02:00
|
|
|
|
createMMRest(m, lm, len);
|
|
|
|
|
lc.curMeasure = m->mmRest();
|
|
|
|
|
lc.nextMeasure = _showVBox ? lm->next() : lm->nextMeasure();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (m->mmRest())
|
|
|
|
|
undo(new ChangeMMRest(m, 0));
|
|
|
|
|
m->setMMRestCount(0);
|
|
|
|
|
lc.measureNo = mno;
|
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
2016-10-20 11:32:07 +02:00
|
|
|
|
else if (toMeasure(lc.curMeasure)->isMMRest()) {
|
|
|
|
|
qDebug("mmrest: no %d += %d", lc.measureNo, toMeasure(lc.curMeasure)->mmRestCount());
|
|
|
|
|
lc.measureNo += toMeasure(lc.curMeasure)->mmRestCount() - 1;
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!lc.curMeasure->isMeasure()) {
|
|
|
|
|
lc.curMeasure->setTick(lc.tick);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------
|
|
|
|
|
// process one measure
|
|
|
|
|
//-----------------------------------------
|
|
|
|
|
|
|
|
|
|
Measure* measure = toMeasure(lc.curMeasure);
|
|
|
|
|
measure->moveTicks(lc.tick - measure->tick());
|
|
|
|
|
|
2018-12-19 14:36:02 +01:00
|
|
|
|
connectTremolo(measure);
|
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
|
//
|
|
|
|
|
// calculate accidentals and note lines,
|
|
|
|
|
// create stem and set stem direction
|
|
|
|
|
//
|
|
|
|
|
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
|
2018-11-08 15:45:14 +01:00
|
|
|
|
const Staff* staff = Score::staff(staffIdx);
|
2016-12-23 12:05:18 +01:00
|
|
|
|
const Drumset* drumset = staff->part()->instrument()->useDrumset() ? staff->part()->instrument()->drumset() : 0;
|
2016-05-19 13:15:34 +02:00
|
|
|
|
AccidentalState as; // list of already set accidentals for this measure
|
2017-07-06 09:59:29 +02:00
|
|
|
|
as.init(staff->keySigEvent(measure->tick()), staff->clef(measure->tick()));
|
2016-05-19 13:15:34 +02:00
|
|
|
|
|
|
|
|
|
for (Segment& segment : measure->segments()) {
|
|
|
|
|
if (segment.isKeySigType()) {
|
|
|
|
|
KeySig* ks = toKeySig(segment.element(staffIdx * VOICES));
|
|
|
|
|
if (!ks)
|
|
|
|
|
continue;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction tick = segment.tick();
|
2017-02-13 12:42:54 +01:00
|
|
|
|
as.init(staff->keySigEvent(tick), staff->clef(tick));
|
2016-05-19 13:15:34 +02:00
|
|
|
|
ks->layout();
|
|
|
|
|
}
|
|
|
|
|
else if (segment.isChordRestType()) {
|
2018-11-08 15:45:14 +01:00
|
|
|
|
const StaffType* st = staff->staffType(segment.tick());
|
2017-12-20 10:49:54 +01:00
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
|
int endTrack = track + VOICES;
|
2016-12-23 12:05:18 +01:00
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
|
for (int t = track; t < endTrack; ++t) {
|
|
|
|
|
ChordRest* cr = segment.cr(t);
|
2017-07-06 09:59:29 +02:00
|
|
|
|
if (!cr)
|
|
|
|
|
continue;
|
|
|
|
|
qreal m = staff->mag(segment.tick());
|
|
|
|
|
if (cr->small())
|
2018-03-27 15:36:00 +02:00
|
|
|
|
m *= score()->styleD(Sid::smallNoteMag);
|
2017-07-06 09:59:29 +02:00
|
|
|
|
|
|
|
|
|
if (cr->isChord()) {
|
|
|
|
|
Chord* chord = toChord(cr);
|
|
|
|
|
chord->cmdUpdateNotes(&as);
|
|
|
|
|
for (Chord* c : chord->graceNotes()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
c->setMag(m * score()->styleD(Sid::graceNoteMag));
|
2017-07-06 09:59:29 +02:00
|
|
|
|
c->computeUp();
|
|
|
|
|
if (c->stemDirection() != Direction::AUTO)
|
|
|
|
|
c->setUp(c->stemDirection() == Direction::UP);
|
|
|
|
|
else
|
|
|
|
|
c->setUp(!(t % 2));
|
2017-12-20 10:49:54 +01:00
|
|
|
|
if (drumset)
|
|
|
|
|
layoutDrumsetChord(c, drumset, st, spatium());
|
2017-07-06 09:59:29 +02:00
|
|
|
|
c->layoutStem1();
|
|
|
|
|
}
|
2017-12-20 10:49:54 +01:00
|
|
|
|
if (drumset)
|
|
|
|
|
layoutDrumsetChord(chord, drumset, st, spatium());
|
2017-07-06 09:59:29 +02:00
|
|
|
|
chord->computeUp();
|
|
|
|
|
chord->layoutStem1(); // create stems needed to calculate spacing
|
|
|
|
|
// stem direction can change later during beam processing
|
2016-12-23 12:05:18 +01:00
|
|
|
|
}
|
2017-07-06 09:59:29 +02:00
|
|
|
|
cr->setMag(m);
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
else if (segment.isClefType()) {
|
|
|
|
|
Element* e = segment.element(staffIdx * VOICES);
|
|
|
|
|
if (e) {
|
|
|
|
|
toClef(e)->setSmall(true);
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 05:54:53 +02:00
|
|
|
|
else if (segment.isType(SegmentType::TimeSig | SegmentType::Ambitus | SegmentType::HeaderClef)) {
|
2016-05-19 13:15:34 +02:00
|
|
|
|
Element* e = segment.element(staffIdx * VOICES);
|
|
|
|
|
if (e)
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createBeams(measure);
|
|
|
|
|
|
|
|
|
|
for (int staffIdx = 0; staffIdx < score()->nstaves(); ++staffIdx) {
|
|
|
|
|
for (Segment& segment : measure->segments()) {
|
2016-11-16 13:32:22 +01:00
|
|
|
|
if (segment.isChordRestType()) {
|
2016-05-19 13:15:34 +02:00
|
|
|
|
layoutChords1(&segment, staffIdx);
|
2016-11-16 13:32:22 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = segment.cr(staffIdx * VOICES + voice);
|
2018-07-19 13:03:25 +02:00
|
|
|
|
if (cr) {
|
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l)
|
|
|
|
|
l->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-16 13:32:22 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-05 15:17:49 +02:00
|
|
|
|
measure->computeTicks();
|
2019-01-10 12:58:19 +01:00
|
|
|
|
|
2019-02-04 10:21:11 +01:00
|
|
|
|
if (isMaster()) {
|
|
|
|
|
// Reset tempo to set correct time stretch for fermata.
|
2019-01-30 15:13:54 +01:00
|
|
|
|
const Fraction& startTick = measure->tick();
|
2019-02-04 10:21:11 +01:00
|
|
|
|
resetTempoRange(startTick, measure->endTick());
|
|
|
|
|
|
2019-02-06 08:26:02 +01:00
|
|
|
|
// Implement section break rest
|
|
|
|
|
for (MeasureBase* mb = measure->prev(); mb && mb->endTick() == startTick; mb = mb->prev()) {
|
|
|
|
|
if (mb->pause())
|
|
|
|
|
setPause(startTick, mb->pause());
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-04 10:21:11 +01:00
|
|
|
|
// Add pauses from the end of the previous measure (at measure->tick()):
|
|
|
|
|
for (Segment* s = measure->first()->prev1(); s && s->tick() == startTick; s = s->prev1()) {
|
|
|
|
|
if (!s->isBreathType())
|
|
|
|
|
continue;
|
|
|
|
|
qreal length = 0.0;
|
|
|
|
|
for (Element* e : s->elist()) {
|
|
|
|
|
if (e && e->isBreath())
|
|
|
|
|
length = qMax(length, toBreath(e)->pause());
|
|
|
|
|
}
|
|
|
|
|
if (length != 0.0)
|
|
|
|
|
setPause(startTick, length);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-10 12:58:19 +01:00
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
|
for (Segment& segment : measure->segments()) {
|
|
|
|
|
if (segment.isBreathType()) {
|
|
|
|
|
qreal length = 0.0;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction tick = segment.tick();
|
2016-05-19 13:15:34 +02:00
|
|
|
|
// find longest pause
|
|
|
|
|
for (int i = 0, n = ntracks(); i < n; ++i) {
|
2016-06-02 11:02:18 +02:00
|
|
|
|
Element* e = segment.element(i);
|
|
|
|
|
if (e && e->isBreath()) {
|
|
|
|
|
Breath* b = toBreath(e);
|
2016-06-01 19:12:27 +02:00
|
|
|
|
b->layout();
|
2016-05-19 13:15:34 +02:00
|
|
|
|
length = qMax(length, b->pause());
|
2016-06-01 19:12:27 +02:00
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
if (length != 0.0)
|
|
|
|
|
setPause(tick, length);
|
|
|
|
|
}
|
|
|
|
|
else if (segment.isTimeSigType()) {
|
|
|
|
|
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
|
|
|
|
|
TimeSig* ts = toTimeSig(segment.element(staffIdx * VOICES));
|
|
|
|
|
if (ts)
|
|
|
|
|
staff(staffIdx)->addTimeSig(ts);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-28 09:45:41 +01:00
|
|
|
|
else if (segment.isChordRestType()) {
|
|
|
|
|
for (Element* e : segment.annotations()) {
|
|
|
|
|
if (e->isSymbol())
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
if (!isMaster())
|
|
|
|
|
continue;
|
2018-09-11 16:26:27 +02:00
|
|
|
|
#if 0 // ws
|
2016-05-19 13:15:34 +02:00
|
|
|
|
for (Element* e : segment.annotations()) {
|
2018-01-16 13:38:17 +01:00
|
|
|
|
if (!(e->isTempoText()
|
|
|
|
|
|| e->isDynamic()
|
|
|
|
|
|| e->isFermata()
|
|
|
|
|
|| e->isRehearsalMark()
|
|
|
|
|
|| e->isFretDiagram()
|
2018-07-12 10:15:56 +02:00
|
|
|
|
|| e->isHarmony()
|
2018-09-11 16:26:27 +02:00
|
|
|
|
|| e->isStaffText()
|
2018-07-12 10:15:56 +02:00
|
|
|
|
|| e->isFiguredBass())) {
|
2018-09-11 16:26:27 +02:00
|
|
|
|
e->layout(); // system text ?
|
2018-07-12 10:15:56 +02:00
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
#endif
|
2016-06-24 11:58:22 +02:00
|
|
|
|
// TODO, this is not going to work, we just cleaned the tempomap
|
|
|
|
|
// it breaks the test midi/testBaroqueOrnaments.mscx where first note has stretch 2
|
|
|
|
|
// Also see fixTicks
|
2016-05-19 13:15:34 +02:00
|
|
|
|
qreal stretch = 0.0;
|
2018-01-17 13:25:23 +01:00
|
|
|
|
for (Element* e : segment.annotations()) {
|
|
|
|
|
if (e->isFermata())
|
|
|
|
|
stretch = qMax(stretch, toFermata(e)->timeStretch());
|
2018-12-22 10:29:41 +01:00
|
|
|
|
else if (e->isTempoText()) {
|
2019-01-10 12:58:19 +01:00
|
|
|
|
if (isMaster()) {
|
2018-12-22 10:29:41 +01:00
|
|
|
|
TempoText* tt = toTempoText(e);
|
|
|
|
|
setTempo(tt->segment(), tt->tempo());
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-17 13:25:23 +01:00
|
|
|
|
}
|
|
|
|
|
if (stretch != 0.0 && stretch != 1.0) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
qreal otempo = tempomap()->tempo(segment.tick().ticks());
|
2018-01-17 13:25:23 +01:00
|
|
|
|
qreal ntempo = otempo / stretch;
|
|
|
|
|
setTempo(segment.tick(), ntempo);
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction etick = segment.tick() + segment.ticks() - Fraction(1, 480*4);
|
|
|
|
|
auto e = tempomap()->find(etick.ticks());
|
2018-01-17 13:25:23 +01:00
|
|
|
|
if (e == tempomap()->end())
|
|
|
|
|
setTempo(etick, otempo);
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update time signature map
|
|
|
|
|
// create event if measure len and time signature are different
|
|
|
|
|
// even if they are equivalent 4/4 vs 2/2
|
2016-07-09 14:22:06 +02:00
|
|
|
|
// also check if nominal time signature has changed
|
2016-05-19 13:15:34 +02:00
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (isMaster() && ((!measure->ticks().identical(lc.sig)
|
|
|
|
|
&& measure->ticks() != lc.sig * measure->mmRestCount())
|
2016-10-18 15:41:00 +02:00
|
|
|
|
|| (lc.prevMeasure && lc.prevMeasure->isMeasure()
|
|
|
|
|
&& !measure->timesig().identical(toMeasure(lc.prevMeasure)->timesig()))))
|
|
|
|
|
{
|
2018-04-04 06:31:21 +02:00
|
|
|
|
if (measure->isMMRest())
|
2019-01-30 15:13:54 +01:00
|
|
|
|
lc.sig = measure->mmRestFirst()->ticks();
|
2018-04-04 06:31:21 +02:00
|
|
|
|
else
|
2019-01-30 15:13:54 +01:00
|
|
|
|
lc.sig = measure->ticks();
|
|
|
|
|
sigmap()->add(lc.tick.ticks(), SigEvent(lc.sig, measure->timesig(), measure->no()));
|
2016-05-19 13:15:34 +02:00
|
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Segment* seg = measure->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
2016-10-18 15:41:00 +02:00
|
|
|
|
if (measure->repeatStart()) {
|
|
|
|
|
if (!seg)
|
2019-01-30 15:13:54 +01:00
|
|
|
|
seg = measure->getSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
2016-10-25 17:30:55 +02:00
|
|
|
|
measure->barLinesSetSpan(seg); // this also creates necessary barlines
|
2016-10-18 15:41:00 +02:00
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
|
BarLine* b = toBarLine(seg->element(staffIdx * VOICES));
|
|
|
|
|
if (b) {
|
|
|
|
|
b->setBarLineType(BarLineType::START_REPEAT);
|
|
|
|
|
b->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (seg)
|
|
|
|
|
score()->undoRemoveElement(seg);
|
|
|
|
|
|
2016-02-11 18:20:16 +01:00
|
|
|
|
for (Segment& s : measure->segments()) {
|
2016-02-15 12:23:28 +01:00
|
|
|
|
// DEBUG: relayout grace notes as beaming/flags may have changed
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (s.isChordRestType()) {
|
2016-02-15 12:23:28 +01:00
|
|
|
|
for (Element* e : s.elist()) {
|
|
|
|
|
if (e && e->isChord()) {
|
2016-05-19 13:15:34 +02:00
|
|
|
|
Chord* chord = toChord(e);
|
|
|
|
|
chord->layout();
|
2018-12-12 13:40:19 +01:00
|
|
|
|
// if (chord->tremolo()) // debug
|
|
|
|
|
// chord->tremolo()->layout();
|
2016-02-15 12:23:28 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
|
else if (s.isEndBarLineType())
|
2016-01-04 14:48:58 +01:00
|
|
|
|
continue;
|
2016-08-17 16:40:01 +02:00
|
|
|
|
s.createShapes();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
lc.tick += measure->ticks();
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-04-01 14:57:24 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// isTopBeam
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2018-08-16 16:42:55 +02:00
|
|
|
|
bool isTopBeam(ChordRest* cr)
|
2016-04-01 14:57:24 +02:00
|
|
|
|
{
|
2018-12-09 11:02:11 +01:00
|
|
|
|
Beam* b = cr->beam();
|
|
|
|
|
if (b && !b->cross() && b->elements().front() == cr) {
|
2016-04-01 14:57:24 +02:00
|
|
|
|
for (ChordRest* cr1 : b->elements()) {
|
2018-12-09 11:02:11 +01:00
|
|
|
|
if (cr1->staffMove() >= 0)
|
|
|
|
|
return true;
|
2016-04-01 14:57:24 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-19 11:54:45 +01:00
|
|
|
|
|
2016-04-01 14:57:24 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-24 14:49:34 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// notTopBeam
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2018-08-16 16:42:55 +02:00
|
|
|
|
bool notTopBeam(ChordRest* cr)
|
2016-04-01 14:57:24 +02:00
|
|
|
|
{
|
2018-12-09 11:02:11 +01:00
|
|
|
|
Beam* b = cr->beam();
|
2018-12-19 11:54:45 +01:00
|
|
|
|
if (b && b->elements().front() == cr) {
|
|
|
|
|
if (b->cross())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
for (ChordRest* cr1 : b->elements()) {
|
|
|
|
|
if (cr1->staffMove() >= 0)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-01 14:57:24 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-22 12:08:21 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-08-24 14:49:34 +02:00
|
|
|
|
// findLyricsMaxY
|
2016-08-22 12:08:21 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static qreal findLyricsMaxY(Segment& s, int staffIdx)
|
|
|
|
|
{
|
|
|
|
|
qreal yMax = 0.0;
|
|
|
|
|
if (!s.isChordRestType())
|
|
|
|
|
return yMax;
|
2017-03-20 20:28:10 +01:00
|
|
|
|
|
2018-09-07 17:12:35 +02:00
|
|
|
|
qreal lyricsMinTopDistance = s.score()->styleP(Sid::lyricsMinTopDistance);
|
|
|
|
|
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
2017-07-21 12:05:23 +02:00
|
|
|
|
if (cr && !cr->lyrics().empty()) {
|
2018-09-07 17:12:35 +02:00
|
|
|
|
SkylineLine sk(true);
|
|
|
|
|
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l->autoplace() && l->placeBelow()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
l->ryoffset() = 0.0;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
QPointF offset = l->pos() + cr->pos() + s.pos() + s.measure()->pos();
|
|
|
|
|
QRectF r = l->bbox().translated(offset);
|
|
|
|
|
sk.add(r.x(), r.top(), r.width());
|
2017-03-20 20:28:10 +01:00
|
|
|
|
}
|
2016-08-22 12:08:21 +02:00
|
|
|
|
}
|
2018-09-07 17:12:35 +02:00
|
|
|
|
SysStaff* ss = s.measure()->system()->staff(staffIdx);
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l->autoplace() && l->placeBelow()) {
|
2018-09-07 17:12:35 +02:00
|
|
|
|
qreal y = ss->skyline().south().minDistance(sk);
|
2017-03-20 20:28:10 +01:00
|
|
|
|
if (y > -lyricsMinTopDistance)
|
|
|
|
|
yMax = qMax(yMax, y + lyricsMinTopDistance);
|
|
|
|
|
}
|
2016-08-22 12:08:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return yMax;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-24 14:49:34 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// findLyricsMinY
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static qreal findLyricsMinY(Segment& s, int staffIdx)
|
|
|
|
|
{
|
|
|
|
|
qreal yMin = 0.0;
|
|
|
|
|
if (!s.isChordRestType())
|
|
|
|
|
return yMin;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
qreal lyricsMinTopDistance = s.score()->styleP(Sid::lyricsMinTopDistance);
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
2017-07-21 12:05:23 +02:00
|
|
|
|
if (cr && !cr->lyrics().empty()) {
|
2018-09-07 17:12:35 +02:00
|
|
|
|
SkylineLine sk(false);
|
|
|
|
|
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l->autoplace() && l->placeAbove()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
l->ryoffset() = 0.0;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
QRectF r = l->bbox().translated(l->pos() + cr->pos() + s.pos() + s.measure()->pos());
|
|
|
|
|
sk.add(r.x(), r.bottom(), r.width());
|
2017-03-20 20:28:10 +01:00
|
|
|
|
}
|
2016-08-24 14:49:34 +02:00
|
|
|
|
}
|
2018-09-07 17:12:35 +02:00
|
|
|
|
SysStaff* ss = s.measure()->system()->staff(staffIdx);
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l->autoplace() && l->placeAbove()) {
|
2018-09-07 17:12:35 +02:00
|
|
|
|
qreal y = sk.minDistance(ss->skyline().north());
|
2017-03-20 20:28:10 +01:00
|
|
|
|
if (y > -lyricsMinTopDistance)
|
|
|
|
|
yMin = qMin(yMin, -y -lyricsMinTopDistance);
|
|
|
|
|
}
|
2016-08-24 14:49:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return yMin;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-07 17:12:35 +02:00
|
|
|
|
static qreal findLyricsMaxY(Measure* m, int staffIdx)
|
|
|
|
|
{
|
|
|
|
|
qreal yMax = 0.0;
|
|
|
|
|
for (Segment& s : m->segments())
|
|
|
|
|
yMax = qMax(yMax, findLyricsMaxY(s, staffIdx));
|
|
|
|
|
return yMax;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-24 14:49:34 +02:00
|
|
|
|
static qreal findLyricsMinY(Measure* m, int staffIdx)
|
|
|
|
|
{
|
|
|
|
|
qreal yMin = 0.0;
|
|
|
|
|
for (Segment& s : m->segments())
|
|
|
|
|
yMin = qMin(yMin, findLyricsMinY(s, staffIdx));
|
|
|
|
|
return yMin;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-22 12:08:21 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// applyLyricsMax
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void applyLyricsMax(Segment& s, int staffIdx, qreal yMax)
|
|
|
|
|
{
|
|
|
|
|
if (!s.isChordRestType())
|
|
|
|
|
return;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
Skyline& sk = s.measure()->system()->staff(staffIdx)->skyline();
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
|
|
|
|
if (cr && !cr->lyrics().empty()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
qreal lyricsMinBottomDistance = s.score()->styleP(Sid::lyricsMinBottomDistance);
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (l->visible() && l->autoplace() && l->placeBelow()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
l->ryoffset() = yMax;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
QPointF offset = l->pos() + cr->pos() + s.pos() + s.measure()->pos();
|
|
|
|
|
sk.add(l->bbox().translated(offset).adjusted(0.0, 0.0, 0.0, lyricsMinBottomDistance));
|
2017-03-20 20:28:10 +01:00
|
|
|
|
}
|
2016-08-22 12:08:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void applyLyricsMax(Measure* m, int staffIdx, qreal yMax)
|
|
|
|
|
{
|
|
|
|
|
for (Segment& s : m->segments())
|
|
|
|
|
applyLyricsMax(s, staffIdx, yMax);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-24 14:49:34 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// applyLyricsMin
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-08-29 11:34:35 +02:00
|
|
|
|
static void applyLyricsMin(ChordRest* cr, int staffIdx, qreal yMin)
|
2016-08-24 14:49:34 +02:00
|
|
|
|
{
|
2018-09-07 17:12:35 +02:00
|
|
|
|
Skyline& sk = cr->measure()->system()->staff(staffIdx)->skyline();
|
2016-08-29 11:34:35 +02:00
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (l->visible() && l->autoplace() && l->placeAbove()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
|
l->ryoffset() = yMin;
|
2018-09-07 17:12:35 +02:00
|
|
|
|
QPointF offset = l->pos() + cr->pos() + cr->segment()->pos() + cr->segment()->measure()->pos();
|
|
|
|
|
sk.add(l->bbox().translated(offset));
|
2016-08-24 14:49:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 11:34:35 +02:00
|
|
|
|
static void applyLyricsMin(Measure* m, int staffIdx, qreal yMin)
|
2016-08-24 14:49:34 +02:00
|
|
|
|
{
|
2016-08-29 11:34:35 +02:00
|
|
|
|
for (Segment& s : m->segments()) {
|
|
|
|
|
if (s.isChordRestType()) {
|
2017-03-20 20:28:10 +01:00
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
|
|
|
|
if (cr)
|
|
|
|
|
applyLyricsMin(cr, staffIdx, yMin);
|
|
|
|
|
}
|
2016-08-29 11:34:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-24 14:49:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-26 14:33:13 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// restoreBeams
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static void restoreBeams(Measure* m)
|
|
|
|
|
{
|
2017-03-08 13:12:26 +01:00
|
|
|
|
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
|
2016-10-26 14:33:13 +02:00
|
|
|
|
for (Element* e : s->elist()) {
|
|
|
|
|
if (e && e->isChordRest()) {
|
|
|
|
|
ChordRest* cr = toChordRest(e);
|
|
|
|
|
if (isTopBeam(cr)) {
|
|
|
|
|
cr->beam()->layout();
|
2018-09-18 11:34:11 +02:00
|
|
|
|
cr->beam()->addSkyline(m->system()->staff(cr->beam()->staffIdx())->skyline());
|
2016-10-26 14:33:13 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 15:20:06 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutLyrics
|
2018-07-19 13:03:25 +02:00
|
|
|
|
//
|
|
|
|
|
// vertical align lyrics
|
|
|
|
|
//
|
2017-09-19 15:20:06 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Score::layoutLyrics(System* system)
|
|
|
|
|
{
|
2018-07-19 13:03:25 +02:00
|
|
|
|
std::vector<int> visibleStaves;
|
|
|
|
|
for (int staffIdx = system->firstVisibleStaff(); staffIdx < nstaves(); staffIdx = system->nextVisibleStaff(staffIdx))
|
|
|
|
|
visibleStaves.push_back(staffIdx);
|
|
|
|
|
|
2018-07-10 14:57:05 +02:00
|
|
|
|
//int nAbove[nstaves()];
|
|
|
|
|
std::vector<int> VnAbove(nstaves());
|
2018-08-10 11:46:16 +02:00
|
|
|
|
|
2018-07-19 13:03:25 +02:00
|
|
|
|
for (int staffIdx : visibleStaves) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
VnAbove[staffIdx] = 0;
|
2018-07-19 13:03:25 +02:00
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
|
|
|
|
for (Segment& s : m->segments()) {
|
|
|
|
|
if (s.isChordRestType()) {
|
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
|
|
|
|
if (cr) {
|
|
|
|
|
int nA = 0;
|
|
|
|
|
for (Lyrics* l : cr->lyrics()) {
|
|
|
|
|
if (l->placeAbove())
|
|
|
|
|
++nA;
|
|
|
|
|
}
|
2018-07-10 14:57:05 +02:00
|
|
|
|
VnAbove[staffIdx] = qMax(VnAbove[staffIdx], nA);
|
2018-07-19 13:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int staffIdx : visibleStaves) {
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
|
|
|
|
for (Segment& s : m->segments()) {
|
|
|
|
|
if (s.isChordRestType()) {
|
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
|
ChordRest* cr = s.cr(staffIdx * VOICES + voice);
|
|
|
|
|
if (cr) {
|
|
|
|
|
for (Lyrics* l : cr->lyrics())
|
2018-07-10 14:57:05 +02:00
|
|
|
|
l->layout2(VnAbove[staffIdx]);
|
2018-07-19 13:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 15:36:00 +02:00
|
|
|
|
VerticalAlignRange ar = VerticalAlignRange(styleI(Sid::autoplaceVerticalAlignRange));
|
2017-09-19 15:20:06 +02:00
|
|
|
|
|
|
|
|
|
switch (ar) {
|
|
|
|
|
case VerticalAlignRange::MEASURE:
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2018-07-19 13:03:25 +02:00
|
|
|
|
for (int staffIdx : visibleStaves) {
|
2017-09-19 15:20:06 +02:00
|
|
|
|
qreal yMax = findLyricsMaxY(m, staffIdx);
|
|
|
|
|
applyLyricsMax(m, staffIdx, yMax);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case VerticalAlignRange::SYSTEM:
|
2018-07-19 13:03:25 +02:00
|
|
|
|
for (int staffIdx : visibleStaves) {
|
2017-09-19 15:20:06 +02:00
|
|
|
|
qreal yMax = 0.0;
|
|
|
|
|
qreal yMin = 0.0;
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
2018-07-10 14:57:05 +02:00
|
|
|
|
yMax = qMax<qreal>(yMax, findLyricsMaxY(toMeasure(mb), staffIdx));
|
2017-09-19 15:20:06 +02:00
|
|
|
|
yMin = qMin(yMin, findLyricsMinY(toMeasure(mb), staffIdx));
|
|
|
|
|
}
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
applyLyricsMax(toMeasure(mb), staffIdx, yMax);
|
|
|
|
|
applyLyricsMin(toMeasure(mb), staffIdx, yMin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case VerticalAlignRange::SEGMENT:
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2018-07-19 13:03:25 +02:00
|
|
|
|
for (int staffIdx : visibleStaves) {
|
2017-09-19 15:20:06 +02:00
|
|
|
|
for (Segment& s : m->segments()) {
|
|
|
|
|
qreal yMax = findLyricsMaxY(s, staffIdx);
|
|
|
|
|
applyLyricsMax(s, staffIdx, yMax);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-15 12:10:04 +01:00
|
|
|
|
|
2017-12-21 14:03:45 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutTies
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
void layoutTies(Chord* ch, System* system, const Fraction& stick)
|
2017-12-21 14:03:45 +01:00
|
|
|
|
{
|
|
|
|
|
for (Note* note : ch->notes()) {
|
2018-09-27 16:28:25 +02:00
|
|
|
|
Tie* t = note->tieFor();
|
|
|
|
|
if (t) {
|
|
|
|
|
TieSegment* ts = t->layoutFor(system);
|
|
|
|
|
system->staff(ch->staffIdx())->skyline().add(ts->shape().translated(ts->pos()));
|
|
|
|
|
}
|
2018-09-27 17:46:58 +02:00
|
|
|
|
t = note->tieBack();
|
|
|
|
|
if (t) {
|
2018-09-27 16:28:25 +02:00
|
|
|
|
if (t->startNote()->tick() < stick) {
|
|
|
|
|
TieSegment* ts = t->layoutBack(system);
|
|
|
|
|
system->staff(ch->staffIdx())->skyline().add(ts->shape().translated(ts->pos()));
|
|
|
|
|
}
|
2017-12-21 14:03:45 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-19 15:20:06 +02:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutHarmonies
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void layoutHarmonies(const std::vector<Segment*>& sl)
|
|
|
|
|
{
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
|
if (e->isHarmony()) {
|
|
|
|
|
Harmony* h = toHarmony(e);
|
2018-12-15 02:16:59 +01:00
|
|
|
|
// For chord symbols that coincide with a chord or rest,
|
|
|
|
|
// a partial layout can also happen (if needed) during ChordRest layout
|
|
|
|
|
// in order to calculate a bbox and allocate its shape to the ChordRest.
|
|
|
|
|
// But that layout (if it happens at all) does not do autoplace,
|
|
|
|
|
// so we need the full layout here.
|
|
|
|
|
h->layout();
|
2018-12-14 04:08:20 +01:00
|
|
|
|
h->autoplaceSegmentElement(s->score()->styleP(Sid::minHarmonyDistance));
|
2018-12-07 08:38:21 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-03 09:59:50 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// processLines
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2018-08-17 15:55:08 +02:00
|
|
|
|
static void processLines(System* system, std::vector<Spanner*> lines, bool align)
|
2018-01-03 09:59:50 +01:00
|
|
|
|
{
|
|
|
|
|
std::vector<SpannerSegment*> segments;
|
|
|
|
|
for (Spanner* sp : lines) {
|
|
|
|
|
SpannerSegment* ss = sp->layoutSystem(system); // create/layout spanner segment for this system
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (ss->visible() && ss->autoplace())
|
2018-01-03 09:59:50 +01:00
|
|
|
|
segments.push_back(ss);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-12 11:36:54 +01:00
|
|
|
|
if (align && segments.size() > 1) {
|
2018-12-09 14:19:24 +01:00
|
|
|
|
qreal y = segments[0]->rypos();
|
2018-02-12 11:36:54 +01:00
|
|
|
|
for (unsigned i = 1; i < segments.size(); ++i)
|
2018-12-09 14:19:24 +01:00
|
|
|
|
y = qMax(y, segments[i]->rypos());
|
2018-02-12 11:36:54 +01:00
|
|
|
|
for (auto ss : segments)
|
2018-12-09 14:19:24 +01:00
|
|
|
|
ss->rypos() = y;
|
2018-02-12 11:36:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-03 09:59:50 +01:00
|
|
|
|
//
|
2018-09-11 16:26:27 +02:00
|
|
|
|
// add shapes to skyline
|
2018-01-03 09:59:50 +01:00
|
|
|
|
//
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (SpannerSegment* ss : segments)
|
|
|
|
|
system->staff(ss->staffIdx())->skyline().add(ss->shape().translated(ss->pos()));
|
2018-01-03 09:59:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-19 14:36:09 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// collectSystem
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
System* Score::collectSystem(LayoutContext& lc)
|
|
|
|
|
{
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (!lc.curMeasure)
|
2016-01-04 14:48:58 +01:00
|
|
|
|
return 0;
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Measure* measure = _systems.empty() ? 0 : _systems.back()->lastMeasure();
|
|
|
|
|
if (measure) {
|
|
|
|
|
lc.firstSystem = measure->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
|
|
|
|
|
lc.startWithLongNames = lc.firstSystem && measure->sectionBreakElement()->startWithLongNames();
|
2018-07-17 13:41:45 +02:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
System* system = getNextSystem(lc);
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction lcmTick = lc.curMeasure ? lc.curMeasure->tick() : Fraction(0,1);
|
2018-12-03 03:17:26 +01:00
|
|
|
|
system->setInstrumentNames(lc.startWithLongNames, lcmTick);
|
2014-12-30 15:14:05 +01:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
qreal minWidth = 0;
|
2018-12-03 11:08:34 +01:00
|
|
|
|
qreal layoutSystemMinWidth = 0;
|
2016-10-25 17:30:55 +02:00
|
|
|
|
bool firstMeasure = true;
|
2016-12-14 09:56:16 +01:00
|
|
|
|
bool createHeader = false;
|
2018-03-27 15:36:00 +02:00
|
|
|
|
qreal systemWidth = styleD(Sid::pagePrintableWidth) * DPI;
|
2016-10-18 15:41:00 +02:00
|
|
|
|
system->setWidth(systemWidth);
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
while (lc.curMeasure) { // collect measure for system
|
|
|
|
|
System* oldSystem = lc.curMeasure->system();
|
2017-03-14 17:00:38 +01:00
|
|
|
|
system->appendMeasure(lc.curMeasure);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
qreal ww = 0; // width of current measure
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
if (lc.curMeasure->isMeasure()) {
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Measure* m = toMeasure(lc.curMeasure);
|
2016-10-20 11:32:07 +02:00
|
|
|
|
if (firstMeasure) {
|
2018-12-03 11:08:34 +01:00
|
|
|
|
layoutSystemMinWidth = minWidth;
|
2016-10-25 17:30:55 +02:00
|
|
|
|
system->layoutSystem(minWidth);
|
|
|
|
|
minWidth += system->leftMargin();
|
2016-10-20 11:32:07 +02:00
|
|
|
|
if (m->repeatStart()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
2016-10-20 11:32:07 +02:00
|
|
|
|
if (!s->enabled())
|
|
|
|
|
s->setEnabled(true);
|
|
|
|
|
}
|
2016-10-18 15:41:00 +02:00
|
|
|
|
m->addSystemHeader(lc.firstSystem);
|
2016-12-14 09:56:16 +01:00
|
|
|
|
firstMeasure = false;
|
|
|
|
|
createHeader = false;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (createHeader) {
|
|
|
|
|
m->addSystemHeader(false);
|
|
|
|
|
createHeader = false;
|
|
|
|
|
}
|
|
|
|
|
else if (m->header())
|
|
|
|
|
m->removeSystemHeader();
|
2016-10-20 11:32:07 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
2017-12-08 13:44:57 +01:00
|
|
|
|
m->createEndBarLines(true);
|
2016-10-25 17:30:55 +02:00
|
|
|
|
Measure* nm = m->nextMeasure();
|
|
|
|
|
if (nm)
|
|
|
|
|
m->addSystemTrailer(nm);
|
|
|
|
|
m->computeMinWidth();
|
|
|
|
|
ww = m->width();
|
|
|
|
|
}
|
|
|
|
|
else if (lc.curMeasure->isHBox()) {
|
|
|
|
|
lc.curMeasure->computeMinWidth();
|
|
|
|
|
ww = lc.curMeasure->width();
|
2016-12-14 09:56:16 +01:00
|
|
|
|
createHeader = toHBox(lc.curMeasure)->createSystemHeader();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// vbox:
|
|
|
|
|
getNextMeasure(lc);
|
|
|
|
|
system->layout2(); // compute staff distances
|
|
|
|
|
return system;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// check if lc.curMeasure fits, remove if not
|
2016-10-18 15:41:00 +02:00
|
|
|
|
// collect at least one measure and the break
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2018-08-16 16:42:55 +02:00
|
|
|
|
bool doBreak = (system->measures().size() > 1) && ((minWidth + ww) > systemWidth);
|
2016-10-25 17:30:55 +02:00
|
|
|
|
if (doBreak) {
|
|
|
|
|
if (lc.prevMeasure->noBreak() && system->measures().size() > 2) {
|
|
|
|
|
// remove last two measures
|
|
|
|
|
// TODO: check more measures for noBreak()
|
2018-12-20 16:53:14 +01:00
|
|
|
|
system->removeLastMeasure();
|
|
|
|
|
system->removeLastMeasure();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
lc.curMeasure->setSystem(oldSystem);
|
|
|
|
|
lc.prevMeasure->setSystem(oldSystem);
|
|
|
|
|
lc.nextMeasure = lc.curMeasure;
|
|
|
|
|
lc.curMeasure = lc.prevMeasure;
|
|
|
|
|
lc.prevMeasure = lc.curMeasure->prevMeasure();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!lc.prevMeasure->noBreak()) {
|
|
|
|
|
// remove last measure
|
2018-12-20 16:53:14 +01:00
|
|
|
|
system->removeLastMeasure();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
lc.curMeasure->setSystem(oldSystem);
|
|
|
|
|
break;
|
2016-10-12 17:56:05 +02:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
if (lc.prevMeasure && lc.prevMeasure->isMeasure() && lc.prevMeasure->system() == system) {
|
2016-10-25 17:30:55 +02:00
|
|
|
|
//
|
2016-10-18 15:41:00 +02:00
|
|
|
|
// now we know that the previous measure is not the last
|
|
|
|
|
// measure in the system and we finally can create the end barline for it
|
|
|
|
|
|
2016-02-17 14:54:23 +01:00
|
|
|
|
Measure* m = toMeasure(lc.prevMeasure);
|
2016-10-25 17:30:55 +02:00
|
|
|
|
if (m->trailer()) {
|
|
|
|
|
qreal ow = m->width();
|
2016-10-18 15:41:00 +02:00
|
|
|
|
m->removeSystemTrailer();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
minWidth += m->width() - ow;
|
|
|
|
|
}
|
2016-10-20 11:32:07 +02:00
|
|
|
|
// if the prev measure is an end repeat and the cur measure
|
2016-10-18 15:41:00 +02:00
|
|
|
|
// is an repeat, the createEndBarLines() created an start-end repeat barline
|
|
|
|
|
// and we can remove the start repeat barline of the current barline
|
|
|
|
|
|
|
|
|
|
if (lc.curMeasure->isMeasure()) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Measure* m1 = toMeasure(lc.curMeasure);
|
|
|
|
|
if (m1->repeatStart()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Segment* s = m1->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
|
2017-03-20 18:24:16 +01:00
|
|
|
|
if (!s->enabled()) {
|
|
|
|
|
s->setEnabled(true);
|
2018-07-10 14:57:05 +02:00
|
|
|
|
m1->computeMinWidth();
|
|
|
|
|
ww = m1->width();
|
2016-10-18 15:41:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
minWidth += m->createEndBarLines(false); // create final barLine
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-12 17:56:05 +02:00
|
|
|
|
MeasureBase* mb = lc.curMeasure;
|
2016-10-20 11:32:07 +02:00
|
|
|
|
bool lineBreak = false;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
switch (_layoutMode) {
|
|
|
|
|
case LayoutMode::PAGE:
|
|
|
|
|
case LayoutMode::SYSTEM:
|
2016-10-18 15:41:00 +02:00
|
|
|
|
lineBreak = mb->pageBreak() || mb->lineBreak() || mb->sectionBreak();
|
2016-01-04 14:48:58 +01:00
|
|
|
|
break;
|
|
|
|
|
case LayoutMode::FLOAT:
|
|
|
|
|
case LayoutMode::LINE:
|
2016-10-18 15:41:00 +02:00
|
|
|
|
lineBreak = false;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
|
2016-10-18 15:41:00 +02:00
|
|
|
|
getNextMeasure(lc);
|
2018-07-17 16:18:10 +02:00
|
|
|
|
|
2016-10-18 15:41:00 +02:00
|
|
|
|
minWidth += ww;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (lc.endTick < lc.prevMeasure->tick()) {
|
2016-04-15 16:44:48 +02:00
|
|
|
|
// TODO: we may check if another measure fits in this system
|
2016-10-18 15:41:00 +02:00
|
|
|
|
if (lc.prevMeasure == lc.systemOldMeasure) {
|
2016-04-15 16:44:48 +02:00
|
|
|
|
lc.rangeDone = true;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (lc.curMeasure && lc.curMeasure->isMeasure()) {
|
2018-07-17 16:18:10 +02:00
|
|
|
|
Measure* m = toMeasure(lc.curMeasure);
|
|
|
|
|
restoreBeams(m);
|
|
|
|
|
m->stretchMeasure(lc.curMeasure->width());
|
2017-01-05 11:23:47 +01:00
|
|
|
|
}
|
2016-04-15 16:44:48 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2017-01-18 14:16:33 +01:00
|
|
|
|
// ElementType nt = lc.curMeasure ? lc.curMeasure->type() : ElementType::INVALID;
|
2016-10-12 17:56:05 +02:00
|
|
|
|
mb = lc.curMeasure;
|
|
|
|
|
bool tooWide = false; // minWidth + minMeasureWidth > systemWidth; // TODO: noBreak
|
2018-08-16 16:42:55 +02:00
|
|
|
|
if (lineBreak || !mb || mb->isVBox() || mb->isTBox() || mb->isFBox() || tooWide)
|
2016-10-18 15:41:00 +02:00
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-10-18 15:41:00 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
2016-10-18 15:41:00 +02:00
|
|
|
|
// now we have a complete set of measures for this system
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
2016-10-25 17:30:55 +02:00
|
|
|
|
// prevMeasure is the last measure in the system
|
|
|
|
|
if (lc.prevMeasure && lc.prevMeasure->isMeasure()) {
|
2018-12-22 14:55:01 +01:00
|
|
|
|
breakCrossMeasureBeams(toMeasure(lc.prevMeasure));
|
2016-10-25 17:30:55 +02:00
|
|
|
|
qreal w = toMeasure(lc.prevMeasure)->createEndBarLines(true);
|
|
|
|
|
minWidth += w;
|
|
|
|
|
}
|
2016-05-18 15:43:47 +02:00
|
|
|
|
|
2018-12-03 11:08:34 +01:00
|
|
|
|
hideEmptyStaves(system, lc.firstSystem);
|
|
|
|
|
bool allShown = true;
|
|
|
|
|
for (const SysStaff* ss : *system->staves()) {
|
|
|
|
|
if (!ss->show()) {
|
|
|
|
|
allShown = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!allShown) {
|
|
|
|
|
// Relayout system decorations to reuse space properly for
|
|
|
|
|
// hidden staves' instrument names or other hidden elements.
|
|
|
|
|
minWidth -= system->leftMargin();
|
|
|
|
|
system->layoutSystem(layoutSystemMinWidth);
|
|
|
|
|
minWidth += system->leftMargin();
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//-------------------------------------------------------
|
|
|
|
|
// add system trailer if needed
|
|
|
|
|
// (cautionary time/key signatures etc)
|
|
|
|
|
//-------------------------------------------------------
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2018-09-25 15:55:08 +02:00
|
|
|
|
Measure* lm = system->lastMeasure();
|
|
|
|
|
if (lm) {
|
|
|
|
|
Measure* nm = lm->nextMeasure();
|
2018-08-16 16:42:55 +02:00
|
|
|
|
if (nm) {
|
2018-09-25 15:55:08 +02:00
|
|
|
|
qreal w = lm->width();
|
|
|
|
|
lm->addSystemTrailer(nm);
|
|
|
|
|
if (lm->trailer())
|
|
|
|
|
lm->computeMinWidth();
|
|
|
|
|
minWidth += lm->width() - w;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
}
|
2018-08-16 16:42:55 +02:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
//
|
|
|
|
|
// stretch incomplete row
|
|
|
|
|
//
|
|
|
|
|
qreal rest;
|
2018-08-16 16:42:55 +02:00
|
|
|
|
if (MScore::noHorizontalStretch)
|
2016-10-25 17:30:55 +02:00
|
|
|
|
rest = 0;
|
|
|
|
|
else {
|
|
|
|
|
qreal mw = system->leftMargin(); // DEBUG
|
|
|
|
|
qreal totalWeight = 0.0;
|
2015-07-25 08:43:02 +02:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (mb->isHBox()) {
|
|
|
|
|
mw += mb->width();
|
2016-10-18 15:41:00 +02:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
else if (mb->isMeasure()) {
|
|
|
|
|
Measure* m = toMeasure(mb);
|
|
|
|
|
mw += m->width(); // measures are stretched already with basicStretch()
|
2019-01-30 15:13:54 +01:00
|
|
|
|
totalWeight += m->ticks().ticks() * m->basicStretch();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-18 15:41:00 +02:00
|
|
|
|
|
2016-10-18 15:46:23 +02:00
|
|
|
|
#ifndef NDEBUG
|
2016-10-25 17:30:55 +02:00
|
|
|
|
if (!qFuzzyCompare(mw, minWidth))
|
2019-01-30 15:13:54 +01:00
|
|
|
|
qDebug("==layoutSystem %6d old %.1f new %.1f", system->measures().front()->tick().ticks(), minWidth, mw);
|
2016-10-18 15:46:23 +02:00
|
|
|
|
#endif
|
2016-10-25 17:30:55 +02:00
|
|
|
|
rest = systemWidth - minWidth;
|
|
|
|
|
//
|
2018-11-27 22:49:18 +01:00
|
|
|
|
// don’t stretch last system row, if accumulated minWidth is <= lastSystemFillLimit
|
2016-10-25 17:30:55 +02:00
|
|
|
|
//
|
2018-03-27 15:36:00 +02:00
|
|
|
|
if (lc.curMeasure == 0 && ((minWidth / systemWidth) <= styleD(Sid::lastSystemFillLimit))) {
|
2016-10-25 17:30:55 +02:00
|
|
|
|
if (minWidth > rest)
|
|
|
|
|
rest = rest * .5;
|
|
|
|
|
else
|
|
|
|
|
rest = minWidth;
|
2015-07-25 08:43:02 +02:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
rest /= totalWeight;
|
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
2016-10-25 17:30:55 +02:00
|
|
|
|
QPointF pos;
|
|
|
|
|
firstMeasure = true;
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
qreal ww = mb->width();
|
|
|
|
|
if (mb->isMeasure()) {
|
|
|
|
|
if (firstMeasure) {
|
|
|
|
|
pos.rx() += system->leftMargin();
|
|
|
|
|
firstMeasure = false;
|
2016-10-18 15:41:00 +02:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
mb->setPos(pos);
|
|
|
|
|
Measure* m = toMeasure(mb);
|
|
|
|
|
qreal stretch = m->basicStretch();
|
2019-01-30 15:13:54 +01:00
|
|
|
|
ww += rest * m->ticks().ticks() * stretch;
|
2016-10-25 17:30:55 +02:00
|
|
|
|
m->stretchMeasure(ww);
|
2016-12-12 14:55:35 +01:00
|
|
|
|
m->layoutStaffLines();
|
2016-10-25 17:30:55 +02:00
|
|
|
|
}
|
|
|
|
|
else if (mb->isHBox()) {
|
|
|
|
|
mb->setPos(pos + QPointF(toHBox(mb)->topGap(), 0.0));
|
|
|
|
|
mb->layout();
|
2015-07-25 08:43:02 +02:00
|
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
else if (mb->isVBox())
|
|
|
|
|
mb->setPos(pos);
|
|
|
|
|
pos.rx() += ww;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2018-01-11 11:39:14 +01:00
|
|
|
|
system->setWidth(pos.x());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
2018-12-06 15:27:12 +01:00
|
|
|
|
layoutSystemElements(system, lc);
|
|
|
|
|
system->layout2(); // compute staff distances
|
|
|
|
|
|
|
|
|
|
lm = system->lastMeasure();
|
|
|
|
|
if (lm) {
|
|
|
|
|
lc.firstSystem = lm->sectionBreak() && _layoutMode != LayoutMode::FLOAT;
|
|
|
|
|
lc.startWithLongNames = lc.firstSystem && lm->sectionBreakElement()->startWithLongNames();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return system;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layoutSystemElements
|
|
|
|
|
//---------------------------------------------------------
|
2017-08-02 18:19:08 +02:00
|
|
|
|
|
2018-12-06 15:27:12 +01:00
|
|
|
|
void Score::layoutSystemElements(System* system, LayoutContext& lc)
|
|
|
|
|
{
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// create cr segment list to speed up computations
|
|
|
|
|
//-------------------------------------------------------------
|
2018-01-11 11:39:14 +01:00
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
std::vector<Segment*> sl;
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2018-12-09 15:11:26 +01:00
|
|
|
|
m->layoutMeasureNumber();
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (Segment* s = m->first(); s; s = s->next()) {
|
2019-01-07 17:31:00 +01:00
|
|
|
|
if (s->isChordRestType() || !s->annotations().empty())
|
|
|
|
|
sl.push_back(s);
|
2018-09-11 16:26:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-11 11:39:14 +01:00
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// create skylines
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
|
SysStaff* ss = system->staff(staffIdx);
|
2018-11-30 17:13:43 +01:00
|
|
|
|
Skyline& skyline = ss->skyline();
|
|
|
|
|
skyline.clear();
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2019-03-01 16:22:16 +01:00
|
|
|
|
if (MeasureNumber* mno = m->noText(staffIdx))
|
|
|
|
|
ss->skyline().add(mno->bbox().translated(m->pos() + mno->pos()));
|
|
|
|
|
ss->skyline().add(m->staffLines(staffIdx)->bbox().translated(m->pos()));
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (Segment& s : m->segments()) {
|
2018-10-03 14:35:08 +02:00
|
|
|
|
if (!s.enabled() || s.isTimeSigType()) // hack: ignore time signatures
|
2018-09-11 16:26:27 +02:00
|
|
|
|
continue;
|
2018-12-10 16:33:09 +01:00
|
|
|
|
QPointF p(s.pos() + m->pos());
|
2018-11-30 17:13:43 +01:00
|
|
|
|
if (s.segmentType() & (SegmentType::BarLine | SegmentType::EndBarLine | SegmentType::StartRepeatBarLine | SegmentType::BeginBarLine)) {
|
|
|
|
|
BarLine* bl = toBarLine(s.element(0));
|
|
|
|
|
if (bl) {
|
|
|
|
|
qreal w = BarLine::layoutWidth(score(), bl->barLineType());
|
2018-12-10 16:33:09 +01:00
|
|
|
|
skyline.add(QRectF(0.0, 0.0, w, spatium() * 4.0).translated(bl->pos() + p));
|
2018-11-30 17:13:43 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int strack = staffIdx * VOICES;
|
|
|
|
|
int etrack = strack + VOICES;
|
|
|
|
|
for (Element* e : s.elist()) {
|
2019-02-05 14:41:39 +01:00
|
|
|
|
if (!e)
|
|
|
|
|
continue;
|
|
|
|
|
// clear layout for chord-based fingerings
|
|
|
|
|
if (e->isChord()) {
|
|
|
|
|
Chord* c = toChord(e);
|
|
|
|
|
std::list<Note*> notes;
|
|
|
|
|
for (auto gc : c->graceNotes()) {
|
|
|
|
|
for (auto n : gc->notes())
|
|
|
|
|
notes.push_back(n);
|
|
|
|
|
}
|
|
|
|
|
for (auto n : c->notes())
|
|
|
|
|
notes.push_back(n);
|
|
|
|
|
for (Note* note : notes) {
|
|
|
|
|
for (Element* e : note->el()) {
|
|
|
|
|
if (e->isFingering()) {
|
|
|
|
|
Fingering* f = toFingering(e);
|
|
|
|
|
if (f->layoutType() == ElementType::CHORD) {
|
|
|
|
|
f->setPos(QPointF());
|
|
|
|
|
f->setbbox(QRectF());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!e->visible())
|
2018-11-30 17:13:43 +01:00
|
|
|
|
continue;
|
|
|
|
|
int effectiveTrack = e->vStaffIdx() * VOICES + e->voice();
|
2019-03-04 06:47:19 +01:00
|
|
|
|
if (effectiveTrack >= strack && effectiveTrack < etrack) {
|
2018-12-10 16:33:09 +01:00
|
|
|
|
skyline.add(e->shape().translated(e->pos() + p));
|
2019-03-04 06:47:19 +01:00
|
|
|
|
if (e->isChord() && toChord(e)->tremolo()) {
|
|
|
|
|
Tremolo* t = toChord(e)->tremolo();
|
|
|
|
|
if (t->chord() == e && t->autoplace())
|
|
|
|
|
skyline.add(t->shape().translated(t->pos() + e->pos() + p));
|
|
|
|
|
}
|
2018-12-12 13:40:19 +01:00
|
|
|
|
}
|
2018-11-30 17:13:43 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-11 11:39:14 +01:00
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-12 12:33:11 +02:00
|
|
|
|
//-------------------------------------------------------------
|
2018-11-27 11:03:12 +01:00
|
|
|
|
// layout beams + fingering
|
2018-09-12 12:33:11 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (Segment* s : sl) {
|
2018-09-17 14:05:56 +02:00
|
|
|
|
for (Element* e : s->elist()) {
|
2018-12-09 11:02:11 +01:00
|
|
|
|
if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show())
|
2018-09-12 12:33:11 +02:00
|
|
|
|
continue;
|
2018-12-09 11:02:11 +01:00
|
|
|
|
ChordRest* cr = toChordRest(e);
|
2018-12-29 16:13:18 +01:00
|
|
|
|
|
|
|
|
|
// layout beam
|
2018-12-09 11:02:11 +01:00
|
|
|
|
if (isTopBeam(cr)) {
|
|
|
|
|
cr->beam()->layout();
|
|
|
|
|
cr->beam()->addSkyline(system->staff(cr->beam()->staffIdx())->skyline());
|
2018-12-29 16:13:18 +01:00
|
|
|
|
}
|
2018-12-09 11:02:11 +01:00
|
|
|
|
|
2019-02-05 14:41:39 +01:00
|
|
|
|
// layout chord-based fingerings
|
2018-12-29 16:13:18 +01:00
|
|
|
|
if (e->isChord()) {
|
|
|
|
|
Chord* c = toChord(e);
|
2019-02-05 14:41:39 +01:00
|
|
|
|
std::list<Note*> notes;
|
|
|
|
|
for (auto gc : c->graceNotes()) {
|
|
|
|
|
for (auto n : gc->notes())
|
|
|
|
|
notes.push_back(n);
|
|
|
|
|
}
|
|
|
|
|
for (auto n : c->notes())
|
|
|
|
|
notes.push_back(n);
|
|
|
|
|
std::list<Fingering*> fingerings;
|
|
|
|
|
for (Note* note : notes) {
|
2018-12-29 16:13:18 +01:00
|
|
|
|
for (Element* el : note->el()) {
|
|
|
|
|
if (el->isFingering()) {
|
|
|
|
|
Fingering* f = toFingering(el);
|
|
|
|
|
if (f->layoutType() == ElementType::CHORD) {
|
|
|
|
|
if (f->placeAbove())
|
|
|
|
|
fingerings.push_back(f);
|
|
|
|
|
else
|
|
|
|
|
fingerings.push_front(f);
|
|
|
|
|
}
|
2018-12-12 13:40:19 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-29 16:13:18 +01:00
|
|
|
|
for (Fingering* f : fingerings) {
|
|
|
|
|
f->layout();
|
|
|
|
|
if (f->autoplace() && f->visible()) {
|
2019-02-05 14:41:39 +01:00
|
|
|
|
Note* n = f->note();
|
|
|
|
|
QRectF r = f->bbox().translated(f->pos() + n->pos() + n->chord()->pos() + s->pos() + s->measure()->pos());
|
2018-12-29 16:13:18 +01:00
|
|
|
|
system->staff(f->note()->chord()->vStaffIdx())->skyline().add(r);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-12 12:33:11 +02:00
|
|
|
|
}
|
2018-09-17 14:05:56 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
2018-12-14 18:02:46 +01:00
|
|
|
|
// layout articulations, tuplet
|
2018-09-17 14:05:56 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->elist()) {
|
|
|
|
|
if (!e || !e->isChordRest() || !score()->staff(e->staffIdx())->show())
|
2018-09-12 12:33:11 +02:00
|
|
|
|
continue;
|
2018-12-09 11:02:11 +01:00
|
|
|
|
ChordRest* cr = toChordRest(e);
|
2018-12-14 18:02:46 +01:00
|
|
|
|
// articulations
|
2019-02-19 00:56:36 +01:00
|
|
|
|
if (cr->isChord()) {
|
|
|
|
|
Chord* c = toChord(cr);
|
|
|
|
|
c->layoutArticulations();
|
|
|
|
|
c->layoutArticulations2();
|
|
|
|
|
}
|
2018-12-14 18:02:46 +01:00
|
|
|
|
// tuplets
|
2018-12-09 11:02:11 +01:00
|
|
|
|
// sanity check
|
2018-12-20 09:18:35 +01:00
|
|
|
|
if (notTopBeam(cr))
|
2018-09-18 11:34:11 +02:00
|
|
|
|
continue;
|
2018-12-09 11:02:11 +01:00
|
|
|
|
DurationElement* de = cr;
|
2018-09-12 12:33:11 +02:00
|
|
|
|
while (de->tuplet() && de->tuplet()->elements().front() == de) {
|
|
|
|
|
Tuplet* t = de->tuplet();
|
|
|
|
|
t->layout();
|
2018-12-29 16:13:18 +01:00
|
|
|
|
if (t->autoplace() && t->visible())
|
|
|
|
|
system->staff(t->staffIdx())->skyline().add(t->shape().translated(t->pos() + t->measure()->pos()));
|
2018-12-09 11:02:11 +01:00
|
|
|
|
de = t;
|
2018-09-12 12:33:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// layout slurs
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction stick = system->measures().front()->tick();
|
|
|
|
|
Fraction etick = system->measures().back()->endTick();
|
|
|
|
|
auto spanners = score()->spannerMap().findOverlapping(stick.ticks(), etick.ticks());
|
2018-09-11 16:26:27 +02:00
|
|
|
|
|
|
|
|
|
std::vector<Spanner*> spanner;
|
|
|
|
|
for (auto interval : spanners) {
|
|
|
|
|
Spanner* sp = interval.value;
|
2018-11-14 12:59:52 +01:00
|
|
|
|
sp->computeStartElement();
|
|
|
|
|
sp->computeEndElement();
|
2018-11-25 23:35:00 +01:00
|
|
|
|
lc.processedSpanners.insert(sp);
|
2018-09-11 16:26:27 +02:00
|
|
|
|
if (sp->tick() < etick && sp->tick2() >= stick) {
|
|
|
|
|
if (sp->isSlur())
|
|
|
|
|
spanner.push_back(sp);
|
2018-01-11 11:39:14 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
processLines(system, spanner, false);
|
2018-12-14 18:02:46 +01:00
|
|
|
|
for (auto s : spanner) {
|
|
|
|
|
Slur* slur = toSlur(s);
|
|
|
|
|
ChordRest* scr = s->startCR();
|
|
|
|
|
ChordRest* ecr = s->endCR();
|
2019-01-04 16:19:30 +01:00
|
|
|
|
if (scr && scr->isChord())
|
2018-12-14 18:02:46 +01:00
|
|
|
|
toChord(scr)->layoutArticulations3(slur);
|
2019-01-04 16:19:30 +01:00
|
|
|
|
if (ecr && ecr->isChord())
|
2018-12-14 18:02:46 +01:00
|
|
|
|
toChord(ecr)->layoutArticulations3(slur);
|
|
|
|
|
}
|
2018-01-11 11:39:14 +01:00
|
|
|
|
|
2017-12-06 15:06:32 +01:00
|
|
|
|
std::vector<Dynamic*> dynamics;
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->elist()) {
|
|
|
|
|
if (!e)
|
|
|
|
|
continue;
|
2018-09-17 14:05:56 +02:00
|
|
|
|
if (e->isChord()) {
|
|
|
|
|
Chord* c = toChord(e);
|
|
|
|
|
for (Chord* ch : c->graceNotes())
|
|
|
|
|
layoutTies(ch, system, stick);
|
|
|
|
|
layoutTies(c, system, stick);
|
2016-03-18 14:35:15 +01:00
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
}
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (e->isDynamic()) {
|
2018-09-11 16:26:27 +02:00
|
|
|
|
Dynamic* d = toDynamic(e);
|
|
|
|
|
d->layout();
|
|
|
|
|
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (e->visible() && d->autoplace()) {
|
2018-11-14 12:59:52 +01:00
|
|
|
|
d->doAutoplace();
|
|
|
|
|
dynamics.push_back(d);
|
2016-07-19 14:36:09 +02:00
|
|
|
|
}
|
2016-05-02 13:41:41 +02:00
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
else if (e->isFiguredBass())
|
|
|
|
|
e->layout();
|
2016-03-18 14:35:15 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-17 12:52:35 +02:00
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
// add dynamics shape to skyline
|
|
|
|
|
|
2017-12-06 15:06:32 +01:00
|
|
|
|
for (Dynamic* d : dynamics) {
|
|
|
|
|
int si = d->staffIdx();
|
|
|
|
|
Segment* s = d->segment();
|
|
|
|
|
Measure* m = s->measure();
|
2018-09-11 16:26:27 +02:00
|
|
|
|
system->staff(si)->skyline().add(d->shape().translated(d->pos() + s->pos() + m->pos()));
|
2017-12-06 15:06:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// layout SpannerSegments for current system
|
|
|
|
|
// ottavas, pedals, voltas are collected here, but layouted later
|
|
|
|
|
//-------------------------------------------------------------
|
2016-06-16 11:20:22 +02:00
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
spanner.clear();
|
2018-12-28 05:52:31 +01:00
|
|
|
|
std::vector<Spanner*> hairpins;
|
2018-09-11 16:26:27 +02:00
|
|
|
|
std::vector<Spanner*> ottavas;
|
|
|
|
|
std::vector<Spanner*> pedal;
|
2018-12-07 08:38:21 +01:00
|
|
|
|
std::vector<Spanner*> voltas;
|
2016-06-16 11:20:22 +02:00
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (auto interval : spanners) {
|
|
|
|
|
Spanner* sp = interval.value;
|
|
|
|
|
if (sp->tick() < etick && sp->tick2() > stick) {
|
|
|
|
|
if (sp->isOttava())
|
|
|
|
|
ottavas.push_back(sp);
|
|
|
|
|
else if (sp->isPedal())
|
|
|
|
|
pedal.push_back(sp);
|
2018-12-07 08:38:21 +01:00
|
|
|
|
else if (sp->isVolta())
|
|
|
|
|
voltas.push_back(sp);
|
2018-12-28 05:52:31 +01:00
|
|
|
|
else if (sp->isHairpin())
|
|
|
|
|
hairpins.push_back(sp);
|
2018-12-07 08:38:21 +01:00
|
|
|
|
else if (!sp->isSlur() && !sp->isVolta()) // slurs are already
|
2018-09-11 16:26:27 +02:00
|
|
|
|
spanner.push_back(sp);
|
2016-06-16 11:20:22 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-28 05:52:31 +01:00
|
|
|
|
processLines(system, hairpins, false);
|
2018-09-11 16:26:27 +02:00
|
|
|
|
processLines(system, spanner, false);
|
2017-07-04 16:43:25 +02:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
2018-12-07 08:38:21 +01:00
|
|
|
|
// Fermata, TremoloBar
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-12-07 08:38:21 +01:00
|
|
|
|
if (e->isFermata() || e->isTremoloBar())
|
2018-12-07 08:38:21 +01:00
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// Ottava, Pedal
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
processLines(system, ottavas, false);
|
|
|
|
|
processLines(system, pedal, true);
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// Lyric
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
layoutLyrics(system);
|
|
|
|
|
|
|
|
|
|
// here are lyrics dashes and melisma
|
|
|
|
|
for (Spanner* sp : _unmanagedSpanner) {
|
|
|
|
|
if (sp->tick() >= etick || sp->tick2() <= stick)
|
|
|
|
|
continue;
|
|
|
|
|
sp->layoutSystem(system);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//
|
|
|
|
|
// We need to known if we have FretDiagrams in the system to decide when to layout the Harmonies
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool hasFretDiagram = false;
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
|
if (e->isFretDiagram()) {
|
|
|
|
|
hasFretDiagram = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasFretDiagram)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
2018-12-07 08:38:21 +01:00
|
|
|
|
// Harmony, 1st place
|
|
|
|
|
// If we have FretDiagrams, we want the Harmony above this and
|
|
|
|
|
// above the volta, therefore we delay the layout.
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
2018-12-14 18:02:46 +01:00
|
|
|
|
if (!hasFretDiagram)
|
2018-12-07 08:38:21 +01:00
|
|
|
|
layoutHarmonies(sl);
|
2018-12-07 08:38:21 +01:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// StaffText, InstrumentChange
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
|
if (e->isStaffText() || e->isSystemText() || e->isInstrumentChange())
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// Jump, Marker
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (MeasureBase* mb : system->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
|
|
|
|
for (Element* e : m->el()) {
|
|
|
|
|
if (e->isJump() || e->isMarker())
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// TempoText
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-12-22 10:29:41 +01:00
|
|
|
|
if (e->isTempoText())
|
|
|
|
|
e->layout();
|
2018-12-07 08:38:21 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-07 08:38:21 +01:00
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// layout Voltas for current sytem
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
processLines(system, voltas, false);
|
|
|
|
|
|
2018-02-01 10:37:12 +01:00
|
|
|
|
//
|
2018-09-11 16:26:27 +02:00
|
|
|
|
// vertical align volta segments
|
|
|
|
|
//
|
2018-09-13 13:52:03 +02:00
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
|
std::vector<SpannerSegment*> voltaSegments;
|
|
|
|
|
for (SpannerSegment* ss : system->spannerSegments()) {
|
|
|
|
|
if (ss->isVoltaSegment() && ss->staffIdx() == staffIdx)
|
|
|
|
|
voltaSegments.push_back(ss);
|
|
|
|
|
}
|
2018-11-26 10:56:34 +01:00
|
|
|
|
while (!voltaSegments.empty()) {
|
|
|
|
|
// we assume voltas are sorted left to right (by tick values)
|
2018-09-13 13:52:03 +02:00
|
|
|
|
qreal y = 0;
|
2018-11-26 10:56:34 +01:00
|
|
|
|
int idx = 0;
|
|
|
|
|
Volta* prevVolta = 0;
|
|
|
|
|
for (SpannerSegment* ss : voltaSegments) {
|
|
|
|
|
Volta* volta = toVolta(ss->spanner());
|
|
|
|
|
if (prevVolta && prevVolta != volta) {
|
|
|
|
|
// check if volta is adjacent to prevVolta
|
|
|
|
|
if (prevVolta->tick2() != volta->tick())
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-11-13 14:50:28 +01:00
|
|
|
|
y = qMin(y, ss->rypos());
|
2018-11-26 10:56:34 +01:00
|
|
|
|
++idx;
|
|
|
|
|
prevVolta = volta;
|
|
|
|
|
}
|
2018-09-25 10:36:01 +02:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
for (int i = 0; i < idx; ++i) {
|
|
|
|
|
SpannerSegment* ss = voltaSegments[i];
|
|
|
|
|
ss->rypos() = y;
|
|
|
|
|
system->staff(staffIdx)->skyline().add(ss->shape().translated(ss->pos()));
|
|
|
|
|
}
|
2018-02-01 10:37:12 +01:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
voltaSegments.erase(voltaSegments.begin(), voltaSegments.begin() + idx);
|
2018-02-01 10:37:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// FretDiagram
|
|
|
|
|
//-------------------------------------------------------------
|
2018-02-01 10:37:12 +01:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
if (hasFretDiagram) {
|
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
|
|
|
|
if (e->isFretDiagram())
|
|
|
|
|
e->layout();
|
|
|
|
|
}
|
2018-01-22 16:50:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
// Harmony, 2nd place
|
|
|
|
|
// We have FretDiagrams, we want the Harmony above this and
|
|
|
|
|
// above the volta.
|
|
|
|
|
//-------------------------------------------------------------
|
2018-09-11 16:26:27 +02:00
|
|
|
|
|
2018-12-07 08:38:21 +01:00
|
|
|
|
layoutHarmonies(sl);
|
2018-09-11 16:26:27 +02:00
|
|
|
|
}
|
2017-09-19 15:20:06 +02:00
|
|
|
|
|
2018-09-03 09:54:09 +02:00
|
|
|
|
//-------------------------------------------------------------
|
2018-09-11 16:26:27 +02:00
|
|
|
|
// RehearsalMark
|
2018-09-03 09:54:09 +02:00
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
2018-09-11 16:26:27 +02:00
|
|
|
|
for (const Segment* s : sl) {
|
|
|
|
|
for (Element* e : s->annotations()) {
|
2018-09-17 12:44:37 +02:00
|
|
|
|
if (e->isRehearsalMark())
|
2018-09-11 16:26:27 +02:00
|
|
|
|
e->layout();
|
2018-09-03 09:54:09 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-03-29 16:01:44 +02:00
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// collectPage
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
void LayoutContext::collectPage()
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2018-03-27 15:36:00 +02:00
|
|
|
|
const qreal slb = score->styleP(Sid::staffLowerBorder);
|
2017-01-05 11:23:47 +01:00
|
|
|
|
bool breakPages = score->layoutMode() != LayoutMode::SYSTEM;
|
|
|
|
|
qreal y = prevSystem ? prevSystem->y() + prevSystem->height() : page->tm();
|
|
|
|
|
qreal ey = page->height() - page->bm();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
System* nextSystem = 0;
|
|
|
|
|
int systemIdx = -1;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
for (int k = 0;;++k) {
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
|
|
|
|
// calculate distance to previous system
|
|
|
|
|
//
|
|
|
|
|
qreal distance;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (prevSystem)
|
|
|
|
|
distance = prevSystem->minDistance(curSystem);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
else {
|
|
|
|
|
// this is the first system on page
|
2017-10-27 13:06:22 +02:00
|
|
|
|
if (curSystem->vbox())
|
|
|
|
|
distance = 0.0;
|
|
|
|
|
else {
|
2018-03-27 15:36:00 +02:00
|
|
|
|
distance = score->styleP(Sid::staffUpperBorder);
|
2018-09-11 16:26:27 +02:00
|
|
|
|
bool fixedDistance = false;
|
2019-02-23 03:55:29 +01:00
|
|
|
|
// TODO: curSystem->spacerDistance(true)
|
2017-10-27 13:06:22 +02:00
|
|
|
|
for (MeasureBase* mb : curSystem->measures()) {
|
|
|
|
|
if (mb->isMeasure()) {
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2019-02-23 03:55:29 +01:00
|
|
|
|
Spacer* sp = m->vspacerUp(0); // TODO: first visible?
|
2017-10-27 13:06:22 +02:00
|
|
|
|
if (sp) {
|
|
|
|
|
if (sp->spacerType() == SpacerType::FIXED) {
|
|
|
|
|
distance = sp->gap();
|
2018-09-11 16:26:27 +02:00
|
|
|
|
fixedDistance = true;
|
2017-10-27 13:06:22 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
distance = qMax(distance, sp->gap());
|
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
//TODO::ws distance = qMax(distance, -m->staffShape(0).top());
|
2017-10-27 13:06:22 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-11 16:26:27 +02:00
|
|
|
|
if (!fixedDistance)
|
2019-02-22 23:26:07 +01:00
|
|
|
|
distance = qMax(distance, curSystem->minTop());
|
2017-10-27 13:06:22 +02:00
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2018-07-04 16:22:33 +02:00
|
|
|
|
//TODO-ws ??
|
|
|
|
|
// distance += score->staves().front()->userDist();
|
2016-08-23 16:39:27 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
y += distance;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
curSystem->setPos(page->lm(), y);
|
|
|
|
|
page->appendSystem(curSystem);
|
|
|
|
|
y += curSystem->height();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//
|
|
|
|
|
// check for page break or if next system will fit on page
|
|
|
|
|
//
|
2018-12-11 15:16:37 +01:00
|
|
|
|
const bool rangeWasDone = rangeDone;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (rangeDone) {
|
2016-03-24 12:39:18 +01:00
|
|
|
|
// take next system unchanged
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (systemIdx > 0) {
|
|
|
|
|
nextSystem = score->systems().value(systemIdx++);
|
|
|
|
|
if (!nextSystem) {
|
|
|
|
|
// TODO: handle next movement
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nextSystem = systemList.empty() ? 0 : systemList.takeFirst();
|
|
|
|
|
if (nextSystem)
|
|
|
|
|
score->systems().append(nextSystem);
|
|
|
|
|
else if (score->isMaster()) {
|
|
|
|
|
MasterScore* ms = static_cast<MasterScore*>(score)->next();
|
|
|
|
|
if (ms) {
|
|
|
|
|
score = ms;
|
|
|
|
|
systemIdx = 0;
|
|
|
|
|
nextSystem = score->systems().value(systemIdx++);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-24 12:39:18 +01:00
|
|
|
|
}
|
2016-05-25 10:51:59 +02:00
|
|
|
|
else {
|
2017-01-05 11:23:47 +01:00
|
|
|
|
nextSystem = score->collectSystem(*this);
|
|
|
|
|
if (!nextSystem && score->isMaster()) {
|
|
|
|
|
MasterScore* ms = static_cast<MasterScore*>(score)->next();
|
|
|
|
|
if (ms) {
|
|
|
|
|
score = ms;
|
|
|
|
|
QList<System*>& systems = ms->systems();
|
|
|
|
|
if (systems.empty() || systems.front()->measures().empty()) {
|
2017-01-31 12:21:44 +01:00
|
|
|
|
systemList = systems;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
systems.clear();
|
|
|
|
|
measureNo = 0;
|
|
|
|
|
startWithLongNames = true;
|
|
|
|
|
firstSystem = true;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
tick = Fraction(0,1);
|
2017-01-05 11:23:47 +01:00
|
|
|
|
prevMeasure = 0;
|
|
|
|
|
curMeasure = 0;
|
|
|
|
|
nextMeasure = ms->measures()->first();
|
|
|
|
|
ms->getNextMeasure(*this);
|
|
|
|
|
nextSystem = ms->collectSystem(*this);
|
2018-03-27 15:36:00 +02:00
|
|
|
|
ms->setScoreFont(ScoreFont::fontFactory(ms->styleSt(Sid::MusicalSymbolFont)));
|
2017-01-05 11:23:47 +01:00
|
|
|
|
ms->setNoteHeadWidth(ms->scoreFont()->width(SymId::noteheadBlack, ms->spatium() / SPATIUM20));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
rangeDone = true;
|
|
|
|
|
systemIdx = 0;
|
|
|
|
|
nextSystem = score->systems().value(systemIdx++);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-25 10:51:59 +02:00
|
|
|
|
}
|
2017-01-05 11:23:47 +01:00
|
|
|
|
prevSystem = curSystem;
|
|
|
|
|
Q_ASSERT(curSystem != nextSystem);
|
|
|
|
|
curSystem = nextSystem;
|
|
|
|
|
|
|
|
|
|
bool breakPage = !curSystem || (breakPages && prevSystem->pageBreak());
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
|
|
if (!breakPage) {
|
2017-01-05 11:23:47 +01:00
|
|
|
|
qreal dist = prevSystem->minDistance(curSystem) + curSystem->height();
|
2018-06-27 11:16:56 +02:00
|
|
|
|
Box* vbox = curSystem->vbox();
|
2019-02-23 03:55:29 +01:00
|
|
|
|
if (vbox) {
|
2016-03-02 13:20:19 +01:00
|
|
|
|
dist += vbox->bottomGap();
|
2019-02-23 03:55:29 +01:00
|
|
|
|
}
|
|
|
|
|
else if (!prevSystem->hasFixedDownDistance()) {
|
|
|
|
|
qreal margin = qMax(curSystem->minBottom(), curSystem->spacerDistance(false));
|
|
|
|
|
dist += qMax(margin, slb);
|
|
|
|
|
}
|
|
|
|
|
breakPage = (y + dist) >= ey && breakPages;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
|
|
|
|
if (breakPage) {
|
2019-02-23 03:55:29 +01:00
|
|
|
|
qreal dist = qMax(prevSystem->minBottom(), prevSystem->spacerDistance(false));
|
|
|
|
|
dist = qMax(dist, slb);
|
2016-01-04 14:48:58 +01:00
|
|
|
|
layoutPage(page, ey - (y + dist));
|
2018-12-11 15:16:37 +01:00
|
|
|
|
// We don't accept current system to this page
|
|
|
|
|
// so rollback rangeDone variable as well.
|
|
|
|
|
rangeDone = rangeWasDone;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
break;
|
2016-01-04 14:48:58 +01:00
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
|
|
2018-09-05 12:55:56 +02:00
|
|
|
|
qreal height = 0;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction stick = Fraction(-1,1);
|
2016-03-02 13:20:19 +01:00
|
|
|
|
for (System* s : page->systems()) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
Score* currentScore = s->score();
|
2018-09-05 12:55:56 +02:00
|
|
|
|
height += s->rypos();
|
2016-03-02 13:20:19 +01:00
|
|
|
|
for (MeasureBase* mb : s->measures()) {
|
|
|
|
|
if (!mb->isMeasure())
|
|
|
|
|
continue;
|
|
|
|
|
Measure* m = toMeasure(mb);
|
2019-01-30 15:13:54 +01:00
|
|
|
|
if (stick == Fraction(-1,1))
|
2016-03-02 13:20:19 +01:00
|
|
|
|
stick = m->tick();
|
|
|
|
|
|
2018-07-10 14:57:05 +02:00
|
|
|
|
for (int track = 0; track < currentScore->ntracks(); ++track) {
|
2016-03-02 13:20:19 +01:00
|
|
|
|
for (Segment* segment = m->first(); segment; segment = segment->next()) {
|
|
|
|
|
Element* e = segment->element(track);
|
|
|
|
|
if (!e)
|
|
|
|
|
continue;
|
|
|
|
|
if (e->isChordRest()) {
|
2018-07-10 14:57:05 +02:00
|
|
|
|
if (!currentScore->staff(track2staff(track))->show())
|
2016-03-02 13:20:19 +01:00
|
|
|
|
continue;
|
|
|
|
|
ChordRest* cr = toChordRest(e);
|
2018-09-18 11:34:11 +02:00
|
|
|
|
if (notTopBeam(cr)) { // layout cross staff beams
|
2016-03-02 13:20:19 +01:00
|
|
|
|
cr->beam()->layout();
|
|
|
|
|
|
2018-09-18 11:34:11 +02:00
|
|
|
|
// fix layout of tuplets
|
|
|
|
|
DurationElement* de = cr;
|
|
|
|
|
while (de->tuplet() && de->tuplet()->elements().front() == de) {
|
|
|
|
|
Tuplet* t = de->tuplet();
|
|
|
|
|
t->layout();
|
|
|
|
|
m->system()->staff(t->staffIdx())->skyline().add(t->shape().translated(t->measure()->pos()));
|
|
|
|
|
de = de->tuplet();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
|
if (cr->isChord()) {
|
|
|
|
|
Chord* c = toChord(cr);
|
|
|
|
|
for (Chord* cc : c->graceNotes()) {
|
|
|
|
|
if (cc->beam() && cc->beam()->elements().front() == cc)
|
|
|
|
|
cc->beam()->layout();
|
|
|
|
|
for (Note* n : cc->notes()) {
|
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
|
if (tie)
|
|
|
|
|
tie->layout();
|
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
|
sp->layout();
|
|
|
|
|
}
|
2018-07-10 14:57:05 +02:00
|
|
|
|
for (Element* element : cc->el()) {
|
|
|
|
|
if (element->isSlur())
|
|
|
|
|
element->layout();
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c->layoutArpeggio2();
|
|
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
|
Tie* tie = n->tieFor();
|
|
|
|
|
if (tie)
|
|
|
|
|
tie->layout();
|
|
|
|
|
for (Spanner* sp : n->spannerFor())
|
|
|
|
|
sp->layout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (e->isBarLine())
|
2016-12-23 12:05:18 +01:00
|
|
|
|
toBarLine(e)->layout2();
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m->layout2();
|
2016-06-02 14:49:05 +02:00
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
2018-09-07 17:12:35 +02:00
|
|
|
|
|
2018-09-05 12:55:56 +02:00
|
|
|
|
if (score->systemMode())
|
|
|
|
|
page->bbox().setRect(0.0, 0.0, score->loWidth(), height);
|
2018-09-07 17:12:35 +02:00
|
|
|
|
|
2017-09-07 13:25:27 +02:00
|
|
|
|
page->rebuildBspTree();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-01-04 14:48:58 +01:00
|
|
|
|
// doLayout
|
2017-01-05 11:23:47 +01:00
|
|
|
|
// do a complete (re-) layout
|
2012-05-26 14:26:10 +02:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
void Score::doLayout()
|
2012-05-26 14:26:10 +02:00
|
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
|
doLayoutRange(Fraction(0,1), Fraction(-1,1));
|
2016-03-02 13:20:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// doLayoutRange
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
|
void Score::doLayoutRange(const Fraction& st, const Fraction& et)
|
2016-03-02 13:20:19 +01:00
|
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
|
Fraction stick(st);
|
|
|
|
|
Fraction etick(et);
|
|
|
|
|
Q_ASSERT(!(stick == Fraction(-1,1) && etick == Fraction(-1,1)));
|
|
|
|
|
|
2018-10-15 05:08:49 +02:00
|
|
|
|
if (!last() || (lineMode() && !firstMeasure())) {
|
2018-11-05 18:51:35 +01:00
|
|
|
|
qDebug("empty score");
|
2017-09-14 10:30:28 +02:00
|
|
|
|
qDeleteAll(_systems);
|
|
|
|
|
_systems.clear();
|
|
|
|
|
qDeleteAll(pages());
|
|
|
|
|
pages().clear();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-12-09 11:02:11 +01:00
|
|
|
|
// if (!_systems.isEmpty())
|
|
|
|
|
// return;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
bool layoutAll = stick <= Fraction(0,1) && (etick < Fraction(0,1) || etick >= last()->endTick());
|
|
|
|
|
if (stick < Fraction(0,1))
|
|
|
|
|
stick = Fraction(0,1);
|
|
|
|
|
if (etick < Fraction(0,1))
|
2017-09-07 13:25:27 +02:00
|
|
|
|
etick = last()->endTick();
|
2017-01-05 11:23:47 +01:00
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
|
LayoutContext lc;
|
2016-03-24 12:39:18 +01:00
|
|
|
|
lc.endTick = etick;
|
2018-03-27 15:36:00 +02:00
|
|
|
|
_scoreFont = ScoreFont::fontFactory(style().value(Sid::MusicalSymbolFont).toString());
|
2016-03-02 13:20:19 +01:00
|
|
|
|
_noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, spatium() / SPATIUM20);
|
|
|
|
|
|
2016-03-18 09:29:16 +01:00
|
|
|
|
if (cmdState().layoutFlags & LayoutFlag::FIX_PITCH_VELO)
|
2016-03-02 13:20:19 +01:00
|
|
|
|
updateVelo();
|
2018-12-24 12:49:07 +01:00
|
|
|
|
#if 0 // TODO: needed? It was introduced in ab9774ec4098512068b8ef708167d9aa6e702c50
|
2016-03-18 09:29:16 +01:00
|
|
|
|
if (cmdState().layoutFlags & LayoutFlag::PLAY_EVENTS)
|
2016-03-02 13:20:19 +01:00
|
|
|
|
createPlayEvents();
|
2018-12-24 12:49:07 +01:00
|
|
|
|
#endif
|
2016-03-02 13:20:19 +01:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------
|
2016-03-02 13:20:19 +01:00
|
|
|
|
// initialize layout context lc
|
2016-01-04 14:48:58 +01:00
|
|
|
|
//---------------------------------------------------
|
2012-08-01 18:00:27 +02:00
|
|
|
|
|
2017-01-16 20:51:12 +01:00
|
|
|
|
MeasureBase* m = tick2measure(stick);
|
|
|
|
|
if (m == 0)
|
|
|
|
|
m = first();
|
2016-03-18 09:29:16 +01:00
|
|
|
|
// start layout one measure earlier to handle clefs and cautionary elements
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (m->prevMeasureMM())
|
2016-07-08 11:57:33 +02:00
|
|
|
|
m = m->prevMeasureMM();
|
2017-01-16 20:51:12 +01:00
|
|
|
|
else if (m->prev())
|
|
|
|
|
m = m->prev();
|
2017-02-23 11:41:53 +01:00
|
|
|
|
while (!m->isMeasure() && m->prev())
|
|
|
|
|
m = m->prev();
|
2016-07-10 21:27:25 +02:00
|
|
|
|
|
|
|
|
|
// if the first measure of the score is part of a multi measure rest
|
|
|
|
|
// m->system() will return a nullptr. We need to find the multi measure
|
|
|
|
|
// rest which replaces the measure range
|
2017-01-05 11:23:47 +01:00
|
|
|
|
|
2017-03-14 17:00:38 +01:00
|
|
|
|
if (!m->system() && m->isMeasure() && toMeasure(m)->hasMMRest()) {
|
2018-11-27 22:49:18 +01:00
|
|
|
|
qDebug(" don’t start with mmrest");
|
2017-01-16 20:51:12 +01:00
|
|
|
|
m = toMeasure(m)->mmRest();
|
2017-03-14 17:00:38 +01:00
|
|
|
|
}
|
2016-02-06 11:41:16 +01:00
|
|
|
|
|
2017-06-02 10:27:32 +02:00
|
|
|
|
// qDebug("start <%s> tick %d, system %p", m->name(), m->tick(), m->system());
|
2017-01-05 11:23:47 +01:00
|
|
|
|
lc.score = m->score();
|
2017-01-16 20:51:12 +01:00
|
|
|
|
|
2018-11-05 18:51:35 +01:00
|
|
|
|
if (lineMode()) {
|
|
|
|
|
lc.prevMeasure = 0;
|
2018-11-21 10:18:28 +01:00
|
|
|
|
lc.nextMeasure = _showVBox ? first() : firstMeasure();
|
2018-11-05 18:51:35 +01:00
|
|
|
|
layoutLinear(layoutAll, lc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-03-14 17:00:38 +01:00
|
|
|
|
if (!layoutAll && m->system()) {
|
|
|
|
|
System* system = m->system();
|
2017-01-19 16:06:40 +01:00
|
|
|
|
int systemIndex = _systems.indexOf(system);
|
2017-01-05 11:23:47 +01:00
|
|
|
|
lc.page = system->page();
|
|
|
|
|
lc.curPage = pageIdx(lc.page);
|
|
|
|
|
if (lc.curPage == -1)
|
|
|
|
|
lc.curPage = 0;
|
2017-01-16 20:51:12 +01:00
|
|
|
|
lc.curSystem = system;
|
2017-01-05 11:23:47 +01:00
|
|
|
|
lc.systemList = _systems.mid(systemIndex);
|
2017-01-19 16:06:40 +01:00
|
|
|
|
|
|
|
|
|
if (systemIndex == 0)
|
2018-11-21 10:18:28 +01:00
|
|
|
|
lc.nextMeasure = _showVBox ? first() : firstMeasure();
|
2017-03-14 17:00:38 +01:00
|
|
|
|
else {
|
|
|
|
|
System* prevSystem = _systems[systemIndex-1];
|
|
|
|
|
lc.nextMeasure = prevSystem->measures().back()->next();
|
|
|
|
|
}
|
2017-01-19 16:06:40 +01:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
_systems.erase(_systems.begin() + systemIndex, _systems.end());
|
2017-01-19 16:06:40 +01:00
|
|
|
|
if (!lc.nextMeasure->prevMeasure()) {
|
2017-01-05 11:23:47 +01:00
|
|
|
|
lc.measureNo = 0;
|
2019-01-30 15:13:54 +01:00
|
|
|
|
lc.tick = Fraction(0,1);
|
2017-01-19 16:06:40 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2019-01-08 19:55:02 +01:00
|
|
|
|
if (lc.nextMeasure->prevMeasure()->sectionBreak())
|
|
|
|
|
lc.measureNo = 0;
|
|
|
|
|
else
|
|
|
|
|
lc.measureNo = lc.nextMeasure->prevMeasure()->no() + 1; // will be adjusted later with respect
|
|
|
|
|
// to the user-defined offset.
|
2017-01-19 16:06:40 +01:00
|
|
|
|
lc.tick = lc.nextMeasure->tick();
|
|
|
|
|
}
|
2017-01-05 11:23:47 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2018-11-05 18:51:35 +01:00
|
|
|
|
// qDebug("layoutAll, systems %p %d", &_systems, int(_systems.size()));
|
2017-03-14 17:00:38 +01:00
|
|
|
|
//lc.measureNo = 0;
|
|
|
|
|
//lc.tick = 0;
|
|
|
|
|
// qDeleteAll(_systems);
|
|
|
|
|
// _systems.clear();
|
|
|
|
|
// lc.systemList = _systems;
|
|
|
|
|
// _systems.clear();
|
|
|
|
|
|
|
|
|
|
for (System* s : _systems) {
|
2017-03-21 12:11:45 +01:00
|
|
|
|
for (Bracket* b : s->brackets()) {
|
2017-03-31 13:03:15 +02:00
|
|
|
|
if (b->selected()) {
|
2018-11-30 09:04:24 +01:00
|
|
|
|
_selection.remove(b);
|
2017-03-31 13:03:15 +02:00
|
|
|
|
setSelectionChanged(true);
|
|
|
|
|
}
|
2017-03-21 12:11:45 +01:00
|
|
|
|
}
|
2018-08-16 16:42:55 +02:00
|
|
|
|
// for (SpannerSegment* ss : s->spannerSegments())
|
|
|
|
|
// ss->setParent(0);
|
2018-01-25 13:04:06 +01:00
|
|
|
|
s->setParent(nullptr);
|
2017-03-14 17:00:38 +01:00
|
|
|
|
}
|
|
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
|
|
|
|
mb->setSystem(0);
|
|
|
|
|
if (mb->isMeasure() && toMeasure(mb)->mmRest())
|
|
|
|
|
toMeasure(mb)->mmRest()->setSystem(0);
|
|
|
|
|
}
|
2018-11-05 10:29:06 +01:00
|
|
|
|
qDeleteAll(_systems);
|
2017-03-14 17:00:38 +01:00
|
|
|
|
_systems.clear();
|
|
|
|
|
|
|
|
|
|
qDeleteAll(pages());
|
|
|
|
|
pages().clear();
|
|
|
|
|
|
2018-11-21 10:18:28 +01:00
|
|
|
|
lc.nextMeasure = _showVBox ? first() : firstMeasure();
|
2017-01-05 11:23:47 +01:00
|
|
|
|
}
|
2012-10-23 22:16:31 +02:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
lc.prevMeasure = 0;
|
2012-10-26 11:42:54 +02:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
getNextMeasure(lc);
|
|
|
|
|
lc.curSystem = collectSystem(lc);
|
|
|
|
|
|
|
|
|
|
lc.layout();
|
2012-10-26 11:42:54 +02:00
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
|
for (MuseScoreView* v : viewer)
|
|
|
|
|
v->layoutChanged();
|
2012-10-26 11:42:54 +02:00
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// layout
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void LayoutContext::layout()
|
|
|
|
|
{
|
2018-10-15 16:42:04 +02:00
|
|
|
|
do {
|
|
|
|
|
getNextPage();
|
2017-01-05 11:23:47 +01:00
|
|
|
|
collectPage();
|
2018-10-15 16:42:04 +02:00
|
|
|
|
} while (curSystem && !(rangeDone && page->system(0)->measures().back()->tick() > endTick)); // FIXME: perhaps the first measure was meant? Or last system?
|
2017-01-05 11:23:47 +01:00
|
|
|
|
if (!curSystem) {
|
2018-11-08 12:45:53 +01:00
|
|
|
|
// The end of the score. The remaining systems are not needed...
|
|
|
|
|
qDeleteAll(systemList);
|
|
|
|
|
systemList.clear();
|
|
|
|
|
// ...and the remaining pages too
|
|
|
|
|
while (score->npages() > curPage)
|
|
|
|
|
delete score->pages().takeLast();
|
2017-01-05 11:23:47 +01:00
|
|
|
|
}
|
2018-07-27 21:53:33 +02:00
|
|
|
|
else {
|
|
|
|
|
Page* p = curSystem->page();
|
|
|
|
|
if (p && (p != page))
|
|
|
|
|
p->rebuildBspTree();
|
|
|
|
|
}
|
2017-01-05 11:23:47 +01:00
|
|
|
|
score->systems().append(systemList); // TODO
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 23:35:00 +01:00
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// LayoutContext::~LayoutContext
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
LayoutContext::~LayoutContext()
|
|
|
|
|
{
|
|
|
|
|
for (Spanner* s : processedSpanners)
|
|
|
|
|
s->layoutSystemsDone();
|
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
|
}
|