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 "trill.h"
24 :
25 : #include <cmath>
26 :
27 : #include "../editing/addremoveelement.h"
28 : #include "types/typesconv.h"
29 :
30 : #include "iengravingfont.h"
31 :
32 : #include "accidental.h"
33 : #include "chord.h"
34 : #include "clef.h"
35 : #include "factory.h"
36 : #include "measure.h"
37 : #include "ornament.h"
38 : #include "score.h"
39 : #include "system.h"
40 :
41 : using namespace mu::engraving;
42 :
43 : //---------------------------------------------------------
44 : // trillStyle
45 : //---------------------------------------------------------
46 :
47 : static const ElementStyle trillStyle {
48 : { Sid::trillPlacement, Pid::PLACEMENT },
49 : };
50 :
51 63 : TrillSegment::TrillSegment(Trill* sp)
52 63 : : LineSegment(ElementType::TRILL_SEGMENT, sp, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
53 : {
54 63 : }
55 :
56 : //---------------------------------------------------------
57 : // remove
58 : //---------------------------------------------------------
59 :
60 0 : void TrillSegment::remove(EngravingItem* e)
61 : {
62 0 : if (trill()->accidental() == e) {
63 : // accidental is part of trill
64 0 : trill()->setAccidental(nullptr);
65 0 : e->removed();
66 : }
67 0 : }
68 :
69 : //---------------------------------------------------------
70 : // symbolLine
71 : //---------------------------------------------------------
72 :
73 108 : void TrillSegment::symbolLine(SymId start, SymId fill)
74 : {
75 108 : double x1 = 0.0;
76 108 : double x2 = pos2().x();
77 108 : double w = x2 - x1;
78 108 : double mag = magS();
79 108 : IEngravingFontPtr f = score()->engravingFont();
80 :
81 108 : m_symbols.clear();
82 108 : m_symbols.push_back(start);
83 108 : double w1 = f->advance(start, mag);
84 108 : double w2 = f->advance(fill, mag);
85 108 : int n = lrint((w - w1) / w2);
86 1491 : for (int i = 0; i < n; ++i) {
87 1383 : m_symbols.push_back(fill);
88 : }
89 108 : RectF r(f->bbox(m_symbols, mag));
90 108 : setbbox(r);
91 108 : }
92 :
93 8 : void TrillSegment::symbolLine(SymId start, SymId fill, SymId end)
94 : {
95 8 : double x1 = 0.0;
96 8 : double x2 = pos2().x();
97 8 : double w = x2 - x1;
98 8 : double mag = magS();
99 8 : IEngravingFontPtr f = score()->engravingFont();
100 :
101 8 : m_symbols.clear();
102 8 : m_symbols.push_back(start);
103 8 : double w1 = f->advance(start, mag);
104 8 : double w2 = f->advance(fill, mag);
105 8 : double w3 = f->advance(end, mag);
106 8 : int n = lrint((w - w1 - w3) / w2);
107 104 : for (int i = 0; i < n; ++i) {
108 96 : m_symbols.push_back(fill);
109 : }
110 8 : m_symbols.push_back(end);
111 8 : RectF r(f->bbox(m_symbols, mag));
112 8 : setbbox(r);
113 8 : }
114 :
115 0 : void TrillSegment::rebaseAnchors(EditData& ed, Grip grip)
116 : {
117 0 : EngravingItem* startElement = spanner()->startElement();
118 0 : if (startElement && startElement->isChord() && toChord(startElement)->staffMove() != 0) {
119 : // This trill is on a cross-staff chord. Don't try to rebase its anchors when dragging.
120 0 : return;
121 : }
122 :
123 0 : LineSegment::rebaseAnchors(ed, grip);
124 : }
125 :
126 : //---------------------------------------------------------
127 : // scanElements
128 : //---------------------------------------------------------
129 :
130 1 : void TrillSegment::scanElements(std::function<void(EngravingItem*)> func)
131 : {
132 1 : func(this);
133 1 : if (isSingleType() || isBeginType()) {
134 1 : Accidental* a = trill()->accidental();
135 1 : if (a) {
136 0 : func(a);
137 : }
138 1 : Chord* cueNoteChord = trill()->cueNoteChord();
139 1 : if (cueNoteChord) {
140 0 : cueNoteChord->scanElements(func);
141 : }
142 : }
143 1 : }
144 :
145 : //---------------------------------------------------------
146 : // propertyDelegate
147 : //---------------------------------------------------------
148 :
149 195 : EngravingObject* TrillSegment::propertyDelegate(Pid pid) const
150 : {
151 195 : if (pid == Pid::TRILL_TYPE || pid == Pid::ORNAMENT_STYLE || pid == Pid::PLACEMENT) {
152 0 : return spanner();
153 : }
154 195 : return LineSegment::propertyDelegate(pid);
155 : }
156 :
157 : //---------------------------------------------------------
158 : // Trill
159 : //---------------------------------------------------------
160 :
161 67 : Trill::Trill(DummyParentOr<EngravingItem> parent)
162 67 : : SLine(ElementType::TRILL, parent)
163 : {
164 67 : m_trillType = TrillType::TRILL_LINE;
165 67 : m_ornament = nullptr;
166 67 : m_accidental = nullptr;
167 67 : m_cueNoteChord = nullptr;
168 67 : m_ornamentStyle = OrnamentStyle::DEFAULT;
169 67 : initElementStyle(&trillStyle);
170 67 : }
171 :
172 2 : Trill::Trill(const Trill& t)
173 2 : : SLine(t)
174 : {
175 2 : m_trillType = t.m_trillType;
176 2 : m_ornament = t.m_ornament ? t.m_ornament->clone() : nullptr;
177 2 : m_ornamentStyle = t.m_ornamentStyle;
178 2 : initElementStyle(&trillStyle);
179 2 : }
180 :
181 2 : EngravingItem* Trill::linkedClone()
182 : {
183 2 : Trill* linkedTrill = clone();
184 2 : Ornament* linkedOrnament = toOrnament(m_ornament->linkedClone());
185 2 : linkedTrill->setOrnament(linkedOrnament);
186 2 : linkedTrill->setAutoplace(true);
187 2 : score()->undo(new Link(linkedTrill, this));
188 2 : return linkedTrill;
189 : }
190 :
191 : //---------------------------------------------------------
192 : // remove
193 : //---------------------------------------------------------
194 :
195 0 : void Trill::remove(EngravingItem* e)
196 : {
197 0 : if (e == m_accidental) {
198 0 : m_accidental = nullptr;
199 0 : e->removed();
200 : }
201 0 : }
202 :
203 115 : void Trill::setTrack(track_idx_t n)
204 : {
205 115 : EngravingItem::setTrack(n);
206 :
207 118 : for (SpannerSegment* ss : spannerSegments()) {
208 3 : ss->setTrack(n);
209 : }
210 :
211 115 : if (m_ornament) {
212 54 : m_ornament->setTrack(n);
213 : }
214 115 : }
215 :
216 2 : void Trill::setScore(Score* s)
217 : {
218 2 : Spanner::setScore(s);
219 2 : if (m_ornament) {
220 2 : m_ornament->setScore(s);
221 : }
222 2 : }
223 :
224 212 : void Trill::computeStartElement()
225 : {
226 212 : Spanner::computeStartElement();
227 212 : if (startElement() && startElement()->isChord() && m_ornament) {
228 194 : m_ornament->setOwnershipParent(startElement());
229 :
230 194 : Chord* cueChord = m_ornament->cueNoteChord();
231 194 : if (cueChord) {
232 13 : cueChord->setOwnershipParent(toChord(startElement())->segment());
233 : }
234 : }
235 212 : }
236 :
237 286 : PointF Trill::trillLinePos(const SLine* line, Grip grip, System** system)
238 : {
239 286 : if (!line) {
240 0 : return PointF();
241 : }
242 :
243 286 : bool start = grip == Grip::START;
244 286 : bool mmRest = line->style().styleB(Sid::createMultiMeasureRests);
245 286 : double graceOffset = 0.0;
246 286 : double clefOffset = 0.0;
247 :
248 286 : Segment* segment = start ? line->startSegment() : line->endSegment();
249 286 : if (!segment) {
250 0 : return PointF();
251 : }
252 :
253 286 : if (start) {
254 143 : *system = segment->measure()->system();
255 143 : double x = segment->x() + segment->measure()->x();
256 143 : return PointF(x, 0.0);
257 : }
258 :
259 143 : Segment* clefSeg = segment->isClefType() ? segment : nullptr;
260 143 : Fraction curTick = segment->tick();
261 : while (true) {
262 219 : Segment* prevSeg = mmRest ? segment->prev1MM() : segment->prev1();
263 219 : if (prevSeg && prevSeg->tick() == curTick) {
264 76 : clefSeg = prevSeg->isClefType() ? prevSeg : clefSeg;
265 76 : segment = prevSeg;
266 : } else {
267 143 : break;
268 : }
269 76 : }
270 :
271 : // Stop line before clefs
272 143 : if (clefSeg) {
273 0 : EngravingItem* clefItem = clefSeg->element(line->track2());
274 0 : if (clefItem && clefItem->isClef()) {
275 0 : Clef* clef = toClef(clefItem);
276 0 : SymId clefSym = ClefInfo::symId(clef->clefType());
277 0 : Shape clefShape = line->symShapeWithCutouts(clefSym).translated(clef->pos());
278 0 : clefOffset = segment->pageX() - clef->pageX() + clefShape.leftMostEdgeAtTop();
279 0 : }
280 : }
281 :
282 : // Stop line before grace notes
283 143 : if (const EngravingItem* preAppendedItem = segment->preAppendedItem(line->track2());
284 143 : preAppendedItem&& preAppendedItem->isGraceNotesGroup()) {
285 : // get x position of leftmost grace note
286 0 : const Chord* leftMostGraceChord = nullptr;
287 0 : const GraceNotesGroup* graceGroup = toGraceNotesGroup(preAppendedItem);
288 0 : for (const Chord* graceChord : *graceGroup) {
289 0 : leftMostGraceChord = leftMostGraceChord
290 0 : && leftMostGraceChord->x() < graceChord->x() ? leftMostGraceChord : graceChord;
291 : }
292 0 : if (leftMostGraceChord) {
293 0 : graceOffset = segment->pageX() - leftMostGraceChord->pageX();
294 : }
295 : }
296 :
297 143 : double offset = std::max(graceOffset, clefOffset);
298 :
299 143 : *system = segment->measure()->system();
300 143 : double x = segment->x() + segment->measure()->x() - line->spatium() - offset;
301 143 : return PointF(x, 0.0);
302 : }
303 :
304 230 : PointF Trill::linePos(Grip grip, System** system) const
305 : {
306 230 : return trillLinePos(this, grip, system);
307 : }
308 :
309 53 : void Trill::setTrillType(TrillType tt)
310 : {
311 53 : m_trillType = tt;
312 53 : if (!m_ornament) {
313 : // ornament parent will be explicitely set at layout stage
314 53 : m_ornament = Factory::createOrnament(score()->dummy());
315 : }
316 53 : m_ornament->setTrack(track());
317 53 : m_ornament->setSymId(Ornament::fromTrillType(tt));
318 53 : }
319 :
320 : //---------------------------------------------------------
321 : // createLineSegment
322 : //---------------------------------------------------------
323 :
324 : static const ElementStyle trillSegmentStyle {
325 : { Sid::trillMinDistance, Pid::MIN_DISTANCE },
326 : };
327 :
328 63 : LineSegment* Trill::createLineSegment()
329 : {
330 63 : TrillSegment* seg = new TrillSegment(this);
331 63 : seg->setTrack(track());
332 63 : seg->setColor(lineColor());
333 63 : seg->initElementStyle(&trillSegmentStyle);
334 63 : return seg;
335 : }
336 :
337 : //---------------------------------------------------------
338 : // trillTypeName
339 : //---------------------------------------------------------
340 :
341 0 : String Trill::trillTypeUserName() const
342 : {
343 0 : return TConv::translatedUserName(trillType());
344 : }
345 :
346 : //---------------------------------------------------------
347 : // getProperty
348 : //---------------------------------------------------------
349 :
350 259 : PropertyValue Trill::getProperty(Pid propertyId) const
351 : {
352 259 : switch (propertyId) {
353 0 : case Pid::TRILL_TYPE:
354 0 : return int(trillType());
355 18 : case Pid::ORNAMENT_STYLE:
356 18 : return ornamentStyle();
357 241 : default:
358 241 : break;
359 : }
360 241 : return SLine::getProperty(propertyId);
361 : }
362 :
363 : //---------------------------------------------------------
364 : // setProperty
365 : //---------------------------------------------------------
366 :
367 110 : bool Trill::setProperty(Pid propertyId, const PropertyValue& val)
368 : {
369 110 : switch (propertyId) {
370 0 : case Pid::TRILL_TYPE:
371 0 : setTrillType(TrillType(val.toInt()));
372 0 : break;
373 0 : case Pid::ORNAMENT_STYLE:
374 0 : setOrnamentStyle(val.value<OrnamentStyle>());
375 0 : break;
376 0 : case Pid::COLOR:
377 0 : setColor(val.value<Color>());
378 0 : setLineColor(val.value<Color>());
379 0 : break;
380 110 : default:
381 110 : return SLine::setProperty(propertyId, val);
382 : }
383 0 : triggerLayout();
384 0 : return true;
385 : }
386 :
387 : //---------------------------------------------------------
388 : // propertyDefault
389 : //---------------------------------------------------------
390 :
391 255 : PropertyValue Trill::propertyDefault(Pid propertyId) const
392 : {
393 255 : switch (propertyId) {
394 0 : case Pid::TRILL_TYPE:
395 0 : return static_cast<int>(TrillType::TRILL_LINE);
396 18 : case Pid::ORNAMENT_STYLE:
397 18 : return OrnamentStyle::DEFAULT;
398 2 : case Pid::PLACEMENT:
399 2 : return style().styleV(Sid::trillPlacement);
400 :
401 235 : default:
402 235 : return SLine::propertyDefault(propertyId);
403 : }
404 : }
405 :
406 : //---------------------------------------------------------
407 : // subtypeUserName
408 : //---------------------------------------------------------
409 :
410 1 : muse::TranslatableString Trill::subtypeUserName() const
411 : {
412 1 : return TConv::userName(trillType());
413 : }
414 :
415 : //---------------------------------------------------------
416 : // accessibleInfo
417 : //---------------------------------------------------------
418 :
419 0 : String Trill::accessibleInfo() const
420 : {
421 0 : return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), trillTypeUserName());
422 : }
423 :
424 118 : Sid Trill::defaultPosSid() const
425 : {
426 118 : return placeAbove() ? Sid::trillPosAbove : Sid::trillPosBelow;
427 : }
428 :
429 211 : void Trill::doComputeEndElement()
430 : {
431 211 : setEndElement(score()->findChordRestEndingBeforeTickInStaffAndVoice(tick2(), track2staff(track2()), voice()));
432 211 : }
|