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 "editstaff.h"
24 :
25 : #include "containers.h"
26 : #include "translation.h"
27 : #include "global/utils.h"
28 :
29 : #include "ui/view/widgetstatestore.h"
30 : #include "ui/view/widgetutils.h"
31 :
32 : #include "editpitch.h"
33 : #include "editstafftype.h"
34 : #include "editstringdata.h"
35 :
36 : #include "engraving/dom/factory.h"
37 : #include "engraving/dom/instrumentname.h"
38 : #include "engraving/dom/interval.h"
39 : #include "engraving/dom/masterscore.h"
40 : #include "engraving/dom/part.h"
41 : #include "engraving/dom/staff.h"
42 : #include "engraving/dom/stringdata.h"
43 : #include "engraving/dom/system.h"
44 : #include "engraving/dom/text.h"
45 : #include "engraving/dom/utils.h"
46 : #include "engraving/editing/transaction/undostack.h"
47 :
48 : #include "notation/imasternotation.h"
49 : #include "notation/inotation.h"
50 : #include "notation/inotationinteraction.h"
51 : #include "notation/inotationparts.h"
52 : #include "notation/inotationselection.h"
53 :
54 : #include "log.h"
55 :
56 : using namespace mu::notation;
57 : using namespace muse;
58 : using namespace muse::ui;
59 : using namespace mu::engraving;
60 :
61 : static const QChar GO_UP_ICON = iconCodeToChar(IconCode::Code::ARROW_UP);
62 : static const QChar GO_DOWN_ICON = iconCodeToChar(IconCode::Code::ARROW_DOWN);
63 : static const QChar EDIT_ICON = iconCodeToChar(IconCode::Code::EDIT);
64 :
65 0 : EditStaff::EditStaff(QWidget* parent)
66 0 : : muse::ui::WidgetDialog(parent)
67 : {
68 0 : setObjectName("EditStaff");
69 0 : setupUi(this);
70 0 : setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
71 0 : setModal(true);
72 :
73 0 : connect(buttonBox, &QDialogButtonBox::clicked, this, &EditStaff::bboxClicked);
74 0 : connect(changeInstrument, &QPushButton::clicked, this, &EditStaff::showReplaceInstrumentDialog);
75 0 : connect(changeStaffType, &QPushButton::clicked, this, &EditStaff::showStaffTypeDialog);
76 0 : connect(minPitchASelect, &QPushButton::clicked, this, &EditStaff::minPitchAClicked);
77 0 : connect(maxPitchASelect, &QPushButton::clicked, this, &EditStaff::maxPitchAClicked);
78 0 : connect(minPitchPSelect, &QPushButton::clicked, this, &EditStaff::minPitchPClicked);
79 0 : connect(maxPitchPSelect, &QPushButton::clicked, this, &EditStaff::maxPitchPClicked);
80 0 : connect(editStringData, &QPushButton::clicked, this, &EditStaff::editStringDataClicked);
81 0 : connect(nextButton, &QPushButton::clicked, this, &EditStaff::gotoNextStaff);
82 0 : connect(previousButton, &QPushButton::clicked, this, &EditStaff::gotoPreviousStaff);
83 :
84 0 : connect(showClef, &QCheckBox::clicked, this, &EditStaff::showClefChanged);
85 0 : connect(showTimesig, &QCheckBox::clicked, this, &EditStaff::showTimeSigChanged);
86 0 : connect(showBarlines, &QCheckBox::clicked, this, &EditStaff::showBarlinesChanged);
87 0 : connect(invisible, &QCheckBox::clicked, this, &EditStaff::invisibleChanged);
88 0 : connect(instrumentLabelVisibility, &QComboBox::currentIndexChanged, this, &EditStaff::instrumentLabelVisibilityChanged);
89 :
90 0 : connect(useDefaultName, &QRadioButton::clicked, this, [&]() {
91 0 : useCustomNameChanged(!useDefaultName->isChecked());
92 0 : });
93 0 : connect(useCustomName, &QRadioButton::clicked, this, [&]() {
94 0 : useCustomNameChanged(useCustomName->isChecked());
95 0 : });
96 :
97 0 : connect(useDefaultGroupName, &QRadioButton::clicked, this, [&]() {
98 0 : useCustomGroupNameChanged(!useDefaultGroupName->isChecked());
99 0 : });
100 0 : connect(useCustomGroupName, &QRadioButton::clicked, this, [&]() {
101 0 : useCustomGroupNameChanged(useCustomGroupName->isChecked());
102 0 : });
103 :
104 0 : connect(useDefaultIndividualName, &QRadioButton::clicked, this, [&]() {
105 0 : useCustomIndividualNameChanged(!useDefaultIndividualName->isChecked());
106 0 : });
107 0 : connect(useCustomIndividualName, &QRadioButton::clicked, this, [&]() {
108 0 : useCustomIndividualNameChanged(useCustomIndividualName->isChecked());
109 0 : });
110 :
111 0 : connect(color, &Awl::ColorLabel::colorChanged, this, &EditStaff::colorChanged);
112 :
113 0 : connect(mag, &QDoubleSpinBox::valueChanged, this, &EditStaff::magChanged);
114 :
115 0 : connect(iList, &QComboBox::currentIndexChanged, this, &EditStaff::transpositionChanged);
116 :
117 0 : connect(lines, &QSpinBox::valueChanged, this, &EditStaff::numOfLinesChanged);
118 0 : connect(lineDistance, &QDoubleSpinBox::valueChanged, this, &EditStaff::lineDistanceChanged);
119 :
120 0 : connect(longStaffName, &QTextEdit::textChanged, this, &EditStaff::longNameChanged);
121 0 : connect(shortStaffName, &QTextEdit::textChanged, this, &EditStaff::shortNameChanged);
122 :
123 0 : WidgetUtils::setWidgetIcon(nextButton, IconCode::Code::ARROW_DOWN);
124 0 : WidgetUtils::setWidgetIcon(previousButton, IconCode::Code::ARROW_UP);
125 0 : WidgetUtils::setWidgetIcon(minPitchASelect, IconCode::Code::EDIT);
126 0 : WidgetUtils::setWidgetIcon(maxPitchASelect, IconCode::Code::EDIT);
127 0 : WidgetUtils::setWidgetIcon(minPitchPSelect, IconCode::Code::EDIT);
128 0 : WidgetUtils::setWidgetIcon(maxPitchPSelect, IconCode::Code::EDIT);
129 :
130 : //! NOTE: It is necessary for the correct start of navigation in the dialog
131 0 : setFocus();
132 0 : }
133 :
134 0 : void EditStaff::componentComplete()
135 : {
136 0 : editStaffTypeDialog = new EditStaffType(iocContext(), this);
137 0 : editStaffTypeDialog->setWindowModality(Qt::WindowModal);
138 :
139 0 : initStaff();
140 0 : }
141 :
142 0 : void EditStaff::setStaff(Staff* s, const Fraction& tick)
143 : {
144 0 : if (m_staff != nullptr) {
145 0 : delete m_staff;
146 : }
147 :
148 0 : m_orgStaff = s;
149 :
150 0 : if (!m_orgStaff) {
151 0 : return;
152 : }
153 :
154 0 : Part* part = m_orgStaff->part();
155 :
156 0 : auto it = muse::findLessOrEqual(part->instruments(), tick.ticks());
157 0 : if (it == part->instruments().cend()) {
158 0 : return;
159 : }
160 :
161 0 : m_instrument = *it->second;
162 :
163 0 : m_instrumentKey.instrumentId = m_instrument.id();
164 0 : m_instrumentKey.partId = part->id();
165 0 : m_instrumentKey.tick = Fraction::fromTicks(it->first);
166 :
167 0 : m_staff = engraving::Factory::createStaff(part);
168 0 : mu::engraving::StaffType* orgStaffType = m_orgStaff->staffType(tick);
169 0 : m_tick = Fraction::fromTicks(m_orgStaff->staffTypeRange(tick).first);
170 0 : mu::engraving::StaffType* stt = m_staff->setStaffType(m_tick, *orgStaffType);
171 :
172 0 : m_staff->setPart(part);
173 0 : m_staff->setDefaultClefType(m_orgStaff->defaultClefType());
174 0 : m_staff->setUserDist(m_orgStaff->userDist());
175 0 : m_staff->setCutaway(m_orgStaff->cutaway());
176 0 : m_staff->setHideWhenEmpty(m_orgStaff->hideWhenEmpty());
177 0 : m_staff->setShowIfEntireSystemEmpty(m_orgStaff->showIfEntireSystemEmpty());
178 0 : m_staff->setHideSystemBarLine(m_orgStaff->hideSystemBarLine());
179 0 : m_staff->setMergeMatchingRests(m_orgStaff->mergeMatchingRests());
180 0 : m_staff->setReflectTranspositionInLinkedTab(m_orgStaff->reflectTranspositionInLinkedTab());
181 :
182 : // set dlg controls
183 0 : spinExtraDistance->setValue(s->userDist().val());
184 0 : invisible->setChecked(stt->invisible());
185 0 : color->setColor(stt->color().toQColor());
186 0 : mag->setValue(stt->userMag() * 100.0);
187 :
188 0 : hideSystemBarLine->setChecked(m_staff->hideSystemBarLine());
189 0 : mergeMatchingRests->setCurrentIndex(static_cast<int>(m_staff->mergeMatchingRests()));
190 0 : noReflectTranspositionInLinkedTab->setChecked(!m_staff->reflectTranspositionInLinkedTab());
191 :
192 0 : updateStaffType(*stt);
193 0 : updateInstrument();
194 0 : updateNextPreviousButtons();
195 : }
196 :
197 0 : void EditStaff::showEvent(QShowEvent* event)
198 : {
199 0 : WidgetStateStore::restoreGeometry(this);
200 0 : QDialog::showEvent(event);
201 0 : }
202 :
203 0 : void EditStaff::hideEvent(QHideEvent* ev)
204 : {
205 0 : WidgetStateStore::saveGeometry(this);
206 0 : QWidget::hideEvent(ev);
207 0 : }
208 :
209 0 : void EditStaff::updateStaffType(const mu::engraving::StaffType& staffType)
210 : {
211 0 : lines->setValue(staffType.lines());
212 0 : lineDistance->setValue(staffType.lineDistance().val());
213 0 : showClef->setChecked(staffType.genClef());
214 0 : showTimesig->setChecked(staffType.genTimesig());
215 0 : showBarlines->setChecked(staffType.showBarlines());
216 0 : invisible->setChecked(staffType.invisible());
217 0 : instrumentLabelVisibility->setCurrentIndex(static_cast<int>(staffType.instrumentLabelVisibility()));
218 0 : staffGroupName->setText(staffType.translatedGroupName());
219 :
220 0 : longStaffName->setPlainText(TextBase::unEscape(staffType.longName()));
221 0 : shortStaffName->setPlainText(TextBase::unEscape(staffType.shortName()));
222 0 : }
223 :
224 0 : void EditStaff::updateInstrument()
225 : {
226 0 : updateInterval(m_instrument.transpose());
227 :
228 0 : longName->setPlainText(m_instrument.nameAsPlainText());
229 0 : shortName->setPlainText(m_instrument.abbreviatureAsPlainText());
230 0 : instrumentNumber->setValue(m_instrument.number());
231 :
232 0 : const InstrumentLabel& instrLabel = m_instrument.instrumentLabel();
233 0 : showNumberLong->setChecked(instrLabel.showNumberLong());
234 0 : showNumberShort->setChecked(instrLabel.showNumberShort());
235 0 : showTranspositionLong->setChecked(instrLabel.showTranspositionLong());
236 0 : showTranspositionShort->setChecked(instrLabel.showTranspositionShort());
237 0 : transpositionLabel->setText(TextBase::unEscape(instrLabel.transposition()));
238 :
239 0 : useDefaultName->setChecked(!instrLabel.useCustomName());
240 0 : useCustomName->setChecked(instrLabel.useCustomName());
241 0 : customNameGroupbox->setEnabled(instrLabel.useCustomName());
242 0 : customLongName->setText(TextBase::unEscape(instrLabel.customNameLong()));
243 0 : customShortName->setText(TextBase::unEscape(instrLabel.customNameShort()));
244 :
245 0 : allowGroupName->setChecked(instrLabel.allowGroupName());
246 0 : useDefaultGroupName->setChecked(!instrLabel.useCustomGroupName());
247 0 : useCustomGroupName->setChecked(instrLabel.useCustomGroupName());
248 0 : customGroupNameGroupbox->setEnabled(instrLabel.useCustomGroupName());
249 0 : customLongNameGroup->setPlainText(TextBase::unEscape(instrLabel.customNameLongGroup()));
250 0 : customShortNameGroup->setPlainText(TextBase::unEscape(instrLabel.customNameShortGroup()));
251 :
252 0 : useDefaultIndividualName->setChecked(!instrLabel.useCustomIndividualName());
253 0 : useCustomIndividualName->setChecked(instrLabel.useCustomIndividualName());
254 0 : customIndividualNameGroupbox->setEnabled(instrLabel.useCustomIndividualName());
255 0 : customLongNameIndividual->setPlainText(TextBase::unEscape(instrLabel.customNameLongIndividual()));
256 0 : customShortNameIndividual->setPlainText(TextBase::unEscape(instrLabel.customNameShortIndividual()));
257 :
258 0 : const InstrumentTemplate* templ = mu::engraving::searchTemplate(m_instrument.id());
259 0 : if (templ) {
260 0 : instrumentName->setText(formatInstrumentTitle(templ->trackName, templ->trait));
261 : } else {
262 0 : instrumentName->setText(muse::qtrc("notation/editstaff", "Unknown"));
263 : }
264 :
265 0 : m_minPitchA = m_instrument.minPitchA();
266 0 : m_maxPitchA = m_instrument.maxPitchA();
267 0 : m_minPitchP = m_instrument.minPitchP();
268 0 : m_maxPitchP = m_instrument.maxPitchP();
269 0 : minPitchA->setText(midiCodeToStr(m_minPitchA));
270 0 : maxPitchA->setText(midiCodeToStr(m_maxPitchA));
271 0 : minPitchP->setText(midiCodeToStr(m_minPitchP));
272 0 : maxPitchP->setText(midiCodeToStr(m_maxPitchP));
273 :
274 : // only show string data controls if instrument has strings
275 0 : size_t numStr = m_instrument.stringData()->strings();
276 0 : stringDataFrame->setVisible(numStr > 0);
277 0 : numOfStrings->setText(QString::number(numStr));
278 :
279 : // show transp_PreferSharpFlat if instrument isn't non-transposing or octave-transposing
280 0 : bool showPreferSharpFlat = (iList->currentIndex() != 0) && (iList->currentIndex() != 25);
281 0 : transp_PreferSharpFlat->setVisible(showPreferSharpFlat);
282 0 : preferSharpFlat->setCurrentIndex(int(m_orgStaff->part()->preferSharpFlat()));
283 0 : }
284 :
285 0 : void EditStaff::updateInterval(const mu::engraving::Interval& iv)
286 : {
287 0 : bool upFlag = !(iv.chromatic < 0 || iv.diatonic < 0);
288 :
289 0 : int chromatic = std::abs(iv.chromatic);
290 0 : int diatonic = std::abs(iv.diatonic);
291 0 : int oct = chromatic / 12;
292 :
293 0 : chromatic %= 12;
294 0 : diatonic %= 7;
295 :
296 0 : if (diatonic == 0 && chromatic == 11) {
297 0 : diatonic = 7;
298 0 : } else if (chromatic == 0 && diatonic == 6) {
299 0 : chromatic = 12;
300 0 : --oct;
301 : }
302 :
303 0 : size_t intervalIndex = muse::indexOf(Interval::allIntervals, Interval { diatonic, chromatic });
304 0 : IF_ASSERT_FAILED(intervalIndex != muse::nidx) {
305 0 : LOGD("EditStaff: unknown interval %d %d", diatonic, chromatic);
306 0 : intervalIndex = 0;
307 : }
308 :
309 0 : iList->setCurrentIndex(static_cast<int>(intervalIndex));
310 0 : up->setChecked(upFlag);
311 0 : down->setChecked(!upFlag);
312 0 : octave->setValue(oct);
313 0 : }
314 :
315 0 : void EditStaff::updateNextPreviousButtons()
316 : {
317 0 : staff_idx_t staffIdx = m_orgStaff->idx();
318 :
319 0 : nextButton->setEnabled(staffIdx < (m_orgStaff->score()->nstaves() - 1));
320 0 : previousButton->setEnabled(staffIdx != 0);
321 0 : }
322 :
323 0 : void EditStaff::gotoNextStaff()
324 : {
325 0 : apply();
326 0 : staff_idx_t nextStaffIndex = m_orgStaff->idx() + 1;
327 0 : Staff* nextStaff = m_orgStaff->score()->staff(nextStaffIndex);
328 :
329 0 : if (nextStaff) {
330 0 : setStaff(nextStaff, m_tick);
331 : }
332 0 : }
333 :
334 0 : void EditStaff::gotoPreviousStaff()
335 : {
336 0 : apply();
337 0 : staff_idx_t previousStaffIndex = m_orgStaff->idx() - 1;
338 0 : Staff* prevStaff = m_orgStaff->score()->staff(previousStaffIndex);
339 :
340 0 : if (prevStaff) {
341 0 : setStaff(prevStaff, m_tick);
342 : }
343 0 : }
344 :
345 0 : void EditStaff::bboxClicked(QAbstractButton* button)
346 : {
347 0 : QDialogButtonBox::ButtonRole br = buttonBox->buttonRole(button);
348 0 : switch (br) {
349 0 : case QDialogButtonBox::ApplyRole:
350 0 : apply();
351 0 : break;
352 0 : case QDialogButtonBox::AcceptRole:
353 0 : apply();
354 0 : done(1);
355 0 : break;
356 0 : case QDialogButtonBox::RejectRole:
357 0 : done(0);
358 0 : if (m_staff != nullptr) {
359 0 : delete m_staff;
360 : }
361 0 : break;
362 :
363 0 : default:
364 0 : LOGD("EditStaff: unknown button %d", int(br));
365 0 : break;
366 : }
367 0 : }
368 :
369 0 : void EditStaff::apply()
370 : {
371 0 : size_t index = m_staff->score()->undoStack()->currentIndex();
372 0 : applyStaffProperties();
373 0 : applyPartProperties();
374 0 : m_staff->score()->undoStack()->mergeTransactions(index);
375 0 : }
376 :
377 0 : void EditStaff::minPitchAClicked()
378 : {
379 : int newCode;
380 0 : EditPitch ep(this, m_instrument.minPitchA());
381 0 : ep.setWindowModality(Qt::WindowModal);
382 0 : if ((newCode = ep.exec()) != -1) {
383 0 : minPitchA->setText(midiCodeToStr(newCode));
384 0 : m_minPitchA = newCode;
385 : }
386 0 : }
387 :
388 0 : void EditStaff::maxPitchAClicked()
389 : {
390 : int newCode;
391 0 : EditPitch ep(this, m_instrument.maxPitchP());
392 0 : ep.setWindowModality(Qt::WindowModal);
393 0 : if ((newCode = ep.exec()) != -1) {
394 0 : maxPitchA->setText(midiCodeToStr(newCode));
395 0 : m_maxPitchA = newCode;
396 : }
397 0 : }
398 :
399 0 : void EditStaff::minPitchPClicked()
400 : {
401 : int newCode;
402 0 : EditPitch ep(this, m_instrument.minPitchP());
403 0 : ep.setWindowModality(Qt::WindowModal);
404 0 : if ((newCode = ep.exec()) != -1) {
405 0 : minPitchP->setText(midiCodeToStr(newCode));
406 0 : m_minPitchP = newCode;
407 : }
408 0 : }
409 :
410 0 : void EditStaff::maxPitchPClicked()
411 : {
412 : int newCode;
413 0 : EditPitch ep(this, m_instrument.maxPitchP());
414 0 : ep.setWindowModality(Qt::WindowModal);
415 0 : if ((newCode = ep.exec()) != -1) {
416 0 : maxPitchP->setText(midiCodeToStr(newCode));
417 0 : m_maxPitchP = newCode;
418 : }
419 0 : }
420 :
421 0 : void EditStaff::lineDistanceChanged()
422 : {
423 0 : m_staff->staffType(m_tick)->setLineDistance(mu::engraving::Spatium(lineDistance->value()));
424 0 : }
425 :
426 0 : void EditStaff::numOfLinesChanged()
427 : {
428 0 : m_staff->staffType(m_tick)->setLines(lines->value());
429 0 : }
430 :
431 0 : void EditStaff::showClefChanged()
432 : {
433 0 : m_staff->staffType(m_tick)->setGenClef(showClef->isChecked());
434 0 : }
435 :
436 0 : void EditStaff::showTimeSigChanged()
437 : {
438 0 : m_staff->staffType(m_tick)->setGenTimesig(showTimesig->isChecked());
439 0 : }
440 :
441 0 : void EditStaff::showBarlinesChanged()
442 : {
443 0 : m_staff->staffType(m_tick)->setShowBarlines(showBarlines->isChecked());
444 0 : }
445 :
446 0 : void EditStaff::invisibleChanged()
447 : {
448 0 : m_staff->staffType(m_tick)->setInvisible(invisible->isChecked());
449 0 : }
450 :
451 0 : void EditStaff::instrumentLabelVisibilityChanged(int index)
452 : {
453 0 : m_staff->staffType(m_tick)->setInstrumentLabelVisibility(static_cast<InstrumentLabelVisibility>(index));
454 0 : }
455 :
456 0 : void EditStaff::colorChanged()
457 : {
458 0 : m_staff->staffType(m_tick)->setColor(color->color());
459 0 : }
460 :
461 0 : void EditStaff::magChanged(double newValue)
462 : {
463 0 : m_staff->staffType(m_tick)->setUserMag(newValue / 100.0);
464 0 : }
465 :
466 0 : void EditStaff::transpositionChanged()
467 : {
468 : // non-transposing or octave-transposing instrument
469 : // don't show transp_preferSharpFlat
470 0 : if ((iList->currentIndex() == 0) || (iList->currentIndex() == 25)) {
471 0 : transp_PreferSharpFlat->setVisible(false);
472 : } else {
473 0 : transp_PreferSharpFlat->setVisible(true);
474 : }
475 0 : }
476 :
477 0 : void EditStaff::longNameChanged()
478 : {
479 0 : m_staff->staffType(m_tick)->setLongName(longStaffName->toPlainText());
480 0 : }
481 :
482 0 : void EditStaff::shortNameChanged()
483 : {
484 0 : m_staff->staffType(m_tick)->setShortName(shortStaffName->toPlainText());
485 0 : }
486 :
487 0 : void EditStaff::useCustomNameChanged(bool useCustom)
488 : {
489 0 : useDefaultName->setChecked(!useCustom);
490 0 : useCustomName->setChecked(useCustom);
491 0 : customNameGroupbox->setEnabled(useCustom);
492 0 : }
493 :
494 0 : void EditStaff::useCustomGroupNameChanged(bool useCustomGroup)
495 : {
496 0 : useDefaultGroupName->setChecked(!useCustomGroup);
497 0 : useCustomGroupName->setChecked(useCustomGroup);
498 0 : customGroupNameGroupbox->setEnabled(useCustomGroup);
499 0 : }
500 :
501 0 : void EditStaff::useCustomIndividualNameChanged(bool useCustomIndividual)
502 : {
503 0 : useDefaultIndividualName->setChecked(!useCustomIndividual);
504 0 : useCustomIndividualName->setChecked(useCustomIndividual);
505 0 : customIndividualNameGroupbox->setEnabled(useCustomIndividual);
506 0 : }
507 :
508 0 : INotationPtr EditStaff::notation() const
509 : {
510 0 : return projectSession()->currentNotation();
511 : }
512 :
513 0 : IMasterNotationPtr EditStaff::masterNotation() const
514 : {
515 0 : return projectSession()->masterNotation();
516 : }
517 :
518 0 : INotationPartsPtr EditStaff::notationParts() const
519 : {
520 0 : return notation() ? notation()->parts() : nullptr;
521 : }
522 :
523 0 : INotationPartsPtr EditStaff::masterNotationParts() const
524 : {
525 0 : return masterNotation() ? masterNotation()->parts() : nullptr;
526 : }
527 :
528 0 : void EditStaff::initStaff()
529 : {
530 0 : const INotationPtr notation = this->notation();
531 0 : const INotationInteractionPtr interaction = notation ? notation->interaction() : nullptr;
532 0 : auto context = interaction ? interaction->hitElementContext() : INotationInteraction::HitElementContext();
533 0 : const EngravingItem* element = context.element;
534 0 : Staff* staff = context.staff;
535 :
536 0 : if (interaction && !element) {
537 0 : const INotationSelectionPtr selection = interaction->selection();
538 0 : if (selection->isRange()) {
539 0 : const INotationSelectionRangePtr range = selection->range();
540 0 : element = range->measureBaseRange().endMeasureBase;
541 0 : staff = element ? element->score()->staff(range->endStaffIndex() - 1) : nullptr;
542 0 : }
543 0 : }
544 :
545 0 : IF_ASSERT_FAILED(element) {
546 0 : return;
547 : }
548 :
549 0 : Fraction tick = { -1, 1 };
550 0 : if (element->isChordRest()) {
551 0 : tick = mu::engraving::toChordRest(element)->tick();
552 0 : } else if (element->isNote()) {
553 0 : tick = mu::engraving::toNote(element)->chord()->tick();
554 0 : } else if (element->isMeasureBase()) {
555 0 : tick = mu::engraving::toMeasureBase(element)->tick();
556 0 : } else if (element->isInstrumentName()) {
557 0 : const mu::engraving::System* system = mu::engraving::toSystem(mu::engraving::toInstrumentName(element)->ownershipParent());
558 0 : const Measure* measure = system ? system->firstMeasure() : nullptr;
559 0 : staff = element->staff();
560 :
561 0 : if (measure) {
562 0 : tick = measure->tick();
563 : }
564 : }
565 :
566 0 : IF_ASSERT_FAILED(staff) {
567 0 : return;
568 : }
569 :
570 0 : setStaff(staff, tick);
571 0 : }
572 :
573 0 : Instrument EditStaff::instrument() const
574 : {
575 0 : INotationPartsPtr notationParts = this->notationParts();
576 0 : if (!notationParts) {
577 0 : return Instrument();
578 : }
579 :
580 0 : const Part* part = notationParts->part(m_instrumentKey.partId);
581 0 : return part ? *part->instrument(m_instrumentKey.tick) : Instrument();
582 0 : }
583 :
584 0 : std::vector<InstrumentKey> EditStaff::otherInstrumentsInSameGroup() const
585 : {
586 0 : Part* part = m_orgStaff->part();
587 0 : Score* score = m_orgStaff->score();
588 :
589 0 : Measure* measure = score->tick2measure(m_tick);
590 0 : IF_ASSERT_FAILED(measure) {
591 0 : return {};
592 : }
593 :
594 0 : System* system = measure->system();
595 0 : IF_ASSERT_FAILED(system) {
596 0 : return {};
597 : }
598 :
599 0 : const std::unordered_map<Part*, InstrumentName*>& partsWithGroupName = system->ldata()->partsWithGroupName();
600 0 : if (!muse::contains(partsWithGroupName, part)) {
601 0 : return {};
602 : }
603 :
604 0 : InstrumentName* groupName = partsWithGroupName.at(part);
605 :
606 0 : std::vector<InstrumentKey> result;
607 0 : for (const auto& pair : partsWithGroupName) {
608 0 : Part* p = pair.first;
609 0 : if (p == part) {
610 0 : continue;
611 : }
612 :
613 0 : InstrumentName* n = pair.second;
614 0 : if (n == groupName) {
615 0 : Instrument* instrument = p->instrument(m_tick);
616 0 : InstrumentKey key;
617 0 : key.instrumentId = instrument->id();
618 0 : key.partId = p->id();
619 0 : key.tick = Fraction::fromTicks(muse::findLessOrEqual(p->instruments(), m_tick.ticks())->first);
620 0 : result.push_back(key);
621 0 : }
622 : }
623 :
624 0 : return result;
625 0 : }
626 :
627 0 : void EditStaff::applyStaffProperties()
628 : {
629 0 : StaffConfig config;
630 0 : config.visible = m_orgStaff->visible();
631 :
632 0 : config.userDistance = Spatium(spinExtraDistance->value());
633 0 : config.hideSystemBarline = hideSystemBarLine->isChecked();
634 0 : config.mergeMatchingRests = static_cast<AutoOnOff>(mergeMatchingRests->currentIndex());
635 0 : config.clefTypeList = m_staff->defaultClefType();
636 0 : config.staffType = *m_staff->staffType(m_tick);
637 0 : config.reflectTranspositionInLinkedTab = !noReflectTranspositionInLinkedTab->isChecked();
638 :
639 0 : notationParts()->setStaffConfig(m_orgStaff->id(), config, m_tick);
640 0 : }
641 :
642 0 : void EditStaff::applyPartProperties()
643 : {
644 0 : Part* part = m_orgStaff->part();
645 :
646 0 : String _sn = shortName->toPlainText();
647 0 : String _ln = longName->toPlainText();
648 0 : String _ssn = shortStaffName->toPlainText();
649 0 : String _lsn = longStaffName->toPlainText();
650 0 : if (!mu::engraving::Text::validateText(_sn) || !mu::engraving::Text::validateText(_ln)
651 0 : || !mu::engraving::Text::validateText(_ssn) || !mu::engraving::Text::validateText(_lsn)) {
652 0 : interactive()->warning(muse::trc("notation/staffpartproperties", "Invalid instrument name"),
653 0 : muse::trc("notation/staffpartproperties", "The instrument name is invalid."));
654 0 : return;
655 : }
656 :
657 0 : QString sn = _sn;
658 0 : QString ln = _ln;
659 0 : QString ssn = _ssn;
660 0 : QString lsn = _lsn;
661 0 : shortName->setPlainText(sn); // show the fixed text
662 0 : longName->setPlainText(ln);
663 0 : shortStaffName->setPlainText(ssn);
664 0 : longStaffName->setPlainText(lsn);
665 :
666 0 : int intervalIdx = iList->currentIndex();
667 0 : bool upFlag = up->isChecked();
668 :
669 0 : mu::engraving::Interval interval = Interval::allIntervals[intervalIdx];
670 0 : interval.diatonic += static_cast<int8_t>(octave->value() * 7);
671 0 : interval.chromatic += static_cast<int8_t>(octave->value() * 12);
672 :
673 0 : if (!upFlag) {
674 0 : interval.flip();
675 : }
676 :
677 0 : Instrument prevInstrument = instrument();
678 0 : m_instrument.setTranspose(interval);
679 0 : m_instrument.setMinPitchA(m_minPitchA);
680 0 : m_instrument.setMaxPitchA(m_maxPitchA);
681 0 : m_instrument.setMinPitchP(m_minPitchP);
682 0 : m_instrument.setMaxPitchP(m_maxPitchP);
683 :
684 0 : m_instrument.setShortName(String::fromQString(sn));
685 0 : m_instrument.setLongName(String::fromQString(ln));
686 0 : m_instrument.setNumber(instrumentNumber->value());
687 :
688 0 : InstrumentLabel& name = m_instrument.instrumentLabel();
689 0 : name.setShowNumberLong(showNumberLong->isChecked());
690 0 : name.setShowNumberShort(showNumberShort->isChecked());
691 0 : name.setShowTranspositionLong(showTranspositionLong->isChecked());
692 0 : name.setShowTranspositionShort(showTranspositionShort->isChecked());
693 0 : name.setTransposition(transpositionLabel->text());
694 0 : name.setUseCustomName(useCustomName->isChecked());
695 0 : name.setCustomNameLong(customLongName->toPlainText());
696 0 : name.setCustomNameShort(customShortName->toPlainText());
697 0 : name.setAllowGroupName(allowGroupName->isChecked());
698 0 : name.setUseCustomGroupName(useCustomGroupName->isChecked());
699 0 : name.setCustomNameLongGroup(customLongNameGroup->toPlainText());
700 0 : name.setCustomNameShortGroup(customShortNameGroup->toPlainText());
701 0 : name.setUseCustomIndividualName(useCustomIndividualName->isChecked());
702 0 : name.setCustomNameLongIndividual(customLongNameIndividual->toPlainText());
703 0 : name.setCustomNameShortIndividual(customShortNameIndividual->toPlainText());
704 :
705 0 : if (name.useCustomGroupName() && (name.customNameLongGroup().empty() || name.customNameShortGroup().empty())) {
706 0 : interactive()->warning(muse::trc("notation/staffpartproperties", "Invalid group name"),
707 0 : muse::trc("notation/staffpartproperties", "Custom group names cannot be empty."));
708 0 : return;
709 : }
710 :
711 0 : size_t staffIdxInPart = muse::indexOf(part->staves(), m_orgStaff);
712 0 : DO_ASSERT(staffIdxInPart != muse::nidx);
713 :
714 0 : bool instrumentChanged = false;
715 :
716 0 : if (m_instrument.id() != prevInstrument.id()) {
717 0 : masterNotationParts()->replaceInstrument(m_instrumentKey, m_instrument);
718 0 : instrumentChanged = true;
719 0 : } else if (m_instrument != prevInstrument) {
720 0 : bool groupNameChanged = name.useCustomGroupName() != prevInstrument.instrumentLabel().useCustomGroupName()
721 0 : || name.customNameLongGroup() != prevInstrument.instrumentLabel().customNameLongGroup()
722 0 : || name.customNameShortGroup() != prevInstrument.instrumentLabel().customNameShortGroup();
723 0 : if (groupNameChanged) {
724 0 : notationParts()->setInstrumentGroupNameOptions(otherInstrumentsInSameGroup(),
725 0 : name.useCustomGroupName(), name.customNameLongGroup(),
726 0 : name.customNameShortGroup());
727 : }
728 0 : notationParts()->replaceInstrument(m_instrumentKey, m_instrument);
729 0 : instrumentChanged = true;
730 : }
731 :
732 0 : SharpFlat newSharpFlat = SharpFlat(preferSharpFlat->currentIndex());
733 0 : if ((iList->currentIndex() == 0) || (iList->currentIndex() == 25)) {
734 : // instrument becomes non/octave-transposing, preferSharpFlat isn't useful anymore
735 0 : newSharpFlat = SharpFlat::AUTO;
736 : }
737 :
738 0 : if (part->preferSharpFlat() != newSharpFlat) {
739 0 : notationParts()->setPartSharpFlat(m_instrumentKey.partId, newSharpFlat);
740 : }
741 :
742 0 : if (instrumentChanged) {
743 0 : m_instrumentKey.instrumentId = m_instrument.id();
744 : }
745 0 : }
746 :
747 0 : void EditStaff::showReplaceInstrumentDialog()
748 : {
749 0 : async::Promise<InstrumentTemplate> templ = selectInstrumentsScenario()->selectInstrument(m_instrumentKey);
750 0 : templ.onResolve(this, [this](const InstrumentTemplate& val) {
751 0 : const StaffType* staffType = val.staffTypePreset;
752 0 : if (!staffType) {
753 0 : staffType = StaffType::getDefaultPreset(StaffGroup::STANDARD);
754 : }
755 :
756 0 : m_instrument = Instrument::fromTemplate(&val);
757 0 : m_staff->setStaffType(m_tick, *staffType);
758 0 : m_staff->setDefaultClefType(m_instrument.clefType(0));
759 :
760 0 : updateInstrument();
761 0 : updateStaffType(*staffType);
762 0 : });
763 0 : }
764 :
765 0 : void EditStaff::editStringDataClicked()
766 : {
767 0 : int frets = m_instrument.stringData()->frets();
768 0 : std::vector<mu::engraving::instrString> stringList = m_instrument.stringData()->stringList();
769 :
770 0 : EditStringData* esd = new EditStringData(this, stringList, frets);
771 :
772 0 : if (esd->exec()) {
773 0 : frets = esd->frets();
774 0 : stringList = esd->strings();
775 :
776 0 : mu::engraving::StringData stringData(frets, stringList);
777 :
778 : // update instrument pitch ranges as necessary
779 0 : if (stringList.size() > 0) {
780 : // get new string range bottom and top
781 : // as we have to choose an int size, INT16 are surely beyond midi pitch limits
782 0 : int oldHighestStringPitch = INT16_MIN;
783 0 : int highestStringPitch = INT16_MIN;
784 0 : int lowestStringPitch = INT16_MAX;
785 0 : for (const mu::engraving::instrString& str : stringList) {
786 0 : if (str.pitch > highestStringPitch) {
787 0 : highestStringPitch = str.pitch;
788 : }
789 0 : if (str.pitch < lowestStringPitch) {
790 0 : lowestStringPitch = str.pitch;
791 : }
792 : }
793 : // get old string range bottom
794 0 : for (const mu::engraving::instrString& str : m_instrument.stringData()->stringList()) {
795 0 : if (str.pitch > oldHighestStringPitch) {
796 0 : oldHighestStringPitch = str.pitch;
797 : }
798 : }
799 : // if there were no string, arbitrarily set old top to maxPitchA
800 0 : if (oldHighestStringPitch == INT16_MIN) {
801 0 : oldHighestStringPitch = m_instrument.maxPitchA();
802 : }
803 :
804 : // range bottom is surely the pitch of the lowest string
805 0 : m_instrument.setMinPitchA(lowestStringPitch);
806 0 : m_instrument.setMinPitchP(lowestStringPitch);
807 :
808 : // range top should keep the same interval with the highest string it has now
809 0 : m_instrument.setMaxPitchA(m_instrument.maxPitchA() + highestStringPitch - oldHighestStringPitch);
810 0 : m_instrument.setMaxPitchP(m_instrument.maxPitchP() + highestStringPitch - oldHighestStringPitch);
811 :
812 : // update dlg controls
813 0 : minPitchA->setText(midiCodeToStr(m_instrument.minPitchA()));
814 0 : maxPitchA->setText(midiCodeToStr(m_instrument.maxPitchA()));
815 0 : minPitchP->setText(midiCodeToStr(m_instrument.minPitchP()));
816 0 : maxPitchP->setText(midiCodeToStr(m_instrument.maxPitchP()));
817 : // if no longer there is any string, leave everything as it is now
818 : }
819 :
820 : // update instrument data and dlg controls
821 0 : m_instrument.setStringData(stringData);
822 0 : numOfStrings->setText(QString::number(stringData.strings()));
823 0 : }
824 0 : }
825 :
826 0 : QString EditStaff::midiCodeToStr(int midiCode)
827 : {
828 0 : return QString::fromStdString(muse::pitchToString(midiCode));
829 : }
830 :
831 0 : void EditStaff::showStaffTypeDialog()
832 : {
833 0 : editStaffTypeDialog->setStaffType(m_staff->staffType(m_tick));
834 0 : editStaffTypeDialog->setInstrument(m_instrument);
835 :
836 0 : if (editStaffTypeDialog->exec()) {
837 0 : m_staff->setStaffType(m_tick, editStaffTypeDialog->getStaffType());
838 0 : updateStaffType(editStaffTypeDialog->getStaffType());
839 : }
840 0 : }
|