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 "note.h"
|
|
|
|
#include "chord.h"
|
|
|
|
#include "xml.h"
|
|
|
|
#include "slur.h"
|
2013-08-22 12:18:14 +02:00
|
|
|
#include "tie.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "measure.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "score.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "navigate.h"
|
|
|
|
#include "articulation.h"
|
|
|
|
#include "undo.h"
|
|
|
|
#include "stem.h"
|
|
|
|
#include "beam.h"
|
|
|
|
#include "mscore.h"
|
|
|
|
#include "page.h"
|
2013-10-13 11:36:08 +02:00
|
|
|
#include "part.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2014-07-31 16:30:04 +02:00
|
|
|
Element* SlurTie::editEndElement;
|
|
|
|
Element* SlurTie::editStartElement;
|
|
|
|
QList<SlurOffsets> SlurTie::editUps;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// SlurSegment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SlurSegment::SlurSegment(Score* score)
|
|
|
|
: SpannerSegment(score)
|
|
|
|
{
|
2014-11-06 19:18:20 +01:00
|
|
|
setFlag(ElementFlag::ON_STAFF, true);
|
2014-03-30 18:14:55 +02:00
|
|
|
autoAdjustOffset = QPointF();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SlurSegment::SlurSegment(const SlurSegment& b)
|
|
|
|
: SpannerSegment(b)
|
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
for (int i = 0; i < int(Grip::GRIPS); ++i) {
|
|
|
|
_ups[i] = b._ups[i];
|
|
|
|
_ups[i].p = QPointF();
|
2014-11-06 10:44:57 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
path = b.path;
|
2014-03-30 18:14:55 +02:00
|
|
|
autoAdjustOffset = QPointF();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// move
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurSegment::move(const QPointF& s)
|
|
|
|
{
|
2014-03-29 07:42:36 +01:00
|
|
|
Element::move(s);
|
2015-01-19 12:37:17 +01:00
|
|
|
for (int k = 0; k < int(Grip::GRIPS); ++k)
|
|
|
|
_ups[k].p += s;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// draw
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurSegment::draw(QPainter* painter) const
|
|
|
|
{
|
2013-05-12 12:51:42 +02:00
|
|
|
// hide tie toward the second chord of a cross-measure value
|
2014-06-24 18:36:02 +02:00
|
|
|
if ((slurTie()->type() == Element::Type::TIE)
|
2013-05-12 12:43:22 +02:00
|
|
|
&& (static_cast<Tie*>(slurTie())->endNote())
|
2014-05-21 15:41:23 +02:00
|
|
|
&& (static_cast<Tie*>(slurTie())->endNote()->chord()->crossMeasure() == CrossMeasure::SECOND))
|
2013-05-12 12:51:42 +02:00
|
|
|
return;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
QPen pen(curColor());
|
2013-06-10 11:03:34 +02:00
|
|
|
switch (slurTie()->lineType()) {
|
|
|
|
case 0:
|
2014-05-07 12:10:28 +02:00
|
|
|
painter->setBrush(QBrush(pen.color()));
|
2013-06-10 11:03:34 +02:00
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
2016-03-19 11:41:38 +01:00
|
|
|
pen.setWidthF(score()->styleP(StyleIdx::SlurEndWidth));
|
2013-06-10 11:03:34 +02:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
painter->setBrush(Qt::NoBrush);
|
2016-03-19 11:41:38 +01:00
|
|
|
pen.setWidthF(score()->styleP(StyleIdx::SlurDottedWidth));
|
2013-06-10 11:03:34 +02:00
|
|
|
pen.setStyle(Qt::DotLine);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
painter->setBrush(Qt::NoBrush);
|
2016-03-19 11:41:38 +01:00
|
|
|
pen.setWidthF(score()->styleP(StyleIdx::SlurDottedWidth));
|
2013-06-10 11:03:34 +02:00
|
|
|
pen.setStyle(Qt::DashLine);
|
|
|
|
break;
|
2012-07-07 12:14:48 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
painter->setPen(pen);
|
|
|
|
painter->drawPath(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateGrips
|
|
|
|
// return grip rectangles in page coordinates
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
void SlurSegment::updateGrips(Grip* defaultGrip, QVector<QRectF>& r) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
*defaultGrip = Grip::END;
|
2012-05-26 14:26:10 +02:00
|
|
|
QPointF p(pagePos());
|
2014-11-06 19:18:20 +01:00
|
|
|
p -= QPointF(0.0, system()->staff(staffIdx())->y()); // ??
|
2015-01-19 12:37:17 +01:00
|
|
|
for (int i = 0; i < int(Grip::GRIPS); ++i)
|
|
|
|
r[i].translate(_ups[i].p + _ups[i].off * spatium() + p);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-10-13 11:36:08 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchCR
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static ChordRest* searchCR(Segment* segment, int startTrack, int endTrack)
|
|
|
|
{
|
2014-06-25 11:46:10 +02:00
|
|
|
// for (Segment* s = segment; s; s = s->next1MM(Segment::Type::ChordRest)) {
|
|
|
|
for (Segment* s = segment; s; s = s->next(Segment::Type::ChordRest)) { // restrict search to measure
|
2013-10-13 11:36:08 +02:00
|
|
|
if (startTrack > endTrack) {
|
|
|
|
for (int t = startTrack-1; t >= endTrack; --t) {
|
|
|
|
if (s->element(t))
|
|
|
|
return static_cast<ChordRest*>(s->element(t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (int t = startTrack; t < endTrack; ++t) {
|
|
|
|
if (s->element(t))
|
|
|
|
return static_cast<ChordRest*>(s->element(t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// edit
|
|
|
|
// return true if event is accepted
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
bool SlurSegment::edit(MuseScoreView* viewer, Grip curGrip, int key, Qt::KeyboardModifiers modifiers, const QString&)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Slur* sl = static_cast<Slur*>(slurTie());
|
|
|
|
|
|
|
|
if (key == Qt::Key_X) {
|
2016-03-02 13:20:19 +01:00
|
|
|
sl->setSlurDirection(sl->up() ? Direction::DOWN : Direction::UP);
|
2012-05-26 14:26:10 +02:00
|
|
|
sl->layout();
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-16 18:09:26 +02:00
|
|
|
if (key == Qt::Key_Home) {
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(curGrip).off = QPointF();
|
2013-10-16 18:09:26 +02:00
|
|
|
sl->layout();
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (slurTie()->type() != Element::Type::SLUR)
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!((modifiers & Qt::ShiftModifier)
|
2014-05-26 20:38:22 +02:00
|
|
|
&& ((spannerSegmentType() == SpannerSegmentType::SINGLE)
|
2015-01-19 12:37:17 +01:00
|
|
|
|| (spannerSegmentType() == SpannerSegmentType::BEGIN && curGrip == Grip::START)
|
|
|
|
|| (spannerSegmentType() == SpannerSegmentType::END && curGrip == Grip::END)
|
2012-05-26 14:26:10 +02:00
|
|
|
)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ChordRest* cr = 0;
|
2015-01-19 12:37:17 +01:00
|
|
|
ChordRest* e = curGrip == Grip::START ? sl->startCR() : sl->endCR();
|
|
|
|
ChordRest* e1 = curGrip == Grip::START ? sl->endCR() : sl->startCR();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (key == Qt::Key_Left)
|
2013-06-12 14:23:57 +02:00
|
|
|
cr = prevChordRest(e);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (key == Qt::Key_Right)
|
2013-06-12 14:23:57 +02:00
|
|
|
cr = nextChordRest(e);
|
2013-10-13 11:36:08 +02:00
|
|
|
else if (key == Qt::Key_Up) {
|
2015-03-13 11:16:43 +01:00
|
|
|
Part* part = e->part();
|
2013-10-13 11:36:08 +02:00
|
|
|
int startTrack = part->startTrack();
|
|
|
|
int endTrack = e->track();
|
|
|
|
cr = searchCR(e->segment(), endTrack, startTrack);
|
|
|
|
}
|
|
|
|
else if (key == Qt::Key_Down) {
|
|
|
|
int startTrack = e->track() + 1;
|
2015-03-13 11:16:43 +01:00
|
|
|
Part* part = e->part();
|
2013-10-13 11:36:08 +02:00
|
|
|
int endTrack = part->endTrack();
|
|
|
|
cr = searchCR(e->segment(), startTrack, endTrack);
|
|
|
|
}
|
2014-07-10 14:32:04 +02:00
|
|
|
if (cr && cr != e1)
|
2014-05-06 14:00:32 +02:00
|
|
|
changeAnchor(viewer, curGrip, cr);
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// changeAnchor
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
void SlurSegment::changeAnchor(MuseScoreView* viewer, Grip curGrip, Element* element)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
if (curGrip == Grip::START) {
|
2013-08-22 12:18:14 +02:00
|
|
|
spanner()->setStartElement(element);
|
2014-12-18 13:35:47 +01:00
|
|
|
switch (spanner()->anchor()) {
|
2014-07-03 15:02:36 +02:00
|
|
|
case Spanner::Anchor::NOTE: {
|
|
|
|
Tie* tie = static_cast<Tie*>(spanner());
|
2015-01-28 15:20:33 +01:00
|
|
|
Note* note = static_cast<Note*>(element);
|
|
|
|
if (note->chord()->tick() <= tie->endNote()->chord()->tick()) {
|
|
|
|
tie->startNote()->setTieFor(0);
|
|
|
|
tie->setStartNote(note);
|
|
|
|
note->setTieFor(tie);
|
|
|
|
}
|
2014-07-03 15:02:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Spanner::Anchor::CHORD:
|
|
|
|
spanner()->setTick(static_cast<Chord*>(element)->tick());
|
2014-10-01 17:02:41 +02:00
|
|
|
spanner()->setTrack(element->track());
|
2014-07-03 15:02:36 +02:00
|
|
|
break;
|
|
|
|
case Spanner::Anchor::SEGMENT:
|
|
|
|
case Spanner::Anchor::MEASURE:
|
|
|
|
qDebug("SlurSegment::changeAnchor: bad anchor");
|
|
|
|
break;
|
2013-08-22 12:18:14 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2013-08-22 12:18:14 +02:00
|
|
|
spanner()->setEndElement(element);
|
2014-12-18 13:35:47 +01:00
|
|
|
switch (spanner()->anchor()) {
|
2014-07-03 15:02:36 +02:00
|
|
|
case Spanner::Anchor::NOTE: {
|
|
|
|
Tie* tie = static_cast<Tie*>(spanner());
|
2015-01-28 15:20:33 +01:00
|
|
|
Note* note = static_cast<Note*>(element);
|
|
|
|
// do not allow backward ties
|
|
|
|
if (note->chord()->tick() >= tie->startNote()->chord()->tick()) {
|
|
|
|
tie->endNote()->setTieBack(0);
|
|
|
|
tie->setEndNote(note);
|
|
|
|
note->setTieBack(tie);
|
|
|
|
}
|
2014-07-03 15:02:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Spanner::Anchor::CHORD:
|
|
|
|
spanner()->setTick2(static_cast<Chord*>(element)->tick());
|
|
|
|
spanner()->setTrack2(element->track());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Spanner::Anchor::SEGMENT:
|
|
|
|
case Spanner::Anchor::MEASURE:
|
|
|
|
qDebug("SlurSegment::changeAnchor: bad anchor");
|
|
|
|
break;
|
2013-10-13 11:36:08 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-08-22 12:18:14 +02:00
|
|
|
int segments = spanner()->spannerSegments().size();
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(curGrip).off = QPointF();
|
2013-08-22 12:18:14 +02:00
|
|
|
spanner()->layout();
|
|
|
|
if (spanner()->spannerSegments().size() != segments) {
|
|
|
|
QList<SpannerSegment*>& ss = spanner()->spannerSegments();
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
SlurSegment* newSegment = static_cast<SlurSegment*>(curGrip == Grip::END ? ss.back() : ss.front());
|
2012-05-26 14:26:10 +02:00
|
|
|
score()->endCmd();
|
|
|
|
score()->startCmd();
|
|
|
|
viewer->startEdit(newSegment, curGrip);
|
2016-03-02 13:20:19 +01:00
|
|
|
score()->setLayoutAll();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// gripAnchor
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
QPointF SlurSegment::gripAnchor(Grip grip) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
SlurPos spos;
|
|
|
|
slurTie()->slurPos(&spos);
|
|
|
|
|
|
|
|
QPointF sp(system()->pagePos());
|
|
|
|
QPointF p1(spos.p1 + spos.system1->pagePos());
|
|
|
|
QPointF p2(spos.p2 + spos.system2->pagePos());
|
2013-03-05 20:23:59 +01:00
|
|
|
switch (spannerSegmentType()) {
|
2014-05-26 20:38:22 +02:00
|
|
|
case SpannerSegmentType::SINGLE:
|
2015-01-19 12:37:17 +01:00
|
|
|
if (grip == Grip::START)
|
2012-05-26 14:26:10 +02:00
|
|
|
return p1;
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (grip == Grip::END)
|
2012-05-26 14:26:10 +02:00
|
|
|
return p2;
|
|
|
|
break;
|
|
|
|
|
2014-05-26 20:38:22 +02:00
|
|
|
case SpannerSegmentType::BEGIN:
|
2015-01-19 12:37:17 +01:00
|
|
|
if (grip == Grip::START)
|
2012-05-26 14:26:10 +02:00
|
|
|
return p1;
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (grip == Grip::END)
|
2012-05-26 14:26:10 +02:00
|
|
|
return system()->abbox().topRight();
|
|
|
|
break;
|
|
|
|
|
2014-05-26 20:38:22 +02:00
|
|
|
case SpannerSegmentType::MIDDLE:
|
2015-01-19 12:37:17 +01:00
|
|
|
if (grip == Grip::START)
|
2012-05-26 14:26:10 +02:00
|
|
|
return sp;
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (grip == Grip::END)
|
2012-05-26 14:26:10 +02:00
|
|
|
return system()->abbox().topRight();
|
|
|
|
break;
|
|
|
|
|
2014-05-26 20:38:22 +02:00
|
|
|
case SpannerSegmentType::END:
|
2015-01-19 12:37:17 +01:00
|
|
|
if (grip == Grip::START)
|
2012-05-26 14:26:10 +02:00
|
|
|
return sp;
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (grip == Grip::END)
|
2012-05-26 14:26:10 +02:00
|
|
|
return p2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return QPointF();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getGrip
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
QPointF SlurSegment::getGrip(Grip n) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
switch (n) {
|
|
|
|
case Grip::START:
|
|
|
|
case Grip::END:
|
|
|
|
return (ups(n).p - gripAnchor(n)) / spatium() + ups(n).off;
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
2015-01-19 12:37:17 +01:00
|
|
|
return ups(n).off;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setGrip
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
void SlurSegment::setGrip(Grip n, const QPointF& pt)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
switch (n) {
|
|
|
|
case Grip::START:
|
|
|
|
case Grip::END:
|
|
|
|
ups(n).off = ((pt * spatium()) - (ups(n).p - gripAnchor(n))) / spatium();
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
default:
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(n).off = pt;
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
slurTie()->layout();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// editDrag
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurSegment::editDrag(const EditData& ed)
|
|
|
|
{
|
|
|
|
qreal _spatium = spatium();
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(ed.curGrip).off += (ed.delta / _spatium);
|
2014-05-05 12:25:17 +02:00
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
if (ed.curGrip == Grip::START || ed.curGrip == Grip::END) {
|
2012-05-26 14:26:10 +02:00
|
|
|
slurTie()->computeBezier(this);
|
|
|
|
//
|
2014-03-16 13:38:57 +01:00
|
|
|
// move anchor for slurs/ties
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2013-08-21 11:59:41 +02:00
|
|
|
SpannerSegmentType st = spannerSegmentType();
|
|
|
|
if (
|
2015-01-19 12:37:17 +01:00
|
|
|
(ed.curGrip == Grip::START && (st == SpannerSegmentType::SINGLE || st == SpannerSegmentType::BEGIN))
|
|
|
|
|| (ed.curGrip == Grip::END && (st == SpannerSegmentType::SINGLE || st == SpannerSegmentType::END))
|
2012-05-26 14:26:10 +02:00
|
|
|
) {
|
2014-05-05 12:25:17 +02:00
|
|
|
Spanner* spanner = slurTie();
|
|
|
|
Qt::KeyboardModifiers km = qApp->keyboardModifiers();
|
2014-05-06 14:00:32 +02:00
|
|
|
Note* note = static_cast<Note*>(ed.view->elementNear(ed.pos));
|
2014-06-24 18:36:02 +02:00
|
|
|
if (note && note->type() == Element::Type::NOTE &&
|
2015-01-19 12:37:17 +01:00
|
|
|
((ed.curGrip == Grip::END && note->chord()->tick() > slurTie()->tick())
|
|
|
|
|| (ed.curGrip == Grip::START && note->chord()->tick() < slurTie()->tick2()))
|
2014-05-06 14:00:32 +02:00
|
|
|
) {
|
2015-01-19 12:37:17 +01:00
|
|
|
if (ed.curGrip == Grip::END && spanner->type() == Element::Type::TIE) {
|
2014-05-05 12:25:17 +02:00
|
|
|
Tie* tie = static_cast<Tie*>(spanner);
|
2015-01-28 15:20:33 +01:00
|
|
|
if (tie->startNote()->pitch() == note->pitch()
|
2015-04-08 17:09:49 +02:00
|
|
|
&& tie->startNote()->chord()->tick() < note->chord()->tick()) {
|
2014-05-05 12:25:17 +02:00
|
|
|
ed.view->setDropTarget(note);
|
|
|
|
if (note != tie->endNote()) {
|
|
|
|
changeAnchor(ed.view, ed.curGrip, note);
|
|
|
|
return;
|
|
|
|
}
|
2013-08-22 12:18:14 +02:00
|
|
|
}
|
2013-08-21 11:59:41 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (spanner->type() != Element::Type::TIE && km != (Qt::ShiftModifier | Qt::ControlModifier)) {
|
2014-05-05 12:25:17 +02:00
|
|
|
Chord* c = note->chord();
|
|
|
|
ed.view->setDropTarget(note);
|
|
|
|
if (c != spanner->endCR()) {
|
2013-10-16 11:26:45 +02:00
|
|
|
changeAnchor(ed.view, ed.curGrip, c);
|
2015-01-19 12:37:17 +01:00
|
|
|
// QPointF p1 = ed.pos - ups(ed.curGrip).p - canvasPos();
|
|
|
|
// ups(ed.curGrip).off = p1 / _spatium;
|
2013-10-16 18:09:26 +02:00
|
|
|
slurTie()->layout();
|
2013-10-16 11:26:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-05 12:25:17 +02:00
|
|
|
else
|
|
|
|
ed.view->setDropTarget(0);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (ed.curGrip == Grip::BEZIER1 || ed.curGrip == Grip::BEZIER2)
|
2012-05-26 14:26:10 +02:00
|
|
|
slurTie()->computeBezier(this);
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (ed.curGrip == Grip::SHOULDER) {
|
|
|
|
ups(ed.curGrip).off = QPointF();
|
2012-05-26 14:26:10 +02:00
|
|
|
slurTie()->computeBezier(this, ed.delta);
|
|
|
|
}
|
2015-01-19 12:37:17 +01:00
|
|
|
else if (ed.curGrip == Grip::DRAG) {
|
|
|
|
ups(Grip::DRAG).off = QPointF();
|
2012-05-26 14:26:10 +02:00
|
|
|
setUserOff(userOff() + ed.delta);
|
|
|
|
}
|
2014-03-31 06:24:51 +02:00
|
|
|
|
|
|
|
// if this SlurSegment was automatically adjusted to avoid collision
|
|
|
|
// lock this edit by resetting SlurSegment to default position
|
|
|
|
// and incorporating previous adjustment into user offset
|
|
|
|
QPointF offset = getAutoAdjust();
|
|
|
|
if (!offset.isNull()) {
|
|
|
|
setAutoAdjust(0.0, 0.0);
|
|
|
|
setUserOff(userOff() + offset);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// writeProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-05-05 18:28:39 +02:00
|
|
|
void SlurSegment::writeSlur(Xml& xml, int no) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
if (ups(Grip::START).off.isNull()
|
|
|
|
&& ups(Grip::END).off.isNull()
|
|
|
|
&& ups(Grip::BEZIER1).off.isNull()
|
|
|
|
&& ups(Grip::BEZIER2).off.isNull()
|
2012-05-26 14:26:10 +02:00
|
|
|
&& userOff().isNull()
|
2012-06-04 12:57:26 +02:00
|
|
|
&& visible()
|
|
|
|
&& (color() == Qt::black)
|
|
|
|
)
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
xml.stag(QString("SlurSegment no=\"%1\"").arg(no));
|
2014-07-31 16:30:04 +02:00
|
|
|
|
|
|
|
writeProperty(xml, P_ID::SLUR_UOFF1);
|
|
|
|
writeProperty(xml, P_ID::SLUR_UOFF2);
|
|
|
|
writeProperty(xml, P_ID::SLUR_UOFF3);
|
|
|
|
writeProperty(xml, P_ID::SLUR_UOFF4);
|
2012-06-04 12:57:26 +02:00
|
|
|
Element::writeProperties(xml);
|
2012-05-26 14:26:10 +02:00
|
|
|
xml.etag();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readSegment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-01-11 18:10:18 +01:00
|
|
|
void SlurSegment::read(XmlReader& e)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-01-11 18:10:18 +01:00
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
2012-05-26 14:26:10 +02:00
|
|
|
if (tag == "o1")
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::START).off = e.readPoint();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "o2")
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::BEZIER1).off = e.readPoint();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "o3")
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::BEZIER2).off = e.readPoint();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "o4")
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::END).off = e.readPoint();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (!Element::readProperties(e))
|
2013-01-11 18:10:18 +01:00
|
|
|
e.unknown();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// computeBezier
|
|
|
|
// compute help points of slur bezier segment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::computeBezier(SlurSegment* ss, QPointF p6o)
|
|
|
|
{
|
|
|
|
qreal _spatium = spatium();
|
|
|
|
qreal shoulderW; // height as fraction of slur-length
|
|
|
|
qreal shoulderH;
|
|
|
|
//
|
|
|
|
// p1 and p2 are the end points of the slur
|
|
|
|
//
|
2015-01-19 12:37:17 +01:00
|
|
|
QPointF pp1 = ss->ups(Grip::START).p + ss->ups(Grip::START).off * _spatium;
|
|
|
|
QPointF pp2 = ss->ups(Grip::END).p + ss->ups(Grip::END).off * _spatium;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
QPointF p2 = pp2 - pp1;
|
2012-09-17 12:17:54 +02:00
|
|
|
if ((p2.x() == 0.0) && (p2.y() == 0.0)) {
|
2013-06-16 23:33:37 +02:00
|
|
|
Measure* m1 = startCR()->segment()->measure();
|
|
|
|
Measure* m2 = endCR()->segment()->measure();
|
2014-07-21 13:24:21 +02:00
|
|
|
qDebug("zero slur at tick %d(%d) track %d in measure %d-%d",
|
|
|
|
m1->tick(), tick(), track(), m1->no(), m2->no());
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal sinb = atan(p2.y() / p2.x());
|
|
|
|
QTransform t;
|
|
|
|
t.rotateRadians(-sinb);
|
|
|
|
p2 = t.map(p2);
|
|
|
|
p6o = t.map(p6o);
|
|
|
|
|
|
|
|
double smallH = 0.5;
|
|
|
|
qreal d = p2.x() / _spatium;
|
|
|
|
if (d <= 2.0) {
|
|
|
|
shoulderH = d * 0.5 * smallH * _spatium;
|
|
|
|
shoulderW = .6;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
qreal dd = log10(1.0 + (d - 2.0) * .5) * 2.0;
|
|
|
|
if (dd > 3.0)
|
|
|
|
dd = 3.0;
|
|
|
|
shoulderH = (dd + smallH) * _spatium;
|
|
|
|
if (d > 18.0)
|
2012-11-11 14:24:02 +01:00
|
|
|
shoulderW = 0.7; // 0.8;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (d > 10)
|
2012-11-11 14:24:02 +01:00
|
|
|
shoulderW = 0.6; // 0.7;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2012-11-11 14:24:02 +01:00
|
|
|
shoulderW = 0.5; // 0.6;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-15 14:13:44 +02:00
|
|
|
shoulderH -= p6o.y();
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!up())
|
|
|
|
shoulderH = -shoulderH;
|
|
|
|
|
|
|
|
qreal c = p2.x();
|
|
|
|
qreal c1 = (c - c * shoulderW) * .5 + p6o.x();
|
|
|
|
qreal c2 = c1 + c * shoulderW + p6o.x();
|
|
|
|
|
|
|
|
QPointF p5 = QPointF(c * .5, 0.0);
|
|
|
|
|
|
|
|
QPointF p3(c1, -shoulderH);
|
|
|
|
QPointF p4(c2, -shoulderH);
|
|
|
|
|
2016-03-19 11:41:38 +01:00
|
|
|
qreal w = score()->styleP(StyleIdx::SlurMidWidth) - score()->styleP(StyleIdx::SlurEndWidth);
|
2014-10-04 13:39:06 +02:00
|
|
|
if ((c2 - c1) <= _spatium)
|
2012-05-26 14:26:10 +02:00
|
|
|
w *= .5;
|
|
|
|
QPointF th(0.0, w); // thickness of slur
|
|
|
|
|
2015-01-19 12:37:17 +01:00
|
|
|
QPointF p3o = p6o + t.map(ss->ups(Grip::BEZIER1).off * _spatium);
|
|
|
|
QPointF p4o = p6o + t.map(ss->ups(Grip::BEZIER2).off * _spatium);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-06-10 11:03:34 +02:00
|
|
|
if (!p6o.isNull()) {
|
2012-09-15 14:13:44 +02:00
|
|
|
QPointF p6i = t.inverted().map(p6o) / _spatium;
|
2015-01-19 12:37:17 +01:00
|
|
|
ss->ups(Grip::BEZIER1).off += p6i ;
|
|
|
|
ss->ups(Grip::BEZIER2).off += p6i;
|
2012-09-15 14:13:44 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//-----------------------------------calculate p6
|
|
|
|
QPointF pp3 = p3 + p3o;
|
|
|
|
QPointF pp4 = p4 + p4o;
|
|
|
|
QPointF ppp4 = pp4 - pp3;
|
|
|
|
|
|
|
|
qreal r2 = atan(ppp4.y() / ppp4.x());
|
|
|
|
t.reset();
|
|
|
|
t.rotateRadians(-r2);
|
|
|
|
QPointF p6 = QPointF(t.map(ppp4).x() * .5, 0.0);
|
|
|
|
|
|
|
|
t.rotateRadians(2 * r2);
|
2012-09-15 14:13:44 +02:00
|
|
|
p6 = t.map(p6) + pp3 - p6o;
|
2012-05-26 14:26:10 +02:00
|
|
|
//-----------------------------------
|
|
|
|
|
2014-11-06 10:44:57 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
ss->path = QPainterPath();
|
|
|
|
ss->path.moveTo(QPointF());
|
|
|
|
ss->path.cubicTo(p3 + p3o - th, p4 + p4o - th, p2);
|
|
|
|
if (lineType() == 0)
|
|
|
|
ss->path.cubicTo(p4 +p4o + th, p3 + p3o + th, QPointF());
|
|
|
|
|
|
|
|
th = QPointF(0.0, 3.0 * w);
|
|
|
|
ss->shapePath = QPainterPath();
|
|
|
|
ss->shapePath.moveTo(QPointF());
|
|
|
|
ss->shapePath.cubicTo(p3 + p3o - th, p4 + p4o - th, p2);
|
|
|
|
ss->shapePath.cubicTo(p4 +p4o + th, p3 + p3o + th, QPointF());
|
|
|
|
|
|
|
|
// translate back
|
|
|
|
t.reset();
|
|
|
|
t.translate(pp1.x(), pp1.y());
|
|
|
|
t.rotateRadians(sinb);
|
|
|
|
ss->path = t.map(ss->path);
|
|
|
|
ss->shapePath = t.map(ss->shapePath);
|
2015-01-19 12:37:17 +01:00
|
|
|
ss->ups(Grip::BEZIER1).p = t.map(p3);
|
|
|
|
ss->ups(Grip::BEZIER2).p = t.map(p4);
|
|
|
|
ss->ups(Grip::END).p = t.map(p2) - ss->ups(Grip::END).off * _spatium;
|
|
|
|
ss->ups(Grip::DRAG).p = t.map(p5);
|
|
|
|
ss->ups(Grip::SHOULDER).p = t.map(p6);
|
2014-11-06 10:44:57 +01:00
|
|
|
|
2014-11-06 19:18:20 +01:00
|
|
|
QPointF staffOffset;
|
|
|
|
if (ss->system() && ss->track() >= 0)
|
|
|
|
staffOffset = QPointF(0.0, -ss->system()->staff(ss->staffIdx())->y());
|
|
|
|
|
2014-11-06 10:44:57 +01:00
|
|
|
ss->path.translate(staffOffset);
|
|
|
|
ss->shapePath.translate(staffOffset);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layout
|
|
|
|
// p1, p2 are in System coordinates
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-05-05 18:28:39 +02:00
|
|
|
void SlurSegment::layoutSegment(const QPointF& p1, const QPointF& p2)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::START).p = p1;
|
|
|
|
ups(Grip::END).p = p2;
|
2012-05-26 14:26:10 +02:00
|
|
|
slurTie()->computeBezier(this);
|
2014-03-29 07:42:36 +01:00
|
|
|
QRectF bbox = path.boundingRect();
|
2014-03-31 06:24:51 +02:00
|
|
|
|
|
|
|
// adjust position to avoid staff line if necessary
|
2015-03-04 07:07:47 +01:00
|
|
|
Staff* st = staff();
|
2014-08-08 17:31:33 +02:00
|
|
|
bool reverseAdjust = false;
|
2015-03-04 07:07:47 +01:00
|
|
|
if (slurTie()->type() == Element::Type::TIE && st && !st->isTabStaff()) {
|
2014-08-08 17:31:33 +02:00
|
|
|
// multinote chords with ties need special handling
|
|
|
|
// otherwise, adjusted tie might crowd an unadjusted tie unnecessarily
|
|
|
|
Tie* t = static_cast<Tie*>(slurTie());
|
|
|
|
Note* sn = t->startNote();
|
|
|
|
Chord* sc = sn ? sn->chord() : 0;
|
|
|
|
// normally, the adjustment moves ties according to their direction (eg, up if tie is up)
|
|
|
|
// but we will reverse this for notes within chords when appropriate
|
2015-03-04 07:07:47 +01:00
|
|
|
// for two-note chords, it looks better to have notes on spaces tied outside the lines
|
2014-08-08 17:31:33 +02:00
|
|
|
if (sc) {
|
|
|
|
int notes = sc->notes().size();
|
|
|
|
bool onLine = !(sn->line() & 1);
|
|
|
|
if ((onLine && notes > 1) || (!onLine && notes > 2))
|
|
|
|
reverseAdjust = true;
|
|
|
|
}
|
|
|
|
}
|
2014-03-29 07:42:36 +01:00
|
|
|
qreal sp = spatium();
|
2014-03-30 18:14:55 +02:00
|
|
|
qreal minDistance = 0.5;
|
2014-03-31 06:24:51 +02:00
|
|
|
autoAdjustOffset = QPointF();
|
2015-03-04 07:07:47 +01:00
|
|
|
if (bbox.height() < minDistance * 2 * sp && st && !st->isTabStaff()) {
|
2014-03-31 06:24:51 +02:00
|
|
|
// slur/tie is fairly flat
|
2014-03-29 07:42:36 +01:00
|
|
|
bool up = slurTie()->up();
|
2014-03-31 06:24:51 +02:00
|
|
|
qreal ld = st->lineDistance() * sp;
|
2014-11-30 04:55:51 +01:00
|
|
|
qreal topY = bbox.top() / ld;
|
|
|
|
qreal bottomY = bbox.bottom() / ld;
|
2014-03-31 06:24:51 +02:00
|
|
|
int lineY = up ? qRound(topY) : qRound(bottomY);
|
|
|
|
if (lineY >= 0 && lineY < st->lines() * st->lineDistance()) {
|
2014-03-29 07:42:36 +01:00
|
|
|
// on staff
|
2014-03-31 06:24:51 +02:00
|
|
|
if (qAbs(topY - lineY) < minDistance && qAbs(bottomY - lineY) < minDistance) {
|
2014-03-29 07:42:36 +01:00
|
|
|
// too close to line
|
2014-04-01 03:39:01 +02:00
|
|
|
if (!isNudged() && !isEdited()) {
|
|
|
|
// user has not nudged or edited
|
2014-03-30 18:14:55 +02:00
|
|
|
qreal offY;
|
2014-08-08 17:31:33 +02:00
|
|
|
if (up != reverseAdjust) // exclusive or
|
2014-03-31 06:24:51 +02:00
|
|
|
offY = (lineY - minDistance) - topY;
|
2014-03-30 18:14:55 +02:00
|
|
|
else
|
2014-03-31 06:24:51 +02:00
|
|
|
offY = (lineY + minDistance) - bottomY;
|
2014-03-30 18:14:55 +02:00
|
|
|
setAutoAdjust(0.0, offY * sp);
|
2014-03-29 07:42:36 +01:00
|
|
|
bbox = path.boundingRect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
setbbox(path.boundingRect());
|
2016-05-13 06:55:03 +02:00
|
|
|
if ((staffIdx() > 0) && score()->mscVersion() < 206 && !readPos().isNull()) {
|
2014-11-06 19:18:20 +01:00
|
|
|
QPointF staffOffset;
|
|
|
|
if (system() && track() >= 0)
|
|
|
|
staffOffset = QPointF(0.0, system()->staff(staffIdx())->y());
|
|
|
|
setReadPos(readPos() + staffOffset);
|
|
|
|
}
|
2012-06-29 12:25:09 +02:00
|
|
|
adjustReadPos();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-03-30 18:14:55 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setAutoAdjust
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurSegment::setAutoAdjust(const QPointF& offset)
|
|
|
|
{
|
|
|
|
QPointF diff = offset - autoAdjustOffset;
|
2014-03-31 06:24:51 +02:00
|
|
|
if (!diff.isNull()) {
|
|
|
|
path.translate(diff);
|
|
|
|
shapePath.translate(diff);
|
2015-01-19 12:37:17 +01:00
|
|
|
for (int i = 0; i < int(Grip::GRIPS); ++i)
|
|
|
|
_ups[i].p += diff;
|
2014-03-30 18:14:55 +02:00
|
|
|
autoAdjustOffset = offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-01 03:39:01 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// isEdited
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SlurSegment::isEdited() const
|
|
|
|
{
|
2015-01-19 12:37:17 +01:00
|
|
|
for (int i = 0; i < int(Grip::GRIPS); ++i) {
|
|
|
|
if (!_ups[i].off.isNull())
|
2014-04-01 03:39:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// SlurTie
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SlurTie::SlurTie(Score* s)
|
|
|
|
: Spanner(s)
|
|
|
|
{
|
2016-03-02 13:20:19 +01:00
|
|
|
_slurDirection = Direction::AUTO;
|
2012-05-26 14:26:10 +02:00
|
|
|
_up = true;
|
|
|
|
_lineType = 0; // default is solid
|
|
|
|
}
|
|
|
|
|
|
|
|
SlurTie::SlurTie(const SlurTie& t)
|
|
|
|
: Spanner(t)
|
|
|
|
{
|
|
|
|
_up = t._up;
|
|
|
|
_slurDirection = t._slurDirection;
|
|
|
|
_lineType = t._lineType;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// SlurTie
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SlurTie::~SlurTie()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// fixArticulations
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static qreal fixArticulations(qreal yo, Chord* c, qreal _up)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// handle special case of tenuto and staccato;
|
|
|
|
//
|
2016-02-06 22:03:43 +01:00
|
|
|
const QVector<Articulation*>& al = c->articulations();
|
2012-11-20 20:51:18 +01:00
|
|
|
if (al.size() >= 2) {
|
|
|
|
Articulation* a = al.at(1);
|
2014-12-23 05:30:41 +01:00
|
|
|
if (a->up() == c->up())
|
|
|
|
return yo;
|
|
|
|
else if (a->articulationType() == ArticulationType::Tenuto || a->articulationType() == ArticulationType::Staccato)
|
2012-05-26 14:26:10 +02:00
|
|
|
return a->y() + (a->height() + c->score()->spatium() * .3) * _up;
|
|
|
|
}
|
2012-11-20 20:51:18 +01:00
|
|
|
else if (al.size() >= 1) {
|
|
|
|
Articulation* a = al.at(0);
|
2014-12-23 05:30:41 +01:00
|
|
|
if (a->up() == c->up())
|
|
|
|
return yo;
|
|
|
|
else if (a->articulationType() == ArticulationType::Tenuto || a->articulationType() == ArticulationType::Staccato)
|
2012-05-26 14:26:10 +02:00
|
|
|
return a->y() + (a->height() + c->score()->spatium() * .3) * _up;
|
|
|
|
}
|
|
|
|
return yo;
|
|
|
|
}
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// slurPos
|
|
|
|
// Calculate position of start- and endpoint of slur
|
|
|
|
// relative to System() position.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::slurPosChord(SlurPos* sp)
|
|
|
|
{
|
2014-04-23 18:07:38 +02:00
|
|
|
Chord* stChord;
|
|
|
|
Chord* enChord ;
|
2014-07-02 09:55:50 +02:00
|
|
|
if (startChord()->isGraceAfter()){ // grace notes after, coming in reverse order
|
2014-04-23 18:07:38 +02:00
|
|
|
stChord = endChord();
|
|
|
|
enChord = startChord();
|
|
|
|
_up = false;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
stChord = startChord();
|
|
|
|
enChord = endChord();
|
|
|
|
}
|
|
|
|
Note* _startNote = stChord->downNote();
|
|
|
|
Note* _endNote = enChord->downNote();
|
2013-06-16 23:33:37 +02:00
|
|
|
qreal hw = _startNote->headWidth();
|
|
|
|
qreal __up = _up ? -1.0 : 1.0;
|
|
|
|
qreal _spatium = spatium();
|
|
|
|
|
2014-07-03 15:02:36 +02:00
|
|
|
Measure* measure = endChord()->measure();
|
|
|
|
sp->system1 = measure->system();
|
|
|
|
if (!sp->system1) // DEBUG
|
|
|
|
return;
|
|
|
|
Q_ASSERT(sp->system1);
|
2013-06-16 23:33:37 +02:00
|
|
|
sp->system2 = sp->system1;
|
|
|
|
QPointF pp(sp->system1->pagePos());
|
|
|
|
|
|
|
|
qreal xo;
|
|
|
|
qreal yo;
|
|
|
|
|
|
|
|
//------p1
|
|
|
|
if (_up) {
|
|
|
|
xo = _startNote->x() + hw * 1.12;
|
|
|
|
yo = _startNote->pos().y() + hw * .3 * __up;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
xo = _startNote->x() + hw * 0.4;
|
|
|
|
yo = _startNote->pos().y() + _spatium * .75 * __up;
|
|
|
|
}
|
2014-04-23 18:07:38 +02:00
|
|
|
sp->p1 = stChord->pagePos() - pp + QPointF(xo, yo);
|
2013-06-16 23:33:37 +02:00
|
|
|
|
|
|
|
//------p2
|
2014-04-23 18:07:38 +02:00
|
|
|
if ((enChord->notes().size() > 1) || (enChord->stem() && !enChord->up() && !_up)) {
|
2013-06-16 23:33:37 +02:00
|
|
|
xo = _endNote->x() - hw * 0.12;
|
|
|
|
yo = _endNote->pos().y() + hw * .3 * __up;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
xo = _endNote->x() + hw * 0.15;
|
|
|
|
yo = _endNote->pos().y() + _spatium * .75 * __up;
|
|
|
|
}
|
2014-04-23 18:07:38 +02:00
|
|
|
sp->p2 = enChord->pagePos() - pp + QPointF(xo, yo);
|
2013-06-16 23:33:37 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// slurPos
|
|
|
|
// calculate position of start- and endpoint of slur
|
|
|
|
// relative to System() position
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::slurPos(SlurPos* sp)
|
|
|
|
{
|
|
|
|
qreal _spatium = spatium();
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
if (endCR() == 0) {
|
|
|
|
sp->p1 = startCR()->pagePos();
|
|
|
|
sp->p1.rx() += startCR()->width();
|
2012-05-26 14:26:10 +02:00
|
|
|
sp->p2 = sp->p1;
|
|
|
|
sp->p2.rx() += 5 * _spatium;
|
2013-06-16 23:33:37 +02:00
|
|
|
sp->system1 = startCR()->measure()->system();
|
2012-05-26 14:26:10 +02:00
|
|
|
sp->system2 = sp->system1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-06 10:34:42 +02:00
|
|
|
bool useTablature = staff() != nullptr && staff()->isTabStaff();
|
|
|
|
bool staffHasStems = true; // assume staff uses stems
|
|
|
|
StaffType* stt = nullptr;
|
|
|
|
if (useTablature) {
|
|
|
|
stt = staff()->staffType();
|
|
|
|
staffHasStems = stt->stemThrough(); // if tab with stems beside, stems do not count for slur pos
|
|
|
|
}
|
2015-03-09 18:55:46 +01:00
|
|
|
|
2016-02-06 07:39:33 +01:00
|
|
|
// start and end cr, chord, and note
|
2014-07-08 11:16:21 +02:00
|
|
|
ChordRest* scr = startCR();
|
|
|
|
ChordRest* ecr = endCR();
|
|
|
|
Chord* sc = 0;
|
|
|
|
Note* note1 = 0;
|
2016-02-06 07:39:33 +01:00
|
|
|
if (scr->type() == Element::Type::CHORD) {
|
|
|
|
sc = static_cast<Chord*>(scr);
|
2012-09-17 12:17:54 +02:00
|
|
|
note1 = _up ? sc->upNote() : sc->downNote();
|
|
|
|
}
|
|
|
|
Chord* ec = 0;
|
|
|
|
Note* note2 = 0;
|
2016-02-06 07:39:33 +01:00
|
|
|
if (ecr->type() == Element::Type::CHORD) {
|
|
|
|
ec = static_cast<Chord*>(ecr);
|
2012-09-17 12:17:54 +02:00
|
|
|
note2 = _up ? ec->upNote() : ec->downNote();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-09-17 12:17:54 +02:00
|
|
|
sp->system1 = scr->measure()->system();
|
|
|
|
sp->system2 = ecr->measure()->system();
|
2013-10-14 15:34:46 +02:00
|
|
|
if (sp->system1 == 0 || sp->system2 == 0)
|
|
|
|
return;
|
|
|
|
|
2014-07-02 09:55:50 +02:00
|
|
|
sp->p1 = scr->pagePos() - sp->system1->pagePos();
|
|
|
|
sp->p2 = ecr->pagePos() - sp->system2->pagePos();
|
2014-07-22 17:39:25 +02:00
|
|
|
// account for centering or other adjustments (other than mirroring)
|
2014-07-22 21:43:14 +02:00
|
|
|
if (note1 && !note1->mirror())
|
2014-07-22 17:39:25 +02:00
|
|
|
sp->p1.rx() += note1->x();
|
2014-07-22 21:43:14 +02:00
|
|
|
if (note2 && !note2->mirror())
|
2014-07-22 17:39:25 +02:00
|
|
|
sp->p2.rx() += note2->x();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
qreal xo, yo;
|
|
|
|
|
2015-09-06 10:34:42 +02:00
|
|
|
Stem* stem1 = sc && staffHasStems ? sc->stem() : 0;
|
|
|
|
Stem* stem2 = ec && staffHasStems ? ec->stem() : 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-26 12:54:37 +02:00
|
|
|
enum class SlurAnchor : char {
|
|
|
|
NONE, STEM
|
2012-07-24 18:21:22 +02:00
|
|
|
};
|
2014-05-26 12:54:37 +02:00
|
|
|
SlurAnchor sa1 = SlurAnchor::NONE;
|
|
|
|
SlurAnchor sa2 = SlurAnchor::NONE;
|
2016-02-06 07:39:33 +01:00
|
|
|
// if slur is 'embedded' between either stem or both (as it might happen with voices)
|
2015-08-24 00:12:38 +02:00
|
|
|
// link corresponding slur end to stem position
|
2012-09-17 12:17:54 +02:00
|
|
|
if ((scr->up() == ecr->up()) && !scr->beam() && !ecr->beam() && (_up == scr->up())) {
|
2016-02-06 07:39:33 +01:00
|
|
|
// both chords are facing same direction and slur is also in same direction
|
|
|
|
// and no beams
|
2012-08-10 09:21:29 +02:00
|
|
|
if (stem1)
|
2014-05-26 12:54:37 +02:00
|
|
|
sa1 = SlurAnchor::STEM;
|
2012-08-10 09:21:29 +02:00
|
|
|
if (stem2)
|
2014-05-26 12:54:37 +02:00
|
|
|
sa2 = SlurAnchor::STEM;
|
2012-07-24 18:21:22 +02:00
|
|
|
}
|
|
|
|
|
2012-08-10 09:21:29 +02:00
|
|
|
qreal __up = _up ? -1.0 : 1.0;
|
2016-02-06 07:39:33 +01:00
|
|
|
qreal hw1 = note1 ? note1->tabHeadWidth(stt) : scr->width(); // if stt == 0, tabHeadWidth()
|
|
|
|
qreal hw2 = note2 ? note2->tabHeadWidth(stt) : ecr->width(); // defaults to headWidth()
|
2015-08-24 00:12:38 +02:00
|
|
|
QPointF pt;
|
2012-08-10 09:21:29 +02:00
|
|
|
switch (sa1) {
|
2015-08-24 00:12:38 +02:00
|
|
|
case SlurAnchor::STEM: //sc can't be null
|
|
|
|
// place slur starting point at stem base point
|
|
|
|
pt = sc->stemPos() - sc->pagePos() + sc->stem()->p2();
|
|
|
|
if (useTablature) // in tabs, stems are centred on note:
|
2016-05-03 01:21:42 +02:00
|
|
|
pt.rx() = hw1 * 0.5; // skip half notehead to touch stem
|
2015-08-24 00:12:38 +02:00
|
|
|
sp->p1 += pt;
|
2016-05-03 01:21:42 +02:00
|
|
|
sp->p1 += QPointF(0.35 * _spatium, 0.25 * _spatium); // clear the stem (x) and the notehead (y)
|
2012-08-10 09:21:29 +02:00
|
|
|
break;
|
2014-05-26 12:54:37 +02:00
|
|
|
case SlurAnchor::NONE:
|
2012-08-10 09:21:29 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
switch (sa2) {
|
2015-08-24 00:12:38 +02:00
|
|
|
case SlurAnchor::STEM: //ec can't be null
|
|
|
|
pt = ec->stemPos() - ec->pagePos() + ec->stem()->p2();
|
|
|
|
if (useTablature)
|
|
|
|
pt.rx() = hw2 * 0.5;
|
|
|
|
sp->p2 += pt;
|
2012-08-10 09:21:29 +02:00
|
|
|
sp->p2 += QPointF(-0.35 * _spatium, 0.25 * _spatium);
|
|
|
|
break;
|
2014-05-26 12:54:37 +02:00
|
|
|
case SlurAnchor::NONE:
|
2012-08-10 09:21:29 +02:00
|
|
|
break;
|
2012-07-24 18:21:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// default position:
|
2016-05-03 01:21:42 +02:00
|
|
|
// horizontal: middle of notehead
|
|
|
|
// vertical: _spatium * .4 above/below notehead
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
//------p1
|
2015-08-26 15:35:40 +02:00
|
|
|
// Compute x0, y0 and stemPos
|
|
|
|
if (sa1 == SlurAnchor::NONE || sa2 == SlurAnchor::NONE) { // need stemPos if sa2 == SlurAnchor::NONE
|
|
|
|
bool stemPos = false; // p1 starts at chord stem side
|
2016-02-06 07:39:33 +01:00
|
|
|
|
|
|
|
// default positions
|
2015-08-26 15:35:40 +02:00
|
|
|
xo = hw1 * .5;
|
|
|
|
if (note1)
|
|
|
|
yo = note1->pos().y();
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (_up)
|
|
|
|
yo = scr->bbox().top();
|
2015-08-26 15:35:40 +02:00
|
|
|
else
|
2016-02-06 07:39:33 +01:00
|
|
|
yo = scr->bbox().top() + scr->height();
|
2015-08-26 15:35:40 +02:00
|
|
|
yo += _spatium * .9 * __up;
|
|
|
|
|
2016-02-06 07:39:33 +01:00
|
|
|
// adjustments for stem and/or beam
|
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
if (stem1) { //sc not null
|
|
|
|
Beam* beam1 = sc->beam();
|
|
|
|
if (beam1 && (beam1->elements().back() != sc) && (sc->up() == _up)) {
|
2016-02-06 07:39:33 +01:00
|
|
|
// start chord is beamed but not the last chord of beam group
|
|
|
|
// and slur direction is same as start chord (stem side)
|
|
|
|
|
|
|
|
// in these cases, layout start of slur to stem
|
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
qreal sh = stem1->height() + _spatium;
|
|
|
|
if (_up)
|
|
|
|
yo = sc->downNote()->pos().y() - sh;
|
|
|
|
else
|
|
|
|
yo = sc->upNote()->pos().y() + sh;
|
|
|
|
xo = stem1->pos().x();
|
2016-02-06 07:39:33 +01:00
|
|
|
|
|
|
|
// force end of slur to layout to stem as well,
|
|
|
|
// if start and end chords have same stem direction
|
|
|
|
stemPos = true;
|
2015-08-26 15:35:40 +02:00
|
|
|
}
|
|
|
|
else {
|
2016-02-06 07:39:33 +01:00
|
|
|
// start chord is not beamed or is last chord of beam group
|
|
|
|
// or slur direction is opposite that of start chord
|
|
|
|
|
|
|
|
// at this point slur is in default position relative to note on slur side
|
|
|
|
// but we may need to make further adjustments
|
|
|
|
|
|
|
|
// if stem and slur are both up
|
|
|
|
// we need to clear stem horizontally
|
2015-08-26 15:35:40 +02:00
|
|
|
if (sc->up() && _up)
|
|
|
|
xo = hw1 + _spatium * .3;
|
2016-02-06 07:39:33 +01:00
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
//
|
|
|
|
// handle case: stem up - stem down
|
|
|
|
// stem down - stem up
|
|
|
|
//
|
|
|
|
if ((sc->up() != ecr->up()) && (sc->up() == _up)) {
|
2016-02-06 07:39:33 +01:00
|
|
|
// start and end chord have opposite direction
|
|
|
|
// and slur direction is same as start chord
|
|
|
|
// (so slur starts on stem side)
|
|
|
|
|
|
|
|
// float the start point along the stem to follow direction of movement
|
|
|
|
// see for example Gould p. 111
|
|
|
|
|
|
|
|
// get position of note on slur side for start & end chords
|
|
|
|
Note* n1 = sc->up() ? sc->upNote() : sc->downNote();
|
2015-08-26 15:35:40 +02:00
|
|
|
Note* n2 = 0;
|
2016-02-06 07:39:33 +01:00
|
|
|
if (ec)
|
|
|
|
n2 = ec->up() ? ec->upNote() : ec->downNote();
|
2015-08-26 15:35:40 +02:00
|
|
|
|
2016-02-06 07:39:33 +01:00
|
|
|
// differential in note positions
|
|
|
|
qreal yd = (n2 ? n2->pos().y() : ecr->pos().y()) - n1->pos().y();
|
2015-08-26 15:35:40 +02:00
|
|
|
yd *= .5;
|
|
|
|
|
2016-02-06 07:39:33 +01:00
|
|
|
// float along stem according to differential
|
|
|
|
qreal sh = stem1->height();
|
|
|
|
if (_up && yd < 0.0)
|
|
|
|
yo = qMax(yo + yd, sc->downNote()->pos().y() - sh - _spatium);
|
|
|
|
else if (!_up && yd > 0.0)
|
|
|
|
yo = qMin(yo + yd, sc->upNote()->pos().y() + sh + _spatium);
|
|
|
|
|
|
|
|
// we may wish to force end to align to stem as well,
|
|
|
|
// if it is in same direction
|
|
|
|
// (but it won't be, so this assignment should have no effect)
|
2015-08-26 15:35:40 +02:00
|
|
|
stemPos = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (sc->up() != _up) {
|
|
|
|
// slur opposite direction from chord
|
|
|
|
// avoid articulations
|
2015-08-26 15:35:40 +02:00
|
|
|
yo = fixArticulations(yo, sc, __up);
|
2016-02-06 07:39:33 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (sc && sc->up() != _up) {
|
|
|
|
// slur opposite direction from chord
|
|
|
|
// avoid articulations
|
2015-08-26 15:35:40 +02:00
|
|
|
yo = fixArticulations(yo, sc, __up);
|
2016-02-06 07:39:33 +01:00
|
|
|
}
|
2015-08-26 15:35:40 +02:00
|
|
|
|
|
|
|
if (sa1 == SlurAnchor::NONE)
|
|
|
|
sp->p1 += QPointF(xo, yo);
|
|
|
|
|
|
|
|
//------p2
|
|
|
|
if (sa2 == SlurAnchor::NONE) {
|
2016-02-06 07:39:33 +01:00
|
|
|
|
|
|
|
// default positions
|
2015-08-26 15:35:40 +02:00
|
|
|
xo = hw2 * .5;
|
|
|
|
if (note2)
|
|
|
|
yo = note2->pos().y();
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (_up)
|
2015-08-26 15:35:40 +02:00
|
|
|
yo = endCR()->bbox().top();
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2015-08-26 15:35:40 +02:00
|
|
|
yo = endCR()->bbox().top() + endCR()->height();
|
|
|
|
yo += _spatium * .9 * __up;
|
|
|
|
|
2016-02-06 07:39:33 +01:00
|
|
|
// adjustments for stem and/or beam
|
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
if (stem2) { //ec can't be null
|
|
|
|
Beam* beam2 = ec->beam();
|
|
|
|
if ((stemPos && (scr->up() == ec->up()))
|
|
|
|
|| (beam2
|
2016-02-06 22:03:43 +01:00
|
|
|
&& (!beam2->elements().empty())
|
2015-08-26 15:35:40 +02:00
|
|
|
&& (beam2->elements().front() != ec)
|
|
|
|
&& (ec->up() == _up)
|
|
|
|
&& sc && (sc->noteType() == NoteType::NORMAL)
|
|
|
|
)
|
|
|
|
) {
|
2016-02-06 07:39:33 +01:00
|
|
|
|
|
|
|
// slur start was laid out to stem and start and end have same direction
|
|
|
|
// OR
|
|
|
|
// end chord is beamed but not the first chord of beam group
|
|
|
|
// and slur direction is same as end chord (stem side)
|
|
|
|
// and start chordrest is not a grace chord
|
|
|
|
|
|
|
|
// in these cases, layout end of slur to stem
|
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
qreal sh = stem2->height() + _spatium;
|
|
|
|
if (_up)
|
|
|
|
yo = ec->downNote()->pos().y() - sh;
|
|
|
|
else
|
|
|
|
yo = ec->upNote()->pos().y() + sh;
|
|
|
|
xo = stem2->pos().x();
|
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// slur was not aligned to stem or start and end have different direction
|
|
|
|
// AND
|
|
|
|
// end chord is not beamed or is first chord of beam group
|
|
|
|
// or slur direction is opposite that of end chord
|
|
|
|
|
|
|
|
// if stem and slur are both down,
|
|
|
|
// we need to clear stem horizontally
|
|
|
|
if (!ec->up() && !_up)
|
|
|
|
xo = -_spatium * .3 + note2->x();
|
|
|
|
|
|
|
|
//
|
|
|
|
// handle case: stem up - stem down
|
|
|
|
// stem down - stem up
|
|
|
|
//
|
|
|
|
if ((scr->up() != ec->up()) && (ec->up() == _up)) {
|
|
|
|
// start and end chord have opposite direction
|
|
|
|
// and slur direction is same as end chord
|
|
|
|
// (so slur end on stem side)
|
|
|
|
|
|
|
|
// float the end point along the stem to follow direction of movement
|
|
|
|
// see for example Gould p. 111
|
|
|
|
|
|
|
|
Note* n1 = 0;
|
|
|
|
if (sc)
|
|
|
|
n1 = sc->up() ? sc->upNote() : sc->downNote();
|
|
|
|
Note* n2 = ec->up() ? ec->upNote() : ec->downNote();
|
|
|
|
|
|
|
|
qreal yd = n2->pos().y() - (n1 ? n1->pos().y() : startCR()->pos().y());
|
|
|
|
yd *= .5;
|
|
|
|
|
|
|
|
qreal mh = stem2->height();
|
|
|
|
if (_up && yd > 0.0)
|
|
|
|
yo = qMax(yo - yd, ec->downNote()->pos().y() - mh - _spatium);
|
|
|
|
else if (!_up && yd < 0.0)
|
|
|
|
yo = qMin(yo - yd, ec->upNote()->pos().y() + mh + _spatium);
|
2015-08-26 15:35:40 +02:00
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (ec->up() != _up) {
|
|
|
|
// slur opposite direction from chord
|
|
|
|
// avoid articulations
|
|
|
|
yo = fixArticulations(yo, ec, __up);
|
2015-08-26 15:35:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-02-06 07:39:33 +01:00
|
|
|
else if (ec && ec->up() != _up) {
|
|
|
|
// slur opposite direction from chord
|
|
|
|
// avoid articulations
|
2015-08-26 15:35:40 +02:00
|
|
|
yo = fixArticulations(yo, ec, __up);
|
2016-02-06 07:39:33 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-08-26 15:35:40 +02:00
|
|
|
sp->p2 += QPointF(xo, yo);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// writeProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurTie::writeProperties(Xml& xml) const
|
|
|
|
{
|
|
|
|
Element::writeProperties(xml);
|
2016-03-05 19:50:55 +01:00
|
|
|
if (track() != track2() && track2() != -1)
|
2014-01-21 23:28:00 +01:00
|
|
|
xml.tag("track2", track2());
|
2012-05-26 14:26:10 +02:00
|
|
|
int idx = 0;
|
2016-03-05 19:50:55 +01:00
|
|
|
for (const SpannerSegment* ss : spannerSegments())
|
2015-05-05 18:28:39 +02:00
|
|
|
((SlurSegment*)ss)->writeSlur(xml, idx++);
|
2016-03-02 13:20:19 +01:00
|
|
|
if (_slurDirection != Direction::AUTO)
|
2014-05-07 18:09:01 +02:00
|
|
|
xml.tag("up", int(_slurDirection));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (_lineType)
|
|
|
|
xml.tag("lineType", _lineType);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-01-11 18:10:18 +01:00
|
|
|
bool SlurTie::readProperties(XmlReader& e)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-01-11 18:10:18 +01:00
|
|
|
const QStringRef& tag(e.name());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (tag == "SlurSegment") {
|
2013-01-11 18:10:18 +01:00
|
|
|
int idx = e.intAttribute("no", 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
int n = spannerSegments().size();
|
|
|
|
for (int i = n; i < idx; ++i)
|
|
|
|
add(new SlurSegment(score()));
|
|
|
|
SlurSegment* segment = new SlurSegment(score());
|
|
|
|
segment->read(e);
|
|
|
|
add(segment);
|
2014-10-02 09:08:58 +02:00
|
|
|
// in v1.x "visible" is a property of the segment only;
|
|
|
|
// we must ensure that it propagates also to the parent element
|
|
|
|
// That's why the visibility is set after adding the segment
|
|
|
|
// to the corresponding spanner
|
|
|
|
if (score()->mscVersion() <= 114)
|
|
|
|
segment->SpannerSegment::setVisible(segment->visible());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else if (tag == "up")
|
2016-03-02 13:20:19 +01:00
|
|
|
_slurDirection = Direction(e.readInt());
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "lineType")
|
2013-01-11 18:10:18 +01:00
|
|
|
_lineType = e.readInt();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (!Element::readProperties(e))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-24 18:21:22 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetLineType
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurTie::undoSetLineType(int t)
|
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
score()->undoChangeProperty(this, P_ID::LINE_TYPE, t);
|
2012-07-24 18:21:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetSlurDirection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
void SlurTie::undoSetSlurDirection(Direction d)
|
2012-07-24 18:21:22 +02:00
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
score()->undoChangeProperty(this, P_ID::SLUR_DIRECTION, int(d));
|
2012-07-24 18:21:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
2012-11-19 10:08:15 +01:00
|
|
|
// reset
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-11-19 10:08:15 +01:00
|
|
|
void SlurTie::reset()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
score()->undoChangeProperty(this, P_ID::USER_OFF, QPointF());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getProperty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QVariant SlurTie::getProperty(P_ID propertyId) const
|
|
|
|
{
|
|
|
|
switch(propertyId) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE: return lineType();
|
2016-05-12 15:39:49 +02:00
|
|
|
case P_ID::SLUR_DIRECTION: return slurDirection();
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
|
|
|
return Spanner::getProperty(propertyId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setProperty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SlurTie::setProperty(P_ID propertyId, const QVariant& v)
|
|
|
|
{
|
|
|
|
switch(propertyId) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE: setLineType(v.toInt()); break;
|
2016-05-12 15:39:49 +02:00
|
|
|
case P_ID::SLUR_DIRECTION: setSlurDirection(v.value<Direction>()); break;
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
|
|
|
return Spanner::setProperty(propertyId, v);
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
score()->setLayoutAll();
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-01 16:57:44 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// propertyDefault
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QVariant SlurTie::propertyDefault(P_ID id) const
|
|
|
|
{
|
|
|
|
switch (id) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE:
|
2013-07-01 16:57:44 +02:00
|
|
|
return 0;
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::SLUR_DIRECTION:
|
2016-05-12 15:39:49 +02:00
|
|
|
return Direction(Direction::AUTO);
|
2013-07-01 16:57:44 +02:00
|
|
|
default:
|
|
|
|
return Spanner::propertyDefault(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getProperty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QVariant SlurSegment::getProperty(P_ID propertyId) const
|
|
|
|
{
|
|
|
|
switch(propertyId) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE:
|
|
|
|
case P_ID::SLUR_DIRECTION:
|
2013-07-01 16:57:44 +02:00
|
|
|
return slurTie()->getProperty(propertyId);
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF1:
|
2015-01-19 12:37:17 +01:00
|
|
|
return ups(Grip::START).off;
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF2:
|
2015-01-19 12:37:17 +01:00
|
|
|
return ups(Grip::BEZIER1).off;
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF3:
|
2015-01-19 12:37:17 +01:00
|
|
|
return ups(Grip::BEZIER2).off;
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF4:
|
2015-01-19 12:37:17 +01:00
|
|
|
return ups(Grip::END).off;
|
2013-07-01 16:57:44 +02:00
|
|
|
default:
|
|
|
|
return SpannerSegment::getProperty(propertyId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setProperty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SlurSegment::setProperty(P_ID propertyId, const QVariant& v)
|
|
|
|
{
|
|
|
|
switch(propertyId) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE:
|
|
|
|
case P_ID::SLUR_DIRECTION:
|
2013-07-01 16:57:44 +02:00
|
|
|
return slurTie()->setProperty(propertyId, v);
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF1:
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::START).off = v.toPointF();
|
2014-07-31 16:30:04 +02:00
|
|
|
break;
|
|
|
|
case P_ID::SLUR_UOFF2:
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::BEZIER1).off = v.toPointF();
|
2014-07-31 16:30:04 +02:00
|
|
|
break;
|
|
|
|
case P_ID::SLUR_UOFF3:
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::BEZIER2).off = v.toPointF();
|
2014-07-31 16:30:04 +02:00
|
|
|
break;
|
|
|
|
case P_ID::SLUR_UOFF4:
|
2015-01-19 12:37:17 +01:00
|
|
|
ups(Grip::END).off = v.toPointF();
|
2014-07-31 16:30:04 +02:00
|
|
|
break;
|
2013-07-01 16:57:44 +02:00
|
|
|
default:
|
|
|
|
return SpannerSegment::setProperty(propertyId, v);
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
score()->setLayoutAll();
|
2013-07-01 16:57:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// propertyDefault
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QVariant SlurSegment::propertyDefault(P_ID id) const
|
|
|
|
{
|
|
|
|
switch (id) {
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE_TYPE:
|
|
|
|
case P_ID::SLUR_DIRECTION:
|
2013-07-01 16:57:44 +02:00
|
|
|
return slurTie()->propertyDefault(id);
|
2014-07-31 16:30:04 +02:00
|
|
|
case P_ID::SLUR_UOFF1:
|
|
|
|
case P_ID::SLUR_UOFF2:
|
|
|
|
case P_ID::SLUR_UOFF3:
|
|
|
|
case P_ID::SLUR_UOFF4:
|
|
|
|
return QPointF();
|
2013-07-01 16:57:44 +02:00
|
|
|
default:
|
|
|
|
return SpannerSegment::propertyDefault(id);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2012-11-19 10:08:15 +01:00
|
|
|
// reset
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-11-19 10:08:15 +01:00
|
|
|
void SlurSegment::reset()
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-07-31 16:30:04 +02:00
|
|
|
score()->undoChangeProperty(this, P_ID::USER_OFF, QPointF());
|
|
|
|
score()->undoChangeProperty(this, P_ID::SLUR_UOFF1, QPointF());
|
|
|
|
score()->undoChangeProperty(this, P_ID::SLUR_UOFF2, QPointF());
|
|
|
|
score()->undoChangeProperty(this, P_ID::SLUR_UOFF3, QPointF());
|
|
|
|
score()->undoChangeProperty(this, P_ID::SLUR_UOFF4, QPointF());
|
|
|
|
|
2012-11-19 10:08:15 +01:00
|
|
|
parent()->reset();
|
2012-05-26 14:26:10 +02:00
|
|
|
parent()->layout();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Slur
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Slur::Slur(Score* s)
|
|
|
|
: SlurTie(s)
|
|
|
|
{
|
2014-07-05 12:05:27 +02:00
|
|
|
setAnchor(Anchor::CHORD);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Slur
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Slur::~Slur()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// write
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::write(Xml& xml) const
|
|
|
|
{
|
2014-08-15 17:20:20 +02:00
|
|
|
if (!xml.canWrite(this))
|
|
|
|
return;
|
2014-07-21 13:24:21 +02:00
|
|
|
xml.stag(QString("Slur id=\"%1\"").arg(xml.spannerId(this)));
|
2012-05-26 14:26:10 +02:00
|
|
|
SlurTie::writeProperties(xml);
|
|
|
|
xml.etag();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// read
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-01-11 18:10:18 +01:00
|
|
|
void Slur::read(XmlReader& e)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-10-01 09:31:04 +02:00
|
|
|
setTrack(e.track()); // set staff
|
2014-07-21 13:24:21 +02:00
|
|
|
e.addSpanner(e.intAttribute("id"), this);
|
2013-01-11 18:10:18 +01:00
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
2013-09-27 11:18:30 +02:00
|
|
|
if (tag == "track2")
|
|
|
|
setTrack2(e.readInt());
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "startTrack") // obsolete
|
2013-01-11 18:10:18 +01:00
|
|
|
setTrack(e.readInt());
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (tag == "endTrack") // obsolete
|
2013-06-10 11:03:34 +02:00
|
|
|
e.readInt();
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (!SlurTie::readProperties(e))
|
2013-01-11 18:10:18 +01:00
|
|
|
e.unknown();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-10-13 11:36:08 +02:00
|
|
|
if (track2() == -1)
|
2013-10-01 09:31:04 +02:00
|
|
|
setTrack2(track());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// chordsHaveTie
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool chordsHaveTie(Chord* c1, Chord* c2)
|
|
|
|
{
|
2013-01-03 08:42:40 +01:00
|
|
|
int n1 = c1->notes().size();
|
|
|
|
for (int i1 = 0; i1 < n1; ++i1) {
|
|
|
|
Note* n1 = c1->notes().at(i1);
|
|
|
|
int n2 = c2->notes().size();
|
|
|
|
for (int i2 = 0; i2 < n2; ++i2) {
|
|
|
|
Note* n2 = c2->notes().at(i2);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (n1->tieFor() && n1->tieFor() == n2->tieBack())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// directionMixture
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool isDirectionMixture(Chord* c1, Chord* c2)
|
|
|
|
{
|
|
|
|
bool up = c1->up();
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* seg = c1->segment(); seg; seg = seg->next(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Chord* c = static_cast<Chord*>(seg->element(c1->track()));
|
2014-06-24 18:36:02 +02:00
|
|
|
if (!c || c->type() != Element::Type::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
if (c->up() != up)
|
|
|
|
return true;
|
|
|
|
if (seg == c2->segment())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layout
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::layout()
|
|
|
|
{
|
2013-09-27 20:37:40 +02:00
|
|
|
if (track2() == -1)
|
2013-10-03 08:05:32 +02:00
|
|
|
setTrack2(track());
|
2013-09-27 20:37:40 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal _spatium = spatium();
|
|
|
|
|
2013-06-10 11:03:34 +02:00
|
|
|
if (score() == gscore || tick() == -1) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// when used in a palette, slur has no parent and
|
|
|
|
// tick and tick2 has no meaning so no layout is
|
|
|
|
// possible and needed
|
|
|
|
//
|
|
|
|
SlurSegment* s;
|
2016-02-06 22:03:43 +01:00
|
|
|
if (spannerSegments().empty()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
s = new SlurSegment(score());
|
|
|
|
s->setTrack(track());
|
|
|
|
add(s);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s = frontSegment();
|
|
|
|
}
|
2014-05-26 20:38:22 +02:00
|
|
|
s->setSpannerSegmentType(SpannerSegmentType::SINGLE);
|
2015-05-05 18:28:39 +02:00
|
|
|
s->layoutSegment(QPointF(0, 0), QPointF(_spatium * 6, 0));
|
2012-05-26 14:26:10 +02:00
|
|
|
setbbox(frontSegment()->bbox());
|
|
|
|
return;
|
|
|
|
}
|
2013-06-20 18:48:28 +02:00
|
|
|
|
2015-05-12 18:22:48 +02:00
|
|
|
if (startCR() == 0 || startCR()->measure() == 0) {
|
2014-07-21 13:24:21 +02:00
|
|
|
qDebug("Slur::layout(): track %d-%d %p - %p tick %d-%d null start anchor",
|
|
|
|
track(), track2(), startCR(), endCR(), tick(), tick2());
|
2013-06-10 21:13:04 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-08-12 20:10:46 +02:00
|
|
|
if (endCR() == 0) { // sanity check
|
2014-07-05 12:05:27 +02:00
|
|
|
setEndElement(startCR());
|
|
|
|
setTick2(tick());
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
switch (_slurDirection) {
|
2016-03-02 13:20:19 +01:00
|
|
|
case Direction::UP:
|
2012-05-26 14:26:10 +02:00
|
|
|
_up = true;
|
|
|
|
break;
|
2016-03-02 13:20:19 +01:00
|
|
|
case Direction::DOWN:
|
2012-05-26 14:26:10 +02:00
|
|
|
_up = false;
|
|
|
|
break;
|
2016-03-02 13:20:19 +01:00
|
|
|
case Direction::AUTO:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
//
|
|
|
|
// assumption:
|
|
|
|
// slurs have only chords or rests as start/end elements
|
|
|
|
//
|
2013-06-16 23:33:37 +02:00
|
|
|
if (startCR() == 0 || endCR() == 0) {
|
2012-05-26 14:26:10 +02:00
|
|
|
_up = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-05 12:05:27 +02:00
|
|
|
Measure* m1 = startCR()->measure();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
Chord* c1 = (startCR()->type() == Element::Type::CHORD) ? static_cast<Chord*>(startCR()) : 0;
|
|
|
|
Chord* c2 = (endCR()->type() == Element::Type::CHORD) ? static_cast<Chord*>(endCR()) : 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
_up = !(startCR()->up());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
if ((endCR()->tick() - startCR()->tick()) > m1->ticks()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// long slurs are always above
|
|
|
|
_up = true;
|
|
|
|
}
|
|
|
|
else
|
2013-06-16 23:33:37 +02:00
|
|
|
_up = !(startCR()->up());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-27 10:35:28 +02:00
|
|
|
if (c1 && c2 && isDirectionMixture(c1, c2) && (c1->noteType() == NoteType::NORMAL)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// slurs go above if start and end note have different stem directions,
|
|
|
|
// but grace notes are exceptions
|
|
|
|
_up = true;
|
|
|
|
}
|
2014-05-27 10:35:28 +02:00
|
|
|
else if (m1->mstaff(startCR()->staffIdx())->hasVoices && c1 && c1->noteType() == NoteType::NORMAL) {
|
2012-05-26 14:26:10 +02:00
|
|
|
// in polyphonic passage, slurs go on the stem side
|
2013-06-16 23:33:37 +02:00
|
|
|
_up = startCR()->up();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else if (c1 && c2 && chordsHaveTie(c1, c2)) {
|
|
|
|
// could confuse slur with tie, put slur on stem side
|
2013-06-16 23:33:37 +02:00
|
|
|
_up = startCR()->up();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
SlurPos sPos;
|
|
|
|
slurPos(&sPos);
|
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
const QList<System*>& sl = score()->systems();
|
|
|
|
ciSystem is = sl.begin();
|
|
|
|
while (is != sl.end()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (*is == sPos.system1)
|
|
|
|
break;
|
|
|
|
++is;
|
|
|
|
}
|
2016-02-04 11:27:47 +01:00
|
|
|
if (is == sl.end())
|
2012-05-26 14:26:10 +02:00
|
|
|
qDebug("Slur::layout first system not found");
|
|
|
|
setPos(0, 0);
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// count number of segments, if no change, all
|
|
|
|
// user offsets (drags) are retained
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
unsigned nsegs = 1;
|
2016-02-04 11:27:47 +01:00
|
|
|
for (ciSystem iis = is; iis != sl.end(); ++iis) {
|
2016-03-02 13:20:19 +01:00
|
|
|
if ((*iis)->vbox())
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
if (*iis == sPos.system2)
|
|
|
|
break;
|
|
|
|
++nsegs;
|
|
|
|
}
|
|
|
|
|
|
|
|
fixupSegments(nsegs);
|
|
|
|
|
2016-02-04 11:27:47 +01:00
|
|
|
for (int i = 0; is != sl.end(); ++i, ++is) {
|
2012-05-26 14:26:10 +02:00
|
|
|
System* system = *is;
|
2016-03-02 13:20:19 +01:00
|
|
|
if (system->vbox()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
--i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SlurSegment* segment = segmentAt(i);
|
|
|
|
segment->setSystem(system);
|
|
|
|
|
|
|
|
// case 1: one segment
|
|
|
|
if (sPos.system1 == sPos.system2) {
|
2014-05-26 20:38:22 +02:00
|
|
|
segment->setSpannerSegmentType(SpannerSegmentType::SINGLE);
|
2015-05-05 18:28:39 +02:00
|
|
|
segment->layoutSegment(sPos.p1, sPos.p2);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
// case 2: start segment
|
|
|
|
else if (i == 0) {
|
2014-05-26 20:38:22 +02:00
|
|
|
segment->setSpannerSegmentType(SpannerSegmentType::BEGIN);
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal x = system->bbox().width();
|
2015-05-05 18:28:39 +02:00
|
|
|
segment->layoutSegment(sPos.p1, QPointF(x, sPos.p1.y()));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
// case 3: middle segment
|
|
|
|
else if (i != 0 && system != sPos.system2) {
|
2014-05-26 20:38:22 +02:00
|
|
|
segment->setSpannerSegmentType(SpannerSegmentType::MIDDLE);
|
2014-10-31 23:41:31 +01:00
|
|
|
qreal x1 = firstNoteRestSegmentX(system);
|
2012-05-26 14:26:10 +02:00
|
|
|
qreal x2 = system->bbox().width();
|
2013-06-10 11:03:34 +02:00
|
|
|
qreal y = system->staff(staffIdx())->y();
|
2015-05-05 18:28:39 +02:00
|
|
|
segment->layoutSegment(QPointF(x1, y), QPointF(x2, y));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
// case 4: end segment
|
|
|
|
else {
|
2014-05-26 20:38:22 +02:00
|
|
|
segment->setSpannerSegmentType(SpannerSegmentType::END);
|
2014-10-31 23:41:31 +01:00
|
|
|
qreal x = firstNoteRestSegmentX(system);
|
2015-05-05 18:28:39 +02:00
|
|
|
segment->layoutSegment(QPointF(x, sPos.p2.y()), sPos.p2);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
if (system == sPos.system2)
|
|
|
|
break;
|
|
|
|
}
|
2016-02-06 22:03:43 +01:00
|
|
|
setbbox(spannerSegments().empty() ? QRectF() : frontSegment()->bbox());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// firstNoteRestSegmentX
|
|
|
|
// in System() coordinates
|
2014-10-31 23:41:31 +01:00
|
|
|
// returns the position just after the last non-chordrest segment
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
qreal SlurTie::firstNoteRestSegmentX(System* system)
|
|
|
|
{
|
|
|
|
foreach(const MeasureBase* mb, system->measures()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (mb->type() == Element::Type::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
const Measure* measure = static_cast<const Measure*>(mb);
|
|
|
|
for (const Segment* seg = measure->first(); seg; seg = seg->next()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
if (seg->segmentType() == Segment::Type::ChordRest) {
|
2014-10-31 23:41:31 +01:00
|
|
|
// first CR found; back up to previous segment
|
|
|
|
seg = seg->prev();
|
|
|
|
if (seg) {
|
|
|
|
// find maximum width
|
|
|
|
qreal width = 0.0;
|
|
|
|
int n = score()->nstaves();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
if (!system->staff(i)->show())
|
|
|
|
continue;
|
|
|
|
Element* e = seg->element(i * VOICES);
|
|
|
|
if (e)
|
|
|
|
width = qMax(width, e->width());
|
|
|
|
}
|
|
|
|
return seg->measure()->pos().x() + seg->pos().x() + width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0.0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qDebug("firstNoteRestSegmentX: did not find segment");
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTrack
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Slur::setTrack(int n)
|
|
|
|
{
|
|
|
|
Element::setTrack(n);
|
|
|
|
foreach(SpannerSegment* ss, spannerSegments())
|
|
|
|
ss->setTrack(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// fixupSegments
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurTie::fixupSegments(unsigned nsegs)
|
|
|
|
{
|
|
|
|
unsigned onsegs = spannerSegments().size();
|
|
|
|
if (nsegs > onsegs) {
|
|
|
|
for (unsigned i = onsegs; i < nsegs; ++i) {
|
|
|
|
SlurSegment* s;
|
2016-02-06 22:03:43 +01:00
|
|
|
if (!delSegments.empty()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
s = delSegments.dequeue();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s = new SlurSegment(score());
|
|
|
|
}
|
|
|
|
s->setTrack(track());
|
|
|
|
add(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (nsegs < onsegs) {
|
|
|
|
for (unsigned i = nsegs; i < onsegs; ++i) {
|
|
|
|
SlurSegment* s = takeLastSegment();
|
2013-06-10 11:03:34 +02:00
|
|
|
s->setSystem(0);
|
2012-05-26 14:26:10 +02:00
|
|
|
delSegments.enqueue(s); // cannot delete: used in SlurSegment->edit()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-31 16:30:04 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// startEdit
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurTie::startEdit(MuseScoreView* view, const QPointF& pt)
|
|
|
|
{
|
|
|
|
Spanner::startEdit(view, pt);
|
|
|
|
|
|
|
|
editStartElement = startElement();
|
|
|
|
editEndElement = endElement();
|
|
|
|
|
|
|
|
editUps.clear();
|
|
|
|
foreach (SpannerSegment* s, spannerSegments()) {
|
|
|
|
SlurOffsets o;
|
|
|
|
SlurSegment* ss = static_cast<SlurSegment*>(s);
|
|
|
|
o.o[0] = ss->getProperty(P_ID::SLUR_UOFF1).toPointF();
|
|
|
|
o.o[1] = ss->getProperty(P_ID::SLUR_UOFF2).toPointF();
|
|
|
|
o.o[2] = ss->getProperty(P_ID::SLUR_UOFF3).toPointF();
|
|
|
|
o.o[3] = ss->getProperty(P_ID::SLUR_UOFF4).toPointF();
|
|
|
|
editUps.append(o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// endEdit
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SlurTie::endEdit()
|
|
|
|
{
|
|
|
|
Spanner::endEdit();
|
|
|
|
if (type() == Element::Type::SLUR) {
|
|
|
|
if ((editStartElement != startElement()) || (editEndElement != endElement())) {
|
|
|
|
//
|
|
|
|
// handle parts:
|
|
|
|
// search new start/end elements
|
|
|
|
//
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* e : linkList()) {
|
2014-07-31 16:30:04 +02:00
|
|
|
Spanner* spanner = static_cast<Spanner*>(e);
|
|
|
|
if (spanner == this)
|
2016-03-10 10:41:31 +01:00
|
|
|
score()->undoStack()->push1(new ChangeStartEndSpanner(this, editStartElement, editEndElement));
|
2014-07-31 16:30:04 +02:00
|
|
|
else {
|
|
|
|
Element* se = 0;
|
|
|
|
Element* ee = 0;
|
|
|
|
if (startElement()) {
|
2015-02-12 11:41:39 +01:00
|
|
|
QList<ScoreElement*> sel = startElement()->linkList();
|
|
|
|
for (ScoreElement* eee : sel) {
|
|
|
|
Element* e = static_cast<Element*>(eee);
|
2014-07-31 16:30:04 +02:00
|
|
|
if (e->score() == spanner->score() && e->track() == spanner->track()) {
|
|
|
|
se = e;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (endElement()) {
|
2015-02-12 11:41:39 +01:00
|
|
|
QList<ScoreElement*> sel = endElement()->linkList();
|
|
|
|
for (ScoreElement* eee : sel) {
|
|
|
|
Element* e = static_cast<Element*>(eee);
|
2014-07-31 16:30:04 +02:00
|
|
|
if (e->score() == spanner->score() && e->track() == spanner->track2()) {
|
|
|
|
ee = e;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
score()->undo(new ChangeStartEndSpanner(spanner, se, ee));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (spannerSegments().size() != editUps.size()) {
|
|
|
|
qDebug("SlurTie::endEdit(): segment size changed %d != %d", spannerSegments().size(), editUps.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < editUps.size(); ++i) {
|
|
|
|
SpannerSegment* ss = segments[i];
|
|
|
|
SlurOffsets o = editUps[i];
|
|
|
|
score()->undoPropertyChanged(ss, P_ID::SLUR_UOFF1, o.o[0]);
|
|
|
|
score()->undoPropertyChanged(ss, P_ID::SLUR_UOFF2, o.o[1]);
|
|
|
|
score()->undoPropertyChanged(ss, P_ID::SLUR_UOFF3, o.o[2]);
|
|
|
|
score()->undoPropertyChanged(ss, P_ID::SLUR_UOFF4, o.o[3]);
|
|
|
|
}
|
2016-03-02 13:20:19 +01:00
|
|
|
score()->setLayoutAll();
|
2014-07-31 16:30:04 +02:00
|
|
|
}
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|