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 classes Clef (partial) and ClefList (complete).
26 : */
27 :
28 : #include "clef.h"
29 :
30 : #include "translation.h"
31 :
32 : #include "../editing/editclef.h"
33 : #include "../editing/editproperty.h"
34 : #include "../editing/transaction/transaction.h"
35 : #include "../types/typesconv.h"
36 :
37 : #include "ambitus.h"
38 : #include "factory.h"
39 : #include "measure.h"
40 : #include "score.h"
41 : #include "segment.h"
42 : #include "staff.h"
43 :
44 : using namespace mu;
45 : using namespace mu::engraving;
46 :
47 : namespace mu::engraving {
48 : // table must be in sync with enum ClefType in types.h
49 : const ClefInfo ClefInfo::clefTable[] = {
50 : // line pOff|-lines for sharps---||---lines for flats-- | symbol | valid in staff group
51 : { ClefType::G, 2, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef, StaffGroup::STANDARD },
52 : { ClefType::G15_MB, 2, 31, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef15mb, StaffGroup::STANDARD },
53 : { ClefType::G8_VB, 2, 38, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef8vb, StaffGroup::STANDARD },
54 : { ClefType::G8_VA, 2, 52, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef8va, StaffGroup::STANDARD },
55 : { ClefType::G15_MA, 2, 59, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef15ma, StaffGroup::STANDARD },
56 : { ClefType::G8_VB_O, 2, 38, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef8vbOld, StaffGroup::STANDARD },
57 : { ClefType::G8_VB_P, 2, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef8vbParens, StaffGroup::STANDARD },
58 : { ClefType::G_1, 1, 47, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::gClef, StaffGroup::STANDARD },
59 :
60 : { ClefType::C1, 1, 43, { 5, 1, 4, 0, 3, -1, 2, 2, 6, 3, 7, 4, 8, 5 }, SymId::cClef, StaffGroup::STANDARD },
61 : { ClefType::C2, 2, 41, { 3, 6, 2, 5, 1, 4, 0, 0, 4, 1, 5, 2, 6, 3 }, SymId::cClef, StaffGroup::STANDARD },
62 : { ClefType::C3, 3, 39, { 1, 4, 0, 3, 6, 2, 5, 5, 2, 6, 3, 7, 4, 8 }, SymId::cClef, StaffGroup::STANDARD },
63 : { ClefType::C4, 4, 37, { 6, 2, 5, 1, 4, 0, 3, 3, 0, 4, 1, 5, 2, 6 }, SymId::cClef, StaffGroup::STANDARD },
64 : { ClefType::C5, 5, 35, { 4, 0, 3, -1, 2, 5, 1, 1, 5, 2, 6, 3, 7, 4 }, SymId::cClef, StaffGroup::STANDARD },
65 : { ClefType::C_19C, 2, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::cClefSquare, StaffGroup::STANDARD },
66 : { ClefType::C1_F18C, 1, 43, { 5, 1, 4, 0, 3, -1, 2, 2, 6, 3, 7, 4, 8, 5 }, SymId::cClefFrench, StaffGroup::STANDARD },
67 : { ClefType::C3_F18C, 3, 39, { 1, 4, 0, 3, 6, 2, 5, 5, 2, 6, 3, 7, 4, 8 }, SymId::cClefFrench, StaffGroup::STANDARD },
68 : { ClefType::C4_F18C, 4, 37, { 6, 2, 5, 1, 4, 0, 3, 3, 0, 4, 1, 5, 2, 6 }, SymId::cClefFrench, StaffGroup::STANDARD },
69 : { ClefType::C1_F20C, 1, 43, { 5, 1, 4, 0, 3, -1, 2, 2, 6, 3, 7, 4, 8, 5 }, SymId::cClefFrench20C, StaffGroup::STANDARD },
70 : { ClefType::C3_F20C, 3, 39, { 1, 4, 0, 3, 6, 2, 5, 5, 2, 6, 3, 7, 4, 8 }, SymId::cClefFrench20C, StaffGroup::STANDARD },
71 : { ClefType::C4_F20C, 4, 37, { 6, 2, 5, 1, 4, 0, 3, 3, 0, 4, 1, 5, 2, 6 }, SymId::cClefFrench20C, StaffGroup::STANDARD },
72 :
73 : { ClefType::F, 4, 33, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef, StaffGroup::STANDARD },
74 : { ClefType::F15_MB, 4, 19, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef15mb, StaffGroup::STANDARD },
75 : { ClefType::F8_VB, 4, 26, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef8vb, StaffGroup::STANDARD },
76 : { ClefType::F_8VA, 4, 40, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef8va, StaffGroup::STANDARD },
77 : { ClefType::F_15MA, 4, 47, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef15ma, StaffGroup::STANDARD },
78 : { ClefType::F_B, 3, 35, { 4, 0, 3, -1, 2, 5, 1, 1, 5, 2, 6, 3, 7, 4 }, SymId::fClef, StaffGroup::STANDARD },
79 : { ClefType::F_C, 5, 31, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::fClef, StaffGroup::STANDARD },
80 : { ClefType::F_F18C, 4, 33, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClefFrench, StaffGroup::STANDARD },
81 : { ClefType::F_19C, 4, 33, { 2, 5, 1, 4, 7, 3, 6, 6, 3, 7, 4, 8, 5, 9 }, SymId::fClef19thCentury, StaffGroup::STANDARD },
82 :
83 : { ClefType::PERC, 2, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::unpitchedPercussionClef1, StaffGroup::PERCUSSION },
84 : { ClefType::PERC2, 2, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::unpitchedPercussionClef2, StaffGroup::PERCUSSION },
85 :
86 : { ClefType::TAB, 5, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::sixStringTabClef, StaffGroup::TAB },
87 : { ClefType::TAB4, 5, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::fourStringTabClef, StaffGroup::TAB },
88 : { ClefType::TAB_SERIF, 5, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::sixStringTabClefSerif, StaffGroup::TAB },
89 : { ClefType::TAB4_SERIF, 5, 45, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::fourStringTabClefSerif, StaffGroup::TAB },
90 :
91 : { ClefType::C4_8VB, 4, 30, { 6, 2, 5, 1, 4, 0, 3, 3, 0, 4, 1, 5, 2, 6 }, SymId::cClef8vb, StaffGroup::STANDARD },
92 : { ClefType::G8_VB_C, 2, 38, { 0, 3, -1, 2, 5, 1, 4, 4, 1, 5, 2, 6, 3, 7 }, SymId::gClef8vbCClef, StaffGroup::STANDARD },
93 : };
94 :
95 : //---------------------------------------------------------
96 : // Clef
97 : //---------------------------------------------------------
98 :
99 6618 : Clef::Clef(DummyParentOr<Segment> parent)
100 6618 : : EngravingItem(ElementType::CLEF, parent, ElementFlag::ON_STAFF)
101 : {
102 6618 : m_clefToBarlinePosition = ClefToBarlinePosition::AUTO;
103 6618 : }
104 :
105 : //---------------------------------------------------------
106 : // mag
107 : //---------------------------------------------------------
108 :
109 208165 : double Clef::mag() const
110 : {
111 208165 : double mag = staff() ? staff()->staffMag(this) : 1.0;
112 208165 : if (m_isSmall) {
113 32863 : mag *= style().styleD(Sid::smallClefMag);
114 : }
115 208165 : return mag;
116 : }
117 :
118 : //---------------------------------------------------------
119 : // acceptDrop
120 : //---------------------------------------------------------
121 :
122 0 : bool Clef::acceptDrop(EditData& data) const
123 : {
124 0 : return data.dropElement->isClef() || (/*!generated() &&*/ data.dropElement->isAmbitus());
125 : }
126 :
127 : //---------------------------------------------------------
128 : // drop
129 : //---------------------------------------------------------
130 :
131 0 : EngravingItem* Clef::drop(Transaction& tx, EditData& data)
132 : {
133 0 : EngravingItem* e = data.dropElement;
134 0 : Clef* c = 0;
135 0 : if (e->isClef()) {
136 0 : Clef* clef = toClef(e);
137 0 : ClefType stype = clef->clefType();
138 0 : if (clefType() != stype) {
139 0 : EditClef::undoChangeClef(tx, score(), staff(), this, stype);
140 0 : c = this;
141 : }
142 0 : } else if (e->isAmbitus()) {
143 : /*if (!generated())*/
144 : {
145 0 : Measure* meas = measure();
146 0 : Segment* segm = meas->getSegment(SegmentType::Ambitus, meas->tick());
147 0 : if (segm->element(track())) {
148 0 : score()->undoRemoveElement(segm->element(track()));
149 : }
150 0 : Ambitus* r = Factory::createAmbitus(segm);
151 0 : r->setTrack(track());
152 0 : score()->undoAddElement(r);
153 : }
154 : }
155 0 : delete e;
156 0 : return c;
157 : }
158 :
159 : //---------------------------------------------------------
160 : // setSmall
161 : //---------------------------------------------------------
162 :
163 18218 : void Clef::setSmall(bool val)
164 : {
165 18218 : m_isSmall = val;
166 18218 : }
167 :
168 : //---------------------------------------------------------
169 : // setClefType
170 : //---------------------------------------------------------
171 :
172 693 : void Clef::setClefType(ClefType i)
173 : {
174 693 : if (concertPitch()) {
175 14 : m_clefTypes.concertClef = i;
176 14 : if (m_clefTypes.transposingClef == ClefType::INVALID) {
177 14 : m_clefTypes.transposingClef = i;
178 : }
179 : } else {
180 679 : m_clefTypes.transposingClef = i;
181 679 : if (m_clefTypes.concertClef == ClefType::INVALID) {
182 677 : m_clefTypes.concertClef = i;
183 : }
184 : }
185 693 : }
186 :
187 : //---------------------------------------------------------
188 : // setConcertClef
189 : //---------------------------------------------------------
190 :
191 752 : void Clef::setConcertClef(ClefType val)
192 : {
193 752 : m_clefTypes.concertClef = val;
194 752 : }
195 :
196 : //---------------------------------------------------------
197 : // setTransposingClef
198 : //---------------------------------------------------------
199 :
200 752 : void Clef::setTransposingClef(ClefType val)
201 : {
202 752 : m_clefTypes.transposingClef = val;
203 752 : }
204 :
205 : //---------------------------------------------------------
206 : // clefType
207 : //---------------------------------------------------------
208 :
209 169231 : ClefType Clef::clefType() const
210 : {
211 169231 : if (concertPitch()) {
212 1341 : return m_clefTypes.concertClef;
213 : } else {
214 167890 : return m_clefTypes.transposingClef;
215 : }
216 : }
217 :
218 : //---------------------------------------------------------
219 : // spatiumChanged
220 : //---------------------------------------------------------
221 :
222 102 : void Clef::spatiumChanged(double oldValue, double newValue)
223 : {
224 102 : EngravingItem::spatiumChanged(oldValue, newValue);
225 102 : }
226 :
227 : //---------------------------------------------------------
228 : // undoSetShowCourtesy
229 : //---------------------------------------------------------
230 :
231 0 : void Clef::undoSetShowCourtesy(bool v)
232 : {
233 0 : undoChangeProperty(Pid::SHOW_COURTESY, v);
234 0 : }
235 :
236 : //---------------------------------------------------------
237 : // otherClef
238 : // try to locate the 'other clef' of a courtesy / main pair
239 : //---------------------------------------------------------
240 :
241 574 : Clef* Clef::otherClef()
242 : {
243 : // if not in a clef-segment-measure hierarchy, do nothing
244 574 : if (!ownershipParent() || !ownershipParent()->isSegment()) {
245 0 : return nullptr;
246 : }
247 574 : Segment* segm = toSegment(ownershipParent());
248 574 : if (!segm->ownershipParent() || !segm->ownershipParent()->isMeasure()) {
249 0 : return 0;
250 : }
251 574 : Measure* meas = toMeasure(segm->ownershipParent());
252 574 : Measure* otherMeas = nullptr;
253 574 : Segment* otherSegm = nullptr;
254 574 : Fraction segmTick = segm->tick();
255 574 : SegmentType type = SegmentType::Clef;
256 574 : if (segmTick == meas->tick() && segm->segmentType() == SegmentType::HeaderClef) { // if clef segm is measure-initial
257 514 : otherMeas = meas->prevMeasure(); // look for a previous measure
258 60 : } else if (segmTick == meas->tick() + meas->ticks()) { // if clef segm is measure-final
259 60 : otherMeas = meas->nextMeasure(); // look for a next measure
260 60 : type = SegmentType::HeaderClef;
261 : }
262 574 : if (!otherMeas) {
263 508 : return nullptr;
264 : }
265 : // look for a clef segment in the 'other' measure at the same tick of this clef segment
266 66 : otherSegm = otherMeas->findSegment(type, segmTick);
267 66 : if (!otherSegm) {
268 11 : return nullptr;
269 : }
270 : // if any 'other' segment found, look for a clef in the same track as this
271 55 : return toClef(otherSegm->element(track()));
272 : }
273 :
274 : //---------------------------------------------------------
275 : // getProperty
276 : //---------------------------------------------------------
277 :
278 14249 : PropertyValue Clef::getProperty(Pid propertyId) const
279 : {
280 14249 : switch (propertyId) {
281 1093 : case Pid::CLEF_TYPE_CONCERT: return m_clefTypes.concertClef;
282 1093 : case Pid::CLEF_TYPE_TRANSPOSING: return m_clefTypes.transposingClef;
283 7 : case Pid::SHOW_COURTESY: return showCourtesy();
284 0 : case Pid::SMALL: return isSmall();
285 1093 : case Pid::CLEF_TO_BARLINE_POS: return m_clefToBarlinePosition;
286 1093 : case Pid::IS_COURTESY: return m_isCourtesy;
287 9870 : default:
288 9870 : return EngravingItem::getProperty(propertyId);
289 : }
290 : }
291 :
292 : //---------------------------------------------------------
293 : // setProperty
294 : //---------------------------------------------------------
295 :
296 12 : bool Clef::setProperty(Pid propertyId, const PropertyValue& v)
297 : {
298 12 : switch (propertyId) {
299 0 : case Pid::CLEF_TYPE_CONCERT:
300 0 : setConcertClef(v.value<ClefType>());
301 0 : break;
302 0 : case Pid::CLEF_TYPE_TRANSPOSING:
303 0 : setTransposingClef(v.value<ClefType>());
304 0 : break;
305 7 : case Pid::SHOW_COURTESY:
306 7 : m_showCourtesy = v.toBool();
307 7 : if (m_showCourtesy && isHeader() && selected()) {
308 0 : Clef* courtesyClef = otherClef();
309 0 : if (courtesyClef) {
310 0 : score()->deselect(this);
311 0 : score()->select(courtesyClef, SelectType::ADD, staffIdx());
312 : }
313 : }
314 7 : break;
315 0 : case Pid::SMALL:
316 0 : setSmall(v.toBool());
317 0 : break;
318 0 : case Pid::CLEF_TO_BARLINE_POS: {
319 0 : const auto newClefToBlPos = v.value<ClefToBarlinePosition>();
320 :
321 0 : if (newClefToBlPos != m_clefToBarlinePosition && !isHeader()) {
322 0 : changeClefToBarlinePos(newClefToBlPos);
323 : }
324 0 : break;
325 : }
326 0 : case Pid::IS_COURTESY:
327 0 : m_isCourtesy = v.toBool();
328 0 : break;
329 5 : default:
330 5 : return EngravingItem::setProperty(propertyId, v);
331 : }
332 7 : triggerLayout();
333 7 : return true;
334 : }
335 :
336 0 : void Clef::changeClefToBarlinePos(ClefToBarlinePosition newPos)
337 : {
338 0 : m_clefToBarlinePosition = newPos;
339 :
340 0 : if (!ownershipParent()) {
341 0 : return;
342 : }
343 :
344 0 : Segment* seg = segment();
345 :
346 0 : staff_idx_t nStaves = score()->nstaves();
347 0 : for (staff_idx_t staffIndex = 0; staffIndex < nStaves; ++staffIndex) {
348 0 : Clef* clef = toClef(seg->element(staffIndex * VOICES));
349 0 : if (clef) {
350 0 : clef->m_clefToBarlinePosition = newPos;
351 : }
352 : }
353 : }
354 :
355 9 : void Clef::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
356 : {
357 9 : if (id == Pid::SHOW_COURTESY) {
358 7 : if (v.toBool() != m_showCourtesy) {
359 4 : score()->undo(new ChangeProperty(this, id, v, ps));
360 4 : Clef* pairedClef = otherClef();
361 4 : if (pairedClef) {
362 3 : score()->undo(new ChangeProperty(pairedClef, id, v, ps));
363 : }
364 4 : if (!segment()->isHeaderClefType()) {
365 1 : setGenerated(false);
366 : }
367 : }
368 : } else {
369 2 : EngravingObject::undoChangeProperty(id, v, ps);
370 : }
371 9 : }
372 :
373 25702 : bool Clef::isHeader() const
374 : {
375 25702 : const Segment* seg = segment();
376 25702 : return seg && seg->isHeaderClefType();
377 : }
378 :
379 195148 : bool Clef::isMidMeasureClef() const
380 : {
381 195148 : return segment() && segment()->rtick().isNotZero();
382 : }
383 :
384 2 : void Clef::manageExclusionFromParts(bool exclude)
385 : {
386 2 : if (exclude) {
387 1 : EngravingItem::manageExclusionFromParts(exclude);
388 : } else {
389 1 : Measure* measure = findMeasure();
390 1 : EngravingItem* nextEl = measure && rtick() == measure->ticks() ? measure->next() : nullptr;
391 1 : if (!nextEl) {
392 1 : nextEl = nextElement();
393 : }
394 1 : Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
395 1 : EditClef::undoChangeClef(tx, score(), staff(), nextEl, clefType(), false, this);
396 : }
397 2 : }
398 :
399 : //---------------------------------------------------------
400 : // propertyDefault
401 : //---------------------------------------------------------
402 :
403 14209 : PropertyValue Clef::propertyDefault(Pid id) const
404 : {
405 14209 : switch (id) {
406 1093 : case Pid::CLEF_TYPE_CONCERT: return ClefType::INVALID;
407 1093 : case Pid::CLEF_TYPE_TRANSPOSING: return ClefType::INVALID;
408 0 : case Pid::SHOW_COURTESY: return true;
409 0 : case Pid::SMALL: return false;
410 1093 : case Pid::CLEF_TO_BARLINE_POS: return ClefToBarlinePosition::AUTO;
411 1093 : case Pid::IS_COURTESY: return false;
412 9837 : default: return EngravingItem::propertyDefault(id);
413 : }
414 : }
415 :
416 : //---------------------------------------------------------
417 : // nextSegmentElement
418 : //---------------------------------------------------------
419 :
420 1 : EngravingItem* Clef::nextSegmentElement()
421 : {
422 1 : return segment()->firstInNextSegments(staffIdx());
423 : }
424 :
425 : //---------------------------------------------------------
426 : // prevSegmentElement
427 : //---------------------------------------------------------
428 :
429 0 : EngravingItem* Clef::prevSegmentElement()
430 : {
431 0 : return segment()->lastInPrevSegments(staffIdx());
432 : }
433 :
434 : //---------------------------------------------------------
435 : // accessibleInfo
436 : //---------------------------------------------------------
437 :
438 0 : String Clef::accessibleInfo() const
439 : {
440 0 : ClefType type = clefType();
441 0 : if (type == ClefType::INVALID) {
442 0 : return String();
443 : }
444 0 : return muse::mtrc("engraving", TConv::translatedUserName(clefType()));
445 : }
446 :
447 : //---------------------------------------------------------
448 : // subtypeUserName
449 : //---------------------------------------------------------
450 :
451 1 : TranslatableString Clef::subtypeUserName() const
452 : {
453 1 : return TConv::userName(clefType());
454 : }
455 :
456 : //---------------------------------------------------------
457 : // clear
458 : //---------------------------------------------------------
459 :
460 57 : void Clef::clear()
461 : {
462 57 : LayoutData* ldata = mutldata();
463 57 : ldata->clearBbox();
464 57 : ldata->symId = SymId::noSym;
465 57 : Clef* pairedClef = otherClef();
466 57 : if (selected() && score()->selection().isList() && !isHeader() && pairedClef) {
467 0 : score()->deselect(this);
468 0 : score()->select(pairedClef, SelectType::ADD, staffIdx());
469 : }
470 57 : }
471 : }
|