1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2016 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 "global/log.h"
14 
15 #include "measure.h"
16 #include "score.h"
17 #include "system.h"
18 #include "undo.h"
19 #include "slurtie.h"
20 #include "tie.h"
21 #include "chord.h"
22 #include "page.h"
23 
24 namespace Ms {
25 
26 //---------------------------------------------------------
27 //   SlurTieSegment
28 //---------------------------------------------------------
29 
SlurTieSegment(Score * score)30 SlurTieSegment::SlurTieSegment(Score* score)
31    : SpannerSegment(score)
32       {
33       setFlag(ElementFlag::ON_STAFF, true);
34       }
35 
SlurTieSegment(const SlurTieSegment & b)36 SlurTieSegment::SlurTieSegment(const SlurTieSegment& b)
37    : SpannerSegment(b)
38       {
39       for (int i = 0; i < int(Grip::GRIPS); ++i) {
40             _ups[i]   = b._ups[i];
41             _ups[i].p = QPointF();
42             }
43       path = b.path;
44       }
45 
46 //---------------------------------------------------------
47 //   gripAnchorLines
48 //---------------------------------------------------------
49 
gripAnchorLines(Grip grip) const50 QVector<QLineF> SlurTieSegment::gripAnchorLines(Grip grip) const
51       {
52       QVector<QLineF> result;
53 
54       if (!system() || (grip != Grip::START && grip != Grip::END))
55             return result;
56 
57       QPointF sp(system()->pagePos());
58       QPointF pp(pagePos());
59       QPointF p1(ups(Grip::START).p + pp);
60       QPointF p2(ups(Grip::END).p + pp);
61 
62       QPointF anchorPosition;
63       int gripIndex = static_cast<int>(grip);
64 
65       switch (spannerSegmentType()) {
66             case SpannerSegmentType::SINGLE:
67                   anchorPosition = (grip == Grip::START ? p1 : p2);
68                   break;
69 
70             case SpannerSegmentType::BEGIN:
71                   anchorPosition = (grip == Grip::START ? p1 : system()->abbox().topRight());
72                   break;
73 
74             case SpannerSegmentType::MIDDLE:
75                   anchorPosition = (grip == Grip::START ? sp : system()->abbox().topRight());
76                   break;
77 
78             case SpannerSegmentType::END:
79                   anchorPosition = (grip == Grip::START ? sp : p2);
80                   break;
81             }
82 
83       const Page* p = system()->page();
84       const QPointF pageOffset = p ? p->pos() : QPointF();
85 
86       result << QLineF(anchorPosition, gripsPositions().at(gripIndex)).translated(pageOffset);
87 
88       return result;
89       }
90 
91 //---------------------------------------------------------
92 //   move
93 //---------------------------------------------------------
94 
move(const QPointF & s)95 void SlurTieSegment::move(const QPointF& s)
96       {
97       Element::move(s);
98       for (int k = 0; k < int(Grip::GRIPS); ++k)
99             _ups[k].p += s;
100       }
101 
102 //---------------------------------------------------------
103 //   spatiumChanged
104 //---------------------------------------------------------
105 
spatiumChanged(qreal oldValue,qreal newValue)106 void SlurTieSegment::spatiumChanged(qreal oldValue, qreal newValue)
107       {
108       Element::spatiumChanged(oldValue, newValue);
109       qreal diff = newValue / oldValue;
110       for (UP& u : _ups)
111             u.off *= diff;
112       }
113 
114 //---------------------------------------------------------
115 //   gripsPositions
116 //---------------------------------------------------------
117 
gripsPositions(const EditData &) const118 std::vector<QPointF> SlurTieSegment::gripsPositions(const EditData&) const
119       {
120       const int ngrips = gripsCount();
121       std::vector<QPointF> grips(ngrips);
122 
123       const QPointF p(pagePos());
124       for (int i = 0; i < ngrips; ++i)
125             grips[i] = _ups[i].p + _ups[i].off + p;
126 
127       return grips;
128       }
129 
130 //---------------------------------------------------------
131 //   startEditDrag
132 //---------------------------------------------------------
133 
startEditDrag(EditData & ed)134 void SlurTieSegment::startEditDrag(EditData& ed)
135       {
136       ElementEditData* eed = ed.getData(this);
137       IF_ASSERT_FAILED(eed) {
138             return;
139             }
140       for (auto i : { Pid::SLUR_UOFF1, Pid::SLUR_UOFF2, Pid::SLUR_UOFF3, Pid::SLUR_UOFF4, Pid::OFFSET })
141             eed->pushProperty(i);
142       }
143 
144 //---------------------------------------------------------
145 //   endEditDrag
146 //---------------------------------------------------------
147 
endEditDrag(EditData & ed)148 void SlurTieSegment::endEditDrag(EditData& ed)
149       {
150       Element::endEditDrag(ed);
151       triggerLayout();
152       }
153 
154 //---------------------------------------------------------
155 //   editDrag
156 //---------------------------------------------------------
157 
editDrag(EditData & ed)158 void SlurTieSegment::editDrag(EditData& ed)
159       {
160       Grip g     = ed.curGrip;
161       ups(g).off += ed.delta;
162 
163       QPointF delta;
164 
165       switch (g) {
166             case Grip::START:
167             case Grip::END:
168                   //
169                   // move anchor for slurs/ties
170                   //
171                   if ((g == Grip::START && isSingleBeginType()) || (g == Grip::END && isSingleEndType())) {
172                         Spanner* spanner = slurTie();
173                         Qt::KeyboardModifiers km = qApp->keyboardModifiers();
174                         Element* e = ed.view->elementNear(ed.pos);
175                         if (e && e->isNote()) {
176                               Note* note = toNote(e);
177                               Fraction tick = note->chord()->tick();
178                               if ((g == Grip::END && tick > slurTie()->tick()) || (g == Grip::START && tick < slurTie()->tick2())) {
179                                     if (km != (Qt::ShiftModifier | Qt::ControlModifier)) {
180                                           Chord* c = note->chord();
181                                           ed.view->setDropTarget(note);
182                                           if (c->part() == spanner->part() && c != spanner->endCR())
183                                                 changeAnchor(ed, c);
184                                           }
185                                     }
186                               }
187                         else
188                               ed.view->setDropTarget(0);
189                         }
190                   break;
191             case Grip::BEZIER1:
192                   break;
193             case Grip::BEZIER2:
194                   break;
195             case Grip::SHOULDER:
196                   ups(g).off = QPointF();
197                   delta = ed.delta;
198                   break;
199             case Grip::DRAG:
200                   ups(g).off = QPointF();
201                   setOffset(offset() + ed.delta);
202                   break;
203             case Grip::NO_GRIP:
204             case Grip::GRIPS:
205                   break;
206             }
207       computeBezier(delta);
208       }
209 
210 //---------------------------------------------------------
211 //   getProperty
212 //---------------------------------------------------------
213 
getProperty(Pid propertyId) const214 QVariant SlurTieSegment::getProperty(Pid propertyId) const
215       {
216       switch (propertyId) {
217             case Pid::LINE_TYPE:
218             case Pid::SLUR_DIRECTION:
219                   return slurTie()->getProperty(propertyId);
220             case Pid::SLUR_UOFF1:
221                   return ups(Grip::START).off;
222             case Pid::SLUR_UOFF2:
223                   return ups(Grip::BEZIER1).off;
224             case Pid::SLUR_UOFF3:
225                   return ups(Grip::BEZIER2).off;
226             case Pid::SLUR_UOFF4:
227                   return ups(Grip::END).off;
228             default:
229                   return SpannerSegment::getProperty(propertyId);
230             }
231       }
232 
233 //---------------------------------------------------------
234 //   setProperty
235 //---------------------------------------------------------
236 
setProperty(Pid propertyId,const QVariant & v)237 bool SlurTieSegment::setProperty(Pid propertyId, const QVariant& v)
238       {
239       switch(propertyId) {
240             case Pid::LINE_TYPE:
241             case Pid::SLUR_DIRECTION:
242                   return slurTie()->setProperty(propertyId, v);
243             case Pid::SLUR_UOFF1:
244                   ups(Grip::START).off = v.toPointF();
245                   break;
246             case Pid::SLUR_UOFF2:
247                   ups(Grip::BEZIER1).off = v.toPointF();
248                   break;
249             case Pid::SLUR_UOFF3:
250                   ups(Grip::BEZIER2).off = v.toPointF();
251                   break;
252             case Pid::SLUR_UOFF4:
253                   ups(Grip::END).off = v.toPointF();
254                   break;
255             default:
256                   return SpannerSegment::setProperty(propertyId, v);
257             }
258       triggerLayoutAll();
259       return true;
260       }
261 
262 //---------------------------------------------------------
263 //   propertyDefault
264 //---------------------------------------------------------
265 
propertyDefault(Pid id) const266 QVariant SlurTieSegment::propertyDefault(Pid id) const
267       {
268       switch (id) {
269             case Pid::LINE_TYPE:
270             case Pid::SLUR_DIRECTION:
271                   return slurTie()->propertyDefault(id);
272             case Pid::SLUR_UOFF1:
273             case Pid::SLUR_UOFF2:
274             case Pid::SLUR_UOFF3:
275             case Pid::SLUR_UOFF4:
276                   return QPointF();
277             default:
278                   return SpannerSegment::propertyDefault(id);
279             }
280       }
281 
282 //---------------------------------------------------------
283 //   reset
284 //---------------------------------------------------------
285 
reset()286 void SlurTieSegment::reset()
287       {
288       Element::reset();
289       undoResetProperty(Pid::SLUR_UOFF1);
290       undoResetProperty(Pid::SLUR_UOFF2);
291       undoResetProperty(Pid::SLUR_UOFF3);
292       undoResetProperty(Pid::SLUR_UOFF4);
293       slurTie()->reset();
294       }
295 
296 //---------------------------------------------------------
297 //   undoChangeProperty
298 //---------------------------------------------------------
299 
undoChangeProperty(Pid pid,const QVariant & val,PropertyFlags ps)300 void SlurTieSegment::undoChangeProperty(Pid pid, const QVariant& val, PropertyFlags ps)
301       {
302       if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
303             // Switching autoplacement on. Save user-defined
304             // placement properties to undo stack.
305             undoPushProperty(Pid::SLUR_UOFF1);
306             undoPushProperty(Pid::SLUR_UOFF2);
307             undoPushProperty(Pid::SLUR_UOFF3);
308             undoPushProperty(Pid::SLUR_UOFF4);
309             // other will be saved in base classes.
310             }
311       SpannerSegment::undoChangeProperty(pid, val, ps);
312       }
313 
314 //---------------------------------------------------------
315 //   writeProperties
316 //---------------------------------------------------------
317 
writeSlur(XmlWriter & xml,int no) const318 void SlurTieSegment::writeSlur(XmlWriter& xml, int no) const
319       {
320       if (visible() && autoplace()
321          && (color() == Qt::black)
322          && offset().isNull()
323          && ups(Grip::START).off.isNull()
324          && ups(Grip::BEZIER1).off.isNull()
325          && ups(Grip::BEZIER2).off.isNull()
326          && ups(Grip::END).off.isNull()
327          )
328             return;
329 
330       xml.stag(this, QString("no=\"%1\"").arg(no));
331 
332       qreal _spatium = score()->spatium();
333       if (!ups(Grip::START).off.isNull())
334             xml.tag("o1", ups(Grip::START).off / _spatium);
335       if (!ups(Grip::BEZIER1).off.isNull())
336             xml.tag("o2", ups(Grip::BEZIER1).off / _spatium);
337       if (!ups(Grip::BEZIER2).off.isNull())
338             xml.tag("o3", ups(Grip::BEZIER2).off / _spatium);
339       if (!ups(Grip::END).off.isNull())
340             xml.tag("o4", ups(Grip::END).off / _spatium);
341       Element::writeProperties(xml);
342       xml.etag();
343       }
344 
345 //---------------------------------------------------------
346 //   readSegment
347 //---------------------------------------------------------
348 
read(XmlReader & e)349 void SlurTieSegment::read(XmlReader& e)
350       {
351       qreal _spatium = score()->spatium();
352       while (e.readNextStartElement()) {
353             const QStringRef& tag(e.name());
354             if (tag == "o1")
355                   ups(Grip::START).off = e.readPoint() * _spatium;
356             else if (tag == "o2")
357                   ups(Grip::BEZIER1).off = e.readPoint() * _spatium;
358             else if (tag == "o3")
359                   ups(Grip::BEZIER2).off = e.readPoint() * _spatium;
360             else if (tag == "o4")
361                   ups(Grip::END).off = e.readPoint() * _spatium;
362             else if (!Element::readProperties(e))
363                   e.unknown();
364             }
365       }
366 
367 //---------------------------------------------------------
368 //   drawEditMode
369 //---------------------------------------------------------
370 
drawEditMode(QPainter * p,EditData & ed)371 void SlurTieSegment::drawEditMode(QPainter* p, EditData& ed)
372       {
373       QPolygonF polygon(7);
374       polygon[0] = QPointF(ed.grip[int(Grip::START)].center());
375       polygon[1] = QPointF(ed.grip[int(Grip::BEZIER1)].center());
376       polygon[2] = QPointF(ed.grip[int(Grip::SHOULDER)].center());
377       polygon[3] = QPointF(ed.grip[int(Grip::BEZIER2)].center());
378       polygon[4] = QPointF(ed.grip[int(Grip::END)].center());
379       polygon[5] = QPointF(ed.grip[int(Grip::DRAG)].center());
380       polygon[6] = QPointF(ed.grip[int(Grip::START)].center());
381       p->setPen(QPen(MScore::frameMarginColor, 0.0));
382       p->drawPolyline(polygon);
383 
384       p->setPen(QPen(MScore::defaultColor, 0.0));
385       for (int i = 0; i < ed.grips; ++i) {
386             // This must be done with an if-else statement rather than a ternary operator.
387             // This is because there are two setBrush methods that take different types
388             // of argument, either a Qt::BrushStyle or a QBrush. Since a QBrush can be
389             // constructed from a QColour, passing Mscore::frameMarginColor works.
390             // Qt::NoBrush is a Qt::BrushStyle, however, so if it is passed in a ternary
391             // operator with a QColor, a new QColor will be created from it, and from that
392             // a QBrush. Instead, what we really want to do is pass Qt::NoBrush as a
393             // Qt::BrushStyle, therefore this requires two separate function calls:
394             if (Grip(i) == ed.curGrip)
395                   p->setBrush(MScore::frameMarginColor);
396             else
397                   p->setBrush(Qt::NoBrush);
398             p->drawRect(ed.grip[i]);
399             }
400       }
401 
402 //---------------------------------------------------------
403 //   SlurTie
404 //---------------------------------------------------------
405 
SlurTie(Score * s)406 SlurTie::SlurTie(Score* s)
407    : Spanner(s)
408       {
409       _slurDirection = Direction::AUTO;
410       _up            = true;
411       _lineType      = 0;     // default is solid
412       }
413 
SlurTie(const SlurTie & t)414 SlurTie::SlurTie(const SlurTie& t)
415    : Spanner(t)
416       {
417       _up            = t._up;
418       _slurDirection = t._slurDirection;
419       _lineType      = t._lineType;
420       }
421 
422 //---------------------------------------------------------
423 //   SlurTie
424 //---------------------------------------------------------
425 
~SlurTie()426 SlurTie::~SlurTie()
427       {
428       }
429 
430 //---------------------------------------------------------
431 //   writeProperties
432 //---------------------------------------------------------
433 
writeProperties(XmlWriter & xml) const434 void SlurTie::writeProperties(XmlWriter& xml) const
435       {
436       Element::writeProperties(xml);
437       int idx = 0;
438       for (const SpannerSegment* ss : spannerSegments())
439             ((SlurTieSegment*)ss)->writeSlur(xml, idx++);
440       writeProperty(xml, Pid::SLUR_DIRECTION);
441       writeProperty(xml, Pid::LINE_TYPE);
442       }
443 
444 //---------------------------------------------------------
445 //   readProperties
446 //---------------------------------------------------------
447 
readProperties(XmlReader & e)448 bool SlurTie::readProperties(XmlReader& e)
449       {
450       const QStringRef& tag(e.name());
451 
452       if (readProperty(tag, e, Pid::SLUR_DIRECTION))
453             ;
454       else if (tag == "lineType")
455             _lineType = e.readInt();
456       else if (tag == "SlurSegment" || tag == "TieSegment") {
457             const int idx = e.intAttribute("no", 0);
458             const int n = int(spannerSegments().size());
459             for (int i = n; i < idx; ++i)
460                   add(newSlurTieSegment());
461             SlurTieSegment* s = newSlurTieSegment();
462             s->read(e);
463             add(s);
464             }
465       else if (!Element::readProperties(e))
466             return false;
467       return true;
468       }
469 
470 //---------------------------------------------------------
471 //   read
472 //---------------------------------------------------------
473 
read(XmlReader & e)474 void SlurTie::read(XmlReader& e)
475       {
476       while (e.readNextStartElement()) {
477             if (!SlurTie::readProperties(e))
478                   e.unknown();
479             }
480       }
481 
482 //---------------------------------------------------------
483 //   undoSetLineType
484 //---------------------------------------------------------
485 
undoSetLineType(int t)486 void SlurTie::undoSetLineType(int t)
487       {
488       undoChangeProperty(Pid::LINE_TYPE, t);
489       }
490 
491 //---------------------------------------------------------
492 //   undoSetSlurDirection
493 //---------------------------------------------------------
494 
undoSetSlurDirection(Direction d)495 void SlurTie::undoSetSlurDirection(Direction d)
496       {
497       undoChangeProperty(Pid::SLUR_DIRECTION, QVariant::fromValue<Direction>(d));
498       }
499 
500 //---------------------------------------------------------
501 //   getProperty
502 //---------------------------------------------------------
503 
getProperty(Pid propertyId) const504 QVariant SlurTie::getProperty(Pid propertyId) const
505       {
506       switch (propertyId) {
507             case Pid::LINE_TYPE:
508                   return lineType();
509             case Pid::SLUR_DIRECTION:
510                   return QVariant::fromValue<Direction>(slurDirection());
511             default:
512                   return Spanner::getProperty(propertyId);
513             }
514       }
515 
516 //---------------------------------------------------------
517 //   setProperty
518 //---------------------------------------------------------
519 
setProperty(Pid propertyId,const QVariant & v)520 bool SlurTie::setProperty(Pid propertyId, const QVariant& v)
521       {
522       switch (propertyId) {
523             case Pid::LINE_TYPE:
524                   setLineType(v.toInt());
525                   break;
526             case Pid::SLUR_DIRECTION:
527                   setSlurDirection(v.value<Direction>());
528                   break;
529             default:
530                   return Spanner::setProperty(propertyId, v);
531             }
532       triggerLayoutAll();
533       return true;
534       }
535 
536 //---------------------------------------------------------
537 //   propertyDefault
538 //---------------------------------------------------------
539 
propertyDefault(Pid id) const540 QVariant SlurTie::propertyDefault(Pid id) const
541       {
542       switch (id) {
543             case Pid::LINE_TYPE:
544                   return 0;
545             case Pid::SLUR_DIRECTION:
546                   return QVariant::fromValue<Direction>(Direction::AUTO);
547             default:
548                   return Spanner::propertyDefault(id);
549             }
550       }
551 
552 //---------------------------------------------------------
553 //   fixupSegments
554 //---------------------------------------------------------
555 
fixupSegments(unsigned nsegs)556 void SlurTie::fixupSegments(unsigned nsegs)
557       {
558       Spanner::fixupSegments(nsegs, [this]() { return newSlurTieSegment(); });
559       }
560 
561 //---------------------------------------------------------
562 //   reset
563 //---------------------------------------------------------
564 
reset()565 void SlurTie::reset()
566       {
567       Element::reset();
568       undoResetProperty(Pid::SLUR_DIRECTION);
569       undoResetProperty(Pid::LINE_TYPE);
570       }
571 
572 }
573 
574