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 "selectdialog.h"
24 :
25 : /**
26 : \file
27 : Implementation of class Selection plus other selection related functions.
28 : */
29 :
30 : #include "engraving/dom/score.h"
31 : #include "engraving/dom/system.h"
32 :
33 : #include "notation/inotation.h"
34 : #include "notation/inotationelements.h"
35 : #include "notation/inotationinteraction.h"
36 : #include "notation/inotationselection.h" // IWYU pragma: keep
37 : #include "notation/notationtypes.h"
38 :
39 : #include "ui/view/widgetstatestore.h"
40 :
41 : using namespace mu::notation;
42 : using namespace muse::ui;
43 :
44 : //---------------------------------------------------------
45 : // SelectDialog
46 : //---------------------------------------------------------
47 :
48 0 : SelectDialog::SelectDialog(QWidget* parent)
49 0 : : muse::ui::WidgetDialog(parent)
50 : {
51 0 : setObjectName("SelectDialog");
52 0 : setupUi(this);
53 0 : setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
54 0 : }
55 :
56 0 : void SelectDialog::componentComplete()
57 : {
58 0 : const INotationInteractionPtr interaction = projectSession()->currentNotation()->interaction();
59 0 : IF_ASSERT_FAILED(interaction) {
60 0 : return;
61 : }
62 :
63 0 : m_element = interaction->contextItem();
64 0 : IF_ASSERT_FAILED(m_element) {
65 0 : return;
66 : }
67 :
68 0 : type->setText(m_element->translatedTypeUserName().toQString());
69 0 : subtype->setText(m_element->translatedSubtypeUserName().toQString());
70 :
71 0 : sameSubtype->setEnabled(m_element->subtype() != -1);
72 0 : if (m_element->isHairpinSegment() || m_element->isHarmony()) {
73 0 : sameSubtype->setCheckState(Qt::CheckState::Checked);
74 : }
75 0 : subtype->setEnabled(m_element->subtype() != -1);
76 :
77 0 : const auto isSingleSelection = m_element->score()->selection().isSingle();
78 0 : inSelection->setCheckState(isSingleSelection ? Qt::CheckState::Unchecked : Qt::CheckState::Checked);
79 0 : inSelection->setEnabled(!isSingleSelection);
80 :
81 0 : sameDuration->setEnabled(m_element->isRest());
82 :
83 0 : connect(buttonBox, &QDialogButtonBox::clicked, this, &SelectDialog::buttonClicked);
84 :
85 : //! NOTE: It is necessary for the correct start of navigation in the dialog
86 0 : setFocus();
87 0 : }
88 :
89 : //---------------------------------------------------------
90 : // setPattern
91 : //---------------------------------------------------------
92 :
93 0 : FilterElementsOptions SelectDialog::elementOptions() const
94 : {
95 0 : FilterElementsOptions options;
96 0 : options.elementType = m_element->type();
97 0 : options.subtype = m_element->subtype();
98 :
99 0 : if (sameStaff->isChecked()) {
100 0 : options.staffStart = static_cast<int>(m_element->staffIdx());
101 0 : options.staffEnd = static_cast<int>(m_element->staffIdx() + 1);
102 0 : } else if (inSelection->isChecked()) {
103 0 : options.staffStart = static_cast<int>(m_element->score()->selection().staffStart());
104 0 : options.staffEnd = static_cast<int>(m_element->score()->selection().staffEnd());
105 : } else {
106 0 : options.staffStart = -1;
107 0 : options.staffEnd = -1;
108 : }
109 :
110 0 : if (sameDuration->isChecked() && m_element->isRest()) {
111 0 : const Rest* rest = toRest(m_element);
112 0 : options.durationTicks = rest->actualTicks();
113 : } else {
114 0 : options.durationTicks = Fraction(-1, 1);
115 : }
116 :
117 0 : if (sameBeat->isChecked()) {
118 0 : options.beat = m_element->beat();
119 : } else {
120 0 : options.beat = Fraction(0, 0);
121 : }
122 :
123 0 : if (sameMeasure->isChecked()) {
124 0 : auto m = m_element->findMeasure();
125 0 : if (!m && m_element->isSpannerSegment()) {
126 0 : if (auto ss = toSpannerSegment(m_element)) {
127 0 : if (auto s = ss->spanner()) {
128 0 : if (auto se = s->startElement()) {
129 0 : if (auto mse = se->findMeasure()) {
130 0 : m = mse;
131 : }
132 : }
133 : }
134 : }
135 : }
136 0 : options.measure = m;
137 : } else {
138 0 : options.measure = nullptr;
139 : }
140 :
141 0 : options.voice = sameVoice->isChecked() ? static_cast<int>(m_element->voice()) : -1;
142 0 : options.bySubtype = sameSubtype->isChecked();
143 :
144 0 : if (sameSystem->isChecked()) {
145 0 : options.system = elementSystem(m_element);
146 : }
147 :
148 0 : return options;
149 0 : }
150 :
151 0 : mu::engraving::System* SelectDialog::elementSystem(const EngravingItem* element) const
152 : {
153 0 : EngravingItem* _element = const_cast<EngravingItem*>(element);
154 : do {
155 0 : if (_element->isSystem()) {
156 0 : return toSystem(_element);
157 : }
158 0 : _element = _element->layoutParent();
159 0 : } while (element);
160 :
161 0 : return nullptr;
162 : }
163 :
164 0 : bool SelectDialog::doReplace() const
165 : {
166 0 : return replace->isChecked();
167 : }
168 :
169 0 : bool SelectDialog::doAdd() const
170 : {
171 0 : return add->isChecked();
172 : }
173 :
174 0 : bool SelectDialog::doSubtract() const
175 : {
176 0 : return subtract->isChecked();
177 : }
178 :
179 0 : bool SelectDialog::doFromSelection() const
180 : {
181 0 : return fromSelection->isChecked();
182 : }
183 :
184 0 : bool SelectDialog::isInSelection() const
185 : {
186 0 : return inSelection->isChecked();
187 : }
188 :
189 0 : void SelectDialog::buttonClicked(QAbstractButton* button)
190 : {
191 0 : switch (buttonBox->standardButton(button)) {
192 0 : case QDialogButtonBox::Ok:
193 0 : apply();
194 0 : done(1);
195 0 : break;
196 0 : case QDialogButtonBox::Cancel:
197 0 : done(0);
198 0 : break;
199 0 : default:
200 0 : break;
201 : }
202 0 : }
203 :
204 : //---------------------------------------------------------
205 : // showEvent
206 : //---------------------------------------------------------
207 :
208 0 : void SelectDialog::showEvent(QShowEvent* event)
209 : {
210 0 : WidgetStateStore::restoreGeometry(this);
211 0 : QDialog::showEvent(event);
212 0 : }
213 :
214 : //---------------------------------------------------------
215 : // hideEvent
216 : //---------------------------------------------------------
217 :
218 0 : void SelectDialog::hideEvent(QHideEvent* event)
219 : {
220 0 : WidgetStateStore::saveGeometry(this);
221 0 : QWidget::hideEvent(event);
222 0 : }
223 :
224 0 : std::shared_ptr<INotation> SelectDialog::currentNotation() const
225 : {
226 0 : return projectSession()->currentNotation();
227 : }
228 :
229 0 : INotationInteractionPtr SelectDialog::currentNotationInteraction() const
230 : {
231 0 : auto notation = currentNotation();
232 0 : if (!notation) {
233 0 : return nullptr;
234 : }
235 :
236 0 : return notation->interaction();
237 0 : }
238 :
239 0 : INotationElementsPtr SelectDialog::currentNotationElements() const
240 : {
241 0 : auto notation = currentNotation();
242 0 : if (!notation) {
243 0 : return nullptr;
244 : }
245 :
246 0 : return notation->elements();
247 0 : }
248 :
249 0 : void SelectDialog::apply() const
250 : {
251 0 : auto notationElements = currentNotationElements();
252 0 : auto interaction = currentNotationInteraction();
253 0 : if (!notationElements || !interaction) {
254 0 : return;
255 : }
256 :
257 0 : EngravingItem* selectedElement = interaction->contextItem();
258 0 : if (!selectedElement) {
259 0 : return;
260 : }
261 :
262 0 : FilterElementsOptions options = elementOptions();
263 :
264 0 : std::vector<EngravingItem*> elements = notationElements->elements(options);
265 0 : if (elements.empty()) {
266 0 : return;
267 : }
268 0 : if (isInSelection()) {
269 0 : const auto& selectedElements = interaction->selection()->elements();
270 0 : elements.erase(std::remove_if(elements.begin(), elements.end(), [selectedElements](const auto& e) {
271 0 : return std::find(selectedElements.begin(), selectedElements.end(), e) == selectedElements.end();
272 0 : }), elements.end());
273 : }
274 :
275 0 : if (doReplace()) {
276 0 : interaction->clearSelection();
277 0 : interaction->select(elements, SelectType::ADD);
278 0 : } else if (doSubtract()) {
279 0 : std::vector<EngravingItem*> selectionElements = interaction->selection()->elements();
280 0 : for (EngravingItem* element: elements) {
281 0 : selectionElements.erase(std::remove(selectionElements.begin(), selectionElements.end(), element), selectionElements.end());
282 : }
283 :
284 0 : interaction->clearSelection();
285 0 : interaction->select(selectionElements, SelectType::ADD);
286 0 : } else if (doAdd()) {
287 0 : std::vector<EngravingItem*> selectionElements = interaction->selection()->elements();
288 0 : std::copy(selectionElements.begin(), selectionElements.end(), back_inserter(elements));
289 0 : interaction->select(elements, SelectType::ADD);
290 0 : }
291 0 : }
|