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