MuseScore/libmscore/ottava.cpp
2015-03-06 13:38:22 +01:00

562 lines
19 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
//=============================================================================
#include "ottava.h"
#include "style.h"
#include "system.h"
#include "measure.h"
#include "xml.h"
#include "utils.h"
#include "score.h"
#include "text.h"
#include "staff.h"
#include "segment.h"
#include "sym.h"
namespace Ms {
//---------------------------------------------------------
// OttavaDefault
//---------------------------------------------------------
struct OttavaDefault {
SymId id;
SymId numbersOnlyId;
QPointF offset;
qreal hookDirection;
Element::Placement place;
int shift;
const char* name;
const char* numbersOnlyName;
};
// order is important, should be the same as Ottava::Type
static const OttavaDefault ottavaDefault[] = {
{ SymId::ottavaAlta, SymId::ottava, QPointF(0.0, .7), 1.0, Element::Placement::ABOVE, 12, "8va", "8" },
{ SymId::ottavaBassaBa, SymId::ottava, QPointF(0.0, -1.0), -1.0, Element::Placement::BELOW, -12, "8vb", "8" },
{ SymId::quindicesimaAlta, SymId::quindicesima, QPointF(0.0, .7), 1.0, Element::Placement::ABOVE, 24, "15ma", "15" },
{ SymId::quindicesimaBassa, SymId::quindicesima, QPointF(0.0, -1.0), -1.0, Element::Placement::BELOW, -24, "15mb", "15" },
{ SymId::ventiduesimaAlta, SymId::ventiduesima, QPointF(0.0, .7), 1.0, Element::Placement::ABOVE, 36, "22ma", "22" },
{ SymId::ventiduesimaBassa, SymId::ventiduesima, QPointF(0.0, -1.0), -1.0, Element::Placement::BELOW, -36, "22mb", "22" }
};
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void OttavaSegment::layout()
{
TextLineSegment::layout1();
if (parent()) {
qreal yo(score()->styleS(StyleIdx::ottavaY).val() * spatium());
if (ottava()->placement() == Element::Placement::BELOW)
yo = -yo + staff()->height();
rypos() += yo;
}
adjustReadPos();
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant OttavaSegment::getProperty(P_ID id) const
{
switch (id) {
case P_ID::OTTAVA_TYPE:
case P_ID::PLACEMENT:
case P_ID::NUMBERS_ONLY:
return ottava()->getProperty(id);
default:
return TextLineSegment::getProperty(id);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool OttavaSegment::setProperty(P_ID id, const QVariant& v)
{
switch (id) {
case P_ID::LINE_WIDTH:
case P_ID::LINE_STYLE:
case P_ID::OTTAVA_TYPE:
case P_ID::PLACEMENT:
case P_ID::NUMBERS_ONLY:
return ottava()->setProperty(id, v);
default:
return TextLineSegment::setProperty(id, v);
}
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant OttavaSegment::propertyDefault(P_ID id) const
{
switch (id) {
case P_ID::LINE_WIDTH:
case P_ID::LINE_STYLE:
case P_ID::OTTAVA_TYPE:
case P_ID::PLACEMENT:
case P_ID::NUMBERS_ONLY:
return ottava()->propertyDefault(id);
default:
return TextLineSegment::propertyDefault(id);
}
}
//---------------------------------------------------------
// propertyStyle
//---------------------------------------------------------
PropertyStyle OttavaSegment::propertyStyle(P_ID id) const
{
switch (id) {
case P_ID::OTTAVA_TYPE:
case P_ID::LINE_WIDTH:
case P_ID::LINE_STYLE:
case P_ID::PLACEMENT:
case P_ID::NUMBERS_ONLY:
return ottava()->propertyStyle(id);
default:
return TextLineSegment::propertyStyle(id);
}
}
//---------------------------------------------------------
// resetProperty
//---------------------------------------------------------
void OttavaSegment::resetProperty(P_ID id)
{
switch (id) {
case P_ID::OTTAVA_TYPE:
case P_ID::LINE_WIDTH:
case P_ID::LINE_STYLE:
case P_ID::NUMBERS_ONLY:
return ottava()->resetProperty(id);
default:
return TextLineSegment::resetProperty(id);
}
}
//---------------------------------------------------------
// styleChanged
//---------------------------------------------------------
void OttavaSegment::styleChanged()
{
ottava()->styleChanged();
}
//---------------------------------------------------------
// Ottava
//---------------------------------------------------------
Ottava::Ottava(Score* s)
: TextLine(s)
{
_numbersOnly = score()->styleB(StyleIdx::ottavaNumbersOnly);
setOttavaType(Type::OTTAVA_8VA);
setLineWidth(score()->styleS(StyleIdx::ottavaLineWidth));
setLineStyle(Qt::PenStyle(score()->styleI(StyleIdx::ottavaLineStyle)));
setFlag(ElementFlag::ON_STAFF, true);
}
Ottava::Ottava(const Ottava& o)
: TextLine(o)
{
_numbersOnly = o._numbersOnly;
_pitchShift = o._pitchShift;
lineStyleStyle = o.lineStyleStyle;
setOttavaType(o._ottavaType);
}
//---------------------------------------------------------
// setOttavaType
//---------------------------------------------------------
void Ottava::setOttavaType(Type val)
{
setEndHook(true);
_ottavaType = val;
const OttavaDefault* def = &ottavaDefault[int(_ottavaType)];
if (beginTextStyle == PropertyStyle::STYLED)
setBeginText(propertyDefault(P_ID::BEGIN_TEXT).toString(), TextStyleType::OTTAVA);
if (continueTextStyle == PropertyStyle::STYLED)
setContinueText(propertyDefault(P_ID::CONTINUE_TEXT).toString(), TextStyleType::OTTAVA);
setEndHookHeight(score()->styleS(StyleIdx::ottavaHook) * def->hookDirection);
setPlacement(def->place);
_pitchShift = def->shift;
}
//---------------------------------------------------------
// createLineSegment
//---------------------------------------------------------
LineSegment* Ottava::createLineSegment()
{
return new OttavaSegment(score());
}
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void Ottava::endEdit()
{
if (editTick != tick() || editTick2 != tick2()) {
Staff* s = staff();
s->updateOttava();
score()->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO);
score()->setPlaylistDirty();
}
TextLine::endEdit();
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Ottava::write(Xml& xml) const
{
if (!xml.canWrite(this))
return;
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
writeProperty(xml, P_ID::NUMBERS_ONLY);
xml.tag("subtype", ottavaDefault[int(ottavaType())].name);
TextLine::writeProperties(xml);
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void Ottava::read(XmlReader& e)
{
qDeleteAll(spannerSegments());
spannerSegments().clear();
e.addSpanner(e.intAttribute("id", -1), this);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
QString s = e.readElementText();
bool ok;
int idx = s.toInt(&ok);
if (!ok) {
idx = int(Type::OTTAVA_8VA);
for (unsigned i = 0; i < sizeof(ottavaDefault)/sizeof(*ottavaDefault); ++i) {
if (s == ottavaDefault[i].name) {
idx = i;
break;
}
}
}
else if (score()->mscVersion() <= 114) {
//subtype are now in a different order...
if (idx == 1)
idx = 2;
else if (idx == 2)
idx = 1;
}
setOttavaType(Type(idx));
}
else if (tag == "numbersOnly") {
_numbersOnly = e.readInt();
numbersOnlyStyle = PropertyStyle::UNSTYLED;
}
else if (tag == "lineWidth") {
setLineWidth(Spatium(e.readDouble()));
lineWidthStyle = PropertyStyle::UNSTYLED;
}
else if (tag == "lineStyle") {
setLineStyle(Qt::PenStyle(e.readInt()));
lineStyleStyle = PropertyStyle::UNSTYLED;
}
else if (tag == "beginSymbol") { // obsolete
beginTextStyle = PropertyStyle::UNSTYLED;
QString text(e.readElementText());
setBeginText(QString("<sym>%1</sym>").arg(text[0].isNumber() ? Sym::id2name(SymId(text.toInt())) : text));
}
else if (tag == "continueSymbol") { // obsolete
continueTextStyle = PropertyStyle::UNSTYLED;
QString text(e.readElementText());
setContinueText(QString("<sym>%1</sym>").arg(text[0].isNumber() ? Sym::id2name(SymId(text.toInt())) : text));
}
else if (!TextLine::readProperties(e))
e.unknown();
}
if (beginText() != propertyDefault(P_ID::BEGIN_TEXT))
beginTextStyle = PropertyStyle::UNSTYLED;
if (continueText() != propertyDefault(P_ID::CONTINUE_TEXT))
continueTextStyle = PropertyStyle::UNSTYLED;
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Ottava::getProperty(P_ID propertyId) const
{
switch (propertyId) {
case P_ID::OTTAVA_TYPE:
return int(ottavaType());
case P_ID::NUMBERS_ONLY:
return _numbersOnly;
default:
break;
}
return TextLine::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Ottava::setProperty(P_ID propertyId, const QVariant& val)
{
switch (propertyId) {
case P_ID::OTTAVA_TYPE:
setOttavaType(Type(val.toInt()));
break;
case P_ID::PLACEMENT:
if (val != getProperty(propertyId)) {
// reverse hooks
setBeginHookHeight(-beginHookHeight());
setEndHookHeight(-endHookHeight());
}
TextLine::setProperty(propertyId, val);
break;
case P_ID::LINE_WIDTH:
lineWidthStyle = PropertyStyle::UNSTYLED;
TextLine::setProperty(propertyId, val);
break;
case P_ID::LINE_STYLE:
lineStyleStyle = PropertyStyle::UNSTYLED;
TextLine::setProperty(propertyId, val);
break;
case P_ID::NUMBERS_ONLY:
setNumbersOnly(val.toBool());
setOttavaType(_ottavaType);
numbersOnlyStyle = PropertyStyle::UNSTYLED;
break;
case P_ID::SPANNER_TICKS:
setTicks(val.toInt());
staff()->updateOttava();
break;
case P_ID::SPANNER_TICK:
setTick(val.toInt());
staff()->updateOttava();
break;
default:
if (!TextLine::setProperty(propertyId, val))
return false;
break;
}
score()->setLayoutAll(true);
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Ottava::propertyDefault(P_ID propertyId) const
{
switch (propertyId) {
case P_ID::OTTAVA_TYPE:
return 0;
case P_ID::LINE_WIDTH:
return score()->styleS(StyleIdx::ottavaLineWidth).val();
case P_ID::LINE_STYLE:
return int(score()->styleI(StyleIdx::ottavaLineStyle));
case P_ID::PLACEMENT:
return int(ottavaDefault[int(_ottavaType)].place);
case P_ID::END_HOOK_HEIGHT:
return score()->styleS(StyleIdx::ottavaHook).val() * ottavaDefault[int(_ottavaType)].hookDirection;
case P_ID::NUMBERS_ONLY:
return score()->styleB(StyleIdx::ottavaNumbersOnly);
case P_ID::BEGIN_TEXT:
case P_ID::CONTINUE_TEXT:
{
const OttavaDefault* def = &ottavaDefault[int(_ottavaType)];
SymId id = _numbersOnly ? def->numbersOnlyId : def->id;
QString s;
if (symIsValid(id))
s = QString("<sym>%1</sym>").arg(Sym::id2name(id));
else
s = _numbersOnly ? def->numbersOnlyName : def->name;
return s;
}
case P_ID::END_TEXT:
return QString("");
case P_ID::TEXT_STYLE_TYPE:
return int(TextStyleType::OTTAVA);
case P_ID::END_HOOK:
return true;
default:
return TextLine::propertyDefault(propertyId);
}
}
//---------------------------------------------------------
// undoSetOttavaType
//---------------------------------------------------------
void Ottava::undoSetOttavaType(Type val)
{
score()->undoChangeProperty(this, P_ID::OTTAVA_TYPE, int(val));
}
//---------------------------------------------------------
// setYoff
// used in musicxml import
//---------------------------------------------------------
void Ottava::setYoff(qreal val)
{
qreal _spatium = spatium();
qreal yo(score()->styleS(StyleIdx::ottavaY).val() * _spatium);
if (placement() == Element::Placement::BELOW)
yo = -yo + staff()->height();
rUserYoffset() += val * _spatium - yo;
}
//---------------------------------------------------------
// propertyStyle
//---------------------------------------------------------
PropertyStyle Ottava::propertyStyle(P_ID id) const
{
switch (id) {
case P_ID::OTTAVA_TYPE:
case P_ID::PLACEMENT:
return PropertyStyle::NOSTYLE;
case P_ID::LINE_WIDTH:
return lineWidthStyle;
case P_ID::LINE_STYLE:
return lineStyleStyle;
case P_ID::NUMBERS_ONLY:
return numbersOnlyStyle;
default:
return TextLine::propertyStyle(id);
}
}
//---------------------------------------------------------
// resetProperty
//---------------------------------------------------------
void Ottava::resetProperty(P_ID id)
{
switch (id) {
case P_ID::OTTAVA_TYPE:
return;
case P_ID::LINE_WIDTH:
setLineWidth(score()->styleS(StyleIdx::ottavaLineWidth));
lineWidthStyle = PropertyStyle::STYLED;
break;
case P_ID::LINE_STYLE:
setLineStyle(Qt::PenStyle(score()->styleI(StyleIdx::ottavaLineStyle)));
lineStyleStyle = PropertyStyle::STYLED;
break;
case P_ID::NUMBERS_ONLY:
setNumbersOnly(score()->styleB(StyleIdx::ottavaNumbersOnly));
numbersOnlyStyle = PropertyStyle::STYLED;
setOttavaType(_ottavaType);
break;
default:
return TextLine::resetProperty(id);
}
}
//---------------------------------------------------------
// styleChanged
// reset all styled values to actual style
//---------------------------------------------------------
void Ottava::styleChanged()
{
if (lineWidthStyle == PropertyStyle::STYLED)
setLineWidth(score()->styleS(StyleIdx::ottavaLineWidth));
if (lineStyleStyle == PropertyStyle::STYLED)
setLineStyle(Qt::PenStyle(score()->styleI(StyleIdx::ottavaLineStyle)));
if (numbersOnlyStyle == PropertyStyle::STYLED)
setNumbersOnly(score()->styleB(StyleIdx::ottavaNumbersOnly));
setOttavaType(_ottavaType);
}
//---------------------------------------------------------
// reset
//---------------------------------------------------------
void Ottava::reset()
{
if (lineWidthStyle == PropertyStyle::UNSTYLED)
score()->undoChangeProperty(this, P_ID::LINE_WIDTH, propertyDefault(P_ID::LINE_WIDTH), PropertyStyle::STYLED);
if (lineStyleStyle == PropertyStyle::UNSTYLED)
score()->undoChangeProperty(this, P_ID::LINE_STYLE, propertyDefault(P_ID::LINE_STYLE), PropertyStyle::STYLED);
if (numbersOnlyStyle == PropertyStyle::UNSTYLED)
score()->undoChangeProperty(this, P_ID::NUMBERS_ONLY, propertyDefault(P_ID::NUMBERS_ONLY), PropertyStyle::STYLED);
if (beginTextStyle == PropertyStyle::UNSTYLED)
score()->undoChangeProperty(this, P_ID::BEGIN_TEXT, propertyDefault(P_ID::BEGIN_TEXT), PropertyStyle::STYLED);
if (continueTextStyle == PropertyStyle::UNSTYLED)
score()->undoChangeProperty(this, P_ID::CONTINUE_TEXT, propertyDefault(P_ID::CONTINUE_TEXT), PropertyStyle::STYLED);
setOttavaType(_ottavaType);
TextLine::reset();
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString Ottava::accessibleInfo()
{
return QString("%1: %2").arg(Element::accessibleInfo()).arg(ottavaDefault[static_cast<int>(ottavaType())].name);
}
}