removed libmscore/layout.cpp
This commit is contained in:
parent
ea39f9f3af
commit
5f28d661f1
30 changed files with 933 additions and 1114 deletions
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <QCoreApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QWindow>
|
||||
|
||||
#include "translation.h"
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../score.h"
|
||||
#include "../staff.h"
|
||||
#include "../chord.h"
|
||||
#include "../timesig.h"
|
||||
|
||||
#include "layoutcontext.h"
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "../chord.h"
|
||||
#include "../stemslash.h"
|
||||
#include "../hook.h"
|
||||
#include "../accidental.h"
|
||||
|
||||
using namespace mu::engraving;
|
||||
using namespace Ms;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -31,6 +31,7 @@ class LayoutTuplets
|
|||
{
|
||||
public:
|
||||
|
||||
static bool isTopTuplet(Ms::ChordRest* cr);
|
||||
static bool notTopTuplet(Ms::ChordRest* cr);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -544,8 +544,6 @@ private:
|
|||
void cmdMoveRest(Rest*, Direction);
|
||||
void cmdMoveLyrics(Lyrics*, Direction);
|
||||
|
||||
void createMMRest(Measure*, Measure*, const Fraction&);
|
||||
|
||||
void checkSlurs();
|
||||
void checkScore();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -1467,7 +1467,6 @@ enum class VerticalAlignRange {
|
|||
SEGMENT, MEASURE, SYSTEM
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// StyledProperty
|
||||
/// \cond PLUGIN_API \private \endcond
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "accessibledevmodel.h"
|
||||
|
||||
#include <QAccessible>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue