1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-2012 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 "rest.h"
14 #include "score.h"
15 #include "xml.h"
16 #include "style.h"
17 #include "utils.h"
18 #include "tuplet.h"
19 #include "sym.h"
20 #include "stafftext.h"
21 #include "articulation.h"
22 #include "chord.h"
23 #include "note.h"
24 #include "measure.h"
25 #include "undo.h"
26 #include "staff.h"
27 #include "harmony.h"
28 #include "segment.h"
29 #include "stafftype.h"
30 #include "icon.h"
31 #include "image.h"
32 
33 namespace Ms {
34 
35 //---------------------------------------------------------
36 //   restStyle
37 //---------------------------------------------------------
38 
39 static const ElementStyle restStyle {
40       { Sid::mmRestNumberPos, Pid::MMREST_NUMBER_POS },
41       };
42 
43 //---------------------------------------------------------
44 //    Rest
45 //--------------------------------------------------------
46 
Rest(Score * s)47 Rest::Rest(Score* s)
48   : ChordRest(s)
49       {
50       _mmWidth   = 0;
51       _beamMode  = Beam::Mode::NONE;
52       _sym       = SymId::restQuarter;
53       if (score())
54             initElementStyle(&restStyle);
55       }
56 
Rest(Score * s,const TDuration & d)57 Rest::Rest(Score* s, const TDuration& d)
58   : ChordRest(s)
59       {
60       _mmWidth   = 0;
61       _beamMode  = Beam::Mode::NONE;
62       _sym       = SymId::restQuarter;
63       setDurationType(d);
64       if (d.fraction().isValid())
65             setTicks(d.fraction());
66       if (score())
67             initElementStyle(&restStyle);
68       }
69 
Rest(const Rest & r,bool link)70 Rest::Rest(const Rest& r, bool link)
71    : ChordRest(r, link)
72       {
73       if (link) {
74             score()->undo(new Link(this, const_cast<Rest*>(&r)));
75             setAutoplace(true);
76             }
77       _gap     = r._gap;
78       _sym     = r._sym;
79       dotline  = r.dotline;
80       _mmWidth = r._mmWidth;
81       for (NoteDot* dot : r._dots)
82             add(new NoteDot(*dot));
83       }
84 
85 //---------------------------------------------------------
86 //   Rest::draw
87 //---------------------------------------------------------
88 
draw(QPainter * painter) const89 void Rest::draw(QPainter* painter) const
90       {
91       const StaffType* stt = staff() ? staff()->staffTypeForElement(this) : nullptr;
92       if (
93          (stt && stt->isTabStaff()
94          // in tab staff, do not draw rests is rests are off OR if dur. symbols are on
95          && (!stt->showRests() || stt->genDurations())
96          && (!measure() || !measure()->isMMRest()))        // show multi measure rest always
97          || generated()
98             )
99             return;
100       qreal _spatium = spatium();
101 
102       painter->setPen(curColor());
103 
104       if (measure() && measure()->isMMRest()) {
105             //only on voice 1
106             if (track() % VOICES)
107                   return;
108 
109             // draw number
110             int n = measure()->mmRestCount();
111             std::vector<SymId>&& s = toTimeSigString(QString("%1").arg(n));
112             QRectF numberBox = symBbox(s);
113             qreal y = _mmRestNumberPos * spatium() - staff()->height() * .5;
114             qreal x = (_mmWidth - numberBox.width()) * .5;
115             drawSymbols(s, painter, QPointF(x, y));
116 
117             // draw horizontal line
118             qreal pw = _spatium * .7; // line width
119             QPen pen(painter->pen());
120             pen.setWidthF(pw);
121             painter->setPen(pen);
122             qreal x1 = pw * .5; // half of the line width
123             qreal x2 = _mmWidth - x1;
124 
125             // avoid painting the line when it collides with the number.
126             if ((y + (numberBox.height() * .5 )) > -x1  && (y - (numberBox.height() * .5 )) < x1) {
127                   qreal gapDistance = numberBox.width() * .5 + _spatium;
128                   qreal midpoint = (x1 + x2) * .5;
129                   painter->drawLine(QLineF(x1, 0.0, midpoint - gapDistance, 0.0));
130                   painter->drawLine(QLineF(midpoint + gapDistance, 0.0, x2, 0.0));
131                   }
132             else {
133                   painter->drawLine(QLineF(x1, 0.0, x2, 0.0));
134                   }
135 
136             // draw vertical lines
137             pen.setWidthF(_spatium * .2);
138             painter->setPen(pen);
139             painter->drawLine(QLineF(0.0, -_spatium, 0.0, _spatium));
140             painter->drawLine(QLineF(_mmWidth, -_spatium, _mmWidth,  _spatium));
141             }
142       else
143             drawSymbol(_sym, painter);
144       }
145 
146 //---------------------------------------------------------
147 //   setOffset, overridden from Element
148 //    (- raster vertical position in spatium units) -> no
149 //    - half rests and whole rests outside the staff are
150 //      replaced by special symbols with ledger lines
151 //---------------------------------------------------------
152 
setOffset(const QPointF & o)153 void Rest::setOffset(const QPointF& o)
154       {
155       qreal _spatium = spatium();
156       int line = lrint(o.y()/_spatium);
157 
158       if (_sym == SymId::restWhole && (line <= -2 || line >= 3))
159             _sym = SymId::restWholeLegerLine;
160       else if (_sym == SymId::restWholeLegerLine && (line > -2 && line < 4))
161             _sym = SymId::restWhole;
162       else if (_sym == SymId::restHalf && (line <= -3 || line >= 3))
163             _sym = SymId::restHalfLegerLine;
164       else if (_sym == SymId::restHalfLegerLine && (line > -3 && line < 3))
165             _sym = SymId::restHalf;
166 
167       Element::setOffset(o);
168       }
169 
170 //---------------------------------------------------------
171 //   drag
172 //---------------------------------------------------------
173 
drag(EditData & ed)174 QRectF Rest::drag(EditData& ed)
175       {
176       // don't allow drag for Measure Rests, because they can't be easily laid out in correct position while dragging
177       if (measure() && durationType().type() == TDuration::DurationType::V_MEASURE)
178             return QRectF();
179 
180       QPointF s(ed.delta);
181       QRectF r(abbox());
182 
183       // Limit horizontal drag range
184       static const qreal xDragRange = spatium() * 5;
185       if (fabs(s.x()) > xDragRange)
186             s.rx() = xDragRange * (s.x() < 0 ? -1.0 : 1.0);
187       setOffset(QPointF(s.x(), s.y()));
188       layout();
189       score()->rebuildBspTree();
190       return abbox() | r;
191       }
192 
193 //---------------------------------------------------------
194 //   acceptDrop
195 //---------------------------------------------------------
196 
acceptDrop(EditData & data) const197 bool Rest::acceptDrop(EditData& data) const
198       {
199       Element* e = data.dropElement;
200       ElementType type = e->type();
201       if (
202             (type == ElementType::ICON && toIcon(e)->iconType() == IconType::SBEAM)
203          || (type == ElementType::ICON && toIcon(e)->iconType() == IconType::MBEAM)
204          || (type == ElementType::ICON && toIcon(e)->iconType() == IconType::NBEAM)
205          || (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM32)
206          || (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM64)
207          || (type == ElementType::ICON && toIcon(e)->iconType() == IconType::AUTOBEAM)
208          || (type == ElementType::FERMATA)
209          || (type == ElementType::CLEF)
210          || (type == ElementType::KEYSIG)
211          || (type == ElementType::TIMESIG)
212          || (type == ElementType::SYSTEM_TEXT)
213          || (type == ElementType::STAFF_TEXT)
214          || (type == ElementType::BAR_LINE)
215          || (type == ElementType::BREATH)
216          || (type == ElementType::CHORD)
217          || (type == ElementType::NOTE)
218          || (type == ElementType::STAFF_STATE)
219          || (type == ElementType::INSTRUMENT_CHANGE)
220          || (type == ElementType::DYNAMIC)
221          || (type == ElementType::HAIRPIN)
222          || (type == ElementType::HARMONY)
223          || (type == ElementType::TEMPO_TEXT)
224          || (type == ElementType::REHEARSAL_MARK)
225          || (type == ElementType::FRET_DIAGRAM)
226          || (type == ElementType::TREMOLOBAR)
227          || (type == ElementType::IMAGE)
228          || (type == ElementType::SYMBOL)
229          || (type == ElementType::REPEAT_MEASURE && durationType().type() == TDuration::DurationType::V_MEASURE)
230          ) {
231             return true;
232             }
233       return false;
234       }
235 
236 //---------------------------------------------------------
237 //   drop
238 //---------------------------------------------------------
239 
drop(EditData & data)240 Element* Rest::drop(EditData& data)
241       {
242       Element* e = data.dropElement;
243       switch (e->type()) {
244             case ElementType::ARTICULATION:
245                   {
246                   Articulation* a = toArticulation(e);
247                   if (!a->isFermata() || !score()->addArticulation(this, a)) {
248                         delete e;
249                         e = 0;
250                         }
251                   }
252                   return e;
253 
254             case ElementType::CHORD: {
255                   Chord* c = toChord(e);
256                   Note* n  = c->upNote();
257                   Direction dir = c->stemDirection();
258                   // score()->select(0, SelectType::SINGLE, 0);
259                   NoteVal nval;
260                   nval.pitch = n->pitch();
261                   nval.headGroup = n->headGroup();
262                   Fraction d = score()->inputState().ticks();
263                   if (!d.isZero()) {
264                         Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir);
265                         if (seg) {
266                               ChordRest* cr = toChordRest(seg->element(track()));
267                               if (cr)
268                                     score()->nextInputPos(cr, false);
269                               }
270                         }
271                   delete e;
272                   }
273                   break;
274             case ElementType::REPEAT_MEASURE:
275                   delete e;
276                   if (durationType().type() == TDuration::DurationType::V_MEASURE) {
277                         measure()->cmdInsertRepeatMeasure(staffIdx());
278                         }
279                   break;
280 
281             case ElementType::SYMBOL:
282             case ElementType::IMAGE:
283                   e->setParent(this);
284                   score()->undoAddElement(e);
285                   return e;
286 
287             default:
288                   return ChordRest::drop(data);
289             }
290       return 0;
291       }
292 
293 //---------------------------------------------------------
294 //   getSymbol
295 //---------------------------------------------------------
296 
getSymbol(TDuration::DurationType type,int line,int lines,int * yoffset)297 SymId Rest::getSymbol(TDuration::DurationType type, int line, int lines, int* yoffset)
298       {
299       *yoffset = 2;
300       switch(type) {
301             case TDuration::DurationType::V_LONG:
302                   return SymId::restLonga;
303             case TDuration::DurationType::V_BREVE:
304                   return SymId::restDoubleWhole;
305             case TDuration::DurationType::V_MEASURE:
306                   if (ticks() >= Fraction(2, 1))
307                         return SymId::restDoubleWhole;
308                   // fall through
309             case TDuration::DurationType::V_WHOLE:
310                   *yoffset = 1;
311                   return (line <= -2 || line >= (lines - 1)) ? SymId::restWholeLegerLine : SymId::restWhole;
312             case TDuration::DurationType::V_HALF:
313                   return (line <= -3 || line >= (lines - 2)) ? SymId::restHalfLegerLine : SymId::restHalf;
314             case TDuration::DurationType::V_QUARTER:
315                   return SymId::restQuarter;
316             case TDuration::DurationType::V_EIGHTH:
317                   return SymId::rest8th;
318             case TDuration::DurationType::V_16TH:
319                   return SymId::rest16th;
320             case TDuration::DurationType::V_32ND:
321                   return SymId::rest32nd;
322             case TDuration::DurationType::V_64TH:
323                   return SymId::rest64th;
324             case TDuration::DurationType::V_128TH:
325                   return SymId::rest128th;
326             case TDuration::DurationType::V_256TH:
327                   return SymId::rest256th;
328             case TDuration::DurationType::V_512TH:
329                   return SymId::rest512th;
330             case TDuration::DurationType::V_1024TH:
331                   return SymId::rest1024th;
332             default:
333                   qDebug("unknown rest type %d", int(type));
334                   return SymId::restQuarter;
335             }
336       }
337 
338 //---------------------------------------------------------
339 //   layoutMMRest
340 //---------------------------------------------------------
341 
layoutMMRest(qreal val)342 void Rest::layoutMMRest(qreal val)
343       {
344 //      static const qreal verticalLineWidth = .2;
345 
346       qreal _spatium = spatium();
347       _mmWidth       = val;
348 //      qreal h        = _spatium * (2 + verticalLineWidth);
349 //      qreal w        = _mmWidth + _spatium * verticalLineWidth * .5;
350 //      bbox().setRect(-_spatium * verticalLineWidth * .5, -h * .5, w, h);
351       bbox().setRect(0.0, -_spatium, _mmWidth, _spatium * 2);
352 
353       // text
354       addbbox(mmRestNumberRect());
355 }
356 
357 //---------------------------------------------------------
358 //   mmRestNumberRect
359 ///   returns the mmrest number's bounding rectangle
360 //---------------------------------------------------------
361 
mmRestNumberRect() const362 QRectF Rest::mmRestNumberRect() const
363       {
364       int n = measure()->mmRestCount();
365       std::vector<SymId>&& s = toTimeSigString(QString("%1").arg(n));
366 
367       QRectF r = symBbox(s);
368       qreal y = _mmRestNumberPos * spatium() - staff()->height() * .5;
369       qreal x = (_mmWidth - r.width()) * .5;
370 
371       r.translate(QPointF(x, y));
372       return r;
373       }
374 
375 //---------------------------------------------------------
376 //   layout
377 //---------------------------------------------------------
378 
layout()379 void Rest::layout()
380       {
381       if (_gap)
382             return;
383       for (Element* e : el())
384             e->layout();
385       qreal _spatium = spatium();
386       if (measure() && measure()->isMMRest()) {
387             _mmWidth = score()->styleP(Sid::minMMRestWidth) * mag();
388             // setbbox(QRectF(0.0, -_spatium, _mmWidth, 2.0 * _spatium));
389             return;
390             }
391 
392       rxpos() = 0.0;
393       const StaffType* stt = staffType();
394       if (stt && stt->isTabStaff()) {
395             // if rests are shown and note values are shown as duration symbols
396             if (stt->showRests() && stt->genDurations()) {
397                   TDuration::DurationType type = durationType().type();
398                   int                     dots = durationType().dots();
399                   // if rest is whole measure, convert into actual type and dot values
400                   if (type == TDuration::DurationType::V_MEASURE && measure()) {
401                         Fraction ticks = measure()->ticks();
402                         TDuration dur  = TDuration(ticks).type();
403                         type           = dur.type();
404                         dots           = dur.dots();
405                         }
406                   // symbol needed; if not exist, create, if exists, update duration
407                   if (!_tabDur)
408                         _tabDur = new TabDurationSymbol(score(), stt, type, dots);
409                   else
410                         _tabDur->setDuration(type, dots, stt);
411                   _tabDur->setParent(this);
412 // needed?        _tabDur->setTrack(track());
413                   _tabDur->layout();
414                   setbbox(_tabDur->bbox());
415                   setPos(0.0, 0.0);             // no rest is drawn: reset any position might be set for it
416                   return;
417                   }
418             // if no rests or no duration symbols, delete any dur. symbol and chain into standard staff mngmt
419             // this is to ensure horiz space is reserved for rest, even if they are not displayed
420             // Rest::draw() will skip their drawing, if not needed
421             if(_tabDur) {
422                   delete _tabDur;
423                   _tabDur = 0;
424                   }
425             }
426 
427       dotline = Rest::getDotline(durationType().type());
428 
429       qreal yOff       = offset().y();
430       const Staff* stf = staff();
431       const StaffType*  st = stf ? stf->staffTypeForElement(this) : 0;
432       qreal lineDist = st ? st->lineDistance().val() : 1.0;
433       int userLine   = yOff == 0.0 ? 0 : lrint(yOff / (lineDist * _spatium));
434       int lines      = st ? st->lines() : 5;
435       int lineOffset = computeLineOffset(lines);
436 
437       int yo;
438       _sym = getSymbol(durationType().type(), lineOffset / 2 + userLine, lines, &yo);
439       rypos() = (qreal(yo) + qreal(lineOffset) * .5) * lineDist * _spatium;
440       setbbox(symBbox(_sym));
441       layoutDots();
442       }
443 
444 //---------------------------------------------------------
445 //   layout
446 //---------------------------------------------------------
447 
layoutDots()448 void Rest::layoutDots()
449       {
450       checkDots();
451       qreal x = symWidth(_sym) + score()->styleP(Sid::dotNoteDistance) * mag();
452       qreal dx = score()->styleP(Sid::dotDotDistance) * mag();
453       qreal y = dotline * spatium() * .5;
454       for (NoteDot* dot : _dots) {
455             dot->layout();
456             dot->setPos(x, y);
457             x += dx;
458             }
459       }
460 
461 //---------------------------------------------------------
462 //   checkDots
463 //---------------------------------------------------------
464 
checkDots()465 void Rest::checkDots()
466       {
467       int n = dots() - int(_dots.size());
468       for (int i = 0; i < n; ++i) {
469             NoteDot* dot = new NoteDot(score());
470             dot->setParent(this);
471             dot->setVisible(visible());
472             score()->undoAddElement(dot);
473             }
474       if (n < 0) {
475             for (int i = 0; i < -n; ++i)
476                   score()->undoRemoveElement(_dots.back());
477             }
478       }
479 
480 //---------------------------------------------------------
481 //   dot
482 //---------------------------------------------------------
483 
dot(int n)484 NoteDot* Rest::dot(int n)
485       {
486       checkDots();
487       return _dots[n];
488       }
489 
490 
491 //---------------------------------------------------------
492 //   getDotline
493 //---------------------------------------------------------
494 
getDotline(TDuration::DurationType durationType)495 int Rest::getDotline(TDuration::DurationType durationType)
496       {
497       int dl = -1;
498       switch(durationType) {
499             case TDuration::DurationType::V_64TH:
500             case TDuration::DurationType::V_32ND:
501                   dl = -3;
502                   break;
503             case TDuration::DurationType::V_1024TH:
504             case TDuration::DurationType::V_512TH:
505             case TDuration::DurationType::V_256TH:
506             case TDuration::DurationType::V_128TH:
507                   dl = -5;
508                   break;
509             default:
510                   dl = -1;
511                   break;
512             }
513       return dl;
514       }
515 
516 //---------------------------------------------------------
517 //   computeLineOffset
518 //---------------------------------------------------------
519 
computeLineOffset(int lines)520 int Rest::computeLineOffset(int lines)
521       {
522       Segment* s = segment();
523       bool offsetVoices = s && measure() && measure()->hasVoices(staffIdx(), tick(), actualTicks());
524       if (offsetVoices && voice() == 0) {
525             // do not offset voice 1 rest if there exists a matching invisible rest in voice 2;
526             Element* e = s->element(track() + 1);
527             if (e && e->isRest() && !e->visible() && !toRest(e)->isGap()) {
528                   Rest* r = toRest(e);
529                   if (r->globalTicks() == globalTicks()) {
530                         offsetVoices = false;
531                         }
532                   }
533             }
534 
535       if (offsetVoices) {
536             // if the staff contains slash notation then don't offset voices
537             int baseTrack = staffIdx() * VOICES;
538             for (int v = 0; v < VOICES; ++v) {
539                   Element* e = s->element(baseTrack + v);
540                   if (e && e->isChord() && toChord(e)->slash()) {
541                         offsetVoices = false;
542                         break;
543                         }
544                   }
545             }
546 
547       if (offsetVoices && staff()->mergeMatchingRests()) {
548             // automatically merge matching rests if nothing in any other voice
549             // this is not always the right thing to do do, but is useful in choral music
550             // and can be enabled via a staff property
551             bool matchFound = false;
552             int baseTrack = staffIdx() * VOICES;
553             for (int v = 0; v < VOICES; ++v) {
554                   if (v == voice())
555                         continue;
556                   Element* e = s->element(baseTrack + v);
557                   // try to find match in any other voice
558                   if (e) {
559                         if (e->type() == ElementType::REST) {
560                               Rest* r = toRest(e);
561                               if (r->globalTicks() == globalTicks()) {
562                                     matchFound = true;
563                                     continue;
564                                     }
565                               }
566                         // no match found; no sense looking for anything else
567                         matchFound = false;
568                         break;
569                         }
570                   }
571             if (matchFound)
572                   offsetVoices = false;
573             }
574 
575       int lineOffset    = 0;
576       int assumedCenter = 4;
577       int actualCenter  = (lines - 1);
578       int centerDiff    = actualCenter - assumedCenter;
579 
580       if (offsetVoices) {
581             // move rests in a multi voice context
582             bool up = (voice() == 0) || (voice() == 2);     // TODO: use style values
583 
584             // Calculate extra offset to move rests above the highest resp. below the lowest note
585             // of this segment (for measure rests, of the whole measure) in all opposite voices.
586             // Ignore stems and articulations, because which multi-voice they are at the opposite end.
587             int upOffset = up ? 1 : 0;
588             int line = up ? 10 : -10;
589 
590             // For compatibility reasons apply automatic collision avoidance only if y-offset is unchanged
591             if (qFuzzyIsNull(offset().y()) && autoplace()) {
592                   int firstTrack = staffIdx() * 4;
593                   int extraOffsetForFewLines = lines < 5 ? 2 : 0;
594                   bool isMeasureRest = durationType().type() == TDuration::DurationType::V_MEASURE;
595                   Segment* seg = isMeasureRest ? measure()->first() : s;
596                   while (seg) {
597                         for (const int& track : { firstTrack + upOffset, firstTrack + 2 + upOffset }) {
598                               Element* e = seg->element(track);
599                               if (e && e->isChord()) {
600                                     Chord* chord = toChord(e);
601                                     StaffGroup staffGroup = staff()->staffType(chord->tick())->group();
602                                     for (Note* note : chord->notes()) {
603                                           int nline = staffGroup == StaffGroup::TAB
604                                                 ? note->string() * 2
605                                                 : note->line();
606                                           nline = nline - centerDiff;
607                                           if (up && nline <= line) {
608                                                 line = nline - extraOffsetForFewLines;
609                                                 if (note->accidentalType() != AccidentalType::NONE)
610                                                       line--;
611                                                 }
612                                           else if (!up && nline >= line) {
613                                                 line = nline + extraOffsetForFewLines;
614                                                 if (note->accidentalType() != AccidentalType::NONE)
615                                                       line++;
616                                                 }
617                                           }
618                                     }
619                               }
620                         seg = isMeasureRest ? seg->next() : nullptr;
621                         }
622                   }
623 
624             switch(durationType().type()) {
625                   case TDuration::DurationType::V_LONG:
626                         lineOffset = up ? -3 : 5;
627                         lineOffset += up ? (line < 5 ? line - 5 : 0) : (line > 5 ? line - 5 : 0);
628                         break;
629                   case TDuration::DurationType::V_BREVE:
630                         lineOffset = up ? -3 : 5;
631                         lineOffset += up ? (line < 3 ? line - 3 : 0) : (line > 5 ? line - 5 : 0);
632                         break;
633                   case TDuration::DurationType::V_MEASURE:
634                         if (ticks() >= Fraction(2, 1)) {  // breve symbol
635                               lineOffset = up ? -3 : 5;
636                               lineOffset += up ? (line < 3 ? line - 3 : 0) : (line > 5 ? line - 4 : 0);
637                               }
638                         else {
639                               lineOffset = up ? -4 : 6;     // whole symbol
640                               lineOffset += up ? (line < 3 ? line - 2 : 0) : (line > 6 ? line - 5 : 0);
641                               }
642                         break;
643                   case TDuration::DurationType::V_WHOLE:
644                         lineOffset = up ? -4 : 6;
645                         lineOffset += up ? (line < 3 ? line - 2 : 0) : (line > 6 ? line - 5 : 0);
646                         break;
647                   case TDuration::DurationType::V_HALF:
648                         lineOffset = up ? -4 : 4;
649                         lineOffset += up ? (line < 2 ? line - 3 : 0) : (line > 5 ? line - 4 : 0);
650                         break;
651                   case TDuration::DurationType::V_QUARTER:
652                         lineOffset = up ? -4 : 4;
653                         lineOffset += up ? (line < 5 ? line - 4 : 0) : (line > 3 ? line - 3 : 0);
654                         break;
655                   case TDuration::DurationType::V_EIGHTH:
656                         lineOffset = up ? -4 : 4;
657                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
658                         break;
659                   case TDuration::DurationType::V_16TH:
660                         lineOffset = up ? -6 : 4;
661                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
662                         break;
663                   case TDuration::DurationType::V_32ND:
664                         lineOffset = up ? -6 : 6;
665                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
666                         break;
667                   case TDuration::DurationType::V_64TH:
668                         lineOffset = up ? -8 : 6;
669                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
670                         break;
671                   case TDuration::DurationType::V_128TH:
672                         lineOffset = up ? -8 : 8;
673                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
674                         break;
675                   case TDuration::DurationType::V_1024TH:
676                   case TDuration::DurationType::V_512TH:
677                   case TDuration::DurationType::V_256TH:
678                         lineOffset = up ? -10 : 6;
679                         lineOffset += up ? (line < 4 ? line - 4 : 0) : (line > 4 ? line - 4 : 0);
680                         break;
681                   default:
682                         break;
683                   }
684 
685             // adjust offsets for staves with other than five lines
686             if (lines != 5) {
687                   lineOffset += centerDiff;
688                   if (centerDiff & 1) {
689                         // round to line
690                         if (lines == 2 && staff() && staff()->lineDistance(tick()) < 2.0)
691                               ;                                         // leave alone
692                         else if (lines <= 6)
693                               lineOffset += lineOffset > 0 ? -1 : 1;    // round inward
694                         else
695                               lineOffset += lineOffset > 0 ? 1 : -1;    // round outward
696                         }
697                   }
698             }
699       else {
700             // Gould says to center rests on middle line or space
701             // but subjectively, many rests look strange centered on a space
702             // so we do it for 2-line staves only
703             if (centerDiff & 1 && lines != 2)
704                   centerDiff += 1;  // round down
705 
706             lineOffset = centerDiff;
707             switch(durationType().type()) {
708                   case TDuration::DurationType::V_LONG:
709                   case TDuration::DurationType::V_BREVE:
710                   case TDuration::DurationType::V_MEASURE:
711                   case TDuration::DurationType::V_WHOLE:
712                         if (lineOffset & 1)
713                               lineOffset += 1;  // always round to nearest line
714                         else if (lines <= 3)
715                               lineOffset += 2;  // special case - move down for 1-line or 3-line staff
716                         break;
717                   case TDuration::DurationType::V_HALF:
718                         if (lineOffset & 1)
719                               lineOffset += 1;  // always round to nearest line
720                         break;
721                   default:
722                         break;
723                   }
724             }
725       // DEBUG: subtract this off only to be added back in layout()?
726       // that would throw off calculation of when ledger lines are needed
727       //if (staff())
728       //      lineOffset -= staff()->staffType()->stepOffset();
729       return lineOffset;
730       }
731 
732 //---------------------------------------------------------
733 //   upPos
734 //---------------------------------------------------------
735 
upPos() const736 qreal Rest::upPos() const
737       {
738       return symBbox(_sym).y();
739       }
740 
741 //---------------------------------------------------------
742 //   downPos
743 //---------------------------------------------------------
744 
downPos() const745 qreal Rest::downPos() const
746       {
747       return symBbox(_sym).y() + symHeight(_sym);
748       }
749 
750 //---------------------------------------------------------
751 //   scanElements
752 //---------------------------------------------------------
753 
scanElements(void * data,void (* func)(void *,Element *),bool all)754 void Rest::scanElements(void* data, void (*func)(void*, Element*), bool all)
755       {
756       ChordRest::scanElements(data, func, all);
757       for (Element* e : el())
758             e->scanElements(data, func, all);
759       for (NoteDot* dot : _dots)
760             dot->scanElements(data, func, all);
761       if (!isGap())
762             func(data, this);
763       }
764 
765 //---------------------------------------------------------
766 //   setTrack
767 //---------------------------------------------------------
768 
setTrack(int val)769 void Rest::setTrack(int val)
770       {
771       ChordRest::setTrack(val);
772       for (NoteDot* dot : _dots)
773             dot->setTrack(val);
774       }
775 
776 //---------------------------------------------------------
777 //   reset
778 //---------------------------------------------------------
779 
reset()780 void Rest::reset()
781       {
782       undoChangeProperty(Pid::BEAM_MODE, int(Beam::Mode::NONE));
783       ChordRest::reset();
784       }
785 
786 //---------------------------------------------------------
787 //   mag
788 //---------------------------------------------------------
789 
mag() const790 qreal Rest::mag() const
791       {
792       qreal m = staff() ? staff()->mag(this) : 1.0;
793       if (small())
794             m *= score()->styleD(Sid::smallNoteMag);
795       return m;
796       }
797 
798 //---------------------------------------------------------
799 //   upLine
800 //---------------------------------------------------------
801 
upLine() const802 int Rest::upLine() const
803       {
804       qreal _spatium = spatium();
805       return lrint((pos().y() + bbox().top() + _spatium) * 2 / _spatium);
806       }
807 
808 //---------------------------------------------------------
809 //   downLine
810 //---------------------------------------------------------
811 
downLine() const812 int Rest::downLine() const
813       {
814       qreal _spatium = spatium();
815       return lrint((pos().y() + bbox().top() + _spatium) * 2 / _spatium);
816       }
817 
818 //---------------------------------------------------------
819 //   stemPos
820 //    point to connect stem
821 //---------------------------------------------------------
822 
stemPos() const823 QPointF Rest::stemPos() const
824       {
825       return pagePos();
826       }
827 
828 //---------------------------------------------------------
829 //   stemPosBeam
830 //    return stem position of note on beam side
831 //    return canvas coordinates
832 //---------------------------------------------------------
833 
stemPosBeam() const834 QPointF Rest::stemPosBeam() const
835       {
836       QPointF p(pagePos());
837       if (_up)
838             p.ry() += bbox().top() + spatium() * 1.5;
839       else
840             p.ry() += bbox().bottom() - spatium() * 1.5;
841       return p;
842       }
843 
844 //---------------------------------------------------------
845 //   stemPosX
846 //---------------------------------------------------------
847 
stemPosX() const848 qreal Rest::stemPosX() const
849       {
850       if (_up)
851             return bbox().right();
852       else
853             return bbox().left();
854       }
855 
856 //---------------------------------------------------------
857 //   rightEdge
858 //---------------------------------------------------------
859 
rightEdge() const860 qreal Rest::rightEdge() const
861       {
862       return x() + width();
863       }
864 
865 //---------------------------------------------------------
866 //   accent
867 //---------------------------------------------------------
868 
accent()869 bool Rest::accent()
870       {
871       return (voice() >= 2 && small());
872       }
873 
874 //---------------------------------------------------------
875 //   setAccent
876 //---------------------------------------------------------
877 
setAccent(bool flag)878 void Rest::setAccent(bool flag)
879       {
880       undoChangeProperty(Pid::SMALL, flag);
881       if (voice() % 2 == 0) {
882             if (flag) {
883                   qreal yOffset = -(bbox().bottom());
884                   if (durationType() >= TDuration::DurationType::V_HALF)
885                         yOffset -= staff()->spatium(tick()) * 0.5;
886                   // undoChangeProperty(Pid::OFFSET, QPointF(0.0, yOffset));
887                   rypos() += yOffset;
888                   }
889             else {
890                   // undoChangeProperty(Pid::OFFSET, QPointF());  TODO::check
891                   }
892             }
893       }
894 
895 //---------------------------------------------------------
896 //   accessibleInfo
897 //---------------------------------------------------------
898 
accessibleInfo() const899 QString Rest::accessibleInfo() const
900       {
901       QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
902       return QObject::tr("%1; Duration: %2; %3").arg(Element::accessibleInfo(), durationUserName(), voice);
903       }
904 
905 //---------------------------------------------------------
906 //   accessibleInfo
907 //---------------------------------------------------------
908 
screenReaderInfo() const909 QString Rest::screenReaderInfo() const
910       {
911       Measure* m = measure();
912       bool voices = m ? m->hasVoices(staffIdx()) : false;
913       QString voice = voices ? QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1)) : "";
914       return QString("%1 %2 %3").arg(Element::accessibleInfo(), durationUserName(), voice);
915       }
916 
917 //---------------------------------------------------------
918 //   add
919 //---------------------------------------------------------
920 
add(Element * e)921 void Rest::add(Element* e)
922       {
923       e->setParent(this);
924       e->setTrack(track());
925 
926       switch(e->type()) {
927             case ElementType::NOTEDOT:
928                   _dots.push_back(toNoteDot(e));
929                   break;
930             case ElementType::SYMBOL:
931             case ElementType::IMAGE:
932                   el().push_back(e);
933                   break;
934             default:
935                   ChordRest::add(e);
936                   break;
937             }
938       }
939 
940 //---------------------------------------------------------
941 //   remove
942 //---------------------------------------------------------
943 
remove(Element * e)944 void Rest::remove(Element* e)
945       {
946       switch(e->type()) {
947             case ElementType::NOTEDOT:
948                   _dots.pop_back();
949                   break;
950             case ElementType::SYMBOL:
951             case ElementType::IMAGE:
952                   if (!el().remove(e))
953                         qDebug("Rest::remove(): cannot find %s", e->name());
954                   break;
955             default:
956                   ChordRest::remove(e);
957                   break;
958             }
959       }
960 
961 //--------------------------------------------------
962 //   Rest::write
963 //---------------------------------------------------------
964 
write(XmlWriter & xml) const965 void Rest::write(XmlWriter& xml) const
966       {
967       if (_gap)
968             return;
969       writeBeam(xml);
970       xml.stag(this);
971       ChordRest::writeProperties(xml);
972       el().write(xml);
973       bool write_dots = false;
974       for (NoteDot* dot : _dots)
975             if (!dot->offset().isNull() || !dot->visible() || dot->color() != Qt::black || dot->visible() != visible()) {
976                   write_dots = true;
977                   break;
978                   }
979       if (write_dots)
980             for (NoteDot* dot: _dots)
981                   dot->write(xml);
982       xml.etag();
983       }
984 
985 //---------------------------------------------------------
986 //   Rest::read
987 //---------------------------------------------------------
988 
read(XmlReader & e)989 void Rest::read(XmlReader& e)
990       {
991       while (e.readNextStartElement()) {
992             const QStringRef& tag(e.name());
993             if (tag == "Symbol") {
994                   Symbol* s = new Symbol(score());
995                   s->setTrack(track());
996                   s->read(e);
997                   add(s);
998                   }
999             else if (tag == "Image") {
1000                   if (MScore::noImages)
1001                         e.skipCurrentElement();
1002                   else {
1003                         Image* image = new Image(score());
1004                         image->setTrack(track());
1005                         image->read(e);
1006                         add(image);
1007                         }
1008                   }
1009             else if (tag == "NoteDot") {
1010                   NoteDot* dot = new NoteDot(score());
1011                   dot->read(e);
1012                   add(dot);
1013                   }
1014             else if (ChordRest::readProperties(e))
1015                   ;
1016             else
1017                   e.unknown();
1018             }
1019       }
1020 
1021 //---------------------------------------------------------
1022 //   localSpatiumChanged
1023 //---------------------------------------------------------
1024 
localSpatiumChanged(qreal oldValue,qreal newValue)1025 void Rest::localSpatiumChanged(qreal oldValue, qreal newValue)
1026       {
1027       ChordRest::localSpatiumChanged(oldValue, newValue);
1028       for (Element* e : _dots)
1029             e->localSpatiumChanged(oldValue, newValue);
1030       for (Element* e : el())
1031             e->localSpatiumChanged(oldValue, newValue);
1032       }
1033 
1034 //---------------------------------------------------------
1035 //   propertyDefault
1036 //---------------------------------------------------------
1037 
propertyDefault(Pid propertyId) const1038 QVariant Rest::propertyDefault(Pid propertyId) const
1039       {
1040       switch (propertyId) {
1041             case Pid::GAP:
1042                   return false;
1043             case Pid::MMREST_NUMBER_POS:
1044                   return score()->styleV(Sid::mmRestNumberPos);
1045             default:
1046                   return ChordRest::propertyDefault(propertyId);
1047             }
1048       }
1049 
1050 //————————————————————————————
1051 //   resetProperty
1052 //————————————————————————————
1053 
resetProperty(Pid id)1054 void Rest::resetProperty(Pid id)
1055       {
1056       setProperty(id, propertyDefault(id));
1057       return;
1058       }
1059 
1060 //————————————————————————————
1061 //   getPropertyStyle
1062 //————————————————————————————
1063 
getPropertyStyle(Pid pid) const1064 Sid Rest::getPropertyStyle(Pid pid) const
1065       {
1066       if (pid == Pid::MMREST_NUMBER_POS)
1067             return Sid::mmRestNumberPos;
1068       return ChordRest::getPropertyStyle(pid);
1069       }
1070 
1071 //---------------------------------------------------------
1072 //   getProperty
1073 //---------------------------------------------------------
1074 
getProperty(Pid propertyId) const1075 QVariant Rest::getProperty(Pid propertyId) const
1076       {
1077       switch (propertyId) {
1078             case Pid::GAP:
1079                   return _gap;
1080             case Pid::MMREST_NUMBER_POS:
1081                   return _mmRestNumberPos;
1082             default:
1083                   return ChordRest::getProperty(propertyId);
1084             }
1085       }
1086 
1087 //---------------------------------------------------------
1088 //   setProperty
1089 //---------------------------------------------------------
1090 
setProperty(Pid propertyId,const QVariant & v)1091 bool Rest::setProperty(Pid propertyId, const QVariant& v)
1092       {
1093       switch (propertyId) {
1094             case Pid::GAP:
1095                   _gap = v.toBool();
1096                   triggerLayout();
1097                   break;
1098             case Pid::VISIBLE:
1099                   setVisible(v.toBool());
1100                   triggerLayout();
1101                   break;
1102             case Pid::OFFSET:
1103                   score()->addRefresh(canvasBoundingRect());
1104                   setOffset(v.toPointF());
1105                   layout();
1106                   score()->addRefresh(canvasBoundingRect());
1107                   if (measure() && durationType().type() == TDuration::DurationType::V_MEASURE)
1108                          measure()->triggerLayout();
1109                   triggerLayout();
1110                   break;
1111             case Pid::MMREST_NUMBER_POS:
1112                   _mmRestNumberPos = v.toDouble();
1113                   triggerLayout();
1114                   break;
1115             default:
1116                   return ChordRest::setProperty(propertyId, v);
1117             }
1118       return true;
1119       }
1120 
1121 //---------------------------------------------------------
1122 //   undoChangeDotsVisible
1123 //---------------------------------------------------------
1124 
undoChangeDotsVisible(bool v)1125 void Rest::undoChangeDotsVisible(bool v)
1126       {
1127       for (NoteDot* dot : _dots)
1128             dot->undoChangeProperty(Pid::VISIBLE, QVariant(v));
1129       }
1130 
1131 //---------------------------------------------------------
1132 //   nextElement
1133 //---------------------------------------------------------
1134 
nextElement()1135 Element* Rest::nextElement()
1136       {
1137       return ChordRest::nextElement();
1138       }
1139 
1140 //---------------------------------------------------------
1141 //   prevElement
1142 //---------------------------------------------------------
1143 
prevElement()1144 Element* Rest::prevElement()
1145       {
1146       return ChordRest::prevElement();
1147       }
1148 
1149 //---------------------------------------------------------
1150 //   shape
1151 //---------------------------------------------------------
1152 
shape() const1153 Shape Rest::shape() const
1154       {
1155       Shape shape;
1156       if (!_gap) {
1157             shape.add(ChordRest::shape());
1158             if (measure() && measure()->isMMRest()) {
1159                   qreal _spatium = spatium();
1160                   shape.add(QRectF(0.0, -_spatium, _mmWidth, 2.0 * _spatium));
1161 
1162                   shape.add(mmRestNumberRect());
1163                   }
1164             else
1165 #ifndef NDEBUG
1166                   shape.add(bbox(), name());
1167 #else
1168                   shape.add(bbox());
1169 #endif
1170             for (NoteDot* dot : _dots)
1171                   shape.add(symBbox(SymId::augmentationDot).translated(dot->pos()));
1172             }
1173       for (Element* e : el()) {
1174             if (e->addToSkyline())
1175                   shape.add(e->shape().translated(e->pos()));
1176             }
1177       return shape;
1178       }
1179 
1180 //---------------------------------------------------------
1181 //   editDrag
1182 //---------------------------------------------------------
1183 
editDrag(EditData & editData)1184 void Rest::editDrag(EditData& editData)
1185       {
1186       Segment* seg = segment();
1187 
1188       if (editData.modifiers & Qt::ShiftModifier) {
1189             const Spatium deltaSp = Spatium(editData.delta.x() / spatium());
1190             seg->undoChangeProperty(Pid::LEADING_SPACE, seg->extraLeadingSpace() + deltaSp);
1191             }
1192       else {
1193             setOffset(offset() + editData.evtDelta);
1194             }
1195       triggerLayout();
1196       }
1197 
1198 }
1199