1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2013 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "measure.h"
14 #include "score.h"
15 #include "system.h"
16 #include "undo.h"
17 #include "chord.h"
18 #include "tie.h"
19
20 namespace Ms {
21
22 Note* Tie::editStartNote;
23 Note* Tie::editEndNote;
24
25 //---------------------------------------------------------
26 // draw
27 //---------------------------------------------------------
28
draw(QPainter * painter) const29 void TieSegment::draw(QPainter* painter) const
30 {
31 // hide tie toward the second chord of a cross-measure value
32 if (tie()->endNote() && tie()->endNote()->chord()->crossMeasure() == CrossMeasure::SECOND)
33 return;
34
35 QPen pen(curColor());
36 qreal mag = staff() ? staff()->mag(tie()->tick()) : 1.0;
37
38 //Replace generic Qt dash patterns with improved equivalents to show true dots (keep in sync with slur.cpp)
39 QVector<qreal> dotted = { 0.01, 1.99 }; // tighter than Qt DotLine equivalent - woud be { 0.01, 2.99 }
40 QVector<qreal> dashed = { 3.00, 3.00 }; // Compensating for caps. Qt default DashLine is { 4.0, 2.0 }
41 QVector<qreal> wideDashed = { 5.00, 6.00 };
42
43 switch (slurTie()->lineType()) {
44 case 0:
45 painter->setBrush(QBrush(pen.color()));
46 pen.setCapStyle(Qt::RoundCap);
47 pen.setJoinStyle(Qt::RoundJoin);
48 pen.setWidthF(score()->styleP(Sid::SlurEndWidth) * mag);
49 break;
50 case 1:
51 painter->setBrush(Qt::NoBrush);
52 pen.setCapStyle(Qt::RoundCap); // True dots
53 pen.setDashPattern(dotted);
54 pen.setWidthF(score()->styleP(Sid::SlurDottedWidth) * mag);
55 break;
56 case 2:
57 painter->setBrush(Qt::NoBrush);
58 pen.setDashPattern(dashed);
59 pen.setWidthF(score()->styleP(Sid::SlurDottedWidth) * mag);
60 break;
61 case 3:
62 painter->setBrush(Qt::NoBrush);
63 pen.setDashPattern(wideDashed);
64 pen.setWidthF(score()->styleP(Sid::SlurDottedWidth) * mag);
65 break;
66 }
67 painter->setPen(pen);
68 painter->drawPath(path);
69 }
70
71 //---------------------------------------------------------
72 // edit
73 // return true if event is accepted
74 //---------------------------------------------------------
75
edit(EditData & ed)76 bool TieSegment::edit(EditData& ed)
77 {
78 SlurTie* sl = tie();
79
80 if (ed.key == Qt::Key_X) {
81 sl->setSlurDirection(sl->up() ? Direction::DOWN : Direction::UP);
82 sl->layout();
83 return true;
84 }
85 if (ed.key == Qt::Key_Home) {
86 ups(ed.curGrip).off = QPointF();
87 sl->layout();
88 return true;
89 }
90 return false;
91 }
92
93 //---------------------------------------------------------
94 // changeAnchor
95 //---------------------------------------------------------
96
changeAnchor(EditData & ed,Element * element)97 void TieSegment::changeAnchor(EditData& ed, Element* element)
98 {
99 if (ed.curGrip == Grip::START) {
100 spanner()->setStartElement(element);
101 Note* note = toNote(element);
102 if (note->chord()->tick() <= tie()->endNote()->chord()->tick()) {
103 tie()->startNote()->setTieFor(0);
104 tie()->setStartNote(note);
105 note->setTieFor(tie());
106 }
107 }
108 else {
109 spanner()->setEndElement(element);
110 Note* note = toNote(element);
111 // do not allow backward ties
112 if (note->chord()->tick() >= tie()->startNote()->chord()->tick()) {
113 tie()->endNote()->setTieBack(0);
114 tie()->setEndNote(note);
115 note->setTieBack(tie());
116 }
117 }
118
119 const size_t segments = spanner()->spannerSegments().size();
120 ups(ed.curGrip).off = QPointF();
121 spanner()->layout();
122 if (spanner()->spannerSegments().size() != segments) {
123 const std::vector<SpannerSegment*>& ss = spanner()->spannerSegments();
124
125 TieSegment* newSegment = toTieSegment(ed.curGrip == Grip::END ? ss.back() : ss.front());
126 score()->endCmd();
127 score()->startCmd();
128 ed.view->startEdit(newSegment, ed.curGrip);
129 triggerLayoutAll();
130 }
131 }
132
133 //---------------------------------------------------------
134 // editDrag
135 //---------------------------------------------------------
136
editDrag(EditData & ed)137 void TieSegment::editDrag(EditData& ed)
138 {
139 Grip g = ed.curGrip;
140 ups(g).off += ed.delta;
141
142 if (g == Grip::START || g == Grip::END) {
143 computeBezier();
144 //
145 // move anchor for slurs/ties
146 //
147 if ((g == Grip::START && isSingleBeginType()) || (g == Grip::END && isSingleEndType())) {
148 Spanner* spanner = tie();
149 Element* e = ed.view->elementNear(ed.pos);
150 Note* note = (e && e->isNote()) ? toNote(e) : nullptr;
151 if (note && ((g == Grip::END && note->tick() > tie()->tick()) || (g == Grip::START && note->tick() < tie()->tick2()))) {
152 if (g == Grip::END) {
153 Tie* tie = toTie(spanner);
154 if (tie->startNote()->pitch() == note->pitch()
155 && tie->startNote()->chord()->tick() < note->chord()->tick()) {
156 ed.view->setDropTarget(note);
157 if (note != tie->endNote()) {
158 changeAnchor(ed, note);
159 return;
160 }
161 }
162 }
163 }
164 else
165 ed.view->setDropTarget(0);
166 }
167 }
168 else if (g == Grip::BEZIER1 || g == Grip::BEZIER2)
169 computeBezier();
170 else if (g == Grip::SHOULDER) {
171 ups(g).off = QPointF();
172 computeBezier(ed.delta);
173 }
174 else if (g == Grip::DRAG) {
175 ups(Grip::DRAG).off = QPointF();
176 roffset() += ed.delta;
177 }
178
179 // if this SlurSegment was automatically adjusted to avoid collision
180 // lock this edit by resetting SlurSegment to default position
181 // and incorporating previous adjustment into user offset
182 QPointF offset = getAutoAdjust();
183 if (!offset.isNull()) {
184 setAutoAdjust(0.0, 0.0);
185 roffset() += offset;
186 }
187 }
188
189 //---------------------------------------------------------
190 // computeBezier
191 // compute help points of slur bezier segment
192 //---------------------------------------------------------
193
computeBezier(QPointF p6o)194 void TieSegment::computeBezier(QPointF p6o)
195 {
196 qreal _spatium = spatium();
197 qreal shoulderW; // height as fraction of slur-length
198 qreal shoulderH;
199
200 //
201 // pp1 start of slur
202 // pp2 end of slur
203 // pp3 bezier 1
204 // pp4 bezier 2
205 // pp5 drag
206 // pp6 shoulder
207 //
208 QPointF pp1 = ups(Grip::START).p + ups(Grip::START).off;
209 QPointF pp2 = ups(Grip::END).p + ups(Grip::END).off;
210
211 QPointF p2 = pp2 - pp1; // normalize to zero
212 if (p2.x() == 0.0) {
213 qDebug("zero tie");
214 return;
215 }
216
217 qreal sinb = atan(p2.y() / p2.x());
218 QTransform t;
219 t.rotateRadians(-sinb);
220 p2 = t.map(p2);
221 p6o = t.map(p6o);
222
223 double smallH = 0.38;
224 qreal d = p2.x() / _spatium;
225 shoulderH = d * 0.4 * smallH;
226 shoulderH = qBound(0.4, shoulderH, 1.3);
227 shoulderH *= _spatium;
228 shoulderW = .6;
229
230 shoulderH -= p6o.y();
231
232 if (!tie()->up())
233 shoulderH = -shoulderH;
234
235 qreal c = p2.x();
236 qreal c1 = (c - c * shoulderW) * .5 + p6o.x();
237 qreal c2 = c1 + c * shoulderW + p6o.x();
238
239 QPointF p5 = QPointF(c * .5, 0.0);
240
241 QPointF p3(c1, -shoulderH);
242 QPointF p4(c2, -shoulderH);
243
244 qreal w = score()->styleP(Sid::SlurMidWidth) - score()->styleP(Sid::SlurEndWidth);
245 if (staff())
246 w *= staff()->mag(tie()->tick());
247 QPointF th(0.0, w); // thickness of slur
248
249 QPointF p3o = p6o + t.map(ups(Grip::BEZIER1).off);
250 QPointF p4o = p6o + t.map(ups(Grip::BEZIER2).off);
251
252 if(!p6o.isNull()) {
253 QPointF p6i = t.inverted().map(p6o);
254 ups(Grip::BEZIER1).off += p6i ;
255 ups(Grip::BEZIER2).off += p6i;
256 }
257
258 //-----------------------------------calculate p6
259 QPointF pp3 = p3 + p3o;
260 QPointF pp4 = p4 + p4o;
261 QPointF ppp4 = pp4 - pp3;
262
263 qreal r2 = atan(ppp4.y() / ppp4.x());
264 t.reset();
265 t.rotateRadians(-r2);
266 QPointF p6 = QPointF(t.map(ppp4).x() * .5, 0.0);
267
268 t.rotateRadians(2 * r2);
269 p6 = t.map(p6) + pp3 - p6o;
270 //-----------------------------------
271
272 path = QPainterPath();
273 path.moveTo(QPointF());
274 path.cubicTo(p3 + p3o - th, p4 + p4o - th, p2);
275 if (tie()->lineType() == 0)
276 path.cubicTo(p4 +p4o + th, p3 + p3o + th, QPointF());
277
278 th = QPointF(0.0, 3.0 * w);
279 shapePath = QPainterPath();
280 shapePath.moveTo(QPointF());
281 shapePath.cubicTo(p3 + p3o - th, p4 + p4o - th, p2);
282 shapePath.cubicTo(p4 +p4o + th, p3 + p3o + th, QPointF());
283
284 // translate back
285 double y = pp1.y();
286 const double offsetFactor = 0.2;
287 if (staff()->isTabStaff(slurTie()->tick()))
288 y += (_spatium * (slurTie()->up() ? -offsetFactor : offsetFactor));
289 t.reset();
290 t.translate(pp1.x(), y);
291 t.rotateRadians(sinb);
292 path = t.map(path);
293 shapePath = t.map(shapePath);
294 ups(Grip::BEZIER1).p = t.map(p3);
295 ups(Grip::BEZIER2).p = t.map(p4);
296 ups(Grip::END).p = t.map(p2) - ups(Grip::END).off;
297 ups(Grip::DRAG).p = t.map(p5);
298 ups(Grip::SHOULDER).p = t.map(p6);
299
300 // QPointF staffOffset;
301 // if (system() && track() >= 0)
302 // staffOffset = QPointF(0.0, -system()->staff(staffIdx())->y());
303
304 // path.translate(staffOffset);
305 // shapePath.translate(staffOffset);
306
307 _shape.clear();
308 QPointF start;
309 start = t.map(start);
310
311 qreal minH = qAbs(3.0 * w);
312 int nbShapes = 15;
313 const CubicBezier b(pp1, ups(Grip::BEZIER1).pos(), ups(Grip::BEZIER2).pos(), ups(Grip::END).pos());
314 for (int i = 1; i <= nbShapes; i++) {
315 const QPointF point = b.pointAtPercent(i/float(nbShapes));
316 QRectF re = QRectF(start, point).normalized();
317 if (re.height() < minH) {
318 d = (minH - re.height()) * .5;
319 re.adjust(0.0, -d, 0.0, d);
320 }
321 _shape.add(re);
322 start = point;
323 }
324 }
325
326 //---------------------------------------------------------
327 // layout
328 // p1, p2 are in System coordinates
329 //---------------------------------------------------------
330
layoutSegment(const QPointF & p1,const QPointF & p2)331 void TieSegment::layoutSegment(const QPointF& p1, const QPointF& p2)
332 {
333 setPos(QPointF());
334 ups(Grip::START).p = p1;
335 ups(Grip::END).p = p2;
336
337 //Adjust Y pos to staff type offset before other calculations
338 if (staffType())
339 rypos() += staffType()->yoffset().val() * spatium();
340
341 computeBezier();
342
343 QRectF bbox = path.boundingRect();
344
345 // adjust position to avoid staff line if necessary
346 Staff* st = staff();
347 bool reverseAdjust = false;
348
349 if (slurTie()->isTie() && st && !st->isTabStaff(slurTie()->tick())) {
350 // multinote chords with ties need special handling
351 // otherwise, adjusted tie might crowd an unadjusted tie unnecessarily
352 Tie* t = toTie(slurTie());
353 Note* sn = t->startNote();
354 t->setTick(t->startNote()->tick());
355 Chord* sc = sn ? sn->chord() : 0;
356
357 // normally, the adjustment moves ties according to their direction (eg, up if tie is up)
358 // but we will reverse this for notes within chords when appropriate
359 // for two-note chords, it looks better to have notes on spaces tied outside the lines
360
361 if (sc) {
362 size_t notes = sc->notes().size();
363 bool onLine = !(sn->line() & 1);
364 if ((onLine && notes > 1) || (!onLine && notes > 2))
365 reverseAdjust = true;
366 }
367 }
368 qreal sp = spatium();
369 qreal minDistance = 0.5;
370 autoAdjustOffset = QPointF();
371 if (bbox.height() < minDistance * 2 * sp && st && !st->isTabStaff(slurTie()->tick())) {
372 // slur/tie is fairly flat
373 bool up = slurTie()->up();
374 qreal ld = st->lineDistance(tick()) * sp;
375 qreal topY = bbox.top() / ld;
376 qreal bottomY = bbox.bottom() / ld;
377 int lineY = up ? qRound(topY) : qRound(bottomY);
378 if (lineY >= 0 && lineY < st->lines(tick()) * st->lineDistance(tick())) {
379 // on staff
380 if (qAbs(topY - lineY) < minDistance && qAbs(bottomY - lineY) < minDistance) {
381 // too close to line
382 if (!isNudged() && !isEdited()) {
383 // user has not nudged or edited
384 qreal offY;
385 if (up != reverseAdjust) // exclusive or
386 offY = (lineY - minDistance) - topY;
387 else
388 offY = (lineY + minDistance) - bottomY;
389 setAutoAdjust(0.0, offY * sp);
390 }
391 }
392 }
393 }
394 setbbox(path.boundingRect());
395 }
396
397 //---------------------------------------------------------
398 // setAutoAdjust
399 //---------------------------------------------------------
400
setAutoAdjust(const QPointF & offset)401 void TieSegment::setAutoAdjust(const QPointF& offset)
402 {
403 QPointF diff = offset - autoAdjustOffset;
404 if (!diff.isNull()) {
405 path.translate(diff);
406 shapePath.translate(diff);
407 _shape.translate(diff);
408 for (int i = 0; i < int(Grip::GRIPS); ++i)
409 _ups[i].p += diff;
410 autoAdjustOffset = offset;
411 }
412 }
413
414 //---------------------------------------------------------
415 // isEdited
416 //---------------------------------------------------------
417
isEdited() const418 bool TieSegment::isEdited() const
419 {
420 for (int i = 0; i < int(Grip::GRIPS); ++i) {
421 if (!_ups[i].off.isNull())
422 return true;
423 }
424 return false;
425 }
426
427 //---------------------------------------------------------
428 // slurPos
429 // Calculate position of start- and endpoint of slur
430 // relative to System() position.
431 //---------------------------------------------------------
432
slurPos(SlurPos * sp)433 void Tie::slurPos(SlurPos* sp)
434 {
435 bool useTablature = staff() && staff()->isTabStaff(tick());
436 const StaffType* stt = useTablature ? staff()->staffType(tick()) : 0;
437 qreal _spatium = spatium();
438 qreal hw = startNote()->tabHeadWidth(stt); // if stt == 0, defaults to headWidth()
439 qreal __up = _up ? -1.0 : 1.0;
440 // y offset for ties inside chord margins (typically multi-note chords): lined up with note top or bottom margin
441 // or outside (typically single-note chord): overlaps note and is above/below it
442 // Outside: Tab: uses font size and may be asymmetric placed above/below line (frets ON or ABOVE line)
443 // Std: assumes notehead is 1 sp high, 1/2 sp above and 1/2 below line; add 1/4 sp to it
444 // Inside: Tab: 1/2 of Outside offset
445 // Std: use a fixed percentage of note width
446 qreal yOffOutside = useTablature
447 ? (_up ? stt->fretBoxY() : stt->fretBoxY() + stt->fretBoxH()) * magS()
448 : 0.75 * _spatium * __up;
449 qreal yOffInside = useTablature ? yOffOutside * 0.5 : hw * .3 * __up;
450
451 Chord* sc = startNote()->chord();
452 sp->system1 = sc->measure()->system();
453 if (!sp->system1) {
454 Measure* m = sc->measure();
455 qDebug("No system: measure is %d has %d count %d", m->isMMRest(), m->hasMMRest(), m->mmRestCount());
456 }
457
458 qreal xo;
459 qreal yo;
460 bool shortStart = false;
461
462 // determine attachment points
463 // similar code is used in Chord::layoutPitched()
464 // to allocate extra space to enforce minTieLength
465 // so keep these in sync
466
467 sp->p1 = sc->pos() + sc->segment()->pos() + sc->measure()->pos();
468
469 //------p1
470 if ((sc->notes().size() > 1) || (sc->stem() && (sc->up() == _up))) {
471 xo = startNote()->x() + hw * 1.12;
472 yo = startNote()->pos().y() + yOffInside;
473 shortStart = true;
474 }
475 else {
476 xo = startNote()->x() + hw * 0.65;
477 yo = startNote()->pos().y() + yOffOutside;
478 }
479 sp->p1 += QPointF(xo, yo);
480
481 //------p2
482 if (endNote() == 0) {
483 sp->p2 = sp->p1 + QPointF(_spatium * 3, 0.0);
484 sp->system2 = sp->system1;
485 return;
486 }
487 Chord* ec = endNote()->chord();
488 sp->p2 = ec->pos() + ec->segment()->pos() + ec->measure()->pos();
489 sp->system2 = ec->measure()->system();
490
491 // force tie to be horizontal except for cross-staff or if there is a difference of line (tpc, clef, tpc)
492 bool horizontal = startNote()->line() == endNote()->line() && sc->vStaffIdx() == ec->vStaffIdx();
493
494 hw = endNote()->tabHeadWidth(stt);
495 if ((ec->notes().size() > 1) || (ec->stem() && !ec->up() && !_up)) {
496 xo = endNote()->x() - hw * 0.12;
497 if (!horizontal)
498 yo = endNote()->pos().y() + yOffInside;
499 }
500 else if (shortStart) {
501 xo = endNote()->x() + hw * 0.15;
502 if (!horizontal)
503 yo = endNote()->pos().y() + yOffOutside;
504 }
505 else {
506 xo = endNote()->x() + hw * 0.35;
507 if (!horizontal)
508 yo = endNote()->pos().y() + yOffOutside;
509 }
510 sp->p2 += QPointF(xo, yo);
511
512 // adjust for cross-staff
513 if (sc->vStaffIdx() != vStaffIdx() && sp->system1) {
514 qreal diff = sp->system1->staff(sc->vStaffIdx())->y() - sp->system1->staff(vStaffIdx())->y();
515 sp->p1.ry() += diff;
516 }
517 if (ec->vStaffIdx() != vStaffIdx() && sp->system2) {
518 qreal diff = sp->system2->staff(ec->vStaffIdx())->y() - sp->system2->staff(vStaffIdx())->y();
519 sp->p2.ry() += diff;
520 }
521 }
522
523 //---------------------------------------------------------
524 // Tie
525 //---------------------------------------------------------
526
Tie(Score * s)527 Tie::Tie(Score* s)
528 : SlurTie(s)
529 {
530 setAnchor(Anchor::NOTE);
531 }
532
533 //---------------------------------------------------------
534 // write
535 //---------------------------------------------------------
536
write(XmlWriter & xml) const537 void Tie::write(XmlWriter& xml) const
538 {
539 xml.stag(this);
540 SlurTie::writeProperties(xml);
541 xml.etag();
542 }
543
544 //---------------------------------------------------------
545 // calculateDirection
546 //---------------------------------------------------------
547
compareNotesPos(const Note * n1,const Note * n2)548 static int compareNotesPos(const Note* n1, const Note* n2)
549 {
550 if (n1->line() != n2->line()) {
551 return n2->line() - n1->line();
552 } else if (n1->string() != n2->string()) {
553 return n2->string() - n1->string();
554 } else {
555 return n1->pitch() - n2->pitch();
556 }
557 }
558
calculateDirection()559 void Tie::calculateDirection()
560 {
561 Chord* c1 = startNote()->chord();
562 Chord* c2 = endNote()->chord();
563 Measure* m1 = c1->measure();
564 Measure* m2 = c2->measure();
565
566 if (_slurDirection == Direction::AUTO) {
567 std::vector<Note*> notes = c1->notes();
568 size_t n = notes.size();
569 if (m1->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks()) || m2->hasVoices(c2->staffIdx(), c2->tick(), c2->actualTicks())) {
570 // in polyphonic passage, ties go on the stem side
571 _up = c1->up();
572 }
573 else if (n == 1) {
574 //
575 // single note
576 //
577 if (c1->up() != c2->up()) {
578 // if stem direction is mixed, always up
579 _up = true;
580 }
581 else
582 _up = !c1->up();
583 }
584 else {
585 //
586 // chords
587 //
588 int tiesCount = 0;
589 Note* tieNote = startNote();
590 Note* tieAbove = nullptr;
591 Note* tieBelow = nullptr;
592
593 for (size_t i = 0; i < n; ++i) {
594 if (notes[i]->tieFor()) {
595 tiesCount++;
596 int noteDiff = compareNotesPos(notes[i], tieNote);
597
598 if (noteDiff > 0) {
599 if (!tieAbove)
600 tieAbove = notes[i];
601 else if (compareNotesPos(notes[i], tieAbove) < 0)
602 tieAbove = notes[i];
603 }
604 else if (noteDiff < 0) {
605 if (!tieBelow)
606 tieBelow = notes[i];
607 else if (compareNotesPos(notes[i], tieBelow) > 0)
608 tieBelow = notes[i];
609 }
610 }
611 }
612 if (!tieBelow)
613 // bottom tie is up if it is the only tie and not the bottom note of the chord
614 _up = tiesCount == 1 && tieNote != c1->downNote();
615 else if (!tieAbove)
616 // top tie always up
617 _up = true;
618 else {
619 bool tabStaff = onTabStaff();
620 int tieLine = tabStaff ? tieNote->string() : tieNote->line();
621 int belowLine = tabStaff ? tieBelow->string() : tieBelow->line();
622 int aboveLine = tabStaff ? tieAbove->string() : tieAbove->line();
623
624 if (tieLine <= (tabStaff ? 2 : 4))
625 _up = ((belowLine - tieLine) <= 1) || ((tieLine - aboveLine) > 1);
626 else
627 _up = ((belowLine - tieLine) <= 1) && ((tieLine - aboveLine) > 1);
628 }
629 }
630 }
631 else
632 _up = _slurDirection == Direction::UP ? true : false;
633 }
634
635 //---------------------------------------------------------
636 // layoutFor
637 // layout the first SpannerSegment of a slur
638 //---------------------------------------------------------
639
layoutFor(System * system)640 TieSegment* Tie::layoutFor(System* system)
641 {
642 // do not layout ties in tablature if not showing back-tied fret marks
643 StaffType* st = staff()->staffType(startNote() ? startNote()->tick() : Fraction(0, 1));
644 if (st && st->isTabStaff() && !st->showBackTied()) {
645 if (!segmentsEmpty())
646 eraseSpannerSegments();
647 return nullptr;
648 }
649 //
650 // show short bow
651 //
652 if (startNote() == 0 || endNote() == 0) {
653 if (startNote() == 0) {
654 qDebug("no start note");
655 return 0;
656 }
657 Chord* c1 = startNote()->chord();
658 if (_slurDirection == Direction::AUTO) {
659 if (c1->measure()->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks())) {
660 // in polyphonic passage, ties go on the stem side
661 _up = c1->up();
662 }
663 else
664 _up = !c1->up();
665 }
666 else
667 _up = _slurDirection == Direction::UP ? true : false;
668 fixupSegments(1);
669 TieSegment* segment = segmentAt(0);
670 segment->setSpannerSegmentType(SpannerSegmentType::SINGLE);
671 segment->setSystem(startNote()->chord()->segment()->measure()->system());
672 SlurPos sPos;
673 slurPos(&sPos);
674 segment->layoutSegment(sPos.p1, sPos.p2);
675 return segment;
676 }
677 calculateDirection();
678
679 SlurPos sPos;
680 slurPos(&sPos);
681
682 setPos(0, 0);
683
684 int n;
685 if (sPos.system1 != sPos.system2) {
686 n = 2;
687 sPos.p2 = QPointF(system->width(), sPos.p1.y());
688 }
689 else
690 n = 1;
691
692 fixupSegments(n);
693 TieSegment* segment = segmentAt(0);
694 segment->setSystem(system); // Needed to populate System.spannerSegments
695 segment->layoutSegment(sPos.p1, sPos.p2);
696 segment->setSpannerSegmentType(sPos.system1 != sPos.system2 ? SpannerSegmentType::BEGIN : SpannerSegmentType::SINGLE);
697 return segment;
698 }
699
700 //---------------------------------------------------------
701 // layoutBack
702 // layout the second SpannerSegment of a split slur
703 //---------------------------------------------------------
704
layoutBack(System * system)705 TieSegment* Tie::layoutBack(System* system)
706 {
707 // do not layout ties in tablature if not showing back-tied fret marks
708 StaffType* st = staff()->staffType(startNote() ? startNote()->tick() : Fraction(0, 1));
709 if (st->isTabStaff() && !st->showBackTied()) {
710 if (!segmentsEmpty())
711 eraseSpannerSegments();
712 return nullptr;
713 }
714
715 SlurPos sPos;
716 slurPos(&sPos);
717
718 fixupSegments(2);
719 TieSegment* segment = segmentAt(1);
720 segment->setSystem(system);
721
722 qreal x = system->firstNoteRestSegmentX(true);
723
724 segment->layoutSegment(QPointF(x, sPos.p2.y()), sPos.p2);
725 segment->setSpannerSegmentType(SpannerSegmentType::END);
726 return segment;
727 }
728
729 #if 0
730 //---------------------------------------------------------
731 // startEdit
732 //---------------------------------------------------------
733
734 void Tie::startEdit(EditData& ed)
735 {
736 printf("tie start edit %p %p\n", editStartNote, editEndNote);
737 editStartNote = startNote();
738 editEndNote = endNote();
739 SlurTie::startEdit(ed);
740 }
741
742 //---------------------------------------------------------
743 // endEdit
744 //---------------------------------------------------------
745
746 void Tie::endEdit(EditData& ed)
747 {
748 //printf("tie::endEdit\n");
749 // if (editStartNote != startNote() || editEndNote != endNote()) {
750 // score()->undoStack()->push1(new ChangeSpannerElements(this, editStartNote, editEndNote));
751 // }
752 SlurTie::endEdit(ed);
753 }
754 #endif
755
756 //---------------------------------------------------------
757 // setStartNote
758 //---------------------------------------------------------
759
setStartNote(Note * note)760 void Tie::setStartNote(Note* note)
761 {
762 setStartElement(note);
763 setParent(note);
764 }
765
766 //---------------------------------------------------------
767 // startNote
768 //---------------------------------------------------------
769
startNote() const770 Note* Tie::startNote() const
771 {
772 Q_ASSERT(!startElement() || startElement()->type() == ElementType::NOTE);
773 return toNote(startElement());
774 }
775
776 //---------------------------------------------------------
777 // endNote
778 //---------------------------------------------------------
779
endNote() const780 Note* Tie::endNote() const
781 {
782 return toNote(endElement());
783 }
784 }
785
786