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 "scoreelement.h"
24 :
25 : #include "engraving/dom/measure.h"
26 : #include "engraving/dom/range.h"
27 : #include "engraving/dom/engravingobject.h"
28 : #include "engraving/dom/score.h"
29 :
30 : // api
31 : #include "apitypes.h"
32 : #include "apistructs.h"
33 : #include "score.h"
34 : #include "part.h"
35 : #include "elements.h"
36 :
37 : #include "log.h"
38 :
39 : using namespace mu::engraving::apiv1;
40 :
41 40 : ScoreElement::~ScoreElement()
42 : {
43 40 : if (m_ownership == Ownership::PLUGIN) {
44 0 : delete e;
45 : }
46 40 : }
47 :
48 0 : int ScoreElement::apiversion() const
49 : {
50 0 : if (m_apiversion == -1) {
51 0 : QJSEngine* engine = qjsEngine(this);
52 0 : IF_ASSERT_FAILED(engine) {
53 0 : return -1;
54 : }
55 :
56 0 : m_apiversion = engine->property("apiversion").toInt();
57 0 : IF_ASSERT_FAILED(m_apiversion > 0) {
58 0 : m_apiversion = -1;
59 : }
60 : }
61 0 : return m_apiversion;
62 : }
63 :
64 0 : QString ScoreElement::name() const
65 : {
66 0 : return QString(e->typeName());
67 : }
68 :
69 0 : QJSValue ScoreElement::type() const
70 : {
71 0 : if (apiversion() == 1) {
72 0 : return QJSValue(int(e->type()));
73 : } else {
74 0 : static const QMetaEnum meta = QMetaEnum::fromType<enums::ElementType>();
75 0 : return QJSValue(QString(meta.valueToKey(int(e->type()))));
76 : }
77 : }
78 :
79 : //---------------------------------------------------------
80 : // ScoreElement::userName
81 : /// \brief Human-readable element type name
82 : /// \returns Name of the element type, translated
83 : /// according to the current MuseScore locale settings.
84 : //---------------------------------------------------------
85 :
86 0 : QString ScoreElement::userName() const
87 : {
88 0 : return e->translatedTypeUserName();
89 : }
90 :
91 : //---------------------------------------------------------
92 : // ScoreElement::spatium
93 : //---------------------------------------------------------
94 :
95 0 : qreal ScoreElement::spatium() const
96 : {
97 0 : return e->isEngravingItem() ? toEngravingItem(e)->spatium() : e->score()->style().spatium();
98 : }
99 :
100 : //---------------------------------------------------------
101 : // ScoreElement::get
102 : /// Returns an engraving property converted to a plugin-readable value.
103 : //---------------------------------------------------------
104 :
105 8 : QVariant ScoreElement::get(mu::engraving::Pid pid) const
106 : {
107 8 : if (!e) {
108 0 : return QVariant();
109 : }
110 8 : const PropertyValue val = e->getProperty(pid);
111 8 : switch (val.type()) {
112 0 : case P_TYPE::FRACTION: {
113 0 : const mu::engraving::Fraction f(val.value<mu::engraving::Fraction>());
114 0 : return QVariant::fromValue(wrap(f));
115 : }
116 0 : case P_TYPE::ORNAMENT_INTERVAL: {
117 0 : const OrnamentInterval o(val.value<OrnamentInterval>());
118 0 : return QVariant::fromValue(wrap(o));
119 : }
120 0 : case P_TYPE::POINT:
121 0 : return val.value<PointF>().toQPointF() / spatium();
122 0 : case P_TYPE::PAIR_REAL: {
123 0 : const PairF pair = val.value<PairF>();
124 0 : return QVariantList { pair.first, pair.second };
125 : }
126 0 : case P_TYPE::ABSOLUTE:
127 0 : return val.toReal() / spatium();
128 0 : case P_TYPE::SPATIUM:
129 0 : return val.value<Spatium>().val();
130 8 : default:
131 8 : break;
132 : }
133 8 : return val.toQVariant();
134 8 : }
135 :
136 0 : static mu::engraving::AlignH alignHFromApiValue(enums::Align apiValue)
137 : {
138 0 : switch (apiValue & enums::Align::HMASK) {
139 0 : case enums::Align::LEFT: return mu::engraving::AlignH::LEFT;
140 0 : case enums::Align::RIGHT: return mu::engraving::AlignH::RIGHT;
141 0 : case enums::Align::HCENTER: return mu::engraving::AlignH::HCENTER;
142 0 : default:
143 0 : break;
144 : }
145 :
146 0 : return mu::engraving::AlignH::LEFT;
147 : }
148 :
149 0 : static mu::engraving::AlignV alignVFromApiValue(enums::Align apiValue)
150 : {
151 0 : switch (apiValue & enums::Align::VMASK) {
152 0 : case enums::Align::TOP: return mu::engraving::AlignV::TOP;
153 0 : case enums::Align::BOTTOM: return mu::engraving::AlignV::BOTTOM;
154 0 : case enums::Align::VCENTER: return mu::engraving::AlignV::VCENTER;
155 0 : case enums::Align::BASELINE: return mu::engraving::AlignV::BASELINE;
156 0 : default:
157 0 : break;
158 : }
159 :
160 0 : return mu::engraving::AlignV::TOP;
161 : }
162 :
163 : //---------------------------------------------------------
164 : // ScoreElement::set
165 : //---------------------------------------------------------
166 :
167 6 : void ScoreElement::set(mu::engraving::Pid pid, const QVariant& val)
168 : {
169 6 : if (!e) {
170 0 : return;
171 : }
172 :
173 6 : PropertyValue newValue;
174 :
175 6 : switch (propertyType(pid)) {
176 0 : case P_TYPE::FRACTION: {
177 0 : Fraction* f = val.value<Fraction*>();
178 0 : if (!f) {
179 0 : LOGW() << "trying to assign value of wrong type to fractional property";
180 0 : return;
181 : }
182 : // Pid::TIMESIG_ACTUAL is only set when we change the time signature,
183 : // aside from that it's read-only. What the user intends to do here is
184 : // change the actual length of the measure, so we do that instead.
185 0 : if (pid == Pid::TIMESIG_ACTUAL && e->isMeasure() && m_ownership == Ownership::SCORE) {
186 0 : mu::engraving::Measure* m = toMeasure(e);
187 0 : if (m->ticks() != f->fraction()) {
188 0 : mu::engraving::ScoreRange range;
189 0 : range.read(m->first(), m->last());
190 0 : m->adjustToLen(f->fraction());
191 0 : }
192 0 : return;
193 : }
194 0 : newValue = f->fraction();
195 : }
196 0 : break;
197 0 : case P_TYPE::ORNAMENT_INTERVAL: {
198 0 : OrnamentIntervalWrapper* o = val.value<OrnamentIntervalWrapper*>();
199 0 : if (!o) {
200 0 : LOGW() << "trying to assign value of wrong type to fractional property";
201 0 : return;
202 : }
203 0 : newValue = o->ornamentInterval();
204 : }
205 0 : break;
206 0 : case P_TYPE::POINT:
207 0 : newValue = PointF::fromQPointF(val.toPointF() * spatium());
208 0 : break;
209 0 : case P_TYPE::ABSOLUTE:
210 0 : newValue = val.toReal() * spatium();
211 0 : break;
212 0 : case P_TYPE::ALIGN: {
213 0 : apiv1::enums::Align apiValue = apiv1::enums::Align(val.toInt());
214 :
215 0 : newValue = Align { alignHFromApiValue(apiValue), alignVFromApiValue(apiValue) };
216 0 : } break;
217 :
218 6 : default:
219 6 : newValue = PropertyValue::fromQVariant(val, propertyType(pid));
220 6 : break;
221 : }
222 :
223 6 : const PropertyFlags f = e->propertyFlags(pid);
224 6 : const PropertyFlags newFlags = (f == PropertyFlags::NOSTYLE) ? f : PropertyFlags::UNSTYLED;
225 :
226 6 : if (m_ownership == Ownership::SCORE) {
227 6 : e->undoChangeProperty(pid, newValue, newFlags);
228 : } else { // not added to a score so no need (and dangerous) to deal with undo stack
229 0 : e->setProperty(pid, newValue);
230 0 : e->setPropertyFlags(pid, newFlags);
231 : }
232 6 : }
233 :
234 0 : void ScoreElement::reset(mu::engraving::Pid pid)
235 : {
236 0 : if (!e) {
237 0 : return;
238 : }
239 :
240 0 : if (m_ownership == Ownership::SCORE) {
241 0 : e->undoResetProperty(pid);
242 : } else {
243 0 : e->resetProperty(pid);
244 : }
245 : }
246 :
247 : //---------------------------------------------------------
248 : // wrap
249 : /// \cond PLUGIN_API \private \endcond
250 : /// Wraps mu::engraving::ScoreElement choosing the correct wrapper
251 : /// type at runtime based on the actual element type.
252 : //---------------------------------------------------------
253 :
254 0 : ScoreElement* mu::engraving::apiv1::wrap(mu::engraving::EngravingObject* se, Ownership own)
255 : {
256 0 : if (!se) {
257 0 : return nullptr;
258 : }
259 0 : if (se->isEngravingItem()) {
260 0 : return wrap(toEngravingItem(se), own);
261 : }
262 :
263 : using mu::engraving::ElementType;
264 0 : switch (se->type()) {
265 0 : case ElementType::SCORE:
266 0 : return wrap<mu::engraving::apiv1::Score>(toScore(se), own);
267 0 : case ElementType::PART:
268 0 : return wrap<mu::engraving::apiv1::Part>(toPart(se), own);
269 0 : case ElementType::STAFF:
270 0 : return wrap<mu::engraving::apiv1::Staff>(toStaff(se), own);
271 0 : default:
272 0 : break;
273 : }
274 0 : return wrap<ScoreElement>(se, own);
275 : }
|