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 : /**
24 : \file
25 : Implementation of class Selection plus other selection related functions.
26 : */
27 :
28 : #include "selectnotedialog.h"
29 :
30 : #include "translation.h"
31 :
32 : #include "engraving/types/typesconv.h"
33 : #include "engraving/dom/chord.h"
34 : #include "engraving/dom/engravingitem.h"
35 : #include "engraving/dom/note.h"
36 : #include "engraving/dom/score.h"
37 : #include "engraving/dom/segment.h"
38 : #include "engraving/dom/select.h"
39 :
40 : #include "notation/inotation.h"
41 : #include "notation/inotationelements.h"
42 : #include "notation/inotationinteraction.h"
43 : #include "notation/inotationselection.h" // IWYU pragma: keep
44 :
45 : #include "ui/view/widgetstatestore.h"
46 :
47 : #include "log.h"
48 :
49 : using namespace mu::notation;
50 : using namespace mu::engraving;
51 : using namespace muse::ui;
52 :
53 : //---------------------------------------------------------
54 : // SelectDialog
55 : //---------------------------------------------------------
56 :
57 0 : SelectNoteDialog::SelectNoteDialog(QWidget* parent)
58 0 : : muse::ui::WidgetDialog(parent)
59 : {
60 0 : setObjectName("SelectNoteDialog");
61 0 : setupUi(this);
62 0 : setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
63 0 : }
64 :
65 0 : void SelectNoteDialog::componentComplete()
66 : {
67 0 : const INotationInteractionPtr interaction = projectSession()->currentNotation()->interaction();
68 0 : IF_ASSERT_FAILED(interaction) {
69 0 : return;
70 : }
71 :
72 0 : m_note = toNote(interaction->contextItem());
73 0 : IF_ASSERT_FAILED(m_note) {
74 0 : return;
75 : }
76 :
77 0 : notehead->setText(TConv::translatedUserName(m_note->headGroup()));
78 0 : sameNotehead->setAccessibleName(sameNotehead->text() + notehead->text());
79 :
80 0 : pitch->setText(m_note->tpcUserName());
81 0 : samePitch->setAccessibleName(samePitch->text() + pitch->text());
82 :
83 0 : string->setText(QString::number(m_note->string() + 1));
84 0 : sameString->setAccessibleName(sameString->text() + string->text());
85 :
86 0 : type->setText(m_note->noteTypeUserName());
87 0 : sameType->setAccessibleName(sameType->text() + type->text());
88 :
89 : //: %1 is a note duration. If your language does not have different terms for
90 : //: "quarter note" and "quarter" (for example), or if the translations for the
91 : //: durations as separate strings are not suitable to be used as adjectives here,
92 : //: translate this string with "%1", so that just the duration will be shown.
93 0 : durationType->setText(muse::qtrc("notation", "%1 note").arg(TConv::translatedUserName(m_note->chord()->durationType().type())));
94 0 : sameDurationType->setAccessibleName(sameDurationType->text() + durationType->text());
95 :
96 0 : durationTicks->setText(m_note->chord()->durationUserName());
97 0 : sameDurationTicks->setAccessibleName(sameDurationTicks->text() + durationTicks->text());
98 :
99 0 : name->setText(tpc2name(m_note->tpc(), mu::engraving::NoteSpellingType::STANDARD, mu::engraving::NoteCaseType::AUTO, false));
100 0 : sameName->setAccessibleName(sameName->text() + name->text());
101 :
102 0 : const auto isSingleSelection = m_note->score()->selection().isSingle();
103 0 : inSelection->setCheckState(isSingleSelection ? Qt::CheckState::Unchecked : Qt::CheckState::Checked);
104 0 : inSelection->setEnabled(!isSingleSelection);
105 :
106 0 : connect(buttonBox, &QDialogButtonBox::clicked, this, &SelectNoteDialog::buttonClicked);
107 :
108 : //! NOTE: It is necessary for the correct start of navigation in the dialog
109 0 : setFocus();
110 0 : }
111 :
112 0 : FilterNotesOptions SelectNoteDialog::noteOptions() const
113 : {
114 0 : FilterNotesOptions options;
115 0 : options.elementType = ElementType::NOTE;
116 0 : if (sameNotehead->isChecked()) {
117 0 : options.notehead = m_note->headGroup();
118 : }
119 0 : if (samePitch->isChecked()) {
120 0 : options.pitch = m_note->pitch();
121 : }
122 0 : if (sameString->isChecked()) {
123 0 : options.string = m_note->string();
124 : }
125 0 : if (sameName->isChecked()) {
126 0 : options.tpc = m_note->tpc();
127 : }
128 0 : if (sameType->isChecked()) {
129 0 : options.noteType = m_note->noteType();
130 : }
131 0 : if (sameDurationType->isChecked()) {
132 0 : options.durationType = m_note->chord()->actualDurationType();
133 : }
134 :
135 0 : if (sameDurationTicks->isChecked()) {
136 0 : options.durationTicks = m_note->chord()->actualTicks();
137 : } else {
138 0 : options.durationTicks = mu::engraving::Fraction(-1, 1);
139 : }
140 :
141 0 : if (sameBeat->isChecked()) {
142 0 : options.beat = m_note->beat();
143 : } else {
144 0 : options.beat = mu::engraving::Fraction(0, 0);
145 : }
146 :
147 0 : if (sameMeasure->isChecked()) {
148 0 : options.measure = m_note->findMeasure();
149 : } else {
150 0 : options.measure = nullptr;
151 : }
152 :
153 0 : if (sameStaff->isChecked()) {
154 0 : options.staffStart = static_cast<int>(m_note->staffIdx());
155 0 : options.staffEnd = static_cast<int>(m_note->staffIdx() + 1);
156 0 : } else if (inSelection->isChecked()) {
157 0 : options.staffStart = static_cast<int>(m_note->score()->selection().staffStart());
158 0 : options.staffEnd = static_cast<int>(m_note->score()->selection().staffEnd());
159 : } else {
160 0 : options.staffStart = -1;
161 0 : options.staffEnd = -1;
162 : }
163 :
164 0 : options.voice = sameVoice->isChecked() ? static_cast<int>(m_note->voice()) : -1;
165 0 : options.system = nullptr;
166 0 : if (sameSystem->isChecked()) {
167 0 : options.system = m_note->chord()->segment()->system();
168 : }
169 :
170 0 : return options;
171 0 : }
172 :
173 0 : bool SelectNoteDialog::doReplace() const
174 : {
175 0 : return replace->isChecked();
176 : }
177 :
178 0 : bool SelectNoteDialog::doAdd() const
179 : {
180 0 : return add->isChecked();
181 : }
182 :
183 0 : bool SelectNoteDialog::doSubtract() const
184 : {
185 0 : return subtract->isChecked();
186 : }
187 :
188 0 : bool SelectNoteDialog::doFromSelection() const
189 : {
190 0 : return fromSelection->isChecked();
191 : }
192 :
193 0 : bool SelectNoteDialog::isInSelection() const
194 : {
195 0 : return inSelection->isChecked();
196 : }
197 :
198 0 : void SelectNoteDialog::setSameStringVisible(bool v)
199 : {
200 0 : sameString->setVisible(v);
201 0 : string->setVisible(v);
202 0 : }
203 :
204 0 : void SelectNoteDialog::buttonClicked(QAbstractButton* button)
205 : {
206 0 : switch (buttonBox->standardButton(button)) {
207 0 : case QDialogButtonBox::Ok:
208 0 : apply();
209 0 : done(1);
210 0 : break;
211 0 : case QDialogButtonBox::Cancel:
212 0 : done(0);
213 0 : break;
214 0 : default:
215 0 : break;
216 : }
217 0 : }
218 :
219 : //---------------------------------------------------------
220 : // showEvent
221 : //---------------------------------------------------------
222 :
223 0 : void SelectNoteDialog::showEvent(QShowEvent* event)
224 : {
225 0 : WidgetStateStore::restoreGeometry(this);
226 0 : QDialog::showEvent(event);
227 0 : }
228 :
229 : //---------------------------------------------------------
230 : // hideEvent
231 : //---------------------------------------------------------
232 :
233 0 : void SelectNoteDialog::hideEvent(QHideEvent* event)
234 : {
235 0 : WidgetStateStore::saveGeometry(this);
236 0 : QWidget::hideEvent(event);
237 0 : }
238 :
239 0 : std::shared_ptr<INotation> SelectNoteDialog::currentNotation() const
240 : {
241 0 : return projectSession()->currentNotation();
242 : }
243 :
244 0 : INotationInteractionPtr SelectNoteDialog::currentNotationInteraction() const
245 : {
246 0 : auto notation = currentNotation();
247 0 : if (!notation) {
248 0 : return nullptr;
249 : }
250 :
251 0 : return notation->interaction();
252 0 : }
253 :
254 0 : INotationElementsPtr SelectNoteDialog::currentNotationElements() const
255 : {
256 0 : auto notation = currentNotation();
257 0 : if (!notation) {
258 0 : return nullptr;
259 : }
260 :
261 0 : return notation->elements();
262 0 : }
263 :
264 0 : void SelectNoteDialog::apply() const
265 : {
266 0 : auto notationElements = currentNotationElements();
267 0 : auto interaction = currentNotationInteraction();
268 0 : if (!notationElements || !interaction) {
269 0 : return;
270 : }
271 :
272 0 : EngravingItem* selectedElement = interaction->contextItem();
273 0 : if (!selectedElement) {
274 0 : return;
275 : }
276 :
277 0 : FilterNotesOptions options = noteOptions();
278 :
279 0 : std::vector<EngravingItem*> elements = notationElements->elements(options);
280 0 : if (elements.empty()) {
281 0 : return;
282 : }
283 0 : if (isInSelection()) {
284 0 : const auto& selectedElements = interaction->selection()->elements();
285 0 : elements.erase(std::remove_if(elements.begin(), elements.end(), [selectedElements](const auto& e) {
286 0 : return std::find(selectedElements.begin(), selectedElements.end(), e) == selectedElements.end();
287 0 : }), elements.end());
288 : }
289 :
290 0 : if (doReplace()) {
291 0 : interaction->clearSelection();
292 0 : interaction->select(elements, SelectType::ADD);
293 0 : } else if (doSubtract()) {
294 0 : std::vector<EngravingItem*> selectionElements = interaction->selection()->elements();
295 0 : for (EngravingItem* element: elements) {
296 0 : selectionElements.erase(std::remove(selectionElements.begin(), selectionElements.end(), element), selectionElements.end());
297 : }
298 :
299 0 : interaction->clearSelection();
300 0 : interaction->select(selectionElements, SelectType::ADD);
301 0 : } else if (doAdd()) {
302 0 : std::vector<EngravingItem*> selectionElements = interaction->selection()->elements();
303 0 : std::copy(selectionElements.begin(), selectionElements.end(), back_inserter(elements));
304 0 : interaction->select(elements, SelectType::ADD);
305 0 : }
306 0 : }
|