1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 
19 #include "TimeSignatureDialog.h"
20 
21 #include "misc/ConfigGroups.h"
22 #include "base/Composition.h"
23 #include "base/NotationTypes.h"
24 #include "gui/widgets/TimeWidget.h"
25 #include "gui/widgets/BigArrowButton.h"
26 #include "misc/Strings.h"
27 
28 #include <QApplication>
29 #include <QSettings>
30 #include <QDialog>
31 #include <QDialogButtonBox>
32 #include <QGroupBox>
33 #include <QCheckBox>
34 #include <QFont>
TimeDialog(QWidget * parent,QString title,Composition * composition,timeT defaultTime,bool constrainToCompositionDuration)35 #include <QLabel>
36 #include <QObject>
37 #include <QRadioButton>
38 #include <QString>
39 #include <QWidget>
40 #include <QVBoxLayout>
41 #include <QHBoxLayout>
42 #include <QUrl>
43 #include <QDesktopServices>
44 
45 
46 namespace Rosegarden
47 {
48 
49 TimeSignatureDialog::TimeSignatureDialog(QWidget *parent,
50                                          Composition *composition,
51                                          timeT insertionTime,
52                                          TimeSignature sig,
53                                          bool timeEditable,
54                                          QString explanatoryText) :
55         QDialog(parent),
56         m_composition(composition),
57         m_timeSignature(sig),
58         m_time(insertionTime),
59         m_numLabel(nullptr),
60         m_denomLabel(nullptr),
61         m_explanatoryLabel(nullptr),
62         m_commonTimeButton(nullptr),
63         m_hideSignatureButton(nullptr),
64         m_normalizeRestsButton(nullptr),
65         m_asGivenButton(nullptr),
66         m_startOfBarButton(nullptr),
67         m_timeEditor(nullptr)
68 {
69     setModal(true);
70     setWindowTitle(tr("Time Signature"));
71     setObjectName("MinorDialog");
72 
73     static QFont *timeSigFont = nullptr;
74 
75     if (timeSigFont == nullptr) {
76         timeSigFont = new QFont("new century schoolbook", 8, QFont::Bold);
77         timeSigFont->setPixelSize(20);
78     }
79 
80     QVBoxLayout *vboxLayout = new QVBoxLayout;
81     setLayout(vboxLayout);
82 
83     QGroupBox *groupBox = new QGroupBox(tr("Time signature"));
84     QVBoxLayout *groupBoxLayout = new QVBoxLayout;
85     groupBoxLayout->setSpacing(0);
86     vboxLayout->addWidget(groupBox);
87 
88     QWidget *numBox = new QWidget;
89     QHBoxLayout *numBoxLayout = new QHBoxLayout;
90     groupBoxLayout->addWidget(numBox);
91 
92     QWidget *denomBox = new QWidget;
93     QHBoxLayout *denomBoxLayout = new QHBoxLayout;
94     groupBoxLayout->addWidget(denomBox);
95 
96     QLabel *explanatoryLabel = nullptr;
97     if (!explanatoryText.isEmpty()) {
98         explanatoryLabel = new QLabel(explanatoryText, groupBox);
99         groupBoxLayout->addWidget(explanatoryLabel);
100     }
101     groupBox->setLayout(groupBoxLayout);
102 
103     BigArrowButton *numDown = new BigArrowButton(Qt::LeftArrow);
104     numBoxLayout->addWidget(numDown);
105     BigArrowButton *denomDown = new BigArrowButton(Qt::LeftArrow);
106     denomBoxLayout->addWidget(denomDown);
107 
108     m_numLabel = new QLabel(QString("%1").arg(m_timeSignature.getNumerator()), numBox );
109     numBoxLayout->addWidget(m_numLabel);
110     m_denomLabel = new QLabel(QString("%1").arg(m_timeSignature.getDenominator()), denomBox );
111     denomBoxLayout->addWidget(m_denomLabel);
112 
113     m_numLabel->setAlignment(Qt::AlignCenter);
114     m_denomLabel->setAlignment(Qt::AlignCenter);
115 
116     m_numLabel->setFont(*timeSigFont);
117     m_denomLabel->setFont(*timeSigFont);
118 
119     BigArrowButton *numUp = new BigArrowButton(Qt::RightArrow);
120     numBoxLayout->addWidget(numUp);
121     numBox->setLayout(numBoxLayout);
122     BigArrowButton *denomUp = new BigArrowButton(Qt::RightArrow);
123     denomBoxLayout->addWidget(denomUp);
124     denomBox->setLayout(denomBoxLayout);
125 
126     QObject::connect(numDown, &QAbstractButton::clicked, this, &TimeSignatureDialog::slotNumDown);
127     QObject::connect(numUp, &QAbstractButton::clicked, this, &TimeSignatureDialog::slotNumUp);
128     QObject::connect(denomDown, &QAbstractButton::clicked, this, &TimeSignatureDialog::slotDenomDown);
129     QObject::connect(denomUp, &QAbstractButton::clicked, this, &TimeSignatureDialog::slotDenomUp);
130 
131     if (timeEditable) {
132 
133         m_timeEditor = new TimeWidget
134                        (tr("Time where signature takes effect"),
135                         this,
136                         composition,
137                         m_time,
138                         true);
139         vboxLayout->addWidget(m_timeEditor);
140         m_asGivenButton = nullptr;
141         m_startOfBarButton = nullptr;
142 
143     } else {
144 
145         m_timeEditor = nullptr;
146 
147         groupBox = new QGroupBox(tr("Scope"));
148         groupBoxLayout = new QVBoxLayout;
149         vboxLayout->addWidget(groupBox);
150 
151         int barNo = composition->getBarNumber(m_time);
152         bool atStartOfBar = (m_time == composition->getBarStart(barNo));
153 
154         if (!atStartOfBar) {
155 
156             QString scopeText;
157 
158             if (barNo != 0 || !atStartOfBar) {
159                 if (atStartOfBar) {
160                     scopeText = QString
161                                 (tr("Insertion point is at start of measure %1."))
162                                 .arg(barNo + 1);
163                 } else {
164                     scopeText = QString
165                                 (tr("Insertion point is in the middle of measure %1."))
166                                 .arg(barNo + 1);
167                 }
168             } else {
169                 scopeText = QString
170                             (tr("Insertion point is at start of composition."));
171             }
172 
173             groupBoxLayout->addWidget(new QLabel(scopeText));
174             m_asGivenButton = new QRadioButton
175                               (tr("Start measure %1 here").arg(barNo + 2), groupBox);
176             groupBoxLayout->addWidget(m_asGivenButton);
177             if (!atStartOfBar) {
178                 m_startOfBarButton = new QRadioButton
179                                      (tr("Change time from start of measure %1")
180                                        .arg(barNo + 1), groupBox);
181                 groupBoxLayout->addWidget(m_startOfBarButton);
182                 m_startOfBarButton->setChecked(true);
183             } else {
184                 m_asGivenButton->setChecked(true);
185             }
186         } else {
187             groupBoxLayout->addWidget(
188                 new QLabel(tr("Time change will take effect at the start of"
189                                 " measure %1.").arg(barNo + 1)));
190         }
191     }
192     groupBox->setLayout(groupBoxLayout);
193 
194     groupBox = new QGroupBox(tr("Options"));
195     groupBoxLayout = new QVBoxLayout;
196     vboxLayout->addWidget(groupBox);
197     QSettings settings;
198     settings.beginGroup( GeneralOptionsConfigGroup );
199 
200     m_hideSignatureButton = new QCheckBox(tr("Hide the time signature"));
201     groupBoxLayout->addWidget(m_hideSignatureButton);
202     m_hideSignatureButton->setChecked
203     ( qStrToBool( settings.value("timesigdialogmakehidden", "false" ) ) );
204 
205     m_hideBarsButton = new QCheckBox(tr("Hide the affected bar lines"));
206     groupBoxLayout->addWidget(m_hideBarsButton);
207     m_hideBarsButton->setChecked
208     ( qStrToBool( settings.value("timesigdialogmakehiddenbars", "false" ) ) );
209 
210     m_commonTimeButton = new QCheckBox(tr("Show as common time"));
211     groupBoxLayout->addWidget(m_commonTimeButton);
212     m_commonTimeButton->setChecked
213     ( qStrToBool( settings.value("timesigdialogshowcommon", "true" ) ) );
214 
215     m_normalizeRestsButton = new QCheckBox
216                              (tr("Correct the durations of following measures"));
217     groupBoxLayout->addWidget(m_normalizeRestsButton);
218     m_normalizeRestsButton->setChecked
219     ( qStrToBool( settings.value("timesigdialognormalize", "true" ) ) );
220 
221     groupBox->setLayout(groupBoxLayout);
222 
223     QObject::connect(m_hideSignatureButton, &QAbstractButton::clicked, this,
224                      &TimeSignatureDialog::slotUpdateCommonTimeButton);
225     slotUpdateCommonTimeButton();
226     m_explanatoryLabel = explanatoryLabel;
227 
228     settings.endGroup();
229 
230     QDialogButtonBox *buttonBox = new QDialogButtonBox(  QDialogButtonBox::Ok
231                                                        | QDialogButtonBox::Cancel
232                                                        | QDialogButtonBox::Help);
233 
234     vboxLayout->addWidget(buttonBox);
235 
236     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
237     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
238     connect(buttonBox, &QDialogButtonBox::helpRequested, this, &TimeSignatureDialog::slotHelpRequested);
239 
240 }
241 
242 TimeSignature
243 TimeSignatureDialog::getTimeSignature() const
244 {
245     QSettings settings;
246     settings.beginGroup( GeneralOptionsConfigGroup );
247 
248     settings.setValue("timesigdialogmakehidden", m_hideSignatureButton->isChecked());
249     settings.setValue("timesigdialogmakehiddenbars", m_hideBarsButton->isChecked());
250     settings.setValue("timesigdialogshowcommon", m_commonTimeButton->isChecked());
251     settings.setValue("timesigdialognormalize", m_normalizeRestsButton->isChecked());
252 
253     TimeSignature ts(m_timeSignature.getNumerator(),
254                      m_timeSignature.getDenominator(),
255                      (m_commonTimeButton &&
256                       m_commonTimeButton->isEnabled() &&
257                       m_commonTimeButton->isChecked()),
258                      (m_hideSignatureButton &&
259                       m_hideSignatureButton->isEnabled() &&
260                       m_hideSignatureButton->isChecked()),
261                      (m_hideBarsButton &&
262                       m_hideBarsButton->isEnabled() &&
263                       m_hideBarsButton->isChecked()));
264 
265     settings.endGroup();
266 
267     return ts;
268 }
269 
270 void
271 TimeSignatureDialog::slotNumDown()
272 {
273     int n = m_timeSignature.getNumerator();
274     if (--n >= 1) {
275         m_timeSignature = TimeSignature(n, m_timeSignature.getDenominator());
276         m_numLabel->setText(QString("%1").arg(n));
277     }
278     slotUpdateCommonTimeButton();
279 }
280 
281 void
282 TimeSignatureDialog::slotNumUp()
283 {
284     int n = m_timeSignature.getNumerator();
285     if (++n <= 99) {
286         m_timeSignature = TimeSignature(n, m_timeSignature.getDenominator());
287         m_numLabel->setText(QString("%1").arg(n));
288     }
289     slotUpdateCommonTimeButton();
290 }
291 
292 void
293 TimeSignatureDialog::slotDenomDown()
294 {
295     int n = m_timeSignature.getDenominator();
296     if ((n /= 2) >= 1) {
297         m_timeSignature = TimeSignature(m_timeSignature.getNumerator(), n);
298         m_denomLabel->setText(QString("%1").arg(n));
299     }
300     slotUpdateCommonTimeButton();
301 }
302 
303 void
304 TimeSignatureDialog::slotDenomUp()
305 {
306     int n = m_timeSignature.getDenominator();
307     if ((n *= 2) <= 64) {
308         m_timeSignature = TimeSignature(m_timeSignature.getNumerator(), n);
309         m_denomLabel->setText(QString("%1").arg(n));
310     }
311     slotUpdateCommonTimeButton();
312 }
313 
314 void
315 TimeSignatureDialog::slotUpdateCommonTimeButton()
316 {
317     if (m_explanatoryLabel)
318         m_explanatoryLabel->hide();
319     if (!m_hideSignatureButton || !m_hideSignatureButton->isChecked()) {
320         if (m_timeSignature.getDenominator() == m_timeSignature.getNumerator()) {
321             if (m_timeSignature.getNumerator() == 4) {
322                 m_commonTimeButton->setText(tr("Display as common time"));
323                 m_commonTimeButton->setEnabled(true);
324                 return ;
325             } else if (m_timeSignature.getNumerator() == 2) {
326                 m_commonTimeButton->setText(tr("Display as cut common time"));
327                 m_commonTimeButton->setEnabled(true);
328                 return ;
329             }
330         }
331     }
332     m_commonTimeButton->setEnabled(false);
333 }
334 
335 timeT
336 TimeSignatureDialog::getTime() const
337 {
338     if (m_timeEditor) {
339         return m_timeEditor->getTime();
340     } else if (m_asGivenButton && m_asGivenButton->isChecked()) {
341         return m_time;
342     } else if (m_startOfBarButton && m_startOfBarButton->isChecked()) {
343         int barNo = m_composition->getBarNumber(m_time);
344         return m_composition->getBarStart(barNo);
345     } else {
346         return m_time;
347     }
348 }
349 
350 bool
351 TimeSignatureDialog::shouldNormalizeRests() const
352 {
353     return (m_normalizeRestsButton && m_normalizeRestsButton->isEnabled() &&
354             m_normalizeRestsButton->isChecked());
355 }
356 
357 
358 void
359 TimeSignatureDialog::slotHelpRequested()
360 {
361     // TRANSLATORS: if the manual is translated into your language, you can
362     // change the two-letter language code in this URL to point to your language
363     // version, eg. "http://rosegardenmusic.com/wiki/doc:timeSignatureDialog-es" for the
364     // Spanish version. If your language doesn't yet have a translation, feel
365     // free to create one.
366     QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:timeSignatureDialog-en");
367     QDesktopServices::openUrl(QUrl(helpURL));
368 }
369 }
370