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 "dynamichairpingroup.h"
24 :
25 : #include "dynamic.h"
26 : #include "expression.h"
27 : #include "hairpin.h"
28 : #include "score.h"
29 : #include "segment.h"
30 :
31 : using namespace mu;
32 :
33 : namespace mu::engraving {
34 0 : static std::pair<Hairpin*, Hairpin*> findAdjacentHairpins(Dynamic* d)
35 : {
36 0 : EngravingItem* itemSnappedBefore = d->ldata()->itemSnappedBefore();
37 0 : EngravingItem* itemSnappedAfter = d->ldata()->itemSnappedAfter();
38 :
39 0 : Hairpin* leftHairpin = itemSnappedBefore && itemSnappedBefore->isHairpinSegment()
40 0 : ? toHairpinSegment(itemSnappedBefore)->hairpin() : nullptr;
41 0 : Hairpin* rightHairpin = itemSnappedAfter && itemSnappedAfter->isHairpinSegment()
42 0 : ? toHairpinSegment(itemSnappedAfter)->hairpin() : nullptr;
43 :
44 0 : return { leftHairpin, rightHairpin };
45 : }
46 :
47 0 : std::unique_ptr<ElementGroup> HairpinWithDynamicsDragGroup::detectFor(HairpinSegment* hs,
48 : std::function<bool(const EngravingItem*)> isDragged)
49 : {
50 0 : if (!hs->isSingleType()) {
51 0 : return nullptr;
52 : }
53 :
54 0 : EngravingItem* itemSnappedBefore = hs->ldata()->itemSnappedBefore();
55 0 : EngravingItem* itemSnappedAfter = hs->ldata()->itemSnappedAfter();
56 :
57 0 : Dynamic* startDynamic = itemSnappedBefore && itemSnappedBefore->isDynamic() ? toDynamic(itemSnappedBefore) : nullptr;
58 0 : Dynamic* endDynamic = itemSnappedAfter && itemSnappedAfter->isDynamic() ? toDynamic(itemSnappedAfter) : nullptr;
59 :
60 : // Include only dragged dynamics to this group
61 0 : if (!isDragged(startDynamic)) {
62 0 : startDynamic = nullptr;
63 : }
64 0 : if (!isDragged(endDynamic)) {
65 0 : endDynamic = nullptr;
66 : }
67 :
68 0 : if (startDynamic || endDynamic) {
69 0 : return std::unique_ptr<ElementGroup>(new HairpinWithDynamicsDragGroup(startDynamic, hs, endDynamic));
70 : }
71 0 : return nullptr;
72 : }
73 :
74 0 : std::unique_ptr<ElementGroup> HairpinWithDynamicsDragGroup::detectFor(Dynamic* d, std::function<bool(const EngravingItem*)> isDragged)
75 : {
76 0 : Hairpin* leftHairpin = nullptr;
77 0 : Hairpin* rightHairpin = nullptr;
78 :
79 0 : std::tie(leftHairpin, rightHairpin) = findAdjacentHairpins(d);
80 :
81 : // Dynamic will be governed bt HairpinWithDynamicsDragGroup if any of adjacent
82 : // hairpins is dragged, disable separate drag logic for dynamic in this case.
83 0 : if (isDragged(leftHairpin)) {
84 0 : return std::unique_ptr<ElementGroup>(new DisabledElementGroup());
85 : }
86 0 : if (isDragged(rightHairpin)) {
87 0 : return std::unique_ptr<ElementGroup>(new DisabledElementGroup());
88 : }
89 :
90 0 : return nullptr;
91 : }
92 :
93 0 : void HairpinWithDynamicsDragGroup::startDrag(EditData& ed)
94 : {
95 0 : if (m_startDynamic) {
96 0 : m_startDynamic->startDrag(ed);
97 : }
98 0 : static_cast<EngravingItem*>(m_hairpinSegment)->startDrag(ed);
99 0 : if (m_endDynamic) {
100 0 : m_endDynamic->startDrag(ed);
101 : }
102 0 : }
103 :
104 0 : RectF HairpinWithDynamicsDragGroup::drag(EditData& ed)
105 : {
106 0 : RectF r;
107 :
108 0 : if (m_startDynamic) {
109 0 : r.unite(static_cast<EngravingItem*>(m_startDynamic)->drag(ed));
110 : }
111 0 : r.unite(m_hairpinSegment->drag(ed));
112 0 : if (m_endDynamic) {
113 0 : r.unite(static_cast<EngravingItem*>(m_endDynamic)->drag(ed));
114 : }
115 :
116 0 : Hairpin* h = m_hairpinSegment->hairpin();
117 :
118 0 : const Fraction startTick = m_startDynamic ? m_startDynamic->segment()->tick() : h->tick();
119 0 : const Fraction endTick = m_endDynamic ? m_endDynamic->segment()->tick() : h->tick2();
120 :
121 0 : if (endTick > startTick) {
122 0 : if (h->tick() != startTick) {
123 0 : h->undoChangeProperty(Pid::SPANNER_TICK, startTick);
124 : }
125 0 : if (h->tick2() != endTick) {
126 0 : h->undoChangeProperty(Pid::SPANNER_TICKS, endTick - startTick);
127 : }
128 : }
129 :
130 0 : return r;
131 : }
132 :
133 0 : void HairpinWithDynamicsDragGroup::endDrag(EditData& ed)
134 : {
135 0 : if (m_startDynamic) {
136 0 : m_startDynamic->endDrag(ed);
137 0 : m_startDynamic->triggerLayout();
138 : }
139 :
140 0 : m_hairpinSegment->endDrag(ed);
141 0 : m_hairpinSegment->triggerLayout();
142 :
143 0 : if (m_endDynamic) {
144 0 : m_endDynamic->endDrag(ed);
145 0 : m_endDynamic->triggerLayout();
146 : }
147 0 : }
148 :
149 0 : std::unique_ptr<ElementGroup> DynamicNearHairpinsDragGroup::detectFor(Dynamic* d, std::function<bool(const EngravingItem*)> isDragged)
150 : {
151 0 : Hairpin* leftHairpin = nullptr;
152 0 : Hairpin* rightHairpin = nullptr;
153 :
154 0 : std::tie(leftHairpin, rightHairpin) = findAdjacentHairpins(d);
155 :
156 : // Drag hairpins according to this rule only if they are not being dragged themselves
157 0 : if (isDragged(leftHairpin)) {
158 0 : leftHairpin = nullptr;
159 : }
160 0 : if (isDragged(rightHairpin)) {
161 0 : rightHairpin = nullptr;
162 : }
163 :
164 0 : if (leftHairpin || rightHairpin) {
165 0 : return std::unique_ptr<ElementGroup>(new DynamicNearHairpinsDragGroup(leftHairpin, d, rightHairpin));
166 : }
167 0 : return nullptr;
168 : }
169 :
170 0 : void DynamicNearHairpinsDragGroup::startDrag(EditData& ed)
171 : {
172 0 : m_dynamic->startDrag(ed);
173 0 : }
174 :
175 0 : RectF DynamicNearHairpinsDragGroup::drag(EditData& ed)
176 : {
177 0 : RectF r(static_cast<EngravingItem*>(m_dynamic)->drag(ed));
178 :
179 0 : const Fraction tick = m_dynamic->segment()->tick();
180 :
181 0 : if (m_leftHairpin && m_leftHairpin->tick2() != tick && tick > m_leftHairpin->tick()) {
182 0 : m_leftHairpin->undoChangeProperty(Pid::SPANNER_TICKS, tick - m_leftHairpin->tick());
183 : }
184 :
185 0 : if (m_rightHairpin && m_rightHairpin->tick() != tick) {
186 0 : const Fraction tick2 = m_rightHairpin->tick2();
187 0 : if (tick < tick2) {
188 0 : m_rightHairpin->undoChangeProperty(Pid::SPANNER_TICK, tick);
189 0 : m_rightHairpin->undoChangeProperty(Pid::SPANNER_TICKS, tick2 - tick);
190 : }
191 : }
192 :
193 0 : if (m_leftHairpin || m_rightHairpin) {
194 0 : m_dynamic->triggerLayout();
195 : }
196 :
197 0 : return r;
198 : }
199 :
200 0 : void DynamicNearHairpinsDragGroup::endDrag(EditData& ed)
201 : {
202 0 : m_dynamic->endDrag(ed);
203 0 : m_dynamic->triggerLayout();
204 0 : }
205 :
206 : //-------------------------------------------------------
207 : // DynamicExpressionDragGroup
208 : //-------------------------------------------------------
209 :
210 0 : std::unique_ptr<ElementGroup> DynamicExpressionDragGroup::detectFor(Dynamic* d, std::function<bool(const EngravingItem*)> isDragged)
211 : {
212 0 : Expression* snappedExpression = d->snappedExpression();
213 0 : if (snappedExpression && !isDragged(snappedExpression)) {
214 0 : return std::unique_ptr<DynamicExpressionDragGroup>(new DynamicExpressionDragGroup(d, snappedExpression));
215 : }
216 0 : return nullptr;
217 : }
218 :
219 0 : std::unique_ptr<ElementGroup> DynamicExpressionDragGroup::detectFor(Expression* e, std::function<bool(const EngravingItem*)> isDragged)
220 : {
221 0 : Dynamic* snappedDynamic = e->snappedDynamic();
222 0 : if (snappedDynamic && !isDragged(snappedDynamic)) {
223 0 : return std::unique_ptr<DynamicExpressionDragGroup>(new DynamicExpressionDragGroup(snappedDynamic, e));
224 : }
225 0 : return nullptr;
226 : }
227 :
228 0 : void DynamicExpressionDragGroup::startDrag(EditData& ed)
229 : {
230 0 : m_dynamic->startDrag(ed);
231 0 : m_expression->startDrag(ed);
232 0 : }
233 :
234 0 : RectF DynamicExpressionDragGroup::drag(EditData& ed)
235 : {
236 0 : RectF r = m_dynamic->drag(ed);
237 0 : r.unite(m_expression->drag(ed));
238 :
239 0 : m_dynamic->triggerLayout();
240 0 : m_expression->triggerLayout();
241 :
242 0 : return r;
243 : }
244 :
245 0 : void DynamicExpressionDragGroup::endDrag(EditData& ed)
246 : {
247 0 : m_dynamic->endDrag(ed);
248 0 : m_dynamic->triggerLayout();
249 0 : m_expression->endDrag(ed);
250 0 : m_expression->triggerLayout();
251 0 : }
252 : } // namespace mu::engraving
|