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