removed libmscore/layout.cpp

This commit is contained in:
Igor Korsukov 2021-08-20 14:10:03 +02:00
parent ea39f9f3af
commit 5f28d661f1
30 changed files with 933 additions and 1114 deletions

View file

@ -23,6 +23,7 @@
#include <QCoreApplication>
#include <QCloseEvent>
#include <QWindow>
#include "translation.h"

View file

@ -1,492 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cmath>
#include <QtMath>
#include "style/style.h"
#include "accidental.h"
#include "barline.h"
#include "beam.h"
#include "box.h"
#include "chord.h"
#include "clef.h"
#include "element.h"
#include "fingering.h"
#include "glissando.h"
#include "harmony.h"
#include "key.h"
#include "keysig.h"
#include "layoutbreak.h"
#include "layout.h"
#include "lyrics.h"
#include "marker.h"
#include "measure.h"
#include "mmrestrange.h"
#include "mmrest.h"
#include "mscore.h"
#include "notedot.h"
#include "note.h"
#include "ottava.h"
#include "page.h"
#include "part.h"
#include "measurerepeat.h"
#include "score.h"
#include "scorefont.h"
#include "segment.h"
#include "sig.h"
#include "slur.h"
#include "staff.h"
#include "stem.h"
#include "stemslash.h"
#include "sticking.h"
#include "sym.h"
#include "system.h"
#include "text.h"
#include "tie.h"
#include "timesig.h"
#include "tremolo.h"
#include "tuplet.h"
#include "undo.h"
#include "utils.h"
#include "volta.h"
#include "breath.h"
#include "tempotext.h"
#include "systemdivider.h"
#include "hook.h"
#include "ambitus.h"
#include "hairpin.h"
#include "stafflines.h"
#include "articulation.h"
#include "bracket.h"
#include "spacer.h"
#include "fermata.h"
#include "measurenumber.h"
#include "masterscore.h"
using namespace mu;
namespace Ms {
// #define PAGE_DEBUG
#ifdef PAGE_DEBUG
#define PAGEDBG(...) qDebug(__VA_ARGS__)
#else
#define PAGEDBG(...) ;
#endif
//---------------------------------------------------------
// almostZero
//---------------------------------------------------------
static bool inline almostZero(qreal value)
{
// 1e-3 is close enough to zero to see it as zero.
return value > -1e-3 && value < 1e-3;
}
//---------------------------------------------------------
// relayoutForStyles
/// some styles can't properly apply if score hasn't been laid out yet,
/// so temporarily disable them and then reenable after layout
/// (called during score load)
//---------------------------------------------------------
void Score::relayoutForStyles()
{
std::vector<Sid> stylesToTemporarilyDisable;
for (Sid sid : { Sid::createMultiMeasureRests, Sid::mrNumberSeries }) {
// only necessary if boolean style is true
if (styleB(sid)) {
stylesToTemporarilyDisable.push_back(sid);
}
}
if (!stylesToTemporarilyDisable.empty()) {
for (Sid sid : stylesToTemporarilyDisable) {
style().set(sid, false); // temporarily disable
}
doLayout();
for (Sid sid : stylesToTemporarilyDisable) {
style().set(sid, true); // and immediately reenable
}
}
}
//---------------------------------------------------------
// createMMRest
// create a multimeasure rest
// from firstMeasure to lastMeasure (inclusive)
//---------------------------------------------------------
void Score::createMMRest(Measure* firstMeasure, Measure* lastMeasure, const Fraction& len)
{
int numMeasuresInMMRest = 1;
if (firstMeasure != lastMeasure) {
for (Measure* m = firstMeasure->nextMeasure(); m; m = m->nextMeasure()) {
++numMeasuresInMMRest;
m->setMMRestCount(-1);
if (m->mmRest()) {
undo(new ChangeMMRest(m, 0));
}
if (m == lastMeasure) {
break;
}
}
}
// mmrMeasure coexists with n undisplayed measures of rests
Measure* mmrMeasure = firstMeasure->mmRest();
if (mmrMeasure) {
// reuse existing mmrest
if (mmrMeasure->ticks() != len) {
Segment* s = mmrMeasure->findSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
// adjust length
mmrMeasure->setTicks(len);
// move existing end barline
if (s) {
s->setRtick(len);
}
}
mmrMeasure->removeSystemTrailer();
} else {
mmrMeasure = new Measure(this);
mmrMeasure->setTicks(len);
mmrMeasure->setTick(firstMeasure->tick());
undo(new ChangeMMRest(firstMeasure, mmrMeasure));
}
mmrMeasure->setTimesig(firstMeasure->timesig());
mmrMeasure->setPageBreak(lastMeasure->pageBreak());
mmrMeasure->setLineBreak(lastMeasure->lineBreak());
mmrMeasure->setMMRestCount(numMeasuresInMMRest);
mmrMeasure->setNo(firstMeasure->no());
//
// set mmrMeasure with same barline as last underlying measure
//
Segment* lastMeasureEndBarlineSeg = lastMeasure->findSegmentR(SegmentType::EndBarLine, lastMeasure->ticks());
if (lastMeasureEndBarlineSeg) {
Segment* mmrEndBarlineSeg = mmrMeasure->undoGetSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
Element* e = lastMeasureEndBarlineSeg->element(staffIdx * VOICES);
if (e) {
bool generated = e->generated();
if (!mmrEndBarlineSeg->element(staffIdx * VOICES)) {
Element* eClone = generated ? e->clone() : e->linkedClone();
eClone->setGenerated(generated);
eClone->setParent(mmrEndBarlineSeg);
undoAddElement(eClone);
} else {
BarLine* mmrEndBarline = toBarLine(mmrEndBarlineSeg->element(staffIdx * VOICES));
BarLine* lastMeasureEndBarline = toBarLine(e);
if (!generated && !mmrEndBarline->links()) {
undo(new Link(mmrEndBarline, lastMeasureEndBarline));
}
if (mmrEndBarline->barLineType() != lastMeasureEndBarline->barLineType()) {
// change directly when generating mmrests, do not change underlying measures or follow links
undo(new ChangeProperty(mmrEndBarline, Pid::BARLINE_TYPE,
QVariant::fromValue(lastMeasureEndBarline->barLineType()),
PropertyFlags::NOSTYLE));
undo(new ChangeProperty(mmrEndBarline, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
}
}
}
}
}
//
// if last underlying measure ends with clef change, show same at end of mmrest
//
Segment* lastMeasureClefSeg = lastMeasure->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef,
lastMeasure->ticks());
if (lastMeasureClefSeg) {
Segment* mmrClefSeg = mmrMeasure->undoGetSegment(lastMeasureClefSeg->segmentType(), lastMeasure->endTick());
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
const int track = staff2track(staffIdx);
Element* e = lastMeasureClefSeg->element(track);
if (e && e->isClef()) {
Clef* lastMeasureClef = toClef(e);
if (!mmrClefSeg->element(track)) {
Clef* mmrClef = lastMeasureClef->generated() ? lastMeasureClef->clone() : toClef(
lastMeasureClef->linkedClone());
mmrClef->setParent(mmrClefSeg);
undoAddElement(mmrClef);
} else {
Clef* mmrClef = toClef(mmrClefSeg->element(track));
mmrClef->setClefType(lastMeasureClef->clefType());
mmrClef->setShowCourtesy(lastMeasureClef->showCourtesy());
}
}
}
}
mmrMeasure->setRepeatStart(firstMeasure->repeatStart() || lastMeasure->repeatStart());
mmrMeasure->setRepeatEnd(firstMeasure->repeatEnd() || lastMeasure->repeatEnd());
mmrMeasure->setSectionBreak(lastMeasure->sectionBreak());
//
// copy markers to mmrMeasure
//
ElementList oldList = mmrMeasure->takeElements();
ElementList newList = lastMeasure->el();
for (Element* e : firstMeasure->el()) {
if (e->isMarker()) {
newList.push_back(e);
}
}
for (Element* e : newList) {
bool found = false;
for (Element* ee : oldList) {
if (ee->type() == e->type() && ee->subtype() == e->subtype()) {
mmrMeasure->add(ee);
auto i = std::find(oldList.begin(), oldList.end(), ee);
if (i != oldList.end()) {
oldList.erase(i);
}
found = true;
break;
}
}
if (!found) {
mmrMeasure->add(e->clone());
}
}
for (Element* e : oldList) {
delete e;
}
Segment* s = mmrMeasure->undoGetSegmentR(SegmentType::ChordRest, Fraction(0, 1));
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
if (s->element(track) == 0) {
MMRest* mmr = new MMRest(this);
mmr->setDurationType(TDuration::DurationType::V_MEASURE);
mmr->setTicks(mmrMeasure->ticks());
mmr->setTrack(track);
mmr->setParent(s);
undo(new AddElement(mmr));
}
}
//
// further check for clefs
//
Segment* underlyingSeg = lastMeasure->findSegmentR(SegmentType::Clef, lastMeasure->ticks());
Segment* mmrSeg = mmrMeasure->findSegment(SegmentType::Clef, lastMeasure->endTick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::Clef, lastMeasure->ticks());
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setTrailer(underlyingSeg->trailer());
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
Clef* clef = toClef(underlyingSeg->element(track));
if (clef) {
if (mmrSeg->element(track) == 0) {
mmrSeg->add(clef->clone());
} else {
//TODO: check if same clef
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
undo(new RemoveElement(mmrSeg));
}
//
// check for time signature
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegment(SegmentType::TimeSig, firstMeasure->tick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::TimeSig, Fraction(0, 1));
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setHeader(underlyingSeg->header());
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
TimeSig* underlyingTimeSig = toTimeSig(underlyingSeg->element(track));
if (underlyingTimeSig) {
TimeSig* mmrTimeSig = toTimeSig(mmrSeg->element(track));
if (!mmrTimeSig) {
mmrTimeSig = underlyingTimeSig->generated() ? underlyingTimeSig->clone() : toTimeSig(
underlyingTimeSig->linkedClone());
mmrTimeSig->setParent(mmrSeg);
undo(new AddElement(mmrTimeSig));
} else {
mmrTimeSig->setSig(underlyingTimeSig->sig(), underlyingTimeSig->timeSigType());
mmrTimeSig->layout();
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
undo(new RemoveElement(mmrSeg));
}
//
// check for ambitus
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::Ambitus, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegment(SegmentType::Ambitus, firstMeasure->tick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::Ambitus, Fraction(0, 1));
}
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
Ambitus* underlyingAmbitus = toAmbitus(underlyingSeg->element(track));
if (underlyingAmbitus) {
Ambitus* mmrAmbitus = toAmbitus(mmrSeg->element(track));
if (!mmrAmbitus) {
mmrAmbitus = underlyingAmbitus->clone();
mmrAmbitus->setParent(mmrSeg);
undo(new AddElement(mmrAmbitus));
} else {
mmrAmbitus->initFrom(underlyingAmbitus);
mmrAmbitus->layout();
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
undo(new RemoveElement(mmrSeg));
}
//
// check for key signature
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::KeySig, Fraction(0, 1));
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setHeader(underlyingSeg->header());
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
int track = staffIdx * VOICES;
KeySig* underlyingKeySig = toKeySig(underlyingSeg->element(track));
if (underlyingKeySig) {
KeySig* mmrKeySig = toKeySig(mmrSeg->element(track));
if (!mmrKeySig) {
mmrKeySig = underlyingKeySig->generated() ? underlyingKeySig->clone() : toKeySig(
underlyingKeySig->linkedClone());
mmrKeySig->setParent(mmrSeg);
mmrKeySig->setGenerated(true);
undo(new AddElement(mmrKeySig));
} else {
if (!(mmrKeySig->keySigEvent() == underlyingKeySig->keySigEvent())) {
bool addKey = underlyingKeySig->isChange();
undo(new ChangeKeySig(mmrKeySig, underlyingKeySig->keySigEvent(), mmrKeySig->showCourtesy(),
addKey));
}
}
}
}
} else if (mmrSeg) {
mmrSeg->setEnabled(false);
// TODO: remove elements from mmrSeg, then delete mmrSeg
// previously we removed the segment if not empty,
// but this resulted in "stale" keysig in mmrest after removed from underlying measure
//undo(new RemoveElement(mmrSeg));
}
mmrMeasure->checkHeader();
mmrMeasure->checkTrailer();
//
// check for rehearsal mark etc.
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
if (underlyingSeg) {
// clone elements from underlying measure to mmr
for (Element* e : underlyingSeg->annotations()) {
// look at elements in underlying measure
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
continue;
}
// try to find a match in mmr
bool found = false;
for (Element* ee : s->annotations()) {
if (e->linkList().contains(ee)) {
found = true;
break;
}
}
// add to mmr if no match found
if (!found) {
Element* eClone = e->linkedClone();
eClone->setParent(s);
undo(new AddElement(eClone));
}
}
// remove stray elements (possibly leftover from a previous layout of this mmr)
// this should not happen since the elements are linked?
for (Element* e : s->annotations()) {
// look at elements in mmr
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
continue;
}
// try to find a match in underlying measure
bool found = false;
for (Element* ee : underlyingSeg->annotations()) {
if (e->linkList().contains(ee)) {
found = true;
break;
}
}
// remove from mmr if no match found
if (!found) {
undo(new RemoveElement(e));
}
}
}
MeasureBase* nm = _showVBox ? lastMeasure->next() : lastMeasure->nextMeasure();
mmrMeasure->setNext(nm);
mmrMeasure->setPrev(firstMeasure->prev());
}
//---------------------------------------------------------
// doLayout
// do a complete (re-) layout
//---------------------------------------------------------
void Score::doLayout()
{
doLayoutRange(Fraction(0, 1), Fraction(-1, 1));
}
void Score::doLayoutRange(const Fraction& st, const Fraction& et)
{
m_layout.doLayoutRange(st, et);
}
}

View file

@ -1,26 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __LAYOUT_H__
#define __LAYOUT_H__
#endif

View file

@ -42,8 +42,8 @@
#include "../stafflines.h"
#include "../tuplet.h"
#include "../tie.h"
#include "../layout.h"
#include "layoutcontext.h"
#include "layoutmeasure.h"
using namespace mu::engraving;
@ -71,175 +71,6 @@ static bool inline almostZero(qreal value)
return value > -1e-3 && value < 1e-3;
}
//---------------------------------------------------------
// validMMRestMeasure
// return true if this might be a measure in a
// multi measure rest
//---------------------------------------------------------
static bool validMMRestMeasure(Measure* m)
{
if (m->irregular()) {
return false;
}
int n = 0;
for (Segment* s = m->first(); s; s = s->next()) {
for (Element* e : s->annotations()) {
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
return false;
}
}
if (s->isChordRestType()) {
bool restFound = false;
int tracks = m->score()->ntracks();
for (int track = 0; track < tracks; ++track) {
if ((track % VOICES) == 0 && !m->score()->staff(track / VOICES)->show()) {
track += VOICES - 1;
continue;
}
if (s->element(track)) {
if (!s->element(track)->isRest()) {
return false;
}
restFound = true;
}
}
for (Element* e : s->annotations()) {
if (e->isFermata()) {
return false;
}
}
if (restFound) {
++n;
}
// measure is not empty if there is more than one rest
if (n > 1) {
return false;
}
}
}
return true;
}
//---------------------------------------------------------
// breakMultiMeasureRest
// return true if this measure should start a new
// multi measure rest
//---------------------------------------------------------
static bool breakMultiMeasureRest(Measure* m)
{
if (m->breakMultiMeasureRest()) {
return true;
}
if (m->repeatStart()
|| (m->prevMeasure() && m->prevMeasure()->repeatEnd())
|| (m->isIrregular())
|| (m->prevMeasure() && m->prevMeasure()->isIrregular())
|| (m->prevMeasure() && (m->prevMeasure()->sectionBreak()))) {
return true;
}
auto sl = m->score()->spannerMap().findOverlapping(m->tick().ticks(), m->endTick().ticks());
for (auto i : sl) {
Spanner* s = i.value;
// break for first measure of volta or textline and first measure *after* volta
if ((s->isVolta() || s->isTextLine()) && (s->tick() == m->tick() || s->tick2() == m->tick())) {
return true;
}
}
// break for marker in this measure
for (Element* e : m->el()) {
if (e->isMarker()) {
Marker* mark = toMarker(e);
if (!(mark->align() & Align::RIGHT)) {
return true;
}
}
}
// break for marker & jump in previous measure
Measure* pm = m->prevMeasure();
if (pm) {
for (Element* e : pm->el()) {
if (e->isJump()) {
return true;
} else if (e->isMarker()) {
Marker* mark = toMarker(e);
if (mark->align() & Align::RIGHT) {
return true;
}
}
}
}
// break for MeasureRepeat group
for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
if (m->isMeasureRepeatGroup(staffIdx)
|| (m->prevMeasure() && m->prevMeasure()->isMeasureRepeatGroup(staffIdx))) {
return true;
}
}
for (Segment* s = m->first(); s; s = s->next()) {
for (Element* e : s->annotations()) {
if (!e->visible()) {
continue;
}
if (e->isRehearsalMark()
|| e->isTempoText()
|| ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange())
&& (e->systemFlag() || m->score()->staff(e->staffIdx())->show()))) {
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;
}
if (s->isStartRepeatBarLineType()) {
return true;
}
if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && m->tick().isNotZero()) {
return true;
}
if (s->isClefType()) {
if (s->tick() != m->endTick() && m->tick().isNotZero()) {
return true;
}
}
}
}
if (pm) {
Segment* s = pm->findSegmentR(SegmentType::EndBarLine, pm->ticks());
if (s) {
for (int staffIdx = 0; staffIdx < s->score()->nstaves(); ++staffIdx) {
BarLine* bl = toBarLine(s->element(staffIdx * VOICES));
if (bl) {
BarLineType t = bl->barLineType();
if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated()) {
return true;
} else {
break;
}
}
}
}
if (pm->findSegment(SegmentType::Clef, m->tick())) {
return true;
}
}
return false;
}
Layout::Layout(Ms::Score* score)
: m_score(score)
{
@ -566,28 +397,6 @@ void Layout::collectLinearSystem(LayoutContext& lc)
system->setWidth(pos.x());
}
//---------------------------------------------------------
// layoutDrumsetChord
//---------------------------------------------------------
static void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium)
{
for (Note* note : c->notes()) {
int pitch = note->pitch();
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
} else if (!note->fixed()) {
note->undoChangeProperty(Pid::HEAD_GROUP, int(drumset->noteHead(pitch)));
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;
}
}
}
//---------------------------------------------------------
// extendedStemLenWithTwoNotesTremolo
// Goal: To extend stem of one of the chords to make the tremolo less steep
@ -642,298 +451,3 @@ std::pair<qreal, qreal> Layout::extendedStemLenWithTwoNoteTremolo(Tremolo* tremo
return { stemLen1, stemLen2 };
}
//---------------------------------------------------------
// processLines
//---------------------------------------------------------
static void processLines(System* system, std::vector<Spanner*> lines, bool align)
{
std::vector<SpannerSegment*> segments;
for (Spanner* sp : lines) {
SpannerSegment* ss = sp->layoutSystem(system); // create/layout spanner segment for this system
if (ss->autoplace()) {
segments.push_back(ss);
}
}
if (align && segments.size() > 1) {
const int nstaves = system->staves()->size();
constexpr qreal minY = -1000000.0;
const qreal defaultY = segments[0]->rypos();
std::vector<qreal> y(nstaves, minY);
for (SpannerSegment* ss : segments) {
if (ss->visible()) {
qreal& staffY = y[ss->staffIdx()];
staffY = qMax(staffY, ss->rypos());
}
}
for (SpannerSegment* ss : segments) {
if (!ss->isStyled(Pid::OFFSET)) {
continue;
}
const qreal staffY = y[ss->staffIdx()];
if (staffY > minY) {
ss->rypos() = staffY;
} else {
ss->rypos() = defaultY;
}
}
}
//
// add shapes to skyline
//
for (SpannerSegment* ss : segments) {
if (ss->addToSkyline()) {
system->staff(ss->staffIdx())->skyline().add(ss->shape().translated(ss->pos()));
}
}
}
//---------------------------------------------------------
// isTopTuplet
// returns true for the first CR of a tuplet that is not cross-staff
//---------------------------------------------------------
static bool isTopTuplet(ChordRest* cr)
{
Tuplet* t = cr->tuplet();
if (t && t->elements().front() == cr) {
// find top level tuplet
while (t->tuplet()) {
t = t->tuplet();
}
// consider tuplet cross if anything moved within it
if (t->cross()) {
return false;
} else {
return true;
}
}
// no tuplet or not first element
return false;
}
//---------------------------------------------------------
// layoutTies
//---------------------------------------------------------
static void layoutTies(Chord* ch, System* system, const Fraction& stick)
{
SysStaff* staff = system->staff(ch->staffIdx());
if (!staff->show()) {
return;
}
for (Note* note : ch->notes()) {
Tie* t = note->tieFor();
if (t) {
TieSegment* ts = t->layoutFor(system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translated(ts->pos()));
}
}
t = note->tieBack();
if (t) {
if (t->startNote()->tick() < stick) {
TieSegment* ts = t->layoutBack(system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translated(ts->pos()));
}
}
}
}
}
//---------------------------------------------------------
// layoutHarmonies
//---------------------------------------------------------
static void layoutHarmonies(const std::vector<Segment*>& sl)
{
for (const Segment* s : sl) {
for (Element* e : s->annotations()) {
if (e->isHarmony()) {
Harmony* h = toHarmony(e);
// 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();
h->autoplaceSegmentElement();
}
}
}
}
//---------------------------------------------------------
// alignHarmonies
//---------------------------------------------------------
static void alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool harmony, const qreal maxShiftAbove,
const qreal maxShiftBelow)
{
// Help class.
// Contains harmonies/fretboard per segment.
class HarmonyList : public QList<Element*>
{
QMap<const Segment*, QList<Element*> > elements;
QList<Element*> modified;
Element* getReferenceElement(const Segment* s, bool above, bool visible) const
{
// Returns the reference element for aligning.
// When a segments contains multiple harmonies/fretboard, the lowest placed
// element (for placement above, otherwise the highest placed element) is
// used for alignment.
Element* element { nullptr };
for (Element* e : elements[s]) {
// Only chord symbols have styled offset, fretboards don't.
if (!e->autoplace() || (e->isHarmony() && !e->isStyled(Pid::OFFSET)) || (visible && !e->visible())) {
continue;
}
if (!element) {
element = e;
} else {
if ((e->placeAbove() && above && (element->y() < e->y()))
|| (e->placeBelow() && !above && (element->y() > e->y()))) {
element = e;
}
}
}
return element;
}
public:
HarmonyList()
{
elements.clear();
modified.clear();
}
void append(const Segment* s, Element* e)
{
elements[s].append(e);
}
qreal getReferenceHeight(bool above) const
{
// The reference height is the height of
// the lowest element if placed above
// or
// the highest element if placed below.
bool first { true };
qreal ref { 0.0 };
for (auto s : elements.keys()) {
Element* e { getReferenceElement(s, above, true) };
if (!e) {
continue;
}
if (e->placeAbove() && above) {
ref = first ? e->y() : qMin(ref, e->y());
first = false;
} else if (e->placeBelow() && !above) {
ref = first ? e->y() : qMax(ref, e->y());
first = false;
}
}
return ref;
}
bool align(bool above, qreal reference, qreal maxShift)
{
// Align the elements. If a segment contains multiple elements,
// only the reference elements is used in the algorithm. All other
// elements will remain their original placement with respect to
// the reference element.
bool moved { false };
if (almostZero(reference)) {
return moved;
}
for (auto s : elements.keys()) {
QList<Element*> handled;
Element* be = getReferenceElement(s, above, false);
if (!be) {
// If there are only invisible elements, we have to use an invisible
// element for alignment reference.
be = getReferenceElement(s, above, true);
}
if (be && ((above && (be->y() < (reference + maxShift))) || ((!above && (be->y() > (reference - maxShift)))))) {
qreal shift = be->rypos();
be->rypos() = reference - be->ryoffset();
shift -= be->rypos();
for (Element* e : elements[s]) {
if ((above && e->placeBelow()) || (!above && e->placeAbove())) {
continue;
}
modified.append(e);
handled.append(e);
moved = true;
if (e != be) {
e->rypos() -= shift;
}
}
for (auto e : handled) {
elements[s].removeOne(e);
}
}
}
return moved;
}
void addToSkyline(const System* system)
{
for (Element* e : qAsConst(modified)) {
const Segment* s = toSegment(e->parent());
const MeasureBase* m = toMeasureBase(s->parent());
system->staff(e->staffIdx())->skyline().add(e->shape().translated(e->pos() + s->pos() + m->pos()));
if (e->isFretDiagram()) {
FretDiagram* fd = toFretDiagram(e);
Harmony* h = fd->harmony();
if (h) {
system->staff(e->staffIdx())->skyline().add(h->shape().translated(h->pos() + fd->pos() + s->pos() + m->pos()));
} else {
system->staff(e->staffIdx())->skyline().add(fd->shape().translated(fd->pos() + s->pos() + m->pos()));
}
}
}
}
};
if (almostZero(maxShiftAbove) && almostZero(maxShiftBelow)) {
return;
}
// Collect all fret diagrams and chord symbol and store them per staff.
// In the same pass, the maximum height is collected.
QMap<int, HarmonyList> staves;
for (const Segment* s : sl) {
for (Element* e : s->annotations()) {
if ((harmony && e->isHarmony()) || (!harmony && e->isFretDiagram())) {
staves[e->staffIdx()].append(s, e);
}
}
}
for (int idx: staves.keys()) {
// Align the objects.
// Algorithm:
// - Find highest placed harmony/fretdiagram.
// - Align all harmony/fretdiagram objects placed between height and height-maxShiftAbove.
// - Repeat for all harmony/fretdiagram objects below heigt-maxShiftAbove.
bool moved { true };
int pass { 0 };
while (moved && (pass++ < 10)) {
moved = false;
moved |= staves[idx].align(true, staves[idx].getReferenceHeight(true), maxShiftAbove);
moved |= staves[idx].align(false, staves[idx].getReferenceHeight(false), maxShiftBelow);
}
// Add all aligned objects to the sky line.
staves[idx].addToSkyline(system);
}
}

View file

@ -22,16 +22,15 @@
#ifndef MU_ENGRAVING_LAYOUT_H
#define MU_ENGRAVING_LAYOUT_H
#include "../fraction.h"
#include "../score.h"
#include "layoutcontext.h"
namespace Ms {
class Score;
class Fraction;
class System;
class Tremolo;
}
namespace mu::engraving {
class LayoutContext;
class Layout
{
public:

View file

@ -24,6 +24,7 @@
#include "../score.h"
#include "../staff.h"
#include "../chord.h"
#include "../timesig.h"
#include "layoutcontext.h"

View file

@ -29,6 +29,7 @@
#include "../chord.h"
#include "../stemslash.h"
#include "../hook.h"
#include "../accidental.h"
using namespace mu::engraving;
using namespace Ms;

View file

@ -21,10 +21,30 @@
*/
#include "layoutcontext.h"
#include "realfn.h"
#include "../spacer.h"
#include "../systemdivider.h"
#include "../measure.h"
#include "../system.h"
#include "../volta.h"
#include "../staff.h"
#include "../chordrest.h"
#include "../mmrestrange.h"
#include "../chord.h"
#include "../fingering.h"
#include "../barline.h"
#include "../tremolo.h"
#include "../measurenumber.h"
#include "../stafflines.h"
#include "../tuplet.h"
#include "../tie.h"
#include "../dynamic.h"
#include "../harmony.h"
#include "../fret.h"
#include "../bracketItem.h"
#include "../box.h"
#include "../part.h"
#include "layout.h"
#include "layoutlyrics.h"
@ -33,6 +53,7 @@
#include "layouttuplets.h"
#include "verticalgapdata.h"
using namespace mu;
using namespace mu::engraving;
using namespace Ms;
@ -95,6 +116,276 @@ void LayoutContext::layout()
score->systems().append(systemList); // TODO
}
//---------------------------------------------------------
// processLines
//---------------------------------------------------------
static void processLines(System* system, std::vector<Spanner*> lines, bool align)
{
std::vector<SpannerSegment*> segments;
for (Spanner* sp : lines) {
SpannerSegment* ss = sp->layoutSystem(system); // create/layout spanner segment for this system
if (ss->autoplace()) {
segments.push_back(ss);
}
}
if (align && segments.size() > 1) {
const int nstaves = system->staves()->size();
constexpr qreal minY = -1000000.0;
const qreal defaultY = segments[0]->rypos();
std::vector<qreal> y(nstaves, minY);
for (SpannerSegment* ss : segments) {
if (ss->visible()) {
qreal& staffY = y[ss->staffIdx()];
staffY = qMax(staffY, ss->rypos());
}
}
for (SpannerSegment* ss : segments) {
if (!ss->isStyled(Pid::OFFSET)) {
continue;
}
const qreal staffY = y[ss->staffIdx()];
if (staffY > minY) {
ss->rypos() = staffY;
} else {
ss->rypos() = defaultY;
}
}
}
//
// add shapes to skyline
//
for (SpannerSegment* ss : segments) {
if (ss->addToSkyline()) {
system->staff(ss->staffIdx())->skyline().add(ss->shape().translated(ss->pos()));
}
}
}
//---------------------------------------------------------
// layoutTies
//---------------------------------------------------------
static void layoutTies(Chord* ch, System* system, const Fraction& stick)
{
SysStaff* staff = system->staff(ch->staffIdx());
if (!staff->show()) {
return;
}
for (Note* note : ch->notes()) {
Tie* t = note->tieFor();
if (t) {
TieSegment* ts = t->layoutFor(system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translated(ts->pos()));
}
}
t = note->tieBack();
if (t) {
if (t->startNote()->tick() < stick) {
TieSegment* ts = t->layoutBack(system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translated(ts->pos()));
}
}
}
}
}
//---------------------------------------------------------
// layoutHarmonies
//---------------------------------------------------------
static void layoutHarmonies(const std::vector<Segment*>& sl)
{
for (const Segment* s : sl) {
for (Element* e : s->annotations()) {
if (e->isHarmony()) {
Harmony* h = toHarmony(e);
// 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();
h->autoplaceSegmentElement();
}
}
}
}
//---------------------------------------------------------
// alignHarmonies
//---------------------------------------------------------
static void alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool harmony, const qreal maxShiftAbove,
const qreal maxShiftBelow)
{
// Help class.
// Contains harmonies/fretboard per segment.
class HarmonyList : public QList<Element*>
{
QMap<const Segment*, QList<Element*> > elements;
QList<Element*> modified;
Element* getReferenceElement(const Segment* s, bool above, bool visible) const
{
// Returns the reference element for aligning.
// When a segments contains multiple harmonies/fretboard, the lowest placed
// element (for placement above, otherwise the highest placed element) is
// used for alignment.
Element* element { nullptr };
for (Element* e : elements[s]) {
// Only chord symbols have styled offset, fretboards don't.
if (!e->autoplace() || (e->isHarmony() && !e->isStyled(Pid::OFFSET)) || (visible && !e->visible())) {
continue;
}
if (!element) {
element = e;
} else {
if ((e->placeAbove() && above && (element->y() < e->y()))
|| (e->placeBelow() && !above && (element->y() > e->y()))) {
element = e;
}
}
}
return element;
}
public:
HarmonyList()
{
elements.clear();
modified.clear();
}
void append(const Segment* s, Element* e)
{
elements[s].append(e);
}
qreal getReferenceHeight(bool above) const
{
// The reference height is the height of
// the lowest element if placed above
// or
// the highest element if placed below.
bool first { true };
qreal ref { 0.0 };
for (auto s : elements.keys()) {
Element* e { getReferenceElement(s, above, true) };
if (!e) {
continue;
}
if (e->placeAbove() && above) {
ref = first ? e->y() : qMin(ref, e->y());
first = false;
} else if (e->placeBelow() && !above) {
ref = first ? e->y() : qMax(ref, e->y());
first = false;
}
}
return ref;
}
bool align(bool above, qreal reference, qreal maxShift)
{
// Align the elements. If a segment contains multiple elements,
// only the reference elements is used in the algorithm. All other
// elements will remain their original placement with respect to
// the reference element.
bool moved { false };
if (mu::RealIsNull(reference)) {
return moved;
}
for (auto s : elements.keys()) {
QList<Element*> handled;
Element* be = getReferenceElement(s, above, false);
if (!be) {
// If there are only invisible elements, we have to use an invisible
// element for alignment reference.
be = getReferenceElement(s, above, true);
}
if (be && ((above && (be->y() < (reference + maxShift))) || ((!above && (be->y() > (reference - maxShift)))))) {
qreal shift = be->rypos();
be->rypos() = reference - be->ryoffset();
shift -= be->rypos();
for (Element* e : elements[s]) {
if ((above && e->placeBelow()) || (!above && e->placeAbove())) {
continue;
}
modified.append(e);
handled.append(e);
moved = true;
if (e != be) {
e->rypos() -= shift;
}
}
for (auto e : handled) {
elements[s].removeOne(e);
}
}
}
return moved;
}
void addToSkyline(const System* system)
{
for (Element* e : qAsConst(modified)) {
const Segment* s = toSegment(e->parent());
const MeasureBase* m = toMeasureBase(s->parent());
system->staff(e->staffIdx())->skyline().add(e->shape().translated(e->pos() + s->pos() + m->pos()));
if (e->isFretDiagram()) {
FretDiagram* fd = toFretDiagram(e);
Harmony* h = fd->harmony();
if (h) {
system->staff(e->staffIdx())->skyline().add(h->shape().translated(h->pos() + fd->pos() + s->pos() + m->pos()));
} else {
system->staff(e->staffIdx())->skyline().add(fd->shape().translated(fd->pos() + s->pos() + m->pos()));
}
}
}
}
};
if (RealIsNull(maxShiftAbove) && RealIsNull(maxShiftBelow)) {
return;
}
// Collect all fret diagrams and chord symbol and store them per staff.
// In the same pass, the maximum height is collected.
QMap<int, HarmonyList> staves;
for (const Segment* s : sl) {
for (Element* e : s->annotations()) {
if ((harmony && e->isHarmony()) || (!harmony && e->isFretDiagram())) {
staves[e->staffIdx()].append(s, e);
}
}
}
for (int idx: staves.keys()) {
// Align the objects.
// Algorithm:
// - Find highest placed harmony/fretdiagram.
// - Align all harmony/fretdiagram objects placed between height and height-maxShiftAbove.
// - Repeat for all harmony/fretdiagram objects below heigt-maxShiftAbove.
bool moved { true };
int pass { 0 };
while (moved && (pass++ < 10)) {
moved = false;
moved |= staves[idx].align(true, staves[idx].getReferenceHeight(true), maxShiftAbove);
moved |= staves[idx].align(false, staves[idx].getReferenceHeight(false), maxShiftBelow);
}
// Add all aligned objects to the sky line.
staves[idx].addToSkyline(system);
}
}
//---------------------------------------------------------
// layoutSystemElements
//---------------------------------------------------------
@ -343,7 +634,7 @@ void LayoutContext::layoutSystemElements(System* system)
continue;
}
ChordRest* cr = toChordRest(e);
if (!isTopTuplet(cr)) {
if (!LayoutTuplets::isTopTuplet(cr)) {
continue;
}
DurationElement* de = cr;
@ -908,11 +1199,11 @@ static void distributeStaves(Page* page)
// Try to make the gaps equal, taking the spread factors and maximum spacing into account.
static const int maxPasses { 20 }; // Saveguard to prevent endless loops.
int pass { 0 };
while (!almostZero(spaceLeft) && (ngaps > 0) && (++pass < maxPasses)) {
while (!RealIsNull(spaceLeft) && (ngaps > 0) && (++pass < maxPasses)) {
ngaps = 0;
qreal smallest { vgdl.smallest() };
qreal nextSmallest { vgdl.smallest(smallest) };
if (almostZero(smallest) || almostZero(nextSmallest)) {
if (RealIsNull(smallest) || RealIsNull(nextSmallest)) {
break;
}
@ -923,7 +1214,7 @@ static void distributeStaves(Page* page)
qreal addedSpace { 0.0 };
VerticalGapDataList modified;
for (VerticalGapData* vgd : vgdl) {
if (!almostZero(vgd->spacing() - smallest)) {
if (!RealIsNull(vgd->spacing() - smallest)) {
continue;
}
qreal step { nextSmallest - vgd->spacing() };
@ -931,7 +1222,7 @@ static void distributeStaves(Page* page)
continue;
}
step = vgd->addSpacing(step);
if (!almostZero(step)) {
if (!RealIsNull(step)) {
addedSpace += step * vgd->factor();
modified.append(vgd);
++ngaps;
@ -956,13 +1247,13 @@ static void distributeStaves(Page* page)
spaceLeft = qMin(maxPageFill * vgdl.length(), spaceLeft);
pass = 0;
ngaps = 1;
while (!almostZero(spaceLeft) && !almostZero(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) {
while (!RealIsNull(spaceLeft) && !RealIsNull(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) {
ngaps = 0;
qreal addedSpace { 0.0 };
qreal step { spaceLeft / vgdl.sumStretchFactor() };
for (VerticalGapData* vgd : vgdl) {
qreal res { vgd->addFillSpacing(step, maxPageFill) };
if (!almostZero(res)) {
if (!RealIsNull(res)) {
addedSpace += res * vgd->factor();
++ngaps;
}

View file

@ -23,6 +23,15 @@
#include "../score.h"
#include "../measure.h"
#include "../undo.h"
#include "../mmrest.h"
#include "../ambitus.h"
#include "../barline.h"
#include "../keysig.h"
#include "../stem.h"
#include "../lyrics.h"
#include "../marker.h"
#include "../part.h"
#include "layout.h"
#include "layoutcontext.h"
@ -32,6 +41,537 @@
using namespace mu::engraving;
using namespace Ms;
//---------------------------------------------------------
// createMMRest
// create a multimeasure rest
// from firstMeasure to lastMeasure (inclusive)
//---------------------------------------------------------
void LayoutMeasure::createMMRest(Score* score, Measure* firstMeasure, Measure* lastMeasure, const Fraction& len)
{
int numMeasuresInMMRest = 1;
if (firstMeasure != lastMeasure) {
for (Measure* m = firstMeasure->nextMeasure(); m; m = m->nextMeasure()) {
++numMeasuresInMMRest;
m->setMMRestCount(-1);
if (m->mmRest()) {
score->undo(new ChangeMMRest(m, 0));
}
if (m == lastMeasure) {
break;
}
}
}
// mmrMeasure coexists with n undisplayed measures of rests
Measure* mmrMeasure = firstMeasure->mmRest();
if (mmrMeasure) {
// reuse existing mmrest
if (mmrMeasure->ticks() != len) {
Segment* s = mmrMeasure->findSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
// adjust length
mmrMeasure->setTicks(len);
// move existing end barline
if (s) {
s->setRtick(len);
}
}
mmrMeasure->removeSystemTrailer();
} else {
mmrMeasure = new Measure(score);
mmrMeasure->setTicks(len);
mmrMeasure->setTick(firstMeasure->tick());
score->undo(new ChangeMMRest(firstMeasure, mmrMeasure));
}
mmrMeasure->setTimesig(firstMeasure->timesig());
mmrMeasure->setPageBreak(lastMeasure->pageBreak());
mmrMeasure->setLineBreak(lastMeasure->lineBreak());
mmrMeasure->setMMRestCount(numMeasuresInMMRest);
mmrMeasure->setNo(firstMeasure->no());
//
// set mmrMeasure with same barline as last underlying measure
//
Segment* lastMeasureEndBarlineSeg = lastMeasure->findSegmentR(SegmentType::EndBarLine, lastMeasure->ticks());
if (lastMeasureEndBarlineSeg) {
Segment* mmrEndBarlineSeg = mmrMeasure->undoGetSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
Element* e = lastMeasureEndBarlineSeg->element(staffIdx * VOICES);
if (e) {
bool generated = e->generated();
if (!mmrEndBarlineSeg->element(staffIdx * VOICES)) {
Element* eClone = generated ? e->clone() : e->linkedClone();
eClone->setGenerated(generated);
eClone->setParent(mmrEndBarlineSeg);
score->undoAddElement(eClone);
} else {
BarLine* mmrEndBarline = toBarLine(mmrEndBarlineSeg->element(staffIdx * VOICES));
BarLine* lastMeasureEndBarline = toBarLine(e);
if (!generated && !mmrEndBarline->links()) {
score->undo(new Link(mmrEndBarline, lastMeasureEndBarline));
}
if (mmrEndBarline->barLineType() != lastMeasureEndBarline->barLineType()) {
// change directly when generating mmrests, do not change underlying measures or follow links
score->undo(new ChangeProperty(mmrEndBarline, Pid::BARLINE_TYPE,
QVariant::fromValue(lastMeasureEndBarline->barLineType()),
PropertyFlags::NOSTYLE));
score->undo(new ChangeProperty(mmrEndBarline, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
}
}
}
}
}
//
// if last underlying measure ends with clef change, show same at end of mmrest
//
Segment* lastMeasureClefSeg = lastMeasure->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef,
lastMeasure->ticks());
if (lastMeasureClefSeg) {
Segment* mmrClefSeg = mmrMeasure->undoGetSegment(lastMeasureClefSeg->segmentType(), lastMeasure->endTick());
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
const int track = staff2track(staffIdx);
Element* e = lastMeasureClefSeg->element(track);
if (e && e->isClef()) {
Clef* lastMeasureClef = toClef(e);
if (!mmrClefSeg->element(track)) {
Clef* mmrClef = lastMeasureClef->generated() ? lastMeasureClef->clone() : toClef(
lastMeasureClef->linkedClone());
mmrClef->setParent(mmrClefSeg);
score->undoAddElement(mmrClef);
} else {
Clef* mmrClef = toClef(mmrClefSeg->element(track));
mmrClef->setClefType(lastMeasureClef->clefType());
mmrClef->setShowCourtesy(lastMeasureClef->showCourtesy());
}
}
}
}
mmrMeasure->setRepeatStart(firstMeasure->repeatStart() || lastMeasure->repeatStart());
mmrMeasure->setRepeatEnd(firstMeasure->repeatEnd() || lastMeasure->repeatEnd());
mmrMeasure->setSectionBreak(lastMeasure->sectionBreak());
//
// copy markers to mmrMeasure
//
ElementList oldList = mmrMeasure->takeElements();
ElementList newList = lastMeasure->el();
for (Element* e : firstMeasure->el()) {
if (e->isMarker()) {
newList.push_back(e);
}
}
for (Element* e : newList) {
bool found = false;
for (Element* ee : oldList) {
if (ee->type() == e->type() && ee->subtype() == e->subtype()) {
mmrMeasure->add(ee);
auto i = std::find(oldList.begin(), oldList.end(), ee);
if (i != oldList.end()) {
oldList.erase(i);
}
found = true;
break;
}
}
if (!found) {
mmrMeasure->add(e->clone());
}
}
for (Element* e : oldList) {
delete e;
}
Segment* s = mmrMeasure->undoGetSegmentR(SegmentType::ChordRest, Fraction(0, 1));
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
if (s->element(track) == 0) {
MMRest* mmr = new MMRest(score);
mmr->setDurationType(TDuration::DurationType::V_MEASURE);
mmr->setTicks(mmrMeasure->ticks());
mmr->setTrack(track);
mmr->setParent(s);
score->undo(new AddElement(mmr));
}
}
//
// further check for clefs
//
Segment* underlyingSeg = lastMeasure->findSegmentR(SegmentType::Clef, lastMeasure->ticks());
Segment* mmrSeg = mmrMeasure->findSegment(SegmentType::Clef, lastMeasure->endTick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::Clef, lastMeasure->ticks());
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setTrailer(underlyingSeg->trailer());
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Clef* clef = toClef(underlyingSeg->element(track));
if (clef) {
if (mmrSeg->element(track) == 0) {
mmrSeg->add(clef->clone());
} else {
//TODO: check if same clef
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
score->undo(new RemoveElement(mmrSeg));
}
//
// check for time signature
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegment(SegmentType::TimeSig, firstMeasure->tick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::TimeSig, Fraction(0, 1));
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setHeader(underlyingSeg->header());
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
TimeSig* underlyingTimeSig = toTimeSig(underlyingSeg->element(track));
if (underlyingTimeSig) {
TimeSig* mmrTimeSig = toTimeSig(mmrSeg->element(track));
if (!mmrTimeSig) {
mmrTimeSig = underlyingTimeSig->generated() ? underlyingTimeSig->clone() : toTimeSig(
underlyingTimeSig->linkedClone());
mmrTimeSig->setParent(mmrSeg);
score->undo(new AddElement(mmrTimeSig));
} else {
mmrTimeSig->setSig(underlyingTimeSig->sig(), underlyingTimeSig->timeSigType());
mmrTimeSig->layout();
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
score->undo(new RemoveElement(mmrSeg));
}
//
// check for ambitus
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::Ambitus, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegment(SegmentType::Ambitus, firstMeasure->tick());
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::Ambitus, Fraction(0, 1));
}
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Ambitus* underlyingAmbitus = toAmbitus(underlyingSeg->element(track));
if (underlyingAmbitus) {
Ambitus* mmrAmbitus = toAmbitus(mmrSeg->element(track));
if (!mmrAmbitus) {
mmrAmbitus = underlyingAmbitus->clone();
mmrAmbitus->setParent(mmrSeg);
score->undo(new AddElement(mmrAmbitus));
} else {
mmrAmbitus->initFrom(underlyingAmbitus);
mmrAmbitus->layout();
}
}
}
} else if (mmrSeg) {
// TODO: remove elements from mmrSeg?
score->undo(new RemoveElement(mmrSeg));
}
//
// check for key signature
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
mmrSeg = mmrMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
if (underlyingSeg) {
if (mmrSeg == 0) {
mmrSeg = mmrMeasure->undoGetSegmentR(SegmentType::KeySig, Fraction(0, 1));
}
mmrSeg->setEnabled(underlyingSeg->enabled());
mmrSeg->setHeader(underlyingSeg->header());
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
KeySig* underlyingKeySig = toKeySig(underlyingSeg->element(track));
if (underlyingKeySig) {
KeySig* mmrKeySig = toKeySig(mmrSeg->element(track));
if (!mmrKeySig) {
mmrKeySig = underlyingKeySig->generated() ? underlyingKeySig->clone() : toKeySig(
underlyingKeySig->linkedClone());
mmrKeySig->setParent(mmrSeg);
mmrKeySig->setGenerated(true);
score->undo(new AddElement(mmrKeySig));
} else {
if (!(mmrKeySig->keySigEvent() == underlyingKeySig->keySigEvent())) {
bool addKey = underlyingKeySig->isChange();
score->undo(new ChangeKeySig(mmrKeySig, underlyingKeySig->keySigEvent(), mmrKeySig->showCourtesy(),
addKey));
}
}
}
}
} else if (mmrSeg) {
mmrSeg->setEnabled(false);
// TODO: remove elements from mmrSeg, then delete mmrSeg
// previously we removed the segment if not empty,
// but this resulted in "stale" keysig in mmrest after removed from underlying measure
//undo(new RemoveElement(mmrSeg));
}
mmrMeasure->checkHeader();
mmrMeasure->checkTrailer();
//
// check for rehearsal mark etc.
//
underlyingSeg = firstMeasure->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
if (underlyingSeg) {
// clone elements from underlying measure to mmr
for (Element* e : underlyingSeg->annotations()) {
// look at elements in underlying measure
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
continue;
}
// try to find a match in mmr
bool found = false;
for (Element* ee : s->annotations()) {
if (e->linkList().contains(ee)) {
found = true;
break;
}
}
// add to mmr if no match found
if (!found) {
Element* eClone = e->linkedClone();
eClone->setParent(s);
score->undo(new AddElement(eClone));
}
}
// remove stray elements (possibly leftover from a previous layout of this mmr)
// this should not happen since the elements are linked?
for (Element* e : s->annotations()) {
// look at elements in mmr
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
continue;
}
// try to find a match in underlying measure
bool found = false;
for (Element* ee : underlyingSeg->annotations()) {
if (e->linkList().contains(ee)) {
found = true;
break;
}
}
// remove from mmr if no match found
if (!found) {
score->undo(new RemoveElement(e));
}
}
}
MeasureBase* nm = score->_showVBox ? lastMeasure->next() : lastMeasure->nextMeasure();
mmrMeasure->setNext(nm);
mmrMeasure->setPrev(firstMeasure->prev());
}
//---------------------------------------------------------
// validMMRestMeasure
// return true if this might be a measure in a
// multi measure rest
//---------------------------------------------------------
static bool validMMRestMeasure(Measure* m)
{
if (m->irregular()) {
return false;
}
int n = 0;
for (Segment* s = m->first(); s; s = s->next()) {
for (Element* e : s->annotations()) {
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
return false;
}
}
if (s->isChordRestType()) {
bool restFound = false;
int tracks = m->score()->ntracks();
for (int track = 0; track < tracks; ++track) {
if ((track % VOICES) == 0 && !m->score()->staff(track / VOICES)->show()) {
track += VOICES - 1;
continue;
}
if (s->element(track)) {
if (!s->element(track)->isRest()) {
return false;
}
restFound = true;
}
}
for (Element* e : s->annotations()) {
if (e->isFermata()) {
return false;
}
}
if (restFound) {
++n;
}
// measure is not empty if there is more than one rest
if (n > 1) {
return false;
}
}
}
return true;
}
//---------------------------------------------------------
// breakMultiMeasureRest
// return true if this measure should start a new
// multi measure rest
//---------------------------------------------------------
static bool breakMultiMeasureRest(Measure* m)
{
if (m->breakMultiMeasureRest()) {
return true;
}
if (m->repeatStart()
|| (m->prevMeasure() && m->prevMeasure()->repeatEnd())
|| (m->isIrregular())
|| (m->prevMeasure() && m->prevMeasure()->isIrregular())
|| (m->prevMeasure() && (m->prevMeasure()->sectionBreak()))) {
return true;
}
auto sl = m->score()->spannerMap().findOverlapping(m->tick().ticks(), m->endTick().ticks());
for (auto i : sl) {
Spanner* s = i.value;
// break for first measure of volta or textline and first measure *after* volta
if ((s->isVolta() || s->isTextLine()) && (s->tick() == m->tick() || s->tick2() == m->tick())) {
return true;
}
}
// break for marker in this measure
for (Element* e : m->el()) {
if (e->isMarker()) {
Marker* mark = toMarker(e);
if (!(mark->align() & Align::RIGHT)) {
return true;
}
}
}
// break for marker & jump in previous measure
Measure* pm = m->prevMeasure();
if (pm) {
for (Element* e : pm->el()) {
if (e->isJump()) {
return true;
} else if (e->isMarker()) {
Marker* mark = toMarker(e);
if (mark->align() & Align::RIGHT) {
return true;
}
}
}
}
// break for MeasureRepeat group
for (int staffIdx = 0; staffIdx < m->score()->nstaves(); ++staffIdx) {
if (m->isMeasureRepeatGroup(staffIdx)
|| (m->prevMeasure() && m->prevMeasure()->isMeasureRepeatGroup(staffIdx))) {
return true;
}
}
for (Segment* s = m->first(); s; s = s->next()) {
for (Element* e : s->annotations()) {
if (!e->visible()) {
continue;
}
if (e->isRehearsalMark()
|| e->isTempoText()
|| ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange())
&& (e->systemFlag() || m->score()->staff(e->staffIdx())->show()))) {
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;
}
if (s->isStartRepeatBarLineType()) {
return true;
}
if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && m->tick().isNotZero()) {
return true;
}
if (s->isClefType()) {
if (s->tick() != m->endTick() && m->tick().isNotZero()) {
return true;
}
}
}
}
if (pm) {
Segment* s = pm->findSegmentR(SegmentType::EndBarLine, pm->ticks());
if (s) {
for (int staffIdx = 0; staffIdx < s->score()->nstaves(); ++staffIdx) {
BarLine* bl = toBarLine(s->element(staffIdx * VOICES));
if (bl) {
BarLineType t = bl->barLineType();
if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated()) {
return true;
} else {
break;
}
}
}
}
if (pm->findSegment(SegmentType::Clef, m->tick())) {
return true;
}
}
return false;
}
//---------------------------------------------------------
// layoutDrumsetChord
//---------------------------------------------------------
static void layoutDrumsetChord(Chord* c, const Drumset* drumset, const StaffType* st, qreal spatium)
{
for (Note* note : c->notes()) {
int pitch = note->pitch();
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
} else if (!note->fixed()) {
note->undoChangeProperty(Pid::HEAD_GROUP, int(drumset->noteHead(pitch)));
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;
}
}
}
void LayoutMeasure::getNextMeasure(Ms::Score* score, LayoutContext& lc)
{
lc.prevMeasure = lc.curMeasure;
@ -72,7 +612,7 @@ void LayoutMeasure::getNextMeasure(Ms::Score* score, LayoutContext& lc)
nm = toMeasure(mb);
}
if (n >= score->styleI(Sid::minEmptyMeasures)) {
score->createMMRest(m, lm, len);
createMMRest(score, m, lm, len);
lc.curMeasure = m->mmRest();
lc.nextMeasure = score->_showVBox ? lm->next() : lm->nextMeasure();
} else {

View file

@ -24,6 +24,8 @@
namespace Ms {
class Score;
class Measure;
class Fraction;
}
namespace mu::engraving {
@ -34,6 +36,10 @@ public:
LayoutMeasure() = default;
static void getNextMeasure(Ms::Score* score, LayoutContext& lc);
private:
static void createMMRest(Ms::Score* score, Ms::Measure* firstMeasure, Ms::Measure* lastMeasure, const Ms::Fraction& len);
};
}

View file

@ -27,6 +27,26 @@
using namespace mu::engraving;
using namespace Ms;
bool LayoutTuplets::isTopTuplet(ChordRest* cr)
{
Tuplet* t = cr->tuplet();
if (t && t->elements().front() == cr) {
// find top level tuplet
while (t->tuplet()) {
t = t->tuplet();
}
// consider tuplet cross if anything moved within it
if (t->cross()) {
return false;
} else {
return true;
}
}
// no tuplet or not first element
return false;
}
bool LayoutTuplets::notTopTuplet(ChordRest* cr)
{
Tuplet* t = cr->tuplet();

View file

@ -31,6 +31,7 @@ class LayoutTuplets
{
public:
static bool isTopTuplet(Ms::ChordRest* cr);
static bool notTopTuplet(Ms::ChordRest* cr);
};
}

View file

@ -1,54 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "score.h"
#include "page.h"
#include "system.h"
#include "tremolo.h"
#include "measure.h"
#include "layout.h"
#include "bracket.h"
#include "spanner.h"
#include "barline.h"
#include "tie.h"
#include "chord.h"
#include "staff.h"
#include "box.h"
#include "spacer.h"
#include "systemdivider.h"
#include "tuplet.h"
#include "dynamic.h"
#include "stafflines.h"
#include "tempotext.h"
#include "hairpin.h"
#include "part.h"
#include "keysig.h"
#include "sig.h"
#include "breath.h"
#include "tempo.h"
#include "fermata.h"
#include "lyrics.h"
using namespace mu;
namespace Ms {
} // namespace Ms

View file

@ -140,9 +140,6 @@ set(LIBMSCORE_SRC
${CMAKE_CURRENT_LIST_DIR}/lasso.h
${CMAKE_CURRENT_LIST_DIR}/layoutbreak.cpp
${CMAKE_CURRENT_LIST_DIR}/layoutbreak.h
${CMAKE_CURRENT_LIST_DIR}/layout.cpp
${CMAKE_CURRENT_LIST_DIR}/layout.h
${CMAKE_CURRENT_LIST_DIR}/layoutlinear.cpp
${CMAKE_CURRENT_LIST_DIR}/ledgerline.cpp
${CMAKE_CURRENT_LIST_DIR}/ledgerline.h
${CMAKE_CURRENT_LIST_DIR}/letring.cpp

View file

@ -55,7 +55,6 @@
#include "key.h"
#include "keysig.h"
#include "layoutbreak.h"
#include "layout.h"
#include "note.h"
#include "ottava.h"
#include "page.h"

View file

@ -5280,32 +5280,6 @@ void Score::connectTies(bool silent)
}
}
}
#if 0 // chords are set in tremolo->layout()
// 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)) {
Element* element = ls->element(i);
if (!element) {
continue;
}
if (!element->isChord()) {
qDebug("cannot connect tremolo");
} else {
Chord* nc = toChord(element);
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);
}
}
break;
}
}
#endif
}
}
}
@ -5315,6 +5289,50 @@ mu::score::AccessibleScore* Score::accessible() const
return m_accessible;
}
//---------------------------------------------------------
// relayoutForStyles
/// some styles can't properly apply if score hasn't been laid out yet,
/// so temporarily disable them and then reenable after layout
/// (called during score load)
//---------------------------------------------------------
void Score::relayoutForStyles()
{
std::vector<Sid> stylesToTemporarilyDisable;
for (Sid sid : { Sid::createMultiMeasureRests, Sid::mrNumberSeries }) {
// only necessary if boolean style is true
if (styleB(sid)) {
stylesToTemporarilyDisable.push_back(sid);
}
}
if (!stylesToTemporarilyDisable.empty()) {
for (Sid sid : stylesToTemporarilyDisable) {
style().set(sid, false); // temporarily disable
}
doLayout();
for (Sid sid : stylesToTemporarilyDisable) {
style().set(sid, true); // and immediately reenable
}
}
}
//---------------------------------------------------------
// doLayout
// do a complete (re-) layout
//---------------------------------------------------------
void Score::doLayout()
{
doLayoutRange(Fraction(0, 1), Fraction(-1, 1));
}
void Score::doLayoutRange(const Fraction& st, const Fraction& et)
{
m_layout.doLayoutRange(st, et);
}
UndoStack* Score::undoStack() const { return _masterScore->undoStack(); }
const RepeatList& Score::repeatList() const { return _masterScore->repeatList(); }
const RepeatList& Score::repeatList2() const { return _masterScore->repeatList2(); }

View file

@ -544,8 +544,6 @@ private:
void cmdMoveRest(Rest*, Direction);
void cmdMoveLyrics(Lyrics*, Direction);
void createMMRest(Measure*, Measure*, const Fraction&);
void checkSlurs();
void checkScore();

View file

@ -2505,6 +2505,7 @@ Pid TextBase::propertyId(const QStringRef& name) const
if (name == "text") {
return Pid::TEXT;
}
for (Pid pid : TextBasePropertyId) {
if (propertyName(pid) == name) {
return pid;

View file

@ -38,7 +38,6 @@
#include "libmscore/clef.h"
#include "libmscore/textlinebase.h"
#include "libmscore/tuplet.h"
#include "libmscore/layout.h"
#include "libmscore/property.h"
#include "libmscore/undo.h"

View file

@ -1467,7 +1467,6 @@ enum class VerticalAlignRange {
SEGMENT, MEASURE, SYSTEM
};
//---------------------------------------------------------
// StyledProperty
/// \cond PLUGIN_API \private \endcond

View file

@ -22,6 +22,7 @@
#include "accessibledevmodel.h"
#include <QAccessible>
#include <QMetaEnum>
#include "log.h"

View file

@ -43,6 +43,7 @@
#include "diagnostics/idiagnosticspathsregister.h"
using namespace mu::framework;
using namespace mu::modularity;
static std::shared_ptr<GlobalConfiguration> s_globalConf = std::make_shared<GlobalConfiguration>();

View file

@ -46,7 +46,7 @@ AlsaMidiInPort::~AlsaMidiInPort()
void AlsaMidiInPort::init()
{
m_alsa = std::unique_ptr<Alsa>(new Alsa());
m_alsa = std::shared_ptr<Alsa>(new Alsa());
m_devicesListener.startWithCallback([this]() {
return devices();

View file

@ -58,7 +58,7 @@ private:
bool deviceExists(const MidiDeviceID& deviceId) const;
struct Alsa;
std::unique_ptr<Alsa> m_alsa;
std::shared_ptr<Alsa> m_alsa;
MidiDeviceID m_deviceID;
std::shared_ptr<std::thread> m_thread;
std::atomic<bool> m_running{ false };

View file

@ -46,7 +46,7 @@ AlsaMidiOutPort::~AlsaMidiOutPort()
void AlsaMidiOutPort::init()
{
m_alsa = std::unique_ptr<Alsa>(new Alsa());
m_alsa = std::shared_ptr<Alsa>(new Alsa());
m_devicesListener.startWithCallback([this]() {
return devices();

View file

@ -51,7 +51,7 @@ private:
bool deviceExists(const MidiDeviceID& deviceId) const;
struct Alsa;
std::unique_ptr<Alsa> m_alsa;
std::shared_ptr<Alsa> m_alsa;
MidiDeviceID m_deviceID;
async::Notification m_devicesChanged;

View file

@ -22,7 +22,6 @@
#include "midimodule.h"
#include <QQmlEngine>
#include "log.h"
#include "modularity/ioc.h"
@ -33,6 +32,8 @@
#include "ui/iuiengine.h"
#include "view/devtools/midiportdevmodel.h"
#include "log.h"
using namespace mu::midi;
static std::shared_ptr<MidiConfiguration> s_configuration = std::make_shared<MidiConfiguration>();

View file

@ -23,11 +23,13 @@
#include <algorithm>
#include "accessibility/iaccessible.h"
#include "navigationsection.h"
#include "translation.h"
#include "log.h"
using namespace mu::ui;
using namespace mu::accessibility;
NavigationPanel::NavigationPanel(QObject* parent)
: AbstractNavigation(parent)

View file

@ -33,13 +33,15 @@
#include "libmscore/utils.h"
#include "libmscore/undo.h"
#include "libmscore/iname.h"
#include "libmscore/system.h"
#include "log.h"
#include "translation.h"
#include "ui/view/iconcodes.h"
#include "widgetstatestore.h"
#include "log.h"
using namespace mu::notation;
using namespace mu::ui;

View file

@ -30,15 +30,14 @@
#include "fontStyleSelect.h"
#include "framework/global/widgetstatestore.h"
#include "libmscore/figuredbass.h"
#include "libmscore/layout.h"
#include "libmscore/scorefont.h"
#include "libmscore/sym.h"
#include "libmscore/realizedharmony.h"
#include "log.h"
#include "offsetSelect.h"
#include "settings.h"
#include "translation.h"
#include "ui/view/iconcodes.h"
#include "log.h"
using namespace mu::notation;
using namespace mu::ui;