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 "spacer.h"
24 :
25 : #include "../editing/editdata.h"
26 : #include "../editing/elementeditdata.h"
27 :
28 : #include "measure.h"
29 : #include "score.h"
30 : #include "system.h"
31 :
32 : using namespace mu;
33 : using namespace muse::draw;
34 : using namespace mu::engraving;
35 :
36 : namespace mu::engraving {
37 : //---------------------------------------------------------
38 : // LayoutBreak
39 : //---------------------------------------------------------
40 :
41 1 : Spacer::Spacer(DummyParentOr<Measure> parent)
42 1 : : EngravingItem(ElementType::SPACER, parent)
43 : {
44 1 : m_spacerType = SpacerType::UP;
45 1 : m_gap = 0.0_sp;
46 1 : m_z = -10; // Ensure behind notation
47 1 : }
48 :
49 0 : Spacer::Spacer(const Spacer& s)
50 0 : : EngravingItem(s)
51 : {
52 0 : m_gap = s.m_gap;
53 0 : m_spacerType = s.m_spacerType;
54 0 : }
55 :
56 : //---------------------------------------------------------
57 : // setGap
58 : //---------------------------------------------------------
59 :
60 2 : void Spacer::setGap(Spatium sp)
61 : {
62 2 : m_gap = sp;
63 2 : }
64 :
65 : //---------------------------------------------------------
66 : // startDragGrip
67 : //---------------------------------------------------------
68 :
69 0 : void Spacer::startDragGrip(EditData& ed)
70 : {
71 0 : ElementEditDataPtr eed = ed.getData(this);
72 0 : eed->pushProperty(Pid::SPACE);
73 0 : }
74 :
75 : //---------------------------------------------------------
76 : // dragGrip
77 : //---------------------------------------------------------
78 :
79 0 : void Spacer::dragGrip(EditData& ed)
80 : {
81 0 : double s = ed.delta.y();
82 :
83 0 : switch (spacerType()) {
84 0 : case SpacerType::DOWN:
85 : case SpacerType::FIXED:
86 0 : m_gap += Spatium::fromAbsolute(s, spatium());
87 0 : break;
88 0 : case SpacerType::UP:
89 0 : m_gap -= Spatium::fromAbsolute(s, spatium());
90 0 : break;
91 : }
92 0 : m_gap = std::max(m_gap, 2.0_sp);
93 0 : triggerLayout();
94 0 : }
95 :
96 : //---------------------------------------------------------
97 : // gripsPositions
98 : //---------------------------------------------------------
99 :
100 0 : std::vector<PointF> Spacer::gripsPositions(const EditData&) const
101 : {
102 0 : double _spatium = spatium();
103 0 : PointF p;
104 0 : switch (spacerType()) {
105 0 : case SpacerType::DOWN:
106 : case SpacerType::FIXED:
107 0 : p = PointF(_spatium * .5, absoluteGap());
108 0 : break;
109 0 : case SpacerType::UP:
110 0 : p = PointF(_spatium * .5, 0.0);
111 0 : break;
112 : }
113 0 : return { pagePos() + p };
114 : }
115 :
116 : //---------------------------------------------------------
117 : // getProperty
118 : //---------------------------------------------------------
119 :
120 0 : PropertyValue Spacer::getProperty(Pid propertyId) const
121 : {
122 0 : switch (propertyId) {
123 0 : case Pid::SPACE:
124 0 : return gap();
125 0 : default:
126 0 : return EngravingItem::getProperty(propertyId);
127 : }
128 : }
129 :
130 : //---------------------------------------------------------
131 : // setProperty
132 : //---------------------------------------------------------
133 :
134 0 : bool Spacer::setProperty(Pid propertyId, const PropertyValue& v)
135 : {
136 0 : switch (propertyId) {
137 0 : case Pid::SPACE:
138 0 : setGap(v.value<Spatium>());
139 0 : break;
140 0 : default:
141 0 : if (!EngravingItem::setProperty(propertyId, v)) {
142 0 : return false;
143 : }
144 0 : break;
145 : }
146 0 : triggerLayout();
147 0 : setGenerated(false);
148 0 : return true;
149 : }
150 :
151 : //---------------------------------------------------------
152 : // propertyDefault
153 : //---------------------------------------------------------
154 :
155 0 : PropertyValue Spacer::propertyDefault(Pid id) const
156 : {
157 0 : switch (id) {
158 0 : case Pid::SPACE:
159 0 : return 0.0_sp;
160 0 : default:
161 0 : return EngravingItem::propertyDefault(id);
162 : }
163 : }
164 :
165 : //---------------------------------------------------------
166 : // triggerLayout
167 : //---------------------------------------------------------
168 :
169 0 : void Spacer::triggerLayout() const
170 : {
171 0 : if (!ownershipParent()) {
172 0 : return;
173 : }
174 :
175 0 : Measure* m = measure();
176 0 : if (!m) {
177 0 : EngravingItem::triggerLayout();
178 0 : return;
179 : }
180 :
181 0 : Score* s = score();
182 0 : System* system = m->system();
183 0 : if (!s || !system || s->nstaves() == 0) {
184 0 : m->triggerLayout();
185 0 : return;
186 : }
187 :
188 : // Most spacer edits can stay local to the measure and are much cheaper.
189 : // Escalate only for down/fixed spacers on the last non-vbox system of the page,
190 : // where edits can repaginate and affect page-end spacing.
191 0 : if (spacerType() == SpacerType::UP) {
192 0 : m->triggerLayout();
193 0 : return;
194 : }
195 :
196 0 : System* nextSystem = m->nextNonVBoxSystem();
197 0 : if (nextSystem && nextSystem->page() == system->page()) {
198 0 : m->triggerLayout();
199 0 : return;
200 : }
201 :
202 : // For the last notation system on a page, include the next system boundary
203 : // so page-end spacing can be recomputed without relaying out the whole score.
204 0 : Measure* firstMeasure = system->firstMeasure();
205 0 : if (!firstMeasure) {
206 0 : m->triggerLayout();
207 0 : return;
208 : }
209 :
210 0 : Fraction startTick = firstMeasure->tick();
211 0 : Fraction endTick = s->endTick();
212 0 : if (nextSystem) {
213 0 : if (Measure* nextFirstMeasure = nextSystem->firstMeasure()) {
214 0 : endTick = nextFirstMeasure->tick();
215 : }
216 : }
217 :
218 0 : s->setLayout(startTick, endTick, 0, s->nstaves() - 1, this);
219 : }
220 :
221 : //---------------------------------------------------------
222 : // subtypeUserName
223 : //---------------------------------------------------------
224 :
225 0 : muse::TranslatableString Spacer::subtypeUserName() const
226 : {
227 0 : switch (m_spacerType) {
228 0 : case SpacerType::UP:
229 0 : return TranslatableString("engraving/spacertype", "Staff spacer up");
230 0 : case SpacerType::DOWN:
231 0 : return TranslatableString("engraving/spacertype", "Staff spacer down");
232 0 : case SpacerType::FIXED:
233 0 : return TranslatableString("engraving/spacertype", "Staff spacer fixed down");
234 : }
235 0 : return TranslatableString::untranslatable("Unknown spacer");
236 : }
237 : }
|