MuseScore/libmscore/system.cpp
MarcSabatella 10b3da92db reimplement firstVisibleSysStaff and lastVisibleSysStaff to return int
Previosly, these functions returned the SysStaff itself,
but there is no way to get the numeric index from that.
The functions now return the numeric index,
and their callers are adjusted to derived the SysStaff from that
(if necessary, in one case the index was the goal all along).
2019-11-20 13:42:09 -07:00

1407 lines
49 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
/**
\file
Implementation of classes SysStaff and System.
*/
#include "system.h"
#include "measure.h"
#include "segment.h"
#include "score.h"
#include "sig.h"
#include "key.h"
#include "xml.h"
#include "clef.h"
#include "text.h"
#include "navigate.h"
#include "select.h"
#include "staff.h"
#include "part.h"
#include "page.h"
#include "style.h"
#include "bracket.h"
#include "mscore.h"
#include "barline.h"
#include "system.h"
#include "box.h"
#include "chordrest.h"
#include "iname.h"
#include "spanner.h"
#include "sym.h"
#include "spacer.h"
#include "systemdivider.h"
#include "textframe.h"
#include "stafflines.h"
#include "bracketItem.h"
namespace Ms {
//---------------------------------------------------------
// ~SysStaff
//---------------------------------------------------------
SysStaff::~SysStaff()
{
qDeleteAll(instrumentNames);
}
//---------------------------------------------------------
// System
//---------------------------------------------------------
System::System(Score* s)
: Element(s)
{
}
//---------------------------------------------------------
// ~System
//---------------------------------------------------------
System::~System()
{
for (SpannerSegment* ss : spannerSegments()) {
if (ss->system() == this)
ss->setParent(nullptr);
}
for (MeasureBase* mb : measures()) {
if (mb->system() == this)
mb->setSystem(nullptr);
}
qDeleteAll(_staves);
qDeleteAll(_brackets);
delete _systemDividerLeft;
delete _systemDividerRight;
}
//---------------------------------------------------------
/// clear
/// Clear layout of System
//---------------------------------------------------------
void System::clear()
{
for (MeasureBase* mb : measures()) {
if (mb->system() == this)
mb->setSystem(nullptr);
}
ml.clear();
for (SpannerSegment* ss : _spannerSegments) {
if (ss->system() == this)
ss->setParent(0); // assume parent() is System
}
_spannerSegments.clear();
// _systemDividers are reused
}
//---------------------------------------------------------
// appendMeasure
//---------------------------------------------------------
void System::appendMeasure(MeasureBase* mb)
{
Q_ASSERT(!mb->isMeasure() || !(score()->styleB(Sid::createMultiMeasureRests) && toMeasure(mb)->hasMMRest()));
mb->setSystem(this);
ml.push_back(mb);
}
//---------------------------------------------------------
// removeMeasure
//---------------------------------------------------------
void System::removeMeasure(MeasureBase* mb)
{
ml.erase(std::remove(ml.begin(), ml.end(), mb), ml.end());
if (mb->system() == this)
mb->setSystem(nullptr);
}
//---------------------------------------------------------
// removeLastMeasure
//---------------------------------------------------------
void System::removeLastMeasure()
{
if (ml.empty())
return;
MeasureBase* mb = ml.back();
ml.pop_back();
if (mb->system() == this)
mb->setSystem(nullptr);
}
//---------------------------------------------------------
// vbox
// a system can only contain one vertical frame
//---------------------------------------------------------
Box* System::vbox() const
{
if (!ml.empty()) {
if (ml[0]->isVBox() || ml[0]->isTBox())
return toBox(ml[0]);
}
return 0;
}
//---------------------------------------------------------
// insertStaff
//---------------------------------------------------------
SysStaff* System::insertStaff(int idx)
{
SysStaff* staff = new SysStaff;
if (idx) {
// HACK: guess position
staff->bbox().setY(_staves[idx-1]->y() + 6 * spatium());
}
_staves.insert(idx, staff);
return staff;
}
//---------------------------------------------------------
// removeStaff
//---------------------------------------------------------
void System::removeStaff(int idx)
{
_staves.takeAt(idx);
}
//---------------------------------------------------------
// adjustStavesNumber
//---------------------------------------------------------
void System::adjustStavesNumber(int nstaves)
{
for (int i = _staves.size(); i < nstaves; ++i)
insertStaff(i);
const int dn = _staves.size() - nstaves;
for (int i = 0; i < dn; ++i)
removeStaff(_staves.size() - 1);
}
//---------------------------------------------------------
// layoutSystem
/// Layout the System
//---------------------------------------------------------
void System::layoutSystem(qreal xo1)
{
if (_staves.empty()) // ignore vbox
return;
static const Spatium instrumentNameOffset(1.0); // TODO: make style value
int nstaves = _staves.size();
//---------------------------------------------------
// find x position of staves
// create brackets
//---------------------------------------------------
qreal xoff2 = 0.0; // x offset for instrument name
int columns = getBracketsColumnsCount();
#if (!defined (_MSCVER) && !defined (_MSC_VER))
qreal bracketWidth[columns];
#else
// MSVC does not support VLA. Replace with std::vector. If profiling determines that the
// heap allocation is slow, an optimization might be used.
std::vector<qreal> bracketWidth(columns);
#endif
for (int i = 0; i < columns; ++i)
bracketWidth[i] = 0.0;
QList<Bracket*> bl;
bl.swap(_brackets);
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
Staff* s = score()->staff(staffIdx);
for (int i = 0; i < columns; ++i) {
for (auto bi : s->brackets()) {
if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET)
continue;
Bracket* b = createBracket(bi, i, staffIdx, bl, this->firstMeasure());
if (b != nullptr) bracketWidth[i] = qMax(bracketWidth[i], b->width());
}
}
if (!staff(staffIdx)->show())
continue;
for (InstrumentName* t : _staves[staffIdx]->instrumentNames) {
t->layout();
qreal w = t->width() + point(instrumentNameOffset);
if (w > xoff2)
xoff2 = w;
}
}
for (Bracket* b : bl)
delete b;
//---------------------------------------------------
// layout SysStaff and StaffLines
//---------------------------------------------------
_leftMargin = xoff2;
qreal bd = score()->styleP(Sid::bracketDistance);
if (!_brackets.empty()) {
for (int w : bracketWidth)
_leftMargin += w + bd;
}
int nVisible = 0;
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
SysStaff* s = _staves[staffIdx];
Staff* staff = score()->staff(staffIdx);
if (!staff->show() || !s->show()) {
s->setbbox(QRectF());
continue;
}
++nVisible;
qreal staffMag = staff->mag(Fraction(0,1)); // ??? TODO
int staffLines = staff->lines(Fraction(0,1));
if (staffLines == 1) {
qreal h = staff->lineDistance(Fraction(0,1)) * staffMag * spatium();
s->bbox().setRect(_leftMargin + xo1, -h, 0.0, 2 * h);
}
else {
qreal h = (staffLines - 1) * staff->lineDistance(Fraction(0,1));
h = h * staffMag * spatium();
s->bbox().setRect(_leftMargin + xo1, 0.0, 0.0, h);
}
}
//---------------------------------------------------
// layout brackets
//---------------------------------------------------
setBracketsXPosition(xo1 + _leftMargin);
//---------------------------------------------------
// layout instrument names x position
//---------------------------------------------------
int idx = 0;
for (const Part* p : score()->parts()) {
SysStaff* s = staff(idx);
if (s->show() && p->show()) {
for (InstrumentName* t : s->instrumentNames) {
switch (int(t->align()) & int(Align::HMASK)) {
case int(Align::LEFT):
t->rxpos() = 0;
break;
case int(Align::HCENTER):
t->rxpos() = (xoff2 - point(instrumentNameOffset) + xo1) * .5;
break;
case int(Align::RIGHT):
default:
t->rxpos() = xoff2 - point(instrumentNameOffset) + xo1;
break;
}
// t->rxpos() += t->offset(t->spatium()).x();
}
}
idx += p->nstaves();
}
}
//---------------------------------------------------------
// addBrackets
// Add brackets in front of this measure, typically behind a HBox
//---------------------------------------------------------
void System::addBrackets(Measure* measure)
{
if (_staves.empty()) // ignore vbox
return;
int nstaves = _staves.size();
//---------------------------------------------------
// find x position of staves
// create brackets
//---------------------------------------------------
int columns = getBracketsColumnsCount();
QList<Bracket*> bl;
bl.swap(_brackets);
for (int staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
Staff* s = score()->staff(staffIdx);
for (int i = 0; i < columns; ++i) {
for (auto bi : s->brackets()) {
if (bi->column() != i || bi->bracketType() == BracketType::NO_BRACKET)
continue;
createBracket(bi, i, staffIdx, bl, measure);
}
}
if (!staff(staffIdx)->show())
continue;
}
//---------------------------------------------------
// layout brackets
//---------------------------------------------------
setBracketsXPosition(measure->x());
_brackets.append(bl);
}
//---------------------------------------------------------
// createBracket
// Create a bracket if it spans more then one visible system
// If measure is NULL adds the bracket in front of the system, else in front of the measure.
// Returns the bracket if it got created, else NULL
//---------------------------------------------------------
Bracket* System::createBracket(Ms::BracketItem* bi, int column, int staffIdx, QList<Ms::Bracket *>& bl, Measure* measure)
{
int nstaves = _staves.size();
int firstStaff = staffIdx;
int lastStaff = staffIdx + bi->bracketSpan() - 1;
if (lastStaff >= nstaves)
lastStaff = nstaves - 1;
for (; firstStaff <= lastStaff; ++firstStaff) {
if (score()->staff(firstStaff)->show())
break;
}
for (; lastStaff >= firstStaff; --lastStaff) {
if (score()->staff(lastStaff)->show())
break;
}
int span = lastStaff - firstStaff + 1;
//
// do not show bracket if it only spans one
// system due to some invisible staves
//
if ((span > 1) || (bi->bracketSpan() == span)) {
//
// this bracket is visible
//
Bracket* b = 0;
int track = staffIdx * VOICES;
for (int k = 0; k < bl.size(); ++k) {
if (bl[k]->track() == track && bl[k]->column() == column && bl[k]->bracketType() == bi->bracketType() && bl[k]->measure() == measure) {
b = bl.takeAt(k);
break;
}
}
if (b == 0) {
b = new Bracket(score());
b->setBracketItem(bi);
b->setGenerated(true);
b->setTrack(track);
b->setMeasure(measure);
}
add(b);
b->setStaffSpan(firstStaff, lastStaff);
return b;
}
return nullptr;
}
int System::getBracketsColumnsCount()
{
int columns = 0;
int nstaves = _staves.size();
for (int idx = 0; idx < nstaves; ++idx) {
for (auto bi : score()->staff(idx)->brackets())
columns = qMax(columns, bi->column() + 1);
}
return columns;
}
void System::setBracketsXPosition(const qreal xPosition)
{
qreal bracketDistance = score()->styleP(Sid::bracketDistance);
for (Bracket* b1 : _brackets) {
qreal xOffset = 0;
for (const Bracket* b2 : _brackets) {
bool b1FirstStaffInB2 = (b1->firstStaff() >= b2->firstStaff() && b1->firstStaff() <= b2->lastStaff());
bool b1LastStaffInB2 = (b1->lastStaff() >= b2->firstStaff() && b1->lastStaff() <= b2->lastStaff());
if (b1->column() > b2->column() &&
(b1FirstStaffInB2 || b1LastStaffInB2))
xOffset += b2->width() + bracketDistance;
}
b1->rxpos() = xPosition - xOffset - b1->width();
}
}
//---------------------------------------------------------
// nextVisibleStaff
//---------------------------------------------------------
int System::nextVisibleStaff(int staffIdx) const
{
int i;
for (i = staffIdx + 1; i < _staves.size(); ++i) {
Staff* s = score()->staff(i);
SysStaff* ss = _staves[i];
if (s->show() && ss->show())
break;
}
return i;
}
//---------------------------------------------------------
// firstVisibleStaff
//---------------------------------------------------------
int System::firstVisibleStaff() const
{
return nextVisibleStaff(-1);
}
//---------------------------------------------------------
// layout2
// called after measure layout
// adjusts staff distance
//---------------------------------------------------------
void System::layout2()
{
Box* vb = vbox();
if (vb) {
vb->layout();
setbbox(vb->bbox());
return;
}
setPos(0.0, 0.0);
QList<std::pair<int,SysStaff*>> visibleStaves;
for (int i = 0; i < _staves.size(); ++i) {
Staff* s = score()->staff(i);
SysStaff* ss = _staves[i];
if (s->show() && ss->show())
visibleStaves.append(std::pair<int,SysStaff*>(i, ss));
else
ss->setbbox(QRectF()); // already done in layout() ?
}
qreal _spatium = spatium();
qreal y = 0.0;
qreal minVerticalDistance = score()->styleP(Sid::minVerticalDistance);
qreal staffDistance = score()->styleP(Sid::staffDistance);
qreal akkoladeDistance = score()->styleP(Sid::akkoladeDistance);
if (visibleStaves.empty()) {
qDebug("====no visible staves, staves %d, score staves %d", _staves.size(), score()->nstaves());
return;
}
for (auto i = visibleStaves.begin();; ++i) {
SysStaff* ss = i->second;
int si1 = i->first;
Staff* staff = score()->staff(si1);
auto ni = i + 1;
qreal h = staff->height();
if (ni == visibleStaves.end()) {
// ss->setYOff(staff->lines(0) == 1 ? _spatium * staff->mag(0) : 0.0);
ss->setYOff(0.0);
ss->bbox().setRect(_leftMargin, y, width() - _leftMargin, h);
break;
}
int si2 = ni->first;
Staff* staff2 = score()->staff(si2);
qreal dist = h;
#if 1
if (staff->part() == staff2->part()) {
Measure* m = firstMeasure();
qreal mag = m ? staff->mag(m->tick()) : 1.0;
dist += akkoladeDistance * mag;
}
else {
dist += staffDistance;
}
#else
// TODO: provide style setting or brace property to allow braces to also define a grand staff
switch (staff2->innerBracket()) {
case BracketType::BRACE:
dist += akkoladeDistance;
break;
case BracketType::NORMAL:
case BracketType::SQUARE:
case BracketType::LINE:
case BracketType::NO_BRACKET:
dist += staffDistance;
break;
}
#endif
dist += staff2->userDist();
#if 0
for (MeasureBase* mb : ml) {
if (!mb->isMeasure())
continue;
Measure* m = toMeasure(mb);
Shape& s1 = m->staffShape(si1);
Shape& s2 = m->staffShape(si2);
qreal d = score()->lineMode() ? 0.0 : s1.minVerticalDistance(s2);
dist = qMax(dist, d + minVerticalDistance);
Spacer* sp = m->vspacerDown(si1);
if (sp) {
if (sp->spacerType() == SpacerType::FIXED) {
dist = staff->height() + sp->gap();
break;
}
else
dist = qMax(dist, staff->height() + sp->gap());
}
sp = m->vspacerUp(si2);
if (sp)
dist = qMax(dist, sp->gap());
}
#else
bool fixedSpace = false;
for (MeasureBase* mb : ml) {
if (!mb->isMeasure())
continue;
Measure* m = toMeasure(mb);
Spacer* sp = m->vspacerDown(si1);
if (sp) {
if (sp->spacerType() == SpacerType::FIXED) {
dist = staff->height() + sp->gap();
fixedSpace = true;
break;
}
else
dist = qMax(dist, staff->height() + sp->gap());
}
sp = m->vspacerUp(si2);
if (sp)
dist = qMax(dist, sp->gap() + h);
}
if (!fixedSpace) {
qreal d = score()->lineMode() ? 0.0 : ss->skyline().minDistance(System::staff(si2)->skyline());
dist = qMax(dist, d + minVerticalDistance);
}
#endif
// ss->setYOff(staff->lines(0) == 1 ? _spatium * staff->mag(0) : 0.0);
ss->setYOff(0.0);
ss->bbox().setRect(_leftMargin, y, width() - _leftMargin, h);
y += dist;
}
qreal systemHeight = staff(visibleStaves.back().first)->bbox().bottom();
setHeight(systemHeight);
for (MeasureBase* m : ml) {
if (m->isMeasure()) {
// note that the factor 2 * _spatium must be corrected for when exporting
// system distance in MusicXML (issue #24733)
m->bbox().setRect(0.0, -_spatium, m->width(), systemHeight + 2.0 * _spatium);
}
else if (m->isHBox()) {
m->bbox().setRect(0.0, 0.0, m->width(), systemHeight);
toHBox(m)->layout2();
}
else if (m->isTBox()) {
// m->bbox().setRect(0.0, 0.0, m->width(), systemHeight);
toTBox(m)->layout();
}
else
qDebug("unhandled measure type %s", m->name());
}
//---------------------------------------------------
// layout brackets vertical position
//---------------------------------------------------
for (Bracket* b : _brackets) {
int staffIdx1 = b->firstStaff();
int staffIdx2 = b->lastStaff();
qreal sy = 0; // assume bracket not visible
qreal ey = 0;
// if start staff not visible, try next staff
while (staffIdx1 <= staffIdx2 && !_staves[staffIdx1]->show())
++staffIdx1;
// if end staff not visible, try prev staff
while (staffIdx1 <= staffIdx2 && !_staves[staffIdx2]->show())
--staffIdx2;
// the bracket will be shown IF:
// it spans at least 2 visible staves (staffIdx1 < staffIdx2) OR
// it spans just one visible staff (staffIdx1 == staffIdx2) but it is required to do so
// (the second case happens at least when the bracket is initially dropped)
bool notHidden = (staffIdx1 < staffIdx2) || (b->span() == 1 && staffIdx1 == staffIdx2);
if (notHidden) { // set vert. pos. and height to visible spanned staves
sy = _staves[staffIdx1]->bbox().top();
ey = _staves[staffIdx2]->bbox().bottom();
}
b->rypos() = sy;
b->setHeight(ey - sy);
b->layout();
}
//---------------------------------------------------
// layout instrument names
//---------------------------------------------------
int staffIdx = 0;
for (Part* p : score()->parts()) {
SysStaff* s = staff(staffIdx);
SysStaff* s2;
int nstaves = p->nstaves();
if (s->show()) {
for (InstrumentName* t : s->instrumentNames) {
//
// override Text->layout()
//
qreal y1, y2;
switch (t->layoutPos()) {
default:
case 0: // center at part
y1 = s->bbox().top();
s2 = staff(staffIdx);
for (int i = staffIdx + nstaves - 1; i > 0; --i) {
SysStaff* s3 = staff(i);
if (s3->show()) {
s2 = s3;
break;
}
}
y2 = s2->bbox().bottom();
break;
case 1: // center at first staff
y1 = s->bbox().top();
y2 = s->bbox().bottom();
break;
// TODO:
// sort out invisible staves
case 2: // center between first and second staff
y1 = s->bbox().top();
y2 = staff(staffIdx + 1)->bbox().bottom();
break;
case 3: // center at second staff
y1 = staff(staffIdx + 1)->bbox().top();
y2 = staff(staffIdx + 1)->bbox().bottom();
break;
case 4: // center between first and second staff
y1 = staff(staffIdx + 1)->bbox().top();
y2 = staff(staffIdx + 2)->bbox().bottom();
break;
case 5: // center at third staff
y1 = staff(staffIdx + 2)->bbox().top();
y2 = staff(staffIdx + 2)->bbox().bottom();
break;
}
t->rypos() = y1 + (y2 - y1) * .5 + t->offset().y();
}
}
staffIdx += nstaves;
}
//---------------------------------------------------
// layout cross-staff slurs and ties
//---------------------------------------------------
Fraction stick = measures().front()->tick();
Fraction etick = measures().back()->endTick();
auto spanners = score()->spannerMap().findOverlapping(stick.ticks(), etick.ticks());
std::vector<Spanner*> spanner;
for (auto interval : spanners) {
Spanner* sp = interval.value;
if (sp->tick() < etick && sp->tick2() >= stick) {
if (sp->isSlur()) {
ChordRest* scr = sp->startCR();
ChordRest* ecr = sp->endCR();
int idx = sp->vStaffIdx();
if (scr && ecr && (scr->vStaffIdx() != idx || ecr->vStaffIdx() != idx))
sp->layoutSystem(this);
}
}
}
}
//---------------------------------------------------------
// setInstrumentNames
//---------------------------------------------------------
void System::setInstrumentNames(bool longName, Fraction tick)
{
//
// remark: add/remove instrument names is not undo/redoable
// as add/remove of systems is not undoable
//
if (vbox()) // ignore vbox
return;
if (!score()->showInstrumentNames()
|| (score()->styleB(Sid::hideInstrumentNameIfOneInstrument) && score()->parts().size() == 1)) {
for (SysStaff* staff : _staves) {
foreach (InstrumentName* t, staff->instrumentNames)
score()->removeElement(t);
}
return;
}
int staffIdx = 0;
for (SysStaff* staff : _staves) {
Staff* s = score()->staff(staffIdx);
if (!s->isTop() || !s->show()) {
for (InstrumentName* t : staff->instrumentNames)
score()->removeElement(t);
++staffIdx;
continue;
}
Part* part = s->part();
const QList<StaffName>& names = longName? part->longNames(tick) : part->shortNames(tick);
int idx = 0;
for (const StaffName& sn : names) {
InstrumentName* iname = staff->instrumentNames.value(idx);
if (iname == 0) {
iname = new InstrumentName(score());
// iname->setGenerated(true);
iname->setParent(this);
iname->setTrack(staffIdx * VOICES);
iname->setInstrumentNameType(longName ? InstrumentNameType::LONG : InstrumentNameType::SHORT);
iname->setLayoutPos(sn.pos());
score()->addElement(iname);
}
iname->setXmlText(sn.name());
++idx;
}
for (; idx < staff->instrumentNames.size(); ++idx)
score()->removeElement(staff->instrumentNames[idx]);
++staffIdx;
}
}
//---------------------------------------------------------
// y2staff
//---------------------------------------------------------
/**
Return staff number for canvas relative y position \a y
or -1 if not found.
To allow drag and drop above and below the staff, the actual y range
considered "inside" the staff is increased by "margin".
*/
int System::y2staff(qreal y) const
{
y -= pos().y();
int idx = 0;
qreal margin = spatium() * 2;
foreach (SysStaff* s, _staves) {
qreal y1 = s->bbox().top() - margin;
qreal y2 = s->bbox().bottom() + margin;
if (y >= y1 && y < y2)
return idx;
++idx;
}
return -1;
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void System::add(Element* el)
{
if (!el)
return;
// qDebug("%p System::add: %p %s", this, el, el->name());
el->setParent(this);
switch(el->type()) {
case ElementType::INSTRUMENT_NAME:
// qDebug(" staffIdx %d, staves %d", el->staffIdx(), _staves.size());
_staves[el->staffIdx()]->instrumentNames.append(toInstrumentName(el));
break;
case ElementType::BEAM:
score()->addElement(el);
break;
case ElementType::BRACKET: {
Bracket* b = toBracket(el);
#if 0
int staffIdx = b->staffIdx();
int column = b->column();
if (column == -1) {
column = 0;
for (const Bracket* bb : _brackets) {
if (staffIdx >= bb->firstStaff() && staffIdx <= bb->lastStaff())
++column;
}
// b->setLevel(column);
// b->setSpan(1);
}
// b->staff()->setBracket(column, b->bracketType());
// b->staff()->setBracketSpan(column, b->span());
#endif
_brackets.append(b);
}
break;
case ElementType::MEASURE:
case ElementType::HBOX:
case ElementType::VBOX:
case ElementType::TBOX:
case ElementType::FBOX:
score()->addElement(el);
break;
case ElementType::TEXTLINE_SEGMENT:
case ElementType::HAIRPIN_SEGMENT:
case ElementType::OTTAVA_SEGMENT:
case ElementType::TRILL_SEGMENT:
case ElementType::VIBRATO_SEGMENT:
case ElementType::VOLTA_SEGMENT:
case ElementType::SLUR_SEGMENT:
case ElementType::TIE_SEGMENT:
case ElementType::PEDAL_SEGMENT:
case ElementType::LYRICSLINE_SEGMENT:
case ElementType::GLISSANDO_SEGMENT:
case ElementType::LET_RING_SEGMENT:
case ElementType::PALM_MUTE_SEGMENT:
{
SpannerSegment* ss = toSpannerSegment(el);
#ifndef NDEBUG
if (_spannerSegments.contains(ss))
qDebug("System::add() %s %p already there", ss->name(), ss);
else
#endif
_spannerSegments.append(ss);
}
break;
case ElementType::SYSTEM_DIVIDER:
{
SystemDivider* sd = toSystemDivider(el);
if (sd->dividerType() == SystemDivider::Type::LEFT)
_systemDividerLeft = sd;
else
_systemDividerRight = sd;
}
break;
default:
qDebug("System::add(%s) not implemented", el->name());
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void System::remove(Element* el)
{
switch (el->type()) {
case ElementType::INSTRUMENT_NAME:
_staves[el->staffIdx()]->instrumentNames.removeOne(toInstrumentName(el));
break;
case ElementType::BEAM:
score()->removeElement(el);
break;
case ElementType::BRACKET:
{
Bracket* b = toBracket(el);
if (!_brackets.removeOne(b))
qDebug("System::remove: bracket not found");
}
break;
case ElementType::MEASURE:
case ElementType::HBOX:
case ElementType::VBOX:
case ElementType::TBOX:
case ElementType::FBOX:
score()->removeElement(el);
break;
case ElementType::TEXTLINE_SEGMENT:
case ElementType::HAIRPIN_SEGMENT:
case ElementType::OTTAVA_SEGMENT:
case ElementType::TRILL_SEGMENT:
case ElementType::VIBRATO_SEGMENT:
case ElementType::VOLTA_SEGMENT:
case ElementType::SLUR_SEGMENT:
case ElementType::TIE_SEGMENT:
case ElementType::PEDAL_SEGMENT:
case ElementType::LYRICSLINE_SEGMENT:
case ElementType::GLISSANDO_SEGMENT:
if (!_spannerSegments.removeOne(toSpannerSegment(el))) {
qDebug("System::remove: %p(%s) not found, score %p", el, el->name(), score());
Q_ASSERT(score() == el->score());
}
break;
case ElementType::SYSTEM_DIVIDER:
if (el == _systemDividerLeft)
_systemDividerLeft = 0;
else {
Q_ASSERT(_systemDividerRight == el);
_systemDividerRight = 0;
}
break;
default:
qDebug("System::remove(%s) not implemented", el->name());
break;
}
}
//---------------------------------------------------------
// change
//---------------------------------------------------------
void System::change(Element* o, Element* n)
{
remove(o);
add(n);
}
//---------------------------------------------------------
// snap
//---------------------------------------------------------
Fraction System::snap(const Fraction& tick, const QPointF p) const
{
for (const MeasureBase* m : ml) {
if (p.x() < m->x() + m->width())
return toMeasure(m)->snap(tick, p - m->pos()); //TODO: MeasureBase
}
return toMeasure(ml.back())->snap(tick, p-pos()); //TODO: MeasureBase
}
//---------------------------------------------------------
// snap
//---------------------------------------------------------
Fraction System::snapNote(const Fraction& tick, const QPointF p, int staff) const
{
for (const MeasureBase* m : ml) {
if (p.x() < m->x() + m->width())
return toMeasure(m)->snapNote(tick, p - m->pos(), staff); //TODO: MeasureBase
}
return toMeasure(ml.back())->snap(tick, p-pos()); // TODO: MeasureBase
}
//---------------------------------------------------------
// firstMeasure
//---------------------------------------------------------
Measure* System::firstMeasure() const
{
auto i = std::find_if(ml.begin(), ml.end(), [](MeasureBase* mb){ return mb->isMeasure(); });
return i != ml.end() ? toMeasure(*i) : 0;
}
//---------------------------------------------------------
// lastMeasure
//---------------------------------------------------------
Measure* System::lastMeasure() const
{
auto i = std::find_if(ml.rbegin(), ml.rend(), [](MeasureBase* mb){return mb->isMeasure();});
return i != ml.rend() ? toMeasure(*i) : 0;
}
//---------------------------------------------------------
// nextMeasure
//---------------------------------------------------------
MeasureBase* System::nextMeasure(const MeasureBase* m) const
{
if (m == ml.back())
return 0;
MeasureBase* nm = m->next();
if (nm->isMeasure() && score()->styleB(Sid::createMultiMeasureRests) && toMeasure(nm)->hasMMRest())
nm = toMeasure(nm)->mmRest();
return nm;
}
//---------------------------------------------------------
// scanElements
// collect all visible elements
//---------------------------------------------------------
void System::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
if (vbox())
return;
for (Bracket* b : _brackets)
func(data, b);
if (_systemDividerLeft)
func(data, _systemDividerLeft);
if (_systemDividerRight)
func(data, _systemDividerRight);
int idx = 0;
for (const SysStaff* st : _staves) {
if (all || st->show()) {
for (InstrumentName* t : st->instrumentNames)
func(data, t);
}
++idx;
}
for (SpannerSegment* ss : _spannerSegments) {
int staffIdx = ss->spanner()->staffIdx();
if (staffIdx == -1) {
qDebug("System::scanElements: staffIDx == -1: %s %p", ss->spanner()->name(), ss->spanner());
staffIdx = 0;
}
bool v = true;
Spanner* spanner = ss->spanner();
if (spanner->anchor() == Spanner::Anchor::SEGMENT || spanner->anchor() == Spanner::Anchor::CHORD) {
Element* se = spanner->startElement();
Element* ee = spanner->endElement();
bool v1 = true;
if (se && se->isChordRest()) {
ChordRest* cr = toChordRest(se);
Measure* m = cr->measure();
v1 = m->visible(cr->staffIdx());
}
bool v2 = true;
if (!v1 && ee && ee->isChordRest()) {
ChordRest* cr = toChordRest(ee);
Measure* m = cr->measure();
v2 = m->visible(cr->staffIdx());
}
v = v1 || v2; // hide spanner if both chords are hidden
}
if (all || (score()->staff(staffIdx)->show() && _staves[staffIdx]->show() && v) || spanner->isVolta())
ss->scanElements(data, func, all);
}
}
//---------------------------------------------------------
// staffYpage
// return page coordinates
//---------------------------------------------------------
qreal System::staffYpage(int staffIdx) const
{
if (_staves.size() <= staffIdx || staffIdx < 0) {
qFatal("staffY: staves %d: bad staffIdx %d", _staves.size(), staffIdx);
return pagePos().y();
}
return _staves[staffIdx]->y() + y();
}
//---------------------------------------------------------
// staffCanvasYpage
// return canvas coordinates
//---------------------------------------------------------
qreal System::staffCanvasYpage(int staffIdx) const
{
return _staves[staffIdx]->y() + y() + page()->canvasPos().y();
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void System::write(XmlWriter& xml) const
{
xml.stag(this);
if (_systemDividerLeft && _systemDividerLeft->isUserModified())
_systemDividerLeft->write(xml);
if (_systemDividerRight && _systemDividerRight->isUserModified())
_systemDividerRight->write(xml);
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void System::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "SystemDivider") {
SystemDivider* sd = new SystemDivider(score());
sd->read(e);
add(sd);
}
else
e.unknown();
}
}
//---------------------------------------------------------
// nextSegmentElement
//---------------------------------------------------------
Element* System::nextSegmentElement()
{
Measure* m = firstMeasure();
if (m) {
Segment* firstSeg = m->segments().first();
if (firstSeg)
return firstSeg->element(0);
}
return score()->lastElement();
}
//---------------------------------------------------------
// prevSegmentElement
//---------------------------------------------------------
Element* System::prevSegmentElement()
{
Segment* seg = firstMeasure()->first();
Element* re = 0;
while (!re) {
seg = seg->prev1MM();
if (!seg)
return score()->firstElement();
if (seg->segmentType() == SegmentType::EndBarLine)
score()->inputState().setTrack((score()->staves().size() - 1) * VOICES); //corection
re = seg->lastElement(score()->staves().size() - 1);
}
return re;
}
//---------------------------------------------------------
// minDistance
// Return the minimum distance between this system and s2
// without any element collisions.
//
// this - top system
// s2 - bottom system
//---------------------------------------------------------
qreal System::minDistance(System* s2) const
{
if (vbox() && !s2->vbox())
return qMax(vbox()->bottomGap(), s2->minTop());
else if (!vbox() && s2->vbox())
return qMax(s2->vbox()->topGap(), minBottom());
else if (vbox() && s2->vbox())
return s2->vbox()->topGap() + vbox()->bottomGap();
qreal minVerticalDistance = score()->styleP(Sid::minVerticalDistance);
qreal dist = score()->styleP(Sid::minSystemDistance);
int firstStaff;
int lastStaff;
for (firstStaff = 0; firstStaff < _staves.size()-1; ++firstStaff) {
if (score()->staff(firstStaff)->show() && s2->staff(firstStaff)->show())
break;
}
for (lastStaff = _staves.size() -1; lastStaff > 0; --lastStaff) {
if (score()->staff(lastStaff)->show() && staff(lastStaff)->show())
break;
}
dist = qMax(dist, score()->staff(firstStaff)->userDist());
fixedDownDistance = false;
for (MeasureBase* mb1 : ml) {
if (mb1->isMeasure()) {
Measure* m = toMeasure(mb1);
Spacer* sp = m->vspacerDown(lastStaff);
if (sp) {
if (sp->spacerType() == SpacerType::FIXED) {
dist = sp->gap();
fixedDownDistance = true;
break;
}
else
dist = qMax(dist, sp->gap());
}
}
}
if (!fixedDownDistance) {
for (MeasureBase* mb2 : s2->ml) {
if (mb2->isMeasure()) {
Measure* m = toMeasure(mb2);
Spacer* sp = m->vspacerUp(firstStaff);
if (sp)
dist = qMax(dist, sp->gap());
}
}
qreal sld = staff(lastStaff)->skyline().minDistance(s2->staff(firstStaff)->skyline());
sld -= staff(lastStaff)->bbox().height() - minVerticalDistance;
dist = qMax(dist, sld);
// dist = dist - staff(lastStaff)->bbox().height() + minVerticalDistance;
}
return dist;
}
//---------------------------------------------------------
// topDistance
// return minimum distance to the above south skyline
//---------------------------------------------------------
qreal System::topDistance(int staffIdx, const SkylineLine& s) const
{
Q_ASSERT(!vbox());
Q_ASSERT(!s.isNorth());
if (score()->lineMode())
return 0.0;
return s.minDistance(staff(staffIdx)->skyline().north());
}
//---------------------------------------------------------
// bottomDistance
//---------------------------------------------------------
qreal System::bottomDistance(int staffIdx, const SkylineLine& s) const
{
Q_ASSERT(!vbox());
Q_ASSERT(s.isNorth());
if (score()->lineMode())
return 0.0;
return staff(staffIdx)->skyline().south().minDistance(s);
}
//---------------------------------------------------------
// firstVisibleSysStaff
//---------------------------------------------------------
int System::firstVisibleSysStaff() const
{
int nstaves = _staves.size();
for (int i = 0; i < nstaves; ++i) {
if (_staves[i]->show())
return i;
}
qDebug("no sys staff");
return -1;
}
//---------------------------------------------------------
// lastVisibleSysStaff
//---------------------------------------------------------
int System::lastVisibleSysStaff() const
{
int nstaves = _staves.size();
for (int i = nstaves - 1; i >= 0; --i) {
if (_staves[i]->show())
return i;
}
qDebug("no sys staff");
return -1;
}
//---------------------------------------------------------
// minTop
// Return the minimum top margin.
//---------------------------------------------------------
qreal System::minTop() const
{
int si = firstVisibleSysStaff();
SysStaff* s = si < 0 ? nullptr : staff(si);
if (s)
return -s->skyline().north().max();
return 0.0;
}
//---------------------------------------------------------
// minBottom
// Return the minimum bottom margin.
//---------------------------------------------------------
qreal System::minBottom() const
{
if (vbox())
return vbox()->bottomGap();
int si = lastVisibleSysStaff();
SysStaff* s = si < 0 ? nullptr : staff(si);
if (s)
return s->skyline().south().max() - s->bbox().height();
return 0.0;
}
//---------------------------------------------------------
// spacerDistance
// Return the distance needed due to spacers
//---------------------------------------------------------
qreal System::spacerDistance(bool up) const
{
int staff = up ? firstVisibleSysStaff() : lastVisibleSysStaff();
if (staff < 0)
return 0.0;
qreal dist = 0.0;
for (MeasureBase* mb : measures()) {
if (mb->isMeasure()) {
Measure* m = toMeasure(mb);
Spacer* sp = up ? m->vspacerUp(staff) : m->vspacerDown(staff);
if (sp) {
if (sp->spacerType() == SpacerType::FIXED) {
dist = sp->gap();
break;
}
else
dist = qMax(dist, sp->gap());
}
}
}
return dist;
}
//---------------------------------------------------------
// moveBracket
//---------------------------------------------------------
void System::moveBracket(int /*staffIdx*/, int /*srcCol*/, int /*dstCol*/)
{
#if 0
printf("System::moveBracket\n");
if (vbox())
return;
for (Bracket* b : _brackets) {
if (b->staffIdx() == staffIdx && b->column() == srcCol)
b->setLevel(dstCol);
}
#endif
}
//---------------------------------------------------------
// pageBreak
//---------------------------------------------------------
bool System::pageBreak() const
{
return ml.empty() ? false : ml.back()->pageBreak();
}
//---------------------------------------------------------
// endTick
//---------------------------------------------------------
Fraction System::endTick() const
{
return measures().back()->endTick();
}
}