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 "slurtie.h"
24 :
25 : #include "iengravingconfiguration.h" // IWYU pragma: keep
26 :
27 : #include "../editing/elementeditdata.h"
28 : #include "../editing/mscoreview.h"
29 : #include "../rendering/iscorerenderer.h"
30 :
31 : #include "note.h"
32 : #include "page.h"
33 : #include "score.h"
34 : #include "system.h"
35 :
36 : #include "log.h"
37 :
38 : using namespace mu;
39 : using namespace muse::draw;
40 : using namespace mu::engraving;
41 :
42 : namespace mu::engraving {
43 : //---------------------------------------------------------
44 : // SlurTieSegment
45 : //---------------------------------------------------------
46 :
47 2520 : SlurTieSegment::SlurTieSegment(const ElementType& type, SlurTie* sp)
48 2520 : : SpannerSegment(type, sp)
49 : {
50 2520 : setFlag(ElementFlag::ON_STAFF, true);
51 2520 : }
52 :
53 481 : SlurTieSegment::SlurTieSegment(const SlurTieSegment& b)
54 481 : : SpannerSegment(b)
55 : {
56 3367 : for (int i = 0; i < int(Grip::GRIPS); ++i) {
57 2886 : m_ups[i] = b.m_ups[i];
58 2886 : m_ups[i].p = PointF();
59 : }
60 481 : }
61 :
62 0 : bool SlurTieSegment::isEditAllowed(EditData& ed) const
63 : {
64 0 : if (ed.key == Key_Home && !(ed.modifiers & ~KeyboardModifier::KeypadModifier) && ed.hasCurrentGrip()) {
65 0 : return true;
66 : }
67 :
68 0 : return false;
69 : }
70 :
71 : //---------------------------------------------------------
72 : // edit
73 : // return true if event is accepted
74 : //---------------------------------------------------------
75 :
76 0 : bool SlurTieSegment::edit(EditData& ed)
77 : {
78 0 : if (!isEditAllowed(ed)) {
79 0 : return false;
80 : }
81 :
82 0 : if (ed.key == Key_Home && !(ed.modifiers & ~KeyboardModifier::KeypadModifier)) {
83 0 : if (ed.hasCurrentGrip()) {
84 0 : startDragGrip(ed);
85 0 : if (ed.curGrip == Grip::SHOULDER) {
86 0 : ups(Grip::BEZIER1).off = PointF();
87 0 : ups(Grip::BEZIER2).off = PointF();
88 : } else {
89 0 : ups(ed.curGrip).off = PointF();
90 : }
91 0 : renderer()->layoutItem(spanner());
92 0 : endDragGrip(ed);
93 : }
94 0 : return true;
95 : }
96 0 : return false;
97 : }
98 :
99 : //---------------------------------------------------------
100 : // gripAnchorLines
101 : //---------------------------------------------------------
102 :
103 0 : std::vector<LineF> SlurTieSegment::gripAnchorLines(Grip grip) const
104 : {
105 0 : std::vector<LineF> result;
106 :
107 0 : if (!system() || (grip != Grip::START && grip != Grip::END)) {
108 0 : return result;
109 : }
110 :
111 0 : PointF sp(system()->pagePos());
112 0 : PointF pp(pagePos());
113 0 : PointF p1(ups(Grip::START).p + pp);
114 0 : PointF p2(ups(Grip::END).p + pp);
115 :
116 0 : PointF anchorPosition;
117 0 : int gripIndex = static_cast<int>(grip);
118 :
119 0 : switch (spannerSegmentType()) {
120 0 : case SpannerSegmentType::SINGLE:
121 0 : anchorPosition = (grip == Grip::START ? p1 : p2);
122 0 : break;
123 :
124 0 : case SpannerSegmentType::BEGIN:
125 0 : anchorPosition = (grip == Grip::START ? p1 : system()->pageBoundingRect().topRight());
126 0 : break;
127 :
128 0 : case SpannerSegmentType::MIDDLE:
129 0 : anchorPosition = (grip == Grip::START ? sp : system()->pageBoundingRect().topRight());
130 0 : break;
131 :
132 0 : case SpannerSegmentType::END:
133 0 : anchorPosition = (grip == Grip::START ? sp : p2);
134 0 : break;
135 : }
136 :
137 0 : const Page* p = system()->page();
138 0 : const PointF pageOffset = p ? p->pos() : PointF();
139 0 : result.push_back(LineF(anchorPosition, gripsPositions().at(gripIndex)).translated(pageOffset));
140 :
141 0 : return result;
142 0 : }
143 :
144 : //---------------------------------------------------------
145 : // move
146 : //---------------------------------------------------------
147 :
148 0 : void SlurTieSegment::move(const PointF& s)
149 : {
150 0 : EngravingItem::move(s);
151 0 : for (int k = 0; k < int(Grip::GRIPS); ++k) {
152 0 : m_ups[k].p += s;
153 : }
154 0 : }
155 :
156 : //---------------------------------------------------------
157 : // spatiumChanged
158 : //---------------------------------------------------------
159 :
160 16 : void SlurTieSegment::spatiumChanged(double oldValue, double newValue)
161 : {
162 16 : EngravingItem::spatiumChanged(oldValue, newValue);
163 16 : double diff = newValue / oldValue;
164 112 : for (UP& u : m_ups) {
165 96 : u.off *= diff;
166 : }
167 16 : }
168 :
169 : //---------------------------------------------------------
170 : // gripsPositions
171 : //---------------------------------------------------------
172 :
173 0 : std::vector<PointF> SlurTieSegment::gripsPositions(const EditData&) const
174 : {
175 0 : const int ngrips = gripsCount();
176 0 : std::vector<PointF> grips(ngrips);
177 :
178 0 : const PointF p(pagePos());
179 0 : for (int i = 0; i < ngrips; ++i) {
180 0 : grips[i] = m_ups[i].p + m_ups[i].off + p;
181 : }
182 :
183 0 : return grips;
184 0 : }
185 :
186 940 : bool SlurTieSegment::isUserModified() const
187 : {
188 1877 : return SpannerSegment::isUserModified() || !(visible() && autoplace()
189 937 : && color() == configuration()->defaultColor()
190 937 : && offset().isNull()
191 937 : && ups(Grip::START).off.isNull()
192 937 : && ups(Grip::BEZIER1).off.isNull()
193 937 : && ups(Grip::BEZIER2).off.isNull()
194 1877 : && ups(Grip::END).off.isNull());
195 : }
196 :
197 : //---------------------------------------------------------
198 : // startDragGrip
199 : //---------------------------------------------------------
200 :
201 0 : void SlurTieSegment::startDragGrip(EditData& ed)
202 : {
203 0 : ElementEditDataPtr eed = ed.getData(this);
204 0 : IF_ASSERT_FAILED(eed) {
205 0 : return;
206 : }
207 0 : for (Pid i : { Pid::BEZIER_OFFSET1, Pid::BEZIER_OFFSET2, Pid::BEZIER_OFFSET3, Pid::BEZIER_OFFSET4, Pid::OFFSET }) {
208 0 : eed->pushProperty(i);
209 : }
210 0 : }
211 :
212 : //---------------------------------------------------------
213 : // endDragGrip
214 : //---------------------------------------------------------
215 :
216 0 : void SlurTieSegment::endDragGrip(EditData& ed)
217 : {
218 0 : EngravingItem::endDragGrip(ed);
219 0 : triggerLayout();
220 0 : }
221 :
222 : //---------------------------------------------------------
223 : // propertyDelegate
224 : //---------------------------------------------------------
225 :
226 11995 : EngravingObject* SlurTieSegment::propertyDelegate(Pid pid) const
227 : {
228 11995 : switch (pid) {
229 3331 : case Pid::SLUR_STYLE_TYPE:
230 : case Pid::SLUR_DIRECTION:
231 : case Pid::MASK_SLURTIE:
232 3331 : return slurTie();
233 8664 : default:
234 8664 : return SpannerSegment::propertyDelegate(pid);
235 : }
236 : }
237 :
238 : //---------------------------------------------------------
239 : // getProperty
240 : //---------------------------------------------------------
241 :
242 5048 : PropertyValue SlurTieSegment::getProperty(Pid propertyId) const
243 : {
244 5048 : if (EngravingObject* e = const_cast<SlurTieSegment*>(this)->propertyDelegate(propertyId)) {
245 3716 : return e->getProperty(propertyId);
246 : }
247 :
248 1332 : switch (propertyId) {
249 59 : case Pid::BEZIER_OFFSET1:
250 59 : return ups(Grip::START).off;
251 59 : case Pid::BEZIER_OFFSET2:
252 59 : return ups(Grip::BEZIER1).off;
253 59 : case Pid::BEZIER_OFFSET3:
254 59 : return ups(Grip::BEZIER2).off;
255 59 : case Pid::BEZIER_OFFSET4:
256 59 : return ups(Grip::END).off;
257 1096 : default:
258 1096 : return SpannerSegment::getProperty(propertyId);
259 : }
260 : }
261 :
262 : //---------------------------------------------------------
263 : // setProperty
264 : //---------------------------------------------------------
265 :
266 117 : bool SlurTieSegment::setProperty(Pid propertyId, const PropertyValue& v)
267 : {
268 117 : if (EngravingObject* e = const_cast<SlurTieSegment*>(this)->propertyDelegate(propertyId)) {
269 5 : return e->setProperty(propertyId, v);
270 : }
271 :
272 112 : switch (propertyId) {
273 3 : case Pid::BEZIER_OFFSET1:
274 3 : ups(Grip::START).off = v.value<PointF>();
275 3 : break;
276 6 : case Pid::BEZIER_OFFSET2:
277 6 : ups(Grip::BEZIER1).off = v.value<PointF>();
278 6 : break;
279 5 : case Pid::BEZIER_OFFSET3:
280 5 : ups(Grip::BEZIER2).off = v.value<PointF>();
281 5 : break;
282 4 : case Pid::BEZIER_OFFSET4:
283 4 : ups(Grip::END).off = v.value<PointF>();
284 4 : break;
285 94 : default:
286 94 : return SpannerSegment::setProperty(propertyId, v);
287 : }
288 18 : triggerLayout();
289 18 : return true;
290 : }
291 :
292 : //---------------------------------------------------------
293 : // propertyDefault
294 : //---------------------------------------------------------
295 :
296 1653 : PropertyValue SlurTieSegment::propertyDefault(Pid id) const
297 : {
298 1653 : if (EngravingObject* e = const_cast<SlurTieSegment*>(this)->propertyDelegate(id)) {
299 371 : return e->propertyDefault(id);
300 : }
301 :
302 1282 : switch (id) {
303 212 : case Pid::BEZIER_OFFSET1:
304 : case Pid::BEZIER_OFFSET2:
305 : case Pid::BEZIER_OFFSET3:
306 : case Pid::BEZIER_OFFSET4:
307 424 : return PointF();
308 1070 : default:
309 1070 : return SpannerSegment::propertyDefault(id);
310 : }
311 : }
312 :
313 : //---------------------------------------------------------
314 : // reset
315 : //---------------------------------------------------------
316 :
317 0 : void SlurTieSegment::reset()
318 : {
319 0 : EngravingItem::reset();
320 0 : undoResetProperty(Pid::BEZIER_OFFSET1);
321 0 : undoResetProperty(Pid::BEZIER_OFFSET2);
322 0 : undoResetProperty(Pid::BEZIER_OFFSET3);
323 0 : undoResetProperty(Pid::BEZIER_OFFSET4);
324 0 : slurTie()->reset();
325 0 : }
326 :
327 : //---------------------------------------------------------
328 : // undoChangeProperty
329 : //---------------------------------------------------------
330 :
331 30 : void SlurTieSegment::undoChangeProperty(Pid pid, const PropertyValue& val, PropertyFlags ps)
332 : {
333 30 : if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
334 : // Switching autoplacement on. Save user-defined
335 : // placement properties to undo stack.
336 3 : undoPushProperty(Pid::BEZIER_OFFSET1);
337 3 : undoPushProperty(Pid::BEZIER_OFFSET2);
338 3 : undoPushProperty(Pid::BEZIER_OFFSET3);
339 3 : undoPushProperty(Pid::BEZIER_OFFSET4);
340 : // other will be saved in base classes.
341 : }
342 30 : SpannerSegment::undoChangeProperty(pid, val, ps);
343 30 : }
344 :
345 : //---------------------------------------------------------
346 : // SlurTie
347 : //---------------------------------------------------------
348 :
349 2358 : SlurTie::SlurTie(const ElementType& type, EngravingObject* parent)
350 2358 : : Spanner(type, parent)
351 : {
352 2358 : m_slurDirection = DirectionV::AUTO;
353 2358 : m_up = true;
354 2358 : m_styleType = SlurStyleType::Solid;
355 2358 : m_maskSlurTie = AutoOnOff::AUTO;
356 2358 : }
357 :
358 506 : SlurTie::SlurTie(const SlurTie& t)
359 506 : : Spanner(t)
360 : {
361 506 : m_up = t.m_up;
362 506 : m_slurDirection = t.m_slurDirection;
363 506 : m_styleType = t.m_styleType;
364 506 : m_maskSlurTie = t.m_maskSlurTie;
365 506 : }
366 :
367 : //---------------------------------------------------------
368 : // SlurTie
369 : //---------------------------------------------------------
370 :
371 2390 : SlurTie::~SlurTie()
372 : {
373 2390 : }
374 :
375 : //---------------------------------------------------------
376 : // undoSetSlurDirection
377 : //---------------------------------------------------------
378 :
379 0 : void SlurTie::undoSetSlurDirection(DirectionV d)
380 : {
381 0 : undoChangeProperty(Pid::SLUR_DIRECTION, PropertyValue::fromValue<DirectionV>(d));
382 0 : }
383 :
384 : //---------------------------------------------------------
385 : // getProperty
386 : //---------------------------------------------------------
387 :
388 16470 : PropertyValue SlurTie::getProperty(Pid propertyId) const
389 : {
390 16470 : switch (propertyId) {
391 967 : case Pid::SLUR_STYLE_TYPE:
392 967 : return styleType();
393 973 : case Pid::SLUR_DIRECTION:
394 973 : return PropertyValue::fromValue<DirectionV>(slurDirection());
395 4298 : case Pid::MASK_SLURTIE:
396 4298 : return maskSlurTie();
397 10232 : default:
398 10232 : return Spanner::getProperty(propertyId);
399 : }
400 : }
401 :
402 : //---------------------------------------------------------
403 : // setProperty
404 : //---------------------------------------------------------
405 :
406 196 : bool SlurTie::setProperty(Pid propertyId, const PropertyValue& v)
407 : {
408 196 : switch (propertyId) {
409 18 : case Pid::SLUR_STYLE_TYPE:
410 18 : setStyleType(v.value<SlurStyleType>());
411 18 : break;
412 49 : case Pid::SLUR_DIRECTION:
413 49 : setSlurDirection(v.value<DirectionV>());
414 49 : break;
415 0 : case Pid::MASK_SLURTIE:
416 0 : setMaskSlurTie(v.value<AutoOnOff>());
417 0 : break;
418 129 : default:
419 129 : return Spanner::setProperty(propertyId, v);
420 : }
421 67 : triggerLayout();
422 67 : return true;
423 : }
424 :
425 : //---------------------------------------------------------
426 : // propertyDefault
427 : //---------------------------------------------------------
428 :
429 12946 : PropertyValue SlurTie::propertyDefault(Pid id) const
430 : {
431 12946 : switch (id) {
432 967 : case Pid::SLUR_STYLE_TYPE:
433 967 : return SlurStyleType::Solid;
434 967 : case Pid::SLUR_DIRECTION:
435 1934 : return PropertyValue::fromValue<DirectionV>(DirectionV::AUTO);
436 967 : case Pid::MASK_SLURTIE:
437 1934 : return AutoOnOff::AUTO;
438 10045 : default:
439 10045 : return Spanner::propertyDefault(id);
440 : }
441 : }
442 :
443 : //---------------------------------------------------------
444 : // fixupSegments
445 : //---------------------------------------------------------
446 :
447 5351 : void SlurTie::fixupSegments(unsigned nsegs)
448 : {
449 7329 : Spanner::fixupSegments(nsegs, [this]() { return newSlurTieSegment(); });
450 5351 : }
451 :
452 : //---------------------------------------------------------
453 : // reset
454 : //---------------------------------------------------------
455 :
456 1 : void SlurTie::reset()
457 : {
458 1 : EngravingItem::reset();
459 1 : undoResetProperty(Pid::SLUR_DIRECTION);
460 1 : undoResetProperty(Pid::SLUR_STYLE_TYPE);
461 1 : undoResetProperty(Pid::MASK_SLURTIE);
462 1 : }
463 :
464 0 : muse::TranslatableString SlurTie::subtypeUserName() const
465 : {
466 0 : switch (m_styleType) {
467 0 : case SlurStyleType::Solid:
468 0 : return TranslatableString("engraving/slurstyletype", "Solid");
469 0 : case SlurStyleType::Dotted:
470 0 : return TranslatableString("engraving/slurstyletype", "Dotted");
471 0 : case SlurStyleType::Dashed:
472 0 : return TranslatableString("engraving/slurstyletype", "Dashed");
473 0 : case SlurStyleType::WideDashed:
474 0 : return TranslatableString("engraving/slurstyletype", "Wide dashed");
475 0 : default:
476 0 : return TranslatableString("engraving/slurstyletype", "Undefined");
477 : }
478 : }
479 :
480 0 : int SlurTieSegment::subtype() const
481 : {
482 0 : return slurTie()->subtype();
483 : }
484 :
485 0 : muse::TranslatableString SlurTieSegment::subtypeUserName() const
486 : {
487 0 : return slurTie()->subtypeUserName();
488 : }
489 : }
|