MuseScore/libmscore/chord.cpp

2410 lines
88 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2013 Werner Schweer
2012-05-26 14:26:10 +02:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
/**
\file
Implementation of classes Chord
2012-05-26 14:26:10 +02:00
*/
#include "chord.h"
#include "note.h"
#include "xml.h"
#include "style.h"
#include "segment.h"
#include "text.h"
#include "measure.h"
#include "system.h"
#include "tuplet.h"
#include "hook.h"
2013-08-22 12:18:14 +02:00
#include "tie.h"
2012-05-26 14:26:10 +02:00
#include "arpeggio.h"
#include "score.h"
#include "tremolo.h"
#include "glissando.h"
#include "staff.h"
2013-06-10 21:13:04 +02:00
#include "part.h"
2012-05-26 14:26:10 +02:00
#include "utils.h"
#include "articulation.h"
#include "undo.h"
#include "chordline.h"
#include "lyrics.h"
#include "navigate.h"
#include "stafftype.h"
#include "stem.h"
#include "mscore.h"
#include "accidental.h"
#include "noteevent.h"
#include "pitchspelling.h"
#include "stemslash.h"
#include "ledgerline.h"
2013-06-10 21:13:04 +02:00
#include "drumset.h"
#include "key.h"
2013-11-11 16:53:03 +01:00
#include "sym.h"
2013-05-13 18:49:17 +02:00
namespace Ms {
//---------------------------------------------------------
// upNote / downNote
//---------------------------------------------------------
Note* Chord::upNote() const
{
Note* result = _notes.back();
if (staff() && staff()->isDrumStaff()) {
foreach(Note*n, _notes) {
if (n->line() < result->line()) {
result = n;
}
}
}
return result;
}
Note* Chord::downNote() const
{
Note* result = _notes.front();
if (staff() && staff()->isDrumStaff()) {
foreach(Note*n, _notes) {
if (n->line() > result->line()) {
result = n;
}
}
}
return result;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// upLine / downLine
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
int Chord::upLine() const
{
return (staff() && staff()->isTabStaff()) ? upString()*2 : upNote()->line();
2012-05-26 14:26:10 +02:00
}
int Chord::downLine() const
{
return (staff() && staff()->isTabStaff()) ? downString()*2 : downNote()->line();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// upString / downString
//
// return the topmost / bottommost string used by chord
// Top and bottom refer to the DRAWN position, not the position in the instrument
// (i.e., upside-down TAB are taken into account)
//
// If no staff, always return 0
// If staf is not a TAB, always returns TOP and BOTTOM staff lines
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
int Chord::upString() const
2012-05-26 14:26:10 +02:00
{
// if no staff or staff not a TAB, return 0 (=topmost line)
if(!staff() || !staff()->isTabStaff())
return 0;
StaffTypeTablature* tab = (StaffTypeTablature*) staff()->staffType();
int line = tab->lines() - 1; // start at bottom line
int noteLine;
// scan each note: if TAB strings are not in sequential order,
// visual order of notes might not correspond to pitch order
int n = _notes.size();
for (int i = 0; i < n; ++i) {
noteLine = tab->physStringToVisual(_notes.at(i)->string());
if (noteLine < line)
line = noteLine;
}
return line;
}
int Chord::downString() const
{
if(!staff()) // if no staff, return 0
return 0;
if(!staff()->isTabStaff()) // if staff not a TAB, return bottom line
return staff()->lines()-1;
StaffTypeTablature* tab = (StaffTypeTablature*) staff()->staffType();
int line = 0; // start at top line
int noteLine;
int n = _notes.size();
for (int i = 0; i < n; ++i) {
noteLine = tab->physStringToVisual(_notes.at(i)->string());
if(noteLine > line)
line = noteLine;
}
return line;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// Chord
//---------------------------------------------------------
Chord::Chord(Score* s)
: ChordRest(s)
{
_ledgerLines = 0;
2012-11-19 10:08:15 +01:00
_stem = 0;
_hook = 0;
_stemDirection = MScore::AUTO;
_arpeggio = 0;
_tremolo = 0;
2012-05-26 14:26:10 +02:00
_tremoloChordType = TremoloSingle;
2012-11-19 10:08:15 +01:00
_glissando = 0;
_noteType = NOTE_NORMAL;
_stemSlash = 0;
_noStem = false;
_userPlayEvents = false;
2013-05-12 12:51:42 +02:00
_crossMeasure = CROSSMEASURE_UNKNOWN;
2013-06-16 23:33:37 +02:00
_graceIndex = 0;
2012-05-26 14:26:10 +02:00
setFlags(ELEMENT_MOVABLE | ELEMENT_ON_STAFF);
}
Chord::Chord(const Chord& c)
: ChordRest(c)
{
_ledgerLines = 0;
int n = c._notes.size();
for (int i = 0; i < n; ++i)
add(new Note(*c._notes.at(i)));
2012-05-26 14:26:10 +02:00
for (Chord* gn : c.graceNotes()) {
add(new Chord(*gn));
}
2012-05-26 14:26:10 +02:00
_stem = 0;
_hook = 0;
_glissando = 0;
_arpeggio = 0;
_stemSlash = 0;
2013-06-16 23:33:37 +02:00
_graceIndex = c._graceIndex;
2012-11-19 10:08:15 +01:00
_noStem = c._noStem;
_userPlayEvents = c._userPlayEvents;
2012-05-26 14:26:10 +02:00
if (c._stem)
add(new Stem(*(c._stem)));
if (c._hook)
add(new Hook(*(c._hook)));
if (c._glissando)
add(new Glissando(*(c._glissando)));
if (c._arpeggio)
add(new Arpeggio(*(c._arpeggio)));
if (c._stemSlash)
add(new StemSlash(*(c._stemSlash)));
_stemDirection = c._stemDirection;
_tremoloChordType = TremoloSingle;
_tremolo = 0;
_noteType = c._noteType;
2013-05-12 12:51:42 +02:00
_crossMeasure = CROSSMEASURE_UNKNOWN;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// linkedClone
//---------------------------------------------------------
Chord* Chord::linkedClone()
{
Chord* chord = new Chord(*this);
linkTo(chord);
int n = notes().size();
for (int i = 0; i < n; ++i)
_notes[i]->linkTo(chord->_notes[i]);
n = _graceNotes.size();
for (int i = 0; i < n; ++i)
_graceNotes[i]->linkTo(chord->_graceNotes[i]);
2012-05-26 14:26:10 +02:00
return chord;
}
//---------------------------------------------------------
// ~Chord
//---------------------------------------------------------
Chord::~Chord()
{
delete _arpeggio;
if (_tremolo && _tremolo->chord1() == this) {
if (_tremolo->chord2())
_tremolo->chord2()->setTremolo(0);
2012-05-26 14:26:10 +02:00
delete _tremolo;
}
2012-05-26 14:26:10 +02:00
delete _glissando;
delete _stemSlash;
delete _stem;
delete _hook;
for (LedgerLine* ll = _ledgerLines; ll;) {
LedgerLine* llNext = ll->next();
delete ll;
ll = llNext;
}
qDeleteAll(_notes);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setStem
//---------------------------------------------------------
void Chord::setStem(Stem* s)
{
delete _stem;
_stem = s;
if (_stem) {
_stem->setParent(this);
_stem->setTrack(track());
}
}
2013-05-23 12:58:06 +02:00
//---------------------------------------------------------
// stemPosX
2013-08-01 13:16:07 +02:00
// return Chord coordinates
2013-05-23 12:58:06 +02:00
//---------------------------------------------------------
qreal Chord::stemPosX() const
{
2013-08-01 13:16:07 +02:00
if (staff() && staff()->isTabStaff())
return static_cast<StaffTypeTablature*>(staff()->staffType())->chordStemPosX(this) * spatium();
2013-05-23 16:58:22 +02:00
if (_up) {
qreal nhw = score()->noteHeadWidth();
if (_noteType != NOTE_NORMAL)
2013-06-20 10:19:04 +02:00
nhw *= score()->styleD(ST_graceNoteMag);
2013-07-13 11:17:47 +02:00
nhw *= mag();
2013-08-01 13:16:07 +02:00
return nhw;
2013-05-23 16:58:22 +02:00
}
2013-08-01 13:16:07 +02:00
return 0.0;
2013-05-23 12:58:06 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// stemPos
// return page coordinates
//---------------------------------------------------------
QPointF Chord::stemPos() const
{
2013-05-23 12:32:40 +02:00
qreal _spatium = spatium();
2013-08-01 13:16:07 +02:00
QPointF p(pagePos());
2013-01-02 09:29:17 +01:00
if (staff() && staff()->isTabStaff())
2013-08-01 13:16:07 +02:00
return (static_cast<StaffTypeTablature*>(staff()->staffType())->chordStemPos(this) * _spatium) + p;
2013-05-23 12:32:40 +02:00
if (_up) {
2013-05-23 16:58:22 +02:00
qreal nhw = score()->noteHeadWidth();
if (_noteType != NOTE_NORMAL)
nhw *= score()->styleD(ST_graceNoteMag);
2013-07-13 18:15:30 +02:00
nhw *= mag();
2013-05-23 16:58:22 +02:00
p.rx() += nhw;
2013-05-23 12:58:06 +02:00
p.ry() += downNote()->pos().y();
2013-05-23 12:32:40 +02:00
}
else
2013-05-23 12:58:06 +02:00
p.ry() += upNote()->pos().y();
2013-05-23 12:32:40 +02:00
return p;
2013-01-02 14:33:23 +01:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// stemPosBeam
// return stem position of note on beam side
// return canvas coordinates
//---------------------------------------------------------
QPointF Chord::stemPosBeam() const
{
2013-05-23 12:32:40 +02:00
qreal _spatium = spatium();
2013-01-02 14:33:23 +01:00
if (staff() && staff()->isTabStaff())
2013-05-23 12:32:40 +02:00
return (static_cast<StaffTypeTablature*>(staff()->staffType())->chordStemPosBeam(this) * _spatium) + pagePos();
QPointF p(pagePos());
if (_up) {
2013-05-23 16:58:22 +02:00
qreal nhw = score()->noteHeadWidth();
if (_noteType != NOTE_NORMAL)
2013-06-16 23:33:37 +02:00
nhw *= score()->styleD(ST_graceNoteMag);
2013-05-23 16:58:22 +02:00
p.rx() += nhw;
2013-05-23 12:58:06 +02:00
p.ry() += upNote()->pos().y();
2013-05-23 12:32:40 +02:00
}
else
2013-05-23 12:58:06 +02:00
p.ry() += downNote()->pos().y();
2013-05-23 12:32:40 +02:00
return p;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setSelected
//---------------------------------------------------------
void Chord::setSelected(bool f)
{
Element::setSelected(f);
int n = _notes.size();
for (int i = 0; i < n; ++i)
_notes.at(i)->setSelected(f);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Chord::add(Element* e)
{
e->setParent(this);
e->setTrack(track());
switch(e->type()) {
case NOTE:
{
Note* note = static_cast<Note*>(e);
bool found = false;
for (int idx = 0; idx < _notes.size(); ++idx) {
if (note->pitch() < _notes[idx]->pitch()) {
_notes.insert(idx, note);
found = true;
break;
}
}
if (!found)
_notes.append(note);
if (note->tieFor()) {
if (note->tieFor()->endNote())
note->tieFor()->endNote()->setTieBack(note->tieFor());
}
}
break;
case ARPEGGIO:
_arpeggio = static_cast<Arpeggio*>(e);
break;
case TREMOLO:
{
Tremolo* tr = static_cast<Tremolo*>(e);
if (tr->twoNotes()) {
if (!(_tremolo && _tremolo->twoNotes())) {
TDuration d = durationType();
int dots = d.dots();
d = d.shift(-1);
d.setDots(dots);
if (tr->chord1())
tr->chord1()->setDurationType(d);
if (tr->chord2())
tr->chord2()->setDurationType(d);
}
_tremoloChordType = TremoloFirstNote;
tr->chord2()->setTremolo(tr);
tr->chord2()->setTremoloChordType(TremoloSecondNote);
}
else
_tremoloChordType = TremoloSingle;
_tremolo = tr;
}
break;
case GLISSANDO:
_glissando = static_cast<Glissando*>(e);
break;
case STEM:
_stem = static_cast<Stem*>(e);
break;
case HOOK:
_hook = static_cast<Hook*>(e);
break;
case CHORDLINE:
_el.push_back(e);
break;
2013-06-16 23:33:37 +02:00
case SLUR:
{
2013-03-25 16:27:20 +01:00
_el.push_back(e);
Spanner* s = static_cast<Spanner*>(e);
foreach (SpannerSegment* ss, s->spannerSegments()) {
2013-06-16 23:33:37 +02:00
if (ss->system())
ss->system()->add(ss);
}
}
2012-05-26 14:26:10 +02:00
break;
case STEM_SLASH:
_stemSlash = static_cast<StemSlash*>(e);
break;
2013-06-12 14:23:57 +02:00
case CHORD:
2013-06-16 23:33:37 +02:00
{
Chord* gc = static_cast<Chord*>(e);
int idx = gc->graceIndex();
gc->setFlags(ELEMENT_MOVABLE);
_graceNotes.insert(_graceNotes.begin() + idx, gc);
}
2013-06-12 14:23:57 +02:00
break;
2012-05-26 14:26:10 +02:00
default:
ChordRest::add(e);
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Chord::remove(Element* e)
{
switch(e->type()) {
case NOTE:
{
Note* note = static_cast<Note*>(e);
if (_notes.removeOne(note)) {
if (note->tieFor()) {
if (note->tieFor()->endNote()) {
2012-05-26 14:26:10 +02:00
note->tieFor()->endNote()->setTieBack(0);
// update accidentals for endNote
Chord* chord = note->tieFor()->endNote()->chord();
Measure* m = chord->segment()->measure();
note->score()->updateAccidentals(m,chord->staffIdx());
}
2012-05-26 14:26:10 +02:00
}
}
else
qDebug("Chord::remove() note %p not found!\n", e);
}
break;
case ARPEGGIO:
_arpeggio = 0;
break;
case TREMOLO:
{
Tremolo* tremolo = static_cast<Tremolo*>(e);
if (tremolo->twoNotes()) {
TDuration d = durationType();
int dots = d.dots();
d = d.shift(1);
d.setDots(dots);
Fraction f = duration();
if (f.numerator() > 0)
d = TDuration(f);
2012-05-26 14:26:10 +02:00
if (tremolo->chord1())
tremolo->chord1()->setDurationType(d);
if (tremolo->chord2())
tremolo->chord2()->setDurationType(d);
tremolo->chord2()->setTremolo(0);
2013-11-14 18:20:08 +01:00
tremolo->chord2()->setTremoloChordType(TremoloSingle);
2012-05-26 14:26:10 +02:00
}
_tremolo = 0;
}
break;
case GLISSANDO:
_glissando = 0;
break;
case STEM:
_stem = 0;
break;
case HOOK:
_hook = 0;
break;
2013-06-16 23:33:37 +02:00
case SLUR:
{
_el.remove(e);
Slur* gs = static_cast<Slur*>(e);
foreach (SpannerSegment* ss, gs->spannerSegments()) {
if (ss->system())
ss->system()->remove(ss);
}
}
break;
case STEM_SLASH:
_stemSlash = 0;
break;
2012-05-26 14:26:10 +02:00
case CHORDLINE:
2013-03-25 16:27:20 +01:00
_el.remove(e);
2012-05-26 14:26:10 +02:00
break;
2013-06-12 14:23:57 +02:00
case CHORD:
2013-06-16 23:33:37 +02:00
{
auto i = std::find(_graceNotes.begin(), _graceNotes.end(), static_cast<Chord*>(e));
Chord* grace = *i;
grace->setGraceIndex(i - _graceNotes.begin());
_graceNotes.erase(i);
}
2013-06-12 14:23:57 +02:00
break;
2012-05-26 14:26:10 +02:00
default:
ChordRest::remove(e);
break;
}
}
//---------------------------------------------------------
// maxHeadWidth
//---------------------------------------------------------
2013-09-02 19:07:39 +02:00
qreal Chord::maxHeadWidth() const
{
// determine max head width in chord
qreal hw = 0;
for (int i = 0; i < _notes.size(); i++) {
qreal t = _notes.at(i)->headWidth();
if (t > hw)
hw = t;
}
return hw;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// addLedgerLine
/// Add a ledger line to a chord.
/// \arg track track the ledger line belongs to
2012-05-26 14:26:10 +02:00
/// \arg line vertical position of line
/// \arg visible whether the line is visible or not
/// \arg x start x-position
/// \arg len line length
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
/*
void Chord::addLedgerLine(int track, int line, bool visible, qreal x, Spatium len)
2012-05-26 14:26:10 +02:00
{
qreal _spatium = spatium();
2013-01-02 18:24:02 +01:00
LedgerLine* h = new LedgerLine(score());
2012-05-26 14:26:10 +02:00
h->setParent(this);
h->setTrack(track);
2012-05-26 14:26:10 +02:00
h->setVisible(visible);
//
// Experimental:
// shorten ledger line to avoid collisions with accidentals
//
int n = _notes.size();
for (int i = 0; i < n; ++i) {
const Note* n = _notes.at(i);
2012-05-26 14:26:10 +02:00
if (n->line() >= (line-1) && n->line() <= (line+1) && n->accidental()) {
x += _spatium * .25;
len -= Spatium(.25);
break;
}
}
2012-05-26 14:26:10 +02:00
h->setLen(len);
2013-01-02 18:24:02 +01:00
h->setPos(x, line * _spatium * .5);
h->setNext(_ledgerLines);
_ledgerLines = h;
2012-05-26 14:26:10 +02:00
}
*/
//---------------------------------------------------------
// createLedgerLines
/// Creates the ledger lines fro a chord
/// \arg track track the ledger line belongs to
/// \arg lines a vector of LedgerLineData describing thelines to add
/// \arg visible whether the line is visible or not
//---------------------------------------------------------
void Chord::createLedgerLines(int track, vector<LedgerLineData>& vecLines, bool visible)
{
qreal _spatium = spatium();
for (auto lld : vecLines) {
LedgerLine* h = new LedgerLine(score());
h->setParent(this);
h->setTrack(track);
h->setVisible(lld.visible && visible);
h->setLen(Spatium( (lld.maxX - lld.minX) / _spatium) );
h->setPos(lld.minX, lld.line * _spatium * .5);
h->setNext(_ledgerLines);
_ledgerLines = h;
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// addLedgerLines
//---------------------------------------------------------
void Chord::addLedgerLines(int move)
2012-05-26 14:26:10 +02:00
{
LedgerLineData lld;
qreal _spatium = spatium();
int idx = staffIdx() + move;
int track = staff2track(idx); // the track lines belong to
qreal hw = _notes[0]->headWidth();
// the line pos corresponding to the bottom line of the staff
int lineBelow = (score()->staff(idx)->lines()-1) * 2;
qreal minX, maxX; // note extrema in raster units
// qreal minXr, maxXr; // ledger line extrema in raster units
int minLine, maxLine;
bool visible = false;
qreal x;
// the extra length of a ledger line with respect to note head (half of it on each side)
qreal extraLen = score()->styleS(ST_ledgerLineLength).val() * _spatium * 0.5;
// scan chord notes, collecting visibility and x and y extrema
// NOTE: notes are sorted from bottom to top (line no. decreasing)
// notes are scanned twice from outside (bottom or top) toward the staff
// each pass stops at the first note without ledger lines
int n = _notes.size();
for (int j = 0; j < 2; j++) { // notes are scanned twice...
int from, delta;
vector<LedgerLineData> vecLines;
minX = maxX = 0;
minLine = 0;
maxLine = lineBelow;
if (j == 0) { // ...once from lowest up...
from = 0;
delta = +1;
}
else {
from = n-1; // ...once from highest down
delta = -1;
}
for (int i = from; i < n && i >=0 ; i += delta) {
const Note* note = _notes.at(i);
int l = note->line();
if ( (!j && l < lineBelow) || // if 1st pass and note not below staff
(j && l >= 0) ) // or 2nd pass and note not above staff
break; // stop this pass
// round line number to even number toward 0
if (l < 0) l = (l+1) & ~ 1;
else l = l & ~ 1;
if (note->visible()) // if one note is visible,
visible = true; // all lines between it and the staff are visible
//
// Experimental:
// shorten ledger line to avoid collisions with accidentals
//
2013-09-05 16:37:49 +02:00
// bool accid = (note->accidental() && note->line() >= (l-1) && note->line() <= (l+1) );
//
// TODO : do something with this accid flag in the following code!
//
// check if note horiz. pos. is outside current range
// if more length on the right, increase range
x = note->pos().x();
if (x-extraLen < minX) {
minX = x - extraLen;
// minXr = minX - extraLen;
// increase width of all lines between this one and the staff
for (auto& d : vecLines)
if (!d.accidental && ((l < 0 && d.line >= l) || (l > 0 && d.line <= l)) )
d.minX = minX ;
}
// same for left side
if (x+hw+extraLen > maxX) {
maxX = x + hw + extraLen;
// maxXr = maxX + extraLen;
for (auto& d : vecLines)
if ( (l < 0 && d.line >= l) || (l > 0 && d.line <= l) )
d.maxX = maxX;
}
// check if note vert. pos. is outside current range
// and, in case, add data for new line(s)
if (l < minLine) {
for (int i = l; i < minLine; i += 2) {
lld.line = i;
lld.minX = minX;
lld.maxX = maxX;
lld.visible = visible;
lld.accidental = false;
vecLines.push_back(lld);
}
minLine = l;
}
if (l > maxLine) {
for (int i = maxLine+2; i <= l; i += 2) {
lld.line = i;
lld.minX = minX;
lld.maxX = maxX;
lld.visible = visible;
lld.accidental = false;
vecLines.push_back(lld);
}
maxLine = l;
}
}
if (minLine < 0 || maxLine > lineBelow)
createLedgerLines(track, vecLines, !staff()->invisible());
2012-05-26 14:26:10 +02:00
}
return; // no ledger lines for this chord
2012-05-26 14:26:10 +02:00
}
//-----------------------------------------------------------------------------
// computeUp
// rules:
// single note:
// All notes beneath the middle line: upward stems
// All notes on or above the middle line: downward stems
// two notes:
// If the interval above the middle line is greater than the interval
// below the middle line: downward stems
// If the interval below the middle line is greater than the interval
// above the middle line: upward stems
// If the two notes are the same distance from the middle line:
// stem can go in either direction. but most engravers prefer
// downward stems
// > two notes:
// If the interval of the highest note above the middle line is greater
// than the interval of the lowest note below the middle line:
// downward stems
// If the interval of the lowest note below the middle line is greater
// than the interval of the highest note above the middle line:
// upward stem
// If the highest and the lowest notes are the same distance from
// the middle line:, use these rules to determine stem direction:
// - If the majority of the notes are above the middle:
// downward stems
// - If the majority of the notes are below the middle:
// upward stems
// TABlatures:
// stems beside staves:
// All stems are up / down according to TAB::stemsDown() setting
// stems through staves:
// Same rules as per pitched staves
2012-05-26 14:26:10 +02:00
//-----------------------------------------------------------------------------
void Chord::computeUp()
{
StaffTypeTablature* tab = 0;
// TAB STAVES
if (staff() && staff()->isTabStaff()) {
tab = (StaffTypeTablature*)staff()->staffType();
// if no stems or stem beside staves
if (tab->slashStyle() || !tab->stemThrough()) {
// if measure has voices, set stem direction according to voice
2013-08-09 11:42:24 +02:00
if (measure()->mstaff(staffIdx())->hasVoices)
_up = !(track() % 2);
else // if only voice 1,
_up = !tab->stemsDown();// unconditionally set _up according to TAB stem direction
return; // (if no stems, _up does not really matter!)
}
// if TAB has stems through staves, chain into standard processing
2012-05-26 14:26:10 +02:00
}
// PITCHED STAVES (or TAB with stems through staves)
2012-08-04 15:46:43 +02:00
if (_stemDirection != MScore::AUTO) {
_up = _stemDirection == MScore::UP;
2012-05-26 14:26:10 +02:00
}
else if (_noteType != NOTE_NORMAL) {
//
// stem direction for grace notes
//
2013-08-09 11:42:24 +02:00
if (measure()->mstaff(staffIdx())->hasVoices)
_up = !(track() % 2);
2012-05-26 14:26:10 +02:00
else
_up = true;
}
2013-01-29 15:21:01 +01:00
else if (staffMove()) {
_up = staffMove() > 0;
}
2012-05-26 14:26:10 +02:00
else if (measure()->mstaff(staffIdx())->hasVoices) {
2013-08-09 11:42:24 +02:00
_up = !(track() % 2);
2012-05-26 14:26:10 +02:00
}
else {
int dnMaxLine = staff()->staffType()->lines() - 1;
int ud = (tab ? upString()*2 : upNote()->line() ) - dnMaxLine;
// standard case: if only 1 note or cross beaming
if (_notes.size() == 1 || staffMove()) {
if (staffMove() > 0)
_up = true;
else if (staffMove() < 0)
_up = false;
else
_up = ud > 0;
}
// if more than 1 note, compare extrema (topmost and bottommost notes)
else {
int dd = (tab ? downString()*2 : downNote()->line() ) - dnMaxLine;
// if extrema symmetrical, average directions of intermediate notes
if (-ud == dd) {
int up = 0;
int n = _notes.size();
for (int i = 0; i < n; ++i) {
const Note* n = _notes.at(i);
int l = tab ? n->string()*2 : n->line();
if (l <= dnMaxLine)
--up;
else
++up;
}
_up = up > 0;
2012-05-26 14:26:10 +02:00
}
// if extrema not symmetrical, set _up to prevailing
else
_up = dd > -ud;
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// selectedNote
//---------------------------------------------------------
Note* Chord::selectedNote() const
{
Note* note = 0;
int n = _notes.size();
for (int i = 0; i < n; ++i) {
Note* n = _notes.at(i);
2012-05-26 14:26:10 +02:00
if (n->selected()) {
if (note)
return 0;
note = n;
}
}
return note;
}
//---------------------------------------------------------
// Chord::write
//---------------------------------------------------------
void Chord::write(Xml& xml) const
{
2013-06-19 16:25:29 +02:00
for (Chord* c : _graceNotes) {
c->writeBeam(xml);
2013-06-12 14:23:57 +02:00
c->write(xml);
2013-06-19 16:25:29 +02:00
}
2013-06-16 23:33:37 +02:00
for (Element* e : _el) {
if (e->type() == Element::SLUR) {
static_cast<Slur*>(e)->setId(++xml.spannerId);
e->write(xml);
}
}
2013-10-14 15:34:46 +02:00
2012-05-26 14:26:10 +02:00
xml.stag("Chord");
ChordRest::writeProperties(xml);
2013-06-19 16:25:29 +02:00
switch (_noteType) {
case NOTE_INVALID:
case NOTE_NORMAL:
break;
case NOTE_ACCIACCATURA:
xml.tagE("acciaccatura");
break;
case NOTE_APPOGGIATURA:
xml.tagE("appoggiatura");
break;
case NOTE_GRACE4:
xml.tagE("grace4");
break;
case NOTE_GRACE16:
xml.tagE("grace16");
break;
case NOTE_GRACE32:
xml.tagE("grace32");
break;
2012-05-26 14:26:10 +02:00
}
2013-06-19 16:25:29 +02:00
2012-05-26 14:26:10 +02:00
if (_noStem)
xml.tag("noStem", _noStem);
else if (_stem && (!_stem->userOff().isNull() || (_stem->userLen() != 0.0) || !_stem->visible() || (_stem->color() != MScore::defaultColor)))
_stem->write(xml);
if (_hook && (!_hook->visible() || !_hook->userOff().isNull() || (_hook->color() != MScore::defaultColor)))
_hook->write(xml);
switch(_stemDirection) {
2012-08-04 15:46:43 +02:00
case MScore::UP: xml.tag("StemDirection", QVariant("up")); break;
case MScore::DOWN: xml.tag("StemDirection", QVariant("down")); break;
case MScore::AUTO: break;
2012-05-26 14:26:10 +02:00
}
2013-06-19 16:25:29 +02:00
for (Note* n : _notes)
n->write(xml);
2012-05-26 14:26:10 +02:00
if (_arpeggio)
_arpeggio->write(xml);
if (_glissando)
_glissando->write(xml);
if (_tremolo)
_tremolo->write(xml);
2013-06-16 23:33:37 +02:00
for (Element* e : _el) {
if (e->type() == Element::SLUR) {
Slur* gs = static_cast<Slur*>(e);
xml.tagE(QString("Slur type=\"start\" number=\"%1\"").arg(gs->id()));
}
else
e->write(xml);
}
for (Chord* c : _graceNotes) {
for (Element* e : c->el()) {
if (e->type() == Element::SLUR) {
Slur* gs = static_cast<Slur*>(e);
xml.tagE(QString("Slur type=\"stop\" number=\"%1\"").arg(gs->id()));
}
}
}
2012-05-26 14:26:10 +02:00
xml.etag();
}
//---------------------------------------------------------
// Chord::read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Chord::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 == "Note") {
Note* note = new Note(score());
// the note needs to know the properties of the track it belongs to
note->setTrack(track());
note->setChord(this);
note->read(e);
add(note);
}
2013-05-29 10:31:26 +02:00
else if (ChordRest::readProperties(e))
;
else if (tag == "Stem") {
_stem = new Stem(score());
_stem->read(e);
add(_stem);
}
else if (tag == "Hook") {
_hook = new Hook(score());
_hook->read(e);
add(_hook);
}
2013-01-11 18:10:18 +01:00
else if (tag == "appoggiatura") {
2012-05-26 14:26:10 +02:00
_noteType = NOTE_APPOGGIATURA;
2013-01-11 18:10:18 +01:00
e.readNext();
}
else if (tag == "acciaccatura") {
2012-05-26 14:26:10 +02:00
_noteType = NOTE_ACCIACCATURA;
2013-01-11 18:10:18 +01:00
e.readNext();
}
else if (tag == "grace4") {
2012-05-26 14:26:10 +02:00
_noteType = NOTE_GRACE4;
2013-01-11 18:10:18 +01:00
e.readNext();
}
else if (tag == "grace16") {
2012-05-26 14:26:10 +02:00
_noteType = NOTE_GRACE16;
2013-01-11 18:10:18 +01:00
e.readNext();
}
else if (tag == "grace32") {
2012-05-26 14:26:10 +02:00
_noteType = NOTE_GRACE32;
2013-01-11 18:10:18 +01:00
e.readNext();
}
2012-05-26 14:26:10 +02:00
else if (tag == "StemDirection") {
2013-01-11 18:10:18 +01:00
QString val(e.readElementText());
2012-05-26 14:26:10 +02:00
if (val == "up")
2012-08-04 15:46:43 +02:00
_stemDirection = MScore::UP;
2012-05-26 14:26:10 +02:00
else if (val == "down")
2012-08-04 15:46:43 +02:00
_stemDirection = MScore::DOWN;
2012-05-26 14:26:10 +02:00
else
2012-08-04 15:46:43 +02:00
_stemDirection = MScore::Direction(val.toInt());
2012-05-26 14:26:10 +02:00
}
else if (tag == "noStem")
2013-01-11 18:10:18 +01:00
_noStem = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "Arpeggio") {
_arpeggio = new Arpeggio(score());
_arpeggio->setTrack(track());
_arpeggio->read(e);
_arpeggio->setParent(this);
}
else if (tag == "Glissando") {
_glissando = new Glissando(score());
_glissando->setTrack(track());
_glissando->read(e);
_glissando->setParent(this);
}
else if (tag == "Tremolo") {
_tremolo = new Tremolo(score());
_tremolo->setTrack(track());
_tremolo->read(e);
_tremolo->setParent(this);
}
else if (tag == "tickOffset") // obsolete
;
else if (tag == "ChordLine") {
ChordLine* cl = new ChordLine(score());
cl->read(e);
add(cl);
}
2013-05-29 10:31:26 +02:00
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
if (score()->mscVersion() <= 114) { // #19988
Note * n = upNote();
if (n) {
if (notes().size() == 1) {
setUserOff(n->userOff() + userOff());
n->setUserOff(QPoint());
n->setReadPos(QPoint());
}
else if(!n->userOff().isNull()) {
if(!_stem) {
_stem = new Stem(score());
add(_stem);
}
_stem->setUserOff(n->userOff() + _stem->userOff());
}
}
}
2013-05-15 17:48:43 +02:00
// hack:
2013-07-13 16:37:41 +02:00
#if 0
2013-05-15 17:48:43 +02:00
if (_notes.size() == 1 && readPos().isNull()) {
Note* note = _notes.front();
if (!note->readPos().isNull()) {
2013-07-13 16:37:41 +02:00
setUserOff(QPointF(note->readPos().x(), 0.0));
2013-05-15 17:48:43 +02:00
note->setReadPos(QPointF(0.0, note->readPos().y()));
}
}
2013-07-13 16:37:41 +02:00
#endif
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// upPos
//---------------------------------------------------------
qreal Chord::upPos() const
{
return upNote()->pos().y();
}
//---------------------------------------------------------
// downPos
//---------------------------------------------------------
qreal Chord::downPos() const
{
return downNote()->pos().y();
}
//---------------------------------------------------------
// centerX
// return x position for attributes
//---------------------------------------------------------
qreal Chord::centerX() const
{
// TAB 'notes' are always centered on the stem
if (staff()->isTabStaff())
return ((StaffTypeTablature*)staff()->staffType())->chordStemPosX(this) * spatium();
2012-05-26 14:26:10 +02:00
const Note* note = up() ? upNote() : downNote();
qreal x = note->pos().x();
x += note->headWidth() * .5;
if (note->mirror()) {
x += note->headWidth() * (up() ? -1.0 : 1.0);
}
return x;
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Chord::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
2013-07-15 08:32:59 +02:00
bool slash = measure() && measure()->slashStyle(staffIdx());
if (_hook && !slash)
func(data, _hook );
2013-07-15 08:32:59 +02:00
if (_stem && !slash)
2012-05-26 14:26:10 +02:00
func(data, _stem);
if (_stemSlash)
func(data, _stemSlash);
if (_arpeggio)
func(data, _arpeggio);
if (_tremolo && (_tremoloChordType != TremoloSecondNote))
func(data, _tremolo);
if (_glissando)
func(data, _glissando);
2013-07-15 08:32:59 +02:00
if (staff() && staff()->showLedgerLines())
for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
func(data, ll);
int n = _notes.size();
for (int i = 0; i < n; ++i)
_notes.at(i)->scanElements(data, func, all);
n = _el.size();
2013-06-10 21:13:04 +02:00
for (Chord* chord : _graceNotes)
chord->scanElements(data, func, all);
2013-03-25 16:27:20 +01:00
for (Element* e : _el)
e->scanElements(data, func, all);
2012-05-26 14:26:10 +02:00
ChordRest::scanElements(data, func, all);
}
//---------------------------------------------------------
2013-03-25 16:27:20 +01:00
// processSiblings
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-03-25 16:27:20 +01:00
void Chord::processSiblings(std::function<void(Element*)> func)
2012-05-26 14:26:10 +02:00
{
if (_hook)
2013-03-25 16:27:20 +01:00
func(_hook);
2012-05-26 14:26:10 +02:00
if (_stem)
2013-03-25 16:27:20 +01:00
func(_stem);
2012-05-26 14:26:10 +02:00
if (_stemSlash)
2013-03-25 16:27:20 +01:00
func(_stemSlash);
2012-05-26 14:26:10 +02:00
if (_arpeggio)
2013-03-25 16:27:20 +01:00
func(_arpeggio);
2012-05-26 14:26:10 +02:00
if (_tremolo)
2013-03-25 16:27:20 +01:00
func(_tremolo);
if (_glissando)
func(_glissando);
for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
2013-03-25 16:27:20 +01:00
func(ll);
for (int i = 0; i < _notes.size(); ++i)
func(_notes.at(i));
2013-06-10 21:13:04 +02:00
for (Chord* chord : _graceNotes)
func(chord);
2013-03-25 16:27:20 +01:00
for (Element* e : _el)
func(e);
}
2012-05-26 14:26:10 +02:00
2013-03-25 16:27:20 +01:00
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Chord::setTrack(int val)
{
2012-05-26 14:26:10 +02:00
ChordRest::setTrack(val);
2013-03-25 16:27:20 +01:00
processSiblings([val] (Element* e) { e->setTrack(val); } );
2012-05-26 14:26:10 +02:00
}
2013-03-25 16:27:20 +01:00
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void Chord::setScore(Score* s)
{
ChordRest::setScore(s);
processSiblings([s] (Element* e) { e->setScore(s); } );
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layoutStem1
2013-07-13 11:17:47 +02:00
/// Layout _stem and _stemSlash
2012-05-26 14:26:10 +02:00
//
2013-07-13 11:17:47 +02:00
// Called before layout spacing of notes.
// Create stem if necessary.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Chord::layoutStem1()
{
2013-07-13 11:17:47 +02:00
bool hasStem = durationType().hasStem() && !(_noStem || measure()->slashStyle(staffIdx()));
2012-05-26 14:26:10 +02:00
if (hasStem) {
2013-07-13 11:17:47 +02:00
if (!_stem) {
Stem* stem = new Stem(score());
stem->setParent(this);
stem->setGenerated(true);
score()->undoAddElement(stem);
}
2012-05-26 14:26:10 +02:00
}
2013-07-13 11:17:47 +02:00
else if (_stem)
score()->undoRemoveElement(_stem);
2012-05-26 14:26:10 +02:00
if (hasStem && (_noteType == NOTE_ACCIACCATURA)) {
2013-07-13 11:17:47 +02:00
if (_stemSlash == 0) {
StemSlash* slash = new StemSlash(score());
2013-10-30 09:00:01 +01:00
add(slash);
// slash->setParent(this);
// slash->setGenerated(true);
// score()->undoAddElement(slash);
2013-07-13 11:17:47 +02:00
}
2012-05-26 14:26:10 +02:00
}
2013-07-13 11:17:47 +02:00
else if (_stemSlash)
2013-10-30 09:00:01 +01:00
// score()->undoRemoveElement(_stemSlash);
setStemSlash(0);
2013-07-11 12:25:25 +02:00
}
2012-05-26 14:26:10 +02:00
2013-07-11 12:25:25 +02:00
//---------------------------------------------------------
// layoutHook1
2013-07-13 11:17:47 +02:00
/// Layout hook
//
// Called before layout spacing of notes.
// Create hook if necessary to get right note width for next
// pass.
2013-07-11 12:25:25 +02:00
//---------------------------------------------------------
void Chord::layoutHook1()
{
int hookIdx = durationType().hooks();
if (hookIdx && !(_noStem || measure()->slashStyle(staffIdx()))) {
2012-05-26 14:26:10 +02:00
if (!_hook) {
Hook* hook = new Hook(score());
hook->setParent(this);
2013-07-13 11:17:47 +02:00
hook->setGenerated(true);
2012-05-26 14:26:10 +02:00
score()->undoAddElement(hook);
}
2013-07-13 11:17:47 +02:00
_hook->setHookType(up() ? hookIdx : -hookIdx);
2012-05-26 14:26:10 +02:00
}
else if (_hook)
score()->undoRemoveElement(_hook);
}
2013-06-20 10:19:04 +02:00
//-----------------------------------------------------------------------------
2012-05-26 14:26:10 +02:00
// layoutStem
/// Layout chord tremolo stem and hook.
//
// hook: sets position
// stem: sets length, but not position (assumed to be set in Chord::layout())
2013-06-20 10:19:04 +02:00
//-----------------------------------------------------------------------------
2012-05-26 14:26:10 +02:00
void Chord::layoutStem()
{
2013-06-10 21:13:04 +02:00
for (Chord* c : _graceNotes)
c->layoutStem();
2013-06-16 23:33:37 +02:00
if (beam())
return;
//
// TAB
//
qreal _spatium = spatium();
StaffTypeTablature* tab = 0;
if (staff() && staff()->isTabStaff()) {
tab = (StaffTypeTablature*)staff()->staffType();
// require stems only if TAB is not stemless and this chord has a stem
if (!tab->slashStyle() && _stem) {
// if stems are beside staff, apply special formatting
if (!tab->stemThrough()) {
// process stem:
_stem->setLen(tab->chordStemLength(this) * _spatium);
// process hook
int hookIdx = durationType().hooks();
if (!up())
hookIdx = -hookIdx;
if (hookIdx) {
_hook->setHookType(hookIdx);
2014-02-26 21:31:01 +01:00
qreal x = _stem->pos().x() + _stem->lineWidth() * .5;;
2013-11-07 19:58:42 +01:00
qreal y = _stem->pos().y();
if (up()) {
2014-02-26 21:31:01 +01:00
y -= _stem->bbox().height();
2013-11-07 19:58:42 +01:00
x -= _stem->width();
}
else {
2014-02-26 21:31:01 +01:00
y += _stem->bbox().height();
x -= _stem->width();
2013-11-07 19:58:42 +01:00
}
_hook->setPos(x, y);
_hook->adjustReadPos();
}
return;
2012-05-26 14:26:10 +02:00
}
// if stems are through staff, use standard formatting
2012-05-26 14:26:10 +02:00
}
}
//
// NON-TAB (or TAB with stems through staff)
//
2012-05-26 14:26:10 +02:00
if (segment()) {
System* s = segment()->measure()->system();
if (s == 0) //DEBUG
return;
}
if (_stem) {
Note* downnote;
int dl, ul;
2012-05-26 14:26:10 +02:00
qreal stemLen;
int hookIdx = durationType().hooks();
downnote = downNote();
ul = upLine();
dl = downLine();
if (tab && !tab->onLines()) { // if TAB and frets above strings, move 1 position up
--ul;
--dl;
}
2012-05-26 14:26:10 +02:00
bool shortenStem = score()->styleB(ST_shortenStem);
2012-11-14 12:24:08 +01:00
if (hookIdx >= 2 || _tremolo)
shortenStem = false;
2012-05-26 14:26:10 +02:00
Spatium progression(score()->styleS(ST_shortStemProgression));
qreal shortest(score()->styleS(ST_shortestStem).val());
qreal normalStemLen = small() ? 2.5 : 3.5;
switch(hookIdx) {
case 3: normalStemLen += small() ? .5 : 0.75; break; //32nd notes
case 4: normalStemLen += small() ? 1.0 : 1.5; break; //64th notes
case 5: normalStemLen += small() ? 1.5 : 2.25; break; //128th notes
}
if (_hook && tab == 0) {
2012-05-26 14:26:10 +02:00
if (up() && durationType().dots()) {
//
// avoid collision of dot with hook
//
if (!(ul & 1))
normalStemLen += .5;
shortenStem = false;
}
}
if (_noteType != NOTE_NORMAL) {
// grace notes stems are not subject to normal
// stem rules
stemLen = qAbs(ul - dl) * .5;
stemLen += normalStemLen * score()->styleD(ST_graceNoteMag);
if (up())
stemLen *= -1;
}
else {
// normal note (not grace)
qreal staffHeight = staff() ? (staff()->lines()- 1) : 4;
qreal staffHlfHgt = staffHeight * 0.5;
if (up()) { // stem up
qreal dy = dl * .5; // note-side vert. pos.
qreal sel = ul * .5 - normalStemLen; // stem end vert. pos
// if stem ends above top line (with some exceptions), shorten it
if (shortenStem && (sel < 0.0)
&& (hookIdx == 0 || tab || !downnote->mirror()))
2012-05-26 14:26:10 +02:00
sel -= sel * progression.val();
if (sel > staffHlfHgt) // if stem ends below ('>') staff mid position,
sel = staffHlfHgt; // stretch it to mid position
stemLen = sel - dy; // actual stem length
if (-stemLen < shortest) // is stem too short,
stemLen = -shortest; // lengthen it to shortest possible length
2012-05-26 14:26:10 +02:00
}
else { // stem down
qreal uy = ul * .5; // note-side vert. pos.
qreal sel = dl * .5 + normalStemLen; // stem end vert. pos.
// if stem ends below bottom line (with some exceptions), shorten it
if (shortenStem && (sel > staffHeight)
2013-11-07 19:58:42 +01:00
&& (hookIdx == 0 || tab || downnote->mirror()))
sel -= (sel - staffHeight) * progression.val();
if (sel < staffHlfHgt) // if stem ends above ('<') staff mid position,
sel = staffHlfHgt; // stretch it to mid position
stemLen = sel - uy; // actual stem length
if (stemLen < shortest) // if stem too short,
stemLen = shortest; // lengthen it to shortest possible position
2012-05-26 14:26:10 +02:00
}
}
2012-11-14 12:24:08 +01:00
// adjust stem len for tremolo
if (_tremolo && !_tremolo->twoNotes()) {
// hook up odd lines
int tab[2][2][2][4] = {
{ { { 0, 0, 0, 1 }, // stem - down - even - lines
{ 0, 0, 0, 2 } // stem - down - odd - lines
},
{ { 0, 0, 0, -1 }, // stem - up - even - lines
{ 0, 0, 0, -2 } // stem - up - odd - lines
}
},
{ { { 0, 0, 1, 2 }, // hook - down - even - lines
{ 0, 0, 1, 2 } // hook - down - odd - lines
},
{ { 0, 0, -1, -2 }, // hook - up - even - lines
{ 0, 0, -1, -2 } // hook - up - odd - lines
}
}
};
int odd = (up() ? upLine() : downLine()) & 1;
int n = tab[_hook ? 1 : 0][up() ? 1 : 0][odd][_tremolo->lines()-1];
stemLen += n * .5;
}
// scale stemLen according to staff line spacing
2013-06-16 23:33:37 +02:00
if (staff())
stemLen *= staff()->staffType()->lineDistance().val();
2013-06-16 23:33:37 +02:00
_stem->setLen(stemLen * _spatium);
// if (isGrace())
// abort();
2012-05-26 14:26:10 +02:00
if (_hook) {
2013-11-07 19:58:42 +01:00
QPointF p(_stem->hookPos());
if (up()) {
p.ry() -= _hook->bbox().top();
p.rx() -= _stem->width();
}
else {
p.ry() -= _hook->bbox().bottom();
p.rx() -= _stem->width();
}
_hook->setPos(p);
2012-05-26 14:26:10 +02:00
_hook->adjustReadPos();
}
if (_stemSlash)
_stemSlash->layout();
2012-05-26 14:26:10 +02:00
}
//-----------------------------------------
// process tremolo
//-----------------------------------------
if (_tremolo)
_tremolo->layout();
}
//---------------------------------------------------------
// layout2
// Called after horizontal positions of all elements
// are fixed.
//---------------------------------------------------------
void Chord::layout2()
{
2013-06-10 21:13:04 +02:00
for (Chord* c : _graceNotes)
c->layout2();
2012-05-26 14:26:10 +02:00
if (glissando())
glissando()->layout();
qreal _spatium = spatium();
//
// Experimental:
// look for colliding ledger lines
//
const qreal minDist = _spatium * .17;
2013-06-12 14:23:57 +02:00
Segment* s = segment()->prev(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
if (s) {
2013-01-02 20:13:58 +01:00
int strack = staff2track(staffIdx());
2012-05-26 14:26:10 +02:00
int etrack = strack + VOICES;
for (LedgerLine* h = _ledgerLines; h; h = h->next()) {
2012-05-26 14:26:10 +02:00
Spatium len(h->len());
qreal y = h->y();
qreal x = h->x();
bool found = false;
qreal cx = h->measureXPos();
for (int track = strack; track < etrack; ++track) {
Chord* e = static_cast<Chord*>(s->element(track));
2012-05-26 14:26:10 +02:00
if (!e || e->type() != CHORD)
continue;
for (LedgerLine* ll = e->ledgerLines(); ll; ll = ll->next()) {
2012-05-26 14:26:10 +02:00
if (ll->y() != y)
continue;
qreal d = cx - ll->measureXPos() - (ll->len().val() * _spatium);
if (d < minDist) {
//
// the ledger lines overlap
//
qreal shorten = (minDist - d) * .5;
x += shorten;
len -= Spatium(shorten / _spatium);
ll->setLen(ll->len() - Spatium(shorten / _spatium));
h->setLen(len);
h->setPos(x, y);
}
found = true;
break;
}
if (found)
break;
}
}
}
}
2013-06-10 21:13:04 +02:00
//---------------------------------------------------------
// layout10
//---------------------------------------------------------
void Chord::layout10(AccidentalState* as)
{
for (Chord* c : _graceNotes)
c->layout10(as);
Drumset* drumset = 0;
if (staff()->part()->instr()->useDrumset())
drumset = staff()->part()->instr()->drumset();
for (int i = 0; i < notes().size(); ++i) {
Note* note = notes().at(i);
if (drumset) {
int pitch = note->pitch();
if (!drumset->isValid(pitch)) {
// qDebug("unmapped drum note %d", pitch);
}
else {
note->setHeadGroup(drumset->noteHead(pitch));
note->setLine(drumset->line(pitch));
continue;
}
}
note->layout10(as);
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Chord::layout()
{
if (_notes.empty())
return;
2013-05-23 15:39:14 +02:00
if (staff() && staff()->isTabStaff())
layoutTablature();
else
2013-06-16 23:33:37 +02:00
layoutPitched();
2013-05-23 15:39:14 +02:00
}
2012-05-26 14:26:10 +02:00
2013-05-23 15:39:14 +02:00
//---------------------------------------------------------
2013-06-16 23:33:37 +02:00
// layoutPitched
2013-05-23 15:39:14 +02:00
//---------------------------------------------------------
2013-06-16 23:33:37 +02:00
void Chord::layoutPitched()
2013-05-23 15:39:14 +02:00
{
2013-06-16 23:33:37 +02:00
for (Chord* c : _graceNotes)
c->layoutPitched();
2013-06-19 16:25:29 +02:00
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
2013-06-16 23:33:37 +02:00
qreal minNoteDistance = score()->styleS(ST_dotNoteDistance).val() * _spatium;
2014-02-23 17:16:39 +01:00
qreal minShortTieDistance = 1.0 * _spatium;
2012-05-26 14:26:10 +02:00
while (_ledgerLines) {
LedgerLine* l = _ledgerLines->next();
delete _ledgerLines;
_ledgerLines = l;
}
2012-05-26 14:26:10 +02:00
2013-06-19 16:25:29 +02:00
qreal lll = 0.0; // space to leave at left of chord
qreal rrr = 0.0; // space to leave at right of chord
Note* upnote = upNote();
2012-05-26 14:26:10 +02:00
2013-05-23 15:39:14 +02:00
delete _tabDur; // no TAB? no duration symbol! (may happen when converting a TAB into PITCHED)
_tabDur = 0;
if (!segment()) {
2012-05-26 14:26:10 +02:00
//
2013-05-23 15:39:14 +02:00
// hack for use in palette
2012-05-26 14:26:10 +02:00
//
int n = _notes.size();
2013-05-23 15:39:14 +02:00
for (int i = 0; i < n; i++) {
Note* note = _notes.at(i);
2012-05-26 14:26:10 +02:00
note->layout();
2013-05-23 15:39:14 +02:00
qreal x = 0.0;
qreal y = note->line() * _spatium * .5;
note->setPos(x, y);
2012-05-26 14:26:10 +02:00
}
2013-05-23 15:39:14 +02:00
computeUp();
layoutStem();
return;
}
//-----------------------------------------
// process notes
//-----------------------------------------
for (int i = 0; i < _notes.size(); ++i) {
Note* note = _notes.at(i);
note->layout();
2013-05-23 16:58:22 +02:00
qreal x1 = note->pos().x();
if (_noteType == NOTE_NORMAL)
x1 += ipos().x();
2013-05-23 16:58:22 +02:00
qreal x2 = x1 + note->headWidth();
2013-07-18 17:01:45 +02:00
lll = qMax(lll, -x1);
rrr = qMax(rrr, x2);
2013-05-23 15:39:14 +02:00
Accidental* accidental = note->accidental();
if (accidental) {
qreal x = accidental->x() + note->x();
2013-07-31 14:09:35 +02:00
x -= score()->styleS(ST_accidentalDistance).val() * _spatium;
2013-07-18 17:01:45 +02:00
lll = qMax(lll, -x);
2012-05-26 14:26:10 +02:00
}
2014-02-22 21:29:29 +01:00
// allow extra space for shortened ties
// this code should be synchronized with
// the tie positioning code in Tie::slurPos()
Tie* tie;
tie = note->tieBack();
if (tie) {
tie->calculateDirection();
2014-02-23 17:16:39 +01:00
qreal spaceNeeded = 0.0;
qreal spaceFound = 0.0;
Note* sn = tie->startNote();
Chord* sc = sn->chord();
2014-02-22 21:29:29 +01:00
if (sc && sc->measure() == measure()) {
2014-02-23 17:16:39 +01:00
if (sc->notes().size() > 1 || (sc->stem() && sc->up() == tie->up())) {
spaceNeeded += minShortTieDistance * 0.75;
if (sc->width() > sn->width()) {
// chord with second?
// account for noteheads further to right
qreal snEnd = sn->x() + sn->headWidth();
qreal scEnd = snEnd;
for (int i = 0; i < sc->notes().size(); ++i)
scEnd = qMax(scEnd, sc->notes().at(i)->x() + sc->notes().at(i)->headWidth());
spaceFound += scEnd - snEnd;
}
}
if (notes().size() > 1 || (stem() && !up() && !tie->up())) {
spaceNeeded += minShortTieDistance * 0.75;
// for positive offset:
// use available space
// for negative x offset:
// space is allocated elsewhere, so don't re-allocate here
spaceFound += qAbs(note->ipos().x());
}
spaceNeeded = qMin(spaceNeeded, minShortTieDistance);
qreal d = qMax(spaceNeeded - spaceFound, 0.0);
2014-02-22 21:29:29 +01:00
lll = qMax(lll, d);
}
}
2013-05-23 15:39:14 +02:00
}
2013-05-23 16:58:22 +02:00
//-----------------------------------------
// stem position
2013-05-23 16:58:22 +02:00
//-----------------------------------------
2013-05-23 15:39:14 +02:00
// Y: only needed if there is an actual stem
2013-05-24 11:44:21 +02:00
if (stem())
2013-05-23 16:58:22 +02:00
stem()->rypos() = (_up ? downNote() : upNote())->rypos();
2013-05-24 11:44:21 +02:00
//-----------------------------------------
// create ledger lines
//-----------------------------------------
2013-05-24 11:44:21 +02:00
addLedgerLines(staffMove());
2013-05-23 15:39:14 +02:00
for (LedgerLine* ll = _ledgerLines; ll; ll = ll->next())
ll->layout();
if (_arpeggio) {
2013-11-19 12:12:07 +01:00
_arpeggio->layout(); // only for width() !
lll += _arpeggio->width() + _spatium * .5;
qreal y1 = upnote->pos().y() - upnote->headHeight() * .5;
_arpeggio->setPos(-lll, y1);
_arpeggio->adjustReadPos();
2013-11-19 12:12:07 +01:00
// _arpeggio->layout() called in layoutArpeggio2()
2013-05-23 15:39:14 +02:00
// handle the special case of _arpeggio->span() > 1
// in layoutArpeggio2() after page layout has done so we
// know the y position of the next staves
}
if (_glissando)
lll += _spatium * .5;
if (dots()) {
2013-06-16 23:33:37 +02:00
qreal x = dotPosX() + minNoteDistance
+ (dots()-1) * score()->styleS(ST_dotDotDistance).val() * _spatium;
2013-11-11 15:11:28 +01:00
x += symWidth(SymId::augmentationDot);
2013-07-18 17:01:45 +02:00
rrr = qMax(rrr, x);
2013-05-23 15:39:14 +02:00
}
if (_hook) {
if (beam())
score()->undoRemoveElement(_hook);
2012-05-26 14:26:10 +02:00
else {
2013-05-23 15:39:14 +02:00
_hook->layout();
if (up() && stem()) {
2013-05-23 15:39:14 +02:00
// hook position is not set yet
qreal x = _hook->bbox().right() + stem()->hookPos().x();
rrr = qMax(rrr, x);
2012-05-26 14:26:10 +02:00
}
2013-05-23 15:39:14 +02:00
}
}
2012-05-26 14:26:10 +02:00
if (_ledgerLines) {
// increase distance to previous chord if both have
// ledger lines
Segment* s = segment();
s = s->prev(Segment::SegChordRest);
if (s && s->element(track()) && s->element(track())->type() == CHORD
&& static_cast<Chord*>(s->element(track()))->ledgerLines()) {
// TODO: detect case were one chord is above staff, the other below
2013-10-29 22:56:13 +01:00
lll = qMax(_spatium * 0.8f, lll);
}
}
2013-05-23 15:39:14 +02:00
_space.setLw(lll);
2013-06-19 16:25:29 +02:00
_space.setRw(rrr);
2013-05-23 15:39:14 +02:00
2013-06-16 23:33:37 +02:00
int n = _graceNotes.size();
2013-06-19 16:25:29 +02:00
if (n) {
qreal x = -(_space.lw() + minNoteDistance);
qreal graceMag = score()->styleD(ST_graceNoteMag);
for (int i = n-1; i >= 0; --i) {
Chord* c = _graceNotes[i];
x -= c->space().rw();
c->setPos(x, 0);
x -= c->space().lw() + minNoteDistance * graceMag;
2012-05-26 14:26:10 +02:00
}
2013-06-19 16:25:29 +02:00
if (-x > _space.lw())
_space.setLw(-x);
2013-05-23 15:39:14 +02:00
}
2012-05-26 14:26:10 +02:00
2013-06-16 23:33:37 +02:00
for (Element* e : _el) {
2013-06-20 17:23:24 +02:00
if (e->type() == Element::SLUR) // we cannot at this time as chordpositions are not fixed
continue;
e->layout();
if (e->type() == CHORDLINE) {
int x = bbox().translated(e->pos()).right();
if (x > _space.rw())
_space.setRw(x);
2013-06-16 23:33:37 +02:00
}
2013-06-12 14:23:57 +02:00
}
2013-05-23 15:39:14 +02:00
for (int i = 0; i < _notes.size(); ++i)
_notes.at(i)->layout2();
QRectF bb;
processSiblings([&bb] (Element* e) { bb |= e->bbox().translated(e->pos()); } );
2013-05-23 16:58:22 +02:00
setbbox(bb.translated(_spatium*2, 0));
2013-05-23 15:39:14 +02:00
}
2012-05-26 14:26:10 +02:00
2013-05-23 15:39:14 +02:00
//---------------------------------------------------------
// layoutTablature
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
2013-05-23 15:39:14 +02:00
void Chord::layoutTablature()
{
qreal _spatium = spatium();
2013-06-16 23:33:37 +02:00
qreal minNoteDistance = score()->styleS(ST_dotNoteDistance).val() * _spatium;
2013-01-02 09:29:17 +01:00
for (Chord* c : _graceNotes)
c->layoutTablature();
2013-05-23 15:39:14 +02:00
while (_ledgerLines) {
LedgerLine* l = _ledgerLines->next();
delete _ledgerLines;
_ledgerLines = l;
}
2013-01-02 14:33:23 +01:00
qreal lll = 0.0; // space to leave at left of chord
qreal rrr = 0.0; // space to leave at right of chord
Note* upnote = upNote();
2013-11-11 15:11:28 +01:00
qreal headWidth = symWidth(SymId::noteheadBlack);
StaffTypeTablature* tab = (StaffTypeTablature*)staff()->staffType();
qreal lineDist = tab->lineDistance().val() *_spatium;
qreal stemX = tab->chordStemPosX(this) *_spatium;
2012-05-26 14:26:10 +02:00
int numOfNotes = _notes.size();
for (int i = 0; i < numOfNotes; ++i) {
2013-05-23 15:39:14 +02:00
Note* note = _notes.at(i);
note->layout();
// set headWidth to max fret text width
qreal fretWidth = note->bbox().width();
if (headWidth < fretWidth)
headWidth = fretWidth;
// centre fret string on stem
qreal x = stemX - fretWidth*0.5;
note->setPos(x, tab->physStringToVisual(note->string()) * lineDist);
2013-05-23 15:39:14 +02:00
}
// horiz. spacing: leave half width at each side of the (potential) stem
qreal halfHeadWidth = headWidth * 0.5;
if (lll < stemX - halfHeadWidth)
lll = stemX - halfHeadWidth;
if (rrr < stemX + halfHeadWidth)
rrr = stemX + halfHeadWidth;
// if tab type is stemless or chord is stemless (possible when imported from MusicXML)
// or duration longer than half (if halves have stems) or duration longer than crochet
// remove stems
if (tab->slashStyle() || _noStem || durationType().type() <
(tab->minimStyle() != TAB_MINIM_NONE ? TDuration::V_HALF : TDuration::V_QUARTER) ) {
delete _stem;
delete _hook;
_stem = 0;
_hook = 0;
}
// if stem is required but missing, add it;
// set stem position (stem length is set in Chord:layoutStem() )
else {
if (_stem == 0)
setStem(new Stem(score()));
_stem->setPos(tab->chordStemPos(this) * _spatium);
if (_hook) {
2013-07-11 14:44:35 +02:00
if (beam())
score()->undoRemoveElement(_hook);
2013-05-23 15:39:14 +02:00
else {
_hook->layout();
if (rrr < stemX + _hook->width())
rrr = stemX + _hook->width();
2013-03-04 12:00:02 +01:00
}
2012-05-26 14:26:10 +02:00
}
}
2013-05-23 15:39:14 +02:00
// unconditionally delete grace slashes
delete _stemSlash;
_stemSlash = 0;
2012-05-26 14:26:10 +02:00
2013-05-23 15:39:14 +02:00
if (!tab->genDurations() // if tab is not set for duration symbols
|| track2voice(track()) // or not in first voice
|| isGrace()) { // or chord is a grace (no dur. symbols for graces
// // wich are not used in hist. tabs anyway)
2013-05-23 15:39:14 +02:00
// no tab duration symbols
//
delete _tabDur; // delete an existing duration symbol
_tabDur = 0;
}
else {
//
// tab duration symbols
//
// check duration of prev. CR segm
ChordRest * prevCR = prevChordRest(this);
// if no previous CR
// OR duration type and/or number of dots is different from current CR
// OR previous CR is a rest
// set a duration symbol (trying to re-use existing symbols where existing to minimize
// symbol creation and deletion)
if (prevCR == 0 || prevCR->durationType().type() != durationType().type()
|| prevCR->dots() != dots()
|| prevCR->type() == REST) {
// symbol needed; if not exist, create; if exists, update duration
if (!_tabDur)
_tabDur = new TabDurationSymbol(score(), tab, durationType().type(), dots());
else
_tabDur->setDuration(durationType().type(), dots(), tab);
_tabDur->setParent(this);
// _tabDur->setMag(mag()); // useless to set grace mag: graces have no dur. symbol
2013-05-23 15:39:14 +02:00
_tabDur->layout();
}
else { // symbol not needed: if exists, delete
delete _tabDur;
_tabDur = 0;
}
} // end of if(duration_symbols)
2012-05-26 14:26:10 +02:00
if (_arpeggio) {
qreal headHeight = upnote->headHeight();
_arpeggio->layout();
lll += _arpeggio->width() + _spatium * .5;
qreal y = upNote()->pos().y() - headHeight * .5;
2013-04-26 17:14:37 +02:00
qreal h = downNote()->pos().y() + downNote()->headHeight() - y;
2012-05-26 14:26:10 +02:00
_arpeggio->setHeight(h);
_arpeggio->setPos(-lll, y);
// handle the special case of _arpeggio->span() > 1
// in layoutArpeggio2() after page layout has done so we
// know the y position of the next staves
}
if (_glissando)
lll += _spatium * .5;
if (dots()) {
2013-06-16 23:33:37 +02:00
qreal x = dotPosX() + minNoteDistance
+ (dots()-1) * score()->styleS(ST_dotDotDistance).val() * _spatium;
2013-11-11 15:11:28 +01:00
x += symWidth(SymId::augmentationDot);
2012-05-26 14:26:10 +02:00
if (x > rrr)
rrr = x;
}
if (_hook) {
if (beam())
score()->undoRemoveElement(_hook);
else if(tab == 0) {
2013-03-04 12:00:02 +01:00
_hook->layout();
2013-03-25 12:45:35 +01:00
if (up()) {
// hook position is not set yet
qreal x = _hook->bbox().right() + stem()->hookPos().x();
rrr = qMax(rrr, x);
}
2013-03-04 12:00:02 +01:00
}
2012-05-26 14:26:10 +02:00
}
_space.setLw(lll);
_space.setRw(rrr);
int numOfGraces = _graceNotes.size();
if (numOfGraces) {
qreal x = -(_space.lw() + minNoteDistance);
qreal graceMag = score()->styleD(ST_graceNoteMag);
for (int i = numOfGraces-1; i >= 0; --i) {
Chord* c = _graceNotes[i];
x -= c->space().rw();
c->setPos(x, 0);
x -= c->space().lw() + minNoteDistance * graceMag;
}
if (-x > _space.lw())
_space.setLw(-x);
}
2012-05-26 14:26:10 +02:00
2013-03-25 16:27:20 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
e->layout();
if (e->type() == CHORDLINE) {
int x = bbox().translated(e->pos()).right();
if (x > _space.rw())
_space.setRw(x);
}
}
2013-01-02 09:29:17 +01:00
// bbox();
2012-05-26 14:26:10 +02:00
for (int i = 0; i < numOfNotes; ++i)
2013-03-25 16:27:20 +01:00
_notes.at(i)->layout2();
2012-07-24 14:20:43 +02:00
QRectF bb;
2013-03-25 16:27:20 +01:00
processSiblings([&bb] (Element* e) { bb |= e->bbox().translated(e->pos()); } );
2013-05-23 15:39:14 +02:00
if (_tabDur)
bb |= _tabDur->bbox().translated(_tabDur->pos());
2012-07-24 14:20:43 +02:00
setbbox(bb);
2012-05-26 14:26:10 +02:00
}
2013-05-12 12:51:42 +02:00
//---------------------------------------------------------
// crossMeasureSetup
//---------------------------------------------------------
void Chord::crossMeasureSetup(bool on)
{
2013-05-22 15:20:14 +02:00
if (!on) {
if (_crossMeasure != CROSSMEASURE_UNKNOWN) {
_crossMeasure = CROSSMEASURE_UNKNOWN;
layoutStem1();
}
2013-05-12 12:51:42 +02:00
return;
}
2013-05-22 15:20:14 +02:00
if (_crossMeasure == CROSSMEASURE_UNKNOWN) {
2013-05-12 12:51:42 +02:00
int tempCross = CROSSMEASURE_NONE; // assume no cross-measure modification
// if chord has only one note and note is tied forward
if(notes().size() == 1 && _notes[0]->tieFor()) {
Chord* tiedChord = _notes[0]->tieFor()->endNote()->chord();
// if tied note belongs to another measure and to a single-note chord
if(tiedChord->measure() != measure() && tiedChord->notes().size() == 1) {
// get total duration
QList<TDuration> durList = toDurationList(
actualDurationType().fraction() +
tiedChord->actualDurationType().fraction(), true);
// if duration can be expressed as a single duration
// apply cross-measure modification
if(durList.size() == 1) {
_crossMeasure = tempCross = CROSSMEASURE_FIRST;
2013-05-12 12:51:42 +02:00
_crossMeasureTDur = durList[0];
layoutStem1();
2013-05-12 12:51:42 +02:00
}
}
_crossMeasure = tempCross;
tiedChord->setCrossMeasure(tempCross == CROSSMEASURE_FIRST ?
CROSSMEASURE_SECOND : CROSSMEASURE_NONE);
}
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layoutArpeggio2
// called after layout of page
//---------------------------------------------------------
void Chord::layoutArpeggio2()
{
if (!_arpeggio)
return;
2013-11-19 12:12:07 +01:00
qreal y = upNote()->pagePos().y() - upNote()->headHeight() * .5;
2012-05-26 14:26:10 +02:00
int span = _arpeggio->span();
int btrack = track() + (span - 1) * VOICES;
ChordRest* bchord = static_cast<ChordRest*>(segment()->element(btrack));
2013-11-19 12:12:07 +01:00
Note* dnote = (bchord && bchord->type() == CHORD) ? static_cast<Chord*>(bchord)->downNote() : downNote();
2012-05-26 14:26:10 +02:00
2013-11-19 12:12:07 +01:00
qreal h = dnote->pagePos().y() + dnote->headHeight() * .5 - y;
2012-05-26 14:26:10 +02:00
_arpeggio->setHeight(h);
2013-11-19 12:12:07 +01:00
_arpeggio->layout();
2012-05-26 14:26:10 +02:00
2013-11-19 12:12:07 +01:00
#if 0 // collect notes for arpeggio
2012-05-26 14:26:10 +02:00
QList<Note*> notes;
int n = _notes.size();
for (int j = n - 1; j >= 0; --j) {
Note* note = _notes[j];
if (note->tieBack())
continue;
notes.prepend(note);
}
for (int i = 1; i < span; ++i) {
ChordRest* c = static_cast<ChordRest*>(segment()->element(track() + i * VOICES));
if (c && c->type() == CHORD) {
QList<Note*> nl = static_cast<Chord*>(c)->notes();
int n = nl.size();
for (int j = n - 1; j >= 0; --j) {
Note* note = nl[j];
if (note->tieBack())
continue;
notes.prepend(note);
}
}
}
2013-11-19 12:12:07 +01:00
#endif
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// findNote
//---------------------------------------------------------
Note* Chord::findNote(int pitch) const
{
int n = _notes.size();
for (int i = 0; i < n; ++i) {
Note* n = _notes.at(i);
2012-05-26 14:26:10 +02:00
if (n->pitch() == pitch)
return n;
}
return 0;
}
//---------------------------------------------------------
// pitchChanged
//---------------------------------------------------------
2013-07-15 14:36:51 +02:00
void Chord::lineChanged()
2012-05-26 14:26:10 +02:00
{
2013-07-15 16:08:53 +02:00
qSort(_notes.begin(), _notes.end(),
[](Note* n1, const Note* n2) ->bool {return n1->line() > n2->line(); } );
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Chord::drop(const DropData& data)
{
Element* e = data.element;
switch (e->type()) {
case ARTICULATION:
{
Articulation* atr = static_cast<Articulation*>(e);
Articulation* oa = hasArticulation(atr);
if (oa) {
delete atr;
atr = 0;
// if attribute is already there, remove
// score()->cmdRemove(oa); // unexpected behaviour?
score()->select(oa, SELECT_SINGLE, 0);
}
else {
atr->setParent(this);
atr->setTrack(track());
score()->undoAddElement(atr);
}
return atr;
}
2012-11-19 10:08:15 +01:00
2012-05-26 14:26:10 +02:00
case CHORDLINE:
e->setParent(this);
2013-02-28 15:06:54 +01:00
e->setTrack(track());
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
break;
2012-11-19 10:08:15 +01:00
case TREMOLO:
{
Tremolo* t = static_cast<Tremolo*>(e);
if (t->twoNotes()) {
Segment* s = segment()->next();
while (s) {
if (s->element(track()) && s->element(track())->type() == CHORD)
break;
s = s->next();
}
if (s == 0) {
qDebug("no segment for second note of tremolo found\n");
delete e;
return 0;
}
Chord* ch2 = static_cast<Chord*>(s->element(track()));
t->setChords(this, ch2);
}
}
if (tremolo())
score()->undoRemoveElement(tremolo());
e->setParent(this);
e->setTrack(track());
score()->undoAddElement(e);
break;
2012-11-20 20:51:18 +01:00
case ARPEGGIO:
{
Arpeggio* a = static_cast<Arpeggio*>(e);
if (arpeggio())
score()->undoRemoveElement(arpeggio());
2013-02-28 17:46:30 +01:00
a->setTrack(track());
2012-11-20 20:51:18 +01:00
a->setParent(this);
a->setHeight(spatium() * 5); //DEBUG
score()->undoAddElement(a);
}
return e;
2012-05-26 14:26:10 +02:00
default:
return ChordRest::drop(data);
}
return 0;
}
//---------------------------------------------------------
// dotPosX
//---------------------------------------------------------
qreal Chord::dotPosX() const
{
2013-05-29 17:11:57 +02:00
if (parent())
return segment()->dotPosX(staffIdx());
return -1000.0;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Chord::getProperty(P_ID propertyId) const
{
switch(propertyId) {
case P_NO_STEM: return noStem();
case P_SMALL: return small();
case P_STEM_DIRECTION: return int(stemDirection());
default:
return ChordRest::getProperty(propertyId);
}
}
2013-03-14 13:30:25 +01:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Chord::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
case P_NO_STEM: return false;
case P_SMALL: return false;
case P_STEM_DIRECTION: return int(MScore::AUTO);
default:
return ChordRest::getProperty(propertyId);
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Chord::setProperty(P_ID propertyId, const QVariant& v)
{
switch(propertyId) {
case P_NO_STEM:
setNoStem(v.toBool());
score()->setLayoutAll(true);
break;
case P_SMALL:
setSmall(v.toBool());
score()->setLayoutAll(true);
break;
case P_STEM_DIRECTION:
2012-08-04 15:46:43 +02:00
setStemDirection(MScore::Direction(v.toInt()));
2012-05-26 14:26:10 +02:00
score()->setLayoutAll(true);
break;
default:
return ChordRest::setProperty(propertyId, v);
}
return true;
}
//---------------------------------------------------------
// layoutArticulation
// called from chord()->layoutArticulations()
//---------------------------------------------------------
QPointF Chord::layoutArticulation(Articulation* a)
{
qreal _spatium = spatium();
qreal _spStaff = _spatium * staff()->lineDistance(); // scaled to staff line distance for vert. pos. within a staff
2012-05-26 14:26:10 +02:00
a->layout();
ArticulationAnchor aa = a->anchor();
qreal chordTopY = upPos(); // note position of highest note
qreal chordBotY = downPos(); // note position of lowest note
qreal x = centerX();
qreal y = 0.0;
ArticulationType st = a->articulationType();
2012-05-26 14:26:10 +02:00
// TENUTO and STACCATO: always near the note head (or stem end if beyond a stem)
2013-08-14 10:49:39 +02:00
if ((st == Articulation_Tenuto || st == Articulation_Staccato) && (aa != A_TOP_STAFF && aa != A_BOTTOM_STAFF)) {
bool bottom; // true: artic. is below chord | false: artic. is above chord
// if there area voices, articulation is on stem side
2012-05-26 14:26:10 +02:00
if ((aa == A_CHORD) && measure()->hasVoices(a->staffIdx()))
bottom = !up();
// otherwise, look at specific anchor type (and at chord up/down if necessary)
2012-05-26 14:26:10 +02:00
else
bottom = (aa == A_BOTTOM_CHORD) || (aa == A_CHORD && up());
bool stemSide = (bottom != up()) && stem(); // true if there a stem between the nearest note and the articulation
2012-05-26 14:26:10 +02:00
a->setUp(!bottom);
QPointF pos; // computed articulation position
if (stem()) // if there is a stem, assume artic. will be beyond the stem
2012-05-26 14:26:10 +02:00
pos = stem()->hookPos();
qreal _spatium2 = _spatium * .5;
qreal _spStaff2 = _spStaff * .5;
if (stemSide) { // if artic. is really beyond a stem,
qreal lineDelta = up() ? -_spStaff2 : _spStaff2; // move it 1/2sp away from stem
int line = lrint((pos.y() + lineDelta) / _spStaff); // round to nearest staff line
if (line >= 0 && line <= staff()->lines()-1) // if within staff, align between staff lines
pos.ry() = (line * _spStaff) + (bottom ? _spStaff2 : -_spStaff2);
else { // if outside staff, add some more space (?)
2012-05-26 14:26:10 +02:00
qreal dy = (score()->styleS(ST_beamWidth).val() + 1) * _spatium2;
pos.ry() += bottom ? dy : - dy;
}
}
else { // if articulation is not beyond a stem
2012-05-26 14:26:10 +02:00
int line;
if (bottom) { // if below chord
line = downLine(); // staff position (lines and spaces) of chord lowest note
int lines = (staff()->lines() - 1) * 2; // num. of staff positions within staff
if (line < lines) // if note above staff bottom line
// round space pos. to line pos. above ("line & ~1") and move to 2nd space below ("+3")
2012-05-26 14:26:10 +02:00
line = (line & ~1) + 3;
else // if note on or below staff bottom line,
line += 2; // move 1 whole space below
if (!staff()->isTabStaff()) // on pitched staves, note is at left of stem:
pos.rx() -= upNote()->headWidth() * .5; // move half-a-note-head to left
2012-05-26 14:26:10 +02:00
}
else { // if above chord
line = upLine(); // staff position (lines and spaces) of chord highest note
if (line > 0) // if note below staff top line
// round space pos. to line pos. below ("(line+1) & ~1") and move to 2nd space above ("-3")
2012-05-26 14:26:10 +02:00
line = ((line+1) & ~1) - 3;
else // if note or or above staff top line
line -= 2; // move 1 whole space above
if (!staff()->isTabStaff()) // on pitched staves, note is at right of stem:
pos.rx() += upNote()->headWidth() * .5; // move half-a-note-head to right
2012-05-26 14:26:10 +02:00
}
pos.ry() = line * _spStaff2; // convert staff position to sp distance
2012-05-26 14:26:10 +02:00
}
a->setPos(pos);
a->adjustReadPos();
return QPointF(pos);
}
// other articulations are outside of area occupied by the staff or the chord
2012-05-26 14:26:10 +02:00
// reserve space for slur
bool botGap = false;
bool topGap = false;
2013-08-14 10:49:39 +02:00
const std::vector< ::Interval<Spanner*> >& si = score()->spannerMap().findOverlapping(tick(), tick());
for (::Interval<Spanner*> is : si) {
Spanner* sp = is.value;
if ((sp->type() != SLUR) || (sp->tick() != tick() && sp->tick2() != tick()))
2012-05-26 14:26:10 +02:00
continue;
if ( sp->tick() == tick() && sp->track() != track())
continue;
if ( sp->tick2() == tick() && sp->track2() != track())
continue;
2012-05-26 14:26:10 +02:00
Slur* s = static_cast<Slur*>(sp);
if (s->up())
topGap = true;
else
botGap = true;
}
2013-08-14 10:49:39 +02:00
2012-05-26 14:26:10 +02:00
if (botGap)
chordBotY += _spStaff;
2012-05-26 14:26:10 +02:00
else
chordBotY += _spStaff * .5;
2012-05-26 14:26:10 +02:00
if (topGap)
chordTopY -= _spStaff;
2012-05-26 14:26:10 +02:00
else
chordTopY -= _spStaff * .5;
2012-05-26 14:26:10 +02:00
// avoid collisions of staff articulations with chord notes:
// gap between note and staff articulation is distance0 + 0.5 spatium
qreal staffTopY = 0; // top of occupied area
qreal staffBotY = staff()->height(); // bottom of occupied area
2012-05-26 14:26:10 +02:00
if (stem()) { // if there is a stem, occupied area may be larger
2012-05-26 14:26:10 +02:00
#if 0
y = stem()->pos().y() + pos().y();
if (up() && stem()->stemLen() < 0.0)
y += stem()->stemLen();
else if (!up() && stem()->stemLen() > 0.0)
y -= stem()->stemLen();
#endif
y = stem()->hookPos().y() + pos().y(); // vert. pos. of end of stem
2012-05-26 14:26:10 +02:00
if (beam()) { // if there is a beam, stem end is further away
2012-05-26 14:26:10 +02:00
qreal bw = score()->styleS(ST_beamWidth).val() * _spatium;
y += up() ? -bw : bw;
}
if (up()) // if up chord, top is topmost between staff top and stem end
2012-05-26 14:26:10 +02:00
staffTopY = qMin(staffTopY, y);
else // if chord is down, bottom is bottommost btw staff bottom and stem end
2012-05-26 14:26:10 +02:00
staffBotY = qMax(staffBotY, y);
}
staffTopY = qMin(staffTopY, qreal(chordTopY)); // top is topmost between staff top and chord stop
staffBotY = qMax(staffBotY, qreal(chordBotY)); // bottom is bottom between staff bottom and chord bottom
2012-05-26 14:26:10 +02:00
//
// determine Direction
//
2012-08-04 15:46:43 +02:00
if (a->direction() != MScore::AUTO) {
a->setUp(a->direction() == MScore::UP);
2012-05-26 14:26:10 +02:00
}
else {
if (measure()->hasVoices(a->staffIdx())) {
a->setUp(up());
aa = up() ? A_TOP_STAFF : A_BOTTOM_STAFF;
}
else {
if (aa == A_CHORD)
a->setUp(!up());
else
a->setUp(aa == A_TOP_STAFF || aa == A_TOP_CHORD);
}
}
qreal dist; // distance between occupied area and articulation
2012-05-26 14:26:10 +02:00
switch(st) {
case Articulation_Marcato: dist = 1.0 * _spStaff; break;
case Articulation_Sforzatoaccent: dist = 1.5 * _spStaff; break;
default: dist = score()->styleS(ST_propertyDistance).val() * _spStaff;
2012-05-26 14:26:10 +02:00
}
if (aa == A_CHORD || aa == A_TOP_CHORD || aa == A_BOTTOM_CHORD) {
bool bottom;
if ((aa == A_CHORD) && measure()->hasVoices(a->staffIdx()))
bottom = !up();
else
bottom = (aa == A_BOTTOM_CHORD) || (aa == A_CHORD && up());
y = bottom ? chordBotY + dist : chordTopY - dist;
}
else if (aa == A_TOP_STAFF || aa == A_BOTTOM_STAFF) {
y = a->up() ? staffTopY - dist : staffBotY + dist;
}
a->setPos(x, y);
a->adjustReadPos();
return QPointF(x, y);
}
2012-08-23 17:40:04 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-08-23 17:40:04 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Chord::reset()
2012-08-23 17:40:04 +02:00
{
score()->undoChangeProperty(this, P_STEM_DIRECTION, int(MScore::AUTO));
score()->undoChangeProperty(this, P_BEAM_MODE, int(BeamMode::AUTO));
2013-07-01 16:28:39 +02:00
score()->createPlayEvents(this);
2012-11-19 10:08:15 +01:00
ChordRest::reset();
2012-08-23 17:40:04 +02:00
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// setStemSlash
//---------------------------------------------------------
void Chord::setStemSlash(StemSlash* s)
{
delete _stemSlash;
_stemSlash = s;
}
2013-05-28 15:42:02 +02:00
//---------------------------------------------------------
// mag
//---------------------------------------------------------
qreal Chord::mag() const
{
2013-05-28 16:55:02 +02:00
qreal m = staff() ? staff()->mag() : 1.0;
2013-05-28 15:42:02 +02:00
if (small())
m *= score()->styleD(ST_smallNoteMag);
if (_noteType != NOTE_NORMAL)
m *= score()->styleD(ST_graceNoteMag);
return m;
}
2013-06-10 21:13:04 +02:00
//---------------------------------------------------------
// segment
//---------------------------------------------------------
Segment* Chord::segment() const
{
Element* e = parent();
for (; e && e->type() != SEGMENT; e = e->parent())
;
return static_cast<Segment*>(e);
}
//---------------------------------------------------------
// measure
//---------------------------------------------------------
Measure* Chord::measure() const
{
Element* e = parent();
for (; e && e->type() != MEASURE; e = e->parent())
;
return static_cast<Measure*>(e);
}
2013-06-16 23:33:37 +02:00
2013-05-13 18:49:17 +02:00
}