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) 2026 MuseScore Limited
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 "editvisibility.h"
24 :
25 : #include "global/containers.h"
26 :
27 : #include "../dom/accidental.h"
28 : #include "../dom/beam.h"
29 : #include "../dom/chord.h"
30 : #include "../dom/engravingitem.h"
31 : #include "../dom/note.h"
32 : #include "../dom/notedot.h"
33 : #include "../dom/ornament.h"
34 : #include "../dom/rest.h"
35 : #include "../dom/score.h"
36 : #include "../dom/select.h"
37 : #include "../dom/trill.h"
38 :
39 : #include "editproperty.h"
40 : #include "transaction/transaction.h"
41 :
42 : using namespace mu::engraving;
43 :
44 0 : void EditVisibility::setSelectedElementsVisible(Transaction& tx, Score* score)
45 : {
46 0 : for (EngravingItem* e : score->selection().elements()) {
47 0 : tx.push(new ChangeProperty(e, Pid::VISIBLE, true));
48 : }
49 0 : }
50 :
51 0 : void EditVisibility::setSelectedElementsInvisible(Transaction& tx, Score* score)
52 : {
53 0 : for (EngravingItem* e : score->selection().elements()) {
54 0 : tx.push(new ChangeProperty(e, Pid::VISIBLE, false));
55 : }
56 0 : }
57 :
58 4 : void EditVisibility::toggleVisible(Transaction& tx, Score* score)
59 : {
60 4 : bool allVisible = true;
61 :
62 34 : for (EngravingItem* item : score->selection().elements()) {
63 32 : if (!item->getProperty(Pid::VISIBLE).toBool()) {
64 2 : allVisible = false;
65 2 : break;
66 : }
67 : }
68 :
69 4 : bool newVisible = !allVisible;
70 :
71 80 : for (EngravingItem* item : score->selection().elements()) {
72 76 : undoChangeVisible(tx, item, newVisible);
73 : }
74 4 : }
75 :
76 1352 : static bool chordHasVisibleNote(const Chord* chord)
77 : {
78 2441 : for (const Note* note : chord->notes()) {
79 1378 : if (note->visible()) {
80 289 : return true;
81 : }
82 : }
83 :
84 1063 : return false;
85 : }
86 :
87 256 : static bool beamHasVisibleNote(const Beam* beam)
88 : {
89 1054 : for (const EngravingItem* item : beam->elements()) {
90 887 : if (item->isChord() && chordHasVisibleNote(toChord(item))) {
91 89 : return true;
92 : }
93 : }
94 :
95 167 : return false;
96 : }
97 :
98 : static void undoChangeOrnamentVisibility(Ornament* ornament, bool visible);
99 :
100 79 : static void undoChangeNoteVisibility(Note* note, bool visible)
101 : {
102 79 : note->undoChangeProperty(Pid::VISIBLE, visible);
103 :
104 79 : if (note->bendBack() || note->bendFor()) {
105 0 : note->setOverrideBendVisibilityRules(true);
106 : } else {
107 79 : note->setOverrideBendVisibilityRules(false);
108 : }
109 :
110 90 : for (NoteDot* dot : note->dots()) {
111 11 : dot->undoChangeProperty(Pid::VISIBLE, visible);
112 : }
113 :
114 79 : for (EngravingItem* e : note->el()) {
115 0 : e->undoChangeProperty(Pid::VISIBLE, visible);
116 : }
117 :
118 79 : if (note->accidental()) {
119 13 : note->accidental()->undoChangeProperty(Pid::VISIBLE, visible);
120 : }
121 :
122 79 : Chord* noteChord = note->chord();
123 79 : Beam* beam = noteChord->beam();
124 79 : std::vector<Chord*> chords;
125 :
126 79 : if (beam) {
127 276 : for (EngravingItem* item : beam->elements()) {
128 219 : if (item->isChord()) {
129 219 : chords.push_back(toChord(item));
130 : }
131 : }
132 : } else {
133 22 : chords.push_back(noteChord);
134 : }
135 :
136 : static const std::unordered_set<ElementType> IGNORED_TYPES {
137 : ElementType::NOTE,
138 : ElementType::LYRICS,
139 : ElementType::SLUR,
140 : ElementType::HAMMER_ON_PULL_OFF,
141 : ElementType::CHORD, // grace notes
142 : ElementType::LEDGER_LINE, // temporary objects, impossible to change visibility
143 81 : };
144 :
145 320 : for (const Chord* chord : chords) {
146 706 : for (const EngravingObject* obj : chord->linkList()) {
147 465 : const Chord* linkedChord = toChord(obj);
148 465 : const bool chordHasVisibleNote_ = chordHasVisibleNote(linkedChord);
149 : // note visibility can differ between scores, so check this score's own beam
150 465 : const Beam* linkedBeam = linkedChord->beam();
151 465 : const bool beamHasVisibleNote_ = chordHasVisibleNote_ || (linkedBeam && beamHasVisibleNote(linkedBeam));
152 1651 : for (EngravingObject* child : linkedChord->getChildren()) {
153 1186 : const ElementType type = child->type();
154 :
155 1186 : if (muse::contains(IGNORED_TYPES, type)) {
156 1059 : continue;
157 : }
158 :
159 683 : if (beam) {
160 572 : if (type == ElementType::STEM || type == ElementType::BEAM) {
161 556 : child->undoChangeProperty(Pid::VISIBLE, beamHasVisibleNote_);
162 556 : continue;
163 : }
164 : }
165 127 : if (child->isOrnament()) {
166 1 : undoChangeOrnamentVisibility(toOrnament(child), visible);
167 : } else {
168 126 : child->undoChangeProperty(Pid::VISIBLE, chordHasVisibleNote_);
169 : }
170 465 : }
171 241 : }
172 : }
173 79 : }
174 :
175 11 : static void undoChangeRestVisibility(Rest* rest, bool visible)
176 : {
177 11 : rest->undoChangeProperty(Pid::VISIBLE, visible);
178 :
179 16 : for (NoteDot* dot : rest->dotList()) {
180 5 : dot->undoChangeProperty(Pid::VISIBLE, visible);
181 : }
182 11 : }
183 :
184 3 : static void undoChangeOrnamentVisibility(Ornament* ornament, bool visible)
185 : {
186 3 : ornament->undoChangeProperty(Pid::VISIBLE, visible);
187 3 : Chord* cueNoteChord = ornament->cueNoteChord();
188 3 : if (cueNoteChord) {
189 1 : undoChangeNoteVisibility(cueNoteChord->upNote(), visible);
190 : }
191 3 : if (ornament->accidentalAbove()) {
192 2 : ornament->accidentalAbove()->undoChangeProperty(Pid::VISIBLE, visible);
193 : }
194 3 : if (ornament->accidentalBelow()) {
195 2 : ornament->accidentalBelow()->undoChangeProperty(Pid::VISIBLE, visible);
196 : }
197 3 : }
198 :
199 144 : void EditVisibility::undoChangeVisible(Transaction&, EngravingItem* item, bool visible)
200 : {
201 144 : if (item->isNote()) {
202 78 : undoChangeNoteVisibility(toNote(item), visible);
203 66 : } else if (item->isRest()) {
204 11 : undoChangeRestVisibility(toRest(item), visible);
205 55 : } else if (item->isOrnament()) {
206 2 : undoChangeOrnamentVisibility(toOrnament(item), visible);
207 53 : } else if (item->isTrillSegment()) {
208 0 : item->undoChangeProperty(Pid::VISIBLE, visible);
209 0 : Ornament* orn = toTrillSegment(item)->trill()->ornament();
210 0 : if (orn) {
211 0 : undoChangeOrnamentVisibility(orn, visible);
212 : }
213 : } else {
214 53 : item->undoChangeProperty(Pid::VISIBLE, visible);
215 : }
216 144 : }
|