543 lines
19 KiB
C++
543 lines
19 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Music Composition & Notation
|
|
//
|
|
// Copyright (C) 2002-2013 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 "bracket.h"
|
|
#include "xml.h"
|
|
#include "style.h"
|
|
#include "utils.h"
|
|
#include "staff.h"
|
|
#include "score.h"
|
|
#include "system.h"
|
|
#include "sym.h"
|
|
#include "mscore.h"
|
|
#include "bracketItem.h"
|
|
|
|
namespace Ms {
|
|
|
|
//---------------------------------------------------------
|
|
// Bracket
|
|
//---------------------------------------------------------
|
|
|
|
Bracket::Bracket(Score* s)
|
|
: Element(s)
|
|
{
|
|
ay1 = 0;
|
|
h2 = 3.5 * spatium();
|
|
_firstStaff = 0;
|
|
_lastStaff = 0;
|
|
_bi = 0;
|
|
_braceSymbol = SymId::noSym;
|
|
_magx = 1.;
|
|
setGenerated(true); // brackets are not saved
|
|
}
|
|
|
|
Bracket::~Bracket()
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setHeight
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::setHeight(qreal h)
|
|
{
|
|
h2 = h * .5;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// width
|
|
//---------------------------------------------------------
|
|
|
|
qreal Bracket::width() const
|
|
{
|
|
qreal w;
|
|
switch (bracketType()) {
|
|
case BracketType::BRACE:
|
|
if (score()->styleSt(Sid::MusicalSymbolFont) == "Emmentaler" || score()->styleSt(Sid::MusicalSymbolFont) == "Gonville")
|
|
w = score()->styleP(Sid::akkoladeWidth) + score()->styleP(Sid::akkoladeBarDistance);
|
|
else
|
|
w = (symWidth(_braceSymbol) * _magx) + score()->styleP(Sid::akkoladeBarDistance);
|
|
break;
|
|
case BracketType::NORMAL:
|
|
w = score()->styleP(Sid::bracketWidth) + score()->styleP(Sid::bracketDistance);
|
|
break;
|
|
case BracketType::SQUARE:
|
|
w = score()->styleP(Sid::staffLineWidth) + spatium() * .5;
|
|
break;
|
|
case BracketType::LINE:
|
|
w = 0.67f * score()->styleP(Sid::bracketWidth) + score()->styleP(Sid::bracketDistance);
|
|
break;
|
|
case BracketType::NO_BRACKET:
|
|
default:
|
|
w = 0.0;
|
|
break;
|
|
}
|
|
return w;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setStaffSpan
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::setStaffSpan(int a, int b)
|
|
{
|
|
_firstStaff = a;
|
|
_lastStaff = b;
|
|
|
|
if (bracketType() == BracketType::BRACE &&
|
|
score()->styleSt(Sid::MusicalSymbolFont) != "Emmentaler" && score()->styleSt(Sid::MusicalSymbolFont) != "Gonville")
|
|
{
|
|
int v = _lastStaff - _firstStaff + 1;
|
|
// total default height of a system of n staves / height of a 5 line staff
|
|
_magx = v + ((v - 1) * score()->styleS(Sid::akkoladeDistance).val() / 4.0);
|
|
if (v == 1)
|
|
_braceSymbol = SymId::braceSmall;
|
|
else if (v <= 2)
|
|
_braceSymbol = SymId::brace;
|
|
else if (v <= 3)
|
|
_braceSymbol = SymId::braceLarge;
|
|
else
|
|
_braceSymbol = SymId::braceLarger;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layout
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::layout()
|
|
{
|
|
path = QPainterPath();
|
|
if (h2 == 0.0)
|
|
return;
|
|
|
|
_shape.clear();
|
|
switch (bracketType()) {
|
|
case BracketType::BRACE: {
|
|
if (score()->styleSt(Sid::MusicalSymbolFont) == "Emmentaler" || score()->styleSt(Sid::MusicalSymbolFont) == "Gonville") {
|
|
_braceSymbol = SymId::noSym;
|
|
qreal w = score()->styleP(Sid::akkoladeWidth);
|
|
|
|
#define XM(a) (a+700)*w/700
|
|
#define YM(a) (a+7100)*h2/7100
|
|
|
|
path.moveTo( XM( -8), YM(-2048));
|
|
path.cubicTo(XM( -8), YM(-3192), XM(-360), YM(-4304), XM( -360), YM(-5400)); // c 0
|
|
path.cubicTo(XM( -360), YM(-5952), XM(-264), YM(-6488), XM( 32), YM(-6968)); // c 1
|
|
path.cubicTo(XM( 36), YM(-6974), XM( 38), YM(-6984), XM( 38), YM(-6990)); // c 0
|
|
path.cubicTo(XM( 38), YM(-7008), XM( 16), YM(-7024), XM( 0), YM(-7024)); // c 0
|
|
path.cubicTo(XM( -8), YM(-7024), XM( -22), YM(-7022), XM( -32), YM(-7008)); // c 1
|
|
path.cubicTo(XM( -416), YM(-6392), XM(-544), YM(-5680), XM( -544), YM(-4960)); // c 0
|
|
path.cubicTo(XM( -544), YM(-3800), XM(-168), YM(-2680), XM( -168), YM(-1568)); // c 0
|
|
path.cubicTo(XM( -168), YM(-1016), XM(-264), YM( -496), XM( -560), YM( -16)); // c 1
|
|
path.lineTo( XM( -560), YM( 0)); // l 1
|
|
path.lineTo( XM( -560), YM( 16)); // l 1
|
|
path.cubicTo(XM( -264), YM( 496), XM(-168), YM( 1016), XM( -168), YM( 1568)); // c 0
|
|
path.cubicTo(XM( -168), YM( 2680), XM(-544), YM( 3800), XM( -544), YM( 4960)); // c 0
|
|
path.cubicTo(XM( -544), YM( 5680), XM(-416), YM( 6392), XM( -32), YM( 7008)); // c 1
|
|
path.cubicTo(XM( -22), YM( 7022), XM( -8), YM( 7024), XM( 0), YM( 7024)); // c 0
|
|
path.cubicTo(XM( 16), YM( 7024), XM( 38), YM( 7008), XM( 38), YM( 6990)); // c 0
|
|
path.cubicTo(XM( 38), YM( 6984), XM( 36), YM( 6974), XM( 32), YM( 6968)); // c 1
|
|
path.cubicTo(XM( -264), YM( 6488), XM(-360), YM( 5952), XM( -360), YM( 5400)); // c 0
|
|
path.cubicTo(XM( -360), YM( 4304), XM( -8), YM( 3192), XM( -8), YM( 2048)); // c 0
|
|
path.cubicTo(XM( - 8), YM( 1320), XM(-136), YM( 624), XM( -512), YM( 0)); // c 1
|
|
path.cubicTo(XM( -136), YM( -624), XM( -8), YM(-1320), XM( -8), YM(-2048)); // c 0*/
|
|
setbbox(path.boundingRect());
|
|
_shape.add(bbox());
|
|
}
|
|
else {
|
|
if (_braceSymbol == SymId::noSym)
|
|
_braceSymbol = SymId::brace;
|
|
qreal h = h2 * 2;
|
|
qreal w = symWidth(_braceSymbol) * _magx;
|
|
bbox().setRect(0, 0, w, h);
|
|
_shape.add(bbox());
|
|
}
|
|
}
|
|
break;
|
|
case BracketType::NORMAL: {
|
|
qreal _spatium = spatium();
|
|
qreal w = score()->styleP(Sid::bracketWidth) * .5;
|
|
qreal x = -w;
|
|
|
|
qreal bd = _spatium * .25;
|
|
_shape.add(QRectF(x, -bd, w * 2, 2 * (h2+bd)));
|
|
_shape.add(symBbox(SymId::bracketTop).translated(QPointF(-w, -bd)));
|
|
_shape.add(symBbox(SymId::bracketBottom).translated(QPointF(-w, bd + 2*h2)));
|
|
|
|
w += symWidth(SymId::bracketTop);
|
|
qreal y = - symHeight(SymId::bracketTop) - bd;
|
|
qreal h = (-y + h2) * 2;
|
|
bbox().setRect(x, y, w, h);
|
|
}
|
|
break;
|
|
case BracketType::SQUARE: {
|
|
qreal w = score()->styleP(Sid::staffLineWidth) * .5;
|
|
qreal x = -w;
|
|
qreal y = -w;
|
|
qreal h = (h2 + w) * 2 ;
|
|
w += (.5 * spatium() + 3* w);
|
|
bbox().setRect(x, y, w, h);
|
|
_shape.add(bbox());
|
|
}
|
|
break;
|
|
case BracketType::LINE: {
|
|
qreal _spatium = spatium();
|
|
qreal w = 0.67 * score()->styleP(Sid::bracketWidth) * .5;
|
|
qreal x = -w;
|
|
qreal bd = _spatium * .25;
|
|
qreal y = -bd;
|
|
qreal h = (-y + h2) * 2;
|
|
bbox().setRect(x, y, w, h);
|
|
_shape.add(bbox());
|
|
}
|
|
break;
|
|
case BracketType::NO_BRACKET:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// draw
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::draw(QPainter* painter) const
|
|
{
|
|
if (h2 == 0.0)
|
|
return;
|
|
switch (bracketType()) {
|
|
case BracketType::BRACE: {
|
|
if (_braceSymbol == SymId::noSym) {
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(QBrush(curColor()));
|
|
painter->drawPath(path);
|
|
}
|
|
else {
|
|
qreal h = 2 * h2;
|
|
qreal _spatium = spatium();
|
|
qreal mag = h / (4 *_spatium);
|
|
painter->setPen(curColor());
|
|
painter->save();
|
|
painter->scale(_magx, mag);
|
|
drawSymbol(_braceSymbol, painter, QPointF(0, h/mag));
|
|
painter->restore();
|
|
}
|
|
}
|
|
break;
|
|
case BracketType::NORMAL: {
|
|
qreal h = 2 * h2;
|
|
qreal _spatium = spatium();
|
|
qreal w = score()->styleP(Sid::bracketWidth);
|
|
qreal bd = _spatium * .25;
|
|
QPen pen(curColor(), w, Qt::SolidLine, Qt::FlatCap);
|
|
painter->setPen(pen);
|
|
painter->drawLine(QLineF(0.0, -bd - w * .5, 0.0, h + bd + w * .5));
|
|
qreal x = -w * .5;
|
|
qreal y1 = -bd;
|
|
qreal y2 = h + bd;
|
|
drawSymbol(SymId::bracketTop, painter, QPointF(x, y1));
|
|
drawSymbol(SymId::bracketBottom, painter, QPointF(x, y2));
|
|
}
|
|
break;
|
|
case BracketType::SQUARE: {
|
|
qreal h = 2 * h2;
|
|
qreal _spatium = spatium();
|
|
qreal w = score()->styleP(Sid::staffLineWidth);
|
|
QPen pen(curColor(), w, Qt::SolidLine, Qt::SquareCap);
|
|
painter->setPen(pen);
|
|
painter->drawLine(QLineF(0.0, 0.0, 0.0, h));
|
|
painter->drawLine(QLineF(0.0, 0.0, w + .5 *_spatium, 0.0));
|
|
painter->drawLine(QLineF(0.0, h , w + .5 *_spatium, h));
|
|
}
|
|
break;
|
|
case BracketType::LINE: {
|
|
qreal h = 2 * h2;
|
|
qreal _spatium = spatium();
|
|
qreal w = 0.67 * score()->styleP(Sid::bracketWidth);
|
|
QPen pen(curColor(), w, Qt::SolidLine, Qt::FlatCap);
|
|
painter->setPen(pen);
|
|
qreal bd = _spatium * .25;
|
|
painter->drawLine(QLineF(0.0, -bd, 0.0, h + bd));
|
|
}
|
|
break;
|
|
case BracketType::NO_BRACKET:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// startEdit
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::startEdit(EditData& ed)
|
|
{
|
|
Element::startEdit(ed);
|
|
ay1 = pagePos().y();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// gripsPositions
|
|
//---------------------------------------------------------
|
|
|
|
std::vector<QPointF> Bracket::gripsPositions(const EditData&) const
|
|
{
|
|
return { QPointF(0.0, h2 * 2) + pagePos() };
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// endEdit
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::endEdit(EditData& ed)
|
|
{
|
|
// endEditDrag(ed);
|
|
triggerLayoutAll();
|
|
score()->update();
|
|
ed.element = 0; // score layout invalidates element
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// editDrag
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::editDrag(EditData& ed)
|
|
{
|
|
h2 += ed.delta.y() * .5;
|
|
layout();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// endEditDrag
|
|
// snap to nearest staff
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::endEditDrag(EditData&)
|
|
{
|
|
qreal ay2 = ay1 + h2 * 2;
|
|
|
|
int staffIdx1 = staffIdx();
|
|
int staffIdx2;
|
|
int n = system()->staves()->size();
|
|
if (staffIdx1 + 1 >= n)
|
|
staffIdx2 = staffIdx1;
|
|
else {
|
|
qreal ay = parent()->pagePos().y();
|
|
System* s = system();
|
|
qreal y = s->staff(staffIdx1)->y() + ay;
|
|
qreal h1 = staff()->height();
|
|
|
|
for (staffIdx2 = staffIdx1 + 1; staffIdx2 < n; ++staffIdx2) {
|
|
qreal h = s->staff(staffIdx2)->y() + ay - y;
|
|
if (ay2 < (y + (h + h1) * .5))
|
|
break;
|
|
y += h;
|
|
}
|
|
staffIdx2 -= 1;
|
|
}
|
|
|
|
qreal sy = system()->staff(staffIdx1)->y();
|
|
qreal ey = system()->staff(staffIdx2)->y() + score()->staff(staffIdx2)->height();
|
|
h2 = (ey - sy) * .5;
|
|
bracketItem()->undoChangeProperty(Pid::BRACKET_SPAN, staffIdx2 - staffIdx1 + 1);
|
|
// brackets do not survive layout
|
|
// make sure layout is not called:
|
|
score()->cmdState()._setUpdateMode(UpdateMode::Update);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// acceptDrop
|
|
//---------------------------------------------------------
|
|
|
|
bool Bracket::acceptDrop(EditData& data) const
|
|
{
|
|
return data.dropElement->type() == ElementType::BRACKET;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// drop
|
|
//---------------------------------------------------------
|
|
|
|
Element* Bracket::drop(EditData& data)
|
|
{
|
|
Element* e = data.dropElement;
|
|
Bracket* b = 0;
|
|
if (e->isBracket()) {
|
|
b = toBracket(e);
|
|
undoChangeProperty(Pid::SYSTEM_BRACKET, int(b->bracketType()));
|
|
}
|
|
delete e;
|
|
return this;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// edit
|
|
// return true if event is accepted
|
|
//---------------------------------------------------------
|
|
|
|
bool Bracket::edit(EditData& ed)
|
|
{
|
|
if (!(ed.modifiers & Qt::ShiftModifier))
|
|
return false;
|
|
|
|
if (ed.key == Qt::Key_Left) {
|
|
bracketItem()->undoChangeProperty(Pid::BRACKET_COLUMN, bracketItem()->column()+1);
|
|
return true;
|
|
}
|
|
if (ed.key == Qt::Key_Right) {
|
|
if (bracketItem()->column() == 0)
|
|
return true;
|
|
bracketItem()->undoChangeProperty(Pid::BRACKET_COLUMN, bracketItem()->column()-1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// getProperty
|
|
//---------------------------------------------------------
|
|
|
|
QVariant Bracket::getProperty(Pid id) const
|
|
{
|
|
QVariant v = Element::getProperty(id);
|
|
if (!v.isValid())
|
|
v = _bi->getProperty(id);
|
|
return v;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setProperty
|
|
//---------------------------------------------------------
|
|
|
|
bool Bracket::setProperty(Pid id, const QVariant& v)
|
|
{
|
|
return _bi->setProperty(id, v);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyDefault
|
|
//---------------------------------------------------------
|
|
|
|
QVariant Bracket::propertyDefault(Pid id) const
|
|
{
|
|
if (id == Pid::BRACKET_COLUMN)
|
|
return 0;
|
|
QVariant v = Element::propertyDefault(id);
|
|
if (!v.isValid())
|
|
v = _bi->propertyDefault(id);
|
|
return v;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// undoChangeProperty
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
|
|
{
|
|
// brackets do not survive layout() and therefore cannot be on
|
|
// the undo stack; delegate to BracketItem:
|
|
BracketItem* bi = bracketItem();
|
|
bi->undoChangeProperty(id, v, ps);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setSelected
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::setSelected(bool f)
|
|
{
|
|
// _bi->setSelected(f);
|
|
Element::setSelected(f);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Bracket::bracketTypeName
|
|
//---------------------------------------------------------
|
|
|
|
const char* Bracket::bracketTypeName(BracketType type)
|
|
{
|
|
switch(type) {
|
|
case BracketType::BRACE:
|
|
return "Brace";
|
|
case BracketType::NORMAL:
|
|
return "Normal";
|
|
case BracketType::SQUARE:
|
|
return "Square";
|
|
case BracketType::LINE:
|
|
return "Line";
|
|
case BracketType::NO_BRACKET:
|
|
return "NoBracket";
|
|
}
|
|
Q_UNREACHABLE();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Bracket::write
|
|
// used only for palettes
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::write(XmlWriter& xml) const
|
|
{
|
|
switch (_bi->bracketType()) {
|
|
case BracketType::BRACE:
|
|
case BracketType::SQUARE:
|
|
case BracketType::LINE:
|
|
{
|
|
const char* type = bracketTypeName(_bi->bracketType());
|
|
xml.stag(this, QString("type=\"%1\"").arg(type));
|
|
}
|
|
break;
|
|
case BracketType::NORMAL:
|
|
xml.stag(this);
|
|
break;
|
|
case BracketType::NO_BRACKET:
|
|
break;
|
|
}
|
|
if (_bi->column())
|
|
xml.tag("level", _bi->column());
|
|
Element::writeProperties(xml);
|
|
xml.etag();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Bracket::read
|
|
// used only for palettes
|
|
//---------------------------------------------------------
|
|
|
|
void Bracket::read(XmlReader& e)
|
|
{
|
|
QString t(e.attribute("type", "Normal"));
|
|
_bi = new BracketItem(score());
|
|
|
|
if (t == "Normal")
|
|
_bi->setBracketType(BracketType::NORMAL);
|
|
else if (t == "Akkolade") //compatibility, not used anymore
|
|
_bi->setBracketType(BracketType::BRACE);
|
|
else if (t == "Brace")
|
|
_bi->setBracketType(BracketType::BRACE);
|
|
else if (t == "Square")
|
|
_bi->setBracketType(BracketType::SQUARE);
|
|
else if (t == "Line")
|
|
_bi->setBracketType(BracketType::LINE);
|
|
else
|
|
qDebug("unknown brace type <%s>", qPrintable(t));
|
|
|
|
while (e.readNextStartElement()) {
|
|
if (e.name() == "level")
|
|
_bi->setColumn(e.readInt());
|
|
else if (!Element::readProperties(e))
|
|
e.unknown();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|