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) 2025 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 "editspanner.h"
24 :
25 : #include "../dom/chord.h"
26 : #include "../dom/engravingitem.h"
27 : #include "../dom/measure.h"
28 : #include "../dom/note.h"
29 : #include "../dom/score.h"
30 : #include "../dom/segment.h"
31 : #include "../dom/spanner.h"
32 : #include "../dom/textlinebase.h"
33 : #include "../dom/tie.h"
34 :
35 : using namespace mu::engraving;
36 :
37 : //---------------------------------------------------------
38 : // EditSpanner
39 : //---------------------------------------------------------
40 :
41 0 : void EditSpanner::addSpanner(Transaction&, Score* score, Spanner* spanner, const PointF& pos, bool systemStavesOnly)
42 : {
43 0 : staff_idx_t staffIdx = spanner->staffIdx();
44 : Segment* segment;
45 0 : Measure* measure = score->pos2measure(pos, &staffIdx, /*pitch*/ 0, &segment, /*offset*/ 0);
46 0 : if (systemStavesOnly) {
47 0 : staffIdx = 0;
48 : }
49 : // ignore if we do not have a measure
50 0 : if (!measure) {
51 0 : LOGD("addSpanner: cannot put object here");
52 0 : delete spanner;
53 0 : return;
54 : }
55 :
56 : // all spanners live in voice 0 (except slurs/ties)
57 0 : track_idx_t track = staffIdx == muse::nidx ? muse::nidx : staffIdx * VOICES;
58 :
59 0 : spanner->setTrack(track);
60 0 : spanner->setTrack2(track);
61 :
62 0 : if (spanner->anchor() == Spanner::Anchor::SEGMENT) {
63 0 : spanner->setTick(segment->tick());
64 0 : Fraction tick2 = std::min(segment->measure()->endTick(), score->lastMeasure()->endTick());
65 0 : spanner->setTick2(tick2);
66 : } else { // Anchor::MEASURE, Anchor::CHORD, Anchor::NOTE
67 0 : spanner->setTick(measure->tick());
68 0 : spanner->setTick2(measure->endTick());
69 : }
70 0 : spanner->eraseSpannerSegments();
71 :
72 0 : bool ctrlModifier = isSystemTextLine(spanner) && !systemStavesOnly;
73 0 : score->undoAddElement(spanner, true /*addToLinkedStaves*/, ctrlModifier);
74 : }
75 :
76 0 : void EditSpanner::addSpanner(Transaction&, Score* score, Spanner* spanner, staff_idx_t staffIdx, Segment* startSegment,
77 : Segment* endSegment, bool ctrlModifier)
78 : {
79 0 : track_idx_t track = staffIdx * VOICES;
80 0 : spanner->setTrack(track);
81 0 : spanner->setTrack2(track);
82 0 : for (auto ss : spanner->spannerSegments()) {
83 0 : ss->setTrack(track);
84 : }
85 :
86 0 : bool isMeasureAnchor = spanner->anchor() == Spanner::Anchor::MEASURE;
87 0 : Fraction tick1 = isMeasureAnchor ? startSegment->measure()->tick() : startSegment->tick();
88 0 : spanner->setTick(tick1);
89 :
90 0 : Fraction tick2;
91 0 : if (!endSegment) {
92 0 : tick2 = score->lastSegment()->tick();
93 0 : } else if (endSegment == startSegment) {
94 0 : tick2 = startSegment->measure()->last()->tick();
95 : } else {
96 0 : tick2 = endSegment->tick();
97 : }
98 0 : if (isMeasureAnchor) {
99 0 : Measure* endMeasure = score->tick2measureMM(tick2);
100 0 : if (endMeasure->tick() != tick2) {
101 0 : tick2 = endMeasure->endTick();
102 : }
103 : }
104 :
105 0 : spanner->setTick2(tick2);
106 0 : score->undoAddElement(spanner, true, ctrlModifier);
107 0 : }
108 :
109 : //---------------------------------------------------------
110 : // ChangeSpannerElements
111 : //---------------------------------------------------------
112 :
113 102 : void ChangeSpannerElements::flip()
114 : {
115 102 : EngravingItem* oldStartElement = spanner->startElement();
116 102 : EngravingItem* oldEndElement = spanner->endElement();
117 102 : bool isPartialSpanner = spanner->isPartialTie() || spanner->isLaissezVib();
118 102 : if (spanner->anchor() == Spanner::Anchor::NOTE) {
119 : // be sure new spanner elements are of the right type
120 0 : if (!isPartialSpanner && (!startElement || !startElement->isNote() || !endElement || !endElement->isNote())) {
121 0 : return;
122 : }
123 0 : Note* oldStartNote = toNote(oldStartElement);
124 0 : Note* oldEndNote = toNote(oldEndElement);
125 0 : Note* newStartNote = toNote(startElement);
126 0 : Note* newEndNote = toNote(endElement);
127 : // update spanner's start and end notes
128 0 : if ((newStartNote && newEndNote) || (isPartialSpanner && (newStartNote || newEndNote))) {
129 0 : spanner->setNoteSpan(newStartNote, newEndNote);
130 0 : if (spanner->isTie()) {
131 0 : Tie* tie = toTie(spanner);
132 0 : if (oldStartNote && newStartNote) {
133 0 : oldStartNote->setTieFor(nullptr);
134 0 : newStartNote->setTieFor(tie);
135 : }
136 0 : if (oldEndNote && newEndNote) {
137 0 : oldEndNote->setTieBack(nullptr);
138 0 : newEndNote->setTieBack(tie);
139 : }
140 : } else {
141 0 : oldStartNote->removeSpannerFor(spanner);
142 0 : oldEndNote->removeSpannerBack(spanner);
143 0 : newStartNote->addSpannerFor(spanner);
144 0 : newEndNote->addSpannerBack(spanner);
145 0 : if (spanner->isGlissando()) {
146 0 : oldEndNote->chord()->updateEndsNoteAnchoredLine();
147 : }
148 : }
149 : }
150 : } else {
151 102 : spanner->setStartElement(startElement);
152 102 : spanner->setEndElement(endElement);
153 : }
154 102 : startElement = oldStartElement;
155 102 : endElement = oldEndElement;
156 102 : spanner->triggerLayout();
157 : }
158 :
159 : //---------------------------------------------------------
160 : // ChangeStartEndSpanner
161 : //---------------------------------------------------------
162 :
163 16 : void ChangeStartEndSpanner::flip()
164 : {
165 16 : EngravingItem* s = spanner->startElement();
166 16 : EngravingItem* e = spanner->endElement();
167 16 : spanner->setStartElement(start);
168 16 : spanner->setEndElement(end);
169 16 : start = s;
170 16 : end = e;
171 16 : }
|