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