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 "arpeggio.h"
24 :
25 : #include "../editing/elementeditdata.h"
26 : #include "../editing/mscoreview.h"
27 : #include "../types/typesconv.h"
28 :
29 : #include "accidental.h"
30 : #include "chord.h"
31 : #include "note.h"
32 : #include "page.h"
33 : #include "part.h"
34 : #include "property.h"
35 : #include "score.h"
36 : #include "segment.h"
37 : #include "staff.h"
38 :
39 : #include "log.h"
40 :
41 : using namespace mu;
42 : using namespace mu::engraving;
43 :
44 144 : Arpeggio::Arpeggio(DummyParentOr<Chord> parent, ElementType type)
45 144 : : EngravingItem(type, parent, ElementFlag::MOVABLE)
46 : {
47 144 : m_arpeggioType = ArpeggioType::NORMAL;
48 144 : m_span = 1;
49 144 : m_userLen1 = 0.0;
50 144 : m_userLen2 = 0.0;
51 144 : m_playArpeggio = true;
52 144 : m_stretch = 1.0;
53 :
54 144 : EngravingObject* p = parent;
55 144 : if (type == ElementType::ARPEGGIO && p->isChord()) {
56 132 : toChord(p)->setSpanArpeggio(this);
57 : }
58 144 : }
59 :
60 306 : Arpeggio::~Arpeggio()
61 : {
62 : // Remove reference to this arpeggio in any chords it may have spanned
63 153 : Chord* _chord = chord();
64 153 : if (!_chord || !_chord->segment()) {
65 0 : return;
66 : }
67 558 : for (track_idx_t _track = track(); _track <= track() + m_span; _track++) {
68 405 : EngravingItem* e = _chord->segment()->element(_track);
69 405 : if (e && e->isChord() && toChord(e)->spanArpeggio() == this) {
70 210 : toChord(e)->setSpanArpeggio(nullptr);
71 : }
72 : }
73 306 : }
74 :
75 1 : const muse::TranslatableString& Arpeggio::arpeggioTypeName() const
76 : {
77 1 : return TConv::userName(m_arpeggioType);
78 : }
79 :
80 233 : void Arpeggio::findAndAttachToChords()
81 : {
82 233 : const track_idx_t strack = track();
83 233 : const Chord* _chord = chord();
84 233 : const Part* part = _chord->part();
85 233 : const track_idx_t btrack = part->trackRange().endTrack;
86 :
87 233 : if (strack + m_span > btrack) {
88 0 : rebaseEndAnchor(AnchorRebaseDirection::UP);
89 : }
90 :
91 233 : const track_idx_t etrack = strack + (m_span - 1);
92 233 : track_idx_t lastTrack = strack;
93 614 : for (track_idx_t track = strack; track <= etrack; track++) {
94 381 : EngravingItem* e = _chord->segment()->element(track);
95 381 : if (e && e->isChord()) {
96 311 : toChord(e)->undoChangeSpanArpeggio(this);
97 311 : lastTrack = track;
98 : }
99 : }
100 :
101 233 : if (lastTrack != etrack) {
102 0 : track_idx_t newSpan = lastTrack - strack + 1;
103 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
104 : }
105 233 : }
106 :
107 0 : void Arpeggio::detachFromChords(track_idx_t strack, track_idx_t etrack)
108 : {
109 0 : Chord* _chord = chord();
110 0 : if (!_chord) {
111 0 : return;
112 : }
113 0 : for (track_idx_t track = strack; track <= etrack; track++) {
114 0 : EngravingItem* e = _chord->segment()->element(track);
115 0 : if (e && e->isChord() && toChord(e)->spanArpeggio() == this) {
116 0 : toChord(e)->undoChangeSpanArpeggio(nullptr);
117 : }
118 : }
119 : }
120 :
121 1 : void Arpeggio::rebaseStartAnchor(AnchorRebaseDirection direction)
122 : {
123 1 : if (direction == AnchorRebaseDirection::UP) {
124 : // Move arpeggio to chord above
125 0 : Staff* s = staff();
126 0 : Part* part = s->part();
127 0 : track_idx_t topTrack = part->trackRange().startTrack;
128 0 : if (track() > topTrack) {
129 : // Loop through voices til we find a chord
130 0 : for (int curTrack = static_cast<int>(track()) - 1; curTrack >= static_cast<int>(topTrack); curTrack--) {
131 0 : EngravingItem* e = chord()->segment()->element(curTrack);
132 0 : if (e && e->isChord()) {
133 0 : track_idx_t newSpan = m_span + track() - curTrack;
134 0 : if (newSpan != 0) {
135 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
136 0 : score()->undoChangeParent(this, e, e->staffIdx());
137 0 : break;
138 : }
139 : }
140 : }
141 : }
142 1 : } else if (direction == AnchorRebaseDirection::DOWN) {
143 : // Move arpeggio to chord below
144 1 : for (track_idx_t curTrack = track() + 1; curTrack <= track() + m_span - 1; curTrack++) {
145 0 : EngravingItem* e = chord()->segment()->element(curTrack);
146 0 : if (e && e->isChord()) {
147 0 : track_idx_t newSpan = m_span + track() - curTrack;
148 0 : if (newSpan != 0) {
149 0 : chord()->undoChangeSpanArpeggio(nullptr);
150 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
151 0 : score()->undoChangeParent(this, e, e->staffIdx());
152 0 : break;
153 : }
154 : }
155 : }
156 : }
157 1 : }
158 :
159 0 : void Arpeggio::rebaseEndAnchor(AnchorRebaseDirection direction)
160 : {
161 0 : if (direction == AnchorRebaseDirection::UP) {
162 : // Move end to chord above
163 0 : for (int curTrack = static_cast<int>(track()) + m_span - 2; curTrack >= static_cast<int>(track()); curTrack--) {
164 0 : EngravingItem* e = chord()->segment()->element(curTrack);
165 0 : if (e && e->isChord()) {
166 0 : track_idx_t newSpan = curTrack - track() + 1;
167 0 : if (newSpan != 0) {
168 0 : EngravingItem* oldEndChord = chord()->segment()->element(endTrack());
169 0 : if (oldEndChord && oldEndChord->isChord()) {
170 0 : toChord(oldEndChord)->undoChangeSpanArpeggio(nullptr);
171 : }
172 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
173 : }
174 0 : break;
175 : }
176 : }
177 0 : } else if (direction == AnchorRebaseDirection::DOWN) {
178 : // Move end to chord below
179 0 : Staff* s = staff();
180 0 : Part* part = s->part();
181 0 : track_idx_t btrack = part->trackRange().endTrack;
182 0 : if (track() + m_span < btrack) {
183 : // Loop through voices til we find a chord
184 0 : for (track_idx_t curTrack = track() + m_span; curTrack < btrack; curTrack++) {
185 0 : EngravingItem* e = chord()->segment()->element(curTrack);
186 0 : if (e && e->isChord()) {
187 0 : track_idx_t newSpan = curTrack - track() + 1;
188 0 : toChord(e)->undoChangeSpanArpeggio(this);
189 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
190 0 : break;
191 : }
192 : }
193 : }
194 : }
195 0 : }
196 :
197 : //---------------------------------------------------------
198 : // gripsPositions
199 : //---------------------------------------------------------
200 :
201 0 : std::vector<PointF> Arpeggio::gripsPositions(const EditData&) const
202 : {
203 0 : const LayoutData* ldata = this->ldata();
204 0 : const PointF pp(pagePos());
205 0 : PointF p1(ldata->bbox().width() / 2, ldata->bbox().top());
206 0 : PointF p2(ldata->bbox().width() / 2, ldata->bbox().bottom());
207 0 : PointF p3(ldata->bbox().center());
208 0 : return { p1 + pp, p2 + pp, p3 + pp };
209 : }
210 :
211 : //---------------------------------------------------------
212 : // dragGrip
213 : //---------------------------------------------------------
214 :
215 0 : void Arpeggio::dragGrip(EditData& ed)
216 : {
217 0 : if (ed.curGrip == Grip::MIDDLE) {
218 0 : setOffset(offset() + ed.delta);
219 0 : triggerLayout();
220 0 : return;
221 : }
222 :
223 0 : PointF pos = PointF(chord()->canvasPos().x(), ed.pos.y());
224 0 : EngravingItem* e = ed.view()->elementNear(pos);
225 :
226 0 : if (ed.curGrip == Grip::START) {
227 0 : m_userLen1 -= ed.delta.y();
228 0 : if (e && e->isNote() && e->part() == part()) {
229 0 : Chord* c = toNote(e)->chord();
230 0 : int newSpan = std::max(1, m_span + int(track()) - int(c->track()));
231 0 : if (track() != c->track()) {
232 : // if new track is greater than old we have chords to unmark
233 0 : if (track() < c->track()) {
234 0 : detachFromChords(track(), c->track() - 1);
235 : }
236 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
237 0 : score()->undoChangeParent(this, c, c->staffIdx());
238 0 : m_userLen1 = 0.0;
239 : }
240 : }
241 0 : } else if (ed.curGrip == Grip::END) {
242 0 : m_userLen2 += ed.delta.y();
243 : // Increase span
244 0 : if (e && e->isNote() && e->part() == part()) {
245 0 : int newSpan = std::max(1, int(e->track()) - int(track()) + 1);
246 0 : if (e->track() != endTrack()) {
247 : // if new endTrack is less than old we have chords to unmark
248 0 : if (e->track() < endTrack()) {
249 0 : detachFromChords(e->track() + 1, endTrack());
250 : }
251 0 : undoChangeProperty(Pid::ARPEGGIO_SPAN, newSpan);
252 0 : m_userLen2 = 0.0;
253 : }
254 : }
255 : } else {
256 0 : UNREACHABLE;
257 : return;
258 : }
259 :
260 0 : triggerLayout();
261 : }
262 :
263 : //---------------------------------------------------------
264 : // dragAnchorLines
265 : //---------------------------------------------------------
266 :
267 0 : std::vector<LineF> Arpeggio::dragAnchorLines() const
268 : {
269 0 : std::vector<LineF> result;
270 :
271 0 : Chord* c = chord();
272 0 : if (c) {
273 0 : result.push_back(LineF(canvasPos(), c->upNote()->canvasPos()));
274 : }
275 0 : return std::vector<LineF>();
276 0 : }
277 :
278 : //---------------------------------------------------------
279 : // gripAnchorLines
280 : //---------------------------------------------------------
281 :
282 0 : std::vector<LineF> Arpeggio::gripAnchorLines(Grip grip) const
283 : {
284 0 : std::vector<LineF> result;
285 :
286 0 : const int gripIndex = static_cast<int>(grip);
287 0 : if (gripIndex >= gripsCount() || grip == Grip::MIDDLE) {
288 0 : return result;
289 : }
290 :
291 0 : Chord* _chord = chord();
292 0 : if (!_chord) {
293 0 : return result;
294 : }
295 :
296 0 : const Page* p = toPage(findAncestor(ElementType::PAGE));
297 0 : const PointF pageOffset = p ? p->pos() : PointF();
298 0 : const PointF gripCanvasPos = gripsPositions().at(gripIndex) + pageOffset;
299 :
300 0 : if (grip == Grip::START) {
301 0 : Note* upNote = _chord->upNote();
302 0 : EngravingItem* e = _chord->segment()->element(track());
303 0 : if (e && e->isChord()) {
304 0 : upNote = toChord(e)->upNote();
305 : }
306 0 : result.push_back(LineF(upNote->canvasPos(), gripCanvasPos));
307 0 : } else if (grip == Grip::END) {
308 0 : Note* downNote = _chord->downNote();
309 0 : EngravingItem* e = _chord->segment()->element(track() + m_span - 1);
310 0 : if (e && e->isChord()) {
311 0 : downNote = toChord(e)->downNote();
312 : }
313 :
314 0 : result.push_back(LineF(downNote->canvasPos(), gripCanvasPos));
315 : }
316 0 : return result;
317 0 : }
318 :
319 : //---------------------------------------------------------
320 : // startEdit
321 : //---------------------------------------------------------
322 :
323 0 : void Arpeggio::startEdit(EditData& ed)
324 : {
325 0 : EngravingItem::startEdit(ed);
326 0 : ElementEditDataPtr eed = ed.getData(this);
327 0 : eed->pushProperty(Pid::ARP_USER_LEN1);
328 0 : eed->pushProperty(Pid::ARP_USER_LEN2);
329 0 : }
330 :
331 0 : void Arpeggio::startDragGrip(EditData& ed)
332 : {
333 0 : EngravingItem::startDragGrip(ed);
334 0 : ElementEditDataPtr eed = ed.getData(this);
335 0 : if (!eed) {
336 0 : return;
337 : }
338 0 : eed->pushProperty(Pid::ARP_USER_LEN1);
339 0 : eed->pushProperty(Pid::ARP_USER_LEN2);
340 0 : }
341 :
342 0 : bool Arpeggio::isEditAllowed(EditData& ed) const
343 : {
344 0 : if ((ed.curGrip != Grip::END && ed.curGrip != Grip::START) || !(ed.modifiers & ShiftModifier)) {
345 0 : return false;
346 : }
347 :
348 0 : return ed.key == Key_Down || ed.key == Key_Up;
349 : }
350 :
351 : //---------------------------------------------------------
352 : // edit
353 : //---------------------------------------------------------
354 :
355 0 : bool Arpeggio::edit(EditData& ed)
356 : {
357 0 : if (!isEditAllowed(ed)) {
358 0 : return false;
359 : }
360 :
361 0 : if (ed.curGrip == Grip::START) {
362 0 : if (ed.key == Key_Down) {
363 0 : rebaseStartAnchor(AnchorRebaseDirection::DOWN);
364 0 : } else if (ed.key == Key_Up) {
365 0 : rebaseStartAnchor(AnchorRebaseDirection::UP);
366 : }
367 : }
368 :
369 0 : if (ed.curGrip == Grip::END) {
370 0 : if (ed.key == Key_Down) {
371 0 : rebaseEndAnchor(AnchorRebaseDirection::DOWN);
372 0 : } else if (ed.key == Key_Up) {
373 0 : if (m_span > 1) {
374 0 : rebaseEndAnchor(AnchorRebaseDirection::UP);
375 : }
376 : }
377 : }
378 :
379 0 : triggerLayout();
380 :
381 0 : return true;
382 : }
383 :
384 : //---------------------------------------------------------
385 : // spatiumChanged
386 : //---------------------------------------------------------
387 :
388 0 : void Arpeggio::spatiumChanged(double oldValue, double newValue)
389 : {
390 0 : m_userLen1 *= (newValue / oldValue);
391 0 : m_userLen2 *= (newValue / oldValue);
392 0 : }
393 :
394 : //---------------------------------------------------------
395 : // acceptDrop
396 : //---------------------------------------------------------
397 :
398 0 : bool Arpeggio::acceptDrop(EditData& data) const
399 : {
400 0 : return data.dropElement->type() == ElementType::ARPEGGIO || data.dropElement->type() == ElementType::CHORD_BRACKET;
401 : }
402 :
403 : //---------------------------------------------------------
404 : // drop
405 : //---------------------------------------------------------
406 :
407 0 : EngravingItem* Arpeggio::drop(Transaction&, EditData& data)
408 : {
409 0 : EngravingItem* e = data.dropElement;
410 0 : switch (e->type()) {
411 0 : case ElementType::ARPEGGIO:
412 : case ElementType::CHORD_BRACKET:
413 : {
414 0 : Arpeggio* a = toArpeggio(e);
415 0 : if (ownershipParent()) {
416 0 : score()->undoRemoveElement(this);
417 : }
418 0 : a->setTrack(track());
419 0 : a->setOwnershipParent(ownershipParent());
420 0 : score()->undoAddElement(a);
421 : }
422 0 : return e;
423 0 : default:
424 0 : delete e;
425 0 : break;
426 : }
427 0 : return 0;
428 : }
429 :
430 : //---------------------------------------------------------
431 : // reset
432 : //---------------------------------------------------------
433 :
434 0 : void Arpeggio::reset()
435 : {
436 0 : undoChangeProperty(Pid::ARP_USER_LEN1, 0.0);
437 0 : undoChangeProperty(Pid::ARP_USER_LEN2, 0.0);
438 0 : EngravingItem::reset();
439 0 : }
440 :
441 431 : bool Arpeggio::crossStaff() const
442 : {
443 431 : return (track() + span() - 1) / VOICES != vStaffIdx();
444 : }
445 :
446 2390 : staff_idx_t Arpeggio::vStaffIdx() const
447 : {
448 2390 : return chord() ? chord()->vStaffIdx() : EngravingItem::vStaffIdx();
449 : }
450 :
451 : //---------------------------------------------------------
452 : // getProperty
453 : //---------------------------------------------------------
454 :
455 1369 : engraving::PropertyValue Arpeggio::getProperty(Pid propertyId) const
456 : {
457 1369 : switch (propertyId) {
458 114 : case Pid::ARPEGGIO_TYPE:
459 114 : return int(m_arpeggioType);
460 114 : case Pid::TIME_STRETCH:
461 114 : return Stretch();
462 0 : case Pid::ARP_USER_LEN1:
463 0 : return userLen1();
464 0 : case Pid::ARP_USER_LEN2:
465 0 : return userLen2();
466 114 : case Pid::PLAY:
467 114 : return m_playArpeggio;
468 0 : case Pid::ARPEGGIO_SPAN:
469 0 : return m_span;
470 1027 : default:
471 1027 : break;
472 : }
473 1027 : return EngravingItem::getProperty(propertyId);
474 : }
475 :
476 : //---------------------------------------------------------
477 : // setProperty
478 : //---------------------------------------------------------
479 :
480 1 : bool Arpeggio::setProperty(Pid propertyId, const engraving::PropertyValue& val)
481 : {
482 1 : switch (propertyId) {
483 0 : case Pid::ARPEGGIO_TYPE:
484 0 : setArpeggioType(ArpeggioType(val.toInt()));
485 0 : break;
486 0 : case Pid::TIME_STRETCH:
487 0 : setStretch(val.toDouble());
488 0 : break;
489 0 : case Pid::ARP_USER_LEN1:
490 0 : setUserLen1(val.toDouble());
491 0 : break;
492 0 : case Pid::ARP_USER_LEN2:
493 0 : setUserLen2(val.toDouble());
494 0 : break;
495 0 : case Pid::PLAY:
496 0 : setPlayArpeggio(val.toBool());
497 0 : break;
498 0 : case Pid::ARPEGGIO_SPAN:
499 0 : setSpan(val.toInt());
500 0 : break;
501 1 : default:
502 1 : if (!EngravingItem::setProperty(propertyId, val)) {
503 0 : return false;
504 : }
505 1 : break;
506 : }
507 1 : triggerLayout();
508 1 : return true;
509 : }
510 :
511 : //---------------------------------------------------------
512 : // propertyDefault
513 : //---------------------------------------------------------
514 :
515 1368 : engraving::PropertyValue Arpeggio::propertyDefault(Pid propertyId) const
516 : {
517 1368 : switch (propertyId) {
518 0 : case Pid::ARP_USER_LEN1:
519 0 : return 0.0;
520 0 : case Pid::ARP_USER_LEN2:
521 0 : return 0.0;
522 114 : case Pid::TIME_STRETCH:
523 114 : return 1.0;
524 114 : case Pid::PLAY:
525 114 : return true;
526 0 : case Pid::ARPEGGIO_SPAN:
527 0 : return 1;
528 1140 : default:
529 1140 : break;
530 : }
531 1140 : return EngravingItem::propertyDefault(propertyId);
532 : }
533 :
534 : //---------------------------------------------------------
535 : // subtypeUserName
536 : //---------------------------------------------------------
537 :
538 1 : muse::TranslatableString Arpeggio::subtypeUserName() const
539 : {
540 1 : return arpeggioTypeName();
541 : }
|