Element* Rest::drop(const DropData& data) { Element* e = data.element; switch (e->type()) { case ARTICULATION: if (e->subtype() == Articulation_Fermata) score()->addArticulation(this, (Articulation*)e); return 0; case ICON: { switch(e->subtype()) { case ICON_SBEAM: score()->undoChangeBeamMode(this, BEAM_BEGIN); break; case ICON_MBEAM: score()->undoChangeBeamMode(this, BEAM_MID); break; case ICON_NBEAM: score()->undoChangeBeamMode(this, BEAM_NO); break; case ICON_BEAM32: score()->undoChangeBeamMode(this, BEAM_BEGIN32); break; case ICON_BEAM64: score()->undoChangeBeamMode(this, BEAM_BEGIN64); break; case ICON_AUTOBEAM: score()->undoChangeBeamMode(this, BEAM_AUTO); break; } } delete e; break; case CHORD: { Chord* c = static_cast<Chord*>(e); Note* n = c->upNote(); Direction dir = c->stemDirection(); score()->select(0, SELECT_SINGLE, 0); NoteVal nval; nval.pitch = n->pitch(); nval.headGroup = n->headGroup(); Fraction d = score()->inputState().duration().fraction(); if (!d.isZero()) { Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir); if (seg) { ChordRest* cr = static_cast<ChordRest*>(seg->element(track())); if (cr) score()->nextInputPos(cr, true); } } delete e; } break; default: return ChordRest::drop(data); } return 0; }
QLineF Arpeggio::dragAnchor() const { Chord* c = chord(); if (c) return QLineF(pagePos(), c->upNote()->pagePos()); return QLineF(); }
void Glissando::layout() { Chord* chord = static_cast<Chord*>(parent()); if (chord == 0) { return; } Note* anchor2 = chord->upNote(); Segment* s = chord->segment(); s = s->prev1(); while (s) { if ((s->subtype() & (Segment::SegChordRestGrace)) && s->element(track())) break; s = s->prev1(); } if (s == 0) { qDebug("no segment for first note of glissando found\n"); return; } ChordRest* cr = static_cast<ChordRest*>(s->element(track())); if (cr == 0 || cr->type() != CHORD) { qDebug("no first note for glissando found, track %d\n", track()); return; } Note* anchor1 = static_cast<Chord*>(cr)->upNote(); setPos(0.0, 0.0); QPointF cp1 = anchor1->pagePos(); QPointF cp2 = anchor2->pagePos(); // construct line from notehead to notehead qreal x1 = (anchor1->headWidth()) - (cp2.x() - cp1.x()); qreal y1 = anchor1->pos().y(); qreal x2 = anchor2->pos().x(); qreal y2 = anchor2->pos().y(); // on TAB's, adjust lower end point from string line height to base of note height (= ca. half line spacing) if (chord->staff()->isTabStaff()) { qreal yOff = chord->staff()->lineDistance() * 0.5 * spatium(); if (anchor1->pitch() > anchor2->pitch()) // descending glissando: y2 += yOff; // move ending point to base of note else // ascending glissando: y1 += yOff; // move starting point to base of note } QLineF fullLine(x1, y1, x2, y2); // shorten line on each side by offsets qreal xo = spatium() * .5; qreal yo = xo; // spatium() * .5; QPointF p1 = fullLine.pointAt(xo / fullLine.length()); QPointF p2 = fullLine.pointAt(1 - (yo / fullLine.length())); line = QLineF(p1, p2); qreal lw = spatium() * .15 * .5; QRectF r = QRectF(line.p1(), line.p2()).normalized(); setbbox(r.adjusted(-lw, -lw, lw, lw)); }
void TestNote::grace() { Score* score = readScore(DIR + "grace.mscx"); score->doLayout(); Chord* chord = score->firstMeasure()->findChord(0, 0); Note* note = chord->upNote(); // create score->setGraceNote(chord, note->pitch(), NoteType::APPOGGIATURA, MScore::division/2); Chord* gc = chord->graceNotes().first(); Note* gn = gc->notes().first(); // Note* n = static_cast<Note*>(writeReadElement(gn)); // QCOMPARE(n->noteType(), NoteType::APPOGGIATURA); // delete n; // tie score->startCmd(); score->select(gn); score->cmdAddTie(); score->endCmd(); // n = static_cast<Note*>(writeReadElement(gn)); // QVERIFY(n->tieFor() != 0); // delete n; // tremolo score->startCmd(); Tremolo* tr = new Tremolo(score); tr->setTremoloType(TremoloType::R16); tr->setParent(gc); tr->setTrack(gc->track()); score->undoAddElement(tr); score->endCmd(); // Chord* c = static_cast<Chord*>(writeReadElement(gc)); // QVERIFY(c->tremolo() != 0); // delete c; // articulation score->startCmd(); Articulation* ar = new Articulation(score); ar->setArticulationType(ArticulationType::Sforzatoaccent); ar->setParent(gc); ar->setTrack(gc->track()); score->undoAddElement(ar); score->endCmd(); // c = static_cast<Chord*>(writeReadElement(gc)); // QVERIFY(c->articulations().size() == 1); // delete c; QVERIFY(saveCompareScore(score, "grace-test.mscx", DIR + "grace-ref.mscx")); }
void Glissando::layout() { Chord* chord = static_cast<Chord*>(parent()); if (chord == 0) { return; } Note* anchor2 = chord->upNote(); Segment* s = chord->segment(); s = s->prev1(); while (s) { if ((s->subtype() == SegChordRest || s->subtype() == SegGrace) && s->element(track())) break; s = s->prev1(); } if (s == 0) { qDebug("no segment for first note of glissando found\n"); return; } ChordRest* cr = static_cast<ChordRest*>(s->element(track())); if (cr == 0 || cr->type() != CHORD) { qDebug("no first note for glissando found, track %d\n", track()); return; } Note* anchor1 = static_cast<Chord*>(cr)->upNote(); setPos(0.0, 0.0); QPointF cp1 = anchor1->pagePos(); QPointF cp2 = anchor2->pagePos(); // construct line from notehead to notehead qreal x1 = (anchor1->headWidth()) - (cp2.x() - cp1.x()); qreal y1 = anchor1->pos().y(); qreal x2 = anchor2->pos().x(); qreal y2 = anchor2->pos().y(); QLineF fullLine(x1, y1, x2, y2); // shorten line on each side by offsets qreal xo = spatium() * .5; qreal yo = xo; // spatium() * .5; QPointF p1 = fullLine.pointAt(xo / fullLine.length()); QPointF p2 = fullLine.pointAt(1 - (yo / fullLine.length())); line = QLineF(p1, p2); qreal lw = spatium() * .15 * .5; QRectF r = QRectF(line.p1(), line.p2()).normalized(); setbbox(r.adjusted(-lw, -lw, lw, lw)); }
Element* Rest::drop(const DropData& data) { Element* e = data.element; switch (e->type()) { case ElementType::ARTICULATION: { Articulation* a = static_cast<Articulation*>(e); if (a->articulationType() != ArticulationType::Fermata || !score()->addArticulation(this, a)) { delete e; e = 0; } } return e; case ElementType::CHORD: { Chord* c = static_cast<Chord*>(e); Note* n = c->upNote(); Direction dir = c->stemDirection(); // score()->select(0, SelectType::SINGLE, 0); NoteVal nval; nval.pitch = n->pitch(); nval.headGroup = n->headGroup(); Fraction d = score()->inputState().duration().fraction(); if (!d.isZero()) { Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir); if (seg) { ChordRest* cr = static_cast<ChordRest*>(seg->element(track())); if (cr) score()->nextInputPos(cr, true); } } delete e; } break; case ElementType::REPEAT_MEASURE: delete e; if (durationType().type() == TDuration::DurationType::V_MEASURE) { measure()->cmdInsertRepeatMeasure(staffIdx()); } break; default: return ChordRest::drop(data); } return 0; }
QPointF Arpeggio::gripAnchor(int n) const { Chord* c = chord(); if (c == 0) return QPointF(); if (n == 0) return c->upNote()->pagePos(); else if (n == 1) { Note* dnote = c->downNote(); int btrack = track() + (_span - 1) * VOICES; ChordRest* bchord = static_cast<ChordRest*>(c->segment()->element(btrack)); if (bchord && bchord->type() == CHORD) dnote = static_cast<Chord*>(bchord)->downNote(); return dnote->pagePos(); } return QPointF(); }
Element* Rest::drop(const DropData& data) { Element* e = data.element; switch (e->type()) { case ARTICULATION: { Articulation* a = static_cast<Articulation*>(e); if (a->articulationType() != Articulation_Fermata || !score()->addArticulation(this, a)) { delete e; e = 0; } } return e; case CHORD: { Chord* c = static_cast<Chord*>(e); Note* n = c->upNote(); MScore::Direction dir = c->stemDirection(); // score()->select(0, SELECT_SINGLE, 0); NoteVal nval; nval.pitch = n->pitch(); nval.headGroup = n->headGroup(); Fraction d = score()->inputState().duration().fraction(); if (!d.isZero()) { Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir); if (seg) { ChordRest* cr = static_cast<ChordRest*>(seg->element(track())); if (cr) score()->nextInputPos(cr, true); } } delete e; } break; default: return ChordRest::drop(data); } return 0; }
void Stem::layout() { qreal l = _len + _userLen; if (up()) l = -l; Staff* st = staff(); qreal lw5 = point(score()->styleS(ST_stemWidth)) * .5; QPointF p1(0.0, 0.0); QPointF p2(0.0, l); if (st) { // if TAB, use simplified positioning if (st->isTabStaff()) { p1.rx() = -lw5; p2.rx() = -lw5; } // for any other staff type, use standard positioning else if (chord()) { // adjust P1 for note head Chord* c = chord(); if (c->up()) { Note* n = c->downNote(); p1 = symbols[score()->symIdx()][n->noteHead()].attach(n->magS()); p1.rx() = -lw5; p2.rx() = -lw5; } else { Note* n = c->upNote(); p1 = -symbols[score()->symIdx()][n->noteHead()].attach(n->magS()); p1.rx() = lw5; p2.rx() = lw5; } } } line.setP1(p1); line.setP2(p2); // compute bounding rectangle QRectF r(line.p1(), line.p2()); setbbox(r.normalized().adjusted(-lw5, -lw5, lw5, lw5)); }
void Slur::slurPos(SlurPos* sp) { qreal _spatium = spatium(); Element* e1 = startElement(); Element* e2 = endElement(); if (e2 == 0) { sp->p1 = e1->pagePos(); sp->p1.rx() += e1->width(); sp->p2 = sp->p1; sp->p2.rx() += 5 * _spatium; sp->system1 = static_cast<ChordRest*>(e1)->measure()->system(); sp->system2 = sp->system1; return; } if ((e1->type() != CHORD) || (e2->type() != CHORD)) { sp->p1 = e1->pagePos(); sp->p2 = e2->pagePos(); sp->p1.rx() += e1->width(); sp->p2.rx() += e2->width(); sp->system1 = static_cast<ChordRest*>(e1)->measure()->system(); sp->system2 = static_cast<ChordRest*>(e2)->measure()->system(); return; } Chord* sc = static_cast<Chord*>(e1); Chord* ec = static_cast<Chord*>(e2); Note* note1 = _up ? sc->upNote() : sc->downNote(); Note* note2 = _up ? ec->upNote() : ec->downNote(); sp->system1 = sc->measure()->system(); sp->system2 = ec->measure()->system(); sp->p1 = sc->pagePos() - sp->system1->pagePos(); sp->p2 = ec->pagePos() - sp->system2->pagePos(); qreal xo, yo; Stem* stem1 = sc->stem(); Stem* stem2 = ec->stem(); enum SlurAnchor { SA_NONE, SA_STEM }; SlurAnchor sa1 = SA_NONE; SlurAnchor sa2 = SA_NONE; if ((sc->up() == ec->up()) && !sc->beam() && !ec->beam() && (_up == sc->up())) { if (stem1) sa1 = SA_STEM; if (stem2) sa2 = SA_STEM; } qreal __up = _up ? -1.0 : 1.0; qreal hw = note1->headWidth(); switch (sa1) { case SA_STEM: sp->p1 += sc->stemPosBeam() - sc->pagePos() + sc->stem()->p2(); sp->p1 += QPointF(0.35 * _spatium, 0.25 * _spatium); break; case SA_NONE: break; } switch(sa2) { case SA_STEM: sp->p2 += ec->stemPosBeam() - ec->pagePos() + ec->stem()->p2(); sp->p2 += QPointF(-0.35 * _spatium, 0.25 * _spatium); break; case SA_NONE: break; } // // default position: // horizontal: middle of note head // vertical: _spatium * .4 above/below note head // //------p1 bool stemPos = false; // p1 starts at chord stem side yo = note1->pos().y() + _spatium * .9 * __up; xo = hw * .5; if (stem1) { Beam* beam1 = sc->beam(); if (beam1 && (beam1->elements().back() != sc) && (sc->up() == _up)) { qreal sh = stem1->height() + _spatium; yo = sc->downNote()->pos().y() + sh * __up; xo = stem1->pos().x(); stemPos = true; } else { if (sc->up() && _up) xo = hw + _spatium * .3; // // handle case: stem up - stem down // stem down - stem up // if ((sc->up() != ec->up()) && (sc->up() == _up)) { Note* n1 = sc->up() ? sc->downNote() : sc->upNote(); Note* n2 = ec->up() ? ec->downNote() : ec->upNote(); qreal yd = n2->pos().y() - n1->pos().y(); yd *= .5; qreal sh = stem1->height(); // limit y move if (yd > 0.0) { if (yd > sh) yd = sh; } else { if (yd < - sh) yd = -sh; } stemPos = true; if ((_up && (yd < -_spatium)) || (!_up && (yd > _spatium))) yo += yd; } else if (sc->up() != _up) yo = fixArticulations(yo, sc, __up); } } if (sa1 == SA_NONE) sp->p1 += QPointF(xo, yo); //------p2 xo = hw * .5; yo = note2->pos().y() + _spatium * .9 * __up; if (stem2) { Beam* beam2 = ec->beam(); if ((stemPos && (sc->up() == ec->up())) || (beam2 && (!beam2->elements().isEmpty()) && (beam2->elements().front() != ec) && (ec->up() == _up) && (sc->noteType() == NOTE_NORMAL) ) ) { qreal sh = stem2->height() + _spatium; if (_up) yo = ec->downNote()->pos().y() - sh; else yo = ec->upNote()->pos().y() + sh; xo = stem2->pos().x(); } else if (!ec->up() && !_up) xo = -_spatium * .3 + note2->x(); // // handle case: stem up - stem down // stem down - stem up // if ((sc->up() != ec->up()) && (ec->up() == _up)) { Note* n1 = sc->up() ? sc->downNote() : sc->upNote(); Note* n2 = ec->up() ? ec->downNote() : ec->upNote(); qreal yd = n2->pos().y() - n1->pos().y(); yd *= .5; qreal mh = stem2->height(); // limit y move if (yd > 0.0) { if (yd > mh) yd = mh; } else { if (yd < - mh) yd = -mh; } if ((_up && (yd > _spatium)) || (!_up && (yd < -_spatium))) yo -= yd; } else if (ec->up() != _up) yo = fixArticulations(yo, ec, __up); } if (sa2 == SA_NONE) sp->p2 += QPointF(xo, yo); }
void Fingering::layout() { if (parent()) { Fraction tick = parent()->tick(); const Staff* st = staff(); if (st && st->isTabStaff(tick) && !st->staffType(tick)->showTabFingering()) { setbbox(QRectF()); return; } } TextBase::layout(); rypos() = 0.0; // handle placement below if (autoplace() && note()) { Note* n = note(); Chord* chord = n->chord(); bool voices = chord->measure()->hasVoices(chord->staffIdx()); bool tight = voices && chord->notes().size() == 1 && !chord->beam() && tid() != Tid::STRING_NUMBER; qreal headWidth = n->bboxRightPos(); // update offset after drag qreal rebase = 0.0; if (offsetChanged() != OffsetChange::NONE) rebase = rebaseOffset(); // temporarily exclude self from chord shape setAutoplace(false); if (layoutType() == ElementType::CHORD) { Stem* stem = chord->stem(); Segment* s = chord->segment(); Measure* m = s->measure(); qreal sp = spatium(); qreal md = minDistance().val() * sp; SysStaff* ss = m->system()->staff(chord->vStaffIdx()); Staff* vStaff = chord->staff(); // TODO: use current height at tick if (n->mirror()) rxpos() -= n->ipos().x(); rxpos() += headWidth * .5; if (placeAbove()) { if (tight) { if (chord->stem()) rxpos() -= 0.8 * sp; rypos() -= 1.5 * sp; } else { QRectF r = bbox().translated(m->pos() + s->pos() + chord->pos() + n->pos() + pos()); SkylineLine sk(false); sk.add(r.x(), r.bottom(), r.width()); qreal d = sk.minDistance(ss->skyline().north()); qreal yd = 0.0; if (d > 0.0 && isStyled(Pid::MIN_DISTANCE)) yd -= d + height() * .25; // force extra space above staff & chord (but not other fingerings) qreal top; if (chord->up() && chord->beam() && stem) { top = stem->y() + stem->bbox().top(); } else { Note* un = chord->upNote(); top = qMin(0.0, un->y() + un->bbox().top()); } top -= md; qreal diff = (bbox().bottom() + ipos().y() + yd + n->y()) - top; if (diff > 0.0) yd -= diff; if (offsetChanged() != OffsetChange::NONE) { // user moved element within the skyline // we may need to adjust minDistance, yd, and/or offset bool inStaff = placeAbove() ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height(); rebaseMinDistance(md, yd, sp, rebase, inStaff); } rypos() += yd; } } else { if (tight) { if (chord->stem()) rxpos() += 0.8 * sp; rypos() += 1.5 * sp; } else { QRectF r = bbox().translated(m->pos() + s->pos() + chord->pos() + n->pos() + pos()); SkylineLine sk(true); sk.add(r.x(), r.top(), r.width()); qreal d = ss->skyline().south().minDistance(sk); qreal yd = 0.0; if (d > 0.0 && isStyled(Pid::MIN_DISTANCE)) yd += d + height() * .25; // force extra space below staff & chord (but not other fingerings) qreal bottom; if (!chord->up() && chord->beam() && stem) { bottom = stem->y() + stem->bbox().bottom(); } else { Note* dn = chord->downNote(); bottom = qMax(vStaff->height(), dn->y() + dn->bbox().bottom()); } bottom += md; qreal diff = bottom - (bbox().top() + ipos().y() + yd + n->y()); if (diff > 0.0) yd += diff; if (offsetChanged() != OffsetChange::NONE) { // user moved element within the skyline // we may need to adjust minDistance, yd, and/or offset bool inStaff = placeAbove() ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height(); rebaseMinDistance(md, yd, sp, rebase, inStaff); } rypos() += yd; } } } else if (tid() == Tid::LH_GUITAR_FINGERING) { // place to left of note qreal left = n->shape().left(); if (left - n->x() > 0.0) rxpos() -= left; else rxpos() -= n->x(); } // for other fingering styles, do not autoplace // restore autoplace setAutoplace(true); } else if (offsetChanged() != OffsetChange::NONE) { // rebase horizontally too, as autoplace may have adjusted it rebaseOffset(false); } setOffsetChanged(false); }
Note* Glissando::guessFinalNote(Chord* chord) { switch (chord->noteType()) { // case NoteType::INVALID: // return nullptr; // for grace notes before, return top note of parent chord // TODO : if the grace-before is not the LAST ONE, this still returns the main note // which is probably not correct; however a glissando between two grace notes // probably makes little sense. case NoteType::ACCIACCATURA: case NoteType::APPOGGIATURA: case NoteType::GRACE4: case NoteType::GRACE16: case NoteType::GRACE32: if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) return static_cast<Chord*>(chord->parent())->upNote(); else // no parent or parent is not a chord? return nullptr; // for grace notes after, next chord is next chord of parent chord // TODO : same note as case above! case NoteType::GRACE8_AFTER: case NoteType::GRACE16_AFTER: case NoteType::GRACE32_AFTER: // move unto parent chord and proceed to standard case if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) chord = static_cast<Chord*>(chord->parent()); else return nullptr; break; case NoteType::NORMAL: { // if chord has grace notes after, the first one is the next note QVector<Chord*>graces = chord->graceNotesAfter(); if (graces.size() > 0) return graces.first()->upNote(); } break; default: break; } // standard case (NORMAL or grace after chord) // if parent not a segment, can't locate a target note if (chord->parent()->type() != Element::Type::SEGMENT) return nullptr; // look for first ChordRest segment after initial note is elapsed Segment* segm = chord->score()->tick2rightSegment(chord->tick() + chord->actualTicks()); int chordTrack = chord->track(); Part* part = chord->part(); while (segm) { // if next segment is a ChordRest segment if (segm->segmentType() == Segment::Type::ChordRest) { Chord* target = nullptr; // look for a Chord in the same track if (segm->element(chordTrack) && segm->element(chordTrack)->type() == Element::Type::CHORD) target = static_cast<Chord*>(segm->element(chordTrack)); else // if no same track, look for other chords in the same instrument for (Element* currChord : segm->elist()) if (currChord != nullptr && currChord->type() == Element::Type::CHORD && static_cast<Chord*>(currChord)->part() == part) { target = static_cast<Chord*>(currChord); break; } // if we found a target next chord if (target) { // if chord has grace notes before, the first one is the next note QVector<Chord*>graces = target->graceNotesBefore(); if (graces.size() > 0) return graces.first()->upNote(); return target->upNote(); // if no grace before, return top note } } segm = segm->next1(); } qDebug("no second note for glissando found"); return nullptr; }
Note* Glissando::guessInitialNote(Chord* chord) { switch (chord->noteType()) { // case NoteType::INVALID: // return nullptr; // for grace notes before, previous chord is previous chord of parent chord case NoteType::ACCIACCATURA: case NoteType::APPOGGIATURA: case NoteType::GRACE4: case NoteType::GRACE16: case NoteType::GRACE32: // move unto parent chord and proceed to standard case if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) chord = static_cast<Chord*>(chord->parent()); else return nullptr; break; // for grace notes after, return top note of parent chord case NoteType::GRACE8_AFTER: case NoteType::GRACE16_AFTER: case NoteType::GRACE32_AFTER: if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) return static_cast<Chord*>(chord->parent())->upNote(); else // no parent or parent is not a chord? return nullptr; case NoteType::NORMAL: { // if chord has grace notes before, the last one is the previous note QVector<Chord*>graces = chord->graceNotesBefore(); if (graces.size() > 0) return graces.last()->upNote(); } break; // else process to standard case default: break; } // standard case (NORMAL or grace before chord) // if parent not a segment, can't locate a target note if (chord->parent()->type() != Element::Type::SEGMENT) return nullptr; int chordTrack = chord->track(); Segment* segm = chord->segment(); Part* part = chord->part(); if (segm != nullptr) segm = segm->prev1(); while (segm) { // if previous segment is a ChordRest segment if (segm->segmentType() == Segment::Type::ChordRest) { Chord* target = nullptr; // look for a Chord in the same track if (segm->element(chordTrack) && segm->element(chordTrack)->type() == Element::Type::CHORD) target = static_cast<Chord*>(segm->element(chordTrack)); else // if no same track, look for other chords in the same instrument for (Element* currChord : segm->elist()) if (currChord != nullptr && currChord->type() == Element::Type::CHORD && static_cast<Chord*>(currChord)->part() == part) { target = static_cast<Chord*>(currChord); break; } // if we found a target previous chord if (target) { // if chord has grace notes after, the last one is the previous note QVector<Chord*>graces = target->graceNotesAfter(); if (graces.size() > 0) return graces.last()->upNote(); return target->upNote(); // if no grace after, return top note } } segm = segm->prev1(); } qDebug("no first note for glissando found"); return nullptr; }
void Glissando::layout() { Chord* chord = static_cast<Chord*>(parent()); if (chord == 0) return; Note* anchor2 = chord->upNote(); Segment* s = chord->segment(); s = s->prev1(); while (s) { if ((s->segmentType() & (Segment::SegChordRest)) && s->element(track())) break; s = s->prev1(); } if (s == 0) { qDebug("no segment for first note of glissando found\n"); return; } ChordRest* cr = static_cast<ChordRest*>(s->element(track())); if (cr == 0 || cr->type() != CHORD) { qDebug("no first note for glissando found, track %d", track()); return; } qreal _spatium = spatium(); Note* anchor1 = static_cast<Chord*>(cr)->upNote(); setPos(0.0, 0.0); adjustReadPos(); QPointF cp1 = anchor1->pagePos(); QPointF cp2 = anchor2->pagePos(); // line starting point int dots = static_cast<Chord*>(cr)->dots(); LedgerLine * ledLin = static_cast<Chord*>(cr)->ledgerLines(); // if dots, from right of last dot (assume a standard dot with of 1/4 sp) // if no dots, from right of ledger line, if any; from right of note head, if no ledger line qreal x1 = (dots ? anchor1->dot(dots-1)->pos().x() + anchor1->dot(dots-1)->width() : (ledLin ? ledLin->pos().x() + ledLin->width() : anchor1->headWidth()) ) - (cp2.x() - cp1.x()); // make relative to end note qreal y1 = anchor1->pos().y(); // line end point: left of note head qreal x2 = anchor2->pos().x(); qreal y2 = anchor2->pos().y(); // angle glissando between notes with the same pitch letter if (anchor1->line() == anchor2->line()) { int upDown = anchor2->pitch() - anchor1->pitch(); if (upDown != 0) upDown /= abs(upDown); y1 += _spatium * 0.25 * upDown; y2 -= _spatium * 0.25 * upDown; } // on TAB's, adjust lower end point from string line height to base of note height (= ca. half line spacing) if (chord->staff()->isTabStaff()) { qreal yOff = chord->staff()->lineDistance() * 0.3 * _spatium; if (anchor1->pitch() > anchor2->pitch()) { // descending glissando: y2 += yOff; y1 -= yOff; } // move ending point to base of note else { // ascending glissando: y1 += yOff; // move starting point to base of note y2 -= yOff; } } // shorten line to avoid end note ledger line ledLin=anchor2->chord()->ledgerLines(); if (ledLin) x2 = ledLin->pos().x(); // shorten line so it doesn't go through end note accidental or arpeggio if (Accidental* a = anchor2->accidental()) { x2 = a->pos().x() + a->userOff().x(); } if (Arpeggio* a = chord->arpeggio()) { x2 = a->pos().x() + a->userOff().x(); } QLineF fullLine(x1, y1, x2, y2); // shorten line on each side by offsets qreal xo = _spatium * .5; qreal yo = xo; // spatium() * .5; QPointF p1 = fullLine.pointAt(xo / fullLine.length()); QPointF p2 = fullLine.pointAt(1 - (yo / fullLine.length())); line = QLineF(p1, p2); qreal lw = _spatium * .15 * .5; QRectF r = QRectF(line.p1(), line.p2()).normalized(); setbbox(r.adjusted(-lw, -lw, lw, lw)); }
void Glissando::layout() { Chord* chord = static_cast<Chord*>(parent()); if (chord == 0) return; Note* anchor2 = chord->upNote(); Segment* s2 = chord->segment(); Segment* s1 = s2->prev1(); while (s1) { if ((s1->segmentType() & (Segment::Type::ChordRest)) && s1->element(track())) break; s1 = s1->prev1(); } if (s1 == 0) { qDebug("no segment for first note of glissando found"); return; } ChordRest* cr = static_cast<ChordRest*>(s1->element(track())); if (cr == 0 || cr->type() != Element::Type::CHORD) { qDebug("no first note for glissando found, track %d", track()); return; } qreal _spatium = spatium(); Note* anchor1 = static_cast<Chord*>(cr)->upNote(); setPos(0.0, 0.0); adjustReadPos(); // since line will be drawn relative to end note, // calculate offsets for start note coordinates relative to end note qreal x1off = 0.0; qreal y1off = 0.0; QPointF cp1 = anchor1->pagePos(); QPointF cp2 = anchor2->pagePos(); // layout of glissandi happens before we have staff positions within the system // so these "page" positions are not accurate across different staves // cheap partial fix for cross-staff glissandi: adjust vertical position according to difference in staffMove int moveDiff = anchor2->chord()->staffMove() - anchor1->chord()->staffMove(); if (moveDiff) y1off = moveDiff * 4.0 * _spatium; // now calculate offsets if (s1->system() == s2->system()) { // normal case - start and end note in same system x1off = cp2.x() - cp1.x(); if (!moveDiff) y1off = cp2.y() - cp1.y(); } else { // cheap partial fix for cross system glissandi: just draw a short line into end note // TODO: draw line coming out of start note on previous system x1off = 4.0 * _spatium; if (!moveDiff) y1off = anchor2->pos().y() - anchor1->pos().y(); } // line starting point int dots = static_cast<Chord*>(cr)->dots(); LedgerLine * ledLin = static_cast<Chord*>(cr)->ledgerLines(); // if dots, from right of last dot (assume a standard dot with of 1/4 sp) // if no dots, from right of ledger line, if any; from right of note head, if no ledger line qreal x1 = (dots && anchor1->dot(dots-1) ? anchor1->dot(dots-1)->pos().x() + anchor1->dot(dots-1)->width() : (ledLin ? ledLin->pos().x() + ledLin->width() : anchor1->headWidth()) ) - x1off; // make relative to end note qreal y1 = anchor2->y() - y1off; // line end point: left of note head qreal x2 = anchor2->pos().x(); qreal y2 = anchor2->pos().y(); // angle glissando between notes with the same pitch letter if (anchor1->line() == anchor2->line()) { int upDown = anchor2->pitch() - anchor1->pitch(); if (upDown != 0) upDown /= abs(upDown); y1 += _spatium * 0.25 * upDown; y2 -= _spatium * 0.25 * upDown; } // on TAB's, adjust lower end point from string line height to base of note height (= ca. half line spacing) if (chord->staff()->isTabStaff()) { qreal yOff = chord->staff()->lineDistance() * 0.3 * _spatium; if (anchor1->pitch() > anchor2->pitch()) { // descending glissando: y2 += yOff; y1 -= yOff; } // move ending point to base of note else { // ascending glissando: y1 += yOff; // move starting point to base of note y2 -= yOff; } } // shorten line to avoid end note ledger line ledLin = anchor2->chord()->ledgerLines(); if (ledLin) x2 = ledLin->pos().x(); // shorten line so it doesn't go through end note accidental or arpeggio if (Accidental* a = anchor2->accidental()) { x2 = a->pos().x() + a->userOff().x(); } if (Arpeggio* a = chord->arpeggio()) { x2 = a->pos().x() + a->userOff().x(); } QLineF fullLine(x1, y1, x2, y2); // shorten line on each side by offsets qreal xo = _spatium * .5; qreal yo = xo; // spatium() * .5; QPointF p1 = fullLine.pointAt(xo / fullLine.length()); QPointF p2 = fullLine.pointAt(1 - (yo / fullLine.length())); line = QLineF(p1, p2); qreal lw = _spatium * .15 * .5; QRectF r = QRectF(line.p1(), line.p2()).normalized(); setbbox(r.adjusted(-lw, -lw, lw, lw)); }