Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "barline.h"
24 :
25 : #include "translation.h"
26 :
27 : #include "../types/symnames.h"
28 :
29 : #include "articulation.h"
30 : #include "marker.h"
31 : #include "masterscore.h"
32 : #include "measure.h"
33 : #include "part.h"
34 : #include "playcounttext.h"
35 : #include "score.h"
36 : #include "segment.h"
37 : #include "spanner.h"
38 : #include "staff.h"
39 : #include "stafflines.h"
40 : #include "stafftype.h"
41 : #include "system.h"
42 :
43 : #include "log.h"
44 :
45 : using namespace mu;
46 : using namespace muse;
47 : using namespace muse::draw;
48 : using namespace mu::engraving;
49 : using namespace mu::engraving::read400;
50 :
51 : //---------------------------------------------------------
52 : // BarLineTable
53 : //---------------------------------------------------------
54 :
55 : const std::vector<BarLineTableItem> BarLine::barLineTable {
56 : { BarLineType::NORMAL, SymNames::userNameForSymId(SymId::barlineSingle) },
57 : { BarLineType::DOUBLE, SymNames::userNameForSymId(SymId::barlineDouble) },
58 : { BarLineType::START_REPEAT, SymNames::userNameForSymId(SymId::repeatLeft) },
59 : { BarLineType::END_REPEAT, SymNames::userNameForSymId(SymId::repeatRight) },
60 : { BarLineType::BROKEN, SymNames::userNameForSymId(SymId::barlineDashed) },
61 : { BarLineType::END, SymNames::userNameForSymId(SymId::barlineFinal) },
62 : { BarLineType::END_START_REPEAT, SymNames::userNameForSymId(SymId::repeatRightLeft) },
63 : { BarLineType::DOTTED, SymNames::userNameForSymId(SymId::barlineDotted) },
64 : { BarLineType::REVERSE_END, SymNames::userNameForSymId(SymId::barlineReverseFinal) },
65 : { BarLineType::HEAVY, SymNames::userNameForSymId(SymId::barlineHeavy) },
66 : { BarLineType::DOUBLE_HEAVY, SymNames::userNameForSymId(SymId::barlineHeavyHeavy) },
67 : };
68 :
69 : //---------------------------------------------------------
70 : // translatedUserTypeName
71 : //---------------------------------------------------------
72 :
73 0 : String BarLine::translatedUserTypeName(BarLineType t)
74 : {
75 0 : for (const auto& i : barLineTable) {
76 0 : if (i.type == t) {
77 0 : return i.userName.translated();
78 : }
79 : }
80 0 : return String();
81 : }
82 :
83 : //---------------------------------------------------------
84 : // BarLine
85 : //---------------------------------------------------------
86 :
87 27880 : BarLine::BarLine(DummyParentOr<Segment> parent)
88 27880 : : EngravingItem(ElementType::BAR_LINE, parent)
89 : {
90 27880 : setHeight(4 * spatium()); // for use in palettes
91 27880 : }
92 :
93 167 : BarLine::BarLine(const BarLine& bl)
94 167 : : EngravingItem(bl)
95 : {
96 167 : m_spanStaff = bl.m_spanStaff;
97 167 : m_spanFrom = bl.m_spanFrom;
98 167 : m_spanTo = bl.m_spanTo;
99 167 : m_barLineType = bl.m_barLineType;
100 167 : m_playCount = bl.m_playCount;
101 :
102 167 : for (EngravingItem* e : bl.m_el) {
103 0 : add(e->clone());
104 : }
105 167 : }
106 :
107 52196 : BarLine::~BarLine()
108 : {
109 26098 : muse::DeleteAll(m_el);
110 52196 : }
111 :
112 19 : void BarLine::setOwnershipParent(DummyParentOr<Segment> parent)
113 : {
114 19 : EngravingItem::setOwnershipParent(parent);
115 19 : }
116 :
117 : //---------------------------------------------------------
118 : // canvasPos
119 : //---------------------------------------------------------
120 :
121 0 : PointF BarLine::canvasPos() const
122 : {
123 0 : PointF pos = EngravingItem::canvasPos();
124 0 : if (ownershipParent()) {
125 0 : System* system = measure()->system();
126 0 : double yoff = system ? system->staff(staffIdx())->y() : 0.0;
127 0 : pos.ry() += yoff;
128 : }
129 0 : return pos;
130 : }
131 :
132 : //---------------------------------------------------------
133 : // pagePos
134 : //---------------------------------------------------------
135 :
136 8604 : PointF BarLine::pagePos() const
137 : {
138 8604 : if (segment() == 0) {
139 0 : return pos();
140 : }
141 8604 : System* system = segment()->measure()->system();
142 :
143 8604 : double yp = y();
144 8604 : if (system) {
145 : // get first not hidden staff
146 8604 : staff_idx_t startIdx = staffIdx();
147 8604 : staff_idx_t endIdx = startIdx + (spanStaff() ? 1 : 0);
148 8604 : staff_idx_t staffIdx1 = startIdx;
149 8604 : Staff* staff1 = score()->staff(staffIdx1);
150 8604 : SysStaff* sysStaff1 = system->staff(staffIdx1);
151 :
152 8604 : while (staff1 && sysStaff1 && !(sysStaff1->show() && staff1->show())) {
153 0 : if (++staffIdx1 >= endIdx) {
154 : // no visible staves spanned; just use first
155 0 : staffIdx1 = startIdx;
156 0 : break;
157 : }
158 0 : staff1 = score()->staff(staffIdx1);
159 0 : sysStaff1 = system->staff(staffIdx1);
160 : }
161 8604 : yp += system->staffYpage(staffIdx1);
162 : }
163 8604 : return PointF(pageX(), yp);
164 : }
165 :
166 : //---------------------------------------------------------
167 : // prevVisiblespannedStaff
168 : //---------------------------------------------------------
169 :
170 10 : static staff_idx_t prevVisibleSpannedStaff(const BarLine* bl)
171 : {
172 10 : Score* score = bl->score();
173 10 : int staffIdx = static_cast<int>(bl->staffIdx());
174 10 : Segment* segment = bl->segment();
175 10 : for (int i = staffIdx - 1; i >= 0; --i) {
176 10 : BarLine* nbl = toBarLine(segment->element(i * VOICES));
177 10 : if (!nbl || !nbl->spanStaff()) {
178 0 : break;
179 : }
180 10 : Staff* s = score->staff(i);
181 10 : if (s->part()->show() && bl->measure()->visible(i)) {
182 10 : return i;
183 : }
184 : }
185 0 : return staffIdx;
186 : }
187 :
188 : //---------------------------------------------------------
189 : // nextVisiblespannedStaff
190 : //---------------------------------------------------------
191 :
192 15851 : static size_t nextVisibleSpannedStaff(const BarLine* bl)
193 : {
194 15851 : Score* score = bl->score();
195 15851 : size_t nstaves = score->nstaves();
196 15851 : size_t staffIdx = bl->staffIdx();
197 15851 : Segment* segment = bl->segment();
198 17037 : for (size_t i = staffIdx + 1; i < nstaves; ++i) {
199 12386 : Staff* s = score->staff(i);
200 12386 : if (s->part()->show()) {
201 : // span/show bar line if this measure is visible
202 10744 : if (bl->measure()->visible(i)) {
203 10336 : return i;
204 : }
205 : // or if this is an endBarLine and:
206 408 : if (segment && segment->isEndBarLineType()) {
207 : // ...this measure contains a (cutaway) courtesy clef only
208 286 : if (bl->measure()->isCutawayClef(i)) {
209 0 : return i;
210 : }
211 : // ...or next measure is both visible and in the same system
212 286 : Measure* nm = bl->measure()->nextMeasure();
213 286 : if ((nm ? nm->visible(i) : false) && (nm ? nm->system() == bl->measure()->system() : false)) {
214 0 : return i;
215 : }
216 : }
217 : }
218 2050 : BarLine* nbl = toBarLine(segment->element(i * VOICES));
219 2050 : if (!nbl || !nbl->spanStaff()) {
220 864 : break;
221 : }
222 : }
223 5515 : return staffIdx;
224 : }
225 :
226 : //---------------------------------------------------------
227 : // calcY
228 : //---------------------------------------------------------
229 :
230 55635 : void BarLine::calcY()
231 : {
232 55635 : BarLine::LayoutData* data = mutldata();
233 55635 : double _spatium = spatium();
234 55635 : if (!ownershipParent()) {
235 : // for use in palette
236 0 : data->y1 = m_spanFrom * _spatium * .5;
237 0 : data->y2 = (8 - m_spanTo) * _spatium * .5;
238 0 : return;
239 : }
240 55635 : staff_idx_t staffIdx1 = staffIdx();
241 55635 : staff_idx_t staffIdx2 = m_spanStaff ? nextVisibleSpannedStaff(this) : staffIdx1;
242 :
243 55635 : bool spanStaff = staffIdx2 != staffIdx1;
244 :
245 55635 : Measure* measure = segment()->measure();
246 55635 : System* system = measure->system();
247 55635 : if (!system) {
248 0 : return;
249 : }
250 :
251 55635 : Fraction tick = segment()->measure()->tick();
252 55635 : const Staff* staff1 = score()->staff(staffIdx1);
253 55635 : const StaffType* staffType1 = staff1->staffType(tick);
254 :
255 55635 : bool oneLine = staffType1->lines() <= 1;
256 :
257 55635 : int from = m_spanFrom;
258 55635 : int to = m_spanTo;
259 :
260 55635 : if (oneLine && m_spanFrom == 0 && m_spanTo == 0) {
261 53 : from = BARLINE_SPAN_1LINESTAFF_FROM;
262 53 : if (!spanStaff) {
263 46 : to = BARLINE_SPAN_1LINESTAFF_TO;
264 : }
265 : }
266 :
267 55635 : double spatium1 = staffType1->spatium();
268 55635 : double lineDistance = staffType1->lineDistance().val() * spatium1;
269 55635 : double offset = staffType1->yoffset().val() * spatium1;
270 55635 : double lineWidth = style().styleS(Sid::staffLineWidth).val() * spatium1 * .5;
271 :
272 55635 : double y1 = offset + from * lineDistance * .5 - lineWidth;
273 55635 : double y2 = offset + (staffType1->lines() * 2 - 2 + to) * lineDistance * .5 + lineWidth;
274 :
275 55635 : if (spanStaff) {
276 : // we need spatium and line distance of bottom staff
277 : // as it may be scalled diferently
278 10326 : const Staff* staff2 = score()->staff(staffIdx2);
279 10326 : const StaffType* staffType2 = staff2 ? staff2->staffType(tick) : staffType1;
280 10326 : double spatium2 = staffType2->spatium();
281 10326 : double lineDistance2 = staffType2->lineDistance().val() * spatium2;
282 10326 : double startStaffY = system->staff(staffIdx1)->y();
283 :
284 10326 : y2 = measure->staffLines(staffIdx2)->y1() - startStaffY - to * lineDistance2 * 0.5;
285 :
286 : // if bottom staff is single line, set span-to zeropoint to the top of the standard barline
287 10326 : if (staffType2->lines() <= 1) {
288 8 : y2 += BARLINE_SPAN_1LINESTAFF_FROM * lineDistance2 * 0.5;
289 : }
290 : }
291 :
292 : // if stafftype change in next measure, check new staff positions
293 55635 : Fraction tickNext = tick + measure->ticks();
294 55635 : if (staff1->isStaffTypeStartFrom(tickNext)) {
295 19 : Measure* measureNext = measure->nextMeasure();
296 19 : System* systemNext = measureNext ? measureNext->system() : nullptr;
297 19 : const StaffType* staffType1Next = staff1->staffType(tickNext);
298 19 : bool oneLineNext = staffType1Next->lines() <= 1;
299 19 : if (systemNext && systemNext == system && staffType1Next != staffType1) {
300 19 : if (oneLine && !oneLineNext) {
301 0 : from = m_spanFrom;
302 0 : to = m_spanTo;
303 : }
304 19 : if (oneLineNext && m_spanFrom == 0 && m_spanTo == 0) {
305 0 : from = BARLINE_SPAN_1LINESTAFF_FROM;
306 0 : if (!spanStaff) {
307 0 : to = BARLINE_SPAN_1LINESTAFF_TO;
308 : }
309 : }
310 19 : double spatium1Next = staffType1Next->spatium();
311 19 : double lineDistanceNext = staffType1Next->lineDistance().val() * spatium1Next;
312 19 : double offsetNext = staffType1Next->yoffset().val() * spatium1Next;
313 19 : double lineWidthNext = style().styleS(Sid::staffLineWidth).val() * spatium1Next * .5;
314 :
315 19 : int y1Next = offsetNext + from * lineDistanceNext * .5 - lineWidthNext;
316 :
317 : // change barline, only if it is not initial one
318 19 : if (rtick().isNotZero()) {
319 19 : if (y1Next < y1) {
320 0 : y1 = y1Next;
321 : }
322 :
323 19 : if (!spanStaff) {
324 0 : double y2Next = offsetNext + (staffType1Next->lines() * 2 - 2 + to) * lineDistanceNext * .5 + lineWidthNext;
325 0 : if (y2Next > y2) {
326 0 : y2 = y2Next;
327 : }
328 : }
329 : }
330 : }
331 : }
332 :
333 55635 : data->y1 = y1;
334 55635 : data->y2 = y2;
335 : }
336 :
337 : //---------------------------------------------------------
338 : // isTop
339 : //---------------------------------------------------------
340 :
341 20 : bool BarLine::isTop() const
342 : {
343 20 : staff_idx_t idx = staffIdx();
344 20 : if (idx == 0) {
345 10 : return true;
346 : } else {
347 10 : return prevVisibleSpannedStaff(this) == idx;
348 : }
349 : }
350 :
351 : //---------------------------------------------------------
352 : // isBottom
353 : //---------------------------------------------------------
354 :
355 20 : bool BarLine::isBottom() const
356 : {
357 20 : if (!m_spanStaff) {
358 10 : return true;
359 : }
360 10 : size_t idx = staffIdx();
361 10 : if (idx == score()->nstaves() - 1) {
362 0 : return true;
363 : } else {
364 10 : return nextVisibleSpannedStaff(this) == idx;
365 : }
366 : }
367 :
368 : //---------------------------------------------------------
369 : // acceptDrop
370 : //---------------------------------------------------------
371 :
372 0 : bool BarLine::acceptDrop(EditData& data) const
373 : {
374 0 : EngravingItem* e = data.dropElement;
375 :
376 0 : if (e->isBarLine()) {
377 0 : return true;
378 0 : } else if (e->isFermata() || e->isSymbol() || e->isImage()) {
379 0 : return segment() && segment()->isEndBarLineType();
380 0 : } else if (e->isMeasureNumber() || e->isJump() || e->isMarker() || e->isLayoutBreak()) {
381 0 : if (Measure* m = measure()) {
382 0 : bool left = (e->isMarker() && !toMarker(e)->isRightMarker()) || e->isMeasureNumber();
383 0 : if (left && segment()->isEndBarLineType() && m->nextMeasureMM()) {
384 0 : m = m->nextMeasureMM();
385 : }
386 0 : return m->acceptDrop(data);
387 : }
388 : }
389 0 : return false;
390 : }
391 :
392 : //---------------------------------------------------------
393 : // drop
394 : //---------------------------------------------------------
395 :
396 12 : EngravingItem* BarLine::drop(Transaction& tx, EditData& data)
397 : {
398 12 : EngravingItem* e = data.dropElement;
399 :
400 12 : if (e->isBarLine()) {
401 12 : BarLine* bl = toBarLine(e);
402 12 : BarLineType st = bl->barLineType();
403 :
404 12 : if (segment()->segmentType() == SegmentType::BarLine) {
405 : // barline exists mid measure
406 0 : undoChangeProperty(Pid::BARLINE_TYPE, st);
407 0 : undoChangeProperty(Pid::BARLINE_SPAN, false);
408 0 : undoChangeProperty(Pid::BARLINE_SPAN_FROM, bl->spanFrom());
409 0 : undoChangeProperty(Pid::BARLINE_SPAN_TO, bl->spanTo());
410 : } else {
411 : // check if the new property can apply to this single bar line
412 12 : BarLineType bt = BarLineType::START_REPEAT | BarLineType::END_REPEAT | BarLineType::END_START_REPEAT;
413 12 : bool oldRepeat = barLineType() & bt;
414 12 : bool newRepeat = bl->barLineType() & bt;
415 :
416 12 : Measure* m = measure();
417 12 : if (bl->playCount() != -1) {
418 0 : m->undoChangeProperty(Pid::REPEAT_COUNT, bl->playCount());
419 : }
420 :
421 : // if ctrl was used and repeats are not involved,
422 : // or if drop refers to span rather than subtype =>
423 : // single bar line drop
424 :
425 12 : if ((data.control() && !oldRepeat && !newRepeat) || (bl->spanFrom() || bl->spanTo())) {
426 : // if drop refers to span, update this bar line span
427 0 : if (bl->spanFrom() || bl->spanTo()) {
428 : // if dropped spanFrom or spanTo are below the middle of standard staff (5 lines)
429 : // adjust to the number of staff lines
430 0 : int spanFrom = bl->spanFrom();
431 0 : int spanTo = bl->spanTo();
432 0 : undoChangeProperty(Pid::BARLINE_SPAN, false);
433 0 : undoChangeProperty(Pid::BARLINE_SPAN_FROM, spanFrom);
434 0 : undoChangeProperty(Pid::BARLINE_SPAN_TO, spanTo);
435 : }
436 : // if drop refers to subtype, update this bar line subtype
437 : else {
438 0 : score()->undoChangeBarLineType(this, st, false);
439 : }
440 : } else {
441 12 : score()->undoChangeBarLineType(this, st, true);
442 : }
443 12 : score()->undoUpdatePlayCountText(m);
444 : }
445 12 : delete e;
446 0 : } else if (e->isArticulationFamily()) {
447 0 : Articulation* atr = toArticulation(e);
448 0 : atr->setOwnershipParent(this);
449 0 : atr->setTrack(track());
450 0 : score()->undoAddElement(atr);
451 0 : return atr;
452 0 : } else if (e->isSymbol() || e->isImage()) {
453 0 : e->setOwnershipParent(this);
454 0 : e->setTrack(track());
455 0 : score()->undoAddElement(e);
456 0 : return e;
457 0 : } else if (e->isFermata()) {
458 0 : e->setPlacement(track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
459 0 : for (EngravingItem* el: segment()->annotations()) {
460 0 : if (el->isFermata() && (el->track() == track())) {
461 0 : if (el->subtype() == e->subtype()) {
462 0 : delete e;
463 0 : return el;
464 : } else {
465 0 : e->setPlacement(el->placement());
466 0 : e->setTrack(track());
467 0 : e->setOwnershipParent(segment());
468 0 : score()->undoChangeElement(el, e);
469 0 : return e;
470 : }
471 : }
472 : }
473 0 : e->setTrack(track());
474 0 : e->setOwnershipParent(segment());
475 0 : score()->undoAddElement(e);
476 0 : return e;
477 0 : } else if (e->isMeasureNumber() || e->isJump() || e->isMarker() || e->isLayoutBreak()) {
478 0 : if (Measure* m = measure()) {
479 0 : bool left = (e->isMarker() && !toMarker(e)->isRightMarker()) || e->isMeasureNumber();
480 0 : if (left && segment()->isEndBarLineType() && m->nextMeasureMM()) {
481 0 : m = m->nextMeasureMM();
482 : }
483 0 : return m->drop(tx, data);
484 : }
485 : }
486 12 : return nullptr;
487 : }
488 :
489 : //---------------------------------------------------------
490 : // setShowTips
491 : //---------------------------------------------------------
492 :
493 0 : void BarLine::setShowTips(bool val)
494 : {
495 0 : if (!score()) {
496 0 : return;
497 : }
498 :
499 0 : score()->undoChangeStyleVal(Sid::repeatBarTips, val);
500 : }
501 :
502 : //---------------------------------------------------------
503 : // showTips
504 : //---------------------------------------------------------
505 :
506 0 : bool BarLine::showTips() const
507 : {
508 0 : if (!score()) {
509 0 : return false;
510 : }
511 :
512 0 : return style().styleB(Sid::repeatBarTips);
513 : }
514 :
515 0 : void BarLine::setSelected(bool f)
516 : {
517 0 : if (f == selected()) {
518 0 : return;
519 : }
520 :
521 0 : if (playCountText()) {
522 0 : playCountText()->setSelected(f);
523 : }
524 0 : EngravingItem::setSelected(f);
525 : }
526 :
527 : //---------------------------------------------------------
528 : // gripsPositions
529 : //---------------------------------------------------------
530 :
531 0 : std::vector<PointF> BarLine::gripsPositions(const EditData& ed) const
532 : {
533 0 : const BarLineEditData* bed = static_cast<const BarLineEditData*>(ed.getData(this).get());
534 :
535 0 : double lw = style().styleAbsolute(Sid::barWidth) * staff()->staffMag(tick());
536 0 : const_cast<BarLine*>(this)->calcY();
537 :
538 0 : const PointF pp = pagePos();
539 :
540 : return {
541 : //PointF(lw * .5, y1 + bed->yoff1) + pp,
542 0 : PointF(lw * .5, ldata()->y2 + bed->yoff2) + pp
543 0 : };
544 : }
545 :
546 1794 : void BarLine::styleChanged()
547 : {
548 1794 : if (ownershipParent() && m_barLineType == BarLineType::END_REPEAT) {
549 2 : score()->undoUpdatePlayCountText(measure());
550 : }
551 1794 : EngravingItem::styleChanged();
552 1794 : }
553 :
554 : //---------------------------------------------------------
555 : // startEdit
556 : //---------------------------------------------------------
557 :
558 0 : void BarLine::startEdit(EditData& ed)
559 : {
560 0 : std::shared_ptr<BarLineEditData> bed = std::make_shared<BarLineEditData>();
561 0 : bed->e = this;
562 0 : bed->yoff1 = 0;
563 0 : bed->yoff2 = 0;
564 0 : ed.addData(bed);
565 0 : }
566 :
567 0 : bool BarLine::isEditAllowed(EditData& ed) const
568 : {
569 0 : return (ed.key == Key_Up && spanStaff()) || (ed.key == Key_Down && !spanStaff())
570 0 : || EngravingItem::isEditAllowed(ed);
571 : }
572 :
573 0 : bool BarLine::edit(EditData& ed)
574 : {
575 0 : if (!isEditAllowed(ed)) {
576 0 : return false;
577 : }
578 :
579 0 : bool local = ed.control() || segment()->isBarLineType() || spanStaff() != score()->staff(staffIdx())->barLineSpan();
580 0 : if ((ed.key == Key_Up && spanStaff()) || (ed.key == Key_Down && !spanStaff())) {
581 0 : if (local) {
582 0 : BarLine* b = toBarLine(segment()->element(staffIdx() * VOICES));
583 0 : if (b) {
584 0 : b->undoChangeProperty(Pid::BARLINE_SPAN, !spanStaff());
585 : }
586 : } else {
587 0 : score()->staff(staffIdx())->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, !spanStaff());
588 : }
589 0 : return true;
590 : }
591 0 : return EngravingItem::edit(ed);
592 : }
593 :
594 : //---------------------------------------------------------
595 : // dragGrip
596 : //---------------------------------------------------------
597 :
598 0 : void BarLine::dragGrip(EditData& ed)
599 : {
600 0 : IF_ASSERT_FAILED(ed.curGrip == Grip::START) {
601 0 : return;
602 : }
603 :
604 0 : BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this).get());
605 :
606 0 : double lineDist = staff()->lineDistance(tick()) * spatium();
607 0 : calcY();
608 :
609 : // min for bottom grip is 1 line below top grip
610 0 : const double min = ldata()->y1 - ldata()->y2 + lineDist;
611 : // max is the bottom of the system
612 0 : const System* system = segment() ? segment()->system() : nullptr;
613 0 : const staff_idx_t st = staffIdx();
614 0 : const double max = (system && st != muse::nidx)
615 0 : ? (system->height() - ldata()->y2 - system->staff(st)->y())
616 0 : : std::numeric_limits<double>::max();
617 : // update yoff2 and bring it within limit
618 0 : bed->yoff2 += ed.delta.y();
619 0 : if (bed->yoff2 < min) {
620 0 : bed->yoff2 = min;
621 : }
622 0 : if (bed->yoff2 > max) {
623 0 : bed->yoff2 = max;
624 : }
625 : }
626 :
627 : //---------------------------------------------------------
628 : // endDragGrip
629 : // snap to nearest staff / staff line
630 : //---------------------------------------------------------
631 :
632 0 : void BarLine::endDragGrip(EditData& ed)
633 : {
634 0 : calcY();
635 0 : BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this).get());
636 :
637 0 : double ay0 = pagePos().y();
638 0 : double ay2 = ay0 + ldata()->y2 + bed->yoff2; // absolute (page-relative) bar line bottom coord
639 0 : staff_idx_t staffIdx1 = staffIdx();
640 0 : System* syst = segment()->measure()->system();
641 0 : double systTopY = syst->pagePos().y();
642 :
643 : // determine new span value
644 : staff_idx_t staffIdx2;
645 0 : size_t numOfStaves = syst->staves().size();
646 0 : if (staffIdx1 + 1 >= numOfStaves) {
647 : // if initial staff is last staff, ending staff must be the same
648 0 : staffIdx2 = staffIdx1;
649 : } else {
650 : // if there are other staves after it, look for staff nearest to bar line bottom coord
651 0 : double staff1TopY = syst->staff(staffIdx1)->y() + systTopY;
652 :
653 0 : for (staffIdx2 = staffIdx1 + 1; staffIdx2 < numOfStaves; ++staffIdx2) {
654 : // compute 1st staff height, absolute top Y of 2nd staff and height of blank between the staves
655 0 : Staff* staff1 = score()->staff(staffIdx2 - 1);
656 0 : double staff1Hght = staff1->staffHeight(tick());
657 0 : double staff2TopY = systTopY + syst->staff(staffIdx2)->y();
658 0 : double blnkBtwnStaff = staff2TopY - staff1TopY - staff1Hght;
659 : // if bar line bottom coord is above than mid-way of blank between staves...
660 0 : if (ay2 < (staff1TopY + staff1Hght + blnkBtwnStaff * .5)) {
661 0 : break; // ...staff 1 is ending staff
662 : }
663 : // if bar line is below, advance to next staff
664 0 : staff1TopY = staff2TopY;
665 : }
666 0 : staffIdx2 -= 1;
667 : }
668 :
669 : // determine new spanFrom and spanTo values
670 0 : int newSpanFrom = 0;
671 0 : int newSpanTo = 0;
672 :
673 0 : bool localDrag = ed.control() || segment()->isBarLineType() || spanStaff() != score()->staff(staffIdx())->barLineSpan();
674 0 : if (localDrag) {
675 0 : Segment* s = segment();
676 0 : bool breakLast = staffIdx1 == staffIdx2;
677 0 : for (staff_idx_t staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx) {
678 0 : BarLine* b = toBarLine(s->element(staffIdx * VOICES));
679 0 : if (!b) {
680 0 : b = toBarLine(linkedClone());
681 0 : b->setSpanStaff(true);
682 0 : b->setTrack(staffIdx * VOICES);
683 0 : b->setOwnershipParent(s);
684 0 : score()->undoAddElement(b);
685 : }
686 0 : breakLast = b->spanTo();
687 0 : b->undoChangeProperty(Pid::BARLINE_SPAN, true);
688 : }
689 0 : if (breakLast) {
690 0 : BarLine* b = toBarLine(s->element(staffIdx2 * VOICES));
691 0 : if (b) {
692 0 : b->undoChangeProperty(Pid::BARLINE_SPAN, false);
693 : }
694 : }
695 : } else {
696 0 : bool breakLast = staffIdx1 == staffIdx2;
697 0 : for (staff_idx_t staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx) {
698 0 : breakLast = score()->staff(staffIdx)->barLineSpan();
699 0 : score()->staff(staffIdx)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, true);
700 : }
701 0 : if (breakLast) {
702 0 : score()->staff(staffIdx2)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, false);
703 : }
704 0 : staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, newSpanFrom);
705 0 : staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, newSpanTo);
706 : }
707 :
708 0 : bed->yoff1 = 0.0;
709 0 : bed->yoff2 = 0.0;
710 0 : }
711 :
712 : //---------------------------------------------------------
713 : // scanElements
714 : //---------------------------------------------------------
715 :
716 3937 : void BarLine::scanElements(std::function<void(EngravingItem*)> func)
717 : {
718 3937 : func(this);
719 3937 : for (EngravingItem* e : m_el) {
720 0 : e->scanElements(func);
721 : }
722 3937 : }
723 :
724 : //---------------------------------------------------------
725 : // setTrack
726 : //---------------------------------------------------------
727 :
728 29945 : void BarLine::setTrack(track_idx_t t)
729 : {
730 29945 : EngravingItem::setTrack(t);
731 29945 : for (EngravingItem* e : m_el) {
732 0 : e->setTrack(t);
733 : }
734 29945 : }
735 :
736 : //---------------------------------------------------------
737 : // add
738 : //---------------------------------------------------------
739 :
740 0 : void BarLine::add(EngravingItem* e)
741 : {
742 0 : e->setOwnershipParent(this);
743 0 : switch (e->type()) {
744 0 : case ElementType::ARTICULATION:
745 : case ElementType::SYMBOL:
746 : case ElementType::IMAGE:
747 0 : m_el.push_back(e);
748 0 : setGenerated(false);
749 0 : e->added();
750 0 : break;
751 0 : default:
752 0 : LOGD("BarLine::add() not impl. %s", e->typeName());
753 0 : break;
754 : }
755 0 : }
756 :
757 : //---------------------------------------------------------
758 : // remove
759 : //---------------------------------------------------------
760 :
761 0 : void BarLine::remove(EngravingItem* e)
762 : {
763 0 : switch (e->type()) {
764 0 : case ElementType::ARTICULATION:
765 : case ElementType::SYMBOL:
766 : case ElementType::IMAGE:
767 0 : if (!m_el.remove(e)) {
768 0 : LOGD("BarLine::remove(): cannot find %s", e->typeName());
769 : } else {
770 0 : e->removed();
771 : }
772 0 : break;
773 0 : default:
774 0 : LOGD("BarLine::remove() not impl. %s", e->typeName());
775 0 : return;
776 : }
777 : }
778 :
779 : //---------------------------------------------------------
780 : // getProperty
781 : //---------------------------------------------------------
782 :
783 19491 : PropertyValue BarLine::getProperty(Pid id) const
784 : {
785 19491 : if (EngravingObject* e = const_cast<BarLine*>(this)->propertyDelegate(id)) {
786 0 : return e->getProperty(id);
787 : }
788 :
789 19491 : switch (id) {
790 1497 : case Pid::BARLINE_TYPE:
791 1497 : return PropertyValue::fromValue(m_barLineType);
792 1491 : case Pid::BARLINE_SPAN:
793 1491 : return spanStaff();
794 1491 : case Pid::BARLINE_SPAN_FROM:
795 1491 : return spanFrom();
796 1491 : case Pid::BARLINE_SPAN_TO:
797 1491 : return spanTo();
798 0 : case Pid::BARLINE_SHOW_TIPS:
799 0 : return showTips();
800 13521 : default:
801 13521 : break;
802 : }
803 13521 : return EngravingItem::getProperty(id);
804 : }
805 :
806 : //---------------------------------------------------------
807 : // setProperty
808 : //---------------------------------------------------------
809 :
810 2685 : bool BarLine::setProperty(Pid id, const PropertyValue& v)
811 : {
812 2685 : if (EngravingObject* e = propertyDelegate(id)) {
813 0 : return e->setProperty(id, v);
814 : }
815 :
816 2685 : switch (id) {
817 9 : case Pid::BARLINE_TYPE:
818 9 : setBarLineType(v.value<BarLineType>());
819 9 : break;
820 860 : case Pid::BARLINE_SPAN:
821 860 : setSpanStaff(v.toBool());
822 860 : break;
823 890 : case Pid::BARLINE_SPAN_FROM:
824 890 : setSpanFrom(v.toInt());
825 890 : break;
826 890 : case Pid::BARLINE_SPAN_TO:
827 890 : setSpanTo(v.toInt());
828 890 : break;
829 0 : case Pid::BARLINE_SHOW_TIPS:
830 0 : setShowTips(v.toBool());
831 0 : break;
832 36 : default:
833 36 : return EngravingItem::setProperty(id, v);
834 : }
835 2649 : setGenerated(false);
836 2649 : triggerLayout();
837 2649 : return true;
838 : }
839 :
840 : //---------------------------------------------------------
841 : // undoChangeProperty
842 : //---------------------------------------------------------
843 :
844 36 : void BarLine::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
845 : {
846 36 : if (EngravingObject* e = propertyDelegate(id)) {
847 0 : e->undoChangeProperty(id, v, ps);
848 0 : return;
849 : }
850 :
851 36 : if (id == Pid::BARLINE_TYPE && segment() && segment()->segmentType() != SegmentType::BarLine) {
852 9 : score()->undoChangeBarLineType(this, v.value<BarLineType>(), true, true);
853 : } else {
854 27 : EngravingObject::undoChangeProperty(id, v, ps);
855 : }
856 : }
857 :
858 72297 : EngravingObject* BarLine::propertyDelegate(Pid pid) const
859 : {
860 72297 : if (pid == Pid::REPEAT_COUNT) {
861 0 : return measure();
862 72297 : } else if (pid == Pid::PLAY_COUNT_TEXT || pid == Pid::PLAY_COUNT_TEXT_SETTING) {
863 0 : return playCountText();
864 : }
865 :
866 72297 : return EngravingItem::propertyDelegate(pid);
867 : }
868 :
869 0 : PlayCountText* BarLine::playCountText() const
870 : {
871 0 : Segment* endBarSeg = segment();
872 0 : if (!endBarSeg) {
873 0 : return nullptr;
874 : }
875 0 : PlayCountText* playCountText = toPlayCountText(endBarSeg->findAnnotation(ElementType::PLAY_COUNT_TEXT, track(), track()));
876 :
877 0 : return playCountText;
878 : }
879 :
880 : //---------------------------------------------------------
881 : // propertyDefault
882 : //---------------------------------------------------------
883 :
884 22006 : PropertyValue BarLine::propertyDefault(Pid propertyId) const
885 : {
886 22006 : if (EngravingObject* e = const_cast<BarLine*>(this)->propertyDelegate(propertyId)) {
887 0 : return e->propertyDefault(propertyId);
888 : }
889 :
890 22006 : switch (propertyId) {
891 1488 : case Pid::BARLINE_TYPE:
892 : // dynamic default values are a bad idea: writing to xml the value maybe omitted resulting in
893 : // wrong values on read (as the default may be different on read)
894 : // if (segment() && segment()->measure() && !segment()->measure()->nextMeasure())
895 : // return QVariant::fromValue(BarLineType::END);
896 2976 : return PropertyValue::fromValue(BarLineType::NORMAL);
897 :
898 2347 : case Pid::BARLINE_SPAN:
899 2347 : return staff() ? staff()->barLineSpan() : false;
900 :
901 2377 : case Pid::BARLINE_SPAN_FROM:
902 2377 : return staff() ? staff()->barLineFrom() : 0;
903 :
904 2377 : case Pid::BARLINE_SPAN_TO:
905 2377 : return staff() ? staff()->barLineTo() : 0;
906 :
907 0 : case Pid::BARLINE_SHOW_TIPS:
908 0 : return false;
909 :
910 13417 : default:
911 13417 : break;
912 : }
913 13417 : return EngravingItem::propertyDefault(propertyId);
914 : }
915 :
916 : //---------------------------------------------------------
917 : // nextSegmentElement
918 : //---------------------------------------------------------
919 :
920 0 : EngravingItem* BarLine::nextSegmentElement()
921 : {
922 0 : return segment()->firstInNextSegments(staffIdx()); //score()->inputState().prevTrack() / VOICES);
923 : }
924 :
925 : //---------------------------------------------------------
926 : // prevSegmentElement
927 : //---------------------------------------------------------
928 :
929 0 : EngravingItem* BarLine::prevSegmentElement()
930 : {
931 0 : return segment()->lastInPrevSegments(staffIdx()); //score()->inputState().prevTrack() / VOICES);
932 : }
933 :
934 : //---------------------------------------------------------
935 : // subtypeUserName
936 : //---------------------------------------------------------
937 :
938 5 : muse::TranslatableString BarLine::subtypeUserName() const
939 : {
940 10 : for (const auto& i : barLineTable) {
941 10 : if (i.type == barLineType()) {
942 5 : return i.userName;
943 : }
944 : }
945 0 : return TranslatableString();
946 : }
947 :
948 : //---------------------------------------------------------
949 : // accessibleInfo
950 : //---------------------------------------------------------
951 :
952 0 : String BarLine::accessibleInfo() const
953 : {
954 0 : return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), BarLine::translatedUserTypeName(barLineType()));
955 : }
956 :
957 : //---------------------------------------------------------
958 : // accessibleExtraInfo
959 : //---------------------------------------------------------
960 :
961 0 : String BarLine::accessibleExtraInfo() const
962 : {
963 0 : Segment* seg = segment();
964 0 : String rez;
965 :
966 0 : for (const EngravingItem* e : *el()) {
967 0 : if (!score()->selectionFilter().canSelect(e)) {
968 0 : continue;
969 : }
970 0 : rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
971 : }
972 :
973 0 : for (const EngravingItem* e : seg->annotations()) {
974 0 : if (!score()->selectionFilter().canSelect(e)) {
975 0 : continue;
976 : }
977 0 : if (e->track() == track()) {
978 0 : rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
979 : }
980 : }
981 0 : Measure* m = seg->measure();
982 :
983 0 : if (m) { // always true?
984 : //jumps
985 0 : for (const EngravingItem* e : m->el()) {
986 0 : if (!score()->selectionFilter().canSelect(e)) {
987 0 : continue;
988 : }
989 0 : if (e->isJump()) {
990 0 : rez= String(u"%1 %2").arg(rez, e->screenReaderInfo());
991 : }
992 0 : if (e->isMarker()) {
993 0 : const Marker* m1 = toMarker(e);
994 0 : if (m1->markerType() == MarkerType::FINE) {
995 0 : rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
996 : }
997 : }
998 : }
999 : //markers
1000 0 : Measure* nextM = m->nextMeasureMM();
1001 0 : if (nextM) {
1002 0 : for (const EngravingItem* e : nextM->el()) {
1003 0 : if (!score()->selectionFilter().canSelect(e)) {
1004 0 : continue;
1005 : }
1006 0 : if (e->isMarker()) {
1007 0 : if (toMarker(e)->markerType() == MarkerType::FINE) {
1008 0 : continue; //added above^
1009 : }
1010 0 : rez = String(u"%1 %2").arg(rez, e->screenReaderInfo());
1011 : }
1012 : }
1013 : }
1014 : }
1015 :
1016 0 : Fraction tick = seg->tick();
1017 :
1018 0 : auto spanners = score()->spannerMap().findOverlapping(tick.ticks(), tick.ticks());
1019 0 : for (auto interval : spanners) {
1020 0 : Spanner* s = interval.value;
1021 0 : if (!score()->selectionFilter().canSelect(s)) {
1022 0 : continue;
1023 : }
1024 0 : if (s->isVolta()) {
1025 0 : if (s->tick() == tick) {
1026 0 : rez += u"; " + muse::mtrc("engraving", "Start of %1").arg(s->screenReaderInfo());
1027 : }
1028 0 : if (s->tick2() == tick) {
1029 0 : rez += u"; " + muse::mtrc("engraving", "End of %1").arg(s->screenReaderInfo());
1030 : }
1031 : }
1032 : }
1033 0 : return rez;
1034 0 : }
|