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 "timesignaturepropertiesdialog.h"
24 :
25 : #include <QRegularExpression>
26 :
27 : #include "engraving/iengravingfont.h"
28 : #include "engraving/dom/masterscore.h"
29 : #include "engraving/dom/measure.h"
30 : #include "engraving/dom/score.h"
31 : #include "engraving/dom/timesig.h"
32 :
33 : #include "notation/inotation.h"
34 : #include "notation/inotationinteraction.h"
35 : #include "notation/inotationundostack.h" // IWYU pragma: keep
36 :
37 : #include "ui/view/musicalsymbolcodes.h"
38 : #include "ui/view/widgetstatestore.h"
39 :
40 : static const QString TIME_SIGNATURE_PROPERTIES_DIALOG_NAME("TimeSignaturePropertiesDialog");
41 :
42 : using namespace mu::palette;
43 : using namespace muse::ui;
44 : using namespace mu::notation;
45 : using namespace mu::engraving;
46 :
47 : //---------------------------------------------------------
48 : // TimeSigProperties
49 : //---------------------------------------------------------
50 :
51 0 : TimeSignaturePropertiesDialog::TimeSignaturePropertiesDialog(QWidget* parent)
52 0 : : muse::ui::WidgetDialog(parent)
53 : {
54 0 : setObjectName(TIME_SIGNATURE_PROPERTIES_DIALOG_NAME);
55 0 : setupUi(this);
56 0 : }
57 :
58 0 : TimeSignaturePropertiesDialog::~TimeSignaturePropertiesDialog()
59 : {
60 0 : delete m_editedTimeSig;
61 0 : }
62 :
63 0 : void TimeSignaturePropertiesDialog::classBegin()
64 : {
65 0 : groups->classBegin();
66 :
67 0 : QString musicalFontFamily = QString::fromStdString(uiConfiguration()->musicalFontFamily());
68 0 : int musicalFontSize = uiConfiguration()->musicalFontSize();
69 :
70 0 : QString radioButtonStyle = QString("QRadioButton { font-family: %1; font-size: %2px }")
71 0 : .arg(musicalFontFamily)
72 0 : .arg(musicalFontSize);
73 :
74 0 : fourfourButton->setStyleSheet(radioButtonStyle);
75 0 : fourfourButton->setText(musicalSymbolToString(MusicalSymbolCodes::Code::TIMESIG_COMMON));
76 0 : fourfourButton->setMaximumHeight(30);
77 0 : allaBreveButton->setStyleSheet(radioButtonStyle);
78 0 : allaBreveButton->setText(musicalSymbolToString(MusicalSymbolCodes::Code::TIMESIG_CUT));
79 0 : allaBreveButton->setMaximumHeight(30);
80 :
81 0 : setWindowFlag(Qt::WindowContextHelpButtonHint, false);
82 :
83 0 : const INotationInteractionPtr interaction = notation() ? notation()->interaction() : nullptr;
84 0 : EngravingItem* element = interaction ? interaction->hitElementContext().element : nullptr;
85 0 : IF_ASSERT_FAILED(element && element->isTimeSig()) {
86 0 : return;
87 : }
88 :
89 0 : m_originTimeSig = toTimeSig(element);
90 0 : m_editedTimeSig = m_originTimeSig->clone();
91 :
92 0 : zText->setText(m_editedTimeSig->numeratorString());
93 0 : nText->setText(m_editedTimeSig->denominatorString());
94 : // set validators for numerator and denominator strings
95 : // which only accept '+', '*' (or 'x'), '(', ')', '[', ']', digits and some time symb conventional representations
96 0 : QRegularExpression regex("[0-9+COXx()\\[\\]\\*\\x00A2\\x00D7\\x00D8\\x00BD\\x00BC]*");
97 0 : QValidator* validator = new QRegularExpressionValidator(regex, this);
98 0 : zText->setValidator(validator);
99 0 : nText->setValidator(validator);
100 :
101 0 : Fraction nominal = m_editedTimeSig->sig() / m_editedTimeSig->stretch();
102 0 : nominal.reduce();
103 :
104 : // TODO: fix https://musescore.org/en/node/42341
105 : // for now, editing of actual (local) time sig is disabled in dialog
106 : // but more importantly, the dialog should make it clear that this is "local" change only
107 : // and not normally the right way to add 7/4 to a score
108 0 : switch (m_editedTimeSig->timeSigType()) {
109 0 : case TimeSigType::NORMAL:
110 0 : textButton->setChecked(true);
111 0 : break;
112 0 : case TimeSigType::FOUR_FOUR:
113 0 : fourfourButton->setChecked(true);
114 0 : break;
115 0 : case TimeSigType::ALLA_BREVE:
116 0 : allaBreveButton->setChecked(true);
117 0 : break;
118 0 : case TimeSigType::CUT_BACH:
119 : // cutBachButton->setChecked(true);
120 : // break;
121 : case TimeSigType::CUT_TRIPLE:
122 : // cutTripleButton->setChecked(true);
123 0 : break;
124 : }
125 :
126 : static const SymIdList prolatioList = {
127 : SymId::mensuralProlation1, // tempus perfectum, prol. perfecta
128 : SymId::mensuralProlation2, // tempus perfectum, prol. imperfecta
129 : SymId::mensuralProlation3, // tempus perfectum, prol. imperfecta, dimin.
130 : SymId::mensuralProlation4, // tempus perfectum, prol. perfecta, dimin.
131 : SymId::mensuralProlation5, // tempus imperf., prol. perfecta
132 : SymId::mensuralProlation6, // tempus imperf., prol. imperfecta
133 : SymId::mensuralProlation7, // tempus imperf., prol. imperfecta, dimin. 1
134 : SymId::mensuralProlation8, // tempus imperf., prol. imperfecta, dimin. 2
135 : SymId::mensuralProlation9, // tempus imperf., prol. imperfecta, dimin. 3
136 : SymId::mensuralProlation10, // tempus imperf., prol. imperfecta, dimin. 4
137 : SymId::mensuralProlation11, // tempus imperf., prol. imperfecta, dimin. 5
138 0 : };
139 :
140 0 : IEngravingFontPtr symbolFont = paletteScoreProvider()->paletteScore()->engravingFont();
141 :
142 0 : otherCombo->clear();
143 0 : otherCombo->setStyleSheet(QString("QComboBox { font-family: \"%1 Text\"; font-size: %2px; max-height: 30px } ")
144 0 : .arg(QString::fromStdString(symbolFont->family())).arg(musicalFontSize));
145 :
146 0 : int idx = 0;
147 0 : for (SymId prolatio : prolatioList) {
148 0 : const QString& str = symbolFont->toString(prolatio);
149 0 : if (str.size() > 0) {
150 0 : otherCombo->addItem(str, int(prolatio));
151 :
152 : // if time sig matches this symbol string, set as selected
153 0 : if (m_editedTimeSig->timeSigType() == TimeSigType::NORMAL && m_editedTimeSig->denominatorString().isEmpty()
154 0 : && m_editedTimeSig->numeratorString() == str) {
155 0 : textButton->setChecked(false);
156 0 : otherButton->setChecked(true);
157 0 : otherCombo->setCurrentIndex(idx);
158 :
159 : // set the custom text fields to empty
160 0 : zText->setText(QString());
161 0 : nText->setText(QString());
162 : }
163 : }
164 0 : idx++;
165 0 : }
166 :
167 0 : Groups g = m_editedTimeSig->groups();
168 0 : if (g.empty()) {
169 0 : g = Groups::endings(m_editedTimeSig->sig()); // initialize with default
170 : }
171 0 : groups->setSig(m_editedTimeSig->sig(), g, m_editedTimeSig->numeratorString(), m_editedTimeSig->denominatorString());
172 0 : }
173 :
174 0 : void TimeSignaturePropertiesDialog::showEvent(QShowEvent* event)
175 : {
176 0 : WidgetStateStore::restoreGeometry(this);
177 0 : QDialog::showEvent(event);
178 0 : }
179 :
180 0 : void TimeSignaturePropertiesDialog::hideEvent(QHideEvent* event)
181 : {
182 0 : WidgetStateStore::saveGeometry(this);
183 0 : QDialog::hideEvent(event);
184 0 : }
185 :
186 : //---------------------------------------------------------
187 : // accept
188 : //---------------------------------------------------------
189 :
190 0 : void TimeSignaturePropertiesDialog::accept()
191 : {
192 0 : auto notation = this->notation();
193 0 : if (!notation || !m_editedTimeSig || !m_originTimeSig) {
194 0 : return;
195 : }
196 :
197 0 : TimeSigType ts = TimeSigType::NORMAL;
198 0 : if (textButton->isChecked() || otherButton->isChecked()) {
199 0 : ts = TimeSigType::NORMAL;
200 0 : } else if (fourfourButton->isChecked()) {
201 0 : ts = TimeSigType::FOUR_FOUR;
202 0 : } else if (allaBreveButton->isChecked()) {
203 0 : ts = TimeSigType::ALLA_BREVE;
204 : }
205 :
206 0 : m_editedTimeSig->setProperty(mu::engraving::Pid::TIMESIG_TYPE, static_cast<int>(ts));
207 :
208 0 : if (zText->text() != m_editedTimeSig->numeratorString()) {
209 0 : m_editedTimeSig->setNumeratorString(zText->text());
210 : }
211 0 : if (nText->text() != m_editedTimeSig->denominatorString()) {
212 0 : m_editedTimeSig->setDenominatorString(nText->text());
213 : }
214 :
215 0 : if (otherButton->isChecked()) {
216 0 : IEngravingFontPtr symbolFont = m_editedTimeSig->score()->engravingFont();
217 0 : SymId symId = (SymId)(otherCombo->itemData(otherCombo->currentIndex()).toInt());
218 : // ...and set numerator to font string for symbol and denominator to empty string
219 0 : m_editedTimeSig->setNumeratorString(symbolFont->toString(symId));
220 0 : m_editedTimeSig->setDenominatorString(QString());
221 0 : }
222 :
223 0 : Groups g = groups->groups();
224 0 : m_editedTimeSig->setGroups(g);
225 :
226 0 : notation->undoStack()->prepareChanges(TranslatableString("undoableAction", "Edit time signature properties"));
227 :
228 : // Change linked mmr timesigs too
229 0 : for (EngravingObject* obj : m_originTimeSig->linkList()) {
230 0 : TimeSig* timeSig = toTimeSig(obj);
231 0 : if (timeSig == m_originTimeSig || (timeSig->track() == m_originTimeSig->track() && timeSig->score() == m_originTimeSig->score())) {
232 0 : timeSig->undoChangeProperty(Pid::TIMESIG_TYPE, int(m_editedTimeSig->timeSigType()));
233 0 : timeSig->undoChangeProperty(Pid::SHOW_COURTESY, m_editedTimeSig->showCourtesySig());
234 0 : timeSig->undoChangeProperty(Pid::NUMERATOR_STRING, m_editedTimeSig->numeratorString());
235 0 : timeSig->undoChangeProperty(Pid::DENOMINATOR_STRING, m_editedTimeSig->denominatorString());
236 0 : timeSig->undoChangeProperty(Pid::GROUP_NODES, g.nodes());
237 : }
238 0 : }
239 :
240 0 : if (m_editedTimeSig->sig() != m_originTimeSig->sig()) {
241 0 : notation->interaction()->addTimeSignature(m_originTimeSig->measure(), m_originTimeSig->staffIdx(), m_editedTimeSig);
242 : }
243 :
244 0 : m_originTimeSig->triggerLayoutAll();
245 0 : notation->undoStack()->commitChanges();
246 0 : notation->notationChanged().send(muse::RectF());
247 :
248 0 : QDialog::accept();
249 0 : }
250 :
251 0 : INotationPtr TimeSignaturePropertiesDialog::notation() const
252 : {
253 0 : return projectSession()->currentNotation();
254 : }
|