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) 2025 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 "editstaffbrackets.h"
24 :
25 : #include "../dom/bracket.h"
26 : #include "../dom/bracketitem.h"
27 : #include "../dom/factory.h"
28 : #include "../dom/score.h"
29 : #include "../dom/staff.h"
30 :
31 : using namespace mu::engraving;
32 :
33 66 : void EditStaffBrackets::undoAddBracket(Score* score, Staff* staff, size_t level, BracketType type, size_t span)
34 : {
35 66 : staff_idx_t startStaffIdx = staff->idx();
36 66 : staff_idx_t totStaves = score->nstaves();
37 :
38 : // Make sure this brackets won't overlap with others sharing same column.
39 : // If overlaps are found, move the other brackets outwards (i.e. increase column).
40 171 : for (staff_idx_t staffIdx = startStaffIdx; staffIdx < startStaffIdx + span && staffIdx < totStaves; ++staffIdx) {
41 105 : const std::vector<BracketItem*>& scoreBrackets = score->brackets(staffIdx);
42 :
43 105 : bool collision = false;
44 172 : for (BracketItem* b : scoreBrackets) {
45 82 : if (b->bracketType() != BracketType::NO_BRACKET && b->bracketType() != BracketType::GROUP
46 82 : && b->column() == level) {
47 0 : collision = true;
48 0 : break;
49 : }
50 : }
51 :
52 105 : if (!collision) {
53 105 : continue;
54 : }
55 :
56 0 : for (int i = static_cast<int>(scoreBrackets.size()) - 1; i >= static_cast<int>(level); --i) {
57 0 : if (i >= static_cast<int>(scoreBrackets.size())) {
58 : // This might theoretically happen when a lot of brackets get cleaned up
59 : // after changing the column of the first bracket we see
60 0 : continue;
61 : }
62 :
63 0 : BracketItem* bi = scoreBrackets[i];
64 0 : if (bi->column() > level) {
65 0 : continue;
66 : }
67 :
68 0 : if (bi->bracketType() == BracketType::NO_BRACKET || bi->bracketType() == BracketType::GROUP) {
69 0 : continue;
70 : }
71 :
72 0 : bi->undoChangeProperty(Pid::BRACKET_COLUMN, bi->column() + 1);
73 : }
74 : }
75 :
76 66 : score->undo(new AddBracket(staff, level, type, span));
77 66 : }
78 :
79 0 : void EditStaffBrackets::undoRemoveBracket(Score* score, Bracket* b)
80 : {
81 0 : score->undo(new RemoveBracket(b->staff(), b->column(), b->bracketType(), b->span()));
82 0 : }
83 :
84 1418 : void EditStaffBrackets::fillBrackets(Score* score, staff_idx_t staffIdx, size_t idx)
85 : {
86 : // make sure index idx is valid
87 1418 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
88 2234 : for (size_t i = staffBrackets.size(); i <= idx; ++i) {
89 816 : BracketItem* bi = Factory::createBracketItem(score->dummy());
90 816 : bi->setStartStaffIdx(staffIdx);
91 816 : bi->setColumn(i);
92 816 : bi->setScore(score);
93 816 : staffBrackets.push_back(bi);
94 : }
95 1418 : }
96 :
97 631 : void EditStaffBrackets::cleanBrackets(Score* score, staff_idx_t staffIdx)
98 : {
99 : // remove NO_BRACKET entries from the end of list
100 631 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
101 1440 : while (!staffBrackets.empty() && (staffBrackets.back()->bracketType() == BracketType::NO_BRACKET)) {
102 178 : BracketItem* bi = muse::takeLast(staffBrackets);
103 178 : delete bi;
104 : }
105 631 : }
106 :
107 12 : void EditStaffBrackets::adjustBracketsDel(Score* score, size_t sidx, size_t eidx)
108 : {
109 12 : IF_ASSERT_FAILED(sidx < eidx && eidx <= score->staves().size()) {
110 0 : return;
111 : }
112 :
113 27 : for (size_t staffIdx = 0; staffIdx < eidx; ++staffIdx) {
114 15 : const std::vector<BracketItem*> staffBrackets = score->brackets(staffIdx);
115 28 : for (BracketItem* bi : staffBrackets) {
116 13 : size_t span = bi->bracketSpan();
117 13 : if ((span == 0) || ((staffIdx + span) <= sidx)) {
118 1 : continue;
119 : }
120 12 : const bool startsOutsideDeletedRange = (staffIdx < sidx);
121 12 : const bool endsOutsideDeletedRange = ((staffIdx + span) >= eidx);
122 12 : if (startsOutsideDeletedRange && endsOutsideDeletedRange) {
123 : // Shorten the bracket by the number of staves deleted
124 3 : bi->undoChangeProperty(Pid::BRACKET_SPAN, int(span - (eidx - sidx)));
125 9 : } else if (startsOutsideDeletedRange) {
126 : // Shorten the bracket by the number of staves deleted that were spanned by it
127 0 : bi->undoChangeProperty(Pid::BRACKET_SPAN, int(sidx - staffIdx));
128 9 : } else if (endsOutsideDeletedRange) {
129 9 : const size_t column = bi->column();
130 9 : const BracketType bracketType = bi->bracketType();
131 9 : score->undo(new RemoveBracket(score->staves().at(staffIdx), bi->column(), bi->bracketType(), span));
132 9 : int newSpan = int(span - (eidx - staffIdx));
133 9 : if (eidx < score->staves().size() && newSpan > 0) {
134 : // Move the bracket past the end of the deleted range,
135 : // and shorten it by the number of staves deleted that were spanned by it.
136 :
137 6 : EditStaffBrackets::undoAddBracket(score, score->staves().at(eidx), column, bracketType, newSpan);
138 : }
139 : }
140 : }
141 15 : }
142 : }
143 :
144 0 : void EditStaffBrackets::adjustBracketsIns(Score* score, size_t sidx, size_t eidx)
145 : {
146 0 : for (size_t staffIdx = 0; staffIdx < score->staves().size(); ++staffIdx) {
147 0 : const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
148 0 : for (BracketItem* bi : staffBrackets) {
149 0 : size_t span = bi->bracketSpan();
150 0 : if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx)) {
151 0 : continue;
152 : }
153 0 : if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) {
154 0 : bi->undoChangeProperty(Pid::BRACKET_SPAN, int(span + (eidx - sidx)));
155 : }
156 : }
157 : }
158 0 : }
159 :
160 631 : void EditStaffBrackets::setBracketType(Score* score, staff_idx_t staffIdx, size_t idx, BracketType val)
161 : {
162 631 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
163 :
164 631 : fillBrackets(score, staffIdx, idx);
165 631 : staffBrackets[idx]->setBracketType(val);
166 631 : cleanBrackets(score, staffIdx);
167 631 : }
168 :
169 0 : void EditStaffBrackets::changeBracketColumn(Score* score, staff_idx_t staffIdx, size_t oldColumn, size_t newColumn)
170 : {
171 0 : if (oldColumn == newColumn) {
172 0 : return;
173 : }
174 0 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
175 :
176 0 : size_t idx = std::max(oldColumn, newColumn);
177 0 : fillBrackets(score, staffIdx, idx);
178 0 : int step = newColumn > oldColumn ? 1 : -1;
179 0 : for (size_t i = oldColumn; i != newColumn; i += step) {
180 0 : size_t oldIdx = i;
181 0 : size_t newIdx = i + step;
182 0 : staffBrackets[oldIdx]->setColumn(newIdx);
183 0 : staffBrackets[newIdx]->setColumn(oldIdx);
184 0 : muse::swapItemsAt(staffBrackets, oldIdx, newIdx);
185 : }
186 0 : cleanBrackets(score, staffIdx);
187 : }
188 :
189 549 : void EditStaffBrackets::setBracketSpan(Score* score, staff_idx_t staffIdx, size_t idx, size_t val)
190 : {
191 549 : const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
192 549 : fillBrackets(score, staffIdx, idx);
193 549 : staffBrackets[idx]->setBracketSpan(val);
194 549 : }
195 :
196 233 : void EditStaffBrackets::setBracketVisible(Score* score, staff_idx_t staffIdx, size_t idx, bool v)
197 : {
198 233 : const std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
199 233 : fillBrackets(score, staffIdx, idx);
200 233 : staffBrackets[idx]->setVisible(v);
201 233 : }
202 :
203 34 : void EditStaffBrackets::addBracket(Score* score, staff_idx_t staffIdx, BracketItem* b)
204 : {
205 34 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
206 34 : b->setStartStaffIdx(staffIdx);
207 34 : if (!staffBrackets.empty() && staffBrackets[0]->bracketType() == BracketType::NO_BRACKET) {
208 6 : delete staffBrackets[0];
209 6 : staffBrackets[0] = b;
210 : } else {
211 : //
212 : // create new bracket level
213 : //
214 113 : for (staff_idx_t sidx = 0; sidx < score->nstaves(); ++sidx) {
215 85 : if (sidx == staffIdx) {
216 28 : staffBrackets.push_back(b);
217 28 : b->setScore(score);
218 : } else {
219 57 : BracketItem* bi = Factory::createBracketItem(score->dummy());
220 57 : bi->setStartStaffIdx(sidx);
221 57 : bi->setScore(score);
222 57 : score->brackets(sidx).push_back(bi);
223 : }
224 : }
225 : }
226 34 : }
227 :
228 38 : void EditStaffBrackets::insertBracket(Score* score, staff_idx_t staffIdx, BracketItem* b)
229 : {
230 38 : std::vector<BracketItem*>& staffBrackets = score->brackets(staffIdx);
231 38 : b->setStartStaffIdx(staffIdx);
232 38 : size_t column = b->column();
233 38 : if (column < staffBrackets.size()) {
234 0 : if (staffBrackets[column]) {
235 0 : delete staffBrackets[column];
236 : }
237 0 : staffBrackets[column] = b;
238 38 : } else if (column == staffBrackets.size()) {
239 33 : staffBrackets.push_back(b);
240 : } else {
241 5 : fillBrackets(score, staffIdx, column - 1);
242 5 : staffBrackets.push_back(b);
243 : }
244 38 : }
245 :
246 : //---------------------------------------------------------
247 : // AddBracket
248 : //---------------------------------------------------------
249 :
250 71 : void AddBracket::redo()
251 : {
252 71 : EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, bracketType);
253 71 : EditStaffBrackets::setBracketSpan(staff->score(), staff->idx(), level, span);
254 71 : staff->triggerLayout();
255 71 : }
256 :
257 38 : void AddBracket::undo()
258 : {
259 38 : EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, BracketType::NO_BRACKET);
260 38 : staff->triggerLayout();
261 38 : }
262 :
263 : //---------------------------------------------------------
264 : // RemoveBracket
265 : //---------------------------------------------------------
266 :
267 44 : void RemoveBracket::redo()
268 : {
269 44 : EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, BracketType::NO_BRACKET);
270 44 : staff->triggerLayout();
271 44 : }
272 :
273 27 : void RemoveBracket::undo()
274 : {
275 27 : EditStaffBrackets::setBracketType(staff->score(), staff->idx(), level, bracketType);
276 27 : EditStaffBrackets::setBracketSpan(staff->score(), staff->idx(), level, span);
277 27 : staff->triggerLayout();
278 27 : }
|