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 "pedal.h"
24 :
25 : #include "chord.h"
26 : #include "chordrest.h"
27 : #include "measure.h"
28 : #include "note.h"
29 : #include "score.h"
30 : #include "system.h"
31 : #include "text.h"
32 : #include "rest.h"
33 :
34 : #include "log.h"
35 :
36 : using namespace mu;
37 :
38 : namespace mu::engraving {
39 : static const ElementStyle pedalStyle {
40 : { Sid::pedalText, Pid::BEGIN_TEXT },
41 : { Sid::pedalContinueText, Pid::CONTINUE_TEXT },
42 : { Sid::pedalRosetteEndText, Pid::END_TEXT },
43 : { Sid::pedalFontFace, Pid::BEGIN_FONT_FACE },
44 : { Sid::pedalFontFace, Pid::CONTINUE_FONT_FACE },
45 : { Sid::pedalFontFace, Pid::END_FONT_FACE },
46 : { Sid::pedalFontSize, Pid::BEGIN_FONT_SIZE },
47 : { Sid::pedalFontSize, Pid::CONTINUE_FONT_SIZE },
48 : { Sid::pedalFontSize, Pid::END_FONT_SIZE },
49 : { Sid::pedalFontStyle, Pid::BEGIN_FONT_STYLE },
50 : { Sid::pedalFontStyle, Pid::CONTINUE_FONT_STYLE },
51 : { Sid::pedalFontStyle, Pid::END_FONT_STYLE },
52 : { Sid::pedalMusicalSymbolsScale, Pid::BEGIN_TEXT_MUSICAL_SYMBOLS_SCALE },
53 : { Sid::pedalMusicalSymbolsScale, Pid::CONTINUE_TEXT_MUSICAL_SYMBOLS_SCALE },
54 : { Sid::pedalMusicalSymbolsScale, Pid::END_TEXT_MUSICAL_SYMBOLS_SCALE },
55 : { Sid::dummyMusicalSymbolSize, Pid::BEGIN_TEXT_MUSIC_SYMBOLS_SIZE },
56 : { Sid::dummyMusicalSymbolSize, Pid::CONTINUE_TEXT_MUSIC_SYMBOLS_SIZE },
57 : { Sid::dummyMusicalSymbolSize, Pid::END_TEXT_MUSIC_SYMBOLS_SIZE },
58 : { Sid::pedalTextAlign, Pid::BEGIN_TEXT_ALIGN },
59 : { Sid::pedalTextAlign, Pid::CONTINUE_TEXT_ALIGN },
60 : { Sid::pedalTextAlign, Pid::END_TEXT_ALIGN },
61 : { Sid::pedalPosition, Pid::BEGIN_TEXT_POSITION },
62 : { Sid::pedalPosition, Pid::CONTINUE_TEXT_POSITION },
63 : { Sid::pedalPosition, Pid::END_TEXT_POSITION },
64 : { Sid::pedalHookHeight, Pid::BEGIN_HOOK_HEIGHT },
65 : { Sid::pedalHookHeight, Pid::END_HOOK_HEIGHT },
66 : { Sid::pedalLineWidth, Pid::LINE_WIDTH },
67 : { Sid::pedalDashLineLen, Pid::DASH_LINE_LEN },
68 : { Sid::pedalDashGapLen, Pid::DASH_GAP_LEN },
69 : { Sid::pedalPlacement, Pid::PLACEMENT },
70 : { Sid::pedalLineStyle, Pid::LINE_STYLE },
71 : { Sid::pedalFontSpatiumDependent, Pid::TEXT_SIZE_SPATIUM_DEPENDENT },
72 : { Sid::pedalEndLineArrowHeight, Pid::END_LINE_ARROW_HEIGHT },
73 : { Sid::pedalEndLineArrowWidth, Pid::END_LINE_ARROW_WIDTH },
74 : { Sid::pedalBeginLineArrowHeight, Pid::BEGIN_LINE_ARROW_HEIGHT },
75 : { Sid::pedalBeginLineArrowWidth, Pid::BEGIN_LINE_ARROW_WIDTH },
76 : { Sid::pedalEndFilledArrowHeight, Pid::END_FILLED_ARROW_HEIGHT },
77 : { Sid::pedalEndFilledArrowWidth, Pid::END_FILLED_ARROW_WIDTH },
78 : { Sid::pedalBeginFilledArrowHeight, Pid::BEGIN_FILLED_ARROW_HEIGHT },
79 : { Sid::pedalBeginFilledArrowWidth, Pid::BEGIN_FILLED_ARROW_WIDTH },
80 : };
81 :
82 : const String Pedal::PEDAL_SYMBOL = u"<sym>keyboardPedalPed</sym>";
83 : const String Pedal::STAR_SYMBOL = u"<sym>keyboardPedalUp</sym>";
84 :
85 74 : PedalSegment::PedalSegment(Pedal* sp)
86 74 : : TextLineBaseSegment(ElementType::PEDAL_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
87 : {
88 74 : m_text->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
89 74 : m_endText->setTextStyleType(propertyDefault(Pid::TEXT_STYLE).value<TextStyleType>());
90 74 : }
91 :
92 7248 : Sid Pedal::getPropertyStyle(Pid pid) const
93 : {
94 7248 : switch (pid) {
95 483 : case Pid::END_TEXT:
96 483 : return lineVisible() ? Sid::pedalEndText : Sid::pedalRosetteEndText;
97 352 : case Pid::BEGIN_TEXT:
98 352 : return beginHookType() == HookType::NONE ? Sid::pedalText : Sid::pedalHookText;
99 327 : case Pid::CONTINUE_TEXT:
100 327 : return beginHookType() == HookType::NONE ? Sid::pedalContinueText : Sid:: pedalContinueHookText;
101 6086 : default:
102 6086 : return TextLineBase::getPropertyStyle(pid);
103 : }
104 : }
105 :
106 : //---------------------------------------------------------
107 : // Pedal
108 : //---------------------------------------------------------
109 :
110 155 : Pedal::Pedal(DummyParentOr<EngravingItem> parent)
111 155 : : TextLineBase(ElementType::PEDAL, parent)
112 : {
113 155 : initElementStyle(&pedalStyle);
114 155 : setLineVisible(true);
115 :
116 155 : resetProperty(Pid::LINE_WIDTH);
117 155 : resetProperty(Pid::LINE_STYLE);
118 :
119 155 : resetProperty(Pid::BEGIN_HOOK_TYPE);
120 155 : resetProperty(Pid::END_HOOK_TYPE);
121 :
122 155 : resetProperty(Pid::BEGIN_TEXT_PLACE);
123 155 : resetProperty(Pid::LINE_VISIBLE);
124 155 : resetProperty(Pid::END_TEXT);
125 155 : }
126 :
127 : //---------------------------------------------------------
128 : // createLineSegment
129 : //---------------------------------------------------------
130 :
131 : static const ElementStyle pedalSegmentStyle {
132 : { Sid::pedalMinDistance, Pid::MIN_DISTANCE },
133 : };
134 :
135 74 : LineSegment* Pedal::createLineSegment()
136 : {
137 74 : PedalSegment* p = new PedalSegment(this);
138 74 : p->setTrack(track());
139 74 : p->initElementStyle(&pedalSegmentStyle);
140 74 : return p;
141 : }
142 :
143 : //---------------------------------------------------------
144 : // propertyDefault
145 : //---------------------------------------------------------
146 :
147 3213 : engraving::PropertyValue Pedal::propertyDefault(Pid propertyId) const
148 : {
149 3213 : switch (propertyId) {
150 158 : case Pid::LINE_WIDTH:
151 158 : return style().styleS(Sid::pedalLineWidth); // return point, not spatium
152 :
153 158 : case Pid::LINE_STYLE:
154 158 : return style().styleV(Sid::pedalLineStyle);
155 :
156 687 : case Pid::BEGIN_TEXT:
157 : case Pid::CONTINUE_TEXT:
158 : case Pid::END_TEXT:
159 687 : return style().styleV(getPropertyStyle(propertyId));
160 :
161 344 : case Pid::BEGIN_TEXT_PLACE:
162 : case Pid::CONTINUE_TEXT_PLACE:
163 : case Pid::END_TEXT_PLACE:
164 344 : return TextPlace::LEFT;
165 :
166 189 : case Pid::BEGIN_TEXT_OFFSET:
167 : case Pid::CONTINUE_TEXT_OFFSET:
168 : case Pid::END_TEXT_OFFSET:
169 189 : return PropertyValue::fromValue(PointF(0, 0));
170 :
171 436 : case Pid::BEGIN_HOOK_TYPE:
172 : case Pid::END_HOOK_TYPE:
173 436 : return HookType::NONE;
174 :
175 218 : case Pid::LINE_VISIBLE:
176 218 : return true;
177 :
178 9 : case Pid::PLACEMENT:
179 9 : return style().styleV(Sid::pedalPlacement);
180 :
181 148 : case Pid::TEXT_STYLE:
182 148 : return TextStyleType::PEDAL;
183 :
184 866 : default:
185 866 : return TextLineBase::propertyDefault(propertyId);
186 : }
187 : }
188 :
189 7753 : bool Pedal::setProperty(Pid propertyId, const PropertyValue& v)
190 : {
191 : // Update style flag for text
192 7753 : if (propertyId == Pid::BEGIN_HOOK_TYPE) {
193 158 : setBeginHookType(v.value<HookType>());
194 :
195 316 : PropertyFlags beginTextStyleFlag = beginText() == propertyDefault(Pid::BEGIN_TEXT).value<String>()
196 158 : ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
197 158 : setPropertyFlags(Pid::BEGIN_TEXT, beginTextStyleFlag);
198 316 : PropertyFlags continueTextStyleFlag = continueText() == propertyDefault(Pid::CONTINUE_TEXT).value<String>()
199 158 : ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
200 158 : setPropertyFlags(Pid::CONTINUE_TEXT, continueTextStyleFlag);
201 7595 : } else if (propertyId == Pid::LINE_VISIBLE) {
202 158 : setLineVisible(v.toBool());
203 316 : PropertyFlags endTextStyleFlag = endText() == propertyDefault(Pid::END_TEXT).value<String>()
204 158 : ? PropertyFlags::STYLED : PropertyFlags::UNSTYLED;
205 158 : setPropertyFlags(Pid::END_TEXT, endTextStyleFlag);
206 : } else {
207 7437 : return TextLineBase::setProperty(propertyId, v);
208 : }
209 :
210 316 : triggerLayout();
211 316 : return true;
212 : }
213 :
214 115 : Pedal* Pedal::findNextInStaff() const
215 : {
216 115 : Fraction endTick = tick2();
217 115 : auto spanners = score()->spannerMap().findOverlapping(endTick.ticks(), score()->endTick().ticks());
218 831 : for (auto element : spanners) {
219 733 : Spanner* spanner = element.value;
220 733 : if (spanner->isPedal() && spanner != this && spanner->staffIdx() == staffIdx() && spanner->tick() == endTick) {
221 17 : return toPedal(spanner);
222 : }
223 : }
224 :
225 98 : return nullptr;
226 115 : }
227 :
228 24 : bool Pedal::connect45HookToNext() const
229 : {
230 24 : if (endHookType() != HookType::HOOK_45) {
231 24 : return false;
232 : }
233 :
234 0 : Pedal* nextPedal = findNextInStaff();
235 :
236 0 : return nextPedal && nextPedal->tick() == tick2() && nextPedal->beginHookType() == HookType::HOOK_45;
237 : }
238 :
239 : //---------------------------------------------------------
240 : // linePos
241 : // return System() coordinates
242 : //---------------------------------------------------------
243 :
244 230 : PointF Pedal::linePos(Grip grip, System** sys) const
245 : {
246 230 : bool start = grip == Grip::START;
247 :
248 230 : if (start) {
249 115 : Segment* startSeg = startSegment();
250 115 : if (!startSeg) {
251 0 : return PointF();
252 : }
253 115 : *sys = startSeg->measure()->system();
254 115 : double x = startSeg->x() + startSeg->measure()->x();
255 115 : if (beginText() == "<sym>keyboardPedalPed</sym>") {
256 85 : x -= 0.5 * spatium();
257 30 : } else if (beginHookType() == HookType::HOOK_90 || beginHookType() == HookType::HOOK_90T) {
258 17 : x += 0.5 * absoluteFromSpatium(lineWidth());
259 13 : } else if (beginHookType() == HookType::HOOK_45) {
260 8 : EngravingItem* item = startElement();
261 8 : if (item && item->isChord()) {
262 8 : Note* downNote = toChord(item)->downNote();
263 8 : x += 0.5 * downNote->headWidth();
264 0 : } else if (item && item->isRest()) {
265 0 : x += toRest(item)->centerX();
266 : }
267 : }
268 115 : return PointF(x, 0.0);
269 : }
270 :
271 115 : Segment* endSeg = endSegment();
272 115 : if (!endSeg) {
273 0 : return PointF();
274 : }
275 :
276 115 : Pedal* nextPedal = findNextInStaff();
277 :
278 115 : if (nextPedal && endHookType() == HookType::HOOK_45) {
279 8 : *sys = endSeg->measure()->system();
280 8 : double x = endSeg->x() + endSeg->measure()->x();
281 8 : EngravingItem* item = endElement();
282 8 : if (item && item->isChord()) {
283 8 : Note* downNote = toChord(item)->downNote();
284 8 : x += 0.5 * downNote->headWidth();
285 0 : } else if (item && item->isRest()) {
286 0 : x += toRest(item)->centerX();
287 : }
288 8 : return PointF(x, 0.0);
289 : }
290 :
291 107 : if (endSeg->rtick() == Fraction(0, 1)) {
292 56 : Segment* prevSeg = endSeg->prev1(SegmentType::EndBarLine);
293 56 : if (prevSeg) {
294 56 : endSeg = prevSeg;
295 : }
296 : }
297 :
298 107 : *sys = endSeg->measure()->system();
299 107 : double x = endSeg->x() + endSeg->measure()->x();
300 :
301 107 : x -= (endSeg->isChordRestType() && nextPedal ? 1.25 : 0.75) * spatium();
302 :
303 107 : return PointF(x, 0.0);
304 : }
305 :
306 122 : Sid Pedal::defaultPosSid() const
307 : {
308 122 : return placeAbove() ? Sid::pedalPosAbove : Sid::pedalPosBelow;
309 : }
310 : }
|