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 : #include "palettecell.h"
23 : #include "palettecompat.h"
24 :
25 : #include "mimedatautils.h"
26 :
27 : #include "engraving/dom/actionicon.h"
28 : #include "engraving/dom/engravingitem.h"
29 : #include "engraving/dom/factory.h"
30 : #include "engraving/dom/fret.h"
31 : #include "engraving/dom/masterscore.h"
32 : #include "engraving/dom/textbase.h"
33 : #include "engraving/dom/tremolosinglechord.h"
34 : #include "engraving/dom/tremolotwochord.h"
35 :
36 : #include "engraving/rw/rwregister.h"
37 : #include "engraving/rw/compat/tremolocompat.h"
38 :
39 : #include "widgets/palettewidget.h"
40 :
41 : #include "log.h"
42 : #include "translation.h"
43 :
44 : using namespace muse;
45 : using namespace mu::palette;
46 : using namespace mu::engraving;
47 :
48 0 : static bool needsStaff(ElementPtr e)
49 : {
50 0 : if (!e) {
51 0 : return false;
52 : }
53 0 : switch (e->type()) {
54 0 : case ElementType::CHORD:
55 : case ElementType::BAR_LINE:
56 : case ElementType::CLEF:
57 : case ElementType::KEYSIG:
58 : case ElementType::TIMESIG:
59 : case ElementType::REST:
60 : case ElementType::BAGPIPE_EMBELLISHMENT:
61 0 : return true;
62 0 : default:
63 0 : return false;
64 : }
65 : }
66 :
67 0 : PaletteCell::PaletteCell(const muse::modularity::ContextPtr& iocCtx, QObject* parent)
68 0 : : QObject(parent), muse::Contextable(iocCtx)
69 : {
70 0 : id = makeId();
71 0 : }
72 :
73 0 : PaletteCell::PaletteCell(const muse::modularity::ContextPtr& iocCtx, ElementPtr e, const QString& _name, qreal _mag, const QPointF& _offset,
74 0 : const QString& _tag, QObject* parent)
75 0 : : QObject(parent), muse::Contextable(iocCtx), element(e), name(_name), mag(_mag), xoffset(_offset.x()), yoffset(_offset.y()), tag(_tag)
76 : {
77 0 : id = makeId();
78 0 : drawStaff = needsStaff(element);
79 0 : }
80 :
81 0 : QAccessibleInterface* PaletteCell::accessibleInterface(QObject* object)
82 : {
83 0 : PaletteCell* cell = qobject_cast<PaletteCell*>(object);
84 0 : IF_ASSERT_FAILED(cell) {
85 0 : return nullptr;
86 : }
87 :
88 0 : return static_cast<QAccessibleInterface*>(new AccessiblePaletteCellInterface(cell));
89 : }
90 :
91 0 : QString PaletteCell::makeId()
92 : {
93 : static unsigned int id = 0;
94 0 : return QString::number(++id);
95 : }
96 :
97 0 : const char* PaletteCell::translationContext() const
98 : {
99 : // Keep in sync with PaletteCreator and all element types
100 0 : const ElementType type = element ? element->type() : ElementType::INVALID;
101 0 : switch (type) {
102 0 : case ElementType::ACTION_ICON:
103 0 : return "action";
104 0 : case ElementType::ARPEGGIO:
105 : case ElementType::CHORD_BRACKET:
106 : case ElementType::CHORDLINE:
107 : case ElementType::GLISSANDO:
108 : case ElementType::HARMONY:
109 : case ElementType::JUMP:
110 : case ElementType::KEYSIG:
111 : case ElementType::MARKER:
112 0 : return "engraving";
113 0 : case ElementType::BAGPIPE_EMBELLISHMENT:
114 0 : return "engraving/bagpipeembellishment";
115 0 : case ElementType::CLEF:
116 0 : return "engraving/cleftype";
117 0 : case ElementType::DYNAMIC:
118 0 : return "engraving/dynamictype";
119 0 : case ElementType::HAIRPIN:
120 0 : return "engraving/hairpintype";
121 0 : case ElementType::LAYOUT_BREAK:
122 0 : return "engraving/layoutbreaktype";
123 0 : case ElementType::NOTEHEAD:
124 0 : return "engraving/noteheadgroup";
125 0 : case ElementType::OTTAVA:
126 0 : return "engraving/ottavatype";
127 0 : case ElementType::SPACER:
128 0 : return "engraving/spacertype";
129 0 : case ElementType::ACCIDENTAL:
130 : case ElementType::ARTICULATION:
131 : case ElementType::BAR_LINE:
132 : case ElementType::BREATH:
133 : case ElementType::FERMATA:
134 : case ElementType::MEASURE_REPEAT:
135 : case ElementType::ORNAMENT:
136 : case ElementType::SYMBOL:
137 0 : return "engraving/sym";
138 0 : case ElementType::PLAYTECH_ANNOTATION:
139 0 : return "engraving/playtechtype";
140 0 : case ElementType::TIMESIG:
141 0 : return "engraving/timesig";
142 0 : case ElementType::TREMOLO_SINGLECHORD:
143 : case ElementType::TREMOLO_TWOCHORD:
144 0 : return "engraving/tremolotype";
145 0 : case ElementType::TRILL:
146 0 : return "engraving/trilltype";
147 0 : case ElementType::TRIPLET_FEEL:
148 0 : return "engraving/tripletfeel";
149 0 : case ElementType::VIBRATO:
150 0 : return "engraving/vibratotype";
151 0 : default:
152 0 : break;
153 : }
154 0 : return "palette";
155 : }
156 :
157 0 : QString PaletteCell::translatedName() const
158 : {
159 0 : const QString trName = muse::qtrc(translationContext(), name.toUtf8());
160 :
161 0 : if (element && element->isTextBase() && name.contains("%1")) {
162 0 : return trName.arg(toTextBase(element.get())->plainText());
163 : }
164 0 : return trName;
165 0 : }
166 :
167 : /// Retranslates cell content, e.g. text if the element is TextBase.
168 0 : void PaletteCell::retranslate()
169 : {
170 0 : if (untranslatedElement && element->isTextBase()) {
171 0 : TextBase* target = toTextBase(element.get());
172 0 : TextBase* orig = toTextBase(untranslatedElement.get());
173 0 : const QString& text = orig->xmlText();
174 0 : target->setXmlText(muse::qtrc("palette", text.toUtf8().constData()));
175 0 : }
176 0 : }
177 :
178 0 : void PaletteCell::setElementTranslated(bool translate)
179 : {
180 0 : if (translate && element) {
181 0 : untranslatedElement = element;
182 0 : element.reset(untranslatedElement->clone());
183 0 : retranslate();
184 : } else {
185 0 : untranslatedElement.reset();
186 : }
187 0 : }
188 :
189 0 : bool PaletteCell::read(XmlReader& e, bool pasteMode)
190 : {
191 : UNUSED(pasteMode);
192 :
193 0 : bool add = true;
194 0 : name = e.attribute("name");
195 :
196 : // using attributes instead of nested tags for
197 : // pre-3.3 version compatibility
198 0 : custom = e.hasAttribute("custom") ? e.intAttribute("custom") : false; // TODO: actually check master palette?
199 0 : visible = e.hasAttribute("visible") ? e.intAttribute("visible") : true;
200 :
201 0 : const bool translateElement = e.hasAttribute("trElement") ? e.intAttribute("trElement") : false;
202 :
203 0 : while (e.readNextStartElement()) {
204 0 : const AsciiStringView s(e.name());
205 0 : if (s == "staff") {
206 0 : drawStaff = e.readInt();
207 0 : } else if (s == "xoffset") {
208 0 : xoffset = e.readDouble();
209 0 : } else if (s == "yoffset") {
210 0 : yoffset = e.readDouble();
211 0 : } else if (s == "mag") {
212 0 : mag = e.readDouble();
213 0 : } else if (s == "tag") {
214 0 : tag = e.readText();
215 : }
216 : // added on palettes rework
217 : // TODO: remove or leave to switch from using attributes later?
218 0 : else if (s == "custom") {
219 0 : custom = e.readBool();
220 0 : } else if (s == "visible") {
221 0 : visible = e.readBool();
222 0 : } else if (s == "Tremolo") {
223 0 : Score* paletteScore = paletteScoreProvider()->paletteScore();
224 0 : compat::TremoloCompat tc { paletteScore->dummy() };
225 0 : rw::RWRegister::reader(paletteScore->mscVersion())->readTremoloCompat(&tc, e);
226 0 : if (tc.single) {
227 0 : element.reset(tc.single);
228 0 : } else if (tc.two) {
229 0 : element.reset(tc.two);
230 : } else {
231 0 : UNREACHABLE;
232 : }
233 :
234 0 : if (element) {
235 0 : element->styleChanged();
236 : }
237 : } else {
238 0 : element.reset(Factory::createItemByName(s, paletteScoreProvider()->paletteScore()->dummy()));
239 0 : if (!element) {
240 0 : e.unknown();
241 : } else {
242 0 : rw::RWRegister::reader(paletteScoreProvider()->paletteScore()->mscVersion())->readItem(element.get(), e);
243 : }
244 : }
245 : }
246 :
247 0 : setElementTranslated(translateElement);
248 :
249 0 : if (element) {
250 0 : PaletteCompat::migrateOldPaletteCellIfNeeded(this, paletteScoreProvider()->paletteScore());
251 0 : element->styleChanged();
252 : }
253 :
254 0 : return add && element;
255 : }
256 :
257 0 : void PaletteCell::write(XmlWriter& xml, bool pasteMode) const
258 : {
259 : UNUSED(pasteMode);
260 :
261 0 : if (!element) {
262 0 : xml.tag("Cell");
263 0 : return;
264 : }
265 :
266 : // using attributes for `custom` and `visible` properties instead of nested tags
267 : // for pre-3.3 version compatibility
268 0 : XmlWriter::Attributes cellAttrs;
269 0 : if (!name.isEmpty()) {
270 0 : cellAttrs.push_back({ "name", name });
271 : }
272 0 : if (custom) {
273 0 : cellAttrs.push_back({ "custom", "1" });
274 : }
275 0 : if (!visible) {
276 0 : cellAttrs.push_back({ "visible", "0" });
277 : }
278 :
279 0 : if (untranslatedElement) {
280 0 : cellAttrs.push_back({ "trElement", "1" });
281 : }
282 :
283 0 : xml.startElement("Cell", cellAttrs);
284 :
285 0 : if (drawStaff) {
286 0 : xml.tag("staff", drawStaff);
287 : }
288 0 : if (xoffset) {
289 0 : xml.tag("xoffset", xoffset);
290 : }
291 0 : if (yoffset) {
292 0 : xml.tag("yoffset", yoffset);
293 : }
294 0 : if (!tag.isEmpty()) {
295 0 : xml.tag("tag", tag);
296 : }
297 0 : if (!RealIsEqual(mag, 1.0)) {
298 0 : xml.tag("mag", mag);
299 : }
300 :
301 0 : if (untranslatedElement) {
302 0 : rw::RWRegister::writer()->writeItem(untranslatedElement.get(), xml);
303 : } else {
304 0 : rw::RWRegister::writer()->writeItem(element.get(), xml);
305 : }
306 0 : xml.endElement();
307 0 : }
308 :
309 0 : PaletteCellPtr PaletteCell::fromMimeData(const QByteArray& data, const muse::modularity::ContextPtr& iocCtx)
310 : {
311 0 : return ::fromMimeData<PaletteCell>(data, "Cell", iocCtx);
312 : }
313 :
314 0 : PaletteCellPtr PaletteCell::fromElementMimeData(const QByteArray& data, const muse::modularity::ContextPtr& iocCtx)
315 : {
316 0 : PointF dragOffset;
317 0 : Fraction duration(1, 4);
318 0 : muse::ContextInject<engraving::IPaletteScoreProvider> paletteScoreProvider = { iocCtx };
319 0 : ElementPtr element(EngravingItem::readMimeData(paletteScoreProvider()->paletteScore(),
320 0 : muse::ByteArray::fromQByteArrayNoCopy(data),
321 0 : &dragOffset, &duration));
322 :
323 0 : if (!element) {
324 0 : return nullptr;
325 : }
326 :
327 0 : if (!element->isSymbol()) { // not sure this check is necessary, it was so in the old palette
328 0 : element->setTrack(0);
329 : }
330 :
331 0 : const String name = (element->isFretDiagram()) ? toFretDiagram(element.get())->harmonyPlainText() : element->translatedTypeUserName();
332 :
333 0 : return std::make_shared<PaletteCell>(iocCtx, element, name);
334 0 : }
335 :
336 0 : QByteArray PaletteCell::toMimeData() const
337 : {
338 0 : return ::toMimeData(this);
339 : }
340 :
341 0 : AccessiblePaletteCellInterface::AccessiblePaletteCellInterface(PaletteCell* cell)
342 : {
343 0 : m_cell = cell;
344 0 : }
345 :
346 0 : bool AccessiblePaletteCellInterface::isValid() const
347 : {
348 0 : return m_cell != nullptr;
349 : }
350 :
351 0 : QObject* AccessiblePaletteCellInterface::object() const
352 : {
353 0 : return m_cell;
354 : }
355 :
356 0 : QAccessibleInterface* AccessiblePaletteCellInterface::childAt(int, int) const
357 : {
358 0 : return nullptr;
359 : }
360 :
361 0 : QAccessibleInterface* AccessiblePaletteCellInterface::parent() const
362 : {
363 0 : auto paletteWidget = parentWidget();
364 0 : if (!paletteWidget) {
365 0 : return nullptr;
366 : }
367 :
368 0 : return QAccessible::queryAccessibleInterface(paletteWidget);
369 : }
370 :
371 0 : QAccessibleInterface* AccessiblePaletteCellInterface::child(int) const
372 : {
373 0 : return nullptr;
374 : }
375 :
376 0 : int AccessiblePaletteCellInterface::childCount() const
377 : {
378 0 : return 0;
379 : }
380 :
381 0 : int AccessiblePaletteCellInterface::indexOfChild(const QAccessibleInterface*) const
382 : {
383 0 : return -1;
384 : }
385 :
386 0 : QString AccessiblePaletteCellInterface::text(QAccessible::Text t) const
387 : {
388 0 : switch (t) {
389 0 : case QAccessible::Text::Name:
390 0 : return m_cell->element->accessibleInfo();
391 0 : default:
392 0 : break;
393 : }
394 :
395 0 : return QString();
396 : }
397 :
398 0 : void AccessiblePaletteCellInterface::setText(QAccessible::Text, const QString&)
399 : {
400 0 : }
401 :
402 0 : QRect AccessiblePaletteCellInterface::rect() const
403 : {
404 0 : return QRect();
405 : }
406 :
407 0 : QAccessible::Role AccessiblePaletteCellInterface::role() const
408 : {
409 0 : return QAccessible::ListItem;
410 : }
411 :
412 0 : QAccessible::State AccessiblePaletteCellInterface::state() const
413 : {
414 0 : QAccessible::State state;
415 0 : state.invisible = false;
416 0 : state.invalid = false;
417 0 : state.disabled = false;
418 :
419 0 : state.focusable = true;
420 0 : state.focused = m_cell->focused;
421 :
422 0 : return state;
423 : }
424 :
425 0 : QObject* AccessiblePaletteCellInterface::parentWidget() const
426 : {
427 0 : auto palette = m_cell->parent();
428 0 : if (!palette) {
429 0 : return nullptr;
430 : }
431 :
432 0 : return palette->parent();
433 : }
|