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 "volta.h"
24 :
25 : #include <algorithm>
26 : #include <vector>
27 :
28 : #include "barline.h"
29 : #include "keysig.h"
30 : #include "measure.h"
31 : #include "score.h"
32 : #include "staff.h"
33 : #include "system.h"
34 : #include "text.h"
35 :
36 : #include "log.h"
37 :
38 : using namespace mu;
39 : using namespace mu::engraving;
40 :
41 : namespace mu::engraving {
42 : static const ElementStyle voltaStyle {
43 : { Sid::voltaFontFace, Pid::BEGIN_FONT_FACE },
44 : { Sid::voltaFontFace, Pid::CONTINUE_FONT_FACE },
45 : { Sid::voltaFontFace, Pid::END_FONT_FACE },
46 : { Sid::voltaFontSize, Pid::BEGIN_FONT_SIZE },
47 : { Sid::voltaFontSize, Pid::CONTINUE_FONT_SIZE },
48 : { Sid::voltaFontSize, Pid::END_FONT_SIZE },
49 : { Sid::voltaFontStyle, Pid::BEGIN_FONT_STYLE },
50 : { Sid::voltaFontStyle, Pid::CONTINUE_FONT_STYLE },
51 : { Sid::voltaFontStyle, Pid::END_FONT_STYLE },
52 : { Sid::voltaAlign, Pid::BEGIN_TEXT_ALIGN },
53 : { Sid::voltaAlign, Pid::CONTINUE_TEXT_ALIGN },
54 : { Sid::voltaAlign, Pid::END_TEXT_ALIGN },
55 : { Sid::voltaPosition, Pid::BEGIN_TEXT_POSITION },
56 : { Sid::voltaPosition, Pid::CONTINUE_TEXT_POSITION },
57 : { Sid::voltaPosition, Pid::END_TEXT_POSITION },
58 : { Sid::voltaOffset, Pid::BEGIN_TEXT_OFFSET },
59 : { Sid::voltaOffset, Pid::CONTINUE_TEXT_OFFSET },
60 : { Sid::voltaOffset, Pid::END_TEXT_OFFSET },
61 : { Sid::voltaMusicalSymbolSize, Pid::BEGIN_TEXT_MUSIC_SYMBOLS_SIZE },
62 : { Sid::voltaMusicalSymbolSize, Pid::CONTINUE_TEXT_MUSIC_SYMBOLS_SIZE },
63 : { Sid::voltaMusicalSymbolSize, Pid::END_TEXT_MUSIC_SYMBOLS_SIZE },
64 : { Sid::dummyMusicalSymbolsScale, Pid::BEGIN_TEXT_MUSICAL_SYMBOLS_SCALE },
65 : { Sid::dummyMusicalSymbolsScale, Pid::CONTINUE_TEXT_MUSICAL_SYMBOLS_SCALE },
66 : { Sid::dummyMusicalSymbolsScale, Pid::END_TEXT_MUSICAL_SYMBOLS_SCALE },
67 : { Sid::voltaLineWidth, Pid::LINE_WIDTH },
68 : { Sid::voltaLineStyle, Pid::LINE_STYLE },
69 : { Sid::voltaDashLineLen, Pid::DASH_LINE_LEN },
70 : { Sid::voltaDashGapLen, Pid::DASH_GAP_LEN },
71 : { Sid::voltaHook, Pid::BEGIN_HOOK_HEIGHT },
72 : { Sid::voltaHook, Pid::END_HOOK_HEIGHT },
73 : { Sid::voltaFontSpatiumDependent, Pid::TEXT_SIZE_SPATIUM_DEPENDENT },
74 : };
75 :
76 : //---------------------------------------------------------
77 : // VoltaSegment
78 : //---------------------------------------------------------
79 :
80 180 : VoltaSegment::VoltaSegment(Volta* sp)
81 180 : : TextLineBaseSegment(ElementType::VOLTA_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF | ElementFlag::SYSTEM)
82 : {
83 180 : m_text->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
84 180 : m_endText->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
85 180 : }
86 :
87 : //---------------------------------------------------------
88 : // propertyDelegate
89 : //---------------------------------------------------------
90 :
91 1292 : EngravingObject* VoltaSegment::propertyDelegate(Pid pid) const
92 : {
93 1292 : if (pid == Pid::BEGIN_HOOK_TYPE || pid == Pid::END_HOOK_TYPE || pid == Pid::VOLTA_ENDING) {
94 0 : return spanner();
95 : }
96 1292 : return TextLineBaseSegment::propertyDelegate(pid);
97 : }
98 :
99 : //---------------------------------------------------------
100 : // Volta
101 : //---------------------------------------------------------
102 :
103 176 : Volta::Volta(DummyParentOr<EngravingItem> parent)
104 176 : : TextLineBase(ElementType::VOLTA, parent, ElementFlag::SYSTEM)
105 : {
106 176 : setPlacement(PlacementV::ABOVE);
107 176 : initElementStyle(&voltaStyle);
108 :
109 176 : setBeginTextPlace(TextPlace::BELOW);
110 176 : setContinueTextPlace(TextPlace::BELOW);
111 176 : setLineVisible(true);
112 176 : resetProperty(Pid::BEGIN_TEXT);
113 176 : resetProperty(Pid::CONTINUE_TEXT);
114 176 : resetProperty(Pid::END_TEXT);
115 176 : resetProperty(Pid::BEGIN_TEXT_PLACE);
116 176 : resetProperty(Pid::CONTINUE_TEXT_PLACE);
117 176 : resetProperty(Pid::END_TEXT_PLACE);
118 176 : resetProperty(Pid::BEGIN_HOOK_TYPE);
119 176 : resetProperty(Pid::END_HOOK_TYPE);
120 176 : }
121 :
122 : ///
123 : /// \brief sorts the provided list in ascending order
124 : ///
125 148 : void Volta::setEndings(const std::vector<int>& l)
126 : {
127 148 : m_endings = l;
128 148 : std::sort(m_endings.begin(), m_endings.end());
129 148 : }
130 :
131 : //---------------------------------------------------------
132 : // setText
133 : //---------------------------------------------------------
134 :
135 57 : void Volta::setText(const String& s)
136 : {
137 57 : setBeginText(s);
138 57 : }
139 :
140 : //---------------------------------------------------------
141 : // text
142 : //---------------------------------------------------------
143 :
144 29 : String Volta::text() const
145 : {
146 29 : return beginText();
147 : }
148 :
149 : //---------------------------------------------------------
150 : // createLineSegment
151 : //---------------------------------------------------------
152 :
153 : static const ElementStyle voltaSegmentStyle {
154 : { Sid::voltaMinDistance, Pid::MIN_DISTANCE },
155 : };
156 :
157 180 : LineSegment* Volta::createLineSegment()
158 : {
159 180 : VoltaSegment* vs = new VoltaSegment(this);
160 180 : vs->setTrack(track());
161 180 : vs->initElementStyle(&voltaSegmentStyle);
162 180 : return vs;
163 : }
164 :
165 : //---------------------------------------------------------
166 : // hasEnding
167 : //---------------------------------------------------------
168 :
169 478 : bool Volta::hasEnding(int repeat) const
170 : {
171 825 : for (int ending : endings()) {
172 617 : if (ending == repeat) {
173 270 : return true;
174 : }
175 478 : }
176 208 : return false;
177 : }
178 :
179 : //---------------------------------------------------------
180 : // firstEnding
181 : //---------------------------------------------------------
182 :
183 1 : int Volta::firstEnding() const
184 : {
185 1 : if (m_endings.empty()) {
186 0 : return 0;
187 : }
188 1 : return m_endings.front();
189 : }
190 :
191 : //---------------------------------------------------------
192 : // lastEnding
193 : //---------------------------------------------------------
194 :
195 15 : int Volta::lastEnding() const
196 : {
197 15 : if (m_endings.empty()) {
198 0 : return 0;
199 : }
200 15 : return m_endings.back();
201 : }
202 :
203 : //---------------------------------------------------------
204 : // getProperty
205 : //---------------------------------------------------------
206 :
207 1931 : PropertyValue Volta::getProperty(Pid propertyId) const
208 : {
209 1931 : switch (propertyId) {
210 0 : case Pid::VOLTA_ENDING:
211 0 : return PropertyValue::fromValue(endings());
212 1931 : default:
213 1931 : break;
214 : }
215 1931 : return TextLineBase::getProperty(propertyId);
216 : }
217 :
218 : //---------------------------------------------------------
219 : // setProperty
220 : //---------------------------------------------------------
221 :
222 7093 : bool Volta::setProperty(Pid propertyId, const PropertyValue& val)
223 : {
224 7093 : switch (propertyId) {
225 0 : case Pid::VOLTA_ENDING: {
226 0 : setEndings(val.value<std::vector<int> >());
227 0 : } break;
228 7093 : default:
229 7093 : if (!TextLineBase::setProperty(propertyId, val)) {
230 0 : return false;
231 : }
232 7093 : break;
233 : }
234 7093 : triggerLayout();
235 7093 : return true;
236 : }
237 :
238 : //---------------------------------------------------------
239 : // propertyDefault
240 : //---------------------------------------------------------
241 :
242 3643 : PropertyValue Volta::propertyDefault(Pid propertyId) const
243 : {
244 3643 : switch (propertyId) {
245 0 : case Pid::VOLTA_ENDING:
246 0 : return PropertyValue::fromValue(std::vector<int>());
247 231 : case Pid::BEGIN_HOOK_TYPE:
248 231 : return HookType::HOOK_90;
249 231 : case Pid::END_HOOK_TYPE:
250 231 : return HookType::NONE;
251 693 : case Pid::BEGIN_TEXT:
252 : case Pid::CONTINUE_TEXT:
253 : case Pid::END_TEXT:
254 693 : return "";
255 55 : case Pid::LINE_VISIBLE:
256 55 : return true;
257 693 : case Pid::BEGIN_TEXT_PLACE:
258 : case Pid::CONTINUE_TEXT_PLACE:
259 : case Pid::END_TEXT_PLACE:
260 693 : return TextPlace::ABOVE;
261 :
262 55 : case Pid::PLACEMENT:
263 55 : return PlacementV::ABOVE;
264 :
265 360 : case Pid::TEXT_STYLE:
266 360 : return TextStyleType::VOLTA;
267 :
268 440 : case Pid::BEGIN_FILLED_ARROW_HEIGHT: // No arrow endings for voltas
269 : case Pid::BEGIN_FILLED_ARROW_WIDTH:
270 : case Pid::END_FILLED_ARROW_HEIGHT:
271 : case Pid::END_FILLED_ARROW_WIDTH:
272 : case Pid::BEGIN_LINE_ARROW_HEIGHT:
273 : case Pid::BEGIN_LINE_ARROW_WIDTH:
274 : case Pid::END_LINE_ARROW_HEIGHT:
275 : case Pid::END_LINE_ARROW_WIDTH:
276 440 : return 0.0;
277 :
278 885 : default:
279 885 : return TextLineBase::propertyDefault(propertyId);
280 : }
281 : }
282 :
283 : //---------------------------------------------------------
284 : // setChannel
285 : //---------------------------------------------------------
286 :
287 125 : void Volta::setChannel() const
288 : {
289 125 : Measure* startMeasure = Spanner::startMeasure();
290 125 : Measure* endMeasure = Spanner::endMeasure();
291 :
292 125 : if (startMeasure && endMeasure) {
293 125 : if (!endMeasure->repeatEnd()) {
294 61 : return;
295 : }
296 :
297 64 : Fraction startTick = startMeasure->tick() - Fraction::eps();
298 64 : Fraction endTick = endMeasure->endTick() - Fraction::eps();
299 64 : Staff* st = staff();
300 320 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
301 256 : int channel = st->channel(startTick, voice);
302 256 : st->insertIntoChannelList(voice, endTick, channel);
303 : }
304 : }
305 : }
306 :
307 : //---------------------------------------------------------
308 : // accessibleInfo
309 : //---------------------------------------------------------
310 :
311 0 : String Volta::accessibleInfo() const
312 : {
313 0 : return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), text());
314 : }
315 :
316 546 : PointF Volta::linePos(Grip grip, System** system) const
317 : {
318 546 : bool start = grip == Grip::START;
319 :
320 546 : Segment* segment = score()->tick2leftSegment(start ? tick() : tick2(), true,
321 : SegmentType::ChordRest | SegmentType::StartRepeatBarLine | SegmentType::EndBarLine);
322 546 : if (!segment) {
323 0 : return PointF();
324 : }
325 :
326 546 : const Measure* measure = segment->measure();
327 546 : bool isAtSystemStart = segment->rtick().isZero() && measure && measure->system() && measure->isFirstInSystem();
328 546 : bool searchForPrevBarline = start ? segment->rtick().isZero() && (measure->repeatStart() || !isAtSystemStart) : true;
329 :
330 546 : SegmentType barlineType = start ? (SegmentType::StartRepeatBarLine | SegmentType::EndBarLine) : SegmentType::EndBarLine;
331 :
332 546 : if (searchForPrevBarline) {
333 522 : Segment* prev = segment;
334 1048 : while (prev && !prev->isType(barlineType) && prev->tick() == segment->tick()) {
335 526 : prev = prev->prev1MMenabled();
336 : }
337 :
338 522 : if (prev && prev->isType(barlineType)) {
339 515 : segment = prev;
340 : }
341 : }
342 :
343 546 : if (start && !segment->isType(SegmentType::BarLineTypes) && style().styleB(Sid::voltaAlignStartBeforeKeySig)) {
344 0 : Segment* prev = segment;
345 0 : while (prev && !prev->isType(SegmentType::KeySig) && prev->tick() == segment->tick()) {
346 0 : prev = prev->prev1MMenabled();
347 : }
348 0 : if (prev && prev->isType(SegmentType::KeySig)) {
349 0 : segment = prev;
350 : }
351 : }
352 :
353 546 : *system = segment->measure()->system();
354 546 : double x = segment->x() + segment->measure()->x();
355 :
356 546 : if (start) {
357 273 : bool alignLeftOfRepeatBarLine = false;
358 273 : if (segment->isChordRestType()) {
359 24 : x -= style().styleAbsolute(Sid::barNoteDistance);
360 249 : } else if (segment->isKeySigType()) {
361 0 : KeySig* sig = toKeySig(segment->element(track()));
362 0 : if (sig && !sig->ldata()->keySymbols.empty()) {
363 0 : KeySym keySym = sig->ldata()->keySymbols.front();
364 0 : PointF cutoutNW = score()->engravingFont()->smuflAnchor(keySym.sym, SmuflAnchorId::cutOutNW, 1.0);
365 0 : x += cutoutNW.x();
366 : }
367 249 : } else if (segment->segmentType() & SegmentType::BarLineTypes && !isAtSystemStart) {
368 249 : x += segment->width();
369 249 : const BarLine* barline = toBarLine(segment->element(track()));
370 249 : alignLeftOfRepeatBarLine = barline && barline->barLineType() == BarLineType::END_REPEAT
371 498 : && style().styleB(Sid::voltaAlignEndLeftOfBarline);
372 249 : if (alignLeftOfRepeatBarLine) {
373 0 : x -= style().styleAbsolute(Sid::endBarWidth);
374 : }
375 : }
376 273 : x += (isAtSystemStart || alignLeftOfRepeatBarLine ? 0.5 : -0.5) * absoluteFromSpatium(lineWidth());
377 : } else {
378 273 : if ((*system) && segment->tick() == (*system)->endTick()) {
379 50 : staff_idx_t si = backSegment()->effectiveStaffIdx();
380 50 : if (si == muse::nidx) {
381 0 : return PointF(x, 0.0);
382 : }
383 50 : x += segment->staffShape(si).right();
384 50 : x -= 0.5 * absoluteFromSpatium(lineWidth());
385 223 : } else if (segment->segmentType() & SegmentType::BarLineTypes) {
386 223 : BarLine* barLine = toBarLine(segment->element(track()));
387 223 : if (barLine->barLineType() == BarLineType::END_REPEAT || barLine->barLineType() == BarLineType::END_START_REPEAT) {
388 140 : x += symWidth(SymId::repeatDot) + style().styleAbsolute(Sid::repeatBarlineDotSeparation);
389 : }
390 223 : x += 0.5 * absoluteFromSpatium(lineWidth());
391 : }
392 : }
393 :
394 546 : return PointF(x, 0.0);
395 : }
396 :
397 : //---------------------------------------------------------
398 : // setVoltaType
399 : // deprecated
400 : //---------------------------------------------------------
401 :
402 33 : void Volta::setVoltaType(Type val)
403 : {
404 33 : setEndHookType(Type::CLOSED == val ? HookType::HOOK_90 : HookType::NONE);
405 33 : }
406 :
407 : //---------------------------------------------------------
408 : // voltaType
409 : // deprecated
410 : //---------------------------------------------------------
411 :
412 22 : Volta::Type Volta::voltaType() const
413 : {
414 22 : return endHookType() != HookType::NONE ? Type::CLOSED : Type::OPEN;
415 : }
416 :
417 369 : Sid Volta::defaultPosSid() const
418 : {
419 369 : return Sid::voltaPosAbove;
420 : }
421 : }
|