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 "editclef.h"
24 :
25 : #include "../dom/chordrest.h"
26 : #include "../dom/clef.h"
27 : #include "../dom/factory.h"
28 : #include "../dom/instrument.h"
29 : #include "../dom/linkedobjects.h"
30 : #include "../dom/measure.h"
31 : #include "../dom/note.h"
32 : #include "../dom/part.h"
33 : #include "../dom/score.h"
34 : #include "../dom/segment.h"
35 : #include "../dom/staff.h"
36 :
37 : #include "log.h"
38 :
39 : using namespace mu::engraving;
40 :
41 : // compute line position of noteheads after clef change
42 2 : static void updateNoteLines(Segment* segment, track_idx_t track)
43 : {
44 2 : Staff* staff = segment->score()->staff(track / VOICES);
45 2 : if (staff->isDrumStaff(segment->tick()) || staff->isTabStaff(segment->tick())) {
46 0 : return;
47 : }
48 20 : for (Segment* s = segment->next1(); s; s = s->next1()) {
49 19 : if ((s->segmentType() & (SegmentType::Clef | SegmentType::HeaderClef)) && s->element(track) && !s->element(track)->generated()) {
50 1 : break;
51 : }
52 18 : if (!s->isChordRestType()) {
53 11 : continue;
54 : }
55 35 : for (track_idx_t t = track; t < track + VOICES; ++t) {
56 28 : EngravingItem* e = s->element(t);
57 28 : if (e && e->isChord()) {
58 3 : Chord* chord = toChord(e);
59 6 : for (Note* n : chord->notes()) {
60 3 : n->updateLine();
61 : }
62 3 : chord->sortNotes();
63 3 : for (Chord* gc : chord->graceNotes()) {
64 0 : for (Note* gn : gc->notes()) {
65 0 : gn->updateLine();
66 : }
67 0 : gc->sortNotes();
68 : }
69 : }
70 : }
71 : }
72 : }
73 :
74 : //---------------------------------------------------------
75 : // ChangeClefType
76 : //---------------------------------------------------------
77 :
78 2 : ChangeClefType::ChangeClefType(Clef* c, ClefType cl, ClefType tc)
79 : {
80 2 : clef = c;
81 2 : concertClef = cl;
82 2 : transposingClef = tc;
83 2 : }
84 :
85 2 : void ChangeClefType::flip()
86 : {
87 2 : ClefType ocl = clef->concertClef();
88 2 : ClefType otc = clef->transposingClef();
89 :
90 2 : clef->setConcertClef(concertClef);
91 2 : clef->setTransposingClef(transposingClef);
92 :
93 2 : clef->staff()->setClef(clef);
94 2 : Segment* segment = clef->segment();
95 2 : updateNoteLines(segment, clef->track());
96 2 : clef->triggerLayout();
97 2 : clef->score()->setLayout(clef->staff()->nextClefTick(segment->tick()), clef->staffIdx());
98 :
99 2 : concertClef = ocl;
100 2 : transposingClef = otc;
101 2 : }
102 :
103 : //---------------------------------------------------------
104 : // EditClef
105 : //---------------------------------------------------------
106 :
107 0 : bool EditClef::canInsertClef(const Score* score, ClefType type)
108 : {
109 0 : if (type == ClefType::INVALID) {
110 0 : return false;
111 : }
112 :
113 0 : const Staff* staff = score->staff(score->inputState().track() / VOICES);
114 0 : const ChordRest* cr = score->inputState().cr();
115 :
116 0 : return staff && cr;
117 : }
118 :
119 0 : void EditClef::insertClef(Transaction& tx, Score* score, ClefType type)
120 : {
121 0 : undoChangeClef(tx, score, score->staff(score->inputState().track() / VOICES), score->inputState().cr(), type);
122 0 : }
123 :
124 : //---------------------------------------------------------
125 : // insertClef
126 : // insert clef before cr
127 : //---------------------------------------------------------
128 :
129 0 : void EditClef::insertClef(Transaction& tx, Score* score, Clef* clef, ChordRest* cr)
130 : {
131 0 : undoChangeClef(tx, score, cr->staff(), cr, clef->clefType());
132 0 : delete clef;
133 0 : }
134 :
135 : //---------------------------------------------------------
136 : // undoChangeClef
137 : // change clef if e is a clef
138 : // else
139 : // create a clef before element e
140 : //---------------------------------------------------------
141 :
142 14 : void EditClef::undoChangeClef(Transaction&, Score* score, Staff* ostaff, EngravingItem* e, ClefType ct, bool forInstrumentChange,
143 : Clef* clefToRelink)
144 : {
145 14 : IF_ASSERT_FAILED(ostaff && e) {
146 0 : return;
147 : }
148 :
149 14 : bool moveClef = false;
150 14 : SegmentType st = SegmentType::Clef;
151 14 : if (e->isMeasure()) {
152 13 : if (toMeasure(e)->prevMeasure()) {
153 12 : moveClef = true;
154 : } else {
155 1 : st = SegmentType::HeaderClef;
156 : }
157 1 : } else if (e->isClef()) {
158 0 : Clef* clef = toClef(e);
159 0 : if (clef->segment()->isHeaderClefType()) {
160 0 : if (clef->measure()->prevMeasure()) {
161 0 : moveClef = true;
162 : } else {
163 0 : st = SegmentType::HeaderClef;
164 : }
165 0 : } else if (clef->rtick() == clef->measure()->ticks()) {
166 0 : moveClef = true;
167 : }
168 1 : } else if (e->rtick() == Fraction(0, 1)) {
169 0 : Measure* curMeasure = e->findMeasure();
170 0 : Measure* prevMeasure = curMeasure ? curMeasure->prevMeasure() : nullptr;
171 0 : if (prevMeasure && !prevMeasure->sectionBreak()) {
172 0 : moveClef = true;
173 : }
174 : }
175 :
176 14 : bool concertPitch = score->style().styleB(Sid::concertPitch);
177 14 : Clef* gclef = 0;
178 14 : Fraction tick = e->tick();
179 14 : Fraction rtick = e->rtick();
180 14 : bool isSmall = (st == SegmentType::Clef);
181 29 : for (Staff* staff : ostaff->staffList()) {
182 15 : if (clefToRelink && ostaff == staff) {
183 1 : continue;
184 : }
185 :
186 14 : Score* staffScore = staff->score();
187 14 : Measure* measure = staffScore->tick2measure(tick);
188 :
189 14 : if (!measure) {
190 0 : LOGW("measure for tick %d not found!", tick.ticks());
191 0 : continue;
192 : }
193 :
194 : Segment* destSeg;
195 14 : Fraction rt;
196 14 : if (moveClef) { // if at start of measure and there is a previous measure
197 12 : measure = measure->prevMeasure();
198 12 : rt = measure->ticks();
199 : } else {
200 2 : rt = rtick;
201 : }
202 14 : destSeg = measure->undoGetSegmentR(st, rt);
203 :
204 14 : staff_idx_t staffIdx = staff->idx();
205 14 : track_idx_t track = staffIdx * VOICES;
206 14 : Clef* clef = toClef(destSeg->element(track));
207 :
208 14 : StaffType* staffType = staff->staffType(e->tick());
209 14 : StaffGroup staffGroup = staffType->group();
210 14 : if (ClefInfo::staffGroup(ct) != staffGroup && !forInstrumentChange) {
211 0 : continue;
212 : }
213 :
214 14 : if (clef) {
215 : //
216 : // for transposing instruments, differentiate
217 : // clef type for concertPitch
218 : //
219 2 : Instrument* i = staff->part()->instrument(tick);
220 : ClefType cp, tp;
221 2 : if (i->transpose().isZero()) {
222 2 : cp = ct;
223 2 : tp = ct;
224 : } else {
225 0 : if (concertPitch) {
226 0 : cp = ct;
227 0 : tp = clef->transposingClef();
228 : } else {
229 0 : cp = clef->concertClef();
230 0 : tp = ct;
231 : }
232 : }
233 2 : clef->setGenerated(false);
234 2 : staffScore->undo(new ChangeClefType(clef, cp, tp));
235 2 : Clef* oClef = clef->otherClef();
236 2 : if (oClef && !(oClef->generated())) {
237 0 : staffScore->undo(new ChangeClefType(oClef, cp, tp));
238 : }
239 : // change the clef in the mmRest if any
240 2 : if (measure->hasMMRest()) {
241 0 : Measure* mmMeasure = measure->mmRest();
242 0 : Segment* mmDestSeg = mmMeasure->findSegment(SegmentType::Clef, tick);
243 0 : if (mmDestSeg) {
244 0 : Clef* mmClef = toClef(mmDestSeg->element(clef->track()));
245 0 : if (mmClef) {
246 0 : staffScore->undo(new ChangeClefType(mmClef, cp, tp));
247 : }
248 : }
249 : }
250 : } else {
251 12 : if (gclef) {
252 0 : clef = toClef(gclef->linkedClone());
253 0 : clef->setScore(staffScore);
254 0 : clef->setOwnershipParent(destSeg);
255 : } else {
256 12 : clef = Factory::createClef(destSeg);
257 12 : clef->setClefType(ct);
258 12 : gclef = clef;
259 : }
260 12 : clef->setTrack(track);
261 12 : staffScore->doUndoAddElement(clef);
262 : }
263 14 : if (forInstrumentChange) {
264 0 : clef->setForInstrumentChange(true);
265 : }
266 14 : clef->setSmall(isSmall);
267 :
268 14 : if (clefToRelink) {
269 1 : LinkedObjects* links = clef->links();
270 1 : if (!links) {
271 1 : clef->linkTo(clefToRelink);
272 0 : } else if (!clef->isLinked(clefToRelink)) {
273 0 : clefToRelink->setLinks(links);
274 0 : links->push_back(clefToRelink);
275 : }
276 : }
277 14 : }
278 : }
|