1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-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 "barline.h"
14 #include "score.h"
15 #include "sym.h"
16 #include "staff.h"
17 #include "part.h"
18 #include "system.h"
19 #include "measure.h"
20 #include "segment.h"
21 #include "articulation.h"
22 #include "stafftype.h"
23 #include "xml.h"
24 #include "marker.h"
25 #include "stafflines.h"
26 #include "spanner.h"
27 #include "undo.h"
28 #include "fermata.h"
29 #include "symbol.h"
30 #include "image.h"
31
32 namespace Ms {
33
34 //---------------------------------------------------------
35 // undoChangeBarLineType
36 //---------------------------------------------------------
37
undoChangeBarLineType(BarLine * bl,BarLineType barType,bool allStaves)38 static void undoChangeBarLineType(BarLine* bl, BarLineType barType, bool allStaves)
39 {
40 Measure* m = bl->measure();
41 if (!m)
42 return;
43
44 if (barType == BarLineType::START_REPEAT) {
45 m = m->nextMeasure();
46 if (!m)
47 return;
48 }
49 else if (bl->barLineType() == BarLineType::START_REPEAT) {
50 if (barType != BarLineType::END_REPEAT)
51 m->undoChangeProperty(Pid::REPEAT_START, false);
52 m = m->prevMeasure();
53 if (!m)
54 return;
55 }
56
57 switch (barType) {
58 case BarLineType::END:
59 case BarLineType::NORMAL:
60 case BarLineType::DOUBLE:
61 case BarLineType::BROKEN:
62 case BarLineType::DOTTED:
63 case BarLineType::REVERSE_END:
64 case BarLineType::HEAVY:
65 case BarLineType::DOUBLE_HEAVY: {
66 Segment* segment = bl->segment();
67 SegmentType segmentType = segment->segmentType();
68 if (segmentType == SegmentType::EndBarLine) {
69 // when setting barline type on mmrest, set for underlying measure (and linked staves)
70 // createMMRest will then set for the mmrest directly
71 Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
72
73 bool generated;
74 if (bl->barLineType() == barType)
75 generated = bl->generated(); // no change: keep current status
76 else if (!bl->generated() && (barType == BarLineType::NORMAL))
77 generated = true; // currently non-generated, changing to normal: assume generated
78 else
79 generated = false; // otherwise assume non-generated
80
81 if (allStaves) {
82 // use all staves of master score; we will take care of parts in loop through linked staves below
83 m2 = bl->masterScore()->tick2measure(m2->tick());
84 if (!m2)
85 return; // should never happen
86 segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
87 }
88 const std::vector<Element*>& elist = allStaves ? segment->elist() : std::vector<Element*> { bl };
89 for (Element* e : elist) {
90 if (!e || !e->staff() || !e->isBarLine())
91 continue;
92
93 // handle linked staves/parts:
94 // barlines themselves are not necessarily linked,
95 // so use staffList to find linked staves
96 BarLine* sbl = toBarLine(e);
97 for (Staff* lstaff : sbl->staff()->staffList()) {
98 Score* lscore = lstaff->score();
99 int ltrack = lstaff->idx() * VOICES;
100
101 // handle mmrests:
102 // set the barline on the underlying measure
103 // this will copied to the mmrest during layout, in createMMRest
104 Measure* lmeasure = lscore->tick2measure(m2->tick());
105 if (!lmeasure)
106 continue;
107
108 lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
109 Segment* lsegment = lmeasure->undoGetSegmentR(SegmentType::EndBarLine, lmeasure->ticks());
110 BarLine* lbl = toBarLine(lsegment->element(ltrack));
111 if (!lbl) {
112 lbl = new BarLine(lscore);
113 lbl->setParent(lsegment);
114 lbl->setTrack(ltrack);
115 lbl->setSpanStaff(lstaff->barLineSpan());
116 lbl->setSpanFrom(lstaff->barLineFrom());
117 lbl->setSpanTo(lstaff->barLineTo());
118 lbl->setBarLineType(barType);
119 lbl->setGenerated(generated);
120 lscore->addElement(lbl);
121 if (!generated)
122 lbl->linkTo(sbl);
123 }
124 else {
125 lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
126 lscore->undo(new ChangeProperty(lbl, Pid::BARLINE_TYPE, QVariant::fromValue(barType), PropertyFlags::NOSTYLE));
127 // set generated flag before and after so it sticks on type change and also works on undo/redo
128 lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
129 if (lbl != sbl && !generated && !lbl->links())
130 lscore->undo(new Link(lbl, sbl));
131 else if (lbl != sbl && generated && lbl->isLinked(sbl))
132 lscore->undo(new Unlink(lbl));
133 }
134 }
135 }
136 }
137 else if (segmentType == SegmentType::BeginBarLine) {
138 Segment* segment1 = m->undoGetSegmentR(SegmentType::BeginBarLine, Fraction(0, 1));
139 for (Element* e : segment1->elist()) {
140 if (e) {
141 e->score()->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
142 e->score()->undo(new ChangeProperty(e, Pid::BARLINE_TYPE, QVariant::fromValue(barType), PropertyFlags::NOSTYLE));
143 // set generated flag before and after so it sticks on type change and also works on undo/redo
144 e->score()->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
145 }
146 }
147 }
148 }
149 break;
150 case BarLineType::START_REPEAT: {
151 Measure* m2 = m->isMMRest() ? m->mmRestFirst() : m;
152 for (Score* lscore : m2->score()->scoreList()) {
153 Measure* lmeasure = lscore->tick2measure(m2->tick());
154 if (lmeasure)
155 lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
156 }
157 }
158 break;
159 case BarLineType::END_REPEAT: {
160 Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
161 for (Score* lscore : m2->score()->scoreList()) {
162 Measure* lmeasure = lscore->tick2measure(m2->tick());
163 if (lmeasure)
164 lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
165 }
166 }
167 break;
168 case BarLineType::END_START_REPEAT: {
169 Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
170 for (Score* lscore : m2->score()->scoreList()) {
171 Measure* lmeasure = lscore->tick2measure(m2->tick());
172 if (lmeasure) {
173 lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
174 lmeasure = lmeasure->nextMeasure();
175 if (lmeasure)
176 lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
177 }
178 }
179 }
180 break;
181 }
182 }
183
184 //---------------------------------------------------------
185 // BarLineEditData
186 //---------------------------------------------------------
187
188 class BarLineEditData : public ElementEditData {
189 public:
190 qreal yoff1;
191 qreal yoff2;
type()192 virtual EditDataType type() override { return EditDataType::BarLineEditData; }
193 };
194
195 //---------------------------------------------------------
196 // BarLineTable
197 //---------------------------------------------------------
198
199 const std::vector<BarLineTableItem> BarLine::barLineTable {
200 { BarLineType::NORMAL, Sym::symUserNames[int(SymId::barlineSingle)], "normal" },
201 { BarLineType::DOUBLE, Sym::symUserNames[int(SymId::barlineDouble)], "double" },
202 { BarLineType::START_REPEAT, Sym::symUserNames[int(SymId::repeatLeft)], "start-repeat" },
203 { BarLineType::END_REPEAT, Sym::symUserNames[int(SymId::repeatRight)], "end-repeat" },
204 { BarLineType::BROKEN, Sym::symUserNames[int(SymId::barlineDashed)], "dashed" },
205 { BarLineType::END, Sym::symUserNames[int(SymId::barlineFinal)], "end" },
206 { BarLineType::END_START_REPEAT, Sym::symUserNames[int(SymId::repeatRightLeft)], "end-start-repeat" },
207 { BarLineType::DOTTED, Sym::symUserNames[int(SymId::barlineDotted)], "dotted" },
208 { BarLineType::REVERSE_END, Sym::symUserNames[int(SymId::barlineReverseFinal)], "reverse-end" },
209 { BarLineType::HEAVY, Sym::symUserNames[int(SymId::barlineHeavy)], "heavy" },
210 { BarLineType::DOUBLE_HEAVY, Sym::symUserNames[int(SymId::barlineHeavyHeavy)], "double-heavy" },
211 };
212
213 //---------------------------------------------------------
214 // barLineTableItem
215 //---------------------------------------------------------
216
barLineTableItem(unsigned i)217 const BarLineTableItem* BarLine::barLineTableItem(unsigned i)
218 {
219 if (i >= barLineTable.size())
220 return 0;
221 return &barLineTable[i];
222 }
223
224 //---------------------------------------------------------
225 // userTypeName
226 //---------------------------------------------------------
227
userTypeName(BarLineType t)228 QString BarLine::userTypeName(BarLineType t)
229 {
230 for (const auto& i : barLineTable) {
231 if (i.type == t)
232 return qApp->translate("symUserNames", i.userName);
233 }
234 return QString();
235 }
236
237 //---------------------------------------------------------
238 // barLineTypeName
239 //
240 // Instance form returning the name string of the bar line type and
241 // static form returning the name string for an arbitrary bar line type.
242 //---------------------------------------------------------
243
barLineTypeName() const244 QString BarLine::barLineTypeName() const
245 {
246 return barLineTypeName(barLineType());
247 }
248
barLineTypeName(BarLineType t)249 QString BarLine::barLineTypeName(BarLineType t)
250 {
251 for (const auto& i : barLineTable) {
252 if (i.type == t)
253 return i.name;
254 }
255 return QString("??");
256 }
257
258 //---------------------------------------------------------
259 // setBarLineType
260 //
261 // Set the bar line type from the type name string.
262 // Does not update _customSubtype or _generated flags: to be used when reading from a score file
263 //---------------------------------------------------------
264
setBarLineType(const QString & s)265 void BarLine::setBarLineType(const QString& s)
266 {
267 _barLineType = barLineType(s);
268 }
269
270 //---------------------------------------------------------
271 // barLineType
272 //---------------------------------------------------------
273
barLineType(const QString & s)274 BarLineType BarLine::barLineType(const QString& s)
275 {
276 for (const auto& i : barLineTable) {
277 if (i.name == s)
278 return i.type;
279 }
280 return BarLineType::NORMAL; // silent default
281 }
282
283 //---------------------------------------------------------
284 // BarLine
285 //---------------------------------------------------------
286
BarLine(Score * s)287 BarLine::BarLine(Score* s)
288 : Element(s)
289 {
290 setHeight(4 * spatium()); // for use in palettes
291 }
292
BarLine(const BarLine & bl)293 BarLine::BarLine(const BarLine& bl)
294 : Element(bl)
295 {
296 _spanStaff = bl._spanStaff;
297 _spanFrom = bl._spanFrom;
298 _spanTo = bl._spanTo;
299 _barLineType = bl._barLineType;
300 y1 = bl.y1;
301 y2 = bl.y2;
302
303 for (Element* e : bl._el)
304 add(e->clone());
305 }
306
~BarLine()307 BarLine::~BarLine()
308 {
309 qDeleteAll(_el);
310 }
311
312 //---------------------------------------------------------
313 // canvasPos
314 //---------------------------------------------------------
315
canvasPos() const316 QPointF BarLine::canvasPos() const
317 {
318 QPointF pos = Element::canvasPos();
319 if (parent()) {
320 System* system = measure()->system();
321 qreal yoff = system ? system->staff(staffIdx())->y() : 0.0;
322 pos.ry() += yoff;
323 }
324 return pos;
325 }
326
327 //---------------------------------------------------------
328 // pagePos
329 //---------------------------------------------------------
330
pagePos() const331 QPointF BarLine::pagePos() const
332 {
333 if (segment() == 0)
334 return pos();
335 System* system = segment()->measure()->system();
336
337 qreal yp = y();
338 if (system) {
339 // get first not hidden staff
340 int startIdx = staffIdx();
341 int endIdx = startIdx + (spanStaff() ? 1 : 0);
342 int staffIdx1 = startIdx;
343 Staff* staff1 = score()->staff(staffIdx1);
344 SysStaff* sysStaff1 = system->staff(staffIdx1);
345
346 while (staff1 && sysStaff1 && !(sysStaff1->show() && staff1->show())) {
347 if (++staffIdx1 >= endIdx) {
348 // no visible staves spanned; just use first
349 staffIdx1 = startIdx;
350 break;
351 }
352 staff1 = score()->staff(staffIdx1);
353 sysStaff1 = system->staff(staffIdx1);
354 }
355 yp += system->staffYpage(staffIdx1);
356 }
357 return QPointF(pageX(), yp);
358 }
359
360 //---------------------------------------------------------
361 // prevVisiblespannedStaff
362 //---------------------------------------------------------
363
prevVisibleSpannedStaff(const BarLine * bl)364 int prevVisibleSpannedStaff(const BarLine* bl)
365 {
366 Score* score = bl->score();
367 int staffIdx = bl->staffIdx();
368 Segment* segment = bl->segment();
369 for (int i = staffIdx - 1; i >= 0; --i) {
370 BarLine* nbl = toBarLine(segment->element(i * VOICES));
371 if (!nbl || !nbl->spanStaff())
372 break;
373 Staff* s = score->staff(i);
374 if (s->part()->show() && bl->measure()->visible(i)) {
375 return i;
376 }
377 }
378 return staffIdx;
379 }
380
381 //---------------------------------------------------------
382 // nextVisiblespannedStaff
383 //---------------------------------------------------------
384
nextVisibleSpannedStaff(const BarLine * bl)385 int nextVisibleSpannedStaff(const BarLine* bl)
386 {
387 Score* score = bl->score();
388 int nstaves = score->nstaves();
389 int staffIdx = bl->staffIdx();
390 Segment* segment = bl->segment();
391 for (int i = staffIdx + 1; i < nstaves; ++i) {
392 Staff* s = score->staff(i);
393 if (s->part()->show()) {
394 // span/show bar line if this measure is visible
395 if (bl->measure()->visible(i))
396 return i;
397 // or if this is an endBarLine and:
398 if (segment && segment->isEndBarLineType()) {
399 // ...this measure contains a (cutaway) courtesy clef only
400 if (bl->measure()->isCutawayClef(i))
401 return i;
402 // ...or next measure is both visible and in the same system
403 Measure* nm = bl->measure()->nextMeasure();
404 if ((nm ? nm->visible(i) : false) && (nm ? nm->system() == bl->measure()->system() : false))
405 return i;
406 }
407 }
408 BarLine* nbl = toBarLine(segment->element(i * VOICES));
409 if (!nbl || !nbl->spanStaff())
410 break;
411 }
412 return staffIdx;
413 }
414
415 //---------------------------------------------------------
416 // getY
417 //---------------------------------------------------------
418
getY() const419 void BarLine::getY() const
420 {
421 qreal _spatium = spatium();
422 if (!parent()) {
423 // for use in palette
424 y1 = _spanFrom * _spatium * .5;
425 y2 = (8-_spanTo) * _spatium * .5;
426 return;
427 }
428 int staffIdx1 = staffIdx();
429 const Staff* staff1 = score()->staff(staffIdx1);
430 int staffIdx2 = staffIdx1;
431 int nstaves = score()->nstaves();
432
433 Measure* measure = segment()->measure();
434 if (_spanStaff)
435 staffIdx2 = nextVisibleSpannedStaff(this);
436
437 System* system = measure->system();
438 if (!system)
439 return;
440
441 // test start and end staff visibility
442
443
444 // base y on top visible staff in barline span
445 // after skipping ones with hideSystemBarLine set
446 // and accounting for staves that are shown but have invisible measures
447
448 Fraction tick = segment()->measure()->tick();
449 const StaffType* st1 = staff1->staffType(tick);
450
451 int from = _spanFrom;
452 int to = _spanTo;
453 int oneLine = st1->lines() <= 1;
454 if (oneLine && _spanFrom == 0) {
455 from = BARLINE_SPAN_1LINESTAFF_FROM;
456 if (!_spanStaff || (staffIdx1 == nstaves - 1))
457 to = BARLINE_SPAN_1LINESTAFF_TO;
458 }
459 SysStaff* sysStaff1 = system->staff(staffIdx1);
460 qreal yp = sysStaff1->y();
461 qreal spatium1 = st1->spatium(score());
462 qreal d = st1->lineDistance().val() * spatium1;
463 qreal yy = measure->staffLines(staffIdx1)->y1() - yp;
464 qreal lw = score()->styleS(Sid::staffLineWidth).val() * spatium1 * .5;
465 y1 = yy + from * d * .5 - lw;
466 if (staffIdx2 != staffIdx1)
467 y2 = measure->staffLines(staffIdx2)->y1() - yp - to * d * .5;
468 else
469 y2 = yy + (st1->lines() * 2 - 2 + to) * d * .5 + lw;
470 }
471
472 //---------------------------------------------------------
473 // drawDots
474 //---------------------------------------------------------
475
drawDots(QPainter * painter,qreal x) const476 void BarLine::drawDots(QPainter* painter, qreal x) const
477 {
478 qreal _spatium = spatium();
479
480 qreal y1l;
481 qreal y2l;
482 if (parent() == 0) { // for use in palette (always Bravura)
483 //Bravura shifted repeatDot symbol 0.5sp upper in the font itself (1.272)
484 y1l = 1.5 * _spatium;
485 y2l = 2.5 * _spatium;
486 }
487 else {
488 const StaffType* st = staffType();
489
490 //workaround to make new Bravura, Petaluma and Leland font work correctly with repeatDots
491 qreal offset = (score()->scoreFont()->name() == "Leland" || score()->scoreFont()->name() == "Bravura" || score()->scoreFont()->name() == "Petaluma") ? 0 : 0.5 * score()->spatium() * mag();
492 y1l = st->doty1() * _spatium + offset;
493 y2l = st->doty2() * _spatium + offset;
494
495 //adjust for staffType offset
496 qreal stYOffset = st->yoffset().val() * _spatium;
497 y1l += stYOffset;
498 y2l += stYOffset;
499 }
500 drawSymbol(SymId::repeatDot, painter, QPointF(x, y1l));
501 drawSymbol(SymId::repeatDot, painter, QPointF(x, y2l));
502 }
503
504 //---------------------------------------------------------
505 // drawTips
506 //---------------------------------------------------------
507
drawTips(QPainter * painter,bool reversed,qreal x) const508 void BarLine::drawTips(QPainter* painter, bool reversed, qreal x) const
509 {
510 if (reversed) {
511 if (isTop())
512 drawSymbol(SymId::reversedBracketTop, painter, QPointF(x - symWidth(SymId::reversedBracketTop), y1));
513 if (isBottom())
514 drawSymbol(SymId::reversedBracketBottom, painter, QPointF(x - symWidth(SymId::reversedBracketBottom), y2));
515 }
516 else {
517 if (isTop())
518 drawSymbol(SymId::bracketTop, painter, QPointF(x, y1));
519 if (isBottom())
520 drawSymbol(SymId::bracketBottom, painter, QPointF(x, y2));
521 }
522 }
523
524 //---------------------------------------------------------
525 // isTop
526 //---------------------------------------------------------
527
isTop() const528 bool BarLine::isTop() const
529 {
530 int idx = staffIdx();
531 if (idx == 0)
532 return true;
533 else
534 return (prevVisibleSpannedStaff(this) == idx);
535 }
536
537 //---------------------------------------------------------
538 // isBottom
539 //---------------------------------------------------------
540
isBottom() const541 bool BarLine::isBottom() const
542 {
543 if (!_spanStaff)
544 return true;
545 int idx = staffIdx();
546 if (idx == score()->nstaves() - 1)
547 return true;
548 else
549 return (nextVisibleSpannedStaff(this) == idx);
550 }
551
552 //---------------------------------------------------------
553 // draw
554 //---------------------------------------------------------
555
draw(QPainter * painter) const556 void BarLine::draw(QPainter* painter) const
557 {
558 switch (barLineType()) {
559 case BarLineType::NORMAL: {
560 qreal lw = score()->styleP(Sid::barWidth) * mag();
561 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
562 painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
563 }
564 break;
565
566 case BarLineType::BROKEN: {
567 qreal lw = score()->styleP(Sid::barWidth) * mag();
568 painter->setPen(QPen(curColor(), lw, Qt::DashLine, Qt::FlatCap));
569 painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
570 }
571 break;
572
573 case BarLineType::DOTTED: {
574 qreal lw = score()->styleP(Sid::barWidth) * mag();
575 painter->setPen(QPen(curColor(), lw, Qt::DotLine, Qt::FlatCap));
576 painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
577 }
578 break;
579
580 case BarLineType::END: {
581 qreal lw = score()->styleP(Sid::barWidth) * mag();
582 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
583 qreal x = lw * .5;
584 painter->drawLine(QLineF(x, y1, x, y2));
585
586 qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
587 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
588 x += score()->styleP(Sid::endBarDistance) * mag();
589 painter->drawLine(QLineF(x, y1, x, y2));
590 }
591 break;
592
593 case BarLineType::DOUBLE: {
594 qreal lw2 = score()->styleP(Sid::doubleBarWidth) * mag();
595 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
596 qreal x = lw2 * .5;
597 painter->drawLine(QLineF(x, y1, x, y2));
598 x += score()->styleP(Sid::doubleBarDistance) * mag();
599 painter->drawLine(QLineF(x, y1, x, y2));
600 }
601 break;
602
603 case BarLineType::REVERSE_END: {
604 qreal lw = score()->styleP(Sid::endBarWidth) * mag();
605 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
606 qreal x = lw * .5;
607 painter->drawLine(QLineF(x, y1, x, y2));
608
609 qreal lw2 = score()->styleP(Sid::barWidth) * mag();
610 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
611 x += score()->styleP(Sid::endBarDistance) * mag();
612 painter->drawLine(QLineF(x, y1, x, y2));
613 }
614 break;
615
616 case BarLineType::HEAVY: {
617 qreal lw = score()->styleP(Sid::endBarWidth) * mag();
618 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
619 painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
620 }
621 break;
622
623 case BarLineType::DOUBLE_HEAVY: {
624 qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
625 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
626 qreal x = lw2 * .5;
627 painter->drawLine(QLineF(x, y1, x, y2));
628 x += score()->styleP(Sid::endBarDistance) * mag();
629 painter->drawLine(QLineF(x, y1, x, y2));
630 }
631 break;
632
633 case BarLineType::START_REPEAT: {
634 qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
635 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
636 qreal x = lw2 * .5;
637 painter->drawLine(QLineF(x, y1, x, y2));
638
639 qreal lw = score()->styleP(Sid::barWidth) * mag();
640 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
641 x += score()->styleP(Sid::endBarDistance) * mag();
642 painter->drawLine(QLineF(x, y1, x, y2));
643
644 x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
645 x -= symBbox(SymId::repeatDot).width() * .5;
646 drawDots(painter, x);
647
648 if (score()->styleB(Sid::repeatBarTips))
649 drawTips(painter, false, 0.0);
650 }
651 break;
652
653 case BarLineType::END_REPEAT: {
654 qreal lw = score()->styleP(Sid::barWidth) * mag();
655 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
656
657 qreal x = 0.0; // symBbox(SymId::repeatDot).width() * .5;
658 drawDots(painter, x);
659
660 x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
661 x += symBbox(SymId::repeatDot).width() * .5;
662 painter->drawLine(QLineF(x, y1, x, y2));
663
664 x += score()->styleP(Sid::endBarDistance) * mag();
665
666 qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
667 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
668 painter->drawLine(QLineF(x, y1, x, y2));
669
670 if (score()->styleB(Sid::repeatBarTips))
671 drawTips(painter, true, x + lw2 * .5);
672 }
673 break;
674 case BarLineType::END_START_REPEAT: {
675 qreal lw = score()->styleP(Sid::barWidth) * mag();
676 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
677
678 qreal x = 0.0; // symBbox(SymId::repeatDot).width() * .5;
679 drawDots(painter, x);
680
681 x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
682 x += symBbox(SymId::repeatDot).width() * .5;
683 painter->drawLine(QLineF(x, y1, x, y2));
684
685 x += score()->styleP(Sid::endBarDistance) * mag();
686
687 qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
688 painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
689 painter->drawLine(QLineF(x, y1, x, y2));
690
691 if (score()->styleB(Sid::repeatBarTips))
692 drawTips(painter, true, x + lw2 * .5);
693
694 painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
695 x += score()->styleP(Sid::endBarDistance) * mag();
696 painter->drawLine(QLineF(x, y1, x, y2));
697
698 x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
699 x -= symBbox(SymId::repeatDot).width() * .5;
700 drawDots(painter, x);
701
702 if (score()->styleB(Sid::repeatBarTips))
703 drawTips(painter, false, 0.0);
704 }
705 break;
706 }
707 Segment* s = segment();
708 if (s && s->isEndBarLineType() && !score()->printing() && score()->showUnprintable()) {
709 Measure* m = s->measure();
710 if (m->isIrregular() && score()->markIrregularMeasures() && !m->isMMRest()) {
711 painter->setPen(MScore::layoutBreakColor);
712 QFont f("Edwin");
713 f.setPointSizeF(12 * spatium() * MScore::pixelRatio / SPATIUM20);
714 f.setBold(true);
715 QString str = m->ticks() > m->timesig() ? "+" : "-";
716 QRectF r = QFontMetricsF(f, MScore::paintDevice()).boundingRect(str);
717 painter->setFont(f);
718 painter->drawText(-r.width(), 0.0, str);
719 }
720 }
721 }
722
723 //---------------------------------------------------------
724 // drawEditMode
725 //---------------------------------------------------------
726
drawEditMode(QPainter * p,EditData & ed)727 void BarLine::drawEditMode(QPainter* p, EditData& ed)
728 {
729 Element::drawEditMode(p, ed);
730 BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
731 y1 += bed->yoff1;
732 y2 += bed->yoff2;
733 QPointF pos(canvasPos());
734 p->translate(pos);
735 BarLine::draw(p);
736 p->translate(-pos);
737 y1 -= bed->yoff1;
738 y2 -= bed->yoff2;
739 }
740
741 //---------------------------------------------------------
742 // playTick
743 //---------------------------------------------------------
744
playTick() const745 Fraction BarLine::playTick() const
746 {
747 // Play from the start of the measure to the right of the barline, unless this is the last barline in either the entire score or the system,
748 // in which case we should play from the start of the measure to the left of the barline.
749 const auto measure = findMeasure();
750 if (measure) {
751 const auto nextMeasure = findMeasure()->next();
752 if (!nextMeasure || (nextMeasure->system() != measure->system()))
753 return measure->tick();
754 }
755
756 return tick();
757 }
758
759 //---------------------------------------------------------
760 // write
761 //---------------------------------------------------------
762
write(XmlWriter & xml) const763 void BarLine::write(XmlWriter& xml) const
764 {
765 xml.stag(this);
766
767 writeProperty(xml, Pid::BARLINE_TYPE);
768 writeProperty(xml, Pid::BARLINE_SPAN);
769 writeProperty(xml, Pid::BARLINE_SPAN_FROM);
770 writeProperty(xml, Pid::BARLINE_SPAN_TO);
771
772 for (const Element* e : _el)
773 e->write(xml);
774 Element::writeProperties(xml);
775 xml.etag();
776 }
777
778 //---------------------------------------------------------
779 // read
780 //---------------------------------------------------------
781
read(XmlReader & e)782 void BarLine::read(XmlReader& e)
783 {
784 resetProperty(Pid::BARLINE_SPAN);
785 resetProperty(Pid::BARLINE_SPAN_FROM);
786 resetProperty(Pid::BARLINE_SPAN_TO);
787
788 while (e.readNextStartElement()) {
789 const QStringRef& tag(e.name());
790 if (tag == "subtype")
791 setBarLineType(e.readElementText());
792 else if (tag == "span")
793 _spanStaff = e.readBool();
794 else if (tag == "spanFromOffset")
795 _spanFrom = e.readInt();
796 else if (tag == "spanToOffset")
797 _spanTo = e.readInt();
798 else if (tag == "Articulation") {
799 Articulation* a = new Articulation(score());
800 a->read(e);
801 add(a);
802 }
803 else if (tag == "Symbol") {
804 Symbol* s = new Symbol(score());
805 s->setTrack(track());
806 s->read(e);
807 add(s);
808 }
809 else if (tag == "Image") {
810 if (MScore::noImages)
811 e.skipCurrentElement();
812 else {
813 Image* image = new Image(score());
814 image->setTrack(track());
815 image->read(e);
816 add(image);
817 }
818 }
819 else if (!Element::readProperties(e))
820 e.unknown();
821 }
822 }
823
824 //---------------------------------------------------------
825 // acceptDrop
826 //---------------------------------------------------------
827
acceptDrop(EditData & data) const828 bool BarLine::acceptDrop(EditData& data) const
829 {
830 ElementType type = data.dropElement->type();
831 if (type == ElementType::BAR_LINE) {
832 return true;
833 }
834 else {
835 return ((type == ElementType::ARTICULATION || type == ElementType::FERMATA || type == ElementType::SYMBOL || type == ElementType::IMAGE)
836 && segment()
837 && segment()->isEndBarLineType());
838 }
839 // Prevent unreachable code warning
840 // return false;
841 }
842
843 //---------------------------------------------------------
844 // drop
845 //---------------------------------------------------------
846
drop(EditData & data)847 Element* BarLine::drop(EditData& data)
848 {
849 Element* e = data.dropElement;
850
851 if (e->isBarLine()) {
852 BarLine* bl = toBarLine(e);
853 BarLineType st = bl->barLineType();
854
855 // if no change in subtype or no change in span, do nothing
856 if (st == barLineType() && !bl->spanFrom() && !bl->spanTo()) {
857 delete e;
858 return 0;
859 }
860
861 // check if the new property can apply to this single bar line
862 BarLineType bt = BarLineType::START_REPEAT | BarLineType::END_REPEAT | BarLineType::END_START_REPEAT;
863 bool oldRepeat = barLineType() & bt;
864 bool newRepeat = bl->barLineType() & bt;
865
866 // if ctrl was used and repeats are not involved,
867 // or if drop refers to span rather than subtype =>
868 // single bar line drop
869
870 if ((data.control() && !oldRepeat && !newRepeat) || (bl->spanFrom() || bl->spanTo()) ) {
871 // if drop refers to span, update this bar line span
872 if (bl->spanFrom() || bl->spanTo()) {
873 // if dropped spanFrom or spanTo are below the middle of standard staff (5 lines)
874 // adjust to the number of staff lines
875 // TODO:barlines
876 int spanFrom = bl->spanFrom();
877 int spanTo = bl->spanTo();
878 undoChangeProperty(Pid::BARLINE_SPAN, false);
879 undoChangeProperty(Pid::BARLINE_SPAN_FROM, spanFrom);
880 undoChangeProperty(Pid::BARLINE_SPAN_TO, spanTo);
881 }
882 // if drop refers to subtype, update this bar line subtype
883 else {
884 undoChangeBarLineType(this, st, false);
885 }
886 }
887 else {
888 undoChangeBarLineType(this, st, true);
889 }
890 delete e;
891 }
892 else if (e->isArticulation()) {
893 Articulation* atr = toArticulation(e);
894 atr->setParent(this);
895 atr->setTrack(track());
896 score()->undoAddElement(atr);
897 return atr;
898 }
899 else if (e->isSymbol() || e->isImage()) {
900 e->setParent(this);
901 e->setTrack(track());
902 score()->undoAddElement(e);
903 return e;
904 }
905 else if (e->isFermata()) {
906 e->setPlacement(track() & 1 ? Placement::BELOW : Placement::ABOVE);
907 for (Element* el: segment()->annotations())
908 if (el->isFermata() && (el->track() == track())) {
909 if (el->subtype() == e->subtype()) {
910 delete e;
911 return el;
912 }
913 else {
914 e->setPlacement(el->placement());
915 e->setTrack(track());
916 e->setParent(segment());
917 score()->undoChangeElement(el, e);
918 return e;
919 }
920 }
921 e->setTrack(track());
922 e->setParent(segment());
923 score()->undoAddElement(e);
924 return e;
925 }
926 return 0;
927 }
928
929 //---------------------------------------------------------
930 // gripsPositions
931 //---------------------------------------------------------
932
gripsPositions(const EditData & ed) const933 std::vector<QPointF> BarLine::gripsPositions(const EditData& ed) const
934 {
935 const BarLineEditData* bed = static_cast<const BarLineEditData*>(ed.getData(this));
936
937 qreal lw = score()->styleP(Sid::barWidth) * staff()->mag(tick());
938 getY();
939
940 const QPointF pp = pagePos();
941
942 return {
943 QPointF(lw * .5, y1 + bed->yoff1) + pp,
944 QPointF(lw * .5, y2 + bed->yoff2) + pp
945 };
946 }
947
948 //---------------------------------------------------------
949 // startEdit
950 //---------------------------------------------------------
951
startEdit(EditData & ed)952 void BarLine::startEdit(EditData& ed)
953 {
954 BarLineEditData* bed = new BarLineEditData();
955 bed->e = this;
956 bed->yoff1 = 0;
957 bed->yoff2 = 0;
958 ed.addData(bed);
959 }
960
961 //---------------------------------------------------------
962 // endEdit
963 //---------------------------------------------------------
964
endEdit(EditData &)965 void BarLine::endEdit(EditData&)
966 {
967 #if 0 // TODO
968 if (ctrlDrag) { // if single bar line edit
969 char newSpanStaff = _spanStaff; // copy edited span values
970 char newSpanFrom = _spanFrom;
971 char newSpanTo = _spanTo;
972 _spanStaff = _origSpanStaff; // restore original span values
973 _spanFrom = _origSpanFrom;
974 _spanTo = _origSpanTo;
975 // for mid-measure barline in root score, update parts
976 if (midMeasure && score()->isMaster() && score()->excerpts().size() > 0) {
977 int currIdx = staffIdx();
978 Measure* m = segment()->measure();
979 // change linked barlines as necessary
980 int lastIdx = currIdx + qMax(_span, newSpanStaff);
981 for (int idx = currIdx; idx < lastIdx; ++idx) {
982 Staff* staff = score()->staff(idx);
983 LinkedStaves* ls = staff->linkedStaves();
984 if (ls) {
985 for (Staff* lstaff : ls->staves()) {
986 Score* lscore = lstaff->score();
987 // don't change barlines in root score
988 if (lscore == staff->score())
989 continue;
990 // change barline only in top staff of part
991 if (lstaff != lscore->staff(0))
992 continue;
993 int spannedStaves = qMax(currIdx + newSpanStaff - idx, 0);
994 int lNewSpan = qMin(spannedStaves, lscore->nstaves());
995 Measure* lm = lscore->tick2measure(m->tick());
996 Segment* lseg = lm->undoGetSegmentR(SegmentType::BarLine, rtick());
997 BarLine* lbl = toBarLine(lseg->element(0));
998 if (lbl) {
999 // already a barline here
1000 if (lNewSpan > 0) {
1001 // keep barline, but update span if necessary
1002 if (lbl->span() != lNewSpan)
1003 lbl->undoChangeProperty(Pid::BARLINE_SPAN, lNewSpan);
1004 }
1005 else {
1006 // remove barline
1007 lbl->unlink();
1008 lbl->score()->undoRemoveElement(lbl);
1009 }
1010 }
1011 else {
1012 // new barline needed
1013 lbl = toBarLine(linkedClone());
1014 lbl->setSpan(lNewSpan);
1015 lbl->setTrack(lstaff->idx() * VOICES);
1016 lbl->setScore(lscore);
1017 lbl->setParent(lseg);
1018 lscore->undoAddElement(lbl);
1019 }
1020 }
1021 }
1022 }
1023 }
1024 undoChangeProperty(Pid::BARLINE_SPAN, newSpanStaff);
1025 undoChangeProperty(Pid::BARLINE_SPAN_FROM, newSpanFrom);
1026 undoChangeProperty(Pid::BARLINE_SPAN_FROM, newSpanTo);
1027 return;
1028 }
1029
1030 // if same as staff settings, do nothing
1031 if (staff()->barLineSpan() == _spanStaff && staff()->barLineFrom() == _spanFrom && staff()->barLineTo() == _spanTo)
1032 return;
1033
1034 // int idx1 = staffIdx();
1035 if (_span != staff()->barLineSpan()) {
1036 // if now bar lines span more staves
1037 if (_span > staff()->barLineSpan()) {
1038 int idx2 = idx1 + _span;
1039 // set span 0 to all additional staves
1040 for (int idx = idx1 + 1; idx < idx2; ++idx) {
1041 // Mensurstrich special case:
1042 // if line spans to top line of a stave AND current staff is
1043 // the last spanned staff BUT NOT the last score staff
1044 // keep its bar lines
1045 // otherwise remove them
1046 if (_spanTo > 0 || !(idx == idx2-1 && idx != score()->nstaves()-1)) {
1047 Staff* staff = score()->staff(idx);
1048 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 0);
1049 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, 0);
1050 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, (staff->lines(tick())-1)*2);
1051 }
1052 }
1053 }
1054 // if now bar lines span fewer staves
1055 else {
1056 int idx1 = staffIdx() + _span;
1057 int idx2 = staffIdx() + staff()->barLineSpan();
1058 // set standard span for each no-longer-spanned staff
1059 for (int idx = idx1; idx < idx2; ++idx) {
1060 Staff* staff = score()->staff(idx);
1061 int lines = staff->lines(tick());
1062 int spanFrom = 0;
1063 int spanTo = 0;
1064 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 1);
1065 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, spanFrom);
1066 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, spanTo);
1067 }
1068 }
1069 }
1070 // update span for the staff the edited bar line belongs to
1071 staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, _spanStaff);
1072 staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, _spanFrom);
1073 staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, _spanTo);
1074 #endif
1075 }
1076
1077 //---------------------------------------------------------
1078 // editDrag
1079 //---------------------------------------------------------
1080
editDrag(EditData & ed)1081 void BarLine::editDrag(EditData& ed)
1082 {
1083 BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
1084
1085 qreal lineDist = staff()->lineDistance(tick()) * spatium();
1086 getY();
1087 if (ed.curGrip == Grip::START) {
1088 // Start grip moving is useless currently, so disable it
1089 return;
1090 #if 0
1091 // min offset for top grip is line -1 (-2 for 1-line staves)
1092 // max offset is 1 line above bottom grip or 1 below last staff line, whichever comes first
1093 int lines = staff()->lines(tick());
1094 qreal min = (-y1 - lines == 1) ? lineDist * 2 : lineDist;
1095 qreal max = y2 - y1 - lineDist; // 1 line above bottom grip
1096 qreal lastmax = (lines - _spanFrom/2) * lineDist; // 1 line below last staff line
1097 if (lastmax < max)
1098 max = lastmax;
1099 // update yoff1 and bring it within limits
1100 bed->yoff1 += ed.delta.y();
1101 if (bed->yoff1 < min)
1102 bed->yoff1 = min;
1103 if (bed->yoff1 > max)
1104 bed->yoff1 = max;
1105 #endif
1106 }
1107 else {
1108 // min for bottom grip is 1 line below top grip
1109 const qreal min = y1 - y2 + lineDist;
1110 // max is the bottom of the system
1111 const System* system = segment() ? segment()->system() : nullptr;
1112 const int st = staffIdx();
1113 const qreal max = (system && st != -1) ? (system->height() - y2 - system->staff(st)->y()) : std::numeric_limits<qreal>::max();
1114 // update yoff2 and bring it within limit
1115 bed->yoff2 += ed.delta.y();
1116 if (bed->yoff2 < min)
1117 bed->yoff2 = min;
1118 if (bed->yoff2 > max)
1119 bed->yoff2 = max;
1120 }
1121 }
1122
1123 //---------------------------------------------------------
1124 // endEditDrag
1125 // snap to nearest staff / staff line
1126 //---------------------------------------------------------
1127
endEditDrag(EditData & ed)1128 void BarLine::endEditDrag(EditData& ed)
1129 {
1130 getY();
1131 BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
1132 y1 += bed->yoff1;
1133 y2 += bed->yoff2;
1134
1135 qreal ay0 = pagePos().y();
1136 qreal ay2 = ay0 + y2; // absolute (page-relative) bar line bottom coord
1137 int staffIdx1 = staffIdx();
1138 System* syst = segment()->measure()->system();
1139 qreal systTopY = syst->pagePos().y();
1140
1141 // determine new span value
1142 int staffIdx2;
1143 int numOfStaves = syst->staves()->size();
1144 if (staffIdx1 + 1 >= numOfStaves)
1145 // if initial staff is last staff, ending staff must be the same
1146 staffIdx2 = staffIdx1;
1147
1148 else {
1149 // if there are other staves after it, look for staff nearest to bar line bottom coord
1150 qreal staff1TopY = syst->staff(staffIdx1)->y() + systTopY;
1151
1152 for (staffIdx2 = staffIdx1 + 1; staffIdx2 < numOfStaves; ++staffIdx2) {
1153 // compute 1st staff height, absolute top Y of 2nd staff and height of blank between the staves
1154 Staff * staff1 = score()->staff(staffIdx2-1);
1155 qreal staff1Hght = (staff1->lines(tick())-1) * staff1->lineDistance(tick()) * spatium();
1156 qreal staff2TopY = systTopY + syst->staff(staffIdx2)->y();
1157 qreal blnkBtwnStaff = staff2TopY - staff1TopY - staff1Hght;
1158 // if bar line bottom coord is above than mid-way of blank between staves...
1159 if (ay2 < (staff1TopY + staff1Hght + blnkBtwnStaff * .5))
1160 break; // ...staff 1 is ending staff
1161 // if bar line is below, advance to next staff
1162 staff1TopY = staff2TopY;
1163 }
1164 staffIdx2 -= 1;
1165 }
1166
1167 // determine new spanFrom and spanTo values
1168 int newSpanFrom, newSpanTo;
1169 // Staff * staff2 = score()->staff(staffIdx2);
1170 // int staff1lines = staff()->lines(tick());
1171 // int staff2lines = staff2->lines(tick());
1172
1173 #if 0 // TODO
1174 if (shiftDrag) { // if precision dragging
1175 newSpanFrom = _spanFrom;
1176 if (yoff1 != 0.0) {
1177 // round bar line top coord to nearest line of 1st staff (in half line dist units)
1178 newSpanFrom = ((int)floor(y1 / (staff()->lineDistance(tick()) * spatium()) + 0.5 )) * 2;
1179 // min = 1 line dist above 1st staff line | max = 1 line dist below last staff line
1180 // except for 1-line staves
1181 int minFrom = staff1lines == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : MIN_BARLINE_SPAN_FROMTO;
1182 if (newSpanFrom < minFrom)
1183 newSpanFrom = minFrom;
1184 if (newSpanFrom > staff1lines * 2)
1185 newSpanFrom = staff1lines * 2;
1186 }
1187
1188 newSpanTo = _spanTo;
1189 if (yoff2 != 0.0) {
1190 // round bar line bottom coord to nearest line of 2nd staff (in half line dist units)
1191 qreal staff2TopY = systTopY + syst->staff(staffIdx2)->y();
1192 newSpanTo = ((int)floor( (ay2 - staff2TopY) / (staff2->lineDistance(tick()) * spatium()) + 0.5 )) * 2;
1193 // min = 1 line dist above 1st staff line | max = 1 line dist below last staff line
1194 int maxTo = staff2lines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : staff2lines * 2;
1195 if (newSpanTo < MIN_BARLINE_SPAN_FROMTO)
1196 newSpanTo = MIN_BARLINE_SPAN_FROMTO;
1197 if (newSpanTo > maxTo)
1198 newSpanTo = maxTo;
1199 }
1200 }
1201 #endif
1202 // else { // if coarse dragging
1203 { // if coarse dragging
1204 newSpanFrom = 0;
1205 newSpanTo = 0;
1206 }
1207
1208 bool localDrag = ed.control() || segment()->isBarLineType();
1209 if (localDrag) {
1210 Segment* s = segment();
1211 for (int staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx) {
1212 BarLine* b = toBarLine(s->element(staffIdx * VOICES));
1213 if (!b) {
1214 b = toBarLine(linkedClone());
1215 b->setSpanStaff(true);
1216 b->setTrack(staffIdx * VOICES);
1217 b->setScore(score());
1218 b->setParent(s);
1219 score()->undoAddElement(b);
1220 }
1221 b->undoChangeProperty(Pid::BARLINE_SPAN, true);
1222 }
1223 BarLine* b = toBarLine(s->element(staffIdx2 * VOICES));
1224 if (b)
1225 b->undoChangeProperty(Pid::BARLINE_SPAN, false);
1226 }
1227 else {
1228 for (int staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx)
1229 score()->staff(staffIdx)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, true);
1230 score()->staff(staffIdx2)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, false);
1231 staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, newSpanFrom);
1232 staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, newSpanTo);
1233 }
1234
1235 bed->yoff1 = 0.0;
1236 bed->yoff2 = 0.0;
1237 }
1238
1239 //---------------------------------------------------------
1240 // layoutWidth
1241 //---------------------------------------------------------
1242
layoutWidth(Score * score,BarLineType type)1243 qreal BarLine::layoutWidth(Score* score, BarLineType type)
1244 {
1245 qreal dotwidth = score->scoreFont()->width(SymId::repeatDot, 1.0);
1246
1247 qreal w {0.0};
1248 switch (type) {
1249 case BarLineType::DOUBLE:
1250 w = score->styleP(Sid::doubleBarWidth) + score->styleP(Sid::doubleBarDistance);
1251 break;
1252 case BarLineType::DOUBLE_HEAVY:
1253 w = score->styleP(Sid::endBarWidth) + score->styleP(Sid::endBarDistance);
1254 break;
1255 case BarLineType::END_START_REPEAT:
1256 w = score->styleP(Sid::endBarDistance) * 2
1257 + score->styleP(Sid::repeatBarlineDotSeparation) * 2
1258 + dotwidth;
1259 break;
1260 case BarLineType::START_REPEAT:
1261 case BarLineType::END_REPEAT:
1262 w = score->styleP(Sid::endBarWidth) * .5
1263 + score->styleP(Sid::endBarDistance)
1264 + score->styleP(Sid::repeatBarlineDotSeparation)
1265 + dotwidth * .5;
1266 break;
1267 case BarLineType::END:
1268 case BarLineType::REVERSE_END:
1269 w = (score->styleP(Sid::endBarWidth) + score->styleP(Sid::barWidth)) * .5
1270 + score->styleP(Sid::endBarDistance);
1271 break;
1272 case BarLineType::BROKEN:
1273 case BarLineType::NORMAL:
1274 case BarLineType::DOTTED:
1275 w = score->styleP(Sid::barWidth);
1276 break;
1277 case BarLineType::HEAVY:
1278 w = score->styleP(Sid::endBarWidth);
1279 break;
1280 }
1281 return w;
1282 }
1283
1284 //---------------------------------------------------------
1285 // layoutRect
1286 //---------------------------------------------------------
1287
layoutRect() const1288 QRectF BarLine::layoutRect() const
1289 {
1290 QRectF bb = bbox();
1291 if (staff()) {
1292 // actual height may include span to next staff
1293 // but this should not be included in shapes or skylines
1294 qreal sp = spatium();
1295 int span = staff()->lines(tick()) - 1;
1296 int sFrom;
1297 int sTo;
1298 if (span == 0 && _spanTo == 0) {
1299 sFrom = BARLINE_SPAN_1LINESTAFF_FROM;
1300 sTo = _spanStaff ? 0 : BARLINE_SPAN_1LINESTAFF_TO;
1301 }
1302 else {
1303 sFrom = _spanFrom;
1304 sTo = _spanStaff ? 0 : _spanTo;
1305 }
1306 qreal y = sp * sFrom * 0.5;
1307 qreal h = sp * (span + (sTo - sFrom) * 0.5);
1308 if (score()->styleB(Sid::repeatBarTips)) {
1309 switch (barLineType()) {
1310 case BarLineType::START_REPEAT:
1311 case BarLineType::END_REPEAT:
1312 case BarLineType::END_START_REPEAT: {
1313 if (isTop()) {
1314 qreal top = symBbox(SymId::bracketTop).height();
1315 y -= top;
1316 h += top;
1317 }
1318 if (isBottom()) {
1319 qreal bottom = symBbox(SymId::bracketBottom).height();
1320 h += bottom;
1321 }
1322 }
1323 default:
1324 break;
1325 }
1326 }
1327 bb.setY(y);
1328 bb.setHeight(h);
1329 }
1330 return bb;
1331 }
1332
1333 //---------------------------------------------------------
1334 // layout
1335 //---------------------------------------------------------
1336
layout()1337 void BarLine::layout()
1338 {
1339 setPos(QPointF());
1340 // barlines hidden on this staff
1341 if (staff() && segment()) {
1342 if ((!staff()->staffTypeForElement(this)->showBarlines() && segment()->segmentType() == SegmentType::EndBarLine)
1343 || (staff()->hideSystemBarLine() && segment()->segmentType() == SegmentType::BeginBarLine)) {
1344 setbbox(QRectF());
1345 return;
1346 }
1347 }
1348
1349 setMag(score()->styleB(Sid::scaleBarlines) && staff() ? staff()->mag(tick()) : 1.0);
1350 qreal _spatium = spatium();
1351 y1 = _spatium * .5 * _spanFrom;
1352 y2 = _spatium * .5 * (8.0 + _spanTo);
1353
1354 qreal w = layoutWidth(score(), barLineType()) * mag();
1355 QRectF r(0.0, y1, w, y2 - y1);
1356
1357 if (score()->styleB(Sid::repeatBarTips)) {
1358 switch (barLineType()) {
1359 case BarLineType::START_REPEAT:
1360 r |= symBbox(SymId::bracketTop).translated(0, y1);
1361 // r |= symBbox(SymId::bracketBottom).translated(0, y2);
1362 break;
1363 case BarLineType::END_REPEAT: {
1364 qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
1365 r |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
1366 // r |= symBbox(SymId::reversedBracketBottom).translated(0, y2);
1367 }
1368 break;
1369 case BarLineType::END_START_REPEAT: {
1370 qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
1371 r |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
1372 r |= symBbox(SymId::bracketTop).translated(0, y1);
1373 // r |= symBbox(SymId::reversedBracketBottom).translated(0, y2);
1374 }
1375 break;
1376 default:
1377 break;
1378 }
1379 }
1380 setbbox(r);
1381
1382 for (Element* e : _el) {
1383 e->layout();
1384 if (e->isArticulation()) {
1385 Articulation* a = toArticulation(e);
1386 Direction dir = a->direction();
1387 qreal distance = 0.5 * spatium();
1388 qreal x = width() * .5;
1389 if (dir == Direction::DOWN) {
1390 qreal botY = y2 + distance;
1391 a->setPos(QPointF(x, botY));
1392 }
1393 else {
1394 qreal topY = y1 - distance;
1395 a->setPos(QPointF(x, topY));
1396 }
1397 }
1398 }
1399 }
1400
1401 //---------------------------------------------------------
1402 // layout2
1403 // called after system layout; set vertical dimensions
1404 //---------------------------------------------------------
1405
layout2()1406 void BarLine::layout2()
1407 {
1408 // barlines hidden on this staff
1409 if (staff() && segment()) {
1410 if ((!staff()->staffTypeForElement(this)->showBarlines() && segment()->segmentType() == SegmentType::EndBarLine)
1411 || (staff()->hideSystemBarLine() && segment()->segmentType() == SegmentType::BeginBarLine)) {
1412 setbbox(QRectF());
1413 return;
1414 }
1415 }
1416
1417 getY();
1418 bbox().setTop(y1);
1419 bbox().setBottom(y2);
1420
1421 if (score()->styleB(Sid::repeatBarTips)) {
1422 switch (barLineType()) {
1423 case BarLineType::START_REPEAT:
1424 bbox() |= symBbox(SymId::bracketTop).translated(0, y1);
1425 bbox() |= symBbox(SymId::bracketBottom).translated(0, y2);
1426 break;
1427 case BarLineType::END_REPEAT:
1428 {
1429 qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
1430 bbox() |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
1431 bbox() |= symBbox(SymId::reversedBracketBottom).translated(-w1, y2);
1432 break;
1433 }
1434 case BarLineType::END_START_REPEAT:
1435 {
1436 qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
1437 bbox() |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
1438 bbox() |= symBbox(SymId::reversedBracketBottom).translated(-w1, y2);
1439 bbox() |= symBbox(SymId::bracketTop).translated(0, y1);
1440 bbox() |= symBbox(SymId::bracketBottom).translated(0, y2);
1441 break;
1442 }
1443 default:
1444 break;
1445 }
1446 }
1447 }
1448
1449 //---------------------------------------------------------
1450 // shape
1451 //---------------------------------------------------------
1452
shape() const1453 Shape BarLine::shape() const
1454 {
1455 Shape shape;
1456 #ifndef NDEBUG
1457 shape.add(bbox(), name());
1458 #else
1459 shape.add(bbox());
1460 #endif
1461 return shape;
1462 }
1463
1464 //---------------------------------------------------------
1465 // scanElements
1466 //---------------------------------------------------------
1467
scanElements(void * data,void (* func)(void *,Element *),bool all)1468 void BarLine::scanElements(void* data, void (*func)(void*, Element*), bool all)
1469 {
1470 // if no width (staff has bar lines turned off) and not all requested, do nothing
1471 if (width() == 0.0 && !all)
1472 return;
1473 func(data, this);
1474 for (Element* e : _el)
1475 e->scanElements(data, func, all);
1476 }
1477
1478 //---------------------------------------------------------
1479 // setTrack
1480 //---------------------------------------------------------
1481
setTrack(int t)1482 void BarLine::setTrack(int t)
1483 {
1484 Element::setTrack(t);
1485 for (Element* e : _el)
1486 e->setTrack(t);
1487 }
1488
1489 //---------------------------------------------------------
1490 // setScore
1491 //---------------------------------------------------------
1492
setScore(Score * s)1493 void BarLine::setScore(Score* s)
1494 {
1495 Element::setScore(s);
1496 for (Element* e : _el)
1497 e->setScore(s);
1498 }
1499
1500 //---------------------------------------------------------
1501 // add
1502 //---------------------------------------------------------
1503
add(Element * e)1504 void BarLine::add(Element* e)
1505 {
1506 e->setParent(this);
1507 switch (e->type()) {
1508 case ElementType::ARTICULATION:
1509 case ElementType::SYMBOL:
1510 case ElementType::IMAGE:
1511 _el.push_back(e);
1512 setGenerated(false);
1513 break;
1514 default:
1515 qDebug("BarLine::add() not impl. %s", e->name());
1516 delete e;
1517 break;
1518 }
1519 }
1520
1521 //---------------------------------------------------------
1522 // remove
1523 //---------------------------------------------------------
1524
remove(Element * e)1525 void BarLine::remove(Element* e)
1526 {
1527 switch(e->type()) {
1528 case ElementType::ARTICULATION:
1529 case ElementType::SYMBOL:
1530 case ElementType::IMAGE:
1531 if (!_el.remove(e))
1532 qDebug("BarLine::remove(): cannot find %s", e->name());
1533 break;
1534 default:
1535 qDebug("BarLine::remove() not impl. %s", e->name());
1536 break;
1537 }
1538 }
1539
1540 //---------------------------------------------------------
1541 // getProperty
1542 //---------------------------------------------------------
1543
getProperty(Pid id) const1544 QVariant BarLine::getProperty(Pid id) const
1545 {
1546 switch (id) {
1547 case Pid::BARLINE_TYPE:
1548 return QVariant::fromValue(_barLineType);
1549 case Pid::BARLINE_SPAN:
1550 return spanStaff();
1551 case Pid::BARLINE_SPAN_FROM:
1552 return int(spanFrom());
1553 case Pid::BARLINE_SPAN_TO:
1554 return int(spanTo());
1555 default:
1556 break;
1557 }
1558 return Element::getProperty(id);
1559 }
1560
1561 //---------------------------------------------------------
1562 // setProperty
1563 //---------------------------------------------------------
1564
setProperty(Pid id,const QVariant & v)1565 bool BarLine::setProperty(Pid id, const QVariant& v)
1566 {
1567 switch (id) {
1568 case Pid::BARLINE_TYPE:
1569 setBarLineType(v.value<BarLineType>());
1570 break;
1571 case Pid::BARLINE_SPAN:
1572 setSpanStaff(v.toBool());
1573 break;
1574 case Pid::BARLINE_SPAN_FROM:
1575 setSpanFrom(v.toInt());
1576 break;
1577 case Pid::BARLINE_SPAN_TO:
1578 setSpanTo(v.toInt());
1579 break;
1580 default:
1581 return Element::setProperty(id, v);
1582 }
1583 setGenerated(false);
1584 triggerLayout();
1585 return true;
1586 }
1587
1588 //---------------------------------------------------------
1589 // undoChangeProperty
1590 //---------------------------------------------------------
1591
undoChangeProperty(Pid id,const QVariant & v,PropertyFlags ps)1592 void BarLine::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
1593 {
1594 if (id == Pid::BARLINE_TYPE && segment()) {
1595 const BarLine* bl = this;
1596 BarLineType blType = v.value<BarLineType>();
1597 if (blType == BarLineType::START_REPEAT) { // change next measures endBarLine
1598 if (bl->measure()->nextMeasure())
1599 bl = bl->measure()->nextMeasure()->endBarLine();
1600 else
1601 bl = 0;
1602 }
1603 if (bl)
1604 undoChangeBarLineType(const_cast<BarLine*>(bl), v.value<BarLineType>(), true);
1605 }
1606 else
1607 ScoreElement::undoChangeProperty(id, v, ps);
1608 }
1609
1610 //---------------------------------------------------------
1611 // propertyDefault
1612 //---------------------------------------------------------
1613
propertyDefault(Pid propertyId) const1614 QVariant BarLine::propertyDefault(Pid propertyId) const
1615 {
1616 switch (propertyId) {
1617 case Pid::BARLINE_TYPE:
1618 // dynamic default values are a bad idea: writing to xml the value maybe ommited resulting in
1619 // wrong values on read (as the default may be different on read)
1620 // if (segment() && segment()->measure() && !segment()->measure()->nextMeasure())
1621 // return QVariant::fromValue(BarLineType::END);
1622 return QVariant::fromValue(BarLineType::NORMAL);
1623
1624 case Pid::BARLINE_SPAN:
1625 return staff() ? staff()->barLineSpan() : false;
1626
1627 case Pid::BARLINE_SPAN_FROM:
1628 return staff() ? staff()->barLineFrom() : 0;
1629
1630 case Pid::BARLINE_SPAN_TO:
1631 return staff() ? staff()->barLineTo() : 0;
1632
1633 default:
1634 break;
1635 }
1636 return Element::propertyDefault(propertyId);
1637 }
1638
1639 //---------------------------------------------------------
1640 // propertyId
1641 //---------------------------------------------------------
1642
propertyId(const QStringRef & name) const1643 Pid BarLine::propertyId(const QStringRef& name) const
1644 {
1645 if (name == "subtype")
1646 return Pid::BARLINE_TYPE;
1647 return Element::propertyId(name);
1648 }
1649
1650 //---------------------------------------------------------
1651 // nextSegmentElement
1652 //---------------------------------------------------------
1653
nextSegmentElement()1654 Element* BarLine::nextSegmentElement()
1655 {
1656 return segment()->firstInNextSegments(staffIdx()); //score()->inputState().prevTrack() / VOICES);
1657 }
1658
1659 //---------------------------------------------------------
1660 // prevSegmentElement
1661 //---------------------------------------------------------
1662
prevSegmentElement()1663 Element* BarLine::prevSegmentElement()
1664 {
1665 return segment()->lastInPrevSegments(staffIdx()); //score()->inputState().prevTrack() / VOICES);
1666 }
1667
1668 //---------------------------------------------------------
1669 // accessibleInfo
1670 //---------------------------------------------------------
1671
accessibleInfo() const1672 QString BarLine::accessibleInfo() const
1673 {
1674 return QString("%1: %2").arg(Element::accessibleInfo(), BarLine::userTypeName(barLineType()));
1675 }
1676
1677 //---------------------------------------------------------
1678 // accessibleExtraInfo
1679 //---------------------------------------------------------
1680
accessibleExtraInfo() const1681 QString BarLine::accessibleExtraInfo() const
1682 {
1683 Segment* seg = segment();
1684 QString rez;
1685
1686 for (const Element* e : *el()) {
1687 if (!score()->selectionFilter().canSelect(e))
1688 continue;
1689 rez = QString("%1 %2").arg(rez, e->screenReaderInfo());
1690 }
1691
1692 for (const Element* e : seg->annotations()) {
1693 if (!score()->selectionFilter().canSelect(e))
1694 continue;
1695 if (e->track() == track())
1696 rez = QString("%1 %2").arg(rez, e->screenReaderInfo());
1697 }
1698 Measure* m = seg->measure();
1699
1700 if (m) { // always true?
1701 //jumps
1702 for (const Element* e : m->el()) {
1703 if (!score()->selectionFilter().canSelect(e)) continue;
1704 if (e->type() == ElementType::JUMP)
1705 rez= QString("%1 %2").arg(rez, e->screenReaderInfo());
1706 if (e->type() == ElementType::MARKER) {
1707 const Marker* m1 = toMarker(e);
1708 if (m1->markerType() == Marker::Type::FINE)
1709 rez = QString("%1 %2").arg(rez, e->screenReaderInfo());
1710 }
1711
1712 }
1713 //markers
1714 Measure* nextM = m->nextMeasureMM();
1715 if (nextM) {
1716 for (const Element* e : nextM->el()) {
1717 if (!score()->selectionFilter().canSelect(e))
1718 continue;
1719 if (e->isMarker()) {
1720 if (toMarker(e)->markerType() == Marker::Type::FINE)
1721 continue; //added above^
1722 rez = QString("%1 %2").arg(rez, e->screenReaderInfo());
1723 }
1724 }
1725 }
1726 }
1727
1728 Fraction tick = seg->tick();
1729
1730 auto spanners = score()->spannerMap().findOverlapping(tick.ticks(), tick.ticks());
1731 for (auto interval : spanners) {
1732 Spanner* s = interval.value;
1733 if (!score()->selectionFilter().canSelect(s))
1734 continue;
1735 if (s->type() == ElementType::VOLTA) {
1736 if (s->tick() == tick)
1737 rez = QObject::tr("%1 Start of %2").arg(rez, s->screenReaderInfo());
1738 if (s->tick2() == tick)
1739 rez = QObject::tr("%1 End of %2").arg(rez, s->screenReaderInfo());
1740 }
1741 }
1742 return rez;
1743 }
1744
1745 }
1746
1747