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 #define RG_MODULE_STRING "[GeneralConfigurationPage]"
19 
20 #include "GeneralConfigurationPage.h"
21 
22 #include "misc/Debug.h"
23 #include "misc/Strings.h"
24 #include "misc/ConfigGroups.h"
25 #include "document/RosegardenDocument.h"
26 #include "gui/application/RosegardenMainWindow.h"
27 #include "gui/studio/StudioControl.h"
28 #include "gui/dialogs/ShowSequencerStatusDialog.h"
29 #include "gui/seqmanager/SequenceManager.h"
30 
31 #include <QCheckBox>
32 #include <QComboBox>
33 #include <QFrame>
34 #include <QGridLayout>
35 #include <QLabel>
36 #include <QMessageBox>
37 #include <QPushButton>
38 #include <QSettings>
39 #include <QSpinBox>
40 
41 
42 namespace Rosegarden
43 {
44 
45 
46 GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) :
47     TabbedConfigurationPage(parent)
48 {
49     QSettings settings;
50     settings.beginGroup(GeneralOptionsConfigGroup);
51 
52     QFrame *frame;
53     QGridLayout *layout;
54     QLabel *label = nullptr;
55 
56 
57     // *** Behavior tab
58 
59     frame = new QFrame(m_tabWidget);
60     addTab(frame, tr("Behavior"));
61     layout = new QGridLayout(frame);
62     layout->setContentsMargins(15, 25, 15, 10);
63     layout->setSpacing(5);
64 
main(void)65     int row = 0;
66 
67     // Double-click opens segment in
68     layout->addWidget(
69             new QLabel(tr("Double-click opens segment in"), frame),
70             row, 0);
71 
72     m_openSegmentsIn = new QComboBox(frame);
73     m_openSegmentsIn->addItem(tr("Notation editor"));
74     m_openSegmentsIn->addItem(tr("Matrix editor"));
75     m_openSegmentsIn->addItem(tr("Event List editor"));
76     m_openSegmentsIn->setCurrentIndex(
77             settings.value("doubleclickclient", NotationView).toUInt());
78     connect(m_openSegmentsIn, static_cast<void(QComboBox::*)(int)>(
79                 &QComboBox::activated),
80             this, &GeneralConfigurationPage::slotModified);
81     layout->addWidget(m_openSegmentsIn, row, 1, 1, 2);
82 
83     ++row;
84 
85     // Number of count-in measures when recording
86     layout->addWidget(
87             new QLabel(tr("Number of count-in measures when recording"),
88                        frame),
89             row, 0);
90 
91     m_countIn = new QSpinBox(frame);
92     m_countIn->setMinimum(0);
93     m_countIn->setMaximum(10);
94     m_countIn->setValue(settings.value("countinbars", 0).toUInt());
95     connect(m_countIn, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
96     layout->addWidget(m_countIn, row, 1, 1, 2);
97 
98     ++row;
99 
100     layout->addWidget(new QLabel(tr("Enable metronome during"),
101                                  frame), row, 0);
102 
103     m_enableMetronomeDuring = new QComboBox(frame);
104     connect(m_enableMetronomeDuring,
105                 static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
106             this, &GeneralConfigurationPage::slotModified);
107     m_enableMetronomeDuring->addItem(tr("Count-in"));
108     m_enableMetronomeDuring->addItem(tr("Recording"));
109     m_enableMetronomeDuring->addItem(tr("Count-in and Recording"));
110     m_enableMetronomeDuring->setCurrentIndex(
111             settings.value("enableMetronomeDuring", DuringBoth).toUInt());
112 
113     layout->addWidget(m_enableMetronomeDuring, row, 1, 1, 2);
114 
115     ++row;
116 
117     // Auto-save interval
118     layout->addWidget(new QLabel(tr("Auto-save interval"), frame), row, 0);
119 
120     m_autoSaveInterval = new QComboBox(frame);
121     m_autoSaveInterval->addItem(tr("Every 30 seconds"));
122     m_autoSaveInterval->addItem(tr("Every minute"));
123     m_autoSaveInterval->addItem(tr("Every five minutes"));
124     m_autoSaveInterval->addItem(tr("Every half an hour"));
125     m_autoSaveInterval->addItem(tr("Never"));
126 
127     bool doAutoSave = settings.value("autosave", true).toBool();
128     int autoSaveInterval = settings.value("autosaveinterval", 300).toUInt();
129 
130     if (!doAutoSave  ||  autoSaveInterval == 0) {
131         m_autoSaveInterval->setCurrentIndex(4);  // Never
132     } else if (autoSaveInterval < 45) {
133         m_autoSaveInterval->setCurrentIndex(0);  // Every 30 seconds
134     } else if (autoSaveInterval < 150) {
135         m_autoSaveInterval->setCurrentIndex(1);  // Every minute
136     } else if (autoSaveInterval < 900) {
137         m_autoSaveInterval->setCurrentIndex(2);  // Every five minutes
138     } else {
139         m_autoSaveInterval->setCurrentIndex(3);  // Every half an hour
140     }
141 
142     connect(m_autoSaveInterval,
143                 static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
144             this, &GeneralConfigurationPage::slotModified);
145     layout->addWidget(m_autoSaveInterval, row, 1, 1, 2);
146 
147     ++row;
148 
149     // Append suffixes to segment labels
150     layout->addWidget(
151             new QLabel(tr("Append suffixes to segment labels"), frame),
152             row, 0);
153 
154     m_appendSuffixes = new QCheckBox(frame);
155     // I traditionally had these turned off, and when they reappeared, I found
156     // them incredibly annoying, so I'm making false the default:
157     m_appendSuffixes->setChecked(settings.value("appendlabel", false).toBool());
158     connect(m_appendSuffixes, &QCheckBox::stateChanged,
159             this, &GeneralConfigurationPage::slotModified);
160     layout->addWidget(m_appendSuffixes, row, 1, 1, 2);
161 
162     ++row;
163 
164     // Use track name for new segments
165     label = new QLabel(tr("Use track name for new segments"), frame);
166     QString tipText = tr(
167             "<qt><p>If checked, the label for new segments will always be the "
168             "same as the track name.</p></qt>");
169     label->setToolTip(tipText);
170     layout->addWidget(label, row, 0);
171 
172     m_useTrackName = new QCheckBox(frame);
173     m_useTrackName->setToolTip(tipText);
174     // Leaving unchecked by default to remain compatible with earlier behaviour
175     m_useTrackName->setChecked(settings.value("usetrackname", false).toBool());
176     connect(m_useTrackName, &QCheckBox::stateChanged,
177             this, &GeneralConfigurationPage::slotModified);
178     layout->addWidget(m_useTrackName, row, 1, 1, 2);
179 
180     ++row;
181 
182     // Enable editing during playback
183     // ??? We need to fix the crashes when editing during playback and
184     //     always allow editing during playback at some point.
185     label = new QLabel(tr("Enable editing during playback"), frame);
186     tipText = tr(
187             "<qt><p>WARNING: Editing during playback can lead to "
188             "instability, crashes, and data loss.  Save frequently.</p></qt>");
189     label->setToolTip(tipText);
190     layout->addWidget(label, row, 0);
191 
192     m_enableEditingDuringPlayback = new QCheckBox(frame);
193     m_enableEditingDuringPlayback->setToolTip(tipText);
194     m_enableEditingDuringPlayback->setChecked(
195             settings.value("enableEditingDuringPlayback", false).toBool());
196     connect(m_enableEditingDuringPlayback, &QCheckBox::stateChanged,
197             this, &GeneralConfigurationPage::slotModified);
198     layout->addWidget(m_enableEditingDuringPlayback, row, 1, 1, 2);
199 
200     ++row;
201 
202     settings.endGroup();
203 
204     settings.beginGroup(RecentFilesConfigGroup);
205 
206     // Clean recent files list
207     label = new QLabel(tr("Clean recent files list"), frame);
208     tipText = tr(
209             "<qt><p>Remove entries from the recent files list that no "
210             "longer exist.</p></qt>");
211     label->setToolTip(tipText);
212     layout->addWidget(label, row, 0);
213 
214     m_cleanRecentFilesList = new QCheckBox(frame);
215     m_cleanRecentFilesList->setToolTip(tipText);
216     m_cleanRecentFilesList->setChecked(
217             settings.value("cleanRecentFilesList", false).toBool());
218     connect(m_cleanRecentFilesList, &QCheckBox::stateChanged,
219             this, &GeneralConfigurationPage::slotModified);
220     layout->addWidget(m_cleanRecentFilesList, row, 1, 1, 2);
221 
222     ++row;
223 
224     settings.endGroup();
225 
226 #ifdef HAVE_LIBJACK
227     settings.beginGroup(SequencerOptionsConfigGroup);
228 
229     // Use JACK transport
230     layout->addWidget(new QLabel(tr("Use JACK transport"), frame), row, 0);
231 
232     // ??? Just a checkbox for now.  Originally, three settings were
233     //     proposed for this:
234     //       - Ignore JACK transport
235     //       - Sync
236     //       - Sync, and offer timebase master
237     //     Not sure whether those are still relevant.  Capturing here in case.
238     m_useJackTransport = new QCheckBox(frame);
239     m_useJackTransport->setChecked(
240             settings.value("jacktransport", false).toBool());
241     connect(m_useJackTransport, &QCheckBox::stateChanged,
242             this, &GeneralConfigurationPage::slotModified);
243     layout->addWidget(m_useJackTransport, row, 1, row- row+1, 2);
244 
245     ++row;
246 
247     settings.endGroup();
248 #endif
249 
250     settings.beginGroup(GeneralOptionsConfigGroup);
251 
252     // Skip a row.  Leave some space for the next field.
253     layout->setRowMinimumHeight(row, 20);
254     ++row;
255 
256     // Sequencer status
257     layout->addWidget(new QLabel(tr("Sequencer status"), frame), row, 0);
258 
259     QString status(tr("Unknown"));
260     RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
261     SequenceManager *mgr = doc->getSequenceManager();
262     if (mgr) {
263         int driverStatus = mgr->getSoundDriverStatus() & (AUDIO_OK | MIDI_OK);
264         switch (driverStatus) {
265         case AUDIO_OK:
266             status = tr("No MIDI, audio OK");
267             break;
268         case MIDI_OK:
269             status = tr("MIDI OK, no audio");
270             break;
271         case AUDIO_OK | MIDI_OK:
272             status = tr("MIDI OK, audio OK");
273             break;
274         default:
275             status = tr("No driver");
276             break;
277         }
278     }
279     layout->addWidget(new QLabel(status, frame), row, 1);
280 
281     QPushButton *detailsButton =
282             new QPushButton(tr("Details..."), frame);
283     QObject::connect(detailsButton, &QAbstractButton::clicked,
284                      this, &GeneralConfigurationPage::slotShowStatus);
285     layout->addWidget(detailsButton, row, 2, Qt::AlignRight);
286 
287     ++row;
288 
289     // Make the last row stretch to fill the rest of the space.
290     layout->setRowStretch(row, 10);
291 
292 
293     // *** Presentation tab
294 
295     frame = new QFrame(m_tabWidget);
296     addTab(frame, tr("Presentation"));
297     layout = new QGridLayout(frame);
298     layout->setContentsMargins(15, 25, 15, 10);
299     layout->setSpacing(5);
300 
301     row = 0;
302 
303     // Use Thorn style
304     label = new QLabel(tr("Use Thorn style"), frame);
305     tipText = tr("<qt>When checked, Rosegarden will use the Thorn look and feel, otherwise default system preferences will be used the next time Rosegarden starts.</qt>");
306     label->setToolTip(tipText);
307     layout->addWidget(label, row, 0);
308 
309     m_Thorn = new QCheckBox;
310     m_Thorn->setToolTip(tipText);
311     m_Thorn->setChecked(settings.value("use_thorn_style", true).toBool());
312     connect(m_Thorn, &QCheckBox::stateChanged,
313             this, &GeneralConfigurationPage::slotModified);
314     layout->addWidget(m_Thorn, row, 1, 1, 3);
315 
316     ++row;
317 
318     // Note name style
319     layout->addWidget(new QLabel(tr("Note name style"),
320                                  frame), row, 0);
321 
322     m_nameStyle = new QComboBox(frame);
323     m_nameStyle->addItem(tr("Always use US names (e.g. quarter, 8th)"));
324     m_nameStyle->addItem(tr("Localized (where available)"));
325     m_nameStyle->setCurrentIndex(
326             settings.value("notenamestyle", Local).toUInt());
327     connect(m_nameStyle, static_cast<void(QComboBox::*)(int)>(
328             &QComboBox::activated),
329         this, &GeneralConfigurationPage::slotModified);
330     layout->addWidget(m_nameStyle, row, 1, 1, 3);
331 
332     ++row;
333 
334     // Show textured background on
335     layout->addWidget(
336             new QLabel(tr("Show textured background on"), frame), row, 0);
337 
338     m_backgroundTextures = new QCheckBox(tr("Main window"), frame);
339     m_backgroundTextures->setChecked(
340             settings.value("backgroundtextures", true).toBool());
341     connect(m_backgroundTextures, &QCheckBox::stateChanged,
342             this, &GeneralConfigurationPage::slotModified);
343     layout->addWidget(m_backgroundTextures, row, 1);
344 
345     m_notationBackgroundTextures = new QCheckBox(tr("Notation"), frame);
346     // ??? Wow this is cumbersome.  Maybe we should just prepend the
347     //     group for each call?
348     settings.endGroup();
349     settings.beginGroup(NotationViewConfigGroup);
350     m_notationBackgroundTextures->setChecked(
351             settings.value("backgroundtextures", true).toBool());
352     settings.endGroup();
353     settings.beginGroup(GeneralOptionsConfigGroup);
354     connect(m_notationBackgroundTextures, &QCheckBox::stateChanged,
355             this, &GeneralConfigurationPage::slotModified);
356     layout->addWidget(m_notationBackgroundTextures, row, 2);
357 
358     ++row;
359 
360     // Show full path in window titles
361     layout->addWidget(
362             new QLabel(tr("Show full path in window titles")),
363             row, 0);
364 
365     m_longTitles = new QCheckBox;
366     m_longTitles->setChecked(
367             settings.value("long_window_titles", false).toBool());
368     connect(m_longTitles, &QCheckBox::stateChanged,
369             this, &GeneralConfigurationPage::slotModified);
370     layout->addWidget(m_longTitles, row, 1);
371 
372     ++row;
373 
374     // Track size
375     layout->addWidget(new QLabel(tr("Track size"), frame), row, 0);
376 
377     m_trackSize = new QComboBox(frame);
378     m_trackSize->addItem(tr("Small"));
379     m_trackSize->addItem(tr("Medium"));
380     m_trackSize->addItem(tr("Large"));
381     m_trackSize->setCurrentIndex(
382             settings.value("track_size", 0).toInt());
383     connect(m_trackSize, static_cast<void(QComboBox::*)(int)>(
384                     &QComboBox::activated),
385             this, &GeneralConfigurationPage::slotModified);
386     layout->addWidget(m_trackSize, row, 1, 1, 3);
387 
388     ++row;
389 
390     // Make the last row stretch to fill the rest of the space.
391     layout->setRowStretch(row, 10);
392 
393 
394     // *** External Applications tab
395 
396     frame = new QFrame(m_tabWidget);
397     addTab(frame, tr("External Applications"));
398     layout = new QGridLayout(frame);
399     layout->setContentsMargins(15, 25, 15, 10);
400     layout->setSpacing(5);
401 
402     row = 0;
403 
404     // Explanation
405     label = new QLabel(tr("<qt>Rosegarden relies on external applications to provide certain features.  Each selected application must be installed and available on your path.  When choosing an application to use, please ensure that it can run from a \"run command\" box (typically <b>Alt+F2</b>) which should allow Rosegarden to make use of it when necessary.<br></qt>"),
406                        frame);
407     label->setWordWrap(true);
408     layout->addWidget(label, row, 0, 1, 4);
409 
410     ++row;
411 
412     // PDF viewer
413     label = new QLabel(tr("PDF viewer"), frame);
414     tipText = tr("Used to preview generated LilyPond output");
415     label->setToolTip(tipText);
416     layout->addWidget(label, row, 0);
417 
418     m_pdfViewer = new QComboBox(frame);
419     m_pdfViewer->setToolTip(tipText);
420     m_pdfViewer->addItem(tr("Okular (KDE 4.x)"));
421     m_pdfViewer->addItem(tr("Evince (GNOME)"));
422     m_pdfViewer->addItem(tr("Adobe Acrobat Reader (non-free)"));
423     m_pdfViewer->addItem(tr("MuPDF"));
424     m_pdfViewer->addItem(tr("ePDFView"));
425     m_pdfViewer->addItem(tr("xdg-open (recommended)"));
426     settings.endGroup();
427     settings.beginGroup(ExternalApplicationsConfigGroup);
428     m_pdfViewer->setCurrentIndex(settings.value("pdfviewer", xdgOpen).toUInt());
429     connect(m_pdfViewer, static_cast<void(QComboBox::*)(int)>(
430             &QComboBox::activated),
431         this, &GeneralConfigurationPage::slotModified);
432     layout->addWidget(m_pdfViewer, row, 1, 1, 3);
433 
434     ++row;
435 
436     // Command-line file printing utility
437     label = new QLabel(tr("Command-line file printing utility"), frame);
438     tipText = tr("Used to print generated LilyPond output without previewing it");
439     label->setToolTip(tipText);
440     layout->addWidget(label, row, 0);
441 
442     m_filePrinter = new QComboBox(frame);
443     m_filePrinter->setToolTip(tipText);
444     m_filePrinter->addItem(tr("Gtk-LP (GNOME)"),1);
445     m_filePrinter->addItem(tr("lpr (no GUI)"),2);
446     m_filePrinter->addItem(tr("lp (no GUI)"),3);
447     m_filePrinter->addItem(tr("HPLIP (Qt 4)"),4);
448     // now that I'm actually on KDE 4.2, I see no more KPrinter.  I'll default
449     // to Lpr instead.
450     m_filePrinter->setCurrentIndex(settings.value("fileprinter", Lpr).toUInt());
451     connect(m_filePrinter, static_cast<void(QComboBox::*)(int)>(
452             &QComboBox::activated),
453         this, &GeneralConfigurationPage::slotModified);
454     layout->addWidget(m_filePrinter, row, 1, 1, 3);
455 
456     ++row;
457 
458     // Make the last row stretch to fill the rest of the space.
459     layout->setRowStretch(row, 10);
460 }
461 
462 void
463 GeneralConfigurationPage::slotShowStatus()
464 {
465     ShowSequencerStatusDialog dialog(this);
466     dialog.exec();
467 }
468 
469 void GeneralConfigurationPage::apply()
470 {
471     QSettings settings;
472 
473     // Behavior tab
474 
475     settings.beginGroup(GeneralOptionsConfigGroup);
476 
477     settings.setValue("doubleclickclient", m_openSegmentsIn->currentIndex());
478     settings.setValue("countinbars", m_countIn->value());
479     settings.setValue("enableMetronomeDuring",
480                       m_enableMetronomeDuring->currentIndex());
481 
482     if (m_autoSaveInterval->currentIndex() == 4) {
483         settings.setValue("autosave", false);
484     } else {
485         unsigned interval = 0;
486         settings.setValue("autosave", true);
487         if (m_autoSaveInterval->currentIndex() == 0) {
488             interval = 30;
489         } else if (m_autoSaveInterval->currentIndex() == 1) {
490             interval = 60;
491         } else if (m_autoSaveInterval->currentIndex() == 2) {
492             interval = 300;
493         } else {
494             interval = 1800;
495         }
496         settings.setValue("autosaveinterval", interval);
497         emit updateAutoSaveInterval(interval);
498     }
499 
500     settings.setValue("appendlabel", m_appendSuffixes->isChecked());
501     settings.setValue("usetrackname", m_useTrackName->isChecked());
502     settings.setValue("enableEditingDuringPlayback",
503             m_enableEditingDuringPlayback->isChecked());
504 
505     settings.endGroup();
506 
507     settings.beginGroup(RecentFilesConfigGroup);
508     settings.setValue("cleanRecentFilesList",
509             m_cleanRecentFilesList->isChecked());
510     settings.endGroup();
511 
512 #ifdef HAVE_LIBJACK
513     settings.beginGroup(SequencerOptionsConfigGroup);
514 
515     bool jackTransport = m_useJackTransport->isChecked();
516 
517     // 0 -> nothing, 1 -> sync, 2 -> master
518     int jackValue = jackTransport ? 1 : 0;
519 
520     settings.setValue("jacktransport", jackTransport);
521 
522     MappedEvent mEjackValue(MidiInstrumentBase,  // InstrumentId
523                             MappedEvent::SystemJackTransport,
524                             MidiByte(jackValue));
525     StudioControl::sendMappedEvent(mEjackValue);
526 
527     settings.endGroup();
528 #endif // HAVE_LIBJACK
529 
530     // Presentation tab
531 
532     settings.beginGroup(GeneralOptionsConfigGroup);
533 
534     const bool thornChanged =
535             (settings.value("use_thorn_style", true).toBool() !=
536              m_Thorn->isChecked());
537     //RG_DEBUG << "apply(): NB. use_thorn_style = " << settings.value("use_thorn_style", true).toBool() << ", m_Thorn->isChecked() = " << m_Thorn->isChecked();
538     settings.setValue("use_thorn_style", m_Thorn->isChecked());
539     settings.setValue("notenamestyle", m_nameStyle->currentIndex());
540     const bool mainTextureChanged =
541             (settings.value("backgroundtextures", true).toBool() !=
542              m_backgroundTextures->isChecked());
543     settings.setValue("backgroundtextures", m_backgroundTextures->isChecked());
544 
545     settings.endGroup();
546 
547     settings.beginGroup(NotationViewConfigGroup);
548     settings.setValue("backgroundtextures", m_notationBackgroundTextures->isChecked());
549     settings.endGroup();
550 
551     settings.beginGroup(GeneralOptionsConfigGroup);
552     settings.setValue("long_window_titles", m_longTitles->isChecked());
553     const bool trackSizeChanged =
554             (settings.value("track_size", 0).toInt() !=
555              m_trackSize->currentIndex());
556     settings.setValue("track_size", m_trackSize->currentIndex());
557     settings.endGroup();
558 
559     // External Applications tab
560 
561     settings.beginGroup(ExternalApplicationsConfigGroup);
562 
563     settings.setValue("pdfviewer", m_pdfViewer->currentIndex());
564     settings.setValue("fileprinter", m_filePrinter->currentIndex());
565 
566     settings.endGroup();
567 
568     // Restart Warnings
569 
570     if (mainTextureChanged) {
571         QMessageBox::information(this, tr("Rosegarden"),
572                 tr("Changes to the textured background in the main window will not take effect until you restart Rosegarden."));
573     }
574 
575     if (thornChanged) {
576         QMessageBox::information(this, tr("Rosegarden"),
577                 tr("You must restart Rosegarden for the presentation change to take effect."));
578     }
579 
580     if (trackSizeChanged) {
581         QMessageBox::information(this, tr("Rosegarden"),
582                 tr("You must restart Rosegarden or open a file for the track size change to take effect."));
583     }
584 }
585 
586 
587 }
588