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 "elementrepositoryservice.h"
23 :
24 : #include "qml/MuseScore/PropertiesPanel/types/texttypes.h"
25 :
26 : #include "engraving/dom/barline.h"
27 : #include "engraving/dom/beam.h"
28 : #include "engraving/dom/bracket.h"
29 : #include "engraving/dom/bracketitem.h"
30 : #include "engraving/dom/chord.h"
31 : #include "engraving/dom/durationtype.h"
32 : #include "engraving/dom/glissando.h"
33 : #include "engraving/dom/hairpin.h"
34 : #include "engraving/dom/hook.h"
35 : #include "engraving/dom/layoutbreak.h"
36 : #include "engraving/dom/lyrics.h"
37 : #include "engraving/dom/mscore.h"
38 : #include "engraving/dom/note.h"
39 : #include "engraving/dom/note.h"
40 : #include "engraving/dom/pedal.h"
41 : #include "engraving/dom/playcounttext.h"
42 : #include "engraving/dom/segment.h"
43 : #include "engraving/dom/staff.h"
44 : #include "engraving/dom/stafftype.h"
45 : #include "engraving/dom/stem.h"
46 : #include "engraving/dom/text.h"
47 : #include "engraving/dom/trill.h"
48 : #include "engraving/dom/volta.h"
49 :
50 : #include "log.h"
51 :
52 : using namespace mu::propertiespanel;
53 : using namespace mu::engraving;
54 :
55 0 : bool ElementRepositoryService::needUpdateElementList(const QList<EngravingItem*>& newRawElementList,
56 : SelState selectionState) const
57 : {
58 0 : return m_rawElementList != newRawElementList || m_selectionState != selectionState;
59 : }
60 :
61 0 : void ElementRepositoryService::updateElementList(const QList<EngravingItem*>& newRawElementList,
62 : SelState selectionState)
63 : {
64 0 : if (!needUpdateElementList(newRawElementList, selectionState)) {
65 0 : return;
66 : }
67 :
68 0 : m_exposedElementList = exposeRawElements(newRawElementList);
69 0 : m_rawElementList = newRawElementList;
70 0 : m_selectionState = selectionState;
71 :
72 0 : m_elementsUpdated.send(m_rawElementList);
73 : }
74 :
75 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findElementsByType(const mu::engraving::ElementType elementType) const
76 : {
77 0 : switch (elementType) {
78 0 : case mu::engraving::ElementType::CHORD: return findChords();
79 0 : case mu::engraving::ElementType::NOTE: return findNotes();
80 0 : case mu::engraving::ElementType::NOTEHEAD: return findNoteHeads();
81 0 : case mu::engraving::ElementType::STEM: return findStems();
82 0 : case mu::engraving::ElementType::HOOK: return findHooks();
83 0 : case mu::engraving::ElementType::BEAM: return findBeams();
84 0 : case mu::engraving::ElementType::STAFF: return findStaffs();
85 0 : case mu::engraving::ElementType::LAYOUT_BREAK: return findSectionBreaks(); //Page breaks and line breaks are of type LAYOUT_BREAK, but they don't appear in the propertiespanel for now.
86 0 : case mu::engraving::ElementType::TEXT: return findTexts();
87 0 : case mu::engraving::ElementType::BRACKET: return findBrackets();
88 0 : case mu::engraving::ElementType::REST: return findRests();
89 0 : case mu::engraving::ElementType::ORNAMENT: return findOrnaments();
90 0 : case mu::engraving::ElementType::LYRICS: return findLyrics();
91 0 : case mu::engraving::ElementType::PEDAL:
92 : case mu::engraving::ElementType::GLISSANDO:
93 : case mu::engraving::ElementType::VIBRATO:
94 : case mu::engraving::ElementType::HAIRPIN:
95 : case mu::engraving::ElementType::VOLTA:
96 : case mu::engraving::ElementType::LET_RING:
97 : case mu::engraving::ElementType::OTTAVA:
98 : case mu::engraving::ElementType::TEXTLINE:
99 : case mu::engraving::ElementType::TEXTLINE_BASE:
100 : case mu::engraving::ElementType::NOTELINE:
101 : case mu::engraving::ElementType::SLUR:
102 : case mu::engraving::ElementType::HAMMER_ON_PULL_OFF:
103 : case mu::engraving::ElementType::TIE:
104 : case mu::engraving::ElementType::LAISSEZ_VIB:
105 : case mu::engraving::ElementType::PARTIAL_TIE:
106 : case mu::engraving::ElementType::GRADUAL_TEMPO_CHANGE:
107 : case mu::engraving::ElementType::LYRICSLINE:
108 : case mu::engraving::ElementType::PARTIAL_LYRICSLINE:
109 : case mu::engraving::ElementType::WHAMMY_BAR:
110 : case mu::engraving::ElementType::PALM_MUTE:
111 0 : return findLines(elementType);
112 0 : default:
113 0 : QList<mu::engraving::EngravingItem*> resultList;
114 :
115 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
116 0 : if (element->type() == elementType) {
117 0 : resultList << element;
118 : }
119 : }
120 :
121 0 : return resultList;
122 : }
123 : }
124 :
125 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findElementsByType(const mu::engraving::ElementType elementType,
126 : std::function<bool(const mu::engraving::EngravingItem*)> filterFunc)
127 : const
128 : {
129 0 : QList<mu::engraving::EngravingItem*> resultList;
130 :
131 0 : QList<mu::engraving::EngravingItem*> unfilteredList = findElementsByType(elementType);
132 :
133 0 : for (mu::engraving::EngravingItem* element : unfilteredList) {
134 0 : if (filterFunc(element)) {
135 0 : resultList << element;
136 : }
137 : }
138 :
139 0 : return resultList;
140 0 : }
141 :
142 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::takeAllElements() const
143 : {
144 0 : return m_exposedElementList;
145 : }
146 :
147 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::exposeRawElements(const QList<mu::engraving::EngravingItem*>& rawElementList)
148 : const
149 : {
150 0 : QList<mu::engraving::EngravingItem*> resultList;
151 :
152 0 : for (mu::engraving::EngravingItem* element : rawElementList) {
153 0 : mu::engraving::ElementType elementType = element->type();
154 :
155 : //! NOTE: instrument names can't survive the layout process,
156 : //! so we have to exclude them from the list to prevent
157 : //! crashes on invalid pointers in the propertiespanel
158 0 : if (elementType == mu::engraving::ElementType::INSTRUMENT_NAME) {
159 0 : continue;
160 : }
161 :
162 0 : if (elementType == mu::engraving::ElementType::BRACKET) {
163 0 : resultList << mu::engraving::toBracket(element)->bracketItem();
164 0 : continue;
165 : }
166 :
167 0 : resultList << element;
168 : }
169 :
170 0 : return resultList;
171 0 : }
172 :
173 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findChords() const
174 : {
175 0 : QSet<mu::engraving::EngravingItem*> elements;
176 :
177 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
178 0 : if (element->type() == mu::engraving::ElementType::CHORD) {
179 0 : elements << element;
180 0 : continue;
181 : }
182 :
183 0 : if (element->type() == mu::engraving::ElementType::BEAM) {
184 0 : const mu::engraving::Beam* beam = mu::engraving::toBeam(element);
185 :
186 0 : for (mu::engraving::ChordRest* chordRest : beam->elements()) {
187 0 : if (!chordRest->isChord()) {
188 0 : continue;
189 : }
190 0 : elements << chordRest;
191 : }
192 0 : continue;
193 0 : }
194 :
195 0 : mu::engraving::EngravingItem* chord = element->findAncestor(mu::engraving::ElementType::CHORD);
196 0 : if (chord) {
197 0 : elements << chord;
198 : }
199 : }
200 :
201 0 : return QList<mu::engraving::EngravingItem*>(elements.begin(), elements.end());
202 0 : }
203 :
204 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findNotes() const
205 : {
206 0 : QList<mu::engraving::EngravingItem*> result;
207 :
208 0 : for (engraving::EngravingItem* element : m_rawElementList) {
209 0 : if (element->isNote()) {
210 0 : result << element;
211 0 : continue;
212 : }
213 :
214 0 : engraving::EngravingItem* elementBase = element->elementBase();
215 :
216 0 : if (elementBase->isChord()) {
217 0 : engraving::Chord* chord = engraving::toChord(elementBase);
218 :
219 0 : for (mu::engraving::Note* note : chord->notes()) {
220 0 : result << note;
221 : }
222 0 : } else if (elementBase->isBeam()) {
223 0 : const mu::engraving::Beam* beam = mu::engraving::toBeam(elementBase);
224 :
225 0 : for (mu::engraving::ChordRest* chordRest : beam->elements()) {
226 0 : if (!chordRest->isChord()) {
227 0 : continue;
228 : }
229 :
230 0 : for (mu::engraving::Note* note : engraving::toChord(chordRest)->notes()) {
231 0 : result << note;
232 : }
233 : }
234 : }
235 : }
236 :
237 0 : return result;
238 0 : }
239 :
240 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findNoteHeads() const
241 : {
242 0 : QList<mu::engraving::EngravingItem*> resultList;
243 :
244 0 : for (mu::engraving::EngravingItem* element : m_rawElementList) {
245 0 : if (element->isNote()) {
246 0 : resultList << element;
247 : }
248 : }
249 :
250 0 : return resultList;
251 0 : }
252 :
253 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findStems() const
254 : {
255 0 : QList<mu::engraving::EngravingItem*> resultList;
256 :
257 0 : for (const mu::engraving::EngravingItem* element : findChords()) {
258 0 : const mu::engraving::Chord* chord = mu::engraving::toChord(element);
259 :
260 0 : if (chord && chord->stem()) {
261 0 : resultList << chord->stem();
262 : }
263 0 : }
264 :
265 0 : return resultList;
266 0 : }
267 :
268 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findHooks() const
269 : {
270 0 : QList<mu::engraving::EngravingItem*> resultList;
271 :
272 0 : for (const mu::engraving::EngravingItem* element : findChords()) {
273 0 : const mu::engraving::Chord* chord = mu::engraving::toChord(element);
274 :
275 0 : if (chord && chord->hook()) {
276 0 : resultList << chord->hook();
277 : }
278 0 : }
279 :
280 0 : return resultList;
281 0 : }
282 :
283 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findBeams() const
284 : {
285 0 : QList<mu::engraving::EngravingItem*> resultList;
286 :
287 0 : for (const mu::engraving::EngravingItem* element : findChords()) {
288 0 : mu::engraving::EngravingItem* beam = nullptr;
289 :
290 0 : if (element->isChord()) {
291 0 : const mu::engraving::Chord* chord = mu::engraving::toChord(element);
292 :
293 0 : if (!chord) {
294 0 : continue;
295 : }
296 :
297 0 : beam = chord->beam();
298 0 : } else if (element->isBeam()) {
299 0 : beam = const_cast<mu::engraving::EngravingItem*>(element);
300 : }
301 :
302 0 : if (!beam || resultList.contains(beam)) {
303 0 : continue;
304 : }
305 :
306 0 : resultList << beam;
307 0 : }
308 :
309 0 : return resultList;
310 0 : }
311 :
312 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findLines(mu::engraving::ElementType lineType) const
313 : {
314 : static const QMap<mu::engraving::ElementType, mu::engraving::ElementType> lineTypeToSegmentType {
315 : { mu::engraving::ElementType::GLISSANDO, mu::engraving::ElementType::GLISSANDO_SEGMENT },
316 : { mu::engraving::ElementType::VIBRATO, mu::engraving::ElementType::VIBRATO_SEGMENT },
317 : { mu::engraving::ElementType::PEDAL, mu::engraving::ElementType::PEDAL_SEGMENT },
318 : { mu::engraving::ElementType::HAIRPIN, mu::engraving::ElementType::HAIRPIN_SEGMENT },
319 : { mu::engraving::ElementType::VOLTA, mu::engraving::ElementType::VOLTA_SEGMENT },
320 : { mu::engraving::ElementType::LET_RING, mu::engraving::ElementType::LET_RING_SEGMENT },
321 : { mu::engraving::ElementType::PALM_MUTE, mu::engraving::ElementType::PALM_MUTE_SEGMENT },
322 : { mu::engraving::ElementType::OTTAVA, mu::engraving::ElementType::OTTAVA_SEGMENT },
323 : { mu::engraving::ElementType::TEXTLINE, mu::engraving::ElementType::TEXTLINE_SEGMENT },
324 : { mu::engraving::ElementType::TEXTLINE_BASE, mu::engraving::ElementType::TEXTLINE_SEGMENT },
325 : { mu::engraving::ElementType::NOTELINE, mu::engraving::ElementType::NOTELINE_SEGMENT },
326 : { mu::engraving::ElementType::SLUR, mu::engraving::ElementType::SLUR_SEGMENT },
327 : { mu::engraving::ElementType::HAMMER_ON_PULL_OFF, mu::engraving::ElementType::HAMMER_ON_PULL_OFF_SEGMENT },
328 : { mu::engraving::ElementType::TIE, mu::engraving::ElementType::TIE_SEGMENT },
329 : { mu::engraving::ElementType::LAISSEZ_VIB, mu::engraving::ElementType::LAISSEZ_VIB_SEGMENT },
330 : { mu::engraving::ElementType::PARTIAL_TIE, mu::engraving::ElementType::PARTIAL_TIE_SEGMENT },
331 : { mu::engraving::ElementType::GRADUAL_TEMPO_CHANGE, mu::engraving::ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT },
332 : { mu::engraving::ElementType::LYRICSLINE, mu::engraving::ElementType::LYRICSLINE_SEGMENT },
333 : { mu::engraving::ElementType::PARTIAL_LYRICSLINE, mu::engraving::ElementType::PARTIAL_LYRICSLINE_SEGMENT }
334 0 : };
335 :
336 0 : QList<mu::engraving::EngravingItem*> resultList;
337 :
338 0 : IF_ASSERT_FAILED(lineTypeToSegmentType.contains(lineType)) {
339 0 : return resultList;
340 : }
341 :
342 0 : mu::engraving::ElementType segmentType = lineTypeToSegmentType[lineType];
343 :
344 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
345 0 : if (element->type() == segmentType || (lineType == ElementType::TEXTLINE_BASE && element->isTextLineBaseSegment())) {
346 0 : const mu::engraving::SpannerSegment* segment = mu::engraving::toSpannerSegment(element);
347 0 : mu::engraving::Spanner* line = segment ? segment->spanner() : nullptr;
348 :
349 0 : if (line) {
350 0 : resultList << line;
351 : }
352 0 : } else if (element->type() == lineType) {
353 0 : resultList << element;
354 : }
355 : }
356 :
357 0 : return resultList;
358 0 : }
359 :
360 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findStaffs() const
361 : {
362 0 : QList<mu::engraving::EngravingItem*> resultList;
363 :
364 0 : for (const mu::engraving::EngravingItem* element : m_exposedElementList) {
365 0 : if (!element->staff()) {
366 0 : continue;
367 : }
368 :
369 0 : resultList << element->staff();
370 : }
371 :
372 0 : return resultList;
373 0 : }
374 :
375 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findSectionBreaks() const
376 : {
377 0 : QList<mu::engraving::EngravingItem*> resultList;
378 :
379 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
380 0 : if (element && element->type() == mu::engraving::ElementType::LAYOUT_BREAK) {
381 0 : const mu::engraving::LayoutBreak* layoutBreak = mu::engraving::toLayoutBreak(element);
382 0 : if (layoutBreak->layoutBreakType() != mu::engraving::LayoutBreakType::SECTION) {
383 0 : continue;
384 : }
385 :
386 0 : resultList << element;
387 : }
388 : }
389 :
390 0 : return resultList;
391 0 : }
392 :
393 0 : std::vector<EngravingItem*> ElementRepositoryService::findTextDelegates(EngravingItem* element) const
394 : {
395 0 : if (element->isTextLineBaseSegment()) {
396 0 : std::vector<EngravingItem*> textItems;
397 0 : TextLineBase* tl = toTextLineBaseSegment(element)->textLineBase();
398 0 : for (SpannerSegment* seg : tl->spannerSegments()) {
399 0 : if (Text* text = toTextLineBaseSegment(seg)->text()) {
400 0 : textItems.push_back(text);
401 : }
402 :
403 0 : if (Text* endText = toTextLineBaseSegment(seg)->endText()) {
404 0 : textItems.push_back(endText);
405 : }
406 : }
407 :
408 0 : return textItems;
409 0 : }
410 :
411 0 : if (element->isBarLine()) {
412 0 : Segment* seg = toBarLine(element)->segment();
413 0 : if (PlayCountText* playCountText = toPlayCountText(seg->findAnnotation(ElementType::PLAY_COUNT_TEXT, 0, 0))) {
414 0 : return { playCountText };
415 : }
416 0 : return {};
417 : }
418 :
419 0 : if (TEXT_ELEMENT_TYPES.contains(element->type())) {
420 0 : return { element };
421 : }
422 :
423 0 : return {};
424 : }
425 :
426 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findTexts() const
427 : {
428 0 : QList<mu::engraving::EngravingItem*> resultList;
429 :
430 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
431 0 : std::vector<EngravingItem*> delegateItems = findTextDelegates(element);
432 0 : for (mu::engraving::EngravingItem* el : delegateItems) {
433 0 : resultList << el;
434 : }
435 0 : }
436 :
437 0 : return resultList;
438 0 : }
439 :
440 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findBrackets() const
441 : {
442 0 : QList<mu::engraving::EngravingItem*> resultList;
443 :
444 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
445 0 : if (element->isBracketItem()) {
446 0 : resultList << element;
447 : }
448 : }
449 :
450 0 : return resultList;
451 0 : }
452 :
453 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findRests() const
454 : {
455 0 : QList<mu::engraving::EngravingItem*> resultList;
456 :
457 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
458 0 : if (element->isRest()) {
459 0 : resultList << element;
460 0 : } else if (element->isBeam()) {
461 0 : const mu::engraving::Beam* beam = mu::engraving::toBeam(element);
462 :
463 0 : for (mu::engraving::ChordRest* chordRest : beam->elements()) {
464 0 : if (!chordRest->isRest()) {
465 0 : continue;
466 : }
467 0 : resultList << chordRest;
468 : }
469 : }
470 : }
471 :
472 0 : return resultList;
473 0 : }
474 :
475 0 : QList<mu::engraving::EngravingItem*> ElementRepositoryService::findOrnaments() const
476 : {
477 0 : QList<mu::engraving::EngravingItem*> resultList;
478 :
479 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
480 0 : if (element->isOrnament()) {
481 0 : resultList << element;
482 0 : } else if (element->isTrill()) {
483 0 : resultList << (EngravingItem*)(toTrill(element)->ornament());
484 0 : } else if (element->isTrillSegment()) {
485 0 : resultList << (EngravingItem*)(toTrillSegment(element)->trill()->ornament());
486 : }
487 : }
488 :
489 0 : return resultList;
490 0 : }
491 :
492 0 : QList<EngravingItem*> ElementRepositoryService::findLyrics() const
493 : {
494 0 : QList<mu::engraving::EngravingItem*> resultList;
495 0 : for (mu::engraving::EngravingItem* element : m_exposedElementList) {
496 0 : if (element->isLyrics()) {
497 0 : resultList << element;
498 : }
499 : }
500 :
501 0 : return resultList;
502 0 : }
503 :
504 0 : muse::async::Channel<QList<EngravingItem*> > ElementRepositoryService::elementsUpdated() const
505 : {
506 0 : return m_elementsUpdated;
507 : }
|