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 "timesig.h"
24 :
25 : #include <functional>
26 :
27 : #include "style/style.h"
28 :
29 : #include "../editing/edittimesig.h"
30 : #include "../editing/transaction/transaction.h"
31 :
32 : #include "measure.h"
33 : #include "score.h"
34 : #include "segment.h"
35 : #include "staff.h"
36 :
37 : #include "log.h"
38 :
39 : using namespace mu;
40 : using namespace mu::engraving;
41 :
42 : namespace mu::engraving {
43 : static const ElementStyle tsStyle {
44 : { Sid::timeSigNormalScale, Pid::SCALE },
45 : };
46 :
47 : //---------------------------------------------------------
48 : // TimeSig
49 : // Constructs an invalid time signature element.
50 : // After construction first call setTrack() then
51 : // call setSig().
52 : // Layout() is static and called in setSig().
53 : //---------------------------------------------------------
54 :
55 3599 : TimeSig::TimeSig(DummyParentOr<Segment> parent)
56 3599 : : EngravingItem(ElementType::TIMESIG, parent, ElementFlag::ON_STAFF | ElementFlag::MOVABLE | ElementFlag::PLACE_ABOVE)
57 : {
58 3599 : initElementStyle(&tsStyle);
59 :
60 3599 : m_showCourtesySig = true;
61 3599 : m_stretch.set(1, 1);
62 3599 : m_sig.set(0, 1); // initialize to invalid
63 3599 : m_timeSigType = TimeSigType::NORMAL;
64 3599 : m_largeParentheses = false;
65 3599 : setMinDistance(0.5_sp); // TODO: style
66 3599 : }
67 :
68 90 : void TimeSig::setOwnershipParent(DummyParentOr<Segment> parent)
69 : {
70 90 : EngravingItem::setOwnershipParent(parent);
71 90 : }
72 :
73 : //---------------------------------------------------------
74 : // mag
75 : //---------------------------------------------------------
76 :
77 207723 : double TimeSig::mag() const
78 : {
79 207723 : return timeSigPlacement() == TimeSigPlacement::NORMAL && staff() ? staff()->staffMag(this) : 1.0;
80 : }
81 :
82 : //---------------------------------------------------------
83 : // setSig
84 : // custom text has to be set after setSig()
85 : //---------------------------------------------------------
86 :
87 3429 : void TimeSig::setSig(const Fraction& f, TimeSigType st)
88 : {
89 3429 : m_sig = f;
90 3429 : m_timeSigType = st;
91 3429 : m_largeParentheses = false;
92 3429 : m_numeratorString.clear();
93 3429 : m_denominatorString.clear();
94 3429 : }
95 :
96 : //---------------------------------------------------------
97 : // acceptDrop
98 : //---------------------------------------------------------
99 :
100 0 : bool TimeSig::acceptDrop(EditData& data) const
101 : {
102 0 : return data.dropElement->isTimeSig();
103 : }
104 :
105 : //---------------------------------------------------------
106 : // drop
107 : //---------------------------------------------------------
108 :
109 0 : EngravingItem* TimeSig::drop(Transaction& tx, EditData& data)
110 : {
111 0 : EngravingItem* e = data.dropElement;
112 0 : if (e->isTimeSig()) {
113 : // change timesig applies to all staves, can't simply set subtype
114 : // for this one only
115 : // ownership of e is transferred to EditTimeSig::addTimeSig
116 :
117 0 : if (tick() != measure()->endTick()) {
118 0 : EditTimeSig::addTimeSig(tx, score(), measure(), staffIdx(), toTimeSig(e), false);
119 0 : return nullptr;
120 : }
121 :
122 : // This is a timesig at the end of a measure.
123 0 : if (*toTimeSig(e) == *this) {
124 0 : delete e;
125 0 : return nullptr;
126 : }
127 :
128 0 : if (!measure()->nextMeasure()) {
129 0 : return nullptr;
130 : }
131 :
132 : // Apply change to next measure
133 0 : EditTimeSig::addTimeSig(tx, score(), measure()->nextMeasure(), staffIdx(), toTimeSig(e), false);
134 0 : return nullptr;
135 : }
136 0 : delete e;
137 0 : return nullptr;
138 : }
139 :
140 : //---------------------------------------------------------
141 : // setNumeratorString
142 : // setSig() has to be called first
143 : //---------------------------------------------------------
144 :
145 1771 : void TimeSig::setNumeratorString(const String& a)
146 : {
147 1771 : if (m_timeSigType == TimeSigType::NORMAL) {
148 1718 : m_numeratorString = a;
149 : }
150 1771 : }
151 :
152 : //---------------------------------------------------------
153 : // setDenominatorString
154 : // setSig() has to be called first
155 : //---------------------------------------------------------
156 :
157 1768 : void TimeSig::setDenominatorString(const String& a)
158 : {
159 1768 : if (m_timeSigType == TimeSigType::NORMAL) {
160 1715 : m_denominatorString = a;
161 : }
162 1768 : }
163 :
164 : //---------------------------------------------------------
165 : // setFrom
166 : //---------------------------------------------------------
167 :
168 492 : void TimeSig::setFrom(const TimeSig* ts)
169 : {
170 492 : m_timeSigType = ts->timeSigType();
171 492 : m_numeratorString = ts->m_numeratorString;
172 492 : m_denominatorString = ts->m_denominatorString;
173 492 : m_sig = ts->m_sig;
174 492 : m_stretch = ts->m_stretch;
175 492 : }
176 :
177 : //---------------------------------------------------------
178 : // ssig
179 : //---------------------------------------------------------
180 :
181 0 : String TimeSig::ssig() const
182 : {
183 0 : return String(u"%1/%2").arg(m_sig.numerator()).arg(m_sig.denominator());
184 : }
185 :
186 : //---------------------------------------------------------
187 : // setSSig
188 : //---------------------------------------------------------
189 :
190 0 : void TimeSig::setSSig(const String& s)
191 : {
192 0 : StringList sl = s.split(u'/');
193 0 : if (sl.size() == 2) {
194 0 : m_sig.setNumerator(sl[0].toInt());
195 0 : m_sig.setDenominator(sl[1].toInt());
196 : }
197 0 : }
198 :
199 : //---------------------------------------------------------
200 : // getProperty
201 : //---------------------------------------------------------
202 :
203 27903 : PropertyValue TimeSig::getProperty(Pid propertyId) const
204 : {
205 27903 : switch (propertyId) {
206 1993 : case Pid::SHOW_COURTESY:
207 1993 : return showCourtesySig();
208 1993 : case Pid::NUMERATOR_STRING:
209 1993 : return numeratorString();
210 1993 : case Pid::DENOMINATOR_STRING:
211 1993 : return denominatorString();
212 14 : case Pid::GROUP_NODES:
213 14 : return groups().nodes();
214 45 : case Pid::TIMESIG:
215 45 : return PropertyValue::fromValue(m_sig);
216 14 : case Pid::TIMESIG_STRETCH:
217 14 : return PropertyValue::fromValue(stretch());
218 2009 : case Pid::TIMESIG_TYPE:
219 2009 : return int(m_timeSigType);
220 0 : case Pid::SCALE:
221 0 : return m_scale;
222 1979 : case Pid::IS_COURTESY:
223 1979 : return _isCourtesy;
224 :
225 17863 : default:
226 17863 : return EngravingItem::getProperty(propertyId);
227 : }
228 : }
229 :
230 : //---------------------------------------------------------
231 : // setProperty
232 : //---------------------------------------------------------
233 :
234 3817 : bool TimeSig::setProperty(Pid propertyId, const PropertyValue& v)
235 : {
236 3817 : switch (propertyId) {
237 0 : case Pid::SHOW_COURTESY:
238 0 : if (generated()) {
239 0 : return false;
240 : }
241 0 : setShowCourtesySig(v.toBool());
242 0 : break;
243 0 : case Pid::NUMERATOR_STRING:
244 0 : setNumeratorString(v.value<String>());
245 0 : break;
246 0 : case Pid::DENOMINATOR_STRING:
247 0 : setDenominatorString(v.value<String>());
248 0 : break;
249 0 : case Pid::GROUP_NODES:
250 0 : setGroups(v.value<GroupNodes>());
251 0 : break;
252 17 : case Pid::TIMESIG:
253 17 : setSig(v.value<Fraction>());
254 17 : break;
255 0 : case Pid::TIMESIG_STRETCH:
256 0 : setStretch(v.value<Fraction>());
257 0 : break;
258 2 : case Pid::TIMESIG_TYPE:
259 2 : m_timeSigType = (TimeSigType)(v.toInt());
260 2 : break;
261 3795 : case Pid::SCALE:
262 3795 : m_scale = v.value<ScaleF>();
263 3795 : break;
264 0 : case Pid::IS_COURTESY:
265 0 : _isCourtesy = v.toBool();
266 0 : break;
267 3 : default:
268 3 : if (!EngravingItem::setProperty(propertyId, v)) {
269 0 : return false;
270 : }
271 3 : break;
272 : }
273 3817 : triggerLayoutAll(); // TODO
274 3817 : setGenerated(false);
275 3817 : return true;
276 : }
277 :
278 : //---------------------------------------------------------
279 : // propertyDefault
280 : //---------------------------------------------------------
281 :
282 32003 : PropertyValue TimeSig::propertyDefault(Pid id) const
283 : {
284 32003 : switch (id) {
285 1979 : case Pid::SHOW_COURTESY:
286 1979 : return true;
287 1979 : case Pid::NUMERATOR_STRING:
288 3958 : return String();
289 1979 : case Pid::DENOMINATOR_STRING:
290 3958 : return String();
291 0 : case Pid::TIMESIG:
292 0 : return PropertyValue::fromValue(Fraction(4, 4));
293 1979 : case Pid::TIMESIG_TYPE:
294 1979 : return int(TimeSigType::NORMAL);
295 1979 : case Pid::PLACEMENT:
296 1979 : return PlacementV::ABOVE;
297 1979 : case Pid::IS_COURTESY:
298 1979 : return false;
299 20129 : default:
300 20129 : return EngravingItem::propertyDefault(id);
301 : }
302 : }
303 :
304 18919 : PointF TimeSig::staffOffset() const
305 : {
306 18919 : const Segment* seg = segment();
307 18919 : const Measure* meas = seg ? seg->measure() : nullptr;
308 18919 : const Fraction tsTick = meas ? meas->tick() : tick();
309 18919 : const StaffType* st = staff()->constStaffType(tsTick);
310 18919 : const double yOffset = st ? st->yoffset().val() * spatium() : 0.0;
311 18919 : return PointF(0.0, yOffset);
312 : }
313 :
314 : //---------------------------------------------------------
315 : // nextSegmentElement
316 : //---------------------------------------------------------
317 :
318 0 : EngravingItem* TimeSig::nextSegmentElement()
319 : {
320 0 : return segment()->firstInNextSegments(staffIdx());
321 : }
322 :
323 : //---------------------------------------------------------
324 : // prevSegmentElement
325 : //---------------------------------------------------------
326 :
327 0 : EngravingItem* TimeSig::prevSegmentElement()
328 : {
329 0 : return segment()->lastInPrevSegments(staffIdx());
330 : }
331 :
332 : //---------------------------------------------------------
333 : // subtype
334 : //---------------------------------------------------------
335 :
336 0 : int TimeSig::subtype() const
337 : {
338 0 : size_t h1 = std::hash<int> {}(numerator());
339 0 : size_t h2 = std::hash<int> {}(denominator());
340 0 : size_t h3 = std::hash<TimeSigType> {}(timeSigType());
341 :
342 0 : return static_cast<int>(h1 ^ (h2 << 1) ^ (h3 << 2));
343 : }
344 :
345 : //---------------------------------------------------------
346 : // subtypeUserName
347 : //---------------------------------------------------------
348 :
349 1 : muse::TranslatableString TimeSig::subtypeUserName() const
350 : {
351 1 : switch (timeSigType()) {
352 0 : case TimeSigType::FOUR_FOUR:
353 0 : return TranslatableString("engraving/timesig", "Common time");
354 0 : case TimeSigType::ALLA_BREVE:
355 0 : return TranslatableString("engraving/timesig", "Cut time");
356 0 : case TimeSigType::CUT_BACH:
357 0 : return TranslatableString("engraving/timesig", "Cut time (Bach)");
358 0 : case TimeSigType::CUT_TRIPLE:
359 0 : return TranslatableString("engraving/timesig", "Cut triple time (9/8)");
360 1 : default:
361 1 : return TranslatableString("engraving/timesig", "%1/%2 time").arg(numerator(), denominator());
362 : }
363 : }
364 :
365 : //---------------------------------------------------------
366 : // accessibleInfo
367 : //---------------------------------------------------------
368 :
369 0 : String TimeSig::accessibleInfo() const
370 : {
371 0 : return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), translatedSubtypeUserName());
372 : }
373 :
374 3599 : void TimeSig::initElementStyle(const ElementStyle* elementStype)
375 : {
376 3599 : EngravingItem::initElementStyle(elementStype);
377 :
378 3599 : m_scale = propertyDefault(Pid::SCALE).value<ScaleF>();
379 3599 : }
380 :
381 196 : void TimeSig::styleChanged()
382 : {
383 196 : if (isStyled(Pid::SCALE)) {
384 196 : m_scale = propertyDefault(Pid::SCALE).value<ScaleF>();
385 : }
386 196 : EngravingItem::styleChanged();
387 196 : }
388 :
389 8082 : Sid TimeSig::getPropertyStyle(Pid id) const
390 : {
391 8082 : if (id == Pid::SCALE) {
392 8077 : switch (timeSigPlacement()) {
393 8043 : case TimeSigPlacement::NORMAL: return Sid::timeSigNormalScale;
394 0 : case TimeSigPlacement::ABOVE_STAVES: return Sid::timeSigAboveScale;
395 34 : case TimeSigPlacement::ACROSS_STAVES: return Sid::timeSigAcrossScale;
396 0 : default:
397 0 : return Sid::NOSTYLE;
398 : }
399 : }
400 :
401 5 : return EngravingItem::getPropertyStyle(id);
402 : }
403 :
404 391315 : TimeSigPlacement TimeSig::timeSigPlacement() const
405 : {
406 391315 : return style().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>();
407 : }
408 :
409 6419 : TimeSigStyle TimeSig::timeSigStyle() const
410 : {
411 6419 : switch (timeSigPlacement()) {
412 6385 : case TimeSigPlacement::NORMAL: return style().styleV(Sid::timeSigNormalStyle).value<TimeSigStyle>();
413 0 : case TimeSigPlacement::ABOVE_STAVES: return style().styleV(Sid::timeSigAboveStyle).value<TimeSigStyle>();
414 34 : case TimeSigPlacement::ACROSS_STAVES: return style().styleV(Sid::timeSigAcrossStyle).value<TimeSigStyle>();
415 0 : default:
416 0 : return TimeSigStyle::NORMAL;
417 : }
418 : }
419 :
420 6419 : double TimeSig::numDist() const
421 : {
422 6419 : switch (timeSigPlacement()) {
423 6385 : case TimeSigPlacement::NORMAL: return style().styleAbsolute(Sid::timeSigNormalNumDist);
424 0 : case TimeSigPlacement::ABOVE_STAVES: return style().styleAbsolute(Sid::timeSigAboveNumDist);
425 34 : case TimeSigPlacement::ACROSS_STAVES: return style().styleAbsolute(Sid::timeSigAcrossNumDist);
426 0 : default:
427 0 : return 0.0;
428 : }
429 : }
430 :
431 6419 : double TimeSig::yPos() const
432 : {
433 6419 : switch (timeSigPlacement()) {
434 6385 : case TimeSigPlacement::NORMAL: return style().styleAbsolute(Sid::timeSigNormalY);
435 0 : case TimeSigPlacement::ABOVE_STAVES: return (staff()->hasSystemObjectsBelowBottomStaff() ? -1.0 : 1.0)
436 0 : * style().styleAbsolute(Sid::timeSigAboveY);
437 34 : case TimeSigPlacement::ACROSS_STAVES: return style().styleAbsolute(Sid::timeSigAcrossY);
438 0 : default:
439 0 : return 0.0;
440 : }
441 : }
442 :
443 6762 : bool TimeSig::showOnThisStaff() const
444 : {
445 6762 : return timeSigPlacement() == TimeSigPlacement::NORMAL || staffIdx() == 0 || staff()->isSystemObjectStaff();
446 : }
447 :
448 143059 : bool TimeSig::isAboveStaves() const
449 : {
450 143059 : return timeSigPlacement() == TimeSigPlacement::ABOVE_STAVES;
451 : }
452 :
453 6419 : bool TimeSig::isAcrossStaves() const
454 : {
455 6419 : return timeSigPlacement() == TimeSigPlacement::ACROSS_STAVES;
456 : }
457 :
458 : //---------------------------------------------------------
459 : // operator==
460 : //---------------------------------------------------------
461 :
462 137 : bool TimeSig::operator==(const TimeSig& ts) const
463 : {
464 137 : return (timeSigType() == ts.timeSigType())
465 135 : && (sig().identical(ts.sig()))
466 262 : && (stretch() == ts.stretch())
467 125 : && (groups() == ts.groups())
468 125 : && (m_numeratorString == ts.m_numeratorString)
469 409 : && (m_denominatorString == ts.m_denominatorString)
470 : ;
471 : }
472 :
473 3906 : void TimeSig::added()
474 : {
475 3906 : IF_ASSERT_FAILED(score()) {
476 0 : return;
477 : }
478 :
479 3906 : score()->updateTicksAndTimeSigMapLater();
480 : }
481 :
482 153 : void TimeSig::removed()
483 : {
484 153 : IF_ASSERT_FAILED(score()) {
485 0 : return;
486 : }
487 :
488 153 : score()->updateTicksAndTimeSigMapLater();
489 : }
490 : }
|