MuseScore/libmscore/measurebase.cpp

619 lines
19 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// 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
//=============================================================================
#include "measurebase.h"
#include "measure.h"
#include "staff.h"
#include "score.h"
#include "chord.h"
#include "note.h"
#include "layoutbreak.h"
#include "image.h"
#include "segment.h"
#include "tempo.h"
2016-01-04 14:48:58 +01:00
#include "xml.h"
2016-10-18 15:41:00 +02:00
#include "system.h"
2016-12-18 14:31:13 +01:00
#include "stafftypechange.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// MeasureBase
//---------------------------------------------------------
MeasureBase::MeasureBase(Score* score)
: Element(score)
{
2016-10-20 11:32:07 +02:00
setIrregular(true);
2012-05-26 14:26:10 +02:00
}
MeasureBase::MeasureBase(const MeasureBase& m)
: Element(m)
{
2016-10-20 11:32:07 +02:00
_next = m._next;
_prev = m._prev;
_tick = m._tick;
_no = m._no;
_noOffset = m._noOffset;
2012-05-26 14:26:10 +02:00
2016-01-04 14:48:58 +01:00
for (Element* e : m._el)
2012-05-26 14:26:10 +02:00
add(e->clone());
}
//---------------------------------------------------------
// clearElements
//---------------------------------------------------------
void MeasureBase::clearElements()
{
qDeleteAll(_el);
_el.clear();
}
//---------------------------------------------------------
// takeElements
//---------------------------------------------------------
ElementList MeasureBase::takeElements()
{
ElementList l = _el;
_el.clear();
return l;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void MeasureBase::setScore(Score* score)
{
Element::setScore(score);
2016-02-09 09:20:54 +01:00
for (Element* e : _el)
2012-05-26 14:26:10 +02:00
e->setScore(score);
}
//---------------------------------------------------------
// MeasureBase
//---------------------------------------------------------
MeasureBase::~MeasureBase()
{
2016-02-09 09:20:54 +01:00
qDeleteAll(_el);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void MeasureBase::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
if (isMeasure()) {
2016-02-09 09:20:54 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (score()->tagIsValid(e->tag())) {
2014-11-10 14:18:42 +01:00
if (e->staffIdx() >= score()->staves().size())
qDebug("MeasureBase::scanElements: bad staffIdx %d in element %s", e->staffIdx(), e->name());
if ((e->track() == -1) || e->systemFlag() || ((Measure*)this)->visible(e->staffIdx()))
2012-05-26 14:26:10 +02:00
e->scanElements(data, func, all);
}
}
}
else {
2016-02-09 09:20:54 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (score()->tagIsValid(e->tag()))
e->scanElements(data, func, all);
}
}
if (isBox())
func(data, this);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// add
/// Add new Element \a el to MeasureBase
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void MeasureBase::add(Element* e)
{
e->setParent(this);
if (e->isLayoutBreak()) {
2016-03-30 22:33:04 +02:00
LayoutBreak* b = toLayoutBreak(e);
switch (b->layoutBreakType()) {
case LayoutBreak::PAGE:
2016-10-20 11:32:07 +02:00
setPageBreak(true);
setLineBreak(false);
setNoBreak(false);
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::LINE:
2016-10-20 11:32:07 +02:00
setPageBreak(false);
setLineBreak(true);
setSectionBreak(false);
setNoBreak(false);
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::SECTION:
2016-10-20 11:32:07 +02:00
setLineBreak(false);
setSectionBreak(true);
setNoBreak(false);
2016-10-13 14:28:00 +02:00
//does not work with repeats: score()->tempomap()->setPause(endTick(), b->pause());
2019-10-24 15:49:23 +02:00
triggerLayoutAll();
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::NOBREAK:
2016-10-20 11:32:07 +02:00
setPageBreak(false);
setLineBreak(false);
setSectionBreak(false);
setNoBreak(true);
break;
2012-05-26 14:26:10 +02:00
}
2016-12-09 15:56:48 +01:00
if (next())
2019-10-24 15:49:23 +02:00
next()->triggerLayout();
// triggerLayoutAll(); // TODO
2012-05-26 14:26:10 +02:00
}
2016-10-18 15:41:00 +02:00
triggerLayout();
2013-03-25 16:27:20 +01:00
_el.push_back(e);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// remove
/// Remove Element \a el from MeasureBase.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void MeasureBase::remove(Element* el)
{
2016-02-09 09:20:54 +01:00
if (el->isLayoutBreak()) {
2016-02-17 14:54:23 +01:00
LayoutBreak* lb = toLayoutBreak(el);
switch (lb->layoutBreakType()) {
case LayoutBreak::PAGE:
2016-10-20 11:32:07 +02:00
setPageBreak(false);
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::LINE:
2016-10-20 11:32:07 +02:00
setLineBreak(false);
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::SECTION:
2016-10-20 11:32:07 +02:00
setSectionBreak(false);
score()->setPause(endTick(), 0);
2019-10-24 15:49:23 +02:00
triggerLayoutAll();
2012-05-26 14:26:10 +02:00
break;
case LayoutBreak::NOBREAK:
2016-10-20 11:32:07 +02:00
setNoBreak(false);
break;
2012-05-26 14:26:10 +02:00
}
}
if (!_el.remove(el)) {
qDebug("MeasureBase(%p)::remove(%s,%p) not found", this, el->name(), el);
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// nextMeasure
//---------------------------------------------------------
Measure* MeasureBase::nextMeasure() const
{
2012-08-01 18:00:27 +02:00
MeasureBase* m = _next;
for (;;) {
2016-10-18 15:41:00 +02:00
if (m == 0 || m->isMeasure())
2012-08-01 18:00:27 +02:00
break;
m = m->_next;
2012-05-26 14:26:10 +02:00
}
2016-10-18 15:41:00 +02:00
return toMeasure(m);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// nextMeasureMM
//---------------------------------------------------------
Measure* MeasureBase::nextMeasureMM() const
{
Measure* mm = nextMeasure();
2018-03-27 15:36:00 +02:00
if (mm && score()->styleB(Sid::createMultiMeasureRests) && mm->hasMMRest())
return mm->mmRest();
return mm;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// prevMeasure
//---------------------------------------------------------
Measure* MeasureBase::prevMeasure() const
{
MeasureBase* m = prev();
while (m) {
2016-10-18 15:41:00 +02:00
if (m->isMeasure())
return toMeasure(m);
2012-05-26 14:26:10 +02:00
m = m->prev();
}
return 0;
}
2013-09-27 18:43:25 +02:00
//---------------------------------------------------------
// prevMeasure
//---------------------------------------------------------
Measure* MeasureBase::prevMeasureMM() const
{
MeasureBase* m = prev();
while (m) {
2016-10-18 15:41:00 +02:00
if (m->isMeasure()) {
Measure* mm = toMeasure(m);
2018-03-27 15:36:00 +02:00
if (score()->styleB(Sid::createMultiMeasureRests)) {
2013-09-27 18:43:25 +02:00
if (mm->mmRestCount() >= 0) {
if (mm->hasMMRest())
return mm->mmRest();
return mm;
}
}
else
return mm;
}
m = m->prev();
}
return 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// pause
//---------------------------------------------------------
qreal MeasureBase::pause() const
{
2016-10-20 11:32:07 +02:00
return sectionBreak() ? sectionBreakElement()->pause() : 0.0;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void MeasureBase::layout()
{
int breakCount = 0;
2016-02-09 09:20:54 +01:00
for (Element* element : _el) {
2012-05-26 14:26:10 +02:00
if (!score()->tagIsValid(element->tag()))
continue;
2016-02-09 09:20:54 +01:00
if (element->isLayoutBreak()) {
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
qreal x;
qreal y;
if (toLayoutBreak(element)->isNoBreak()) {
x = width() - element->width() * .5;
y = -(_spatium + element->height());
}
else {
x = -_spatium - element->width() + width()
2012-05-26 14:26:10 +02:00
- breakCount * (element->width() + _spatium * .8);
y = -2 * _spatium - element->height();
breakCount++;
}
2012-05-26 14:26:10 +02:00
element->setPos(x, y);
}
2018-02-01 10:37:12 +01:00
else if (element->isMarker() || element->isJump())
;
else
element->layout();
2012-05-26 14:26:10 +02:00
}
}
2019-10-24 15:49:23 +02:00
//---------------------------------------------------------
// triggerLayout
//---------------------------------------------------------
void MeasureBase::triggerLayout() const
{
if (prev() || next()) // avoid triggering layout before getting added to a score
score()->setLayout(tick(), -1, this);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// first
//---------------------------------------------------------
MeasureBase* Score::first() const
{
return _measures.first();
}
//---------------------------------------------------------
// last
//---------------------------------------------------------
MeasureBase* Score::last() const
{
return _measures.last();
}
2012-08-02 18:33:43 +02:00
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
QVariant MeasureBase::getProperty(Pid id) const
2012-08-02 18:33:43 +02:00
{
2016-02-09 09:20:54 +01:00
switch (id) {
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_END:
2016-02-04 11:27:47 +01:00
return repeatEnd();
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_START:
2016-02-04 11:27:47 +01:00
return repeatStart();
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_JUMP:
2016-02-04 11:27:47 +01:00
return repeatJump();
case Pid::NO_OFFSET:
return noOffset();
case Pid::IRREGULAR:
return irregular();
2012-08-02 18:33:43 +02:00
default:
return Element::getProperty(id);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
bool MeasureBase::setProperty(Pid id, const QVariant& value)
2012-08-02 18:33:43 +02:00
{
2016-02-09 09:20:54 +01:00
switch (id) {
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_END:
2016-02-04 11:27:47 +01:00
setRepeatEnd(value.toBool());
break;
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_START:
2016-02-04 11:27:47 +01:00
setRepeatStart(value.toBool());
break;
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_JUMP:
2016-02-04 11:27:47 +01:00
setRepeatJump(value.toBool());
2012-08-02 18:33:43 +02:00
break;
case Pid::NO_OFFSET:
setNoOffset(value.toInt());
break;
case Pid::IRREGULAR:
setIrregular(value.toBool());
break;
2012-08-02 18:33:43 +02:00
default:
2016-02-04 11:27:47 +01:00
if (!Element::setProperty(id, value))
2012-08-02 18:33:43 +02:00
return false;
break;
}
2019-10-24 15:49:23 +02:00
triggerLayoutAll();
score()->setPlaylistDirty();
2012-08-02 18:33:43 +02:00
return true;
}
//---------------------------------------------------------
2016-02-04 11:27:47 +01:00
// propertyDefault
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
QVariant MeasureBase::propertyDefault(Pid propertyId) const
2016-02-04 11:27:47 +01:00
{
2016-02-09 09:20:54 +01:00
switch (propertyId) {
2018-03-27 15:36:00 +02:00
case Pid::REPEAT_END:
case Pid::REPEAT_START:
case Pid::REPEAT_JUMP:
2016-02-04 11:27:47 +01:00
return false;
default:
break;
}
return Element::propertyDefault(propertyId);
}
//---------------------------------------------------------
// undoSetBreak
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
void MeasureBase::undoSetBreak(bool v, LayoutBreak::Type type)
{
switch (type) {
case LayoutBreak::LINE:
if (lineBreak() == v)
return;
setLineBreak(v);
break;
case LayoutBreak::PAGE:
if (pageBreak() == v)
return;
2016-10-13 14:28:00 +02:00
if (v && lineBreak())
setLineBreak(false);
setPageBreak(v);
break;
case LayoutBreak::SECTION:
2016-10-13 14:28:00 +02:00
if (sectionBreak() == v)
return;
2016-10-13 14:28:00 +02:00
if (v && lineBreak())
setLineBreak(false);
setSectionBreak(v);
break;
case LayoutBreak::NOBREAK:
if (noBreak() == v)
return;
2016-10-13 14:28:00 +02:00
if (v) {
setLineBreak(false);
setPageBreak(false);
setSectionBreak(false);
}
setNoBreak(v);
break;
}
if (v) {
2016-03-10 10:41:31 +01:00
LayoutBreak* lb = new LayoutBreak(score());
lb->setLayoutBreakType(type);
lb->setTrack(-1); // this are system elements
MeasureBase* mb = (isMeasure() && toMeasure(this)->isMMRest()) ? toMeasure(this)->mmRestLast() : this;
lb->setParent(mb);
2016-03-10 10:41:31 +01:00
score()->undoAddElement(lb);
}
2016-10-13 14:28:00 +02:00
cleanupLayoutBreaks(true);
}
//---------------------------------------------------------
// cleanupLayoutBreaks
//---------------------------------------------------------
void MeasureBase::cleanupLayoutBreaks(bool undo)
{
// remove unneeded layout breaks
std::vector<Element*> toDelete;
for (Element* e : el()) {
if (e->isLayoutBreak()) {
switch (toLayoutBreak(e)->layoutBreakType()) {
case LayoutBreak::LINE:
if (!lineBreak())
toDelete.push_back(e);
break;
case LayoutBreak::PAGE:
if (!pageBreak())
toDelete.push_back(e);
break;
case LayoutBreak::SECTION:
if (!sectionBreak())
toDelete.push_back(e);
break;
case LayoutBreak::NOBREAK:
if (!noBreak())
toDelete.push_back(e);
break;
}
}
2016-10-13 14:28:00 +02:00
}
for (Element* e : toDelete) {
if (undo)
score()->undoRemoveElement(e);
else
_el.remove(e);
}
}
//---------------------------------------------------------
// nextMM
//---------------------------------------------------------
MeasureBase* MeasureBase::nextMM() const
{
if (_next
2016-02-09 09:20:54 +01:00
&& _next->isMeasure()
2018-03-27 15:36:00 +02:00
&& score()->styleB(Sid::createMultiMeasureRests)
2016-10-18 15:41:00 +02:00
&& toMeasure(_next)->hasMMRest()) {
2016-02-17 14:54:23 +01:00
return toMeasure(_next)->mmRest();
}
return _next;
}
2016-01-04 14:48:58 +01:00
//---------------------------------------------------------
// writeProperties
//---------------------------------------------------------
2016-11-19 11:51:21 +01:00
void MeasureBase::writeProperties(XmlWriter& xml) const
2016-01-04 14:48:58 +01:00
{
Element::writeProperties(xml);
for (const Element* e : el())
e->write(xml);
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
bool MeasureBase::readProperties(XmlReader& e)
{
const QStringRef& tag(e.name());
if (tag == "LayoutBreak") {
LayoutBreak* lb = new LayoutBreak(score());
lb->read(e);
2016-10-13 14:28:00 +02:00
bool doAdd = true;
switch (lb->layoutBreakType()) {
case LayoutBreak::LINE:
if (lineBreak())
doAdd = false;
break;
case LayoutBreak::PAGE:
if (pageBreak())
doAdd = false;
break;
case LayoutBreak::SECTION:
if (sectionBreak())
doAdd = false;
break;
case LayoutBreak::NOBREAK:
if (noBreak())
doAdd = false;
break;
}
if (doAdd) {
add(lb);
cleanupLayoutBreaks(false);
}
else
delete lb;
2016-01-04 14:48:58 +01:00
}
2016-12-18 14:31:13 +01:00
else if (tag == "StaffTypeChange") {
StaffTypeChange* stc = new StaffTypeChange(score());
stc->setTrack(e.track());
stc->setParent(this);
stc->read(e);
add(stc);
}
2016-01-04 14:48:58 +01:00
else if (Element::readProperties(e))
;
else
return false;
return true;
}
//---------------------------------------------------------
// index
//---------------------------------------------------------
int MeasureBase::index() const
{
int idx = 0;
MeasureBase* m = score()->first();
while (m) {
if (m == this)
return idx;
m = m->next();
++idx;
2016-01-04 14:48:58 +01:00
}
return -1;
}
2016-10-13 14:28:00 +02:00
//---------------------------------------------------------
// measureIndex
// returns index of measure counting only Measures but
// skipping other MeasureBase descendants
//---------------------------------------------------------
int MeasureBase::measureIndex() const
{
int idx = 0;
MeasureBase* m = score()->first();
while (m) {
if (m == this)
return idx;
m = m->next();
if (m && m->isMeasure())
++idx;
}
return -1;
}
2016-10-13 14:28:00 +02:00
//---------------------------------------------------------
// sectionBreakElement
//---------------------------------------------------------
LayoutBreak* MeasureBase::sectionBreakElement() const
{
2016-10-20 11:32:07 +02:00
if (sectionBreak()) {
2016-10-13 14:28:00 +02:00
for (Element* e : el()) {
if (e->isLayoutBreak() && toLayoutBreak(e)->isSectionBreak())
return toLayoutBreak(e);
}
}
return 0;
}
2013-05-13 18:49:17 +02:00
}