MuseScore/libmscore/layoutlinear.cpp

290 lines
12 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2018 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#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 "sym.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"
namespace Ms {
//---------------------------------------------------------
// resetSystems
// in linear mode there is only one page
// which contains one system
//---------------------------------------------------------
void Score::resetSystems(bool layoutAll, LayoutContext& lc)
{
Page* page = 0;
if (layoutAll) {
for (System* s : _systems) {
for (SpannerSegment* ss : s->spannerSegments())
ss->setParent(0);
}
qDeleteAll(_systems);
_systems.clear();
qDeleteAll(pages());
pages().clear();
if (!firstMeasure()) {
qDebug("no measures");
return;
}
for (MeasureBase* mb = first(); mb; mb = mb->next())
mb->setSystem(0);
page = new Page(this);
pages().push_back(page);
page->bbox().setRect(0.0, 0.0, loWidth(), loHeight());
page->setNo(0);
System* system = new System(this);
_systems.push_back(system);
page->appendSystem(system);
system->adjustStavesNumber(nstaves());
}
else {
if (pages().isEmpty())
return;
page = pages().front();
System* system = systems().front();
system->clear();
system->adjustStavesNumber(nstaves());
}
lc.page = page;
}
//---------------------------------------------------------
// collectLinearSystem
// Append all measures to System. VBox is not included to System
//---------------------------------------------------------
void Score::collectLinearSystem(LayoutContext& lc)
{
System* system = systems().front();
system->setInstrumentNames(/* longNames */ true);
QPointF pos;
bool firstMeasure = true; //lc.startTick.isZero();
//set first measure to lc.nextMeasures for following
//utilizing in getNextMeasure()
lc.nextMeasure = _measures.first();
lc.tick = Fraction(0, 1);
getNextMeasure(lc);
while (lc.curMeasure) {
qreal ww = 0.0;
if (lc.curMeasure->isVBox() || lc.curMeasure->isTBox()) {
lc.curMeasure->setParent(nullptr);
getNextMeasure(lc);
continue;
}
system->appendMeasure(lc.curMeasure);
if (lc.curMeasure->isMeasure()) {
Measure* m = toMeasure(lc.curMeasure);
if (m->mmRest()) {
m->mmRest()->setSystem(nullptr);
}
if (firstMeasure) {
system->layoutSystem(0.0);
if (m->repeatStart()) {
Segment* s = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0,1));
if (!s->enabled())
s->setEnabled(true);
}
m->addSystemHeader(true);
pos.rx() += system->leftMargin();
firstMeasure = false;
}
else if (m->header())
m->removeSystemHeader();
if (m->trailer())
m->removeSystemTrailer();
if (m->tick() >= lc.startTick && m->tick() <= lc.endTick) {
// for measures in range, do full layout
m->createEndBarLines(false);
m->computeMinWidth();
ww = m->width();
m->stretchMeasure(ww);
}
else {
// for measures not in range, use existing layout
ww = m->width();
if (m->pos() != pos) {
// fix beam positions
// other elements with system as parent are processed in layoutSystemElements()
// but full beam processing is expensive and not needed if we adjust position here
QPointF p = pos - m->pos();
for (const Segment& s : m->segments()) {
if (!s.isChordRestType())
continue;
for (int track = 0; track < ntracks(); ++track) {
Element* e = s.element(track);
if (e) {
ChordRest* cr = toChordRest(e);
if (cr->beam() && cr->beam()->elements().front() == cr)
cr->beam()->rpos() += p;
}
}
}
}
}
m->setPos(pos);
m->layoutStaffLines();
}
else if (lc.curMeasure->isHBox()) {
lc.curMeasure->setPos(pos + QPointF(toHBox(lc.curMeasure)->topGap(), 0.0));
lc.curMeasure->layout();
ww = lc.curMeasure->width();
}
pos.rx() += ww;
getNextMeasure(lc);
}
system->setWidth(pos.x());
}
//---------------------------------------------------------
// layoutLinear
//---------------------------------------------------------
void Score::layoutLinear(bool layoutAll, LayoutContext& lc)
{
lc.score = this;
resetSystems(layoutAll, lc);
collectLinearSystem(lc);
// hideEmptyStaves(systems().front(), true); this does not make sense
lc.layoutLinear();
}
//---------------------------------------------------------
// layoutLinear
//---------------------------------------------------------
void LayoutContext::layoutLinear()
{
System* system = score->systems().front();
score->layoutSystemElements(system, *this);
system->layout2(); // compute staff distances
for (MeasureBase* mb : system->measures()) {
if (!mb->isMeasure())
continue;
Measure* m = toMeasure(mb);
for (int track = 0; track < score->ntracks(); ++track) {
for (Segment* segment = m->first(); segment; segment = segment->next()) {
Element* e = segment->element(track);
if (!e)
continue;
if (e->isChordRest()) {
if (m->tick() < startTick || m->tick() > endTick)
continue;
if (!score->staff(track2staff(track))->show())
continue;
ChordRest* cr = toChordRest(e);
if (notTopBeam(cr)) // layout cross staff beams
cr->beam()->layout();
if (notTopTuplet(cr)) {
// fix layout of tuplets
DurationElement* de = cr;
while (de->tuplet() && de->tuplet()->elements().front() == de) {
Tuplet* t = de->tuplet();
t->layout();
de = de->tuplet();
}
}
if (cr->isChord()) {
Chord* c = toChord(cr);
for (Chord* cc : c->graceNotes()) {
if (cc->beam() && cc->beam()->elements().front() == cc)
cc->beam()->layout();
for (Note* n : cc->notes()) {
Tie* tie = n->tieFor();
if (tie)
tie->layout();
for (Spanner* sp : n->spannerFor())
sp->layout();
}
for (Element* element : cc->el()) {
if (element->isSlur())
element->layout();
}
}
c->layoutArpeggio2();
for (Note* n : c->notes()) {
Tie* tie = n->tieFor();
if (tie)
tie->layout();
for (Spanner* sp : n->spannerFor())
sp->layout();
}
if (c->tremolo()) {
Tremolo* t = c->tremolo();
Chord* c1 = t->chord1();
Chord* c2 = t->chord2();
if (t->twoNotes() && c1 && c2 && (c1->staffMove() || c2->staffMove()))
t->layout();
}
}
}
else if (e->isBarLine())
toBarLine(e)->layout2();
}
}
m->layout2();
}
page->setPos(0, 0);
system->setPos(page->lm(), page->tm() + score->styleP(Sid::staffUpperBorder));
page->setWidth(system->width() + system->pos().x());
// Set buffer space after the last system to avoid problems with mouse input.
// Mouse input divides space between systems equally (see Score::searchSystem),
// hence the choice of the value.
const qreal buffer = 0.5 * score->styleS(Sid::maxSystemDistance).val() * score->spatium();
page->setHeight(system->height() + system->pos().y() + buffer);
page->rebuildBspTree();
}
} // namespace Ms