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