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 : /**
24 : \file
25 : Implementation of class Selection plus other selection related functions.
26 : */
27 :
28 : #include "global/containers.h"
29 :
30 : #include "../types/types.h"
31 :
32 : #include "rw/rwregister.h"
33 :
34 : #include "accidental.h"
35 : #include "arpeggio.h"
36 : #include "articulation.h"
37 : #include "beam.h"
38 : #include "box.h"
39 : #include "breath.h"
40 : #include "chord.h"
41 : #include "dynamic.h"
42 : #include "engravingitem.h"
43 : #include "expression.h"
44 : #include "figuredbass.h"
45 : #include "fingering.h"
46 : #include "fret.h"
47 : #include "guitarbend.h"
48 : #include "harmony.h"
49 : #include "harppedaldiagram.h"
50 : #include "hook.h"
51 : #include "laissezvib.h"
52 : #include "linkedobjects.h"
53 : #include "lyrics.h"
54 : #include "measure.h"
55 : #include "mscore.h"
56 : #include "note.h"
57 : #include "notedot.h"
58 : #include "pagelockindicator.h"
59 : #include "part.h"
60 : #include "partialtie.h"
61 : #include "rest.h"
62 : #include "score.h"
63 : #include "segment.h"
64 : #include "select.h"
65 : #include "staff.h"
66 : #include "stafftextbase.h"
67 : #include "stem.h"
68 : #include "stemslash.h"
69 : #include "sticking.h"
70 : #include "system.h"
71 : #include "systemlockindicator.h"
72 : #include "tie.h"
73 : #include "tremolosinglechord.h"
74 : #include "tremolotwochord.h"
75 : #include "tuplet.h"
76 :
77 : #include "utils.h"
78 :
79 : #include "log.h"
80 :
81 : using namespace mu;
82 : using namespace mu::engraving;
83 :
84 : // ====================================================
85 : // Selection
86 : // ====================================================
87 :
88 2334 : Selection::Selection(Score* s)
89 : {
90 2334 : m_score = s;
91 2334 : m_state = SelState::NONE;
92 2334 : m_startSegment = 0;
93 2334 : m_endSegment = 0;
94 2334 : m_startBox = 0;
95 2334 : m_endBox = 0;
96 2334 : m_activeSegment = 0;
97 2334 : m_staffStart = 0;
98 2334 : m_staffEnd = 0;
99 2334 : m_activeTrack = 0;
100 2334 : m_currentTick = Fraction(-1, 1);
101 2334 : m_currentTrack = 0;
102 2334 : }
103 :
104 3564 : Fraction Selection::tickStart() const
105 : {
106 3564 : switch (m_state) {
107 3559 : case SelState::RANGE:
108 3559 : return m_startSegment ? m_startSegment->tick() : Fraction(-1, 1);
109 5 : case SelState::LIST: {
110 5 : ChordRest* cr = firstChordRest();
111 5 : return (cr) ? cr->tick() : Fraction(-1, 1);
112 : }
113 0 : default:
114 0 : return Fraction(-1, 1);
115 : }
116 : }
117 :
118 3538 : Fraction Selection::tickEnd() const
119 : {
120 3538 : switch (m_state) {
121 3533 : case SelState::RANGE: {
122 3533 : if (m_endSegment) {
123 2923 : return m_endSegment->tick();
124 : } else { // endsegment == 0 if end of score
125 610 : Measure* m = m_score->lastMeasure();
126 610 : return m->endTick();
127 : }
128 : break;
129 : }
130 5 : case SelState::LIST: {
131 5 : ChordRest* cr = lastChordRest();
132 5 : return (cr) ? cr->segment()->tick() : Fraction(-1, 1);
133 : break;
134 : }
135 0 : default:
136 0 : return Fraction(-1, 1);
137 : }
138 : }
139 :
140 0 : bool Selection::isStartActive() const
141 : {
142 0 : return activeSegment() && activeSegment()->tick() == tickStart();
143 : }
144 :
145 0 : bool Selection::isEndActive() const
146 : {
147 0 : return activeSegment() && activeSegment()->tick() == tickEnd();
148 : }
149 :
150 6542 : EngravingItem* Selection::element() const
151 : {
152 6542 : return ((state() != SelState::RANGE) && (m_el.size() == 1)) ? m_el[0] : 0;
153 : }
154 :
155 0 : ChordRest* Selection::cr() const
156 : {
157 0 : EngravingItem* e = element();
158 0 : if (!e) {
159 0 : return 0;
160 : }
161 0 : if (e->isNote()) {
162 0 : e = toNote(e)->chord();
163 : }
164 0 : if (e->isChordRest()) {
165 0 : return toChordRest(e);
166 : }
167 0 : return 0;
168 : }
169 :
170 0 : ChordRest* Selection::currentCR() const
171 : {
172 : // no selection yet - start at very beginning, not first cr
173 0 : if (m_currentTick == Fraction(-1, 1)) {
174 0 : return nullptr;
175 : }
176 0 : Segment* s = score()->tick2rightSegment(m_currentTick, true);
177 0 : if (!s) {
178 0 : return nullptr;
179 : }
180 0 : track_idx_t track = m_currentTrack;
181 : // staff may have been removed - start at top
182 0 : if (track >= score()->ntracks()) {
183 0 : track = 0;
184 : }
185 0 : EngravingItem* e = s->element(track);
186 0 : if (e && e->isChordRest()) {
187 0 : return toChordRest(e);
188 : } else {
189 0 : return nullptr;
190 : }
191 : }
192 :
193 3438 : ChordRest* Selection::activeCR() const
194 : {
195 3438 : if (m_state != SelState::RANGE || !m_activeSegment) {
196 2759 : return nullptr;
197 : }
198 679 : if (m_activeSegment == m_startSegment) {
199 0 : return firstChordRest(m_activeTrack);
200 : } else {
201 679 : return lastChordRest(m_activeTrack);
202 : }
203 : }
204 :
205 87 : ChordRest* Selection::firstChordRestInRange(track_idx_t preferredTrack) const
206 : {
207 87 : IF_ASSERT_FAILED(isRange()) {
208 0 : return nullptr;
209 : }
210 :
211 87 : Segment* firstCRSegment = nullptr;
212 :
213 87 : for (Segment* currSeg = m_startSegment; currSeg && (currSeg != m_endSegment); currSeg = currSeg->next1MM()) {
214 87 : if (!currSeg->enabled() || !currSeg->isChordRestType()) {
215 0 : continue;
216 : }
217 87 : if (preferredTrack == muse::nidx) {
218 : // no track specified, use the first CR segment we find...
219 87 : firstCRSegment = currSeg;
220 87 : break;
221 : }
222 0 : EngravingItem* e = currSeg->element(preferredTrack);
223 0 : if (e && e->isChordRest()) {
224 0 : return toChordRest(e);
225 : }
226 : }
227 :
228 87 : if (!firstCRSegment) {
229 0 : return nullptr;
230 : }
231 :
232 87 : for (track_idx_t track = staff2track(staffStart()); track < staff2track(staffEnd()); ++track) {
233 87 : EngravingItem* e = firstCRSegment->element(track);
234 87 : if (e && e->isChordRest()) {
235 87 : return toChordRest(e);
236 : }
237 : }
238 0 : return nullptr;
239 : }
240 :
241 757 : ChordRest* Selection::lastChordRestInRange(track_idx_t preferredTrack) const
242 : {
243 757 : IF_ASSERT_FAILED(isRange()) {
244 0 : return nullptr;
245 : }
246 :
247 757 : Segment* lastCRSegment = nullptr;
248 :
249 757 : Segment* lastSegInScore = m_score->lastMeasureMM() ? m_score->lastMeasureMM()->last() : nullptr;
250 757 : Segment* currSeg = m_endSegment ? m_endSegment->prev1MM() : lastSegInScore;
251 872 : while (currSeg) {
252 872 : if (!currSeg->enabled() || !currSeg->isChordRestType()) {
253 31 : currSeg = currSeg->prev1MM();
254 31 : continue;
255 : }
256 841 : if (preferredTrack == muse::nidx) {
257 : // no track specified, use the first CR segment we find...
258 78 : lastCRSegment = currSeg;
259 78 : break;
260 : }
261 763 : EngravingItem* e = currSeg->element(preferredTrack);
262 763 : if (e && e->isChordRest()) {
263 679 : return toChordRest(e);
264 : }
265 84 : currSeg = currSeg->prev1MM();
266 84 : if (currSeg == m_startSegment->prev1MM()) {
267 0 : break;
268 : }
269 : }
270 :
271 78 : if (!lastCRSegment) {
272 0 : return nullptr;
273 : }
274 :
275 79 : for (track_idx_t track = staff2track(staffStart()); track < staff2track(staffEnd()); ++track) {
276 79 : EngravingItem* e = lastCRSegment->element(track);
277 79 : if (e && e->isChordRest()) {
278 78 : return toChordRest(e);
279 : }
280 : }
281 :
282 0 : return nullptr;
283 : }
284 :
285 94 : ChordRest* Selection::firstChordRest(track_idx_t track) const
286 : {
287 94 : if (isRange()) {
288 87 : return firstChordRestInRange(track);
289 : }
290 7 : if (m_el.size() == 1) {
291 2 : EngravingItem* el = m_el[0];
292 2 : if (el->isNote()) {
293 0 : return toChordRest(el->ownershipParent());
294 2 : } else if (el->isChordRest()) {
295 2 : return toChordRest(el);
296 : }
297 0 : return nullptr;
298 : }
299 5 : ChordRest* cr = nullptr;
300 18 : for (EngravingItem* el : m_el) {
301 13 : if (el->isNote()) {
302 3 : el = toNote(el)->chord();
303 : }
304 13 : if (el->isChordRest()) {
305 13 : if (track != muse::nidx && el->track() != track) {
306 0 : continue;
307 : }
308 13 : if (cr) {
309 8 : if (toChordRest(el)->tick() < cr->tick()) {
310 2 : cr = toChordRest(el);
311 : }
312 : } else {
313 5 : cr = toChordRest(el);
314 : }
315 : }
316 : }
317 5 : return cr;
318 : }
319 :
320 762 : ChordRest* Selection::lastChordRest(track_idx_t track) const
321 : {
322 762 : if (isRange()) {
323 757 : return lastChordRestInRange(track);
324 : }
325 5 : if (m_el.size() == 1) {
326 0 : EngravingItem* el = m_el[0];
327 0 : if (el) {
328 0 : if (el->isNote()) {
329 0 : return toChordRest(el->ownershipParent());
330 0 : } else if (el->isChordRest()) {
331 0 : return toChordRest(el);
332 : }
333 : }
334 0 : return nullptr;
335 : }
336 5 : ChordRest* cr = nullptr;
337 18 : for (EngravingItem* el : m_el) {
338 13 : if (el->isNote()) {
339 3 : el = toNote(el)->chord();
340 : }
341 13 : if (el->isChordRest() && toChordRest(el)->segment()->isChordRestType()) {
342 13 : if (track != muse::nidx && el->track() != track) {
343 0 : continue;
344 : }
345 13 : if (cr) {
346 8 : if (toChordRest(el)->tick() >= cr->tick()) {
347 6 : cr = toChordRest(el);
348 : }
349 : } else {
350 5 : cr = toChordRest(el);
351 : }
352 : }
353 : }
354 5 : return cr;
355 : }
356 :
357 0 : Measure* Selection::findMeasure() const
358 : {
359 0 : Measure* m = 0;
360 0 : if (m_el.size() > 0) {
361 0 : EngravingItem* el = m_el[0];
362 0 : m = toMeasure(el->findMeasure());
363 : }
364 0 : return m;
365 : }
366 :
367 21 : MeasureBase* Selection::startMeasureBase() const
368 : {
369 21 : EngravingItem* selectionElement = element();
370 21 : if (selectionElement) {
371 0 : if (selectionElement->isBox()) {
372 0 : return toMeasureBase(selectionElement);
373 : }
374 0 : MeasureBase* mb = selectionElement->findMeasureBase();
375 0 : if (mb) {
376 0 : return mb;
377 : }
378 : }
379 :
380 21 : if (m_startBox) {
381 12 : return m_startBox;
382 : }
383 :
384 9 : if (tickStart().negative()) { // Tick is not set
385 0 : return nullptr;
386 : }
387 :
388 9 : MeasureBase* mb = m_score->tick2measureBase(tickStart());
389 9 : if (mb && mb->isMeasure()) {
390 5 : mb = toMeasure(mb)->coveringMMRestOrThis();
391 : }
392 9 : return mb;
393 : }
394 :
395 18 : MeasureBase* Selection::endMeasureBase() const
396 : {
397 18 : EngravingItem* selectionElement = element();
398 18 : if (selectionElement) {
399 0 : if (selectionElement->isBox()) {
400 0 : return toMeasureBase(selectionElement);
401 : }
402 0 : MeasureBase* mb = selectionElement->findMeasureBase();
403 0 : if (mb) {
404 0 : return mb;
405 : }
406 : }
407 :
408 18 : if (m_endBox) {
409 8 : return m_endBox;
410 : }
411 :
412 10 : if (tickEnd().negative()) { // Tick is not set
413 0 : return nullptr;
414 : }
415 :
416 10 : MeasureBase* mb = m_score->tick2measureBase(tickEnd() - Fraction::eps());
417 10 : if (mb && mb->isMeasure()) {
418 10 : mb = toMeasure(mb)->coveringMMRestOrThis();
419 : }
420 10 : return mb;
421 : }
422 :
423 4 : std::vector<MeasureBase*> Selection::measureBasesInSelection() const
424 : {
425 4 : MeasureBase* startMB = startMeasureBase();
426 4 : MeasureBase* endMB = endMeasureBase();
427 4 : if (!startMB || !endMB) {
428 0 : return {};
429 : }
430 :
431 4 : std::vector<MeasureBase*> measureBases;
432 10 : for (MeasureBase* mb = startMB; mb && mb->isBeforeOrEqual(endMB); mb = mb->nextMM()) {
433 6 : measureBases.push_back(mb);
434 : }
435 :
436 4 : return measureBases;
437 4 : }
438 :
439 2 : std::vector<System*> Selection::selectedSystems() const
440 : {
441 2 : EngravingItem* el = element();
442 2 : if (el && (el->isSystemLockIndicator() || el->isPageLockIndicator() /*TODO: || el->isStaffVisibilityIndicator*/)) {
443 0 : return { const_cast<System*>(toIndicatorIcon(el)->system()) };
444 : }
445 :
446 2 : std::vector<System*> systems;
447 4 : for (const MeasureBase* mb : measureBasesInSelection()) {
448 2 : if (!mb->isMeasure() && !mb->isHBox()) {
449 0 : continue;
450 : }
451 2 : System* sys = mb->system();
452 2 : IF_ASSERT_FAILED(sys) {
453 0 : continue;
454 : }
455 2 : if (systems.empty() || sys != systems.back()) {
456 2 : systems.push_back(sys);
457 : }
458 2 : }
459 :
460 2 : return systems;
461 2 : }
462 :
463 2 : std::vector<Page*> mu::engraving::Selection::pagesContainingSelection() const
464 : {
465 2 : if (const EngravingItem* el = element()) {
466 0 : if (el->isPageLockIndicator()) {
467 0 : return { const_cast<Page*>(toPageLockIndicator(el)->page()) };
468 : }
469 0 : if (el->isSystemLockIndicator()) {
470 0 : Page* page = toSystemLockIndicator(el)->system()->page();
471 0 : return page ? std::vector<Page*> { page } : std::vector<Page*> {};
472 : }
473 : }
474 :
475 2 : std::vector<Page*> pages;
476 6 : for (const MeasureBase* mb : measureBasesInSelection()) {
477 4 : Page* page = mb->page();
478 4 : if (page && !muse::contains(pages, page)) {
479 2 : pages.push_back(page);
480 : }
481 2 : }
482 2 : return pages;
483 2 : }
484 :
485 2802 : void Selection::deselectAll()
486 : {
487 2802 : if (m_state == SelState::RANGE) {
488 291 : m_score->setUpdateAll();
489 : }
490 2802 : clear();
491 2802 : updateState();
492 2802 : }
493 :
494 12311 : static RectF changeSelection(EngravingItem* e, bool b)
495 : {
496 12311 : RectF r = e->canvasBoundingRect();
497 12311 : e->setSelected(b);
498 12311 : r.unite(e->canvasBoundingRect());
499 12311 : return r;
500 : }
501 :
502 2806 : void Selection::clear()
503 : {
504 2806 : IF_ASSERT_FAILED(!isLocked()) {
505 0 : LOGE() << "selection locked, reason: " << lockReason();
506 0 : return;
507 : }
508 :
509 15096 : for (EngravingItem* e : m_el) {
510 12290 : e->score()->addRefresh(changeSelection(e, false));
511 12290 : if (e->isSpanner()) { // TODO: only visible elements should be selectable?
512 21 : Spanner* sp = toSpanner(e);
513 42 : for (auto s : sp->spannerSegments()) {
514 21 : e->score()->addRefresh(changeSelection(s, false));
515 : }
516 : }
517 : }
518 2806 : m_el.clear();
519 2806 : m_startSegment = 0;
520 2806 : m_endSegment = 0;
521 2806 : m_startBox = 0;
522 2806 : m_endBox = 0;
523 2806 : m_activeSegment = 0;
524 2806 : m_staffStart = 0;
525 2806 : m_staffEnd = 0;
526 2806 : m_activeTrack = 0;
527 2806 : setState(SelState::NONE);
528 : }
529 :
530 231282 : void Selection::remove(EngravingItem* el)
531 : {
532 231282 : const bool removed = muse::remove(m_el, el);
533 231282 : el->setSelected(false);
534 231282 : if (removed) {
535 42 : updateState();
536 : }
537 231282 : }
538 :
539 2478 : void Selection::add(EngravingItem* el)
540 : {
541 2478 : IF_ASSERT_FAILED(!isLocked()) {
542 0 : LOGE() << "selection locked, reason: " << lockReason();
543 0 : return;
544 : }
545 2478 : m_el.push_back(el);
546 2478 : update();
547 : }
548 :
549 7980 : void Selection::appendFiltered(EngravingItem* e)
550 : {
551 7980 : IF_ASSERT_FAILED(!isLocked()) {
552 0 : LOGE() << "selection locked, reason: " << lockReason();
553 0 : return;
554 : }
555 7980 : if (canSelectVoice(e->voice()) && canSelect(e)) {
556 7882 : m_el.push_back(e);
557 : }
558 : }
559 :
560 22451 : void Selection::appendFiltered(const std::unordered_set<EngravingItem*>& elems)
561 : {
562 22451 : IF_ASSERT_FAILED(!isLocked()) {
563 0 : LOGE() << "selection locked, reason: " << lockReason();
564 0 : return;
565 : }
566 :
567 24601 : for (EngravingItem* elem : elems) {
568 2150 : IF_ASSERT_FAILED(!elem->isSpannerSegment()) {
569 0 : LOGE() << "Append whole spanners instead of spanner segments";
570 0 : continue;
571 : }
572 :
573 : // Special handling for bends...
574 2150 : if (elem->isGuitarBend()) {
575 0 : GuitarBend* bend = toGuitarBend(elem);
576 0 : appendGuitarBend(bend);
577 0 : continue;
578 0 : }
579 :
580 : // Special handling for spanners...
581 2150 : if (elem->isSpanner() && !elem->isPartialTie() && !elem->isLaissezVib()) {
582 1788 : const Spanner* spanner = toSpanner(elem);
583 1788 : if (!noteAnchoredSpannerIsInRange(spanner, tickStart(), tickEnd())) {
584 0 : continue;
585 : }
586 3576 : for (SpannerSegment* spannerSeg : spanner->spannerSegments()) {
587 1788 : appendFiltered(spannerSeg);
588 : }
589 1788 : continue;
590 1788 : }
591 :
592 : // Special handling for grace notes...
593 362 : if (elem->isChord() && toChord(elem)->isGrace()) {
594 41 : appendChordRest(toChordRest(elem));
595 41 : continue;
596 : }
597 :
598 321 : appendFiltered(elem);
599 : }
600 : }
601 :
602 12613 : void Selection::appendChordRest(ChordRest* cr)
603 : {
604 12613 : IF_ASSERT_FAILED(!isLocked()) {
605 0 : LOGE() << "selection locked, reason: " << lockReason();
606 4968 : return;
607 : }
608 :
609 12613 : if (!canSelectVoice(cr->voice())) {
610 7 : return;
611 : }
612 :
613 12606 : const std::unordered_set<EngravingItem*> crAnchored = collectElementsAnchoredToChordRest(cr);
614 12606 : appendFiltered(crAnchored);
615 :
616 12606 : if (cr->isRestFamily()) {
617 4739 : appendFiltered(cr);
618 4739 : Rest* r = toRest(cr);
619 4847 : for (int i = 0; i < r->dots(); ++i) {
620 108 : appendFiltered(r->dot(i));
621 : }
622 4739 : return;
623 : }
624 :
625 7867 : IF_ASSERT_FAILED(cr->isChord()) {
626 0 : return;
627 : }
628 :
629 7867 : Chord* chord = toChord(cr);
630 :
631 7867 : const size_t totalNotesInChord = chord->notes().size();
632 7867 : const bool isSingleNote = totalNotesInChord == 1;
633 :
634 7867 : size_t totalAppendedNotes = 0;
635 17368 : for (size_t noteIdx = 0; noteIdx < totalNotesInChord; ++noteIdx) {
636 9501 : Note* note = chord->notes().at(noteIdx);
637 :
638 9501 : const std::unordered_set<EngravingItem*> noteAnchored = collectElementsAnchoredToNote(note, true, false);
639 9501 : appendFiltered(noteAnchored);
640 :
641 9501 : if (chord->isGrace() && !canSelect(chord)) {
642 8 : continue;
643 : }
644 :
645 : //! Hack Explainer: Due to the fact that this method is called while we're still in the process of "building" our
646 : //! selection, we can't know for certain whether the selection as a whole will contain multi-note Chords (and thus
647 : //! whether the "includeSingleNotes" flag should apply - see includeSingleNotes in select.h). For this reason, this
648 : //! method ALWAYS appends single note chords. If single note chords should be ommitted from a selection, we simply
649 : //! don't call this method (see usage of appendChordRest in Selection::updateSelectedElements).
650 9493 : if (/*hack*/ !isSingleNote && !canSelectNoteIdx(noteIdx, totalNotesInChord, /*hack*/ true)) {
651 388 : continue;
652 : }
653 :
654 9105 : m_el.push_back(note);
655 9105 : ++totalAppendedNotes;
656 :
657 9105 : if (note->accidental()) {
658 399 : m_el.push_back(note->accidental());
659 : }
660 9109 : for (EngravingItem* el : note->el()) {
661 4 : if (el->isFingering()) {
662 : // Slight hack (already handled, see collectElementsAnchoredToNote)...
663 4 : continue;
664 : }
665 0 : m_el.push_back(el);
666 : }
667 9315 : for (NoteDot* dot : note->dots()) {
668 210 : m_el.push_back(dot);
669 : }
670 9501 : }
671 :
672 : //! NOTE: Beams, stems, etc should only be added if all notes in chord are selected...
673 7867 : if (totalAppendedNotes < totalNotesInChord) {
674 222 : return;
675 : }
676 7645 : if (chord->beam() && !muse::contains(m_el, static_cast<EngravingItem*>(chord->beam()))) {
677 1083 : m_el.push_back(chord->beam());
678 : }
679 7645 : if (chord->stem()) {
680 7299 : m_el.push_back(chord->stem());
681 : }
682 7645 : if (chord->hook()) {
683 932 : m_el.push_back(chord->hook());
684 : }
685 7645 : if (chord->stemSlash()) {
686 13 : m_el.push_back(chord->stemSlash());
687 : }
688 12606 : }
689 :
690 603 : void Selection::appendTupletHierarchy(Tuplet* innermostTuplet)
691 : {
692 603 : if (!canSelectVoice(innermostTuplet->voice()) || muse::contains(m_el, static_cast<EngravingItem*>(innermostTuplet))) {
693 52 : return;
694 : }
695 :
696 : //! NOTE: Not hugely efficient since canSelectTuplet will recursively check all contained tuplets...
697 1102 : if (!selectionFilter().canSelectTuplet(innermostTuplet, tickStart(), tickEnd(),
698 551 : rangeContainsMultiNoteChords())) {
699 280 : return;
700 : }
701 :
702 271 : m_el.push_back(innermostTuplet);
703 :
704 : // Recursively append upwards/outwards
705 271 : Tuplet* outerTuplet = innermostTuplet->tuplet();
706 271 : if (outerTuplet) {
707 99 : appendTupletHierarchy(outerTuplet);
708 : }
709 : }
710 :
711 0 : void Selection::appendGuitarBend(GuitarBend* guitarBend)
712 : {
713 0 : if (!guitarBend || !canSelectVoice(guitarBend->voice())) {
714 0 : return;
715 : }
716 :
717 0 : m_el.push_back(guitarBend);
718 :
719 0 : if (GuitarBendHold* hold = guitarBend->holdLine()) {
720 0 : if (hold->tick2() < tickEnd()) {
721 0 : m_el.push_back(hold);
722 : }
723 : }
724 :
725 0 : if (GuitarBendSegment* bendSeg = toGuitarBendSegment(guitarBend->frontSegment())) {
726 0 : if (GuitarBendText* bendText = bendSeg->bendText()) {
727 0 : m_el.push_back(bendText);
728 : }
729 : }
730 : }
731 :
732 717 : void Selection::updateSelectedElements()
733 : {
734 717 : IF_ASSERT_FAILED(!isLocked()) {
735 0 : LOGE() << "selection locked, reason: " << lockReason();
736 0 : return;
737 : }
738 717 : if (m_state != SelState::RANGE) {
739 0 : m_rangeContainsMultiNoteChords = false;
740 0 : update();
741 0 : return;
742 : }
743 :
744 : // TODO: Quite a lot of nesting - should clean this up...
745 717 : if (m_state == SelState::RANGE && m_plannedTick1 != Fraction(-1, 1) && m_plannedTick2 != Fraction(-1, 1)) {
746 80 : const staff_idx_t staffStart = m_staffStart;
747 80 : const staff_idx_t staffEnd = m_staffEnd;
748 80 : Box* startBox = m_startBox;
749 80 : Box* endBox = m_endBox;
750 :
751 80 : deselectAll();
752 :
753 80 : Segment* s1 = nullptr;
754 80 : Segment* s2 = nullptr;
755 :
756 80 : if (startBox && endBox && m_plannedTick1 == m_plannedTick2) {
757 : // Box-only range...
758 0 : if (m_plannedTick1 == m_score->endTick()) {
759 0 : s1 = m_score->lastSegmentMM();
760 : } else {
761 0 : s1 = m_score->tick2segmentMM(m_plannedTick1, true, SegmentType::Duration);
762 0 : s2 = s1;
763 : }
764 : } else {
765 80 : s1 = m_score->tick2segmentMM(m_plannedTick1);
766 80 : s2 = m_score->tick2segmentMM(m_plannedTick2, /* first */ true /* HACK */);
767 80 : if (s2 && s2->measure()->isMMRest()) {
768 0 : s2 = s2->prev1MM(); // HACK
769 : }
770 : // These hacks are needed to prevent https://musescore.org/node/173381.
771 : // This should exclude any segments belonging to MM-rest range from the selection.
772 80 : if (s1 && s2 && s1->tick() + s1->ticks() > s2->tick()) {
773 : // can happen with MM rests as tick2measure returns only the first segment for them.
774 :
775 0 : m_plannedTick1 = Fraction(-1, 1);
776 0 : m_plannedTick2 = Fraction(-1, 1);
777 0 : return;
778 : }
779 :
780 80 : if (s2 && s2 == s2->measure()->first()) {
781 : // we want the last segment of the previous measure (unless it's part of a MMrest)
782 15 : Measure* prevMeasure = s2->measure()->prevMeasure();
783 15 : if (!(prevMeasure && prevMeasure != prevMeasure->coveringMMRestOrThis())) {
784 15 : s2 = s2->prev1();
785 : }
786 : }
787 : }
788 :
789 80 : setRange(s1, s2, staffStart, staffEnd, startBox, endBox);
790 :
791 80 : m_plannedTick1 = Fraction(-1, 1);
792 80 : m_plannedTick2 = Fraction(-1, 1);
793 : }
794 :
795 14011 : for (EngravingItem* e : m_el) {
796 13294 : e->setSelected(false);
797 : }
798 717 : m_el.clear();
799 :
800 : // assert:
801 717 : size_t staves = m_score->nstaves();
802 717 : if (m_staffStart == muse::nidx || m_staffStart >= staves || m_staffEnd == muse::nidx || m_staffEnd > staves
803 717 : || m_staffStart >= m_staffEnd) {
804 0 : LOGD("updateSelectedElements: bad staff selection %zu - %zu, staves %zu", m_staffStart, m_staffEnd, staves);
805 0 : m_staffStart = 0;
806 0 : m_staffEnd = 0;
807 : }
808 717 : track_idx_t startTrack = m_staffStart * VOICES;
809 717 : track_idx_t endTrack = m_staffEnd * VOICES;
810 :
811 : //! NOTE: See appendChord/appendChordRest - we should include single notes if the selection consists solely of
812 : //! single notes, even if the "include single notes" filter flag is false...
813 717 : std::vector<Chord*> singleNoteChords;
814 717 : size_t totalChordsFound = 0;
815 :
816 : //! NOTE: We also need to delay the appending of tuplets. We should only display tuplets as selected
817 : //! if all of their contained elements are selected...
818 717 : std::unordered_set<Tuplet*> innerTuplets;
819 :
820 4057 : for (track_idx_t st = startTrack; st < endTrack; ++st) {
821 65916 : for (Segment* s = m_startSegment; s && (s != m_endSegment); s = s->next1MM()) {
822 62576 : if (!s->enabled() || s->isEndBarLineType()) { // do not select end bar line
823 54571 : continue;
824 : }
825 57632 : for (EngravingItem* e : s->annotations()) {
826 5060 : if (e->track() != st) {
827 4299 : continue;
828 : }
829 761 : if (e->isFretDiagram()) {
830 145 : FretDiagram* fd = toFretDiagram(e);
831 145 : if (Harmony* harm = fd->harmony()) {
832 72 : appendFiltered(harm);
833 : }
834 : }
835 761 : appendFiltered(e);
836 : }
837 52572 : EngravingItem* e = s->element(st);
838 52572 : if (!e || e->generated() || e->isTimeSig() || e->isKeySig()) {
839 39813 : continue;
840 : }
841 :
842 12759 : if (!e->isChordRest()) {
843 15 : appendFiltered(e);
844 15 : continue;
845 : }
846 :
847 12744 : ChordRest* cr = toChordRest(e);
848 12744 : if (Tuplet* tuplet = cr->tuplet()) {
849 1233 : innerTuplets.emplace(tuplet);
850 : }
851 :
852 12744 : if (!cr->isChord()) {
853 4739 : appendChordRest(cr);
854 4739 : continue;
855 : }
856 :
857 8005 : Chord* chord = toChord(cr);
858 8005 : const std::vector<Note*> notes = chord->notes();
859 8005 : if (notes.size() == 1) {
860 7304 : singleNoteChords.emplace_back(chord);
861 : } else {
862 701 : appendChordRest(chord);
863 : }
864 8005 : ++totalChordsFound;
865 8005 : }
866 : }
867 :
868 717 : m_rangeContainsMultiNoteChords = totalChordsFound > singleNoteChords.size();
869 :
870 8021 : for (Chord* singleNoteChord : singleNoteChords) {
871 7304 : if (!m_rangeContainsMultiNoteChords || selectionFilter().includeSingleNotes()) {
872 7132 : appendChordRest(singleNoteChord);
873 : } else {
874 : // Include elements anchored to the note even if the note itself isn't included...
875 172 : const Note* note = singleNoteChord->notes().front();
876 172 : const std::unordered_set<EngravingItem*> noteAnchored = collectElementsAnchoredToNote(note, true, false);
877 172 : appendFiltered(noteAnchored);
878 172 : const std::unordered_set<EngravingItem*> crAnchored = collectElementsAnchoredToChordRest(singleNoteChord);
879 172 : appendFiltered(crAnchored);
880 172 : }
881 : }
882 :
883 1221 : for (Tuplet* tuplet : innerTuplets) {
884 504 : appendTupletHierarchy(tuplet);
885 : }
886 :
887 717 : const Fraction rangeStart = tickStart();
888 717 : const Fraction rangeEnd = tickEnd();
889 :
890 : //! NOTE: Ties are NOT handled in here - these are note anchored and handled in appendChordRest...
891 2494 : for (auto i = m_score->spanner().begin(); i != m_score->spanner().end(); ++i) {
892 1777 : Spanner* sp = (*i).second;
893 : // ignore spanners belonging to other tracks
894 1777 : if (sp->track() < startTrack || sp->track() >= endTrack) {
895 26 : continue;
896 : }
897 :
898 1751 : const bool startAndEndCalculated = sp->startElement() && sp->endElement();
899 1751 : if (!startAndEndCalculated || !canSelectVoice(sp->track()) || sp->isVolta()) {
900 8 : continue;
901 : }
902 :
903 : //! NOTE: isZeroLengthAtStart (for lack of a better name) covers an edge case that may occur if sp spans from
904 : //! a grace note to its parent note or vice versa. In this scenario sp->tick == sp->tick2 == rangeStart and
905 : //! we should include the spanner in our selection...
906 1743 : const bool overlapsSelectionRange = rangeStart < sp->tick2() && rangeEnd > sp->tick();
907 1743 : const bool isZeroLengthAtStart = sp->tick() == sp->tick2() && sp->tick() == rangeStart;
908 1743 : if (!overlapsSelectionRange && !isZeroLengthAtStart) {
909 1564 : continue;
910 : }
911 :
912 355 : for (auto seg : sp->spannerSegments()) {
913 176 : appendFiltered(seg);
914 : }
915 : }
916 717 : update();
917 717 : m_score->setSelectionChanged(true);
918 717 : }
919 :
920 2820 : bool Selection::rangeContainsMultiNoteChords() const
921 : {
922 2820 : IF_ASSERT_FAILED(m_state == SelState::RANGE) {
923 0 : return false;
924 : }
925 2820 : return m_rangeContainsMultiNoteChords;
926 : }
927 :
928 11 : void Selection::setStartSegment(Segment* s)
929 : {
930 11 : m_startSegment = s;
931 11 : validateBoxesAndSegments();
932 11 : }
933 :
934 11 : void Selection::setEndSegment(Segment* s)
935 : {
936 11 : m_endSegment = s;
937 11 : validateBoxesAndSegments();
938 11 : }
939 :
940 570 : void Selection::validateBoxesAndSegments()
941 : {
942 570 : if (m_startBox) {
943 22 : IF_ASSERT_FAILED(m_startSegment && m_startSegment->tick() == m_startBox->tick()) {
944 0 : m_startBox = nullptr;
945 : }
946 : }
947 :
948 570 : if (m_endBox) {
949 3 : const Fraction endTick = m_endSegment ? m_endSegment->tick() : m_score->endTick();
950 3 : IF_ASSERT_FAILED(endTick == m_endBox->tick()) {
951 0 : m_endBox = nullptr;
952 : }
953 : }
954 570 : }
955 :
956 345 : void Selection::setRange(Segment* startSegment, Segment* endSegment, staff_idx_t staffStart, staff_idx_t staffEnd,
957 : Box* startBox, Box* endBox)
958 : {
959 345 : assert(staffEnd > staffStart && staffEnd <= m_score->nstaves());
960 345 : assert(!(endSegment && !startSegment));
961 :
962 345 : m_startSegment = startSegment;
963 345 : m_endSegment = endSegment;
964 345 : m_startBox = startBox;
965 345 : m_endBox = endBox;
966 345 : validateBoxesAndSegments();
967 :
968 345 : m_activeSegment = endSegment;
969 345 : m_staffStart = staffStart;
970 345 : m_staffEnd = staffEnd;
971 345 : m_activeTrack = staff2track(staffStart);
972 :
973 345 : if (m_state == SelState::RANGE) {
974 0 : m_score->setSelectionChanged(true);
975 : } else {
976 345 : setState(SelState::RANGE);
977 : }
978 345 : }
979 :
980 : //---------------------------------------------------------
981 : // setRangeTicks
982 : // sets the range to be selected on next
983 : // updateSelectedElements() call. Can be used if some
984 : // segment structure changes are expected (e.g. if
985 : // creating MM rests is pending).
986 : //---------------------------------------------------------
987 :
988 92 : void Selection::setRangeTicks(const Fraction& tick1, const Fraction& tick2, staff_idx_t staffStart, staff_idx_t staffEnd,
989 : Box* startBox, Box* endBox)
990 : {
991 92 : assert(staffEnd > staffStart && staffEnd <= m_score->nstaves());
992 :
993 92 : m_plannedTick1 = tick1;
994 92 : m_plannedTick2 = tick2;
995 92 : m_startSegment = m_endSegment = m_activeSegment = nullptr;
996 92 : m_startBox = startBox;
997 92 : m_endBox = endBox;
998 92 : m_staffStart = staffStart;
999 92 : m_staffEnd = staffEnd;
1000 92 : m_activeTrack = staff2track(staffStart);
1001 :
1002 92 : if (m_state == SelState::RANGE) {
1003 22 : m_score->setSelectionChanged(true);
1004 : } else {
1005 70 : setState(SelState::RANGE);
1006 : }
1007 92 : }
1008 :
1009 : //---------------------------------------------------------
1010 : // update
1011 : /// Set select flag for all Elements in select list.
1012 : //---------------------------------------------------------
1013 :
1014 3438 : void Selection::update()
1015 : {
1016 55020 : for (EngravingItem* e : m_el) {
1017 51582 : e->setSelected(true); // also tells accessibility that e has focus
1018 : }
1019 :
1020 3438 : ChordRest* cr = activeCR();
1021 3438 : if (!cr) {
1022 2759 : updateState();
1023 2759 : return;
1024 : }
1025 :
1026 : // Only one element can have focus at a time, and currently the final element in m_el has
1027 : // focus. That's ok for a LIST selection (because it corresponds to the last element the
1028 : // user clicked on) but for range selections, we should focus something in activeCR...
1029 679 : EngravingItem* toSelectAgain = nullptr;
1030 679 : if (cr->isChord()) {
1031 : // Use the top selected note in the chord...
1032 471 : const std::vector<Note*> notes = toChord(cr)->notes();
1033 471 : const size_t noteCount = notes.size();
1034 480 : for (size_t noteIdx = noteCount - 1; noteIdx < noteCount; --noteIdx) {
1035 475 : Note* note = notes.at(noteIdx);
1036 475 : if (selectionFilter().canSelectNoteIdx(noteIdx, notes.size(), rangeContainsMultiNoteChords())) {
1037 466 : toSelectAgain = note;
1038 466 : break;
1039 : }
1040 : }
1041 471 : } else {
1042 208 : toSelectAgain = cr;
1043 : }
1044 :
1045 679 : if (!toSelectAgain || !toSelectAgain->selected()) {
1046 : // Means we have a range with no selected elements...
1047 6 : updateState();
1048 6 : return;
1049 : }
1050 :
1051 673 : toSelectAgain->setSelected(true); // HACK: select it again so accessibility thinks it has focus
1052 :
1053 673 : updateState();
1054 : }
1055 :
1056 0 : void Selection::dump()
1057 : {
1058 0 : LOGD("Selection dump: ");
1059 0 : switch (m_state) {
1060 0 : case SelState::NONE: LOGD("NONE");
1061 0 : return;
1062 0 : case SelState::RANGE: LOGD("RANGE");
1063 0 : break;
1064 0 : case SelState::LIST: LOGD("LIST");
1065 0 : break;
1066 : }
1067 0 : for (const EngravingItem* e : m_el) {
1068 0 : LOGD(" %p %s", e, e->typeName());
1069 : }
1070 : }
1071 :
1072 3026 : bool Selection::rangeIsValid() const
1073 : {
1074 3026 : if (!isRange()) {
1075 3025 : return false;
1076 : }
1077 :
1078 : //! NOTE: m_startSegment being non-null and m_endSegment being null is a
1079 : //! valid case. It means we've selected the last segment of the final measure...
1080 1 : if (!m_startSegment) {
1081 0 : return false;
1082 : }
1083 :
1084 1 : if (m_endSegment && m_endSegment->tick() <= m_startSegment->tick()) {
1085 0 : const bool isBoxOnlyRange = m_startBox && m_endBox && tickStart() == tickEnd()
1086 0 : && m_startBox->isBeforeOrEqual(m_endBox);
1087 0 : if (!isBoxOnlyRange) {
1088 0 : return false;
1089 : }
1090 : }
1091 :
1092 1 : const size_t totalStaves = m_score->nstaves();
1093 1 : if (m_staffStart == muse::nidx || m_staffStart >= totalStaves) {
1094 0 : return false;
1095 : }
1096 1 : if (m_staffEnd == muse::nidx || m_staffEnd > totalStaves) {
1097 0 : return false;
1098 : }
1099 :
1100 1 : return m_staffStart < m_staffEnd;
1101 : }
1102 :
1103 : //---------------------------------------------------------
1104 : // updateState
1105 : /// update selection and input state
1106 : //---------------------------------------------------------
1107 :
1108 6282 : void Selection::updateState()
1109 : {
1110 : //! NOTE: Range can be valid even if no elements are selectable in that range...
1111 6282 : if (m_el.size() == 0 && !rangeIsValid()) {
1112 3025 : setState(SelState::NONE);
1113 3257 : } else if (m_state == SelState::NONE) {
1114 1554 : setState(SelState::LIST);
1115 : }
1116 :
1117 6282 : const EngravingItem* e = element();
1118 6282 : if (!e) {
1119 4692 : return;
1120 : }
1121 :
1122 1590 : if (e->isSpannerSegment()) {
1123 2 : m_currentTick = toSpannerSegment(e)->spanner()->tick();
1124 : } else {
1125 1588 : m_currentTick = e->tick();
1126 : }
1127 : // ignore system elements (e.g., frames)
1128 1590 : if (e->track() != muse::nidx) {
1129 1565 : m_currentTrack = e->track();
1130 : }
1131 : }
1132 :
1133 10276 : void Selection::setState(SelState s)
1134 : {
1135 10276 : if (m_state == s) {
1136 6700 : return;
1137 : }
1138 :
1139 3576 : m_state = s;
1140 3576 : m_score->setSelectionChanged(true);
1141 : }
1142 :
1143 102 : String Selection::mimeType() const
1144 : {
1145 102 : switch (m_state) {
1146 27 : case SelState::LIST:
1147 27 : return isSingle() ? String::fromAscii(mimeSymbolFormat) : String::fromAscii(mimeSymbolListFormat);
1148 75 : case SelState::RANGE:
1149 75 : return String::fromAscii(mimeStaffListFormat);
1150 0 : case SelState::NONE:
1151 : default:
1152 0 : break;
1153 : }
1154 :
1155 0 : return String();
1156 : }
1157 :
1158 108 : muse::ByteArray Selection::mimeData() const
1159 : {
1160 108 : muse::ByteArray a;
1161 108 : switch (m_state) {
1162 27 : case SelState::LIST:
1163 27 : if (isSingle()) {
1164 16 : a = element()->mimeData();
1165 : } else {
1166 11 : a = symbolListMimeData();
1167 : }
1168 27 : break;
1169 0 : case SelState::NONE:
1170 0 : break;
1171 81 : case SelState::RANGE:
1172 81 : a = staffMimeData();
1173 81 : break;
1174 : }
1175 108 : return a;
1176 0 : }
1177 :
1178 81 : muse::ByteArray Selection::staffMimeData() const
1179 : {
1180 324 : return rw::RWRegister::writer()->writeStaffSelection(m_score, selectionFilter(), staffStart(), staffEnd(), tickStart(), tickEnd(),
1181 324 : m_startSegment, m_endSegment);
1182 : }
1183 :
1184 11 : muse::ByteArray Selection::symbolListMimeData() const
1185 : {
1186 : struct MapData {
1187 : EngravingItem* e;
1188 : Segment* s;
1189 : };
1190 :
1191 11 : track_idx_t topTrack = 1000000;
1192 11 : track_idx_t bottomTrack = 0;
1193 11 : Segment* firstSeg = 0;
1194 11 : Fraction firstTick = Fraction(0x7FFFFFFF, 1);
1195 11 : Segment* seg = 0;
1196 11 : std::multimap<int64_t, MapData> map;
1197 :
1198 : // scan selection element list, inserting relevant elements in a tick-sorted map
1199 120 : for (EngravingItem* e : m_el) {
1200 109 : switch (e->type()) {
1201 55 : case ElementType::ARTICULATION:
1202 : case ElementType::ORNAMENT:
1203 : case ElementType::TAPPING:
1204 : case ElementType::ARPEGGIO:
1205 : case ElementType::CHORD_BRACKET:
1206 : case ElementType::TREMOLO_SINGLECHORD: {
1207 : // ignore articulations not attached to chords/rest or segment
1208 55 : if (!e->ownershipParent()->isChordRest()) {
1209 0 : continue;
1210 : }
1211 55 : ChordRest* cr = toChordRest(e->ownershipParent());
1212 55 : seg = cr->segment();
1213 55 : } break;
1214 0 : case ElementType::FERMATA:
1215 0 : seg = toFermata(e)->segment();
1216 0 : break;
1217 0 : case ElementType::BREATH:
1218 0 : seg = toBreath(e)->segment();
1219 0 : break;
1220 15 : case ElementType::PLAYTECH_ANNOTATION:
1221 : case ElementType::CAPO:
1222 : case ElementType::STRING_TUNINGS:
1223 : case ElementType::STAFF_TEXT:
1224 : case ElementType::STAVE_SHARING_LABEL:
1225 15 : seg = toStaffTextBase(e)->segment();
1226 15 : break;
1227 0 : case ElementType::EXPRESSION:
1228 0 : seg = toExpression(e)->segment();
1229 0 : break;
1230 15 : case ElementType::STICKING:
1231 15 : seg = toSticking(e)->segment();
1232 15 : break;
1233 0 : case ElementType::FIGURED_BASS:
1234 0 : seg = toFiguredBass(e)->segment();
1235 0 : break;
1236 11 : case ElementType::LYRICS:
1237 11 : seg = toLyrics(e)->segment();
1238 11 : break;
1239 0 : case ElementType::DYNAMIC:
1240 0 : seg = toDynamic(e)->segment();
1241 0 : break;
1242 13 : case ElementType::HARMONY:
1243 : case ElementType::FRET_DIAGRAM:
1244 : // ignore chord symbols or fret diagrams not attached to segment
1245 13 : if (e->ownershipParent()->isSegment()) {
1246 13 : seg = toSegment(e->ownershipParent());
1247 13 : break;
1248 : }
1249 0 : continue;
1250 0 : case ElementType::HARP_DIAGRAM:
1251 0 : seg = toHarpPedalDiagram(e)->segment();
1252 0 : break;
1253 0 : case ElementType::SLUR_SEGMENT:
1254 : case ElementType::HAIRPIN_SEGMENT:
1255 : case ElementType::OTTAVA_SEGMENT:
1256 : case ElementType::TRILL_SEGMENT:
1257 : case ElementType::LET_RING_SEGMENT:
1258 : case ElementType::VIBRATO_SEGMENT:
1259 : case ElementType::PALM_MUTE_SEGMENT:
1260 : case ElementType::WHAMMY_BAR_SEGMENT:
1261 : case ElementType::RASGUEADO_SEGMENT:
1262 : case ElementType::HARMONIC_MARK_SEGMENT:
1263 : case ElementType::PICK_SCRAPE_SEGMENT:
1264 : case ElementType::TEXTLINE_SEGMENT:
1265 : case ElementType::PEDAL_SEGMENT:
1266 0 : e = toSpannerSegment(e)->spanner();
1267 : [[fallthrough]];
1268 0 : case ElementType::SLUR:
1269 : case ElementType::HAMMER_ON_PULL_OFF:
1270 : case ElementType::HAIRPIN:
1271 : case ElementType::OTTAVA:
1272 : case ElementType::TRILL:
1273 : case ElementType::LET_RING:
1274 : case ElementType::VIBRATO:
1275 : case ElementType::PALM_MUTE:
1276 : case ElementType::WHAMMY_BAR:
1277 : case ElementType::RASGUEADO:
1278 : case ElementType::HARMONIC_MARK:
1279 : case ElementType::PICK_SCRAPE:
1280 : case ElementType::TEXTLINE:
1281 : case ElementType::PEDAL:
1282 0 : seg = toSpanner(e)->startSegment();
1283 0 : break;
1284 0 : default:
1285 : // Elements of other types are ignored. To allow copying them,
1286 : // add support for them here and in `Score::pasteSymbols`.
1287 0 : continue;
1288 0 : }
1289 109 : track_idx_t track = e->track();
1290 109 : if (track < topTrack) {
1291 11 : topTrack = track;
1292 : }
1293 109 : if (track > bottomTrack) {
1294 1 : bottomTrack = track;
1295 : }
1296 109 : if (seg->tick() < firstTick) {
1297 11 : firstSeg = seg;
1298 11 : firstTick = seg->tick();
1299 : }
1300 109 : MapData mapData = { e, seg };
1301 109 : map.insert(std::pair<int64_t, MapData>(((int64_t)track << 32) + seg->tick().ticks(), mapData));
1302 : }
1303 :
1304 : // scan the map, computing for each element a relative track and tick offset,
1305 : // and the number of CR segments to skip
1306 0 : std::vector<SelectedSymbol> symbols;
1307 11 : track_idx_t currTrack = muse::nidx;
1308 120 : for (auto iter = map.cbegin(); iter != map.cend(); ++iter) {
1309 : int numSegs;
1310 109 : track_idx_t track = static_cast<track_idx_t>(iter->first >> 32);
1311 109 : if (currTrack != track) {
1312 12 : currTrack = track;
1313 12 : seg = firstSeg;
1314 : }
1315 109 : int tickOffset = static_cast<int>(iter->first & 0xFFFFFFFF) - firstTick.ticks();
1316 109 : numSegs = 0;
1317 : // with figured bass, we need to look for the proper segment
1318 : // not only according to ChordRest elements, but also annotations
1319 109 : if (iter->second.e->isFiguredBass()) {
1320 0 : bool done = false;
1321 0 : for (; seg; seg = seg->next1()) {
1322 0 : if (seg->isChordRestType()) {
1323 : // if no ChordRest in right track, look in annotations
1324 0 : if (seg->element(currTrack) == nullptr) {
1325 0 : for (EngravingItem* el : seg->annotations()) {
1326 : // do annotations include our element?
1327 0 : if (el == iter->second.e) {
1328 0 : done = true;
1329 0 : break;
1330 : }
1331 : // do annotations include any f.b.?
1332 0 : if (el->isFiguredBass() && el->track() == track) {
1333 0 : numSegs++; //yes: it counts as a step
1334 0 : break;
1335 : }
1336 : }
1337 0 : if (done) {
1338 0 : break;
1339 : }
1340 0 : continue; // segment is not relevant: no ChordRest nor f.b.
1341 0 : } else {
1342 0 : if (iter->second.s == seg) {
1343 0 : break;
1344 : }
1345 : }
1346 0 : numSegs++;
1347 : }
1348 : }
1349 : } else {
1350 231 : while (seg && iter->second.s != seg) {
1351 122 : seg = seg->nextCR(currTrack);
1352 122 : numSegs++;
1353 : }
1354 : }
1355 109 : symbols.push_back({ track, tickOffset, numSegs, iter->second.e });
1356 : }
1357 :
1358 22 : return rw::RWRegister::writer()->writeSymbolListSelection(topTrack, bottomTrack, symbols);
1359 11 : }
1360 :
1361 1 : std::vector<EngravingItem*> Selection::elements(ElementType type) const
1362 : {
1363 1 : std::vector<EngravingItem*> result;
1364 :
1365 24 : for (EngravingItem* element : m_el) {
1366 23 : if (element->type() == type) {
1367 23 : result.push_back(element);
1368 : }
1369 : }
1370 :
1371 1 : return result;
1372 0 : }
1373 :
1374 111 : std::vector<Note*> Selection::noteList(track_idx_t selTrack) const
1375 : {
1376 111 : std::vector<Note*> nl;
1377 :
1378 111 : if (m_state == SelState::LIST) {
1379 237 : for (EngravingItem* e : m_el) {
1380 137 : if (e->isNote()) {
1381 137 : nl.push_back(toNote(e));
1382 : }
1383 : }
1384 11 : } else if (m_state == SelState::RANGE) {
1385 22 : for (staff_idx_t staffIdx = staffStart(); staffIdx < staffEnd(); ++staffIdx) {
1386 11 : track_idx_t startTrack = staffIdx * VOICES;
1387 11 : track_idx_t endTrack = startTrack + VOICES;
1388 203 : for (Segment* seg = m_startSegment; seg && seg != m_endSegment; seg = seg->next1()) {
1389 192 : if (!(seg->segmentType() & (SegmentType::ChordRest))) {
1390 40 : continue;
1391 : }
1392 760 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1393 608 : if (!canSelectVoice(track)) {
1394 476 : continue;
1395 : }
1396 608 : EngravingItem* e = seg->element(track);
1397 152 : if (e == 0 || !e->isChord()
1398 760 : || (selTrack != muse::nidx && selTrack != track)) {
1399 476 : continue;
1400 : }
1401 132 : Chord* c = toChord(e);
1402 132 : const std::vector<Note*> notes = c->notes();
1403 264 : for (size_t noteIdx = 0; noteIdx < notes.size(); ++noteIdx) {
1404 132 : Note* note = notes.at(noteIdx);
1405 132 : if (selectionFilter().canSelectNoteIdx(noteIdx, notes.size(), rangeContainsMultiNoteChords())) {
1406 132 : nl.push_back(note);
1407 : }
1408 : }
1409 132 : for (Chord* g : c->graceNotes()) {
1410 0 : nl.insert(nl.end(), g->notes().begin(), g->notes().end());
1411 : }
1412 132 : }
1413 : }
1414 : }
1415 : }
1416 111 : return nl;
1417 0 : }
1418 :
1419 : //---------------------------------------------------------
1420 : // checkStartForPartialCopy
1421 : // return no error if element is NOT a tuplet or is start of a tuplet/tremolo
1422 : // return error if element is part of a tuplet/tremolo, but not the start
1423 : //---------------------------------------------------------
1424 :
1425 87 : static MsError checkStartForPartialCopy(EngravingItem* e)
1426 : {
1427 87 : if (!e || !e->isChordRest()) {
1428 0 : return MsError::MS_NO_ERROR;
1429 : }
1430 :
1431 87 : const ChordRest* cr = toChordRest(e);
1432 87 : if (cr->tuplet()) {
1433 : // check that complete tuplet is selected, all the way up to top level
1434 2 : Tuplet* tuplet = cr->tuplet();
1435 4 : while (tuplet) {
1436 2 : if (tuplet->elements().front() != e) {
1437 0 : return MsError::SOURCE_PARTIAL_TUPLET;
1438 : }
1439 2 : e = tuplet;
1440 2 : tuplet = tuplet->tuplet();
1441 : }
1442 : }
1443 :
1444 87 : if (cr->isChord()) {
1445 83 : const Chord* chord = toChord(cr);
1446 83 : const TremoloTwoChord* ttc = chord ? chord->tremoloTwoChord() : nullptr;
1447 83 : if (ttc && ttc->chord2() == chord) {
1448 0 : return MsError::SOURCE_PARTIAL_TREMOLO;
1449 : }
1450 : }
1451 :
1452 87 : return MsError::MS_NO_ERROR;
1453 : }
1454 :
1455 : //---------------------------------------------------------
1456 : // checkEndForPartialCopy
1457 : // return no error if element is NOT a tuplet or is end of a tuplet/tremolo
1458 : // return error if element is part of a tuplet/tremolo, but not the end
1459 : //---------------------------------------------------------
1460 :
1461 307 : static MsError checkEndForPartialCopy(EngravingItem* e, const Fraction& endTick)
1462 : {
1463 307 : if (!e || !e->isChordRest()) {
1464 220 : return MsError::MS_NO_ERROR;
1465 : }
1466 :
1467 87 : const ChordRest* cr = toChordRest(e);
1468 87 : if (cr->tuplet()) {
1469 : // check that complete tuplet is selected, all the way up to top level
1470 1 : Tuplet* tuplet = cr->tuplet();
1471 2 : while (tuplet) {
1472 1 : if (tuplet->elements().back() != e) {
1473 0 : return MsError::SOURCE_PARTIAL_TUPLET;
1474 : }
1475 1 : e = tuplet;
1476 1 : tuplet = tuplet->tuplet();
1477 : }
1478 : // also check that the selection extends to the end of the top-level tuplet
1479 1 : tuplet = toTuplet(e);
1480 1 : if (tuplet->endTick() > endTick) {
1481 0 : return MsError::SOURCE_PARTIAL_TUPLET;
1482 : }
1483 : }
1484 :
1485 87 : if (cr->isChord()) {
1486 79 : const Chord* chord = toChord(cr);
1487 79 : const TremoloTwoChord* ttc = chord ? chord->tremoloTwoChord() : nullptr;
1488 79 : if (ttc && ttc->chord1() == chord) {
1489 0 : return MsError::SOURCE_PARTIAL_TREMOLO;
1490 : }
1491 : }
1492 :
1493 87 : return MsError::MS_NO_ERROR;
1494 : }
1495 :
1496 : //---------------------------------------------------------
1497 : // canCopy
1498 : // return false if range selection intersects a tuplet
1499 : // or a tremolo, or a local time signature, or only part
1500 : // of a measure repeat group
1501 : //---------------------------------------------------------
1502 :
1503 87 : bool Selection::canCopy() const
1504 : {
1505 87 : if (m_state != SelState::RANGE) {
1506 12 : return true;
1507 : }
1508 :
1509 75 : Fraction endTick = m_endSegment ? m_endSegment->tick() : m_score->lastSegment()->tick();
1510 75 : Fraction timeStretch(1, 0);
1511 :
1512 153 : for (staff_idx_t staffIdx = m_staffStart; staffIdx != m_staffEnd; ++staffIdx) {
1513 390 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
1514 312 : track_idx_t track = staffIdx * VOICES + voice;
1515 312 : if (!canSelectVoice(track)) {
1516 5 : continue;
1517 : }
1518 :
1519 : // check first cr in track within selection
1520 307 : ChordRest* check = m_startSegment->nextChordRest(track);
1521 307 : if (check && check->tick() < endTick) {
1522 87 : const MsError err = checkStartForPartialCopy(check);
1523 87 : if (err != MsError::MS_NO_ERROR) {
1524 0 : MScore::setError(err);
1525 0 : return false;
1526 : }
1527 : }
1528 :
1529 307 : if (!m_endSegment) {
1530 0 : continue;
1531 : }
1532 :
1533 : // find last segment in the selection.
1534 : // Note that _endSegment is the first segment after the selection
1535 :
1536 307 : Segment* endSegmentSelection = m_startSegment;
1537 554 : while (endSegmentSelection->nextCR(track)
1538 554 : && (endSegmentSelection->nextCR(track)->tick() < m_endSegment->tick())) {
1539 247 : endSegmentSelection = endSegmentSelection->nextCR(track);
1540 : }
1541 :
1542 307 : const MsError err = checkEndForPartialCopy(endSegmentSelection->element(track), endTick);
1543 307 : if (err != MsError::MS_NO_ERROR) {
1544 0 : MScore::setError(err);
1545 0 : return false;
1546 : }
1547 : }
1548 :
1549 : // Check that all selected measures have the same time stretch - allows copy/paste within a local time signature,
1550 : // but don't yet support it between differing local time signatures.
1551 161 : for (Measure* m = m_startSegment->measure(); m && m->tick() < endTick; m = m->nextMeasure()) {
1552 83 : Fraction mTimeStretch = m_score->staff(staffIdx)->timeStretch(m->tick());
1553 83 : if (!timeStretch.isValid()) {
1554 75 : timeStretch = mTimeStretch;
1555 8 : } else if (timeStretch != mTimeStretch) {
1556 0 : return false;
1557 : }
1558 : }
1559 :
1560 : // check if selection starts or ends partway through measure repeat group
1561 78 : const ChordRest* firstCR = firstChordRest();
1562 78 : const ChordRest* lastCR = lastChordRest();
1563 78 : if ((firstCR && firstCR->measure()->isMeasureRepeatGroupWithPrevM(staffIdx))
1564 156 : || (lastCR && lastCR->measure()->isMeasureRepeatGroupWithNextM(staffIdx))) {
1565 0 : return false;
1566 : }
1567 : }
1568 75 : return true;
1569 : }
1570 :
1571 : //---------------------------------------------------------
1572 : // measureBaseRange
1573 : // return false if no measure range selected
1574 : //---------------------------------------------------------
1575 :
1576 0 : bool Selection::measureBaseRange(MeasureBase** mb1, MeasureBase** mb2) const
1577 : {
1578 0 : if (!isRange()) {
1579 0 : return false;
1580 : }
1581 :
1582 0 : if (m_startBox) {
1583 0 : *mb1 = m_startBox;
1584 : } else {
1585 0 : *mb1 = startSegment()->measure();
1586 : }
1587 :
1588 0 : Segment* s2 = endSegment();
1589 0 : if (m_endBox) {
1590 0 : *mb2 = m_endBox;
1591 : } else {
1592 0 : *mb2 = s2 ? s2->measure() : m_score->lastMeasure();
1593 : }
1594 :
1595 0 : if (*mb1 == *mb2) {
1596 0 : return true;
1597 : }
1598 :
1599 : // if selection extends to last segment of a measure,
1600 : // then endSegment() will point to next measure
1601 : // this won't normally happen because end barlines are excluded from range selection
1602 : // but just in case, detect this and back up one measure
1603 0 : if (*mb2 && s2) {
1604 0 : if ((*mb2)->isMeasure() && (*mb2)->tick() == s2->tick()) {
1605 0 : *mb2 = (*mb2)->prevMeasure();
1606 : }
1607 : }
1608 0 : return true;
1609 : }
1610 :
1611 : //---------------------------------------------------------
1612 : // uniqueElements
1613 : // Return list of selected elements.
1614 : // If some elements are linked, only one of the linked
1615 : // elements show up in the list.
1616 : //---------------------------------------------------------
1617 :
1618 1 : const std::list<EngravingItem*> Selection::uniqueElements() const
1619 : {
1620 1 : std::list<EngravingItem*> l;
1621 :
1622 14 : for (EngravingItem* e : elements()) {
1623 13 : bool alreadyThere = false;
1624 91 : for (EngravingItem* ee : l) {
1625 78 : if ((ee->links() && ee->links()->contains(e)) || e == ee) {
1626 0 : alreadyThere = true;
1627 0 : break;
1628 : }
1629 : }
1630 13 : if (!alreadyThere) {
1631 13 : l.push_back(e);
1632 : }
1633 : }
1634 1 : return l;
1635 0 : }
1636 :
1637 : //---------------------------------------------------------
1638 : // uniqueNotes
1639 : // Return list of selected notes.
1640 : // If some notes are linked, only one of the linked
1641 : // elements show up in the list.
1642 : //---------------------------------------------------------
1643 :
1644 21 : std::vector<Note*> Selection::uniqueNotes(track_idx_t track, bool tied) const
1645 : {
1646 21 : std::vector<Note*> l;
1647 :
1648 166 : auto addNote = [&l](Note* note) {
1649 166 : bool alreadyThere = false;
1650 1007 : for (Note* n : l) {
1651 841 : if ((n->links() && n->links()->contains(note)) || n == note) {
1652 0 : alreadyThere = true;
1653 0 : break;
1654 : }
1655 : }
1656 166 : if (!alreadyThere) {
1657 166 : l.push_back(note);
1658 : }
1659 166 : };
1660 :
1661 187 : for (Note* nn : noteList(track)) {
1662 166 : if (!tied) {
1663 34 : addNote(nn);
1664 34 : continue;
1665 : }
1666 :
1667 264 : for (Note* note : nn->tiedNotes()) {
1668 132 : addNote(note);
1669 132 : }
1670 21 : }
1671 21 : return l;
1672 0 : }
1673 :
1674 0 : bool Selection::elementsSelected(const ElementTypeSet& types) const
1675 : {
1676 0 : for (const EngravingItem* element : m_el) {
1677 0 : if (muse::contains(types, element->type())) {
1678 0 : return true;
1679 : }
1680 : }
1681 :
1682 0 : return false;
1683 : }
1684 :
1685 : //---------------------------------------------------------
1686 : // extendRangeSelection
1687 : // Extends the range selection to contain the given
1688 : // chord rest.
1689 : //---------------------------------------------------------
1690 :
1691 0 : void Selection::extendRangeSelection(ChordRest* cr)
1692 : {
1693 0 : extendRangeSelection(cr->segment(),
1694 : cr->nextSegmentAfterCR(SegmentType::ChordRest
1695 : | SegmentType::EndBarLine
1696 : | SegmentType::Clef),
1697 : cr->staffIdx(),
1698 0 : cr->tick(),
1699 0 : cr->tick());
1700 0 : }
1701 :
1702 : //---------------------------------------------------------
1703 : // extendRangeSelection
1704 : // Extends the range selection to contain the given
1705 : // segment. SegAfter should represent the segment
1706 : // that is after seg. Tick and etick represent
1707 : // the start and end tick of an element. Useful when
1708 : // extending by a chord rest.
1709 : //---------------------------------------------------------
1710 :
1711 203 : void Selection::extendRangeSelection(Segment* seg, Segment* segAfter, staff_idx_t staffIdx, const Fraction& tick,
1712 : const Fraction& etick, Box* box)
1713 : {
1714 203 : bool activeSegmentIsStart = false;
1715 203 : staff_idx_t activeStaff = m_activeTrack / VOICES;
1716 :
1717 : // TODO: Quite a lot of nesting - should clean this up...
1718 203 : if (!box) {
1719 203 : if (staffIdx < m_staffStart) {
1720 0 : m_staffStart = staffIdx;
1721 203 : } else if (staffIdx >= m_staffEnd) {
1722 25 : m_staffEnd = staffIdx + 1;
1723 178 : } else if (m_staffEnd - m_staffStart > 1) { // at least 2 staff selected
1724 0 : if (staffIdx == m_staffStart + 1 && activeStaff == m_staffStart) { // going down
1725 0 : m_staffStart = staffIdx;
1726 0 : } else if (staffIdx == m_staffEnd - 2 && activeStaff == m_staffEnd - 1) { // going up
1727 0 : m_staffEnd = staffIdx + 1;
1728 : }
1729 : }
1730 : }
1731 :
1732 : // start/end tick may not have changed, but a Box at the same tick may still lie outside the range...
1733 203 : bool extendsBackwards = tick < tickStart();
1734 203 : bool extendsForwards = etick > tickEnd();
1735 203 : if (box) {
1736 0 : if (box->tick() == tickStart()) {
1737 0 : extendsBackwards = !m_startBox || box->isBefore(m_startBox);
1738 : }
1739 0 : if (box->tick() == tickEnd()) {
1740 0 : extendsForwards = !m_endBox || m_endBox->isBefore(box);
1741 : }
1742 : }
1743 :
1744 203 : if (extendsBackwards) {
1745 : // Range extends backwards...
1746 0 : m_startSegment = seg;
1747 0 : m_startBox = box;
1748 0 : activeSegmentIsStart = true;
1749 203 : } else if (extendsForwards) {
1750 : // Range extends forwards...
1751 172 : m_endSegment = segAfter;
1752 172 : m_endBox = box;
1753 : } else {
1754 31 : if (m_activeSegment == m_startSegment) {
1755 0 : m_startSegment = seg;
1756 0 : m_startBox = box;
1757 0 : activeSegmentIsStart = true;
1758 : } else {
1759 31 : m_endSegment = segAfter;
1760 31 : m_endBox = box;
1761 : }
1762 : }
1763 203 : validateBoxesAndSegments();
1764 :
1765 203 : m_activeSegment = activeSegmentIsStart ? m_startSegment : m_endSegment;
1766 203 : m_score->setSelectionChanged(true);
1767 203 : assert(!(m_endSegment && !m_startSegment));
1768 203 : }
1769 :
1770 36134 : SelectionFilter Selection::selectionFilter() const
1771 : {
1772 36134 : return m_score->selectionFilter();
1773 : }
1774 :
1775 2335 : bool Selection::canSelectNoteIdx(size_t noteIdx, size_t totalNotesInChord, bool selectionContainsMultiNoteChords) const
1776 : {
1777 2335 : return selectionFilter().canSelectNoteIdx(noteIdx, totalNotesInChord, selectionContainsMultiNoteChords);
1778 : }
|