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 "[RosegardenMainWindow]"
19
20 #include "RosegardenMainWindow.h"
21
22 #include "gui/editors/segment/TrackEditor.h"
23 #include "gui/editors/segment/TrackButtons.h"
24 #include "misc/Debug.h"
25 #include "misc/Strings.h"
26 #include "gui/application/TransportStatus.h"
27 #include "base/AnalysisTypes.h"
28 #include "base/AudioPluginInstance.h"
29 #include "base/Clipboard.h"
30 #include "base/Composition.h"
31 #include "base/CompositionTimeSliceAdapter.h"
32 #include "base/Configuration.h"
33 #include "base/Device.h"
34 #include "base/Exception.h"
35 #include "base/Instrument.h"
36 #include "base/MidiDevice.h"
37 #include "base/MidiProgram.h"
38 #include "base/NotationTypes.h"
39 #include "base/Profiler.h"
40 #include "base/QEvents.h"
41 #include "base/RealTime.h"
42 #include "base/Segment.h"
43 #include "base/SegmentNotationHelper.h"
44 #include "base/Selection.h"
45 #include "base/Studio.h"
46 #include "base/Track.h"
47 #include "commands/edit/CopyCommand.h"
48 #include "commands/edit/CutCommand.h"
49 #include "commands/edit/EventQuantizeCommand.h"
50 #include "commands/edit/PasteSegmentsCommand.h"
51 #include "commands/edit/TransposeCommand.h"
52 #include "commands/edit/AddMarkerCommand.h"
53 #include "commands/edit/ModifyMarkerCommand.h"
54 #include "commands/edit/RemoveMarkerCommand.h"
55 #include "commands/notation/KeyInsertionCommand.h"
56 #include "commands/segment/AddTempoChangeCommand.h"
57 #include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
58 #include "commands/segment/AudioSegmentAutoSplitCommand.h"
59 #include "commands/segment/AudioSegmentRescaleCommand.h"
60 #include "commands/segment/AudioSegmentSplitCommand.h"
61 #include "commands/segment/ChangeCompositionLengthCommand.h"
62 #include "commands/segment/CreateTempoMapFromSegmentCommand.h"
63 #include "commands/segment/CutRangeCommand.h"
64 #include "commands/segment/DeleteRangeCommand.h"
65 #include "commands/segment/EraseTempiInRangeCommand.h"
66 #include "commands/segment/ExpandFigurationCommand.h"
67 #include "commands/segment/FitToBeatsCommand.h"
68 #include "commands/segment/InsertRangeCommand.h"
69 #include "commands/segment/PasteConductorDataCommand.h"
70 #include "commands/segment/ModifyDefaultTempoCommand.h"
71 #include "commands/segment/MoveTracksCommand.h"
72 #include "commands/segment/PasteRangeCommand.h"
73 #include "commands/segment/RemoveTempoChangeCommand.h"
74 #include "commands/segment/RemoveTimeSignatureCommand.h"
75 #include "commands/segment/SegmentAutoSplitCommand.h"
76 #include "commands/segment/SegmentChangeTransposeCommand.h"
77 #include "commands/segment/SegmentJoinCommand.h"
78 #include "commands/segment/SegmentLabelCommand.h"
79 #include "commands/segment/SegmentReconfigureCommand.h"
80 #include "commands/segment/SegmentRescaleCommand.h"
81 #include "commands/segment/SegmentSplitByPitchCommand.h"
82 #include "commands/segment/SegmentSplitByRecordingSrcCommand.h"
83 #include "commands/segment/SegmentSplitByDrumCommand.h"
84 #include "commands/segment/SegmentSplitCommand.h"
85 #include "commands/segment/SegmentTransposeCommand.h"
86 #include "commands/segment/SegmentSyncCommand.h"
87 #include "commands/segment/UpdateFigurationCommand.h"
88 #include "commands/studio/CreateOrDeleteDeviceCommand.h"
89 #include "commands/studio/ModifyDeviceCommand.h"
90 #include "document/io/CsoundExporter.h"
91 //#include "document/io/HydrogenLoader.h"
92 #include "document/io/MusicXMLLoader.h"
93 #include "document/io/LilyPondExporter.h"
94 #include "document/CommandHistory.h"
95 #include "document/io/RG21Loader.h"
96 #include "document/io/MupExporter.h"
97 #include "document/io/MusicXmlExporter.h"
98 #include "document/RosegardenDocument.h"
99 #include "document/MetadataHelper.h"
100 #include "misc/ConfigGroups.h"
101 #include "gui/application/RosegardenApplication.h"
102 #include "gui/dialogs/AddTracksDialog.h"
103 #include "gui/dialogs/AboutDialog.h"
104 #include "gui/dialogs/AudioManagerDialog.h"
105 #include "gui/dialogs/AudioPluginDialog.h"
106 #include "gui/dialogs/AudioSplitDialog.h"
107 #include "gui/dialogs/BeatsBarsDialog.h"
108 #include "gui/dialogs/CompositionLengthDialog.h"
109 #include "gui/dialogs/CommentsPopupDialog.h"
110 #include "gui/dialogs/ConfigureDialog.h"
111 #include "gui/dialogs/CountdownDialog.h"
112 #include "gui/dialogs/DialogSuppressor.h"
113 #include "gui/dialogs/DocumentConfigureDialog.h"
114 #include "gui/dialogs/FileMergeDialog.h"
115 #include "gui/dialogs/IdentifyTextCodecDialog.h"
116 #include "gui/dialogs/IntervalDialog.h"
117 #include "gui/dialogs/LilyPondOptionsDialog.h"
118 #include "gui/dialogs/MusicXMLOptionsDialog.h"
119 #include "gui/dialogs/ManageMetronomeDialog.h"
120 #include "gui/dialogs/QuantizeDialog.h"
121 #include "gui/dialogs/RescaleDialog.h"
122 #include "gui/dialogs/SplitByPitchDialog.h"
123 #include "gui/dialogs/SplitByRecordingSrcDialog.h"
124 #include "gui/dialogs/TimeDialog.h"
125 #include "gui/dialogs/TransportDialog.h"
126 #include "gui/editors/parameters/InstrumentParameterBox.h"
127 #include "gui/editors/parameters/RosegardenParameterArea.h"
128 #include "gui/editors/parameters/SegmentParameterBox.h"
129 #include "gui/editors/parameters/TrackParameterBox.h"
130 #include "gui/editors/segment/compositionview/CompositionView.h"
131 #include "gui/editors/segment/MarkerEditor.h"
132 #include "gui/editors/segment/PlayListDialog.h"
133 #include "gui/editors/segment/PlayList.h"
134 #include "gui/editors/segment/compositionview/SegmentEraser.h"
135 #include "gui/editors/segment/compositionview/SegmentJoiner.h"
136 #include "gui/editors/segment/compositionview/SegmentMover.h"
137 #include "gui/editors/segment/compositionview/SegmentPencil.h"
138 #include "gui/editors/segment/compositionview/SegmentResizer.h"
139 #include "gui/editors/segment/compositionview/SegmentSelector.h"
140 #include "gui/editors/segment/compositionview/SegmentSplitter.h"
141 #include "gui/editors/segment/compositionview/SegmentToolBox.h"
142 #include "gui/editors/segment/TrackLabel.h"
143 #include "gui/editors/segment/TriggerSegmentManager.h"
144 #include "gui/editors/tempo/TempoView.h"
145 #include "gui/general/EditViewBase.h"
146 #include "gui/general/EditTempoController.h"
147 #include "gui/general/FileSource.h"
148 #include "gui/general/ResourceFinder.h"
149 #include "gui/general/AutoSaveFinder.h"
150 #include "gui/general/LilyPondProcessor.h"
151 #include "gui/general/ProjectPackager.h"
152 #include "gui/general/PresetHandlerDialog.h"
153 #include "gui/widgets/StartupLogo.h"
154 #include "gui/widgets/TmpStatusMsg.h"
155 #include "gui/widgets/WarningWidget.h"
156 #include "gui/seqmanager/MidiFilterDialog.h"
157 #include "gui/seqmanager/SequenceManager.h"
158 #include "gui/studio/AudioMixerWindow2.h"
159 #include "gui/studio/AudioPlugin.h"
160 #include "gui/studio/AudioPluginManager.h"
161 #include "gui/studio/AudioPluginOSCGUIManager.h"
162 #include "gui/studio/BankEditorDialog.h"
163 #include "gui/studio/DeviceManagerDialog.h"
164 #include "gui/studio/MidiMixerWindow.h"
165 #include "gui/studio/RemapInstrumentDialog.h"
166 #include "gui/studio/StudioControl.h"
167 #include "gui/studio/SynthPluginManagerDialog.h"
168 #include "gui/studio/ControlEditorDialog.h"
169 #include "gui/widgets/ProgressBar.h"
170 #include "gui/widgets/FileDialog.h"
171 #include "LircClient.h"
172 #include "LircCommander.h"
173 #include "RosegardenMainViewWidget.h"
174 #include "SetWaitCursor.h"
175 #include "sequencer/RosegardenSequencer.h"
176 #include "sequencer/SequencerThread.h"
177 #include "sound/AudioFile.h"
178 #include "sound/AudioFileManager.h"
179 #include "sound/MappedCommon.h"
180 #include "sound/MappedEventList.h"
181 #include "sound/MappedEvent.h"
182 #include "sound/MappedStudio.h"
183 #include "sound/MidiFile.h"
184 #include "sound/PluginIdentifier.h"
185 #include "sound/SequencerDataBlock.h"
186 #include "sound/SoundDriver.h"
187 #include "StartupTester.h"
188 #include "gui/widgets/TmpStatusMsg.h"
189 #include "gui/studio/DeviceManagerDialog.h"
190 #include "gui/widgets/InputDialog.h"
191 #include "TranzportClient.h"
192
193 #include "rosegarden-version.h"
194
195 #include <QApplication>
196 #include <QDesktopServices>
197 #include <QSettings>
198 #include <QShortcut>
199 #include <QMessageBox>
200 #include <QProcess>
201 #include <QTemporaryFile>
202 #include <QToolTip>
203 #include <QByteArray>
204 #include <QCursor>
205 #include <QDataStream>
206 #include <QDialog>
207 #include <QDir>
208 #include <QFile>
209 #include <QFileInfo>
210 #include <QIcon>
211 #include <QLabel>
212 #include <QObject>
213 #include <QObjectList>
214 #include <QPixmap>
215 #include <QToolTip>
216 #include <QPushButton>
217 #include <QRegularExpression>
218 #include <QSlider>
219 #include <QString>
220 #include <QStringList>
221 #include <QTextCodec>
222 #include <QTimer>
223 #include <QVector>
224 #include <QWidget>
225 #include <QPushButton>
226 #include <QToolButton>
227 #include <QMainWindow>
228 #include <QMenu>
229 #include <QMenuBar>
230 #include <QToolBar>
231 #include <QStatusBar>
232 #include <QAction>
233 #include <QUrl>
234 #include <QDialog>
235 #include <QPrintDialog>
236 #include <QColorDialog>
237 #include <QFontDialog>
238 #include <QPageSetupDialog>
239 #include <QSharedPointer>
240 #include <QInputDialog>
241
242 // Ladish lv1 support
243 #include <cerrno> // for errno
244 #include <climits> // for LONG_MAX
245 #include <csignal> // for sigaction()
246 #include <cstring> // for strerror()
247 #include <unistd.h> // for pipe()
248 #include <QSocketNotifier>
249
250
251 namespace Rosegarden
252 {
253
254
RosegardenMainWindow(bool enableSound,QObject * startupStatusMessageReceiver)255 RosegardenMainWindow::RosegardenMainWindow(bool enableSound,
256 QObject *startupStatusMessageReceiver) :
257 QMainWindow(nullptr),
258 m_actionsSetup(false),
259 m_notPlaying(true),
260 m_haveSelection(false),
261 m_haveRange(false),
262 m_view(nullptr),
263 m_doc(nullptr),
264 m_recentFiles(),
265 m_sequencerThread(nullptr),
266 m_sequencerCheckedIn(false),
267 #ifdef HAVE_LIBJACK
268 m_jackProcess(nullptr),
269 #endif
270 m_cpuBar(nullptr),
271 m_zoomSlider(nullptr),
272 m_zoomLabel(nullptr),
273 // m_statusBarLabel1(0),
274 m_seqManager(nullptr),
275 m_transport(nullptr),
276 m_audioManagerDialog(nullptr),
277 m_originatingJump(false),
278 m_storedLoopStart(0),
279 m_storedLoopEnd(0),
280 m_useSequencer(enableSound),
281 m_autoSaveTimer(new QTimer(this)),
282 m_clipboard(Clipboard::mainClipboard()),
283 m_playList(nullptr),
284 m_synthManager(nullptr),
285 m_audioMixerWindow2(),
286 m_midiMixer(nullptr),
287 m_bankEditor(nullptr),
288 m_markerEditor(nullptr),
289 m_tempoView(nullptr),
290 m_triggerSegmentManager(nullptr),
291 m_configDlg(nullptr),
292 m_docConfigDlg(nullptr),
293 m_pluginGUIManager(new AudioPluginOSCGUIManager(this)),
294 m_updateUITimer(new QTimer(this)),
295 m_inputTimer(new QTimer(this)),
296 m_editTempoController(new EditTempoController(this)),
297 m_startupTester(nullptr),
298 m_firstRun(false),
299 m_haveAudioImporter(false),
300 m_parameterArea(nullptr),
301 #ifdef HAVE_LIRC
302 m_lircClient(nullptr),
303 m_lircCommander(nullptr),
304 #endif
305 m_tranzport(nullptr),
306 // m_deviceManager(), QPointer inits itself to 0.
307 m_warningWidget(nullptr),
308 m_cpuMeterTimer(new QTimer(this))
309 {
310 setAttribute(Qt::WA_DeleteOnClose);
311
312 setObjectName("App");
313 m_myself = this;
314
315 if (startupStatusMessageReceiver) {
316 QObject::connect(this, SIGNAL(startupStatusMessage(QString)),
317 startupStatusMessageReceiver,
318 SLOT(slotShowStatusMessage(QString)));
319 }
320
321 connect(m_editTempoController, SIGNAL(editTempos(timeT)),
322 this, SLOT(slotEditTempos(timeT)));
323
324 if (m_useSequencer) {
325 emit startupStatusMessage(tr("Starting sequencer..."));
326
327 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
328 // !!! From this point on, we are MULTITHREADED! !!!
329 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
330
331 // Launch the SequencerThread.
332 launchSequencer();
333 }
334
335 // Plugin manager
336 //
337 emit startupStatusMessage(tr("Initializing plugin manager..."));
338 m_pluginManager.reset(new AudioPluginManager(enableSound));
339
340 RosegardenDocument* doc = newDocument();
341
342 m_seqManager = new SequenceManager();
343
344 m_parameterArea = new RosegardenParameterArea(this);
345 m_parameterArea->setObjectName("RosegardenParameterArea");
346
347 // Populate the parameter-box area with the respective
348 // parameter box widgets.
349 m_segmentParameterBox = new SegmentParameterBox(m_parameterArea);
350 m_parameterArea->addRosegardenParameterBox(m_segmentParameterBox);
351 m_trackParameterBox = new TrackParameterBox(m_parameterArea);
352 m_parameterArea->addRosegardenParameterBox(m_trackParameterBox);
353 m_instrumentParameterBox = new InstrumentParameterBox(m_parameterArea);
354 m_parameterArea->addRosegardenParameterBox(m_instrumentParameterBox);
355
356 // Lookup the configuration parameter that specifies the default
357 // arrangement, and instantiate it.
358
359 // call inits to invoke all other construction parts
360 //
361 emit startupStatusMessage(tr("Initializing view..."));
362 initStatusBar();
363 setupActions();
364 initZoomToolbar();
365
366 // ??? TransportDialog should connect itself to SequenceManager. Move
367 // all of this into TransportDialog.
368 Q_ASSERT(m_transport);
369 connect(m_seqManager, &SequenceManager::signalTempoChanged,
370 m_transport, &TransportDialog::slotTempoChanged);
371 connect(m_seqManager, &SequenceManager::signalMidiInLabel,
372 m_transport, &TransportDialog::slotMidiInLabel);
373 connect(m_seqManager, &SequenceManager::signalMidiOutLabel,
374 m_transport, &TransportDialog::slotMidiOutLabel);
375 connect(m_seqManager, &SequenceManager::signalPlaying,
376 m_transport, &TransportDialog::slotPlaying);
377 connect(m_seqManager, &SequenceManager::signalRecording,
378 m_transport, &TransportDialog::slotRecording);
379 connect(m_seqManager, &SequenceManager::signalMetronomeActivated,
380 m_transport, &TransportDialog::slotMetronomeActivated);
381
382 // Set up external controller.
383 ExternalController::self().connectRMW(this);
384
385 // Load the initial document (this includes doc's own autoload)
386 //
387 setDocument(doc);
388
389 emit startupStatusMessage(tr("Starting sequence manager..."));
390 m_seqManager->setDocument(m_doc);
391
392 connect(m_seqManager,
393 &SequenceManager::sendWarning,
394 this,
395 &RosegardenMainWindow::slotDisplayWarning);
396
397 if (m_useSequencer) {
398 // Check the sound driver status and warn the user of any
399 // problems. This warning has to happen early, in case it
400 // affects the ability to load plugins etc from a file on the
401 // command line.
402 m_seqManager->checkSoundDriverStatus(true);
403 }
404
405 if (m_seqManager->getSoundDriverStatus() & AUDIO_OK) {
406 slotStateChanged("got_audio", true);
407 } else {
408 slotStateChanged("got_audio", false);
409 }
410
411 // If we're restarting the gui then make sure any transient
412 // studio objects are cleared away.
413 emit startupStatusMessage(tr("Clearing studio data..."));
414 m_seqManager->reinitialiseSequencerStudio();
415
416 // Send the transport control statuses for MMC and JACK
417 //
418 m_seqManager->sendTransportControlStatuses();
419
420 // Now autoload
421 //
422 enterActionState("new_file"); //@@@ JAS orig. 0
423 leaveActionState("have_segments"); //@@@ JAS orig. KXMLGUIClient::StateReverse
424 leaveActionState("have_selection"); //@@@ JAS orig. KXMLGUIClient::StateReverse
425 leaveActionState("have_clipboard_can_paste_as_links");
426 slotTestClipboard();
427
428 // Check for lack of MIDI devices and disable Studio options accordingly
429 //
430 if (!m_doc->getStudio().haveMidiDevices())
431 leaveActionState("got_midi_devices"); //@@@ JAS orig. KXMLGUIClient::StateReverse
432
433 emit startupStatusMessage(tr("Starting..."));
434
435 readOptions();
436
437 // All toolbars should be created before this is called
438 //### implement or find alternative : rgTempQtIV->setAutoSaveSettings(MainWindowConfigGroup, true);
439
440 #ifdef HAVE_LIRC
441
442 try {
443 m_lircClient = new LircClient();
444 } catch (const Exception &e) {
445 RG_DEBUG << e.getMessage().c_str();
446 // continue without
447 m_lircClient = nullptr;
448 }
449 if (m_lircClient) {
450 m_lircCommander = new LircCommander(m_lircClient, this);
451 }
452 #endif
453
454 // Tranzport
455 try
456 {
457 m_tranzport = new TranzportClient(this);
458 }
459 catch (const Exception &e)
460 {
461 m_tranzport = nullptr;
462 RG_DEBUG << e.getMessage().c_str();
463 }
464
465 enterActionState("have_project_packager");
466 enterActionState("have_lilypondview");
467
468 QTimer::singleShot(1000, this, &RosegardenMainWindow::slotTestStartupTester);
469
470 // Restore window geometry and toolbar state
471 //RG_DEBUG << "ctor: Restoring saved main window geometry...";
472 QSettings settings;
473 settings.beginGroup(WindowGeometryConfigGroup);
474 this->restoreGeometry(settings.value("Main_Window_Geometry").toByteArray());
475 this->restoreState(settings.value("Main_Window_State").toByteArray());
476 settings.endGroup();
477
478 checkAudioPath();
479
480 if (!installSignalHandlers())
481 RG_WARNING << "ctor: Signal handlers not installed!";
482
483 // Update UI time interval
484 settings.beginGroup("Performance_Testing");
485 int updateUITime = settings.value("Update_UI_Time", 50).toInt();
486 // Write it to the file to make it easier to find.
487 settings.setValue("Update_UI_Time", updateUITime);
488 settings.endGroup();
489
490 // Connect the various timers to their handlers.
491 connect(m_updateUITimer, &QTimer::timeout, this, &RosegardenMainWindow::slotUpdateUI);
492 m_updateUITimer->start(updateUITime);
493 connect(m_inputTimer, &QTimer::timeout, this, &RosegardenMainWindow::slotHandleInputs);
494 m_inputTimer->start(20);
495 connect(m_autoSaveTimer, &QTimer::timeout, this, &RosegardenMainWindow::slotAutoSave);
496 connect(m_cpuMeterTimer, &QTimer::timeout, this, &RosegardenMainWindow::slotUpdateCPUMeter);
497 m_cpuMeterTimer->start(1000);
498
499 // Connect Typematic objects.
500 connect(&m_rewindTypematic, &Typematic::click,
501 this, &RosegardenMainWindow::slotRewind);
502 connect(&m_fastForwardTypematic, &Typematic::click,
503 this, &RosegardenMainWindow::slotFastforward);
504 }
505
~RosegardenMainWindow()506 RosegardenMainWindow::~RosegardenMainWindow()
507 {
508 RG_DEBUG << "dtor...";
509
510 if (getView() &&
511 getView()->getTrackEditor() &&
512 getView()->getTrackEditor()->getCompositionView()) {
513 getView()->getTrackEditor()->getCompositionView()->endAudioPreviewGeneration();
514 }
515
516 delete m_pluginGUIManager;
517
518 if (isSequencerRunning()) {
519 RosegardenSequencer::getInstance()->quit();
520 // ??? Can we do better than this? Can we wait for the thread to
521 // end inside of quit()? QThread::wait()?
522 usleep(300000);
523 delete m_sequencerThread;
524 }
525
526 delete m_transport;
527
528 delete m_seqManager;
529
530 #ifdef HAVE_LIRC
531 delete m_lircCommander;
532 delete m_lircClient;
533 #endif
534
535 delete m_tranzport;
536
537 delete m_doc;
538
539 Profiles::getInstance()->dump();
540 }
541
542 int RosegardenMainWindow::sigpipe[2];
543
544 /* Handler for system signals (SIGUSR1, SIGINT...)
545 * Write a message to the pipe and leave as soon as possible
546 */
547 void
handleSignal(int sig)548 RosegardenMainWindow::handleSignal(int sig)
549 {
550 if (write(sigpipe[1], &sig, sizeof(sig)) == -1) {
551 RG_WARNING << "handleSignal(): write() failed:" << std::strerror(errno);
552 }
553 }
554
555 /* Install signal handlers (may be more than one; called from the
556 * constructor of your MainWindow class*/
557 bool
installSignalHandlers()558 RosegardenMainWindow::installSignalHandlers()
559 {
560 /*install pipe to forward received system signals*/
561 if (pipe(sigpipe) < 0) {
562 RG_WARNING << "installSignalHandlers(): pipe() failed:" << std::strerror(errno);
563 return false;
564 }
565
566 /*install notifier to handle pipe messages*/
567 QSocketNotifier* signalNotifier = new QSocketNotifier(sigpipe[0],
568 QSocketNotifier::Read, this);
569 connect(signalNotifier, &QSocketNotifier::activated,
570 this, &RosegardenMainWindow::signalAction);
571
572 /*install signal handlers*/
573 struct sigaction action;
574 memset(&action, 0, sizeof(action));
575 action.sa_handler = handleSignal;
576
577 if (sigaction(SIGUSR1, &action, nullptr) == -1) {
578 RG_WARNING << "installSignalHandlers(): sigaction() failed:" << std::strerror(errno);
579 return false;
580 }
581
582 return true;
583 }
584
585 /* Slot to give response to the incoming pipe message;
586 e.g.: save current file */
587 void
signalAction(int fd)588 RosegardenMainWindow::signalAction(int fd)
589 {
590 int message;
591
592 if (read(fd, &message, sizeof(message)) == -1) {
593 RG_WARNING << "signalAction(): read() failed:" << std::strerror(errno);
594 return;
595 }
596
597 switch (message) {
598 case SIGUSR1:
599 slotFileSave();
600 break;
601 default:
602 RG_WARNING << "signalAction(): Unexpected signal received:" << message;
603 break;
604 }
605 }
606
607 void
closeEvent(QCloseEvent * event)608 RosegardenMainWindow::closeEvent(QCloseEvent *event)
609 {
610 if (queryClose()) {
611 // Save window geometry and toolbar state
612 //RG_DEBUG << "closeEvent(): Saving main window geometry...";
613 QSettings settings;
614 settings.beginGroup(WindowGeometryConfigGroup);
615 settings.setValue("Main_Window_Geometry", this->saveGeometry());
616 settings.setValue("Main_Window_State", this->saveState());
617 settings.endGroup();
618
619 settings.beginGroup(GeneralOptionsConfigGroup);
620 settings.setValue("show_status_bar", !statusBar()->isHidden());
621 settings.setValue("show_stock_toolbar", !findToolbar("Main Toolbar")->isHidden());
622 settings.setValue("show_tools_toolbar", !findToolbar("Tools Toolbar")->isHidden());
623 settings.setValue("show_tracks_toolbar", !findToolbar("Tracks Toolbar")->isHidden());
624 settings.setValue("show_editors_toolbar", !findToolbar("Editors Toolbar")->isHidden());
625 settings.setValue("show_transport_toolbar", !findToolbar("Transport Toolbar")->isHidden());
626 settings.setValue("show_zoom_toolbar", !findToolbar("Zoom Toolbar")->isHidden());
627 settings.setValue("show_transport", findAction("show_transport")->isChecked());
628
629 if (m_transport) {
630 settings.setValue("transport_flap_extended", m_transport->isExpanded());
631 }
632
633 settings.setValue("show_tracklabels", findAction("show_tracklabels")->isChecked());
634 settings.setValue("show_rulers", findAction("show_rulers")->isChecked());
635 settings.setValue("show_tempo_ruler", findAction("show_tempo_ruler")->isChecked());
636 settings.setValue("show_chord_name_ruler", findAction("show_chord_name_ruler")->isChecked());
637 settings.setValue("show_previews", findAction("show_previews")->isChecked());
638 settings.setValue("show_segment_labels", findAction("show_segment_labels")->isChecked());
639 settings.setValue("show_inst_segment_parameters", findAction("show_inst_segment_parameters")->isChecked());
640 settings.endGroup();
641
642 // Continue closing.
643 event->accept();
644 } else {
645 // Do not close.
646 event->ignore();
647 }
648 }
649
650 void
setupActions()651 RosegardenMainWindow::setupActions()
652 {
653 createAction("file_new", SLOT(slotFileNew()));
654 createAction("file_open", SLOT(slotFileOpen()));
655 createAction("file_open_example", SLOT(slotFileOpenExample()));
656 createAction("file_open_template", SLOT(slotFileOpenTemplate()));
657 createAction("file_save", SLOT(slotFileSave()));
658 createAction("file_save_as", SLOT(slotFileSaveAs()));
659 createAction("file_save_as_template", SLOT(slotFileSaveAsTemplate()));
660 createAction("file_revert", SLOT(slotRevertToSaved()));
661 createAction("file_close", SLOT(slotFileClose()));
662 createAction("file_quit", SLOT(slotQuit()));
663
664 createAction("edit_cut", SLOT(slotEditCut()));
665 createAction("edit_copy", SLOT(slotEditCopy()));
666 createAction("edit_paste", SLOT(slotEditPaste()));
667 //uncomment this when time comes to implement paste as links
668 //createAction("edit_paste_as_links", SLOT(slotEditPasteAsLinks()));
669
670 createAction("options_configure", SLOT(slotConfigure()));
671
672 createAction("file_import_project", SLOT(slotImportProject()));
673 createAction("file_import_midi", SLOT(slotImportMIDI()));
674 createAction("file_import_rg21", SLOT(slotImportRG21()));
675 // createAction("file_import_hydrogen", SLOT(slotImportHydrogen()));
676 createAction("file_import_musicxml", SLOT(slotImportMusicXML()));
677 createAction("file_merge", SLOT(slotMerge()));
678 createAction("file_merge_midi", SLOT(slotMergeMIDI()));
679 createAction("file_merge_rg21", SLOT(slotMergeRG21()));
680 // createAction("file_merge_hydrogen", SLOT(slotMergeHydrogen()));
681 createAction("file_merge_musicxml", SLOT(slotMergeMusicXML()));
682 createAction("file_export_project", SLOT(slotExportProject()));
683 createAction("file_export_midi", SLOT(slotExportMIDI()));
684 createAction("file_export_lilypond", SLOT(slotExportLilyPond()));
685 createAction("file_export_musicxml", SLOT(slotExportMusicXml()));
686 createAction("file_export_csound", SLOT(slotExportCsound()));
687 createAction("file_export_mup", SLOT(slotExportMup()));
688 createAction("file_print_lilypond", SLOT(slotPrintLilyPond()));
689 createAction("file_preview_lilypond", SLOT(slotPreviewLilyPond()));
690 createAction("file_show_playlist", SLOT(slotPlayList()));
691
692 // Help menu
693 createAction("manual", SLOT(slotHelp()));
694 createAction("tutorial", SLOT(slotTutorial()));
695 createAction("guidelines", SLOT(slotBugGuidelines()));
696 createAction("help_about_app", SLOT(slotHelpAbout()));
697 createAction("help_about_qt", SLOT(slotHelpAboutQt()));
698 createAction("donate", SLOT(slotDonate()));
699
700 createAction("show_stock_toolbar", SLOT(slotToggleToolBar()));
701 createAction("show_tools_toolbar", SLOT(slotToggleToolsToolBar()));
702 createAction("show_tracks_toolbar", SLOT(slotToggleTracksToolBar()));
703 createAction("show_editors_toolbar", SLOT(slotToggleEditorsToolBar()));
704 createAction("show_transport_toolbar", SLOT(slotToggleTransportToolBar()));
705 createAction("show_zoom_toolbar", SLOT(slotToggleZoomToolBar()));
706 createAction("show_status_bar", SLOT(slotToggleStatusBar()));
707 createAction("show_transport", SLOT(slotUpdateTransportVisibility()));
708 createAction("show_tracklabels", SLOT(slotToggleTrackLabels()));
709 createAction("show_rulers", SLOT(slotToggleRulers()));
710 createAction("show_tempo_ruler", SLOT(slotToggleTempoRuler()));
711 createAction("show_chord_name_ruler", SLOT(slotToggleChordNameRuler()));
712 createAction("show_previews", SLOT(slotTogglePreviews()));
713 createAction("show_inst_segment_parameters", SLOT(slotHideShowParameterArea()));
714 createAction("select", SLOT(slotPointerSelected()));
715 createAction("draw", SLOT(slotDrawSelected()));
716 createAction("erase", SLOT(slotEraseSelected()));
717 createAction("move", SLOT(slotMoveSelected()));
718 createAction("resize", SLOT(slotResizeSelected()));
719 createAction("split", SLOT(slotSplitSelected()));
720 createAction("join", SLOT(slotJoinSelected()));
721 createAction("harmonize_selection", SLOT(slotHarmonizeSelection()));
722 createAction("add_time_signature", SLOT(slotEditTimeSignature()));
723 createAction("edit_tempos", SLOT(slotEditTempos()));
724 createAction("cut_range", SLOT(slotCutRange()));
725 createAction("copy_range", SLOT(slotCopyRange()));
726 createAction("paste_range", SLOT(slotPasteRange()));
727 createAction("delete_range", SLOT(slotDeleteRange()));
728 createAction("insert_range", SLOT(slotInsertRange()));
729 createAction("paste_conductor_data", SLOT(slotPasteConductorData()));
730 createAction("erase_range_tempos", SLOT(slotEraseRangeTempos()));
731 createAction("delete", SLOT(slotDeleteSelectedSegments()));
732 createAction("select_all", SLOT(slotSelectAll()));
733 createAction("add_tempo", SLOT(slotEditTempo()));
734 createAction("change_composition_length", SLOT(slotChangeCompositionLength()));
735 createAction("edit_markers", SLOT(slotEditMarkers()));
736 createAction("edit_doc_properties", SLOT(slotEditDocumentProperties()));
737 // throw a redundant copy on the View menu; even though it edits too, we
738 // just call it "View -> Document Properties" (I got this idea when I
739 // noticed that some piece of configuration in OO.o was on two different
740 // menus, when I looked for it in two different places, and found it in
741 // both. It seems reasonable to me if not overdone.)
742 createAction("view_doc_properties", SLOT(slotEditDocumentProperties()));
743 createAction("edit_default", SLOT(slotEdit()));
744 createAction("edit_matrix", SLOT(slotEditInMatrix()));
745 createAction("edit_percussion_matrix", SLOT(slotEditInPercussionMatrix()));
746 createAction("edit_notation", SLOT(slotEditAsNotation()));
747 createAction("edit_event_list", SLOT(slotEditInEventList()));
748 createAction("edit_pitch_tracker", SLOT(slotEditInPitchTracker()));
749 createAction("quantize_selection", SLOT(slotQuantizeSelection()));
750 createAction("relabel_segment", SLOT(slotRelabelSegments()));
751 createAction("transpose", SLOT(slotTransposeSegments()));
752 createAction("transpose_semitones", SLOT(slotTransposeSemitones()));
753 createAction("switch_preset", SLOT(slotSwitchPreset()));
754 createAction("repeat_quantize", SLOT(slotRepeatQuantizeSelection()));
755 createAction("rescale", SLOT(slotRescaleSelection()));
756 createAction("auto_split", SLOT(slotAutoSplitSelection()));
757 createAction("split_by_pitch", SLOT(slotSplitSelectionByPitch()));
758 createAction("split_by_recording", SLOT(slotSplitSelectionByRecordedSrc()));
759 createAction("split_at_time", SLOT(slotSplitSelectionAtTime()));
760 createAction("split_by_drum", SLOT(slotSplitSelectionByDrum()));
761 createAction("jog_left", SLOT(slotJogLeft()));
762 createAction("jog_right", SLOT(slotJogRight()));
763 createAction("create_anacrusis", SLOT(slotCreateAnacrusis()));
764 createAction("set_segment_start", SLOT(slotSetSegmentStartTimes()));
765 createAction("set_segment_duration", SLOT(slotSetSegmentDurations()));
766 createAction("join_segments", SLOT(slotJoinSegments()));
767 createAction("expand_figuration", SLOT(slotExpandFiguration()));
768 createAction("update_figurations", SLOT(slotUpdateFigurations()));
769 createAction("repeats_to_real_copies", SLOT(slotRepeatingSegments()));
770 createAction("links_to_real_copies", SLOT(slotLinksToCopies()));
771 createAction("manage_trigger_segments", SLOT(slotManageTriggerSegments()));
772 createAction("groove_quantize", SLOT(slotGrooveQuantize()));
773 createAction("fit_beats", SLOT(slotFitToBeats()));
774 createAction("set_tempo_to_segment_length", SLOT(slotTempoToSegmentLength()));
775 createAction("audio_manager", SLOT(slotAudioManager()));
776 createAction("show_segment_labels", SLOT(slotToggleSegmentLabels()));
777 createAction("add_track", SLOT(slotAddTrack()));
778 createAction("add_tracks", SLOT(slotAddTracks()));
779 createAction("delete_track", SLOT(slotDeleteTrack()));
780 createAction("move_track_down", SLOT(slotMoveTrackDown()));
781 createAction("move_track_up", SLOT(slotMoveTrackUp()));
782 createAction("select_next_track", SLOT(slotSelectNextTrack()));
783 createAction("select_previous_track", SLOT(slotSelectPreviousTrack()));
784 createAction("toggle_mute_track", SLOT(slotToggleMute()));
785 createAction("toggle_arm_track", SLOT(slotToggleRecordCurrentTrack()));
786 createAction("mute_all_tracks", SLOT(slotMuteAllTracks()));
787 createAction("unmute_all_tracks", SLOT(slotUnmuteAllTracks()));
788 createAction("remap_instruments", SLOT(slotRemapInstruments()));
789 createAction("audio_mixer", SLOT(slotOpenAudioMixer()));
790 createAction("midi_mixer", SLOT(slotOpenMidiMixer()));
791 createAction("manage_midi_devices", SLOT(slotManageMIDIDevices()));
792 createAction("manage_synths", SLOT(slotManageSynths()));
793 createAction("modify_midi_filters", SLOT(slotModifyMIDIFilters()));
794 createAction("manage_metronome", SLOT(slotManageMetronome()));
795 createAction("save_default_studio", SLOT(slotSaveDefaultStudio()));
796 createAction("load_default_studio", SLOT(slotImportDefaultStudio()));
797 createAction("load_studio", SLOT(slotImportStudio()));
798 createAction("reset_midi_network", SLOT(slotResetMidiNetwork()));
799 createAction("add_marker", SLOT(slotAddMarker2()));
800 createAction("previous_marker", SLOT(slotPreviousMarker()));
801 createAction("next_marker", SLOT(slotNextMarker()));
802 createAction("set_quick_marker", SLOT(slotSetQuickMarker()));
803 createAction("jump_to_quick_marker", SLOT(slotJumpToQuickMarker()));
804
805 createAction("play", SLOT(slotPlay()));
806 createAction("stop", SLOT(slotStop()));
807 createAction("fast_forward", SLOT(slotFastforward()));
808 createAction("rewind", SLOT(slotRewind()));
809 createAction("recordtoggle", SLOT(slotToggleRecord()));
810 createAction("record", SLOT(slotRecord()));
811 createAction("rewindtobeginning", SLOT(slotRewindToBeginning()));
812 createAction("fastforwardtoend", SLOT(slotFastForwardToEnd()));
813 createAction("toggle_tracking", SLOT(slotToggleTracking()));
814 createAction("panic", SLOT(slotPanic()));
815 createAction("debug_dump_segments", SLOT(slotDebugDump()));
816
817 createAction("repeat_segment_onoff", m_segmentParameterBox, SLOT(slotToggleRepeat()));
818
819 createMenusAndToolbars("rosegardenmainwindow.rc");
820
821 createAndSetupTransport();
822
823 // Hook up for aboutToShow() so we can set up the menu when it is
824 // needed.
825 QMenu *fileOpenRecentMenu = findMenu("file_open_recent");
826 connect(fileOpenRecentMenu, &QMenu::aboutToShow,
827 this, &RosegardenMainWindow::setupRecentFilesMenu);
828
829 // transport toolbar is hidden by default - TODO : this should be in options
830 //
831 //toolBar("Transport Toolbar")->hide();
832
833 // was QPopupMenu
834 QMenu* setTrackInstrumentMenu = this->findChild<QMenu*>("set_track_instrument");
835
836 if (setTrackInstrumentMenu) {
837 connect(setTrackInstrumentMenu, &QMenu::aboutToShow,
838 this, &RosegardenMainWindow::slotPopulateTrackInstrumentPopup);
839 } else {
840 RG_DEBUG << "setupActions() : couldn't find set_track_instrument menu - check rosegardenui.rcn\n";
841 }
842
843 // Set the rewind and fast-forward buttons for auto-repeat.
844 enableAutoRepeat("Transport Toolbar", "rewind");
845 enableAutoRepeat("Transport Toolbar", "fast_forward");
846
847 // Do an initial setup of the recent files so that Ctrl+R will
848 // work at startup.
849 setupRecentFilesMenu();
850 }
851
852 void
setupRecentFilesMenu()853 RosegardenMainWindow::setupRecentFilesMenu()
854 {
855 QMenu *fileOpenRecentMenu = findMenu("file_open_recent");
856 if (!fileOpenRecentMenu) {
857 RG_WARNING << "setupRecentFilesMenu(): WARNING: No recent files menu!";
858 return;
859 }
860
861 // Start with a clean slate.
862 fileOpenRecentMenu->clear();
863
864 // Remove non-existent files if configured in the .conf.
865 // This is problematic as it will cause files to be removed from
866 // the recent files list when external storage is unmounted.
867 QSettings settings;
868 settings.beginGroup(RecentFilesConfigGroup);
869 if (settings.value("cleanRecentFilesList", "false").toBool())
870 m_recentFiles.removeNonExistent();
871
872 bool first = true;
873
874 // For each recent file, make a new action and add it to the menu.
875 for (const QString &name : m_recentFiles.get()) {
876 QAction *action = new QAction(name, this);
877 action->setObjectName(name);
878 connect(action, &QAction::triggered,
879 this, &RosegardenMainWindow::slotFileOpenRecent);
880
881 fileOpenRecentMenu->addAction(action);
882
883 if (first) {
884 first = false;
885 action->setShortcut(tr("Ctrl+R"));
886 }
887 }
888 }
889
890 void
initZoomToolbar()891 RosegardenMainWindow::initZoomToolbar()
892 {
893 //### Zoom toolbar has already been created. Find it instead.
894 // QToolBar *zoomToolbar = this->addToolBar("Zoom Toolbar");
895 //
896 QToolBar *zoomToolbar = findToolbar("Zoom Toolbar");
897 if (!zoomToolbar) {
898 RG_DEBUG << "initZoomToolbar() : "
899 << "zoom toolbar not found";
900 return ;
901 }
902
903 QLabel *label = new QLabel(tr(" Zoom: "));
904 zoomToolbar->addWidget(label);
905
906 std::vector<double> zoomSizes; // in units-per-pixel
907 double defaultBarWidth44 = 100.0;
908 double duration44 = TimeSignature(4, 4).getBarDuration();
909 static double factors[] = { 0.025, 0.05, 0.1, 0.2, 0.5,
910 1.0, 1.5, 2.5, 5.0, 10.0 , 20.0 };
911
912 for (size_t i = 0; i < sizeof(factors) / sizeof(factors[0]); ++i) {
913 zoomSizes.push_back(duration44 / (defaultBarWidth44 * factors[i]));
914 }
915
916 // zoom labels
917 QString minZoom = QString("%1%").arg(factors[0] * 100.0);
918 //QString maxZoom = QString("%1%").arg(factors[(sizeof(factors) / sizeof(factors[0])) - 1] * 100.0);
919
920 m_zoomSlider = new ZoomSlider<double>
921 (zoomSizes, -1, Qt::Horizontal, zoomToolbar);
922 m_zoomSlider->setTracking(true);
923 m_zoomSlider->setFocusPolicy(Qt::NoFocus);
924 m_zoomLabel = new QLabel(minZoom, zoomToolbar);
925 m_zoomLabel->setIndent(10);
926
927 connect(m_zoomSlider, &QAbstractSlider::valueChanged,
928 this, &RosegardenMainWindow::slotChangeZoom);
929
930 zoomToolbar->addWidget(m_zoomSlider);
931 zoomToolbar->addWidget(m_zoomLabel);
932
933 // set initial zoom - we might want to make this a settings option
934 // m_zoomSlider->setToDefault();
935
936 }
937
938 void
initStatusBar()939 RosegardenMainWindow::initStatusBar()
940 {
941 m_cpuBar = new ProgressBar(100, statusBar());
942 m_cpuBar->setObjectName("Main Window progress bar"); // to help keep ProgressBar objects straight
943 m_cpuBar->setFixedWidth(60);
944 m_cpuBar->setFixedHeight(18);
945 QFont font = m_cpuBar->font();
946 font.setPixelSize(10);
947 m_cpuBar->setFont(font);
948
949 m_cpuBar->setTextVisible(false);
950 statusBar()->addPermanentWidget(m_cpuBar);
951
952 // status warning widget replaces a glob of annoying startup dialogs
953 m_warningWidget = new WarningWidget(this);
954 statusBar()->addPermanentWidget(m_warningWidget);
955 statusBar()->setContentsMargins(0, 0, 0, 0);
956 }
957
958 void
initView()959 RosegardenMainWindow::initView()
960 {
961 //RG_DEBUG << "initView()...";
962
963 Composition &comp = m_doc->getComposition();
964
965 // Ensure that the start and end markers for the piece are set
966 // to something reasonable
967 //
968 if (comp.getStartMarker() == 0 && comp.getEndMarker() == 0) {
969 int endMarker = comp.getBarRange(100 + comp.getNbBars()).second;
970 comp.setEndMarker(endMarker);
971 }
972
973 // The plan is to set the new central view via setCentralWidget() in
974 // a moment, which schedules the old one for deletion later via
975 // QObject::deleteLater().
976 RosegardenMainViewWidget *oldView = m_view;
977
978 // We need to make sure the parameter boxes don't send any
979 // signals to the old view!
980 disconnect(m_segmentParameterBox, nullptr, oldView, nullptr);
981 disconnect(m_instrumentParameterBox, nullptr, oldView, nullptr);
982 disconnect(m_trackParameterBox, nullptr, oldView, nullptr);
983
984 RosegardenMainViewWidget *swapView = new RosegardenMainViewWidget
985 (findAction("show_tracklabels")->isChecked(),
986 m_segmentParameterBox,
987 m_instrumentParameterBox,
988 m_trackParameterBox,
989 m_parameterArea,
990 this);
991
992 // Connect up this signal so that we can force tool mode
993 // changes from the view
994 connect(swapView, &RosegardenMainViewWidget::activateTool,
995 this, &RosegardenMainWindow::slotActivateTool);
996
997 connect(swapView,
998 &RosegardenMainViewWidget::segmentsSelected,
999 this, &RosegardenMainWindow::segmentsSelected);
1000
1001 connect(swapView,
1002 &RosegardenMainViewWidget::addAudioFile,
1003 this, &RosegardenMainWindow::slotAddAudioFile);
1004
1005 connect(swapView, &RosegardenMainViewWidget::toggleSolo, this, &RosegardenMainWindow::slotToggleSolo);
1006
1007 m_doc->attachView(swapView);
1008
1009 // Transport setup
1010 //
1011 std::string transportMode = m_doc->getConfiguration().get<String>
1012 (DocumentConfiguration::TransportMode);
1013
1014
1015 slotEnableTransport(true);
1016
1017 // and the time signature
1018 //
1019 getTransport()->setTimeSignature(comp.getTimeSignatureAt(comp.getPosition()));
1020
1021 // set the tempo in the transport
1022 //
1023 m_seqManager->setTempo(comp.getCurrentTempo());
1024
1025 // bring the transport to the front
1026 //
1027 getTransport()->raise();
1028
1029 // set the play metronome button
1030 getTransport()->MetronomeButton()->setChecked(comp.usePlayMetronome());
1031
1032 // set the transport mode found in the configuration
1033 getTransport()->setNewMode(transportMode);
1034
1035 // set the pointer position
1036 //
1037 slotSetPointerPosition(m_doc->getComposition().getPosition());
1038
1039 // !!! The call to setCentralWidget() below will delete oldView.
1040 m_view = swapView;
1041
1042 connect(m_view, &RosegardenMainViewWidget::stateChange,
1043 this, &RosegardenMainWindow::slotStateChanged);
1044
1045 // We only check for the SequenceManager to make sure
1046 // we're not on the first pass though - we don't want
1047 // to send these toggles twice on initialisation.
1048 //
1049 // Clunky but we just about get away with it for the
1050 // moment.
1051 //
1052 if (m_seqManager != nullptr) {
1053 slotToggleChordNameRuler();
1054 slotToggleRulers();
1055 slotToggleTempoRuler();
1056 slotTogglePreviews();
1057 slotToggleSegmentLabels();
1058
1059 // Reset any loop on the sequencer
1060 //
1061 try {
1062 if (isUsingSequencer()) m_seqManager->setLoop(0, 0);
1063 leaveActionState("have_range"); //@@@ JAS orig. KXMLGUIClient::StateReverse
1064 } catch (const QString &s) {
1065 StartupLogo::hideIfStillThere();
1066 QMessageBox::critical(dynamic_cast<QWidget*>(this), tr("Rosegarden"), s, QMessageBox::Ok, QMessageBox::Ok);
1067 }
1068 }
1069
1070 // delete m_playList;
1071 // m_playList = 0;
1072
1073 delete m_synthManager;
1074 m_synthManager = nullptr;
1075
1076 // ??? Instead, AMW2 could connect for RMW::documentAboutToChange() which
1077 // it should probably connect for anyway. Then it could close in
1078 // response. That would remove these lines from RMW. That's how the
1079 // old AMW did it. Or even better, we might not close at all. Just
1080 // handle the situation and stay up.
1081 if (m_audioMixerWindow2)
1082 m_audioMixerWindow2->close();
1083
1084 delete m_bankEditor;
1085 m_bankEditor = nullptr;
1086
1087 delete m_markerEditor;
1088 m_markerEditor = nullptr;
1089
1090 delete m_tempoView;
1091 m_tempoView = nullptr;
1092
1093 delete m_triggerSegmentManager;
1094 m_triggerSegmentManager = nullptr;
1095
1096 // !!! This also deletes oldView (via QObject::deleteLater()).
1097 setCentralWidget(m_view);
1098
1099 // set the highlighted track
1100 comp.notifyTrackSelectionChanged(comp.getSelectedTrack());
1101 m_view->slotSelectTrackSegments(comp.getSelectedTrack());
1102
1103 // play tracking on in the editor by default: turn off if need be
1104 /* was toggle */
1105 // old. QAction *trackingAction = dynamic_cast<QAction*>
1106 // (actionCollection()->action("toggle_tracking"));
1107 QAction *trackingAction = findAction("toggle_tracking");
1108 if (trackingAction && !trackingAction->isChecked()) {
1109 m_view->getTrackEditor()->toggleTracking();
1110 }
1111
1112 m_view->show();
1113
1114 connect(m_view->getTrackEditor()->getCompositionView(),
1115 &CompositionView::showContextHelp,
1116 this,
1117 &RosegardenMainWindow::slotShowToolHelp);
1118
1119 // We have to do this to make sure that the 2nd call ("select")
1120 // actually has any effect. Activating the same radio action
1121 // doesn't work the 2nd time (like pressing down the same radio
1122 // button twice - it doesn't have any effect), so if you load two
1123 // files in a row, on the 2nd file a new CompositionView will be
1124 // created but its tool won't be set, even though it will appear
1125 // to be selected.
1126 //
1127 QAction *actionx = this->findAction(QString("move"));
1128 actionx->trigger();
1129
1130 if (m_doc->getComposition().getNbSegments() > 0){
1131 QAction *actionx = this->findAction(QString("select"));
1132 actionx->trigger();
1133 } else {
1134 QAction *actionx = this->findAction(QString("draw"));
1135 actionx->trigger();
1136 }
1137
1138 int zoomLevel = m_doc->getConfiguration().get<Int>(DocumentConfiguration::ZoomLevel);
1139 m_zoomSlider->setSize(double(zoomLevel) / 1000.0);
1140 slotChangeZoom(zoomLevel);
1141
1142 enterActionState("new_file"); //@@@ JAS orig. 0
1143
1144 if (findAction("show_chord_name_ruler")->isChecked()) {
1145 SetWaitCursor swc;
1146 m_view->initChordNameRuler();
1147 } else {
1148 m_view->initChordNameRuler();
1149 }
1150 }
1151
1152 void
setDocument(RosegardenDocument * newDocument)1153 RosegardenMainWindow::setDocument(RosegardenDocument* newDocument)
1154 {
1155 if (m_doc == newDocument) return;
1156
1157 // Save the modified status so that we can put it back after we
1158 // are done here.
1159 // ??? Probably worth investigating why this is necessary, and
1160 // thinking through an alternate approach. Perhaps this
1161 // check/restore could be moved into the culprit at least.
1162 bool modified = newDocument->isModified();
1163
1164 emit documentAboutToChange();
1165 qApp->processEvents(); // to make sure all opened dialogs (mixer, midi devices...) are closed
1166
1167 // Take care of all subparts which depend on the document
1168
1169 // // reset AudioManagerDialog
1170 // //
1171 // delete m_audioManagerDialog; // TODO : replace this with a connection to documentAboutToChange() sig.
1172 // m_audioManagerDialog = 0;
1173
1174 RosegardenDocument* oldDoc = m_doc;
1175
1176 m_doc = newDocument;
1177
1178 updateTitle();
1179
1180 if (m_seqManager) // when we're called at startup, the seq. man. isn't created yet
1181 m_seqManager->setDocument(m_doc);
1182
1183 if (m_markerEditor)
1184 m_markerEditor->setDocument(m_doc);
1185 if (m_tempoView) {
1186 delete m_tempoView;
1187 m_tempoView = nullptr;
1188 }
1189 if (m_triggerSegmentManager)
1190 m_triggerSegmentManager->setDocument(m_doc);
1191
1192 m_trackParameterBox->setDocument(m_doc);
1193 m_editTempoController->setDocument(m_doc);
1194
1195 if (m_pluginGUIManager) {
1196 m_pluginGUIManager->stopAllGUIs();
1197 m_pluginGUIManager->setStudio(&m_doc->getStudio());
1198 }
1199
1200 if (getView() &&
1201 getView()->getTrackEditor() &&
1202 getView()->getTrackEditor()->getCompositionView()) {
1203 getView()->getTrackEditor()->getCompositionView()->endAudioPreviewGeneration();
1204 }
1205
1206 // connect needed signals
1207
1208 connect(m_doc, &RosegardenDocument::pointerPositionChanged,
1209 this, &RosegardenMainWindow::slotSetPointerPosition);
1210
1211 connect(m_doc, &RosegardenDocument::documentModified,
1212 this, &RosegardenMainWindow::slotDocumentModified);
1213
1214 // connecting this independently of slotDocumentModified in the hope that it
1215 // will better reflect the true state of things
1216 connect(m_doc, &RosegardenDocument::documentModified,
1217 this, &RosegardenMainWindow::slotUpdateTitle);
1218
1219 connect(m_doc, SIGNAL(loopChanged(timeT, timeT)),
1220 this, SLOT(slotSetLoop(timeT, timeT)));
1221
1222 // CommandHistory::getInstance()->attachView(actionCollection()); //&&& needed ? how to ?
1223
1224 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1225 SLOT(update()));
1226 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1227 SLOT(slotTestClipboard()));
1228
1229 // start the autosave timer
1230 m_autoSaveTimer->start(m_doc->getAutoSavePeriod() * 1000);
1231
1232 connect(m_doc, &RosegardenDocument::devicesResyncd,
1233 this, &RosegardenMainWindow::slotDocumentDevicesResyncd);
1234
1235 if (m_useSequencer) {
1236 // Connect the devices prior to calling initView() to make sure there
1237 // is connection information for the MIPP to display.
1238 RosegardenSequencer::getInstance()->connectSomething();
1239
1240 newDocument->getStudio().resyncDeviceConnections();
1241
1242 // Send out the channel setups.
1243 // Since the device connections might have changed due to the
1244 // connectSomething() call above, we need to send out the channel
1245 // setups at this point. Otherwise if we load a Composition with
1246 // empty connections (like the example Vivaldi op.44), we get piano
1247 // on every track.
1248 newDocument->initialiseStudio();
1249 }
1250
1251 // Create a new RosegardenMainViewWidget and set it as the central
1252 // widget. The old RMVW instance will be scheduled for deletion later.
1253 initView();
1254
1255 // This will delete all edit views.
1256 delete oldDoc;
1257 oldDoc = nullptr;
1258
1259 if (getView() && getView()->getTrackEditor()) {
1260 connect(m_doc, &RosegardenDocument::makeTrackVisible,
1261 getView()->getTrackEditor(), &TrackEditor::slotScrollToTrack);
1262 }
1263
1264 // Make sure the view and the new document match.
1265 m_view->slotSynchroniseWithComposition();
1266
1267 if (newDocument->getStudio().haveMidiDevices()) {
1268 enterActionState("got_midi_devices"); //@@@ JAS orig. 0
1269 } else {
1270 leaveActionState("got_midi_devices"); //@@@ JAS orig KXMLGUIClient::StateReverse
1271 }
1272
1273 // Ensure the sequencer knows about any audio files
1274 // we've loaded as part of the new Composition
1275 //
1276 m_doc->prepareAudio();
1277
1278 // Remove the audio segments from the clipboard as they point to
1279 // bogus file IDs now.
1280 m_clipboard->removeAudioSegments();
1281
1282 // Do not reset instrument prog. changes after all.
1283 // if (m_seqManager)
1284 // m_seqManager->preparePlayback(true);
1285
1286 Composition &comp = m_doc->getComposition();
1287
1288 // Set any loaded loop at the Composition and
1289 // on the marker on CompositionView and clients
1290 //
1291 if (m_seqManager)
1292 m_doc->setLoop(comp.getLoopStart(), comp.getLoopEnd());
1293
1294 emit documentLoaded(m_doc);
1295
1296 // Restore the document's original modified status.
1297 if (modified)
1298 m_doc->slotDocumentModified();
1299 else
1300 m_doc->clearModifiedStatus();
1301
1302 // Readjust canvas size
1303 //
1304 m_view->getTrackEditor()->updateCanvasSize();
1305
1306 // Show notes about the composition in a pop up dialog if asked for
1307 new CommentsPopupDialog(m_doc, this);
1308 }
1309
1310 void
openFile(QString filePath,ImportType type)1311 RosegardenMainWindow::openFile(QString filePath, ImportType type)
1312 {
1313 //RG_DEBUG << "openFile(): " << filePath;
1314
1315 // If we're opening a .rgp file, delegate to importProject()
1316 if (type == ImportCheckType && filePath.endsWith(".rgp")) {
1317 importProject(filePath);
1318 return;
1319 }
1320
1321 // Are we just reloading the original file? In that case, we'll need
1322 // to avoid locking/releasing the file.
1323 bool revert = false;
1324
1325 if (m_doc) {
1326 QFileInfo newFileInfo(filePath);
1327 revert = (newFileInfo.absoluteFilePath() == m_doc->getAbsFilePath());
1328 }
1329
1330 RosegardenDocument *doc = createDocument(filePath, type, !revert);
1331
1332 if (!doc)
1333 return;
1334
1335 if (revert) {
1336 doc->stealLockFile(m_doc);
1337 }
1338 setDocument(doc);
1339
1340 // fix #782, "SPB combo not updating after document swap"
1341 //RG_DEBUG << "openFile(): calling slotDocColoursChanged() in doc";
1342 doc->slotDocColoursChanged();
1343
1344
1345 // Preferences: Always use default studio when loading files
1346
1347 QSettings settings;
1348 settings.beginGroup(GeneralOptionsConfigGroup);
1349 bool alwaysUseDefaultStudio =
1350 qStrToBool(settings.value("alwaysusedefaultstudio", "false"));
1351 settings.endGroup();
1352
1353 if (alwaysUseDefaultStudio) {
1354
1355 QString autoloadFile = ResourceFinder().getAutoloadPath();
1356 QFileInfo autoloadFileInfo(autoloadFile);
1357
1358 if (autoloadFile != "" && autoloadFileInfo.isReadable()) {
1359
1360 //RG_DEBUG << "openFile(): Importing default studio from " << autoloadFile;
1361
1362 slotImportStudioFromFile(autoloadFile);
1363 }
1364 }
1365
1366
1367 // Add to the MRU list
1368 QFileInfo fileInfo(filePath);
1369 m_recentFiles.add(fileInfo.absoluteFilePath());
1370 // Make sure Ctrl+R is correct.
1371 setupRecentFilesMenu();
1372
1373 // As an empty composition can be saved, we need to look if
1374 // segments exist before enabling print options in menu
1375 // ??? This should be done in response to a Composition change
1376 // notification.
1377 if (doc->getComposition().getSegments().size())
1378 enterActionState("have_segments");
1379 else
1380 leaveActionState("have_segments");
1381 }
1382
1383 RosegardenDocument *
createDocument(QString filePath,ImportType importType,bool lock)1384 RosegardenMainWindow::createDocument(
1385 QString filePath, ImportType importType, bool lock)
1386 {
1387 // ??? This and the create functions it calls might make more sense in
1388 // RosegardenDocument.
1389
1390 QFileInfo fileInfo(filePath);
1391
1392 if (!fileInfo.exists()) {
1393 // can happen with command-line arg, so...
1394 StartupLogo::hideIfStillThere();
1395 QMessageBox::warning(this, tr("Rosegarden"),
1396 tr("File \"%1\" does not exist").arg(filePath),
1397 QMessageBox::Ok, QMessageBox::Ok);
1398 return nullptr;
1399 }
1400
1401 if (fileInfo.isDir()) {
1402 StartupLogo::hideIfStillThere();
1403 QMessageBox::warning(this, tr("Rosegarden"),
1404 tr("File \"%1\" is actually a directory").arg(filePath),
1405 QMessageBox::Ok, QMessageBox::Ok);
1406 return nullptr;
1407 }
1408
1409 QFile file(filePath);
1410
1411 if (!file.open(QIODevice::ReadOnly)) {
1412 StartupLogo::hideIfStillThere();
1413 QMessageBox::warning(this, tr("Rosegarden"),
1414 tr("You do not have read permission for \"%1\"").arg(filePath),
1415 QMessageBox::Ok, QMessageBox::Ok);
1416 return nullptr;
1417 }
1418
1419 // If we need to import based on the filename extension
1420 if (importType == ImportCheckType) {
1421 QString extension = fileInfo.suffix().toLower();
1422
1423 if (extension == "mid" || extension == "midi")
1424 importType = ImportMIDI;
1425 else if (extension == "rg" || extension == "rgt")
1426 importType = ImportRG4;
1427 else if (extension == "rgd")
1428 importType = ImportRGD;
1429 else if (extension == "rose")
1430 importType = ImportRG21;
1431 else if (extension == "xml")
1432 importType = ImportMusicXML;
1433 //else if (extension == "h2song")
1434 // importType = ImportHydrogen;
1435 }
1436
1437 if (importType == ImportRGD) {
1438 StartupLogo::hideIfStillThere();
1439 QMessageBox::warning(this, tr("Rosegarden"),
1440 tr("File \"%1\" is a Rosegarden Device, and must be imported using the MIDI device manager.").arg(filePath),
1441 QMessageBox::Ok, QMessageBox::Ok);
1442 return nullptr;
1443 }
1444
1445 // If the sequencer is playing, stop it.
1446 if (m_seqManager && m_seqManager->getTransportStatus() == PLAYING)
1447 slotStop();
1448
1449 // Prevent playback while loading.
1450 // ??? This only disables the buttons on the TransportDialog. It
1451 // does not disable the toolbar buttons or the menu items.
1452 // Luckily, we do not crash if we are playing and a document
1453 // is loading.
1454 slotEnableTransport(false);
1455
1456 RosegardenDocument *doc = nullptr;
1457
1458 switch (importType) {
1459 case ImportMIDI:
1460 doc = createDocumentFromMIDIFile(filePath);
1461 break;
1462
1463 case ImportRG21:
1464 doc = createDocumentFromRG21File(filePath);
1465 break;
1466
1467 //case ImportHydrogen:
1468 // doc = createDocumentFromHydrogenFile(filePath);
1469 // break;
1470
1471 case ImportMusicXML:
1472 doc = createDocumentFromMusicXMLFile(filePath);
1473 break;
1474
1475 case ImportRG4:
1476 case ImportCheckType:
1477 default:
1478 doc = createDocumentFromRGFile(filePath, lock);
1479 break;
1480
1481 case ImportRGD: // Satisfy compiler warning.
1482 // Handled above.
1483 break;
1484 }
1485
1486 slotEnableTransport(true);
1487
1488 return doc;
1489 }
1490
1491 RosegardenDocument *
createDocumentFromRGFile(const QString & filePath,bool lock)1492 RosegardenMainWindow::createDocumentFromRGFile(
1493 const QString &filePath, bool lock)
1494 {
1495 // ??? This and its caller should probably be moved into
1496 // RosegardenDocument as static factory functions.
1497
1498 // The file we are going to open in the end. This could be either
1499 // the requested file or the auto-saved version of that file.
1500 QString openFilePath = filePath;
1501
1502 // Check for an auto-save file to recover
1503 QString autoSaveFileName = AutoSaveFinder().checkAutoSaveFile(filePath);
1504
1505 bool recovering = (autoSaveFileName != "");
1506
1507 if (recovering) {
1508 QFileInfo fileInfo(filePath);
1509 QFileInfo autoSaveFileInfo(autoSaveFileName);
1510
1511 // If the auto-save file is more recent
1512 if (autoSaveFileInfo.lastModified() > fileInfo.lastModified()) {
1513 // At this point the splash screen may still be there, hide it
1514 // before showing the messagebox.
1515 StartupLogo::hideIfStillThere();
1516
1517 // Ask the user if they want to use the auto-save file
1518 QMessageBox::StandardButton reply = QMessageBox::question(
1519 this, tr("Rosegarden"),
1520 tr("An auto-save file for this document has been found\nDo you want to open it instead ?"),
1521 QMessageBox::Yes | QMessageBox::No);
1522
1523 if (reply == QMessageBox::Yes) {
1524 // open the auto-save file instead
1525 openFilePath = autoSaveFileName;
1526 } else {
1527 // user doesn't want the auto-save, so delete it
1528 // so it won't bother us again if we reload
1529 QFile::remove(autoSaveFileName);
1530 recovering = false;
1531 }
1532 } else { // Auto-save file is older. Ignore it.
1533 recovering = false;
1534 }
1535 }
1536
1537 // Create a new blank document
1538 RosegardenDocument *newDoc =
1539 new RosegardenDocument(this,
1540 m_pluginManager,
1541 true, // skipAutoload
1542 true, // clearCommandHistory
1543 m_useSequencer); // enableSound
1544
1545 // Read the document from the file.
1546 bool readOk = newDoc->openDocument(openFilePath, true, false, lock);
1547
1548 // If the read failed, bail.
1549 if (!readOk) {
1550 delete newDoc;
1551 return nullptr;
1552 }
1553
1554 if (recovering) {
1555 newDoc->slotDocumentModified();
1556
1557 // Replace the auto-save filepath with the filepath of
1558 // the original file.
1559 QFileInfo fileInfo(filePath);
1560 newDoc->setAbsFilePath(fileInfo.absoluteFilePath());
1561 newDoc->setTitle(fileInfo.fileName());
1562 }
1563
1564 return newDoc;
1565 }
1566
1567 void
readOptions()1568 RosegardenMainWindow::readOptions()
1569 {
1570 QSettings settings;
1571
1572 // Statusbar and toolbars toggling action status
1573 //
1574 bool opt;
1575
1576 settings.beginGroup(GeneralOptionsConfigGroup);
1577
1578 opt = qStrToBool(settings.value("show_status_bar", "true"));
1579 findAction("show_status_bar")->setChecked(opt);
1580 slotToggleStatusBar();
1581
1582 opt = qStrToBool(settings.value("show_stock_toolbar", "true"));
1583 findAction("show_stock_toolbar")->setChecked(opt);
1584 slotToggleToolBar();
1585
1586 opt = qStrToBool(settings.value("show_tools_toolbar", "true"));
1587 findAction("show_tools_toolbar")->setChecked(opt);
1588 slotToggleToolsToolBar();
1589
1590 opt = qStrToBool(settings.value("show_tracks_toolbar", "true"));
1591 findAction("show_tracks_toolbar")->setChecked(opt);
1592 slotToggleTracksToolBar();
1593
1594 opt = qStrToBool(settings.value("show_editors_toolbar", "true"));
1595 findAction("show_editors_toolbar")->setChecked(opt);
1596 slotToggleEditorsToolBar();
1597
1598 opt = qStrToBool(settings.value("show_transport_toolbar", "true"));
1599 findAction("show_transport_toolbar")->setChecked(opt);
1600 slotToggleTransportToolBar();
1601
1602 opt = qStrToBool(settings.value("show_zoom_toolbar", "true"));
1603 findAction("show_zoom_toolbar")->setChecked(opt);
1604 slotToggleZoomToolBar();
1605
1606 opt = qStrToBool(settings.value("show_transport", "true")) ;
1607 findAction("show_transport")->setChecked(opt);
1608 slotUpdateTransportVisibility();
1609
1610 opt = qStrToBool(settings.value("transport_flap_extended", "true")) ;
1611
1612 #ifdef SETTING_LOG_DEBUG
1613 RG_DEBUG << "SETTING 3 : transport flap extended = " << opt;
1614 #endif
1615
1616 if (opt)
1617 getTransport()->slotPanelOpenButtonClicked();
1618 else
1619 getTransport()->slotPanelCloseButtonClicked();
1620
1621 opt = qStrToBool(settings.value("show_tracklabels", "true")) ;
1622
1623 #ifdef SETTING_LOG_DEBUG
1624 RG_DEBUG << "SETTING 3 : show track labels = " << opt;
1625 #endif
1626
1627 findAction("show_tracklabels")->setChecked(opt);
1628 slotToggleTrackLabels();
1629
1630 opt = qStrToBool(settings.value("show_rulers", "true")) ;
1631 findAction("show_rulers")->setChecked(opt);
1632 slotToggleRulers();
1633
1634 opt = qStrToBool(settings.value("show_tempo_ruler", "true")) ;
1635 findAction("show_tempo_ruler")->setChecked(opt);
1636 slotToggleTempoRuler();
1637
1638 opt = qStrToBool(settings.value("show_chord_name_ruler", "false")) ;
1639 findAction("show_chord_name_ruler")->setChecked(opt);
1640 slotToggleChordNameRuler();
1641
1642 opt = qStrToBool(settings.value("show_previews", "true")) ;
1643 findAction("show_previews")->setChecked(opt);
1644 slotTogglePreviews();
1645
1646 opt = qStrToBool(settings.value("show_segment_labels", "true")) ;
1647 findAction("show_segment_labels")->setChecked(opt);
1648 slotToggleSegmentLabels();
1649
1650 opt = qStrToBool(settings.value("show_inst_segment_parameters", true));
1651 findAction("show_inst_segment_parameters")->setChecked(opt);
1652 slotHideShowParameterArea();
1653
1654 settings.endGroup();
1655
1656 m_actionsSetup = true;
1657 }
1658
1659 void
saveGlobalProperties()1660 RosegardenMainWindow::saveGlobalProperties()
1661 {
1662 QSettings settings;
1663 //@@@ JAS Do we need a settings.startGroup() here?
1664
1665 if (m_doc->getTitle() != tr("Untitled") && !m_doc->isModified()) {
1666 // saving to tempfile not necessary
1667 } else {
1668 QString filename = m_doc->getAbsFilePath();
1669 settings.setValue("filename", filename);
1670 settings.setValue("modified", m_doc->isModified());
1671
1672 QString tempname = AutoSaveFinder().getAutoSavePath(filename);
1673 if (tempname != "") {
1674 QString errMsg;
1675 bool res = m_doc->saveDocument(tempname, errMsg);
1676 if (!res) {
1677 if (!errMsg.isEmpty()) {
1678 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1\nError was : %2").arg(tempname).arg(errMsg));
1679 } else {
1680 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1").arg(tempname));
1681 }
1682 }
1683 }
1684 }
1685 }
1686
1687 #if 0
1688 void
1689 RosegardenMainWindow::readGlobalProperties()
1690 {
1691 QSettings settings;
1692 //@@@ JAS Do we need a settings.startGroup() here?
1693
1694 QString filename = settings.value("filename", "").toString();
1695 bool modified = qStrToBool(settings.value("modified", "false")) ;
1696
1697 if (modified) {
1698
1699 QString tempname = AutoSaveFinder().checkAutoSaveFile(filename);
1700
1701 if (tempname != "") {
1702 slotEnableTransport(false);
1703 m_doc->openDocument(tempname);
1704 slotEnableTransport(true);
1705 m_doc->slotDocumentModified();
1706 QFileInfo info(filename);
1707 m_doc->setAbsFilePath(info.absoluteFilePath());
1708 m_doc->setTitle(info.fileName());
1709 }
1710 } else {
1711 if (!filename.isEmpty()) {
1712 slotEnableTransport(false);
1713 m_doc->openDocument(filename);
1714 slotEnableTransport(true);
1715 }
1716 }
1717
1718 updateTitle();
1719 }
1720 #endif
1721
1722 void
showEvent(QShowEvent * e)1723 RosegardenMainWindow::showEvent(QShowEvent* e)
1724 {
1725 RG_DEBUG << "showEvent()";
1726
1727 getTransport()->raise();
1728
1729 QMainWindow::showEvent(e);
1730 }
1731
1732 bool
queryClose()1733 RosegardenMainWindow::queryClose()
1734 {
1735 // If we are recording, don't let the user close.
1736 if (m_seqManager->getTransportStatus() == RECORDING)
1737 return false;
1738
1739 // Let the user save any unsaved changes.
1740 return saveIfModified();
1741 }
1742
1743 void
slotFileNew()1744 RosegardenMainWindow::slotFileNew()
1745 {
1746 // RG_DEBUG << "slotFileNew()\n";
1747
1748 TmpStatusMsg msg(tr("Creating new document..."), this);
1749
1750 bool makeNew = false;
1751
1752 if (!m_doc->isModified()) {
1753 makeNew = true;
1754 // m_doc->closeDocument();
1755 } else if (saveIfModified()) {
1756 makeNew = true;
1757 }
1758
1759 if (makeNew) {
1760 setDocument(newDocument());
1761 leaveActionState("have_segments");
1762 }
1763 }
1764
1765 void
slotUpdateTitle(bool modified)1766 RosegardenMainWindow::slotUpdateTitle(bool modified)
1767 {
1768 // NB: I seems like using doc->isModified() would be a more accurate state
1769 // test than the value of "modified", but in practice there is a lag factor
1770 // of a few ms where we have gotten one value in "modified" here, and
1771 // isModified() is in the opposite state briefly. I don't think there's
1772 // any real concern there, so I just switched everything over to use the
1773 // state of the bool passed with the signal and ignore isModified()
1774 //RG_DEBUG << "slotUpdateTitle(" << modified << ")";
1775
1776 // Preferences: Show full path in window titles.
1777 QSettings settings;
1778 settings.beginGroup(GeneralOptionsConfigGroup);
1779 bool showFullPath =
1780 settings.value("long_window_titles", false).toBool();
1781 settings.endGroup();
1782
1783 QString filename;
1784
1785 if (showFullPath) {
1786 // If it's not "Untitled", use the full path.
1787 if (m_doc->getAbsFilePath() != "")
1788 filename = m_doc->getAbsFilePath();
1789 else // Otherwise use the title. Which is probably "Untitled".
1790 filename = m_doc->getTitle();
1791 } else {
1792 filename = m_doc->getTitle();
1793 }
1794
1795 setWindowTitle(
1796 tr("%1%2 - %3").
1797 arg((modified ? "*" : "")).
1798 arg(filename).
1799 arg(qApp->applicationName()));
1800 }
1801
1802 void
updateTitle()1803 RosegardenMainWindow::updateTitle()
1804 {
1805 slotUpdateTitle(m_doc->isModified());
1806 }
1807
1808 void
slotOpenDroppedURL(QString url)1809 RosegardenMainWindow::slotOpenDroppedURL(QString url)
1810 {
1811 qApp->processEvents(QEventLoop::AllEvents, 100);
1812
1813 if (!saveIfModified())
1814 return ;
1815
1816 openURL(QUrl(url));
1817 }
1818
1819 void
openURL(QString url)1820 RosegardenMainWindow::openURL(QString url)
1821 {
1822 //RG_DEBUG << "openURL(): url =" << url;
1823
1824 openURL(QUrl(url));
1825 }
1826
1827 void
openURL(const QUrl & url)1828 RosegardenMainWindow::openURL(const QUrl& url)
1829 {
1830 SetWaitCursor waitCursor;
1831
1832 //RG_DEBUG << "openURL(): url =" << url;
1833
1834 if (!url.isValid()) {
1835 QMessageBox::warning(this, tr("Rosegarden"),
1836 tr("Malformed URL\n%1").arg(url.toString()));
1837
1838 return;
1839 }
1840
1841 FileSource source(url);
1842
1843 if (!source.isAvailable()) {
1844 QMessageBox::critical(this, tr("Rosegarden"),
1845 tr("Cannot open file %1").arg(url.toString()));
1846 return;
1847 }
1848
1849 //RG_DEBUG << "openURL(): local filename =" << source.getLocalFilename();
1850
1851 // Let the user save the current document if it's been modified.
1852 // ??? rename: safeToClobber()? Might be too geared toward the result.
1853 if (!saveIfModified())
1854 return;
1855
1856 // In case the source is remote, wait for the file to be downloaded to
1857 // the local file.
1858 source.waitForData();
1859
1860 openFile(source.getLocalFilename());
1861 }
1862
1863 void
openFileDialogAt(QString target)1864 RosegardenMainWindow::openFileDialogAt(QString target)
1865 {
1866 slotStatusHelpMsg(tr("Opening file..."));
1867
1868 QSettings settings;
1869 QString directory;
1870
1871 // if target is empty, this is a generic file open dialog, otherwise
1872 // we open the specified target (examples, templates)
1873 if (target.isEmpty()) {
1874 // Get the last used path for File > Open.
1875 settings.beginGroup(LastUsedPathsConfigGroup);
1876 directory = settings.value("open_file", QDir::homePath()).toString();
1877 settings.endGroup();
1878 } else {
1879 directory = target;
1880 }
1881
1882 // Launch the Open File dialog.
1883 QString fname = FileDialog::getOpenFileName(this, tr("Open File"), directory,
1884 tr("All supported files") + " (*.rg *.RG *.rgt *.RGT *.rgp *.RGP *.mid *.MID *.midi *.MIDI)" + ";;" +
1885 tr("Rosegarden files") + " (*.rg *.RG *.rgp *.RGP *.rgt *.RGT)" + ";;" +
1886 tr("MIDI files") + " (*.mid *.MID *.midi *.MIDI)" + ";;" +
1887 tr("All files") + " (*)", nullptr);
1888
1889 // If the user has cancelled, bail.
1890 if (fname.isEmpty())
1891 return;
1892
1893 if (target.isEmpty()) {
1894 // Update the last used path for File > Open.
1895 directory = QFileInfo(fname).canonicalPath();
1896 settings.beginGroup(LastUsedPathsConfigGroup);
1897 settings.setValue("open_file", directory);
1898 settings.endGroup();
1899 }
1900
1901 // If a document is currently loaded
1902 if (m_doc) {
1903 // Check to see if the user needs/wants to save the current document.
1904 // ??? openURL() does this too. Should we defer to it?
1905 bool okToOpen = saveIfModified();
1906
1907 // If the user has cancelled, bail.
1908 if (!okToOpen)
1909 return;
1910 }
1911
1912 // Continue opening the file.
1913 openURL(QUrl::fromLocalFile(fname));
1914 }
1915
1916 void
slotFileOpen()1917 RosegardenMainWindow::slotFileOpen()
1918 {
1919 openFileDialogAt("");
1920 }
1921
1922 QString
getDataLocation()1923 RosegardenMainWindow::getDataLocation()
1924 {
1925 #if QT_VERSION >= 0x050000
1926 QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1927 #else
1928 QString dataLocation = QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.local/share/";
1929 #endif
1930 RG_DEBUG << "getDataLocation(): returning: " << dataLocation;
1931 return dataLocation;
1932 }
1933
1934 void
slotFileOpenExample()1935 RosegardenMainWindow::slotFileOpenExample()
1936 {
1937 QString target = getDataLocation() + "/rosegarden/examples";
1938 openFileDialogAt(target);
1939 }
1940
1941 void
slotFileOpenTemplate()1942 RosegardenMainWindow::slotFileOpenTemplate()
1943 {
1944 QString target = getDataLocation() + "/rosegarden/templates";
1945 openFileDialogAt(target);
1946 }
1947
1948 void
slotMerge()1949 RosegardenMainWindow::slotMerge()
1950 {
1951 QSettings settings;
1952 settings.beginGroup(LastUsedPathsConfigGroup);
1953 QString directory = settings.value("merge_file", QDir::homePath()).toString();
1954
1955 const QString file = FileDialog::getOpenFileName(this, tr("Open File"), directory,
1956 tr("Rosegarden files") + " (*.rg *.RG)" + ";;" +
1957 tr("All files") + " (*)", nullptr);
1958
1959 if (file.isEmpty()) {
1960 return ;
1961 }
1962
1963 QDir d = QFileInfo(file).dir();
1964 directory = d.canonicalPath();
1965 settings.setValue("merge_file", directory);
1966 settings.endGroup();
1967
1968 mergeFile(file);
1969 }
1970
1971 void
slotFileOpenRecent()1972 RosegardenMainWindow::slotFileOpenRecent()
1973 {
1974 QObject *obj = sender();
1975 QAction *action = dynamic_cast<QAction *>(obj);
1976
1977 if (!action) {
1978 RG_WARNING << "slotFileOpenRecent(): WARNING: sender is not an action";
1979 return;
1980 }
1981
1982 QString pathOrUrl = action->objectName();
1983 if (pathOrUrl.isEmpty()) return;
1984
1985 TmpStatusMsg msg(tr("Opening file..."), this);
1986
1987 if (m_doc) {
1988 if (!saveIfModified()) {
1989 return ;
1990 }
1991 }
1992
1993 openURL(QUrl::fromUserInput(pathOrUrl));
1994 }
1995
1996 void
slotFileSave()1997 RosegardenMainWindow::slotFileSave()
1998 {
1999 if (!m_doc /*|| !m_doc->isModified()*/)
2000 return ; // ALWAYS save, even if doc is not modified.
2001
2002 TmpStatusMsg msg(tr("Saving file..."), this);
2003
2004 // if it's a new file (no file path), or an imported file
2005 // (file path doesn't end with .rg), call saveAs
2006 //
2007 if (!m_doc->isRegularDotRGFile()) {
2008
2009 slotFileSaveAs();
2010
2011 } else {
2012
2013 SetWaitCursor waitCursor;
2014 QString errMsg, docFilePath = m_doc->getAbsFilePath();
2015
2016 bool res = m_doc->saveDocument(docFilePath, errMsg);
2017 if (!res) {
2018 if (! errMsg.isEmpty())
2019 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1\nError was : %2")
2020 .arg(docFilePath).arg(errMsg));
2021 else
2022 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1")
2023 .arg(docFilePath));
2024 }
2025 }
2026 }
2027
2028 QString
getValidWriteFileName(QString descriptiveExtension,QString label)2029 RosegardenMainWindow::getValidWriteFileName(QString descriptiveExtension,
2030 QString label)
2031 {
2032 // extract first extension listed in descriptiveExtension, for instance,
2033 // ".rg" from "Rosegarden files (*.rg)", or ".mid" from
2034 // "MIDI Files (*.mid *.midi)"
2035 //
2036 int left = descriptiveExtension.indexOf("*.");
2037 int right = descriptiveExtension.indexOf(QRegularExpression("[ ]"),left);
2038 QString extension = descriptiveExtension.mid(left+1,right-left-1);
2039
2040 // keep track of last place used to save, by type of file (this behavior is
2041 // quite new and different, and should probably be considered experimental,
2042 // although the more I think about the unexpected consequences of this idea
2043 // that I didn't think through at the outset, the more I think the idea is
2044 // just better than I realized, and this was a great idea)
2045 QString path_key = "save_file";
2046
2047 if (extension == ".rgt") path_key = "save_template";
2048 else if (extension == ".mid") path_key = "export_midi";
2049 else if (extension == ".xml") path_key = "export_music_xml";
2050 else if (extension == ".ly") path_key = "export_lilypond";
2051 else if (extension == ".csd") path_key = "export_csound";
2052 else if (extension == ".mup") path_key = "export_mup";
2053
2054 //RG_DEBUG <<
2055 // "getValidWriteFileName() : extension = " <<
2056 // extension << endl <<
2057 // " path key = " <<
2058 // path_key;
2059
2060 // Get the directory from the settings
2061 QSettings settings;
2062 settings.beginGroup(LastUsedPathsConfigGroup);
2063 QString directory = settings.value(path_key, QDir::homePath()).toString();
2064
2065 QFileInfo originalFileInfo(m_doc->getAbsFilePath());
2066
2067 // Most applications (e.g. OpenOffice.org and the GIMP) use the document's
2068 // directory for Save As... and Export rather than the last directory
2069 // the user saved to. To use the document's directory, replace
2070 // "directory" in the FileDialog::getSaveFileName() call with
2071 // originalFileInfo.absolutePath().
2072
2073 // Confirm the overwrite of the file later.
2074 //
2075 // (Hah, all these compiler warnings are useful for something after all.
2076 // This used to not do anything with the label parameter, and always said
2077 // "Save File" 100% of the time.)
2078 QString name = FileDialog::getSaveFileName(
2079 this, label, directory,
2080 originalFileInfo.baseName(), descriptiveExtension, nullptr,
2081 FileDialog::DontConfirmOverwrite);
2082
2083 //RG_DEBUG << "getValidWriteFileName() : " <<
2084 // "FileDialog::getSaveFileName returned " << name;
2085
2086 if (name.isEmpty())
2087 return name;
2088
2089 // Append extension if we don't have one
2090 //
2091 if (!extension.isEmpty()) {
2092 static QRegularExpression rgFile("\\..{1,4}$");
2093 if (! rgFile.match(name).hasMatch()) {
2094 name += extension;
2095 }
2096 }
2097
2098 // if we get a string like "/tmp/~/foo.rg" assume the last saved path was
2099 // /tmp and the desired new path is ~ and try to doctor the string up, which
2100 // may well fail, but it's better to try
2101 if (name.contains("~")) {
2102 name = name.remove(0, name.indexOf("~") + 1);
2103 name = name.prepend(QDir::homePath());
2104
2105 //RG_DEBUG << "doctored filename after ~ swap: " << name;
2106
2107 }
2108
2109 QFileInfo info(name);
2110
2111 if (info.isDir()) {
2112 QMessageBox::warning(this, tr("Rosegarden"),
2113 tr("You have specified a folder/directory."));
2114 return "";
2115 }
2116
2117 if (info.exists()) {
2118 int overwrite = QMessageBox::question(
2119 this,
2120 tr("Rosegarden"),
2121 tr("The specified file exists. Overwrite?"),
2122 QMessageBox::Yes | QMessageBox::No,
2123 QMessageBox::No);
2124
2125 if (overwrite != QMessageBox::Yes)
2126 return "";
2127 }
2128
2129 // Write the directory to the settings
2130 QDir d = QFileInfo(name).dir();
2131 directory = d.canonicalPath();
2132 settings.setValue(path_key, directory);
2133 settings.endGroup();
2134
2135 return name;
2136 }
2137
2138 bool
slotFileSaveAs(bool asTemplate)2139 RosegardenMainWindow::slotFileSaveAs(bool asTemplate)
2140 {
2141 if (!m_doc)
2142 return false;
2143
2144 TmpStatusMsg msg(tr("Saving file%1with a new filename...",
2145 "'file%1with' is correct. %1 will either become ' ' or ' as a template ' at runtime").
2146 arg(asTemplate ? tr(" as a template ") : " "), this);
2147
2148 QString fileType(asTemplate ? tr("Rosegarden templates") : tr("Rosegarden files"));
2149 QString fileExtension(asTemplate ? " (*.rgt *.RGT)" : " (*.rg *.RG)");
2150 QString dialogMessage(asTemplate ? tr("Save as template...") : tr("Save as..."));
2151
2152 QString newName = getValidWriteFileName
2153 (fileType + fileExtension + ";;" +
2154 tr("All files") + " (*)",
2155 dialogMessage);
2156 if (newName.isEmpty())
2157 return false;
2158
2159 SetWaitCursor waitCursor;
2160
2161 QString errMsg;
2162 bool res = m_doc->saveAs(newName, errMsg);
2163
2164 // save template as read-only, even though this is largely pointless
2165 if (asTemplate) {
2166 QFileInfo saveAsInfo(newName);
2167 QFile chmod(saveAsInfo.absoluteFilePath());
2168 chmod.setPermissions(QFile::ReadOwner |
2169 QFile::ReadUser | /* for potential platform-independence */
2170 QFile::ReadGroup |
2171 QFile::ReadOther);
2172 }
2173
2174 if (!res) {
2175 if (!errMsg.isEmpty())
2176 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1\nError was : %2")
2177 .arg(newName).arg(errMsg));
2178 else
2179 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1")
2180 .arg(newName));
2181
2182 } else {
2183
2184 m_recentFiles.add(newName);
2185 // Make sure Ctrl+R is correct.
2186 setupRecentFilesMenu();
2187
2188 updateTitle();
2189
2190 // update the edit view's captions too
2191 emit compositionStateUpdate();
2192 }
2193
2194 return res;
2195 }
2196
2197 void
slotFileClose()2198 RosegardenMainWindow::slotFileClose()
2199 {
2200 RG_DEBUG << "slotFileClose()";
2201
2202 if (!m_doc)
2203 return ;
2204
2205 TmpStatusMsg msg(tr("Closing file..."), this);
2206
2207 if (saveIfModified()) {
2208 setDocument(newDocument());
2209 }
2210
2211 // Don't close the whole view (i.e. Quit), just close the doc.
2212 // close();
2213 }
2214
2215 void
slotQuit()2216 RosegardenMainWindow::slotQuit()
2217 {
2218 slotStatusMsg(tr("Exiting..."));
2219
2220 Profiles::getInstance()->dump();
2221
2222 close(); // this calls closeEvent
2223 }
2224
2225 void
slotEditCut()2226 RosegardenMainWindow::slotEditCut()
2227 {
2228 if (!m_view->haveSelection())
2229 return ;
2230 TmpStatusMsg msg(tr("Cutting selection..."), this);
2231
2232 SegmentSelection selection(m_view->getSelection());
2233 CommandHistory::getInstance()->addCommand
2234 (new CutCommand(selection, m_clipboard));
2235 }
2236
2237 void
slotEditCopy()2238 RosegardenMainWindow::slotEditCopy()
2239 {
2240 if (!m_view->haveSelection())
2241 return ;
2242 TmpStatusMsg msg(tr("Copying selection to clipboard..."), this);
2243
2244 SegmentSelection selection(m_view->getSelection());
2245 CommandHistory::getInstance()->addCommand
2246 (new CopyCommand(selection, m_clipboard));
2247 }
2248
2249 void
slotEditPaste()2250 RosegardenMainWindow::slotEditPaste()
2251 {
2252 if (m_clipboard->isEmpty()) {
2253 TmpStatusMsg msg(tr("Clipboard is empty"), this);
2254 return ;
2255 }
2256 TmpStatusMsg msg(tr("Inserting clipboard contents..."), this);
2257
2258 // for now, but we could paste at the time of the first copied
2259 // segment and then do ghosting drag or something
2260 timeT insertionTime = m_doc->getComposition().getPosition();
2261 CommandHistory::getInstance()->addCommand
2262 (new PasteSegmentsCommand(&m_doc->getComposition(),
2263 m_clipboard, insertionTime,
2264 m_doc->getComposition().getSelectedTrack(),
2265 false));
2266
2267 // User preference? Update song pointer position on paste
2268 m_doc->slotSetPointerPosition(m_doc->getComposition().getPosition());
2269 }
2270
2271 void
slotEditPasteAsLinks()2272 RosegardenMainWindow::slotEditPasteAsLinks()
2273 {
2274 //this contains a copy of slotEditPaste() - as and when the time comes to
2275 //implement this properly, the code below needs uncommenting and adapting
2276
2277 /*
2278 if (m_clipboard->isEmpty()) {
2279 TmpStatusMsg msg(tr("Clipboard is empty"), this);
2280 return ;
2281 }
2282 TmpStatusMsg msg(tr("Pasting clipboard contents as linked segments..."), this);
2283
2284 // for now, but we could paste at the time of the first copied
2285 // segment and then do ghosting drag or something
2286 timeT insertionTime = m_doc->getComposition().getPosition();
2287 CommandHistory::getInstance()->addCommand
2288 (new PasteSegmentsAsLinksCommand(&m_doc->getComposition(),
2289 m_clipboard, insertionTime,
2290 m_doc->getComposition().getSelectedTrack(),
2291 false));
2292 // User preference? Update song pointer position on paste
2293 m_doc->slotSetPointerPosition(m_doc->getComposition().getPosition());
2294 */
2295 }
2296
2297 void
slotCutRange()2298 RosegardenMainWindow::slotCutRange()
2299 {
2300 timeT t0 = m_doc->getComposition().getLoopStart();
2301 timeT t1 = m_doc->getComposition().getLoopEnd();
2302
2303 if (t0 == t1)
2304 return ;
2305
2306 CommandHistory::getInstance()->addCommand
2307 (new CutRangeCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
2308 }
2309
2310 void
slotCopyRange()2311 RosegardenMainWindow::slotCopyRange()
2312 {
2313 timeT t0 = m_doc->getComposition().getLoopStart();
2314 timeT t1 = m_doc->getComposition().getLoopEnd();
2315
2316 if (t0 == t1)
2317 return ;
2318
2319 CommandHistory::getInstance()->addCommand
2320 (new CopyCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
2321 }
2322
2323 void
slotPasteRange()2324 RosegardenMainWindow::slotPasteRange()
2325 {
2326 if (m_clipboard->isEmpty())
2327 return ;
2328
2329 CommandHistory::getInstance()->addCommand
2330 (new PasteRangeCommand(&m_doc->getComposition(), m_clipboard,
2331 m_doc->getComposition().getPosition()));
2332 }
2333
2334 void
slotDeleteRange()2335 RosegardenMainWindow::slotDeleteRange()
2336 {
2337 // ??? Dead Code. There is no reference to the delete_range action in
2338 // the rc.
2339
2340 timeT t0 = m_doc->getComposition().getLoopStart();
2341 timeT t1 = m_doc->getComposition().getLoopEnd();
2342
2343 if (t0 == t1)
2344 return ;
2345
2346 CommandHistory::getInstance()->addCommand
2347 (new DeleteRangeCommand(&m_doc->getComposition(), t0, t1));
2348 }
2349
2350 void
slotInsertRange()2351 RosegardenMainWindow::slotInsertRange()
2352 {
2353 timeT t0 = m_doc->getComposition().getPosition();
2354 std::pair<timeT, timeT> r = m_doc->getComposition().getBarRangeForTime(t0);
2355 TimeDialog dialog(m_view, tr("Duration of empty range to insert"),
2356 &m_doc->getComposition(), t0, r.second - r.first, 1, false);
2357 if (dialog.exec() == QDialog::Accepted) {
2358 CommandHistory::getInstance()->addCommand
2359 (new InsertRangeCommand(&m_doc->getComposition(), t0, dialog.getTime()));
2360 }
2361 }
2362
2363 void
slotPasteConductorData()2364 RosegardenMainWindow::slotPasteConductorData()
2365 {
2366 if (m_clipboard->isEmpty())
2367 return ;
2368
2369 CommandHistory::getInstance()->addCommand
2370 (new PasteConductorDataCommand(&m_doc->getComposition(), m_clipboard,
2371 m_doc->getComposition().getPosition()));
2372 }
2373
2374 void
slotEraseRangeTempos()2375 RosegardenMainWindow::slotEraseRangeTempos()
2376 {
2377 timeT t0 = m_doc->getComposition().getLoopStart();
2378 timeT t1 = m_doc->getComposition().getLoopEnd();
2379
2380 if (t0 == t1)
2381 { return; }
2382
2383 CommandHistory::getInstance()->addCommand
2384 (new EraseTempiInRangeCommand(&m_doc->getComposition(), t0, t1));
2385 }
2386
2387 void
slotSelectAll()2388 RosegardenMainWindow::slotSelectAll()
2389 {
2390 m_view->slotSelectAllSegments();
2391 }
2392
2393 void
slotDeleteSelectedSegments()2394 RosegardenMainWindow::slotDeleteSelectedSegments()
2395 {
2396 m_view->getTrackEditor()->deleteSelectedSegments();
2397 }
2398
2399 void
slotQuantizeSelection()2400 RosegardenMainWindow::slotQuantizeSelection()
2401 {
2402 if (!m_view->haveSelection())
2403 return ;
2404
2405 //!!! this should all be in rosegardenguiview
2406
2407 QuantizeDialog dialog(m_view);
2408 if (dialog.exec() != QDialog::Accepted)
2409 return ;
2410
2411 SegmentSelection selection = m_view->getSelection();
2412
2413 MacroCommand *command = new MacroCommand
2414 (EventQuantizeCommand::getGlobalName());
2415
2416 for (SegmentSelection::iterator i = selection.begin();
2417 i != selection.end(); ++i) {
2418 command->addCommand(new EventQuantizeCommand
2419 (**i, (*i)->getStartTime(), (*i)->getEndTime(),
2420 dialog.getQuantizer()));
2421 }
2422
2423 m_view->slotAddCommandToHistory(command);
2424 }
2425
2426 void
slotRepeatQuantizeSelection()2427 RosegardenMainWindow::slotRepeatQuantizeSelection()
2428 {
2429 if (!m_view->haveSelection())
2430 return ;
2431
2432 //!!! this should all be in rosegardenguiview
2433
2434 SegmentSelection selection = m_view->getSelection();
2435
2436 MacroCommand *command = new MacroCommand
2437 (EventQuantizeCommand::getGlobalName());
2438
2439 for (SegmentSelection::iterator i = selection.begin();
2440 i != selection.end(); ++i) {
2441 command->addCommand(new EventQuantizeCommand
2442 (**i, (*i)->getStartTime(), (*i)->getEndTime(),
2443 "Quantize Dialog Grid", // no tr (config group name)
2444 EventQuantizeCommand::QUANTIZE_NORMAL));
2445 }
2446
2447 m_view->slotAddCommandToHistory(command);
2448 }
2449
2450 void
slotGrooveQuantize()2451 RosegardenMainWindow::slotGrooveQuantize()
2452 {
2453 if (!m_view->haveSelection())
2454 return ;
2455
2456 SegmentSelection selection = m_view->getSelection();
2457
2458 if (selection.size() != 1) {
2459 QMessageBox::warning(this, tr("Rosegarden"), tr("This function needs no more than one segment to be selected."));
2460 return ;
2461 }
2462
2463 Segment *s = *selection.begin();
2464 m_view->slotAddCommandToHistory(new CreateTempoMapFromSegmentCommand(s));
2465 }
2466
2467 void
slotFitToBeats()2468 RosegardenMainWindow::slotFitToBeats()
2469 {
2470 if (!m_view->haveSelection())
2471 return ;
2472
2473 SegmentSelection selection = m_view->getSelection();
2474
2475 if (selection.size() != 1) {
2476 QMessageBox::warning(this, tr("Rosegarden"), tr("This function needs no more than one segment to be selected."));
2477 return ;
2478 }
2479
2480 Segment *s = *selection.begin();
2481 m_view->slotAddCommandToHistory(new FitToBeatsCommand(s));
2482 }
2483
2484 void
slotJoinSegments()2485 RosegardenMainWindow::slotJoinSegments()
2486 {
2487 if (!m_view->haveSelection())
2488 return ;
2489
2490 //!!! this should all be in rosegardenguiview
2491 //!!! should it?
2492
2493 SegmentSelection selection = m_view->getSelection();
2494 if (selection.size() == 0)
2495 return ;
2496
2497 for (SegmentSelection::iterator i = selection.begin();
2498 i != selection.end(); ++i) {
2499 if ((*i)->getType() != Segment::Internal) {
2500 QMessageBox::warning(this, tr("Rosegarden"), tr("Can't join Audio segments"));
2501 return ;
2502 }
2503 }
2504
2505 m_view->slotAddCommandToHistory(new SegmentJoinCommand(selection));
2506 m_view->updateSelectedSegments();
2507 }
2508
2509 void
slotExpandFiguration()2510 RosegardenMainWindow::slotExpandFiguration()
2511 {
2512 if (!m_view->haveSelection())
2513 { return; }
2514
2515 //!!! this should all be in rosegardenguiview
2516 //!!! should it?
2517
2518 SegmentSelection selection = m_view->getSelection();
2519 if (selection.size() < 2)
2520 { return; }
2521
2522 for (SegmentSelection::iterator i = selection.begin();
2523 i != selection.end(); ++i) {
2524 if ((*i)->getType() != Segment::Internal) {
2525 QMessageBox::warning(this, tr("Rosegarden"),
2526 tr("Can't expand Audio segments with figuration"));
2527 return ;
2528 }
2529 }
2530
2531 m_view->slotAddCommandToHistory(new ExpandFigurationCommand(selection));
2532 m_view->updateSelectedSegments();
2533 }
2534
2535 void
slotUpdateFigurations()2536 RosegardenMainWindow::slotUpdateFigurations()
2537 {
2538 m_view->slotAddCommandToHistory(new UpdateFigurationCommand());
2539 }
2540
2541 void
slotRescaleSelection()2542 RosegardenMainWindow::slotRescaleSelection()
2543 {
2544 // ??? This routine might make more sense in RosegardenMainViewWidget.
2545
2546 // Nothing selected? Bail.
2547 if (!m_view->haveSelection())
2548 return;
2549
2550 const SegmentSelection selection = m_view->getSelection();
2551
2552 // Find the time range for the selection.
2553
2554 timeT startTime = LONG_MAX;
2555 timeT endTime = 0;
2556 bool haveAudioSegment = false;
2557
2558 // For each segment
2559 for (SegmentSelection::const_iterator i = selection.begin();
2560 i != selection.end(); ++i) {
2561 const Segment *segment = (*i);
2562
2563 if (segment->getStartTime() < startTime)
2564 startTime = segment->getStartTime();
2565
2566 if (segment->getEndMarkerTime() > endTime)
2567 endTime = segment->getEndMarkerTime();
2568
2569 if (segment->getType() == Segment::Audio)
2570 haveAudioSegment = true;
2571 }
2572
2573 // If there's an audio segment, make sure the audio path is ok.
2574 if (haveAudioSegment)
2575 testAudioPath(tr("rescaling an audio file"));
2576
2577 RescaleDialog dialog(
2578 m_view, // parent
2579 &m_doc->getComposition(), // composition
2580 startTime, // startTime
2581 endTime - startTime, // originalDuration
2582 Note(Note::Shortest).getDuration(), // minimumDuration
2583 false, // showCloseGapOption
2584 false); // constrainToCompositionDuration
2585
2586 if (dialog.exec() != QDialog::Accepted)
2587 return;
2588
2589 // Just the audio rescale commands for various housekeeping.
2590 std::vector<AudioSegmentRescaleCommand *> audioRescaleCommands;
2591
2592 int multiplier = dialog.getNewDuration();
2593 int divisor = endTime - startTime;
2594 double ratio = static_cast<double>(multiplier) / divisor;
2595
2596 RG_DEBUG << "slotRescaleSelection(): multiplier = " << multiplier << ", divisor = " << divisor << ", ratio = " << ratio;
2597
2598 // All of the rescale commands, both MIDI and Audio are added to this
2599 // macro command.
2600 MacroCommand *command = new MacroCommand(
2601 SegmentRescaleCommand::getGlobalName());
2602
2603 // For each selected segment
2604 for (SegmentSelection::iterator i = selection.begin();
2605 i != selection.end(); ++i) {
2606 Segment *segment = *i;
2607
2608 if (segment->getType() == Segment::Audio) {
2609 AudioSegmentRescaleCommand *asrc =
2610 new AudioSegmentRescaleCommand(m_doc, segment, ratio);
2611 command->addCommand(asrc);
2612
2613 audioRescaleCommands.push_back(asrc);
2614 } else {
2615 command->addCommand(
2616 new SegmentRescaleCommand(segment, multiplier, divisor));
2617 }
2618 }
2619
2620 // Progress Dialog
2621 // Note: The label text and range will be set later as needed.
2622 // ??? We should make the label text the more generic
2623 // "Rescaling segment...". Then this is OK for audio or MIDI
2624 // segments.
2625 QProgressDialog progressDialog(
2626 tr("Rescaling audio file..."), // labelText
2627 tr("Cancel"), // cancelButtonText
2628 0, 0, // min, max
2629 this); // parent
2630 progressDialog.setWindowTitle(tr("Rosegarden"));
2631 progressDialog.setWindowModality(Qt::WindowModal);
2632 // Don't want to auto close since this is a multi-step
2633 // process. Any of the steps may set progress to 100. We
2634 // will close anyway when this object goes out of scope.
2635 progressDialog.setAutoClose(false);
2636 // Just force the progress dialog up.
2637 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
2638 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
2639 // based distros might lock up. See Bug #1546.
2640 progressDialog.show();
2641
2642 // For each AudioSegmentRescaleCommand, pass on the progress dialog.
2643 for (size_t i = 0; i < audioRescaleCommands.size(); ++i) {
2644 audioRescaleCommands[i]->setProgressDialog(&progressDialog);
2645 }
2646
2647 m_view->slotAddCommandToHistory(command);
2648
2649 if (progressDialog.wasCanceled())
2650 return;
2651
2652 if (!audioRescaleCommands.empty()) {
2653 m_doc->getAudioFileManager().setProgressDialog(&progressDialog);
2654
2655 // For each AudioSegmentRescaleCommand
2656 for (size_t i = 0; i < audioRescaleCommands.size(); ++i) {
2657 int fileId = audioRescaleCommands[i]->getNewAudioFileId();
2658 if (fileId < 0)
2659 continue;
2660
2661 // Add to the sequencer
2662 slotAddAudioFile(fileId);
2663
2664 // Generate a preview
2665 m_doc->getAudioFileManager().generatePreview(fileId);
2666
2667 if (progressDialog.wasCanceled())
2668 return;
2669 }
2670 }
2671 }
2672
2673 bool
testAudioPath(QString op)2674 RosegardenMainWindow::testAudioPath(QString op)
2675 {
2676 try {
2677 m_doc->getAudioFileManager().testAudioPath();
2678 } catch (const AudioFileManager::BadAudioPathException &) {
2679 // changing the following parent to 0 fixes a nasty style problem cheap:
2680 if (QMessageBox::warning
2681 (nullptr, tr("Warning"),
2682 tr("The audio file path does not exist or is not writable.\nYou must set the audio file path to a valid directory in Document Properties before %1.\nWould you like to set it now?", op.toStdString().c_str()),
2683 QMessageBox::Yes | QMessageBox::Cancel,
2684 QMessageBox::Cancel
2685 ) == QMessageBox::Yes
2686 ){
2687 slotOpenAudioPathSettings();
2688 }
2689 return false;
2690 }
2691 return true;
2692 }
2693
2694 void
slotAutoSplitSelection()2695 RosegardenMainWindow::slotAutoSplitSelection()
2696 {
2697 if (!m_view->haveSelection())
2698 return ;
2699
2700 //!!! this should all be in rosegardenguiview
2701 //!!! or should it?
2702
2703 SegmentSelection selection = m_view->getSelection();
2704
2705 MacroCommand *command = new MacroCommand
2706 (SegmentAutoSplitCommand::getGlobalName());
2707
2708 for (SegmentSelection::iterator i = selection.begin();
2709 i != selection.end(); ++i) {
2710
2711 if ((*i)->getType() == Segment::Audio) {
2712 AudioSplitDialog aSD(this, (*i), m_doc);
2713
2714 if (aSD.exec() == QDialog::Accepted) {
2715 // split to threshold
2716 //
2717 command->addCommand(
2718 new AudioSegmentAutoSplitCommand(m_doc,
2719 *i,
2720 aSD.getThreshold()));
2721 }
2722 } else {
2723 command->addCommand(new SegmentAutoSplitCommand(*i));
2724 }
2725 }
2726
2727 m_view->slotAddCommandToHistory(command);
2728 }
2729
2730 void
slotJogLeft()2731 RosegardenMainWindow::slotJogLeft()
2732 {
2733 RG_DEBUG << "slotJogLeft";
2734 jogSelection(-Note(Note::Demisemiquaver).getDuration());
2735 }
2736
2737 void
slotJogRight()2738 RosegardenMainWindow::slotJogRight()
2739 {
2740 RG_DEBUG << "slotJogRight";
2741 jogSelection(Note(Note::Demisemiquaver).getDuration());
2742 }
2743
2744 void
jogSelection(timeT amount)2745 RosegardenMainWindow::jogSelection(timeT amount)
2746 {
2747 if (!m_view->haveSelection())
2748 return ;
2749
2750 SegmentSelection selection = m_view->getSelection();
2751
2752 SegmentReconfigureCommand *command =
2753 new SegmentReconfigureCommand(tr("Jog Selection"),
2754 &m_doc->getComposition());
2755
2756 for (SegmentSelection::iterator i = selection.begin();
2757 i != selection.end(); ++i) {
2758
2759 command->addSegment((*i),
2760 (*i)->getStartTime() + amount,
2761 (*i)->getEndMarkerTime(false) + amount,
2762 (*i)->getTrack());
2763 }
2764
2765 m_view->slotAddCommandToHistory(command);
2766 }
2767
2768 void
createAndSetupTransport()2769 RosegardenMainWindow::createAndSetupTransport()
2770 {
2771 // create the Transport GUI and add the callbacks to the
2772 // buttons and keyboard shortcuts
2773 //
2774 m_transport = new TransportDialog(this);
2775
2776 plugShortcuts(m_transport, m_transport->getShortcuts());
2777
2778
2779 // Ensure that the checkbox is unchecked if the dialog
2780 // is closed
2781 connect(m_transport, &TransportDialog::closed,
2782 this, &RosegardenMainWindow::slotCloseTransport);
2783
2784 // Handle loop setting and unsetting from the transport loop button
2785 //
2786
2787 connect(m_transport, SIGNAL(setLoop()), SLOT(slotSetLoop()));
2788 connect(m_transport, &TransportDialog::unsetLoop, this, &RosegardenMainWindow::slotUnsetLoop);
2789 connect(m_transport, &TransportDialog::panic, this, &RosegardenMainWindow::slotPanic);
2790
2791 connect(m_transport, SIGNAL(editTempo(QWidget*)),
2792 SLOT(slotEditTempo(QWidget*)));
2793
2794 connect(m_transport, SIGNAL(editTimeSignature(QWidget*)),
2795 SLOT(slotEditTimeSignature(QWidget*)));
2796
2797 connect(m_transport, SIGNAL(editTransportTime(QWidget*)),
2798 SLOT(slotEditTransportTime(QWidget*)));
2799
2800 // Handle set loop start/stop time buttons.
2801 //
2802 connect(m_transport, &TransportDialog::setLoopStartTime, this, &RosegardenMainWindow::slotSetLoopStart);
2803 connect(m_transport, &TransportDialog::setLoopStopTime, this, &RosegardenMainWindow::slotSetLoopStop);
2804 }
2805
2806 void
slotSplitSelectionByPitch()2807 RosegardenMainWindow::slotSplitSelectionByPitch()
2808 {
2809 if (!m_view->haveSelection())
2810 return ;
2811
2812 SplitByPitchDialog dialog(m_view);
2813 if (dialog.exec() != QDialog::Accepted)
2814 return ;
2815
2816 SegmentSelection selection = m_view->getSelection();
2817
2818 MacroCommand *command = new MacroCommand
2819 (SegmentSplitByPitchCommand::getGlobalName());
2820
2821 bool haveSomething = false;
2822
2823 for (SegmentSelection::iterator i = selection.begin();
2824 i != selection.end(); ++i) {
2825
2826 if ((*i)->getType() == Segment::Audio) {
2827 // nothing
2828 } else {
2829 command->addCommand
2830 (new SegmentSplitByPitchCommand
2831 (*i,
2832 dialog.getPitch(),
2833 (SegmentSplitByPitchCommand::SplitStrategy)
2834 dialog.getStrategy(),
2835 dialog.getShouldDuplicateNonNoteEvents(),
2836 (SegmentSplitByPitchCommand::ClefHandling)
2837 dialog.getClefHandling()));
2838 haveSomething = true;
2839 }
2840 }
2841
2842 if (haveSomething)
2843 m_view->slotAddCommandToHistory(command);
2844 //!!! else complain
2845 }
2846
2847 void
slotSplitSelectionByRecordedSrc()2848 RosegardenMainWindow::slotSplitSelectionByRecordedSrc()
2849 {
2850 if (!m_view->haveSelection())
2851 return ;
2852
2853 SplitByRecordingSrcDialog dialog(m_view, m_doc);
2854 if (dialog.exec() != QDialog::Accepted)
2855 return ;
2856
2857 SegmentSelection selection = m_view->getSelection();
2858
2859 MacroCommand *command = new MacroCommand
2860 (SegmentSplitByRecordingSrcCommand::getGlobalName());
2861
2862 bool haveSomething = false;
2863
2864 for (SegmentSelection::iterator i = selection.begin();
2865 i != selection.end(); ++i) {
2866
2867 if ((*i)->getType() == Segment::Audio) {
2868 // nothing
2869 } else {
2870 command->addCommand
2871 (new SegmentSplitByRecordingSrcCommand(*i,
2872 dialog.getChannel(),
2873 dialog.getDevice()));
2874 haveSomething = true;
2875 }
2876 }
2877 if (haveSomething)
2878 m_view->slotAddCommandToHistory(command);
2879 }
2880
2881 void
slotSplitSelectionAtTime()2882 RosegardenMainWindow::slotSplitSelectionAtTime()
2883 {
2884 if (!m_view->haveSelection())
2885 return ;
2886
2887 SegmentSelection selection = m_view->getSelection();
2888 if (selection.empty())
2889 return ;
2890
2891 timeT now = m_doc->getComposition().getPosition();
2892
2893 QString title = tr("Split %n Segment(s) at Time", "",
2894 selection.size());
2895
2896 TimeDialog dialog(m_view, title,
2897 &m_doc->getComposition(),
2898 now, true);
2899
2900 MacroCommand *command = new MacroCommand(title);
2901
2902 if (dialog.exec() == QDialog::Accepted) {
2903 int segmentCount = 0;
2904 for (SegmentSelection::iterator i = selection.begin();
2905 i != selection.end(); ++i) {
2906
2907 if ((*i)->getType() == Segment::Audio) {
2908 AudioSegmentSplitCommand *subCommand =
2909 new AudioSegmentSplitCommand(*i, dialog.getTime());
2910 if (subCommand->isValid()) {
2911 command->addCommand(subCommand);
2912 ++segmentCount;
2913 }
2914 } else {
2915 SegmentSplitCommand *subCommand =
2916 new SegmentSplitCommand(*i, dialog.getTime());
2917 if (subCommand->isValid()) {
2918 command->addCommand(subCommand);
2919 ++segmentCount;
2920 }
2921 }
2922 }
2923
2924 if (segmentCount) {
2925 // Change the command's name to indicate how many segments were
2926 // actually split.
2927 title = tr("Split %n Segment(s) at Time", "", segmentCount);
2928 command->setName(title);
2929
2930 m_view->slotAddCommandToHistory(command);
2931 } else {
2932 QMessageBox::information(this, tr("Rosegarden"),
2933 tr("Split time is not within a selected segment.\n"
2934 "No segment will be split."));
2935 }
2936 }
2937 }
2938
2939 void
slotSplitSelectionByDrum()2940 RosegardenMainWindow::slotSplitSelectionByDrum()
2941 {
2942 if (!m_view->haveSelection()) return;
2943
2944 SegmentSelection selection = m_view->getSelection();
2945 if (selection.empty()) return;
2946
2947 // timeT now = m_doc->getComposition().getPosition();
2948
2949 QString title = tr("Split %n Segment(s) by Drum", "", selection.size());
2950
2951 // TimeDialog dialog(m_view, title,
2952 // &m_doc->getComposition(),
2953 // now, true);
2954
2955 MacroCommand *command = new MacroCommand(title);
2956
2957 //TODO there may be an options dialog where you set up where to write what
2958 // pitches... also considering adding a new field to the percussion key map
2959 // to show indicated pitch for each trigger pitch, eg. several hi-hat
2960 // variants all have G on top of staff pitch...
2961 //
2962 // also this magic thingie could hack the events while it's at it, to set up
2963 // special percussion-related properties that do not as yet actually exist,
2964 // like whether to draw with an X head, and also stuff that will be needed
2965 // for LilyPond export to handle things correctly, like this is what in
2966 // LilyPond and this is what else in LilyPond, built into the events as new
2967 // properties or something
2968 //
2969 // haven't gotten that far in my planning yet... figure it's better to get
2970 // something started so I end up shamed into seeing it through, because I
2971 // can chat with myself in design documents for eternity without
2972 // accomplishing anything
2973 //
2974 //
2975 // if (dialog.exec() == QDialog::Accepted) {
2976 int segmentCount = 0;
2977 for (SegmentSelection::iterator i = selection.begin();
2978 i != selection.end(); ++i) {
2979
2980 if ((*i)->getType() == Segment::Audio) {
2981 return;
2982
2983 // message box to inform user that this only works on MIDI
2984 // segments? if we do, we should only show it one time
2985 } else {
2986
2987
2988 // get percussion key map, if available, or a fat 0 otherwise
2989 Composition &comp = m_doc->getComposition();
2990 Track *track = comp.getTrackById((*i)->getTrack());
2991 Instrument *inst = m_doc->getStudio().getInstrumentById(track->getInstrument());
2992 const MidiKeyMapping *keyMap = inst->getKeyMapping();
2993
2994 SegmentSplitByDrumCommand *subCommand = new SegmentSplitByDrumCommand(*i, keyMap);
2995 command->addCommand(subCommand);
2996 ++segmentCount;
2997 }
2998 }
2999
3000 if (segmentCount) {
3001 // Change the command's name to indicate how many segments were
3002 // actually split.
3003 title = tr("Split %n Segment(s) by Drum", "", segmentCount);
3004 command->setName(title);
3005
3006 m_view->slotAddCommandToHistory(command);
3007 } else {
3008 QMessageBox::information(this, tr("Rosegarden"),
3009 tr("No segment was split."));
3010 }
3011 // }
3012 }
3013
3014 void
slotCreateAnacrusis()3015 RosegardenMainWindow::slotCreateAnacrusis()
3016 {
3017 if (!m_view->haveSelection()) return;
3018
3019 SegmentSelection selection = m_view->getSelection();
3020 if (selection.empty()) return;
3021 Composition &comp = m_doc->getComposition();
3022
3023 bool haveBeginningSegment = false;
3024 timeT compOrigStart = comp.getStartMarker();
3025 timeT compositionEnd = comp.getEndMarker();
3026
3027 for (SegmentSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
3028 if ((*i)->getStartTime() == compOrigStart) haveBeginningSegment = true;
3029 }
3030
3031 if (!haveBeginningSegment) {
3032 QMessageBox::information(this,
3033 tr("Rosegarden"),
3034 tr("<qt><p>In order to create anacrusis, at least one of the segments in your selection must start at the beginning of the composition.</p></qt>")
3035 );
3036 return;
3037 }
3038
3039 timeT defaultDuration = Note(Note::QuarterNote).getDuration();
3040 timeT minimumDuration = Note(Note::SixtyFourthNote).getDuration();
3041 bool constrainToCompositionDuration = false;
3042 TimeDialog dialog(m_view,
3043 tr("Anacrusis Amount"),
3044 &comp,
3045 compOrigStart - defaultDuration,
3046 defaultDuration,
3047 minimumDuration,
3048 constrainToCompositionDuration);
3049
3050 // NB. To get this to work correctly and preserve the ability to undo the
3051 // entire operation, I finally resorted to splitting it up into three
3052 // separate commands. This means it takes three undo operations to revert
3053 // what can be done in one swipe. I find this rather irritating, but I've
3054 // decided to quit while I'm ahead, and leave it here.
3055 if (dialog.exec() == QDialog::Accepted) {
3056 timeT anacrusisAmount = dialog.getTime();
3057 timeT newCompStart = compOrigStart - (comp.getBarEnd(1) - comp.getBarStart(1));
3058 MacroCommand *macro = new MacroCommand(tr("Create Anacrusis"));
3059
3060 ChangeCompositionLengthCommand *changeLengthCommand =
3061 new ChangeCompositionLengthCommand(&comp,
3062 newCompStart, compositionEnd,
3063 comp.autoExpandEnabled());
3064
3065 bool plural = (selection.size() > 1);
3066 SegmentReconfigureCommand *reconfigureCommand =
3067 new SegmentReconfigureCommand(plural ?
3068 tr("Set Segment Start Times") :
3069 tr("Set Segment Start Time"),
3070 &m_doc->getComposition());
3071
3072 for (SegmentSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
3073
3074 //timeT newStartTime = compOrigStart - anacrusisAmount;
3075 timeT newStartTime = (*i)->getStartTime() - anacrusisAmount;
3076 reconfigureCommand->addSegment(*i,
3077 newStartTime,
3078 (*i)->getEndMarkerTime(false) - (*i)->getStartTime() + newStartTime,
3079 (*i)->getTrack()
3080 );
3081 }
3082
3083 macro->addCommand(changeLengthCommand);
3084 macro->addCommand(reconfigureCommand);
3085
3086 CommandHistory::getInstance()->addCommand(macro);
3087
3088 macro = new MacroCommand(tr("Insert Corrected Tempo and Time Signature"));
3089 macro->addCommand(new AddTempoChangeCommand(&comp,
3090 comp.getStartMarker(),
3091 comp.getTempoAtTime(compOrigStart)));
3092
3093 macro->addCommand(new AddTimeSignatureAndNormalizeCommand(&comp,
3094 comp.getStartMarker(),
3095 comp.getTimeSignatureAt(compOrigStart)));
3096
3097 CommandHistory::getInstance()->addCommand(macro);
3098
3099 macro = new MacroCommand(tr("Remove Original Tempo and Time Signature"));
3100 macro->addCommand(new RemoveTimeSignatureCommand(&comp,
3101 comp.getTimeSignatureNumberAt(compOrigStart)));
3102
3103 macro->addCommand(new RemoveTempoChangeCommand(&comp,
3104 comp.getTempoChangeNumberAt(compOrigStart)));
3105
3106 CommandHistory::getInstance()->addCommand(macro);
3107 }
3108 }
3109
3110 void
slotSetSegmentStartTimes()3111 RosegardenMainWindow::slotSetSegmentStartTimes()
3112 {
3113 if (!m_view->haveSelection()) return ;
3114
3115 SegmentSelection selection = m_view->getSelection();
3116 if (selection.empty()) return ;
3117
3118 timeT someTime = (*selection.begin())->getStartTime();
3119
3120 TimeDialog dialog(m_view, tr("Segment Start Time"),
3121 &m_doc->getComposition(),
3122 someTime, false);
3123
3124 if (dialog.exec() == QDialog::Accepted) {
3125
3126 bool plural = (selection.size() > 1);
3127
3128 SegmentReconfigureCommand *command =
3129 new SegmentReconfigureCommand(plural ?
3130 tr("Set Segment Start Times") :
3131 tr("Set Segment Start Time"),
3132 &m_doc->getComposition());
3133
3134 for (SegmentSelection::iterator i = selection.begin();
3135 i != selection.end(); ++i) {
3136
3137 command->addSegment
3138 (*i, dialog.getTime(),
3139 (*i)->getEndMarkerTime(false) - (*i)->getStartTime() + dialog.getTime(),
3140 (*i)->getTrack());
3141 }
3142
3143 m_view->slotAddCommandToHistory(command);
3144 }
3145 }
3146
3147 void
slotSetSegmentDurations()3148 RosegardenMainWindow::slotSetSegmentDurations()
3149 {
3150 if (!m_view->haveSelection())
3151 return ;
3152
3153 SegmentSelection selection = m_view->getSelection();
3154 if (selection.empty())
3155 return ;
3156
3157 timeT someTime =
3158 (*selection.begin())->getStartTime();
3159
3160 timeT someDuration =
3161 (*selection.begin())->getEndMarkerTime() -
3162 (*selection.begin())->getStartTime();
3163
3164 TimeDialog dialog(m_view, tr("Segment Duration"),
3165 &m_doc->getComposition(),
3166 someTime,
3167 someDuration,
3168 Note(Note::Shortest).getDuration(),
3169 false);
3170
3171 if (dialog.exec() == QDialog::Accepted) {
3172
3173 bool plural = (selection.size() > 1);
3174
3175 SegmentReconfigureCommand *command =
3176 new SegmentReconfigureCommand(plural ?
3177 tr("Set Segment Durations") :
3178 tr("Set Segment Duration"),
3179 &m_doc->getComposition());
3180
3181 for (SegmentSelection::iterator i = selection.begin();
3182 i != selection.end(); ++i) {
3183
3184 command->addSegment
3185 (*i, (*i)->getStartTime(),
3186 (*i)->getStartTime() + dialog.getTime(),
3187 (*i)->getTrack());
3188 }
3189
3190 m_view->slotAddCommandToHistory(command);
3191 }
3192 }
3193
3194 void
slotHarmonizeSelection()3195 RosegardenMainWindow::slotHarmonizeSelection()
3196 {
3197 if (!m_view->haveSelection())
3198 return ;
3199
3200 SegmentSelection selection = m_view->getSelection();
3201 //!!! This should be somewhere else too
3202
3203 CompositionTimeSliceAdapter adapter(&m_doc->getComposition(),
3204 &selection);
3205
3206 AnalysisHelper helper;
3207 Segment *segment = new Segment;
3208 helper.guessHarmonies(adapter, *segment);
3209
3210 //!!! do nothing with the results yet
3211 delete segment;
3212 }
3213
3214 void
slotTempoToSegmentLength()3215 RosegardenMainWindow::slotTempoToSegmentLength()
3216 {
3217 slotTempoToSegmentLength(this);
3218 }
3219
3220 void
slotTempoToSegmentLength(QWidget * parent)3221 RosegardenMainWindow::slotTempoToSegmentLength(QWidget* parent)
3222 {
3223 RG_DEBUG << "slotTempoToSegmentLength";
3224
3225 if (!m_view->haveSelection())
3226 return ;
3227
3228 SegmentSelection selection = m_view->getSelection();
3229
3230 // Only set for a single selection
3231 //
3232 if (selection.size() == 1 &&
3233 (*selection.begin())->getType() == Segment::Audio) {
3234 Composition &comp = m_doc->getComposition();
3235 Segment *seg = *selection.begin();
3236
3237 TimeSignature timeSig =
3238 comp.getTimeSignatureAt(seg->getStartTime());
3239
3240 // unused warning fix
3241 // timeT endTime = seg->getEndTime();
3242 // if (seg->getRawEndMarkerTime())
3243 // endTime = seg->getEndMarkerTime();
3244
3245 RealTime segDuration =
3246 seg->getAudioEndTime() - seg->getAudioStartTime();
3247
3248 int beats = 0;
3249
3250 // Get user to tell us how many beats or bars the segment contains
3251 BeatsBarsDialog dialog(parent);
3252 if (dialog.exec() == QDialog::Accepted) {
3253 beats = dialog.getQuantity(); // beats (or bars)
3254 if (dialog.getMode() == 1) // bars (multiply by time sig)
3255 beats *= timeSig.getBeatsPerBar();
3256 #ifdef DEBUG_TEMPO_FROM_AUDIO
3257
3258 RG_DEBUG << "slotTempoToSegmentLength - beats = " << beats
3259 << " mode = " << ((dialog.getMode() == 0) ? "bars" : "beats") << endl
3260 << " beats per bar = " << timeSig.getBeatsPerBar()
3261 << " user quantity = " << dialog.getQuantity()
3262 << " user mode = " << dialog.getMode() << endl;
3263 #endif
3264
3265 } else {
3266 RG_DEBUG << "slotTempoToSegmentLength - BeatsBarsDialog aborted";
3267 return ;
3268 }
3269
3270 double beatLengthUsec =
3271 double(segDuration.sec * 1000000 + segDuration.usec()) /
3272 double(beats);
3273
3274 // New tempo is a minute divided by time of beat
3275 // converted up (#1414252) to a sane value via getTempoFoQpm()
3276 //
3277 tempoT newTempo =
3278 comp.getTempoForQpm(60.0 * 1000000.0 / beatLengthUsec);
3279
3280 #ifdef DEBUG_TEMPO_FROM_AUDIO
3281
3282 RG_DEBUG << "slotTempoToSegmentLength info: "
3283 << " beatLengthUsec = " << beatLengthUsec << endl
3284 << " segDuration.usec = " << segDuration.usec() << endl
3285 << " newTempo = " << newTempo << endl;
3286 #endif
3287
3288 MacroCommand *macro = new MacroCommand(tr("Set Global Tempo"));
3289
3290 // Remove all tempo changes in reverse order so as the index numbers
3291 // don't becoming meaningless as the command gets unwound.
3292 //
3293 for (int i = 0; i < comp.getTempoChangeCount(); i++)
3294 macro->addCommand(new RemoveTempoChangeCommand(&comp,
3295 (comp.getTempoChangeCount() - 1 - i)));
3296
3297 // add tempo change at time zero
3298 //
3299 macro->addCommand(new AddTempoChangeCommand(&comp, 0, newTempo));
3300
3301 // execute
3302 CommandHistory::getInstance()->addCommand(macro);
3303 }
3304 }
3305
3306 void
slotToggleSegmentLabels()3307 RosegardenMainWindow::slotToggleSegmentLabels()
3308 {
3309 QAction* act = this->findAction("show_segment_labels");
3310
3311 if (act) {
3312 m_view->slotShowSegmentLabels(act->isChecked());
3313 }
3314 }
3315
3316 void
slotEdit()3317 RosegardenMainWindow::slotEdit()
3318 {
3319 m_view->slotEditSegment(nullptr);
3320 }
3321
3322 void
slotEditAsNotation()3323 RosegardenMainWindow::slotEditAsNotation()
3324 {
3325 m_view->slotEditSegmentNotation(nullptr);
3326 }
3327
3328 void
slotEditInMatrix()3329 RosegardenMainWindow::slotEditInMatrix()
3330 {
3331 m_view->slotEditSegmentMatrix(nullptr);
3332 }
3333
3334 void
slotEditInPercussionMatrix()3335 RosegardenMainWindow::slotEditInPercussionMatrix()
3336 {
3337 m_view->slotEditSegmentPercussionMatrix(nullptr);
3338 }
3339
3340 void
slotEditInEventList()3341 RosegardenMainWindow::slotEditInEventList()
3342 {
3343 m_view->slotEditSegmentEventList(nullptr);
3344 }
3345
3346 void
slotEditInPitchTracker()3347 RosegardenMainWindow::slotEditInPitchTracker()
3348 {
3349 m_view->slotEditSegmentPitchTracker(nullptr);
3350 }
3351
3352 void
slotEditTempos()3353 RosegardenMainWindow::slotEditTempos()
3354 {
3355 slotEditTempos(m_doc->getComposition().getPosition());
3356 }
3357
3358 void
slotToggleToolBar()3359 RosegardenMainWindow::slotToggleToolBar()
3360 {
3361 TmpStatusMsg msg(tr("Toggle the toolbar..."), this);
3362
3363 if (findAction("show_stock_toolbar")->isChecked())
3364 findToolbar("Main Toolbar")->show();
3365 else
3366 findToolbar("Main Toolbar")->hide();
3367 }
3368
3369 void
slotToggleToolsToolBar()3370 RosegardenMainWindow::slotToggleToolsToolBar()
3371 {
3372 TmpStatusMsg msg(tr("Toggle the tools toolbar..."), this);
3373
3374 if (findAction("show_tools_toolbar")->isChecked())
3375 findToolbar("Tools Toolbar")->show();
3376 else
3377 findToolbar("Tools Toolbar")->hide();
3378 }
3379
3380 void
slotToggleTracksToolBar()3381 RosegardenMainWindow::slotToggleTracksToolBar()
3382 {
3383 TmpStatusMsg msg(tr("Toggle the tracks toolbar..."), this);
3384
3385 if (findAction("show_tracks_toolbar")->isChecked())
3386 findToolbar("Tracks Toolbar")->show();
3387 else
3388 findToolbar("Tracks Toolbar")->hide();
3389 }
3390
3391 void
slotToggleEditorsToolBar()3392 RosegardenMainWindow::slotToggleEditorsToolBar()
3393 {
3394 TmpStatusMsg msg(tr("Toggle the editor toolbar..."), this);
3395
3396 if (findAction("show_editors_toolbar")->isChecked())
3397 findToolbar("Editors Toolbar")->show();
3398 else
3399 findToolbar("Editors Toolbar")->hide();
3400 }
3401
3402 void
slotToggleTransportToolBar()3403 RosegardenMainWindow::slotToggleTransportToolBar()
3404 {
3405 TmpStatusMsg msg(tr("Toggle the transport toolbar..."), this);
3406
3407 if (findAction("show_transport_toolbar")->isChecked())
3408 findToolbar("Transport Toolbar")->show();
3409 else
3410 findToolbar("Transport Toolbar")->hide();
3411 }
3412
3413 void
slotToggleZoomToolBar()3414 RosegardenMainWindow::slotToggleZoomToolBar()
3415 {
3416 TmpStatusMsg msg(tr("Toggle the zoom toolbar..."), this);
3417
3418 if (findAction("show_zoom_toolbar")->isChecked())
3419 findToolbar("Zoom Toolbar")->show();
3420 else
3421 findToolbar("Zoom Toolbar")->hide();
3422 }
3423
3424 void
slotUpdateTransportVisibility()3425 RosegardenMainWindow::slotUpdateTransportVisibility()
3426 {
3427 TmpStatusMsg msg(tr("Toggle the Transport"), this);
3428
3429 if (findAction("show_transport")->isChecked()) {
3430 getTransport()->show();
3431 getTransport()->raise();
3432 // Put the window where it belongs.
3433 // ??? We shouldn't need to do this, but...
3434 // If you hide the transport with the menu item or the "T"
3435 // shortcut, the window crawls up the screen. This doesn't
3436 // happen if you hide it with its close button. It appears
3437 // to be some sort of X or window manager problem.
3438 getTransport()->loadGeo();
3439 } else { // Hide
3440 // Save the window location for when we show it again.
3441 // ??? We shouldn't need to do this. See comments above.
3442 // ??? Also see TransportDialog's dtor which depends on this
3443 // saveGeo() call.
3444 getTransport()->saveGeo();
3445 getTransport()->hide();
3446 }
3447 }
3448
3449 void
slotToggleTransportVisibility()3450 RosegardenMainWindow::slotToggleTransportVisibility()
3451 {
3452 /**
3453 * We need this because selecting the menu items automatically toggles
3454 * the "show_transport" state, while pressing "T" key does not.
3455 */
3456 TmpStatusMsg msg(tr("Toggle the Transport"), this);
3457
3458 QAction *a = findAction("show_transport");
3459 if (a->isChecked()) {
3460 a->setChecked(false);
3461 } else {
3462 a->setChecked(true);
3463 }
3464 slotUpdateTransportVisibility();
3465 }
3466
3467 void
slotToggleTrackLabels()3468 RosegardenMainWindow::slotToggleTrackLabels()
3469 {
3470 if (findAction("show_tracklabels")->isChecked()) {
3471 #ifdef SETTING_LOG_DEBUG
3472 RG_DEBUG << "toggle track labels on";
3473 #endif
3474
3475 m_view->getTrackEditor()->getTrackButtons()->
3476 changeLabelDisplayMode(TrackLabel::ShowTrack);
3477 } else {
3478 #ifdef SETTING_LOG_DEBUG
3479 RG_DEBUG << "toggle track labels off";
3480 #endif
3481
3482 m_view->getTrackEditor()->getTrackButtons()->
3483 changeLabelDisplayMode(TrackLabel::ShowInstrument);
3484 }
3485 }
3486
3487 void
slotToggleRulers()3488 RosegardenMainWindow::slotToggleRulers()
3489 {
3490 m_view->slotShowRulers(findAction("show_rulers")->isChecked());
3491 }
3492
3493 void
slotToggleTempoRuler()3494 RosegardenMainWindow::slotToggleTempoRuler()
3495 {
3496 m_view->slotShowTempoRuler(findAction("show_tempo_ruler")->isChecked());
3497 }
3498
3499 void
slotToggleChordNameRuler()3500 RosegardenMainWindow::slotToggleChordNameRuler()
3501 {
3502 m_view->slotShowChordNameRuler(findAction("show_chord_name_ruler")->isChecked());
3503 }
3504
3505 void
slotTogglePreviews()3506 RosegardenMainWindow::slotTogglePreviews()
3507 {
3508 m_view->slotShowPreviews(findAction("show_previews")->isChecked());
3509 }
3510
3511 void
slotHideShowParameterArea()3512 RosegardenMainWindow::slotHideShowParameterArea()
3513 {
3514 m_parameterArea->setVisible(findAction("show_inst_segment_parameters")->isChecked());
3515 }
3516
3517 void
slotParameterAreaHidden()3518 RosegardenMainWindow::slotParameterAreaHidden()
3519 {
3520 RG_DEBUG << "slotParameterAreaHidden(): who called this? Is this the amnesia source?";
3521
3522 // Since the parameter area is now hidden, clear the checkbox in the
3523 // menu to keep things in sync.
3524 findAction("show_inst_segment_parameters")->setChecked(false);
3525 }
3526
3527 void
slotToggleStatusBar()3528 RosegardenMainWindow::slotToggleStatusBar()
3529 {
3530 TmpStatusMsg msg(tr("Toggle the statusbar..."), this);
3531
3532 if (!findAction("show_status_bar")->isChecked())
3533 statusBar()->hide();
3534 else
3535 statusBar()->show();
3536 }
3537
3538 void
slotStatusMsg(QString text)3539 RosegardenMainWindow::slotStatusMsg(QString text)
3540 {
3541 ///////////////////////////////////////////////////////////////////
3542 // change status message permanently
3543 statusBar()->clearMessage();
3544 // statusBar()->changeItem(text, EditViewBase::ID_STATUS_MSG);
3545 // statusBar()->showMessage(text, EditViewBase::ID_STATUS_MSG);
3546 statusBar()->showMessage(text, 0); // note: last param == timeout
3547 }
3548
3549 void
slotStatusHelpMsg(QString text)3550 RosegardenMainWindow::slotStatusHelpMsg(QString text)
3551 {
3552 ///////////////////////////////////////////////////////////////////
3553 // change status message of whole statusbar temporary (text, msec)
3554 statusBar()->showMessage(text, 2000);
3555 }
3556
3557 void
slotEnableTransport(bool enable)3558 RosegardenMainWindow::slotEnableTransport(bool enable)
3559 {
3560 // ??? What about the menu items and the toolbar buttons? Also
3561 // note that the TransportDialog buttons do not gray or
3562 // indicate that they've been disabled in any way.
3563
3564 if (m_transport)
3565 getTransport()->setEnabled(enable);
3566 }
3567
3568 void
slotPointerSelected()3569 RosegardenMainWindow::slotPointerSelected()
3570 {
3571 m_view->selectTool(SegmentSelector::ToolName());
3572 }
3573
3574 void
slotEraseSelected()3575 RosegardenMainWindow::slotEraseSelected()
3576 {
3577 m_view->selectTool(SegmentEraser::ToolName());
3578 }
3579
3580 void
slotDrawSelected()3581 RosegardenMainWindow::slotDrawSelected()
3582 {
3583 m_view->selectTool(SegmentPencil::ToolName());
3584 }
3585
3586 void
slotMoveSelected()3587 RosegardenMainWindow::slotMoveSelected()
3588 {
3589 m_view->selectTool(SegmentMover::ToolName());
3590 }
3591
3592 void
slotResizeSelected()3593 RosegardenMainWindow::slotResizeSelected()
3594 {
3595 m_view->selectTool(SegmentResizer::ToolName());
3596 }
3597
3598 void
slotJoinSelected()3599 RosegardenMainWindow::slotJoinSelected()
3600 {
3601 QMessageBox::information(this,
3602 tr("The join tool isn't implemented yet. Instead please highlight "
3603 "the segments you want to join and then use the menu option:\n\n"
3604 " Segments->Collapse Segments.\n"),
3605 tr("Join tool not yet implemented"));
3606
3607 m_view->selectTool(SegmentJoiner::ToolName());
3608 }
3609
3610 void
slotSplitSelected()3611 RosegardenMainWindow::slotSplitSelected()
3612 {
3613 m_view->selectTool(SegmentSplitter::ToolName());
3614 }
3615
3616 void
slotAddTrack()3617 RosegardenMainWindow::slotAddTrack()
3618 {
3619 if (!m_view)
3620 return ;
3621
3622 // default to the base number - might not actually exist though
3623 //
3624 InstrumentId id = MidiInstrumentBase;
3625
3626 // Get the first Internal/MIDI instrument
3627 //
3628 DeviceList *devices = m_doc->getStudio().getDevices();
3629 bool have = false;
3630
3631 for (DeviceList::iterator it = devices->begin();
3632 it != devices->end() && !have; ++it) {
3633
3634 if ((*it)->getType() != Device::Midi)
3635 continue;
3636
3637 InstrumentList instruments = (*it)->getAllInstruments();
3638 for (InstrumentList::iterator iit = instruments.begin();
3639 iit != instruments.end(); ++iit) {
3640
3641 if ((*iit)->getId() >= MidiInstrumentBase) {
3642 id = (*iit)->getId();
3643 have = true;
3644 break;
3645 }
3646 }
3647 }
3648
3649 Composition &comp = m_doc->getComposition();
3650 TrackId trackId = comp.getSelectedTrack();
3651 Track *track = comp.getTrackById(trackId);
3652
3653 int pos = -1;
3654 if (track) pos = track->getPosition() + 1;
3655
3656 m_view->slotAddTracks(1, id, pos);
3657 }
3658
3659 void
slotAddTracks()3660 RosegardenMainWindow::slotAddTracks()
3661 {
3662 if (!m_view)
3663 return ;
3664
3665 // default to the base number - might not actually exist though
3666 //
3667 InstrumentId id = MidiInstrumentBase;
3668
3669 // Get the first Internal/MIDI instrument
3670 //
3671 DeviceList *devices = m_doc->getStudio().getDevices();
3672 bool have = false;
3673
3674 for (DeviceList::iterator it = devices->begin();
3675 it != devices->end() && !have; ++it) {
3676
3677 if ((*it)->getType() != Device::Midi)
3678 continue;
3679
3680 InstrumentList instruments = (*it)->getAllInstruments();
3681 for (InstrumentList::iterator iit = instruments.begin();
3682 iit != instruments.end(); ++iit) {
3683
3684 if ((*iit)->getId() >= MidiInstrumentBase) {
3685 id = (*iit)->getId();
3686 have = true;
3687 break;
3688 }
3689 }
3690 }
3691
3692 Composition &comp = m_doc->getComposition();
3693 TrackId trackId = comp.getSelectedTrack();
3694 Track *track = comp.getTrackById(trackId);
3695
3696 int pos = 0;
3697 if (track) pos = track->getPosition();
3698
3699 AddTracksDialog dialog(this, pos);
3700
3701 if (dialog.exec() == QDialog::Accepted) {
3702 m_view->slotAddTracks(dialog.getTracks(), id,
3703 dialog.getInsertPosition());
3704 }
3705 }
3706
3707 void
slotDeleteTrack()3708 RosegardenMainWindow::slotDeleteTrack()
3709 {
3710 if (!m_view)
3711 return ;
3712
3713 Composition &comp = m_doc->getComposition();
3714 TrackId trackId = comp.getSelectedTrack();
3715 Track *track = comp.getTrackById(trackId);
3716
3717 if (track == nullptr)
3718 return ;
3719
3720 // Don't let the user delete the last track.
3721 if (comp.getNbTracks() == 1)
3722 return ;
3723
3724 int position = track->getPosition();
3725
3726 // Delete the track
3727 //
3728 std::vector<TrackId> tracks;
3729 tracks.push_back(trackId);
3730
3731 m_view->slotDeleteTracks(tracks);
3732
3733 // Select a new valid track
3734 //
3735 if (comp.getTrackByPosition(position))
3736 trackId = comp.getTrackByPosition(position)->getId();
3737 else if (comp.getTrackByPosition(position - 1))
3738 trackId = comp.getTrackByPosition(position - 1)->getId();
3739 else {
3740 RG_DEBUG << "slotDeleteTrack - "
3741 << "can't select a highlighted track after delete";
3742 }
3743
3744 comp.setSelectedTrack(trackId);
3745 comp.notifyTrackSelectionChanged(trackId);
3746 m_view->slotSelectTrackSegments(trackId);
3747 m_doc->emitDocumentModified();
3748
3749 // unused:
3750 // Instrument *inst = m_doc->getStudio().
3751 // getInstrumentById(comp.getTrackById(trackId)->getInstrument());
3752
3753 //VLADA
3754 // m_view->slotSelectTrackSegments(trackId);
3755 //VLADA
3756 }
3757
3758 void
slotMoveTrackDown()3759 RosegardenMainWindow::slotMoveTrackDown()
3760 {
3761 //RG_DEBUG << "slotMoveTrackDown";
3762
3763 Composition &comp = m_doc->getComposition();
3764 Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
3765
3766 // Check for track object
3767 //
3768 if (srcTrack == nullptr)
3769 return ;
3770
3771 // Check destination track exists
3772 //
3773 Track *destTrack =
3774 comp.getTrackByPosition(srcTrack->getPosition() + 1);
3775
3776 if (destTrack == nullptr)
3777 return ;
3778
3779 MoveTracksCommand *command =
3780 new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
3781
3782 CommandHistory::getInstance()->addCommand(command);
3783
3784 // make sure we're showing the right selection
3785 comp.notifyTrackSelectionChanged(comp.getSelectedTrack());
3786 // ??? This doesn't work right when the user uses the keyboard shortcut
3787 // "Shift + Down Arrow". Since "Shift" is being held down, that
3788 // modifies the behavior of this call. Keep pressing Shift + Down
3789 // and note that the segments alternate between selected and
3790 // unselected.
3791 if (m_view)
3792 m_view->slotSelectTrackSegments(comp.getSelectedTrack());
3793
3794 }
3795
3796 void
slotMoveTrackUp()3797 RosegardenMainWindow::slotMoveTrackUp()
3798 {
3799 //RG_DEBUG << "slotMoveTrackUp";
3800
3801 Composition &comp = m_doc->getComposition();
3802 Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
3803
3804 // Check for track object
3805 //
3806 if (srcTrack == nullptr)
3807 return ;
3808
3809 // Check we're not at the top already
3810 //
3811 if (srcTrack->getPosition() == 0)
3812 return ;
3813
3814 // Check destination track exists
3815 //
3816 Track *destTrack =
3817 comp.getTrackByPosition(srcTrack->getPosition() - 1);
3818
3819 if (destTrack == nullptr)
3820 return ;
3821
3822 MoveTracksCommand *command =
3823 new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
3824
3825 CommandHistory::getInstance()->addCommand(command);
3826
3827 // make sure we're showing the right selection
3828 comp.notifyTrackSelectionChanged(comp.getSelectedTrack());
3829 // ??? This doesn't work right when the user uses the keyboard shortcut
3830 // "Shift + Up Arrow". Since "Shift" is being held down, that
3831 // modifies the behavior of this call. Keep pressing Shift + Up
3832 // and note that the segments alternate between selected and
3833 // unselected.
3834 if (m_view)
3835 m_view->slotSelectTrackSegments(comp.getSelectedTrack());
3836 }
3837
3838 void
slotRevertToSaved()3839 RosegardenMainWindow::slotRevertToSaved()
3840 {
3841 RG_DEBUG << "slotRevertToSaved";
3842
3843 if (m_doc->isModified()) {
3844 int revert =
3845 QMessageBox::question(this, tr("Rosegarden"),
3846 tr("Revert modified document to previous saved version?"));
3847
3848 if (revert == QMessageBox::No)
3849 return ;
3850
3851 openFile(m_doc->getAbsFilePath());
3852 }
3853 }
3854
3855 void
slotImportProject()3856 RosegardenMainWindow::slotImportProject()
3857 {
3858 if (m_doc && !saveIfModified())
3859 return ;
3860
3861 QSettings settings;
3862 settings.beginGroup(LastUsedPathsConfigGroup);
3863 QString directory = settings.value("import_project", QDir::homePath()).toString();
3864
3865 const QString file = FileDialog::getOpenFileName(this, tr("Import Rosegarden Project File"), directory,
3866 tr("Rosegarden Project files") + " (*.rgp *.RGP)" + ";;" +
3867 tr("All files") + " (*)", nullptr);
3868
3869 if (file.isEmpty()) {
3870 return ;
3871 }
3872
3873 QDir d = QFileInfo(file).dir();
3874 directory = d.canonicalPath();
3875 settings.setValue("import_project", directory);
3876 settings.endGroup();
3877
3878 importProject(file);
3879 }
3880
3881 void
importProject(QString filePath)3882 RosegardenMainWindow::importProject(QString filePath)
3883 {
3884 // Launch the project packager script-in-a-dialog in Unpack mode:
3885 ProjectPackager *dialog = new ProjectPackager(this, m_doc, ProjectPackager::Unpack, filePath);
3886 if (dialog->exec() != QDialog::Accepted) {
3887 return;
3888 }
3889
3890 // open the true filename contained within and extracted from the package (foo.rgp might have
3891 // contained bar.rg)
3892 openURL(dialog->getTrueFilename());
3893 }
3894
3895 void
slotImportMIDI()3896 RosegardenMainWindow::slotImportMIDI()
3897 {
3898 if (m_doc && !saveIfModified())
3899 return ;
3900
3901 QSettings settings;
3902 settings.beginGroup(LastUsedPathsConfigGroup);
3903 QString directory = settings.value("import_midi", QDir::homePath()).toString();
3904
3905 const QString file = FileDialog::getOpenFileName(this, tr("Open MIDI File"), directory,
3906 tr("MIDI files") + " (*.mid *.midi *.MID *.MIDI)" + ";;" +
3907 tr("All files") + " (*)", nullptr);
3908
3909 if (file.isEmpty()) {
3910 return ;
3911 }
3912
3913 QDir d = QFileInfo(file).dir();
3914 directory = d.canonicalPath();
3915 settings.setValue("import_midi", directory);
3916 settings.endGroup();
3917
3918 openFile(file, ImportMIDI); // does everything including setting the document
3919 }
3920
3921 void
slotMergeMIDI()3922 RosegardenMainWindow::slotMergeMIDI()
3923 {
3924 QSettings settings;
3925 settings.beginGroup(LastUsedPathsConfigGroup);
3926 QString directory = settings.value("merge_midi", QDir::homePath()).toString();
3927
3928 const QString file = FileDialog::getOpenFileName(this, tr("Merge MIDI File"), directory,
3929 tr("MIDI files") + " (*.mid *.midi *.MID *.MIDI)" + ";;" +
3930 tr("All files") + " (*)", nullptr);
3931
3932 if (file.isEmpty()) {
3933 return ;
3934 }
3935
3936 QDir d = QFileInfo(file).dir();
3937 directory = d.canonicalPath();
3938 settings.setValue("merge_midi", directory);
3939 settings.endGroup();
3940
3941 mergeFile(file, ImportMIDI);
3942 }
3943
3944 QTextCodec *
guessTextCodec(std::string text)3945 RosegardenMainWindow::guessTextCodec(std::string text)
3946 {
3947 QTextCodec *codec = nullptr;
3948
3949 for (int c = 0; c < int(text.length()); ++c) {
3950 if (text[c] & 0x80) {
3951 StartupLogo::hideIfStillThere();
3952
3953 IdentifyTextCodecDialog dialog(nullptr, text);
3954 dialog.exec();
3955
3956 QString codecName = dialog.getCodec();
3957
3958 if (codecName != "") {
3959 codec = QTextCodec::codecForName(codecName.toLatin1());
3960 }
3961 break;
3962 }
3963 }
3964
3965 return codec;
3966 }
3967
3968 void
fixTextEncodings(Composition * c)3969 RosegardenMainWindow::fixTextEncodings(Composition *c)
3970
3971 {
3972 QTextCodec *codec = nullptr;
3973
3974 for (Composition::iterator i = c->begin();
3975 i != c->end(); ++i) {
3976
3977 for (Segment::iterator j = (*i)->begin();
3978 j != (*i)->end(); ++j) {
3979
3980 if ((*j)->isa(Text::EventType)) {
3981
3982 std::string text;
3983
3984 if ((*j)->get
3985 <String>
3986 (Text::TextPropertyName, text)) {
3987
3988 if (!codec)
3989 codec = guessTextCodec(text);
3990
3991 if (codec) {
3992 (*j)->set
3993 <String>
3994 (Text::TextPropertyName,
3995 convertFromCodec(text, codec));
3996 }
3997 }
3998 }
3999 }
4000 }
4001
4002 if (!codec)
4003 codec = guessTextCodec(c->getCopyrightNote());
4004 if (codec)
4005 c->setCopyrightNote(convertFromCodec(c->getCopyrightNote(), codec));
4006
4007 for (Composition::trackcontainer::iterator i =
4008 c->getTracks().begin(); i != c->getTracks().end(); ++i) {
4009 if (!codec)
4010 codec = guessTextCodec(i->second->getLabel());
4011 if (codec)
4012 i->second->setLabel(convertFromCodec(i->second->getLabel(), codec));
4013 }
4014
4015 for (Composition::iterator i = c->begin(); i != c->end(); ++i) {
4016 if (!codec)
4017 codec = guessTextCodec((*i)->getLabel());
4018 if (codec)
4019 (*i)->setLabel(convertFromCodec((*i)->getLabel(), codec));
4020 }
4021 }
4022
4023 RosegardenDocument*
createDocumentFromMIDIFile(QString file)4024 RosegardenMainWindow::createDocumentFromMIDIFile(QString file)
4025 {
4026 //if (!merge && !saveIfModified()) return;
4027
4028 // Create new document (autoload is inherent)
4029 //
4030 RosegardenDocument *newDoc = newDocument();
4031
4032 MidiFile midiFile;
4033
4034 StartupLogo::hideIfStillThere();
4035
4036 // Progress Dialog
4037 // Note: The label text and range will be set later as needed.
4038 QProgressDialog progressDialog(
4039 tr("Importing MIDI file..."), // labelText
4040 tr("Cancel"), // cancelButtonText
4041 0, 100, // min, max
4042 this); // parent
4043 progressDialog.setWindowTitle(tr("Rosegarden"));
4044 progressDialog.setWindowModality(Qt::WindowModal);
4045 // Don't want to auto close since this is a multi-step
4046 // process. Any of the steps may set progress to 100. We
4047 // will close anyway when this object goes out of scope.
4048 progressDialog.setAutoClose(false);
4049 // Just force the progress dialog up.
4050 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
4051 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
4052 // based distros might lock up. See Bug #1546.
4053 progressDialog.show();
4054
4055 midiFile.setProgressDialog(&progressDialog);
4056
4057 if (!midiFile.convertToRosegarden(file, newDoc)) {
4058 // NOTE: Someone flagged midiFile.getError() with a warning about tr().
4059 // This stuff either gets translated at the source, if we own it, or it
4060 // doesn't get translated at all, if we don't (eg. errors from the
4061 // underlying filesystem, a library, etc.)
4062 QMessageBox::critical(this, tr("Rosegarden"), strtoqstr(midiFile.getError()));
4063 delete newDoc;
4064 return nullptr;
4065 }
4066
4067 fixTextEncodings(&newDoc->getComposition());
4068
4069 // Set modification flag
4070 //
4071 newDoc->slotDocumentModified();
4072
4073 // Set the caption
4074 //
4075 newDoc->setTitle(QFileInfo(file).fileName());
4076 newDoc->setAbsFilePath(QFileInfo(file).absoluteFilePath());
4077
4078 // Clean up for notation purposes (after reinitialise, because that
4079 // sets the composition's end marker time which is needed here)
4080
4081 progressDialog.setLabelText(tr("Calculating notation..."));
4082 progressDialog.setValue(0);
4083 qApp->processEvents();
4084
4085 Composition *comp = &newDoc->getComposition();
4086
4087 for (Composition::iterator i = comp->begin();
4088 i != comp->end(); ++i) {
4089
4090 Segment &segment = **i;
4091 SegmentNotationHelper helper(segment);
4092 segment.insert(helper.guessClef(segment.begin(),
4093 segment.getEndMarker())
4094 .getAsEvent(segment.getStartTime()));
4095 }
4096
4097 progressDialog.setValue(10);
4098
4099 for (Composition::iterator i = comp->begin();
4100 i != comp->end(); ++i) {
4101
4102 // find first key event in each segment (we'd have done the
4103 // same for clefs, except there is no MIDI clef event)
4104
4105 Segment &segment = **i;
4106 timeT firstKeyTime = segment.getEndMarkerTime();
4107
4108 for (Segment::iterator si = segment.begin();
4109 segment.isBeforeEndMarker(si); ++si) {
4110 if ((*si)->isa(Rosegarden::Key::EventType)) {
4111 firstKeyTime = (*si)->getAbsoluteTime();
4112 break;
4113 }
4114 }
4115
4116 if (firstKeyTime > segment.getStartTime()) {
4117 CompositionTimeSliceAdapter adapter
4118 (comp, timeT(0), firstKeyTime);
4119 AnalysisHelper helper;
4120 segment.insert(helper.guessKey(adapter).getAsEvent
4121 (segment.getStartTime()));
4122 }
4123 }
4124
4125 progressDialog.setValue(20);
4126
4127 int segmentCount = 1;
4128 const int nbSegments = comp->getNbSegments();
4129
4130 double progressPerSegment = 80;
4131 if (nbSegments > 0)
4132 progressPerSegment = 80.0 / nbSegments;
4133
4134 MacroCommand *command = new MacroCommand(tr("Calculate Notation"));
4135
4136 // For each segment in the composition.
4137 for (Composition::iterator i = comp->begin(); i != comp->end(); ++i) {
4138 Segment &segment = **i;
4139
4140 timeT startTime(segment.getStartTime());
4141 timeT endTime(segment.getEndMarkerTime());
4142
4143 //RG_DEBUG << "segment: start time " << segment.getStartTime() << ", end time " << segment.getEndTime() << ", end marker time " << segment.getEndMarkerTime() << ", events " << segment.size();
4144
4145 EventQuantizeCommand *subCommand = new EventQuantizeCommand
4146 (segment, startTime, endTime, NotationOptionsConfigGroup,
4147 EventQuantizeCommand::QUANTIZE_NOTATION_ONLY);
4148 subCommand->setProgressDialog(&progressDialog);
4149
4150 // Compute progress so far.
4151 double totalProgress = 20 + segmentCount * progressPerSegment;
4152 ++segmentCount;
4153
4154 //RG_DEBUG << "createDocumentFromMIDIFile() totalProgress: " << totalProgress;
4155
4156 subCommand->setProgressTotal(totalProgress, progressPerSegment + 1);
4157
4158 command->addCommand(subCommand);
4159 }
4160
4161 CommandHistory::getInstance()->addCommand(command);
4162
4163 if (comp->getTimeSignatureCount() == 0) {
4164 CompositionTimeSliceAdapter adapter(comp);
4165 AnalysisHelper analysisHelper;
4166 TimeSignature timeSig =
4167 analysisHelper.guessTimeSignature(adapter);
4168 comp->addTimeSignature(0, timeSig);
4169 }
4170
4171 return newDoc;
4172 }
4173
4174 void
slotImportRG21()4175 RosegardenMainWindow::slotImportRG21()
4176 {
4177 if (m_doc && !saveIfModified())
4178 return ;
4179
4180 QSettings settings;
4181 settings.beginGroup(LastUsedPathsConfigGroup);
4182 QString directory = settings.value("import_relic", QDir::homePath()).toString();
4183
4184 const QString file = FileDialog::getOpenFileName(this, tr("Open X11 Rosegarden File"), directory,
4185 tr("X11 Rosegarden files") + " (*.rose)" + ";;" +
4186 tr("All files") + " (*)", nullptr);
4187
4188 if (file.isEmpty()) {
4189 return ;
4190 }
4191
4192 QDir d = QFileInfo(file).dir();
4193 directory = d.canonicalPath();
4194 settings.setValue("import_relic", directory);
4195 settings.endGroup();
4196
4197 openFile(file, ImportRG21);
4198 }
4199
4200 void
slotMergeRG21()4201 RosegardenMainWindow::slotMergeRG21()
4202 {
4203 QSettings settings;
4204 settings.beginGroup(LastUsedPathsConfigGroup);
4205 QString directory = settings.value("merge_relic", QDir::homePath()).toString();
4206
4207 const QString file = FileDialog::getOpenFileName(this, tr("Open X11 Rosegarden File"), directory,
4208 tr("X11 Rosegarden files") + " (*.rose)" + ";;" +
4209 tr("All files") + " (*)", nullptr);
4210
4211 if (file.isEmpty()) {
4212 return ;
4213 }
4214
4215 QDir d = QFileInfo(file).dir();
4216 directory = d.canonicalPath();
4217 settings.setValue("import_relic", directory);
4218 settings.endGroup();
4219
4220 mergeFile(file, ImportRG21);
4221 }
4222
4223 RosegardenDocument*
createDocumentFromRG21File(QString file)4224 RosegardenMainWindow::createDocumentFromRG21File(QString file)
4225 {
4226 StartupLogo::hideIfStillThere();
4227
4228 // Progress Dialog
4229 QProgressDialog progressDialog(
4230 tr("Importing X11 Rosegarden file..."), // labelText
4231 tr("Cancel"), // cancelButtonText
4232 0, 0, // min, max
4233 this); // parent
4234 progressDialog.setWindowTitle(tr("Rosegarden"));
4235 progressDialog.setWindowModality(Qt::WindowModal);
4236 // We will close anyway when this object goes out of scope.
4237 progressDialog.setAutoClose(false);
4238 // Remove the cancel button since RG21Loader doesn't support cancel.
4239 progressDialog.setCancelButton(nullptr);
4240 // Just force the progress dialog up.
4241 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
4242 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
4243 // based distros might lock up. See Bug #1546.
4244 progressDialog.show();
4245
4246 // Inherent autoload
4247 //
4248 RosegardenDocument *newDoc = newDocument();
4249
4250 RG21Loader rg21Loader(&newDoc->getStudio());
4251
4252 if (!rg21Loader.load(file, newDoc->getComposition())) {
4253 QMessageBox::critical(this, tr("Rosegarden"),
4254 tr("Can't load X11 Rosegarden file. It appears to be corrupted."));
4255 delete newDoc;
4256 return nullptr;
4257 }
4258
4259 // Set modification flag
4260 //
4261 newDoc->slotDocumentModified();
4262
4263 // Set the caption and add recent
4264 //
4265 newDoc->setTitle(QFileInfo(file).fileName());
4266 newDoc->setAbsFilePath(QFileInfo(file).absoluteFilePath());
4267
4268 return newDoc;
4269 }
4270
4271 #if 0
4272 void
4273 RosegardenMainWindow::slotImportHydrogen()
4274 {
4275 if (m_doc && !saveIfModified())
4276 return ;
4277
4278 QSettings settings;
4279 settings.beginGroup(LastUsedPathsConfigGroup);
4280 QString directory = settings.value("import_hydrogen", QDir::homePath()).toString();
4281
4282 const QString file = FileDialog::getOpenFileName(this, tr("Open Hydrogen File"), directory,
4283 tr("All files") + " (*)", 0, 0);
4284
4285 if (file.isEmpty()) {
4286 return ;
4287 }
4288
4289 QDir d = QFileInfo(file).dir();
4290 directory = d.canonicalPath();
4291 settings.setValue("import_hydrogen", directory);
4292 settings.endGroup();
4293
4294 openFile(file, ImportHydrogen);
4295 }
4296
4297 void
4298 RosegardenMainWindow::slotMergeHydrogen()
4299 {
4300 QSettings settings;
4301 settings.beginGroup(LastUsedPathsConfigGroup);
4302 QString directory = settings.value("merge_hydrogen", QDir::homePath()).toString();
4303
4304 const QString file = FileDialog::getOpenFileName(this, tr("Open Hydrogen File"), directory,
4305 tr("All files") + " (*)", 0, 0);
4306
4307 if (file.isEmpty()) {
4308 return ;
4309 }
4310
4311 QDir d = QFileInfo(file).dir();
4312 directory = d.canonicalPath();
4313 settings.setValue("merge_hydrogen", directory);
4314 settings.endGroup();
4315
4316 mergeFile(file, ImportHydrogen);
4317 }
4318
4319
4320 RosegardenDocument*
4321 RosegardenMainWindow::createDocumentFromHydrogenFile(QString file)
4322 {
4323 StartupLogo::hideIfStillThere();
4324
4325 // Progress Dialog
4326 QProgressDialog progressDialog(
4327 tr("Importing Hydrogen file..."), // labelText
4328 tr("Cancel"), // cancelButtonText
4329 0, 0, // min, max
4330 this); // parent
4331 progressDialog.setWindowTitle(tr("Rosegarden"));
4332 progressDialog.setWindowModality(Qt::WindowModal);
4333 // We will close anyway when this object goes out of scope.
4334 progressDialog.setAutoClose(false);
4335 // Remove the cancel button since HydrogenLoader doesn't support cancel.
4336 progressDialog.setCancelButton(nullptr);
4337 // Just force the progress dialog up.
4338 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
4339 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
4340 // based distros might lock up. See Bug #1546.
4341 progressDialog.show();
4342
4343 // Inherent autoload
4344 //
4345 RosegardenDocument *newDoc = newDocument();
4346
4347 HydrogenLoader hydrogenLoader(&newDoc->getStudio());
4348
4349 if (!hydrogenLoader.load(file, newDoc->getComposition())) {
4350 QMessageBox::critical(this, tr("Rosegarden"),
4351 tr("Can't load Hydrogen file. It appears to be corrupted."));
4352 delete newDoc;
4353 return 0;
4354 }
4355
4356 // Set modification flag
4357 //
4358 newDoc->slotDocumentModified();
4359
4360 // Set the caption and add recent
4361 //
4362 newDoc->setTitle(QFileInfo(file).fileName());
4363 newDoc->setAbsFilePath(QFileInfo(file).absoluteFilePath());
4364
4365 return newDoc;
4366 }
4367 #endif
4368
4369 void
slotImportMusicXML()4370 RosegardenMainWindow::slotImportMusicXML()
4371 {
4372 if (m_doc && !saveIfModified())
4373 return ;
4374
4375 QSettings settings;
4376 settings.beginGroup(LastUsedPathsConfigGroup);
4377 QString directory = settings.value("import_musicxml", QDir::homePath()).toString();
4378
4379 const QString file = FileDialog::getOpenFileName(this, tr("Open MusicXML File"), directory,
4380 tr("XML files") + " (*.xml *.XML)" + ";;" +
4381 tr("All files") + " (*)", nullptr);
4382
4383 if (file.isEmpty()) {
4384 return ;
4385 }
4386
4387 QDir d = QFileInfo(file).dir();
4388 directory = d.canonicalPath();
4389 settings.setValue("import_musicxml", directory);
4390 settings.endGroup();
4391
4392 openFile(file, ImportMusicXML);
4393 }
4394
4395 void
slotMergeMusicXML()4396 RosegardenMainWindow::slotMergeMusicXML()
4397 {
4398 QSettings settings;
4399 settings.beginGroup(LastUsedPathsConfigGroup);
4400 QString directory = settings.value("merge_musicxml", QDir::homePath()).toString();
4401
4402 const QString file = FileDialog::getOpenFileName(this, tr("Open MusicXML File"), directory,
4403 tr("XML files") + " (*.xml *.XML)" + ";;" +
4404 tr("All files") + " (*)", nullptr);
4405
4406 if (file.isEmpty()) {
4407 return ;
4408 }
4409
4410 QDir d = QFileInfo(file).dir();
4411 directory = d.canonicalPath();
4412 settings.setValue("merge_musicxml", directory);
4413 settings.endGroup();
4414
4415 mergeFile(file, ImportMusicXML);
4416 }
4417
4418 RosegardenDocument*
createDocumentFromMusicXMLFile(QString file)4419 RosegardenMainWindow::createDocumentFromMusicXMLFile(QString file)
4420 {
4421 StartupLogo::hideIfStillThere();
4422
4423 // Progress Dialog
4424 QProgressDialog progressDialog(
4425 tr("Importing MusicXML file..."), // labelText
4426 tr("Cancel"), // cancelButtonText
4427 0, 0, // min, max
4428 this); // parent
4429 progressDialog.setWindowTitle(tr("Rosegarden"));
4430 progressDialog.setWindowModality(Qt::WindowModal);
4431 // We will close anyway when this object goes out of scope.
4432 progressDialog.setAutoClose(false);
4433 // Remove the cancel button since MusicXMLLoader doesn't support cancel.
4434 progressDialog.setCancelButton(nullptr);
4435 // Just force the progress dialog up.
4436 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
4437 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
4438 // based distros might lock up. See Bug #1546.
4439 progressDialog.show();
4440
4441 // Inherent autoload
4442 //
4443 RosegardenDocument *newDoc = newDocument();
4444
4445 MusicXMLLoader musicxmlLoader(&newDoc->getStudio());
4446
4447 if (!musicxmlLoader.load(file, newDoc->getComposition(), newDoc->getStudio())) {
4448 QMessageBox::critical(this, tr("Rosegarden"),
4449 tr("Can't load MusicXML file:\n")+
4450 musicxmlLoader.errorMessage());
4451 delete newDoc;
4452 return nullptr;
4453 }
4454
4455 // Set modification flag
4456 //
4457 newDoc->slotDocumentModified();
4458
4459 // Set the caption and add recent
4460 //
4461 newDoc->setTitle(QFileInfo(file).fileName());
4462 newDoc->setAbsFilePath(QFileInfo(file).absoluteFilePath());
4463
4464 return newDoc;
4465
4466 }
4467
4468 void
mergeFile(QString filePath,ImportType type)4469 RosegardenMainWindow::mergeFile(QString filePath, ImportType type)
4470 {
4471 RosegardenDocument *doc = createDocument(filePath, type);
4472
4473 if (doc) {
4474 if (m_doc) {
4475
4476 bool timingsDiffer = false;
4477 Composition &c1 = m_doc->getComposition();
4478 Composition &c2 = doc->getComposition();
4479
4480 // compare tempos and time sigs in the two -- rather laborious
4481
4482 if (c1.getTimeSignatureCount() != c2.getTimeSignatureCount()) {
4483 timingsDiffer = true;
4484 } else {
4485 for (int i = 0; i < c1.getTimeSignatureCount(); ++i) {
4486 std::pair<timeT, TimeSignature> t1 =
4487 c1.getTimeSignatureChange(i);
4488 std::pair<timeT, TimeSignature> t2 =
4489 c2.getTimeSignatureChange(i);
4490 if (t1.first != t2.first || t1.second != t2.second) {
4491 timingsDiffer = true;
4492 break;
4493 }
4494 }
4495 }
4496
4497 if (c1.getTempoChangeCount() != c2.getTempoChangeCount()) {
4498 timingsDiffer = true;
4499 } else {
4500 for (int i = 0; i < c1.getTempoChangeCount(); ++i) {
4501 std::pair<timeT, tempoT> t1 = c1.getTempoChange(i);
4502 std::pair<timeT, tempoT> t2 = c2.getTempoChange(i);
4503 if (t1.first != t2.first || t1.second != t2.second) {
4504 timingsDiffer = true;
4505 break;
4506 }
4507 }
4508 }
4509
4510 FileMergeDialog dialog(this, filePath, timingsDiffer);
4511 if (dialog.exec() == QDialog::Accepted) {
4512 m_doc->mergeDocument(doc, dialog.getMergeOptions());
4513 }
4514
4515 delete doc;
4516
4517 } else { // ??? I don't think it's possible for m_doc to be nullptr.
4518 setDocument(doc);
4519 }
4520 }
4521 }
4522
processRecordedEvents()4523 void RosegardenMainWindow::processRecordedEvents()
4524 {
4525 if (!m_seqManager)
4526 return;
4527 // Only if we're recording
4528 if (m_seqManager->getTransportStatus() != RECORDING)
4529 return;
4530 if (!m_doc)
4531 return;
4532
4533 // Gather the recorded events and put them where they belong in the
4534 // document.
4535
4536 MappedEventList mC;
4537 if (SequencerDataBlock::getInstance()->getRecordedEvents(mC) > 0) {
4538 m_seqManager->processAsynchronousMidi(mC, nullptr);
4539 m_doc->insertRecordedMidi(mC);
4540 }
4541
4542 m_doc->updateRecordingMIDISegment();
4543 m_doc->updateRecordingAudioSegments();
4544 }
4545
4546 void
slotHandleInputs()4547 RosegardenMainWindow::slotHandleInputs()
4548 {
4549 // ??? This routine does more than handle inputs. How much of this
4550 // is critical? This routine is constantly called, so some
4551 // parts of the system might depend on all of this.
4552
4553 // Update the document from incoming data.
4554 processRecordedEvents();
4555
4556 // Handle transport requests
4557
4558 // ??? These could be implemented more directly as QEvents sent to
4559 // RosegardenMainWindow's event queue. See customEvent().
4560 // We would need to analyze each caller of
4561 // RosegardenSequencer::transportChange() and
4562 // RosegardenSequencer::transportJump() and have them call
4563 // postEvent() instead. See AlsaDriver::handleTransportCCs()
4564 // which uses both approaches right now. Transitioning AlsaDriver
4565 // to QEvent should be relatively smooth. JackDriver requires
4566 // the ability to wait on completion, so it might require
4567 // additional work to transition to QEvent.
4568
4569 RosegardenSequencer::TransportRequest req;
4570 RealTime rt;
4571 bool have = RosegardenSequencer::getInstance()->
4572 getNextTransportRequest(req, rt);
4573
4574 if (have) {
4575 switch (req) {
4576 case RosegardenSequencer::TransportNoChange:
4577 break;
4578 case RosegardenSequencer::TransportStop:
4579 slotStop();
4580 break;
4581 case RosegardenSequencer::TransportStart:
4582 slotPlay();
4583 break;
4584 case RosegardenSequencer::TransportPlay:
4585 slotPlay();
4586 break;
4587 case RosegardenSequencer::TransportRecord:
4588 slotRecord();
4589 break;
4590 case RosegardenSequencer::TransportJumpToTime:
4591 slotJumpToTime(rt);
4592 break;
4593 case RosegardenSequencer::TransportStartAtTime:
4594 slotStartAtTime(rt);
4595 break;
4596 case RosegardenSequencer::TransportStopAtTime:
4597 slotStop();
4598 slotJumpToTime(rt);
4599 break;
4600 }
4601 }
4602
4603 TransportStatus status = RosegardenSequencer::getInstance()->
4604 getStatus();
4605
4606 // ??? Wouldn't it make more sense to do this when playback or
4607 // recording is started/stopped? E.g. in slotStop(), slotPlay(), and
4608 // slotRecord(). That would avoid constantly polling this.
4609 if (status == PLAYING || status == RECORDING) { //@@@ JAS orig ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse
4610 if (m_notPlaying)
4611 leaveActionState("not_playing");
4612 } else {
4613 if (!m_notPlaying)
4614 enterActionState("not_playing");
4615 }
4616
4617 if (m_seqManager) {
4618
4619 // ??? Why are we constantly setting this? Why are we a conduit
4620 // from RosegardenSequencer to SequenceManager? Isn't there
4621 // a more direct route?
4622 m_seqManager->setTransportStatus(status);
4623
4624 // ??? Again, why are we a conduit from RosegardenSequencer to
4625 // SequenceManager? This also incurs a lock. Would it be
4626 // possible to keep this in the sequencer thread and avoid
4627 // the lock?
4628
4629 MappedEventList asynchronousQueue =
4630 RosegardenSequencer::getInstance()->pullAsynchronousMidiQueue();
4631
4632 if (!asynchronousQueue.empty())
4633 m_seqManager->processAsynchronousMidi(asynchronousQueue, nullptr);
4634 }
4635 }
4636
4637 void
slotUpdateUI()4638 RosegardenMainWindow::slotUpdateUI()
4639 {
4640 TransportStatus status = RosegardenSequencer::getInstance()->getStatus();
4641
4642 // If we're stopped
4643 if (status != PLAYING && status != RECORDING) {
4644 // Keep the meters going for monitoring
4645 slotUpdateMonitoring();
4646 return;
4647 }
4648
4649 // Either sequencer mapper or the sequence manager could be missing at
4650 // this point.
4651 //
4652 if (!m_seqManager) return;
4653 if (!m_doc) return;
4654
4655 // Update display of the current MIDI out event on the transport
4656
4657 MappedEvent ev;
4658 bool haveEvent = SequencerDataBlock::getInstance()->getVisual(ev);
4659 if (haveEvent) getTransport()->slotMidiOutLabel(&ev);
4660
4661
4662 // Update the playback position pointer
4663
4664 RealTime position = SequencerDataBlock::getInstance()->getPositionPointer();
4665 Composition &comp = m_doc->getComposition();
4666 timeT elapsedTime = comp.getElapsedTimeForRealTime(position);
4667
4668 // We don't want slotSetPointerPosition() to affect the sequencer.
4669 // Setting m_originatingJump to true causes slotSetPointerPosition()
4670 // to not tell the sequencer to jump to this new position. This
4671 // might be renamed m_seqJump and reverse its value.
4672 // ??? This should just be an argument to slotSetPointerPosition().
4673 // slotSetPointerPosition(elapsedTime, bool jumpSequencer = true);
4674 // (Can we have default args in a slot? Seems unlikely.)
4675 m_originatingJump = true;
4676 // Move the pointer to the current position.
4677 m_doc->slotSetPointerPosition(elapsedTime);
4678 // Future moves (jumps) won't be coming from here.
4679 m_originatingJump = false;
4680
4681
4682 // Update the VU meters
4683 if (m_midiMixer && m_midiMixer->isVisible()) m_midiMixer->updateMeters();
4684 if (m_view) m_view->updateMeters();
4685 }
4686
4687 void
slotUpdateCPUMeter()4688 RosegardenMainWindow::slotUpdateCPUMeter()
4689 {
4690 static std::ifstream *statstream = nullptr;
4691 // Set to true when CPU % has been displayed.
4692 static bool modified = false;
4693 static unsigned long lastBusy = 0, lastIdle = 0;
4694
4695 TransportStatus status = RosegardenSequencer::getInstance()->getStatus();
4696
4697 // If we're playing, display the CPU %
4698 if (status == PLAYING || status == RECORDING) {
4699
4700 if (!statstream) {
4701 statstream = new std::ifstream("/proc/stat", std::ios::in);
4702 }
4703
4704 if (!statstream || !*statstream)
4705 return ;
4706 statstream->seekg(0, std::ios::beg);
4707
4708 std::string cpu;
4709 unsigned long user, nice, sys, idle;
4710 *statstream >> cpu;
4711 *statstream >> user;
4712 *statstream >> nice;
4713 *statstream >> sys;
4714 *statstream >> idle;
4715
4716 unsigned long busy = user + nice + sys;
4717 unsigned long count = 0;
4718
4719 if (lastBusy > 0) {
4720 unsigned long bd = busy - lastBusy;
4721 unsigned long id = idle - lastIdle;
4722 if (bd + id > 0)
4723 count = bd * 100 / (bd + id);
4724 if (count > 100)
4725 count = 100;
4726 }
4727
4728 lastBusy = busy;
4729 lastIdle = idle;
4730
4731 // correct use of m_cpuBar; it's the CPU meter, and from now on,
4732 // nothing else (use QProgressDialog for reporting any kind of progress)
4733 if (m_cpuBar) {
4734 if (!modified) {
4735 m_cpuBar->setTextVisible(true);
4736 m_cpuBar->setFormat("CPU %p%");
4737 }
4738 m_cpuBar->setValue(count);
4739 }
4740
4741 modified = true;
4742
4743 } else if (modified) { // If CPU has been displayed, clear it.
4744 if (m_cpuBar) {
4745 m_cpuBar->setTextVisible(false);
4746 m_cpuBar->setFormat("%p%");
4747 m_cpuBar->setValue(0);
4748 }
4749 // Indicate CPU % display is now clear.
4750 modified = false;
4751 }
4752 }
4753
4754 void
slotUpdateMonitoring()4755 RosegardenMainWindow::slotUpdateMonitoring()
4756 {
4757 if (m_midiMixer && m_midiMixer->isVisible())
4758 m_midiMixer->updateMonitorMeter();
4759
4760 m_view->updateMonitorMeters();
4761 }
4762
4763 void
slotSetPointerPosition(timeT t)4764 RosegardenMainWindow::slotSetPointerPosition(timeT t)
4765 {
4766 Composition &comp = m_doc->getComposition();
4767
4768 //RG_DEBUG << "slotSetPointerPosition(): t = " << t;
4769
4770 if (m_seqManager) {
4771 // If we're playing and we're past the end...
4772 if (m_seqManager->getTransportStatus() == PLAYING &&
4773 t > comp.getEndMarker()) {
4774
4775 // Stop
4776 slotStop();
4777
4778 // Limit the end to the end of the composition.
4779 // RECURSION: Causes this method to be re-invoked.
4780 m_doc->slotSetPointerPosition(comp.getEndMarker());
4781
4782 return;
4783
4784 }
4785 // If we're recording and we're near the end...
4786 if (m_seqManager->getTransportStatus() == RECORDING &&
4787 t > comp.getEndMarker() - timebase) {
4788
4789 // Compute bar duration
4790 std::pair<timeT, timeT> timeRange = comp.getBarRangeForTime(t);
4791 const timeT barDuration = timeRange.second - timeRange.first;
4792
4793 // Add on ten bars.
4794 const timeT newEndMarker = comp.getEndMarker() + 10 * barDuration;
4795 comp.setEndMarker(newEndMarker);
4796
4797 // Update UI
4798 getView()->getTrackEditor()->updateCanvasSize();
4799 getView()->getTrackEditor()->updateRulers();
4800
4801 }
4802
4803 // cc 20050520 - jump at the sequencer even if we're not playing,
4804 // because we might be a transport master of some kind
4805 try {
4806 if (!m_originatingJump) {
4807 m_seqManager->jumpTo(comp.getElapsedRealTime(t));
4808 }
4809 } catch (const QString &s) {
4810 QMessageBox::critical(this, tr("Rosegarden"), s);
4811 }
4812 }
4813
4814 // set the time sig
4815 getTransport()->setTimeSignature(comp.getTimeSignatureAt(t));
4816
4817 // and the tempo
4818 m_seqManager->setTempo(comp.getTempoAtTime(t));
4819
4820 // and the time
4821 //
4822 TransportDialog::TimeDisplayMode mode =
4823 getTransport()->getCurrentMode();
4824
4825 if (mode == TransportDialog::BarMode ||
4826 mode == TransportDialog::BarMetronomeMode) {
4827
4828 slotDisplayBarTime(t);
4829
4830 } else {
4831
4832 RealTime rT(comp.getElapsedRealTime(t));
4833
4834 if (getTransport()->isShowingTimeToEnd()) {
4835 rT = rT - comp.getElapsedRealTime(comp.getDuration());
4836 }
4837
4838 if (mode == TransportDialog::RealMode) {
4839
4840 getTransport()->displayRealTime(rT);
4841
4842 } else if (mode == TransportDialog::SMPTEMode) {
4843
4844 getTransport()->displaySMPTETime(rT);
4845
4846 } else {
4847
4848 getTransport()->displayFrameTime(rT);
4849 }
4850 }
4851
4852 // handle transport mode configuration changes
4853 std::string modeAsString = getTransport()->getCurrentModeAsString();
4854
4855 if (m_doc->getConfiguration().get<String>
4856 (DocumentConfiguration::TransportMode) != modeAsString) {
4857
4858 m_doc->getConfiguration().set<String>
4859 (DocumentConfiguration::TransportMode, modeAsString);
4860
4861 //m_doc->slotDocumentModified(); to avoid being prompted for a file change when merely changing the transport display
4862 }
4863
4864 // Update position on the marker editor if it's available
4865 //
4866 if (m_markerEditor)
4867 m_markerEditor->updatePosition();
4868 }
4869
4870 void
slotDisplayBarTime(timeT t)4871 RosegardenMainWindow::slotDisplayBarTime(timeT t)
4872 {
4873 Composition &comp = m_doc->getComposition();
4874
4875 int barNo = comp.getBarNumber(t);
4876 timeT barStart = comp.getBarStart(barNo);
4877
4878 TimeSignature timeSig = comp.getTimeSignatureAt(t);
4879 timeT beatDuration = timeSig.getBeatDuration();
4880
4881 int beatNo = (t - barStart) / beatDuration;
4882 int unitNo = (t - barStart) - (beatNo * beatDuration);
4883
4884 if (getTransport()->isShowingTimeToEnd()) {
4885 barNo = barNo + 1 - comp.getNbBars();
4886 beatNo = timeSig.getBeatsPerBar() - 1 - beatNo;
4887 unitNo = timeSig.getBeatDuration() - 1 - unitNo;
4888 } else {
4889 // convert to 1-based display bar numbers
4890 barNo += 1;
4891 beatNo += 1;
4892 }
4893
4894 // show units in hemidemis (or whatever), not in raw time ticks
4895 unitNo /= Note(Note::Shortest).getDuration();
4896
4897 getTransport()->displayBarTime(barNo, beatNo, unitNo);
4898 }
4899
4900 void
slotRefreshTimeDisplay()4901 RosegardenMainWindow::slotRefreshTimeDisplay()
4902 {
4903 if (m_seqManager->getTransportStatus() == PLAYING ||
4904 m_seqManager->getTransportStatus() == RECORDING) {
4905 return ; // it'll be refreshed in a moment anyway
4906 }
4907 slotSetPointerPosition(m_doc->getComposition().getPosition());
4908 }
4909
4910 void
slotToggleTracking()4911 RosegardenMainWindow::slotToggleTracking()
4912 {
4913 m_view->getTrackEditor()->toggleTracking();
4914 }
4915
4916 void
slotTestStartupTester()4917 RosegardenMainWindow::slotTestStartupTester()
4918 {
4919 RG_DEBUG << "slotTestStartupTester()";
4920
4921 if (!m_startupTester) {
4922 m_startupTester = new StartupTester();
4923 connect(m_startupTester, &StartupTester::newerVersionAvailable,
4924 this, &RosegardenMainWindow::slotNewerVersionAvailable);
4925 m_startupTester->start();
4926 QTimer::singleShot(100, this, &RosegardenMainWindow::slotTestStartupTester);
4927 return ;
4928 }
4929
4930 if (!m_startupTester->isReady()) {
4931 QTimer::singleShot(100, this, &RosegardenMainWindow::slotTestStartupTester);
4932 return ;
4933 }
4934
4935 /* QStringList missingFeatures;
4936 QStringList allMissing;
4937
4938 QStringList missing;
4939
4940 #ifdef HAVE_LIBJACK
4941 if (m_seqManager && (m_seqManager->getSoundDriverStatus() & AUDIO_OK)) {
4942
4943 m_haveAudioImporter = m_startupTester->haveAudioFileImporter(&missing);
4944
4945 if (!m_haveAudioImporter) {
4946 missingFeatures.push_back(tr("General audio file import and conversion"));
4947 if (missing.count() == 0) {
4948 allMissing.push_back(tr("The Rosegarden Audio File Importer helper script"));
4949 } else {
4950 for (int i = 0; i < missing.count(); ++i) {
4951 if (missingFeatures.count() > 1) {
4952 allMissing.push_back(tr("%1 - for audio file import").arg(missing[i]));
4953 } else {
4954 allMissing.push_back(missing[i]);
4955 }
4956 }
4957 }
4958 }
4959 }
4960 #endif
4961
4962 if (missingFeatures.count() > 0) {
4963 QString message = tr("<h3>Helper programs not found</h3><p>Rosegarden could not find one or more helper programs which it needs to provide some features. The following features will not be available:</p>");
4964 message += tr("<ul>");
4965 for (int i = 0; i < missingFeatures.count(); ++i) {
4966 message += tr("<li>%1</li>").arg(missingFeatures[i]);
4967 }
4968 message += tr("</ul>");
4969 message += tr("<p>To fix this, you should install the following additional programs:</p>");
4970 message += tr("<ul>");
4971 for (int i = 0; i < allMissing.count(); ++i) {
4972 message += tr("<li>%1</li>").arg(allMissing[i]);
4973 }
4974 message += tr("</ul>");
4975
4976 awaitDialogClearance();
4977
4978 QString shortMessage = tr("Helper programs not found");
4979
4980 // QMessageBox info(m_view);
4981 // info.setText(shortMessage);
4982 // info.setInformativeText(message);
4983 // info.setStandardButtons(QMessageBox::Ok);
4984 // info.setDefaultButton(QMessageBox::Ok);
4985 // info.setIcon(QMessageBox::Warning);
4986 //
4987 // if (!DialogSuppressor::shouldSuppress
4988 // (&info, "startuphelpersmissing")) {
4989 // info.exec();
4990 // }
4991
4992 // Looks like Thorn will have to keep the startup test for
4993 // audiofile-importer around indefinitely, and so we need to move that
4994 // irritating @#@^@#^ dialog into the warning widget, and get it out of
4995 // my face before I punch it right in the nose.
4996 m_warningWidget->queueMessage(shortMessage, message);
4997 }*/
4998
4999 m_startupTester->wait();
5000 delete m_startupTester;
5001 m_startupTester = nullptr;
5002 }
5003
5004 void
slotDebugDump()5005 RosegardenMainWindow::slotDebugDump()
5006 {
5007 m_doc->getComposition().dump();
5008 }
5009
5010 bool
launchSequencer()5011 RosegardenMainWindow::launchSequencer()
5012 {
5013 if (!isUsingSequencer()) {
5014 RG_DEBUG << "launchSequencer() - not using seq. - returning\n";
5015 return false; // no need to launch anything
5016 }
5017
5018 if (isSequencerRunning()) {
5019 RG_DEBUG << "launchSequencer() - sequencer already running - returning\n";
5020 if (m_seqManager) m_seqManager->checkSoundDriverStatus(false);
5021 return true;
5022 }
5023
5024 m_sequencerThread = new SequencerThread();
5025 connect(m_sequencerThread, &QThread::finished, this, &RosegardenMainWindow::slotSequencerExited);
5026 m_sequencerThread->start();
5027
5028 RG_DEBUG << "launchSequencer: Sequencer thread is "
5029 << m_sequencerThread;
5030
5031 if (m_doc && m_doc->getStudio().haveMidiDevices()) {
5032 enterActionState("got_midi_devices"); //@@@ JAS orig. 0
5033 } else {
5034 leaveActionState("got_midi_devices"); //@@@ JAS orig. KXMLGUIClient::StateReverse
5035 }
5036
5037 return true;
5038 }
5039
5040 void
slotDocumentDevicesResyncd()5041 RosegardenMainWindow::slotDocumentDevicesResyncd()
5042 {
5043 //!DEVPUSH how to replace this?
5044 m_sequencerCheckedIn = true;
5045 m_trackParameterBox->devicesChanged();
5046 }
5047
5048 void
slotSequencerExited()5049 RosegardenMainWindow::slotSequencerExited()
5050 {
5051 RG_DEBUG << "slotSequencerExited Sequencer exited\n";
5052
5053 StartupLogo::hideIfStillThere();
5054
5055 if (m_sequencerCheckedIn) {
5056
5057 QMessageBox::critical(this, tr("Rosegarden"), tr("The Rosegarden sequencer process has exited unexpectedly. Sound and recording will no longer be available for this session.\nPlease exit and restart Rosegarden to restore sound capability."));
5058
5059 } else {
5060
5061 QMessageBox::critical(this, tr("Rosegarden"), tr("The Rosegarden sequencer could not be started, so sound and recording will be unavailable for this session.\nFor assistance with correct audio and MIDI configuration, go to http://rosegardenmusic.com."));
5062 }
5063
5064 delete m_sequencerThread;
5065 m_sequencerThread = nullptr; // isSequencerRunning() will return false
5066 // but isUsingSequencer() will keep returning true
5067 // so pressing the play button may attempt to restart the sequencer
5068 }
5069
5070 void
slotExportProject()5071 RosegardenMainWindow::slotExportProject()
5072 {
5073 TmpStatusMsg msg(tr("Exporting Rosegarden Project file..."), this);
5074
5075 QString fileName = getValidWriteFileName
5076 (tr("Rosegarden Project files") + " (*.rgp *.RGP)" + ";;" +
5077 tr("All files") + " (*)",
5078 tr("Export as..."));
5079
5080 if (fileName.isEmpty())
5081 return ;
5082
5083 QString rgFile = fileName;
5084 rgFile.replace(QRegularExpression(".rg.rgp$"), ".rg");
5085 rgFile.replace(QRegularExpression(".rgp$"), ".rg");
5086
5087 // I have a ton of weird files and suspect problems with this, but maybe
5088 // not:
5089 RG_DEBUG << "getValidWriteFileName() returned " << fileName.toStdString()
5090 << " rgFile: " << fileName.toStdString();
5091
5092 QString errMsg;
5093 if (!m_doc->saveDocument(rgFile, errMsg,
5094 true)) { // pretend it's autosave
5095 QMessageBox::warning(this, tr("Rosegarden"), tr("Saving Rosegarden file to package failed: %1").arg(errMsg));
5096 return ;
5097 }
5098
5099 // Launch the project packager script-in-a-dialog in Pack mode:
5100 ProjectPackager *dialog = new ProjectPackager(this, m_doc, ProjectPackager::Pack, fileName);
5101 if (dialog->exec() != QDialog::Accepted) {
5102 return;
5103 }
5104 }
5105
5106 void
slotExportMIDI()5107 RosegardenMainWindow::slotExportMIDI()
5108 {
5109 TmpStatusMsg msg(tr("Exporting MIDI file..."), this);
5110
5111 QString fileName = getValidWriteFileName
5112 (tr("Standard MIDI files") + " (*.mid *.midi *.MID *.MIDI)" + ";;" +
5113 tr("All files") + " (*)",
5114 tr("Export as..."));
5115
5116 if (fileName.isEmpty())
5117 return ;
5118
5119 exportMIDIFile(fileName);
5120 }
5121
5122 void
exportMIDIFile(QString file)5123 RosegardenMainWindow::exportMIDIFile(QString file)
5124 {
5125 // Progress Dialog
5126 QProgressDialog progressDialog(
5127 tr("Exporting MIDI file..."), // labelText
5128 tr("Cancel"), // cancelButtonText
5129 0, 100, // min, max
5130 this); // parent
5131 progressDialog.setWindowTitle(tr("Rosegarden"));
5132 progressDialog.setWindowModality(Qt::WindowModal);
5133 // No sense in auto close since we will close anyway when
5134 // this object goes out of scope.
5135 progressDialog.setAutoClose(false);
5136 // Just force the progress dialog up.
5137 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
5138 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
5139 // based distros might lock up. See Bug #1546.
5140 progressDialog.show();
5141
5142 MidiFile midiFile;
5143
5144 midiFile.setProgressDialog(&progressDialog);
5145
5146 if (!midiFile.convertToMidi(m_doc, file)) {
5147 QMessageBox::warning(this, tr("Rosegarden"),
5148 tr("Export failed. The file could not be opened for writing."));
5149 }
5150 }
5151
5152 void
slotExportCsound()5153 RosegardenMainWindow::slotExportCsound()
5154 {
5155 TmpStatusMsg msg(tr("Exporting Csound score file..."), this);
5156
5157 QString fileName = getValidWriteFileName
5158 (tr("Csound files") + " (*.csd *.CSD)" + ";;" +
5159 tr("All files") + " (*)",
5160 tr("Export as..."));
5161
5162 if (fileName.isEmpty())
5163 return ;
5164
5165 exportCsoundFile(fileName);
5166 }
5167
5168 void
exportCsoundFile(QString file)5169 RosegardenMainWindow::exportCsoundFile(QString file)
5170 {
5171 // Progress Dialog
5172 // ??? The Csound export process is so fast, this never has a
5173 // chance to appear. Even with a huge composition. I'm
5174 // just going to leave this as an indeterminate progress
5175 // dialog. It can probably just be removed.
5176 QProgressDialog progressDialog(
5177 tr("Exporting Csound score file..."), // labelText
5178 tr("Cancel"), // cancelButtonText
5179 0, 0, // min, max
5180 this); // parent
5181 progressDialog.setWindowTitle(tr("Rosegarden"));
5182 progressDialog.setWindowModality(Qt::WindowModal);
5183 // No sense in auto close since we will close anyway when
5184 // this object goes out of scope.
5185 progressDialog.setAutoClose(false);
5186 // Get rid of the cancel button for now.
5187 progressDialog.setCancelButton(nullptr);
5188 // Just force the progress dialog up.
5189 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
5190 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
5191 // based distros might lock up. See Bug #1546.
5192 progressDialog.show();
5193
5194 CsoundExporter csoundExporter(
5195 this, // parent
5196 &m_doc->getComposition(), // composition
5197 std::string(file.toLocal8Bit())); // fileName
5198
5199 if (!csoundExporter.write()) {
5200 QMessageBox::warning(this, tr("Rosegarden"),
5201 tr("Export failed. The file could not be opened for writing."));
5202 }
5203 }
5204
5205 void
slotExportMup()5206 RosegardenMainWindow::slotExportMup()
5207 {
5208 TmpStatusMsg msg(tr("Exporting Mup file..."), this);
5209
5210 QString fileName = getValidWriteFileName
5211 (tr("Mup files") + " (*.mup *.MUP)" + ";;" +
5212 tr("All files") + " (*)",
5213 tr("Export as..."));
5214 if (fileName.isEmpty())
5215 return ;
5216
5217 exportMupFile(fileName);
5218 }
5219
5220 void
exportMupFile(QString file)5221 RosegardenMainWindow::exportMupFile(QString file)
5222 {
5223 // Progress Dialog
5224 QProgressDialog progressDialog(
5225 tr("Exporting Mup file..."), // labelText
5226 tr("Cancel"), // cancelButtonText
5227 0, 0, // min, max
5228 this); // parent
5229 progressDialog.setWindowTitle(tr("Rosegarden"));
5230 progressDialog.setWindowModality(Qt::WindowModal);
5231 // No sense in auto close since we will close anyway when
5232 // this object goes out of scope.
5233 progressDialog.setAutoClose(false);
5234 // Get rid of the cancel button for now.
5235 progressDialog.setCancelButton(nullptr);
5236 // Just force the progress dialog up.
5237 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
5238 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
5239 // based distros might lock up. See Bug #1546.
5240 progressDialog.show();
5241
5242 MupExporter mupExporter(
5243 this, // parent
5244 &m_doc->getComposition(), // composition
5245 std::string(file.toLocal8Bit())); // fileName
5246
5247 if (!mupExporter.write()) {
5248 QMessageBox::warning(this, tr("Rosegarden"),
5249 tr("Export failed. The file could not be opened for writing."));
5250 }
5251 }
5252
5253 void
slotExportLilyPond()5254 RosegardenMainWindow::slotExportLilyPond()
5255 {
5256 TmpStatusMsg msg(tr("Exporting LilyPond file..."), this);
5257
5258 QString fileName = getValidWriteFileName
5259 (tr("LilyPond files") + " (*.ly *.LY)" + ";;" +
5260 tr("All files") + " (*)",
5261 tr("Export as..."));
5262
5263 if (fileName.isEmpty())
5264 return ;
5265
5266 exportLilyPondFile(fileName);
5267 }
5268
5269
5270 void
slotPrintLilyPond()5271 RosegardenMainWindow::slotPrintLilyPond()
5272 {
5273 TmpStatusMsg msg(tr("Printing with LilyPond..."), this);
5274
5275 QString filename = getLilyPondTmpFilename();
5276 if (filename.isEmpty()) return;
5277
5278 if (!exportLilyPondFile(filename, true)) {
5279 return ;
5280 }
5281
5282 LilyPondProcessor *dialog = new LilyPondProcessor(this, LilyPondProcessor::Print, filename);
5283 dialog->exec();
5284 }
5285
5286 void
slotPreviewLilyPond()5287 RosegardenMainWindow::slotPreviewLilyPond()
5288 {
5289 TmpStatusMsg msg(tr("Previewing LilyPond file..."), this);
5290
5291 QString filename = getLilyPondTmpFilename();
5292 if (filename.isEmpty()) return;
5293
5294 if (!exportLilyPondFile(filename, true)) {
5295 return ;
5296 }
5297
5298 LilyPondProcessor *dialog = new LilyPondProcessor(this, LilyPondProcessor::Preview, filename);
5299 dialog->exec();
5300 }
5301
5302 QString
getLilyPondTmpFilename()5303 RosegardenMainWindow::getLilyPondTmpFilename()
5304 {
5305 QString mask = QString("%1/rosegarden_tmp_XXXXXX.ly").arg(QDir::tempPath());
5306 RG_WARNING << "getLilyPondTmpName() - using tmp file: " << qstrtostr(mask);
5307
5308 QTemporaryFile *file = new QTemporaryFile(mask);
5309 file->setAutoRemove(true);
5310 if (!file->open()) {
5311 QMessageBox::warning(this, tr("Rosegarden"), tr("<qt><p>Failed to open a temporary file for LilyPond export.</p>"
5312 "<p>This probably means you have run out of disk space on <pre>%1</pre></p></qt>").
5313 arg(QDir::tempPath()));
5314 delete file;
5315 return QString();
5316 }
5317 QString filename = file->fileName(); // must call this before close()
5318 file->close(); // we just want the filename
5319
5320 return filename;
5321 }
5322
5323
5324 bool
exportLilyPondFile(QString file,bool forPreview)5325 RosegardenMainWindow::exportLilyPondFile(QString file, bool forPreview)
5326 {
5327 QString caption;
5328 QString heading;
5329
5330 if (forPreview) {
5331 caption = tr("LilyPond Preview Options");
5332 heading = tr("LilyPond preview options");
5333 }
5334
5335 LilyPondOptionsDialog dialog(this, m_doc, caption, heading);
5336 if (dialog.exec() != QDialog::Accepted)
5337 return false;
5338
5339 // Progress Dialog
5340 QProgressDialog progressDialog(
5341 tr("Exporting LilyPond file..."), // labelText
5342 tr("Cancel"), // cancelButtonText
5343 0, 100, // min, max
5344 this); // parent
5345 progressDialog.setWindowTitle(tr("Rosegarden"));
5346 progressDialog.setWindowModality(Qt::WindowModal);
5347 // No sense in auto close since we will close anyway when
5348 // this object goes out of scope.
5349 progressDialog.setAutoClose(false);
5350 // Just force the progress dialog up.
5351 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
5352 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
5353 // based distros might lock up. See Bug #1546.
5354 progressDialog.show();
5355
5356 LilyPondExporter lilyPondExporter(
5357 m_doc, // document
5358 m_view->getSelection(), // selection
5359 std::string(QFile::encodeName(file))); // fileName
5360
5361 lilyPondExporter.setProgressDialog(&progressDialog);
5362
5363 if (!lilyPondExporter.write()) {
5364 if (!progressDialog.wasCanceled()) {
5365 QMessageBox::warning(this, tr("Rosegarden"),
5366 lilyPondExporter.getMessage());
5367 }
5368
5369 return false;
5370 }
5371
5372 return true;
5373 }
5374
5375 void
slotExportMusicXml()5376 RosegardenMainWindow::slotExportMusicXml()
5377 {
5378 TmpStatusMsg msg(tr("Exporting MusicXML file..."), this);
5379
5380 QString fileName = getValidWriteFileName
5381 (tr("XML files") + " (*.xml *.XML)" + ";;" +
5382 tr("All files") + " (*)",
5383 tr("Export as..."));
5384
5385 if (fileName.isEmpty())
5386 return ;
5387
5388 exportMusicXmlFile(fileName);
5389 }
5390
5391 void
exportMusicXmlFile(QString file)5392 RosegardenMainWindow::exportMusicXmlFile(QString file)
5393 {
5394 MusicXMLOptionsDialog dialog(this, m_doc, "", "");
5395
5396 if (dialog.exec() != QDialog::Accepted)
5397 return;
5398
5399 // Progress Dialog
5400 // Note: Label text will be set later in the process.
5401 QProgressDialog progressDialog(
5402 "...", // labelText
5403 tr("Cancel"), // cancelButtonText
5404 0, 100, // min, max
5405 this); // parent
5406 progressDialog.setWindowTitle(tr("Rosegarden"));
5407 progressDialog.setWindowModality(Qt::WindowModal);
5408 // No sense in auto close since we will close anyway when
5409 // this object goes out of scope.
5410 progressDialog.setAutoClose(false);
5411 // Get rid of the cancel button for now.
5412 progressDialog.setCancelButton(nullptr);
5413 // Just force the progress dialog up.
5414 // Both Qt4 and Qt5 have bugs related to delayed showing of progress
5415 // dialogs. In Qt4, the dialog sometimes won't show. In Qt5, KDE
5416 // based distros might lock up. See Bug #1546.
5417 progressDialog.show();
5418
5419 MusicXmlExporter musicXmlExporter(
5420 this, // parent
5421 m_doc, // document
5422 std::string(file.toLocal8Bit())); // fileName
5423
5424 musicXmlExporter.setProgressDialog(&progressDialog);
5425
5426 if (!musicXmlExporter.write()) {
5427 QMessageBox::warning(this,
5428 tr("Rosegarden"),
5429 tr("Export failed. The file could not be opened for writing."));
5430 }
5431 }
5432
5433 void
slotCloseTransport()5434 RosegardenMainWindow::slotCloseTransport()
5435 {
5436 findAction("show_transport")->setChecked(false);
5437 slotUpdateTransportVisibility();
5438 }
5439
5440 void
slotDeleteTransport()5441 RosegardenMainWindow::slotDeleteTransport()
5442 {
5443 delete m_transport;
5444 m_transport = nullptr;
5445 }
5446
5447 void
slotActivateTool(QString toolName)5448 RosegardenMainWindow::slotActivateTool(QString toolName)
5449 {
5450 if (toolName == SegmentSelector::ToolName()) {
5451 findAction("select")->trigger();
5452 }
5453 }
5454
5455 void
slotToggleMetronome()5456 RosegardenMainWindow::slotToggleMetronome()
5457 {
5458 Composition &comp = m_doc->getComposition();
5459
5460 if (m_seqManager->getTransportStatus() == STARTING_TO_RECORD ||
5461 m_seqManager->getTransportStatus() == RECORDING ||
5462 m_seqManager->getTransportStatus() == RECORDING_ARMED) {
5463 if (comp.useRecordMetronome())
5464 comp.setRecordMetronome(false);
5465 else
5466 comp.setRecordMetronome(true);
5467
5468 getTransport()->MetronomeButton()->setChecked(comp.useRecordMetronome());
5469 } else {
5470 if (comp.usePlayMetronome())
5471 comp.setPlayMetronome(false);
5472 else
5473 comp.setPlayMetronome(true);
5474
5475 getTransport()->MetronomeButton()->setChecked(comp.usePlayMetronome());
5476 }
5477 }
5478
5479 void
slotRewindToBeginning()5480 RosegardenMainWindow::slotRewindToBeginning()
5481 {
5482 // ignore requests if recording
5483 //
5484 if (m_seqManager->getTransportStatus() == RECORDING)
5485 return ;
5486
5487 m_seqManager->rewindToBeginning();
5488 }
5489
5490 void
slotFastForwardToEnd()5491 RosegardenMainWindow::slotFastForwardToEnd()
5492 {
5493 // ignore requests if recording
5494 //
5495 if (m_seqManager->getTransportStatus() == RECORDING)
5496 return ;
5497
5498 m_seqManager->fastForwardToEnd();
5499 }
5500
5501 void
slotSetPlayPosition(timeT time)5502 RosegardenMainWindow::slotSetPlayPosition(timeT time)
5503 {
5504 RG_DEBUG << "slotSetPlayPosition(" << time << ")";
5505 if (m_seqManager->getTransportStatus() == RECORDING)
5506 return ;
5507
5508 m_doc->slotSetPointerPosition(time);
5509
5510 if (m_seqManager->getTransportStatus() == PLAYING)
5511 return ;
5512
5513 slotPlay();
5514 }
5515
5516 void
slotRecord()5517 RosegardenMainWindow::slotRecord()
5518 {
5519 if (!isUsingSequencer())
5520 return ;
5521
5522 if (!isSequencerRunning()) {
5523
5524 // Try to launch sequencer and return if we fail
5525 //
5526 if (!launchSequencer()) return;
5527 }
5528
5529 if (m_seqManager->getTransportStatus() == RECORDING) {
5530 slotStop();
5531 return ;
5532 } else if (m_seqManager->getTransportStatus() == PLAYING) {
5533 slotToggleRecord();
5534 return ;
5535 }
5536
5537 // Attempt to start recording
5538 //
5539 try {
5540 m_seqManager->record(false);
5541 } catch (const QString &s) {
5542 // We should already be stopped by this point so just unset
5543 // the buttons after clicking the dialog.
5544 //
5545 QMessageBox::critical(this, tr("Rosegarden"), s);
5546
5547 getTransport()->MetronomeButton()->setChecked(false);
5548 getTransport()->RecordButton()->setChecked(false);
5549 getTransport()->PlayButton()->setChecked(false);
5550 return ;
5551 } catch (const AudioFileManager::BadAudioPathException &e) {
5552 if (QMessageBox::warning
5553 (nullptr, tr("Warning"),
5554 tr("The audio file path does not exist or is not writable.\nPlease set the audio file path to a valid directory in Document Properties before recording audio.\nWould you like to set it now?"),
5555 QMessageBox::Yes | QMessageBox::Cancel,
5556 QMessageBox::Cancel) // default button
5557 == QMessageBox::Yes)
5558 {
5559 slotOpenAudioPathSettings();
5560 }//end if
5561
5562 getTransport()->MetronomeButton()->setChecked(false);
5563 getTransport()->RecordButton()->setChecked(false);
5564 getTransport()->PlayButton()->setChecked(false);
5565 return ;
5566 } catch (const Exception &e) {
5567 QMessageBox::critical(this, tr("Rosegarden"), strtoqstr(e.getMessage()));
5568
5569 getTransport()->MetronomeButton()->setChecked(false);
5570 getTransport()->RecordButton()->setChecked(false);
5571 getTransport()->PlayButton()->setChecked(false);
5572 return ;
5573 }
5574
5575 // plugin the keyboard shortcuts for focus on this dialog
5576 plugShortcuts(m_seqManager->getCountdownDialog(),
5577 m_seqManager->getCountdownDialog()->getShortcuts());
5578
5579 connect(m_seqManager->getCountdownDialog(), &CountdownDialog::stopped,
5580 this, &RosegardenMainWindow::slotStop);
5581 }
5582
5583 void
slotToggleRecord()5584 RosegardenMainWindow::slotToggleRecord()
5585 {
5586 if (!isUsingSequencer() || (!isSequencerRunning() && !launchSequencer()))
5587 return;
5588
5589 try {
5590 m_seqManager->record(true);
5591 } catch (const QString &s) {
5592 QMessageBox::critical(this, tr("Rosegarden"), s);
5593 } catch (const AudioFileManager::BadAudioPathException &e) {
5594 if (QMessageBox::warning
5595 (this, tr("Error"),
5596 tr("The audio file path does not exist or is not writable.\nPlease set the audio file path to a valid directory in Document Properties before you start to record audio.\nWould you like to set it now?"),
5597 QMessageBox::Yes | QMessageBox::Cancel,
5598 QMessageBox::Cancel
5599 ) == QMessageBox::Yes
5600 ){
5601 //tr("Set audio file path")) == QMessageBox::Continue) {
5602 slotOpenAudioPathSettings();
5603 }
5604 } catch (const Exception &e) {
5605 QMessageBox::critical(this, tr("Rosegarden"), strtoqstr(e.getMessage()));
5606 }
5607
5608 }
5609
5610 void
slotSetLoop(timeT lhs,timeT rhs)5611 RosegardenMainWindow::slotSetLoop(timeT lhs, timeT rhs)
5612 {
5613 try {
5614 m_doc->slotDocumentModified();
5615
5616 m_seqManager->setLoop(lhs, rhs);
5617
5618 // toggle the loop button
5619 if (lhs != rhs) {
5620 getTransport()->LoopButton()->setChecked(true);
5621 enterActionState("have_range"); //@@@ JAS orig. KXMLGUIClient::StateNoReverse
5622 } else {
5623 getTransport()->LoopButton()->setChecked(false);
5624 leaveActionState("have_range"); //@@@ JAS orig. KXMLGUIClient::StateReverse
5625 }
5626 } catch (const QString &s) {
5627 QMessageBox::critical(this, tr("Rosegarden"), s);
5628 }
5629 }
5630
5631 bool
isUsingSequencer()5632 RosegardenMainWindow::isUsingSequencer()
5633 {
5634 return m_useSequencer;
5635 }
5636
5637 bool
isSequencerRunning()5638 RosegardenMainWindow::isSequencerRunning()
5639 {
5640 RG_DEBUG << "isSequencerRunning: m_useSequencer = "
5641 << m_useSequencer << ", m_sequencerThread = " << m_sequencerThread;
5642 return m_useSequencer && (m_sequencerThread != nullptr);
5643 }
5644
5645 void
alive()5646 RosegardenMainWindow::alive()
5647 {
5648 // ??? This routine appears to never be called.
5649
5650 //RG_DEBUG << "alive()";
5651
5652 if (m_doc && m_doc->getStudio().haveMidiDevices()) {
5653 enterActionState("got_midi_devices"); //@@@ JAS orig. 0
5654 } else {
5655 leaveActionState("got_midi_devices"); //@@@ JAS orig. KXMLGUIClient::StateReverse
5656 }
5657 }
5658
5659 void
slotPlay()5660 RosegardenMainWindow::slotPlay()
5661 {
5662 if (!isUsingSequencer())
5663 return ;
5664
5665 if (!isSequencerRunning()) {
5666
5667 // Try to launch sequencer and return if it fails
5668 //
5669 if (!launchSequencer()) return;
5670 }
5671
5672 if (!m_seqManager) return;
5673
5674 // If we're armed and ready to record then do this instead (calling
5675 // slotRecord ensures we don't toggle the recording state in
5676 // SequenceManager)
5677 //
5678 if (m_seqManager->getTransportStatus() == RECORDING_ARMED) {
5679 slotRecord();
5680 return ;
5681 }
5682
5683 try {
5684 m_seqManager->play(); // this will stop playback (pause) if it's already running
5685 } catch (const QString &s) {
5686 QMessageBox::critical(this, tr("Rosegarden"), s);
5687 } catch (const Exception &e) {
5688 QMessageBox::critical(this, tr("Rosegarden"), strtoqstr(e.getMessage()));
5689 }
5690 }
5691
5692 void
slotJumpToTime(RealTime rt)5693 RosegardenMainWindow::slotJumpToTime(RealTime rt)
5694 {
5695 Composition *comp = &m_doc->getComposition();
5696 timeT t = comp->getElapsedTimeForRealTime(rt);
5697 m_doc->slotSetPointerPosition(t);
5698 }
5699
5700 void
slotStartAtTime(RealTime rt)5701 RosegardenMainWindow::slotStartAtTime(RealTime rt)
5702 {
5703 slotJumpToTime(rt);
5704 slotPlay();
5705 }
5706
5707 void
slotStop()5708 RosegardenMainWindow::slotStop()
5709 {
5710 if (m_seqManager &&
5711 m_seqManager->getCountdownDialog()) {
5712 disconnect(m_seqManager->getCountdownDialog(), &CountdownDialog::stopped,
5713 this, &RosegardenMainWindow::slotStop);
5714 disconnect(m_seqManager->getCountdownDialog(), &CountdownDialog::completed,
5715 this, &RosegardenMainWindow::slotStop);
5716 }
5717
5718 try {
5719 if (m_seqManager)
5720 m_seqManager->stop();
5721 } catch (const Exception &e) {
5722 QMessageBox::critical(this, tr("Rosegarden"), strtoqstr(e.getMessage()));
5723 }
5724 }
5725
5726 void
slotRewind()5727 RosegardenMainWindow::slotRewind()
5728 {
5729 // ignore requests if recording
5730 //
5731 if (m_seqManager->getTransportStatus() == RECORDING)
5732 return ;
5733 if (m_seqManager)
5734 m_seqManager->rewind();
5735 }
5736
5737 void
slotFastforward()5738 RosegardenMainWindow::slotFastforward()
5739 {
5740 // ignore requests if recording
5741 //
5742 if (m_seqManager->getTransportStatus() == RECORDING)
5743 return ;
5744
5745 if (m_seqManager)
5746 m_seqManager->fastforward();
5747 }
5748
5749 void
slotSetLoop()5750 RosegardenMainWindow::slotSetLoop()
5751 {
5752 // restore loop
5753 m_doc->setLoop(m_storedLoopStart, m_storedLoopEnd);
5754 }
5755
5756 void
slotUnsetLoop()5757 RosegardenMainWindow::slotUnsetLoop()
5758 {
5759 Composition &comp = m_doc->getComposition();
5760
5761 // store the loop
5762 m_storedLoopStart = comp.getLoopStart();
5763 m_storedLoopEnd = comp.getLoopEnd();
5764
5765 // clear the loop at the composition and propagate to the rest
5766 // of the display items
5767 m_doc->setLoop(0, 0);
5768 }
5769
5770 void
slotSetLoopStart()5771 RosegardenMainWindow::slotSetLoopStart()
5772 {
5773 // Check so that start time is before endtime, othervise move upp the
5774 // endtime to that same pos.
5775 if (m_doc->getComposition().getPosition() < m_doc->getComposition().getLoopEnd()) {
5776 m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getLoopEnd());
5777 } else {
5778 m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
5779 }
5780 }
5781
5782 void
slotSetLoopStop()5783 RosegardenMainWindow::slotSetLoopStop()
5784 {
5785 // Check so that end time is after start time, othervise move upp the
5786 // start time to that same pos.
5787 if (m_doc->getComposition().getLoopStart() < m_doc->getComposition().getPosition()) {
5788 m_doc->setLoop(m_doc->getComposition().getLoopStart(), m_doc->getComposition().getPosition());
5789 } else {
5790 m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
5791 }
5792 }
5793
5794 void
toggleLoop()5795 RosegardenMainWindow::toggleLoop()
5796 {
5797 // if a loop is set
5798 if (m_doc->getComposition().isLooping())
5799 slotUnsetLoop();
5800 else
5801 slotSetLoop();
5802 }
5803
5804 void
slotToggleSolo(bool)5805 RosegardenMainWindow::slotToggleSolo(bool)
5806 {
5807 // Delegate to TrackButtons.
5808 getView()->getTrackEditor()->getTrackButtons()->toggleSolo();
5809 }
5810
5811 void
slotSelectPreviousTrack()5812 RosegardenMainWindow::slotSelectPreviousTrack()
5813 {
5814 if (!m_doc)
5815 return;
5816
5817 Composition &comp = m_doc->getComposition();
5818
5819 TrackId tid = comp.getSelectedTrack();
5820 int pos = comp.getTrackById(tid)->getPosition();
5821
5822 // If at top already
5823 if (pos == 0)
5824 return ;
5825
5826 Track *track = comp.getTrackByPosition(pos - 1);
5827
5828 if (!track)
5829 return;
5830
5831 comp.setSelectedTrack(track->getId());
5832 comp.notifyTrackSelectionChanged(comp.getSelectedTrack());
5833 if (m_view)
5834 m_view->slotSelectTrackSegments(comp.getSelectedTrack());
5835
5836 m_doc->emitDocumentModified();
5837 }
5838
5839 void
slotSelectNextTrack()5840 RosegardenMainWindow::slotSelectNextTrack()
5841 {
5842 if (!m_doc)
5843 return;
5844
5845 Composition &comp = m_doc->getComposition();
5846
5847 TrackId tid = comp.getSelectedTrack();
5848 int pos = comp.getTrackById(tid)->getPosition();
5849
5850 Track *track = comp.getTrackByPosition(pos + 1);
5851
5852 if (!track)
5853 return;
5854
5855 comp.setSelectedTrack(track->getId());
5856 comp.notifyTrackSelectionChanged(comp.getSelectedTrack());
5857 if (m_view)
5858 m_view->slotSelectTrackSegments(comp.getSelectedTrack());
5859
5860 m_doc->emitDocumentModified();
5861 }
5862
5863 void
muteAllTracks(bool mute)5864 RosegardenMainWindow::muteAllTracks(bool mute)
5865 {
5866 //RG_DEBUG << "muteAllTracks()";
5867
5868 if (!m_doc)
5869 return;
5870
5871 Composition &comp = m_doc->getComposition();
5872 Composition::trackcontainer tracks = comp.getTracks();
5873
5874 for (Composition::trackiterator trackIt = tracks.begin();
5875 trackIt != tracks.end(); ++trackIt) {
5876 Track *track = trackIt->second;
5877
5878 if (!track)
5879 continue;
5880
5881 // Mute or unmute the track
5882 track->setMuted(mute);
5883
5884 // Notify observers
5885 comp.notifyTrackChanged(track);
5886 }
5887
5888 m_doc->slotDocumentModified();
5889 }
5890
5891 void
slotMuteAllTracks()5892 RosegardenMainWindow::slotMuteAllTracks()
5893 {
5894 muteAllTracks();
5895 }
5896
5897 void
slotUnmuteAllTracks()5898 RosegardenMainWindow::slotUnmuteAllTracks()
5899 {
5900 muteAllTracks(false);
5901 }
5902
5903 void
slotToggleMute()5904 RosegardenMainWindow::slotToggleMute()
5905 {
5906 if (!m_doc)
5907 return;
5908
5909 Composition &comp = m_doc->getComposition();
5910 Track *track = comp.getTrackById(comp.getSelectedTrack());
5911
5912 if (!track)
5913 return;
5914
5915 // Toggle the mute state
5916 track->setMuted(!track->isMuted());
5917
5918 // Notify observers
5919 comp.notifyTrackChanged(track);
5920 m_doc->slotDocumentModified();
5921 }
5922
5923 void
slotToggleRecordCurrentTrack()5924 RosegardenMainWindow::slotToggleRecordCurrentTrack()
5925 {
5926 if (!m_doc)
5927 return;
5928
5929 Composition &comp = m_doc->getComposition();
5930 TrackId tid = comp.getSelectedTrack();
5931
5932 Track *track = comp.getTrackById(tid);
5933
5934 if (!track)
5935 return;
5936
5937 // Toggle
5938 bool state = !comp.isTrackRecording(tid);
5939
5940 // Update the Track
5941 comp.setTrackRecording(tid, state);
5942 comp.notifyTrackChanged(track);
5943
5944 m_doc->checkAudioPath(track);
5945 }
5946
5947 void
slotConfigure()5948 RosegardenMainWindow::slotConfigure()
5949 {
5950 RG_DEBUG << "slotConfigure\n";
5951
5952 if (!m_configDlg) {
5953 m_configDlg = new ConfigureDialog(m_doc, this);
5954
5955 connect(m_configDlg, &ConfigureDialog::updateAutoSaveInterval,
5956 this, &RosegardenMainWindow::slotUpdateAutoSaveInterval);
5957
5958 // Close the dialog if the document is changed : fix a potential crash
5959 connect(this, SIGNAL(documentAboutToChange()),
5960 m_configDlg, SLOT(slotCancelOrClose()));
5961
5962 // Clear m_configDlg if the dialog is destroyed
5963 connect(m_configDlg, &QObject::destroyed,
5964 this, &RosegardenMainWindow::slotResetConfigDlg);
5965
5966 m_configDlg->show();
5967 }
5968 }
5969
5970 void
slotResetConfigDlg()5971 RosegardenMainWindow::slotResetConfigDlg()
5972 {
5973 // The configuration dialog has been closed.
5974 // Qt should have removed the connections.
5975 m_configDlg = nullptr;
5976 }
5977
5978 void
slotEditDocumentProperties()5979 RosegardenMainWindow::slotEditDocumentProperties()
5980 {
5981 RG_DEBUG << "slotEditDocumentProperties\n";
5982
5983 // Don't create a dialog if there is already one
5984 if (!m_docConfigDlg) {
5985 m_docConfigDlg = new DocumentConfigureDialog(this);
5986
5987 // Close the dialog if the document is changed : fix #1462
5988 connect(this, SIGNAL(documentAboutToChange()),
5989 m_docConfigDlg, SLOT(slotCancelOrClose()));
5990
5991 // Clear m_docConfigDlg if the dialog is destroyed
5992 connect(m_docConfigDlg, &QObject::destroyed,
5993 this, &RosegardenMainWindow::slotResetDocConfigDlg);
5994 }
5995
5996 m_docConfigDlg->show();
5997 }
5998
5999 void
slotOpenAudioPathSettings()6000 RosegardenMainWindow::slotOpenAudioPathSettings()
6001 {
6002 RG_DEBUG << "slotOpenAudioPathSettings\n";
6003
6004 // Don't create a dialog if there is already one
6005 if (!m_docConfigDlg) {
6006 m_docConfigDlg = new DocumentConfigureDialog(this);
6007
6008 // Close the dialog if the document is changed : fix #1462
6009 connect(this, SIGNAL(documentAboutToChange()),
6010 m_docConfigDlg, SLOT(slotCancelOrClose()));
6011
6012 // Clear m_docConfigDlg if the dialog is destroyed
6013 connect(m_docConfigDlg, &QObject::destroyed,
6014 this, &RosegardenMainWindow::slotResetDocConfigDlg);
6015 }
6016
6017 m_docConfigDlg->showAudioPage();
6018 m_docConfigDlg->show();
6019 }
6020
6021 void
slotResetDocConfigDlg()6022 RosegardenMainWindow::slotResetDocConfigDlg()
6023 {
6024 // The document configuration dialog has been closed.
6025 // Qt should have removed the connections.
6026 m_docConfigDlg = nullptr;
6027 }
6028
6029 void
slotUpdateToolbars()6030 RosegardenMainWindow::slotUpdateToolbars()
6031 {
6032 findAction("show_stock_toolbar")->setChecked(!(findToolbar("Main Toolbar")->isHidden()));
6033 }
6034
6035 void
slotEditTempo()6036 RosegardenMainWindow::slotEditTempo()
6037 {
6038 slotEditTempo(this);
6039 }
6040
6041 void
slotEditTempo(timeT atTime)6042 RosegardenMainWindow::slotEditTempo(timeT atTime)
6043 {
6044 slotEditTempo(this, atTime);
6045 }
6046
6047 void
slotEditTempo(QWidget * parent)6048 RosegardenMainWindow::slotEditTempo(QWidget *parent)
6049 {
6050 slotEditTempo(parent, m_doc->getComposition().getPosition());
6051 }
6052
6053 void
slotEditTempo(QWidget * parent,timeT atTime)6054 RosegardenMainWindow::slotEditTempo(QWidget *parent, timeT atTime)
6055 {
6056 RG_DEBUG << "slotEditTempo";
6057 m_editTempoController->editTempo(parent, atTime);
6058 }
6059
6060 void
slotEditTimeSignature()6061 RosegardenMainWindow::slotEditTimeSignature()
6062 {
6063 slotEditTimeSignature(this);
6064 }
6065
6066 void
slotEditTimeSignature(timeT atTime)6067 RosegardenMainWindow::slotEditTimeSignature(timeT atTime)
6068 {
6069 slotEditTimeSignature(this, atTime);
6070 }
6071
6072 void
slotEditTimeSignature(QWidget * parent)6073 RosegardenMainWindow::slotEditTimeSignature(QWidget *parent)
6074 {
6075 slotEditTimeSignature(parent, m_doc->getComposition().getPosition());
6076 }
6077
6078 void
slotEditTimeSignature(QWidget * parent,timeT time)6079 RosegardenMainWindow::slotEditTimeSignature(QWidget *parent,
6080 timeT time)
6081 {
6082 m_editTempoController->editTimeSignature(parent, time);
6083 }
6084
6085 void
slotEditTransportTime()6086 RosegardenMainWindow::slotEditTransportTime()
6087 {
6088 slotEditTransportTime(this);
6089 }
6090
6091 void
slotEditTransportTime(QWidget * parent)6092 RosegardenMainWindow::slotEditTransportTime(QWidget *parent)
6093 {
6094 TimeDialog dialog(parent, tr("Move playback pointer to time"),
6095 &m_doc->getComposition(),
6096 m_doc->getComposition().getPosition(),
6097 true);
6098 if (dialog.exec() == QDialog::Accepted) {
6099 m_doc->slotSetPointerPosition(dialog.getTime());
6100 }
6101 }
6102
6103 void
slotChangeZoom(int)6104 RosegardenMainWindow::slotChangeZoom(int)
6105 {
6106 double duration44 = TimeSignature(4, 4).getBarDuration();
6107 double value = double(m_zoomSlider->getCurrentSize());
6108 m_zoomLabel->setText(tr("%1%").arg(duration44 / value));
6109
6110 RG_DEBUG << "slotChangeZoom : zoom size = "
6111 << m_zoomSlider->getCurrentSize();
6112
6113 // initZoomToolbar sets the zoom value. With some old versions of
6114 // Qt3.0, this can cause slotChangeZoom() to be called while the
6115 // view hasn't been initialized yet, so we need to check it's not
6116 // null
6117 //
6118 if (m_view)
6119 m_view->setZoomSize(m_zoomSlider->getCurrentSize());
6120
6121 long newZoom = int(m_zoomSlider->getCurrentSize() * 1000.0);
6122
6123 if (m_doc->getConfiguration().get<Int>
6124 (DocumentConfiguration::ZoomLevel) != newZoom) {
6125
6126 m_doc->getConfiguration().set<Int>
6127 (DocumentConfiguration::ZoomLevel, newZoom);
6128
6129 m_doc->slotDocumentModified();
6130 }
6131 }
6132
6133 void
slotZoomIn()6134 RosegardenMainWindow::slotZoomIn()
6135 {
6136 m_zoomSlider->increment();
6137 }
6138
6139 void
slotZoomOut()6140 RosegardenMainWindow::slotZoomOut()
6141 {
6142 m_zoomSlider->decrement();
6143 }
6144
6145 void
slotAddMarker(timeT time)6146 RosegardenMainWindow::slotAddMarker(timeT time)
6147 {
6148 AddMarkerCommand *command =
6149 new AddMarkerCommand(&m_doc->getComposition(),
6150 time,
6151 qStrToStrUtf8(tr("new marker")),
6152 qStrToStrUtf8(tr("no description")) );
6153
6154 CommandHistory::getInstance()->addCommand(command);
6155 }
6156
6157 void
slotDeleteMarker(int id,timeT time,QString name,QString description)6158 RosegardenMainWindow::slotDeleteMarker(int id, timeT time, QString name, QString description)
6159 {
6160 RemoveMarkerCommand *command =
6161 new RemoveMarkerCommand(&m_doc->getComposition(),
6162 id,
6163 time,
6164 qstrtostr(name),
6165 qstrtostr(description));
6166
6167 CommandHistory::getInstance()->addCommand(command);
6168 }
6169
6170 void
slotDocumentModified(bool m)6171 RosegardenMainWindow::slotDocumentModified(bool m)
6172 {
6173 RG_DEBUG << "slotDocumentModified(" << m << ") - doc path = " << m_doc->getAbsFilePath();
6174
6175 if (!m_doc->getAbsFilePath().isEmpty()) {
6176 slotStateChanged("saved_file_modified", m);
6177 } else {
6178 slotStateChanged("new_file_modified", m);
6179 }
6180
6181 }
6182
6183 void
slotStateChanged(QString s,bool noReverse)6184 RosegardenMainWindow::slotStateChanged(QString s,
6185 bool noReverse)
6186 {
6187 if (noReverse) {
6188 enterActionState(s);
6189 } else {
6190 leaveActionState(s);
6191 }
6192 }
6193
6194 void
updateActions()6195 RosegardenMainWindow::updateActions()
6196 {
6197 QSettings settings;
6198 settings.beginGroup(GeneralOptionsConfigGroup);
6199 bool enableEditingDuringPlayback =
6200 settings.value("enableEditingDuringPlayback", false).toBool();
6201
6202
6203 // not_playing && have_selection
6204
6205 findAction("delete")->setEnabled(
6206 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6207 findAction("edit_cut")->setEnabled(
6208 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6209 // ??? This doesn't prevent Ctrl+Resize on the edges of a Segment. CRASH.
6210 findAction("rescale")->setEnabled(m_notPlaying && m_haveSelection);
6211 findAction("auto_split")->setEnabled(
6212 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6213 findAction("split_by_pitch")->setEnabled(
6214 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6215 findAction("split_by_recording")->setEnabled(
6216 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6217 // ??? This doesn't prevent the split tool from causing a CRASH.
6218 findAction("split_at_time")->setEnabled(
6219 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6220 findAction("split_by_drum")->setEnabled(
6221 (enableEditingDuringPlayback || m_notPlaying) && m_haveSelection);
6222 findAction("join_segments")->setEnabled(m_notPlaying && m_haveSelection);
6223
6224
6225 // not_playing && have_range
6226
6227 findAction("cut_range")->setEnabled(m_notPlaying && m_haveRange);
6228 }
6229
6230 void
enterActionState(QString stateName)6231 RosegardenMainWindow::enterActionState(QString stateName)
6232 {
6233 if (stateName == "not_playing") {
6234 m_notPlaying = true;
6235 CommandHistory::getInstance()->enableUndo(true);
6236 }
6237
6238 if (stateName == "have_selection")
6239 m_haveSelection = true;
6240 if (stateName == "have_range")
6241 m_haveRange = true;
6242
6243 updateActions();
6244
6245 // Let baseclass take a shot as well.
6246 ActionFileClient::enterActionState(stateName);
6247 }
6248
6249 void
leaveActionState(QString stateName)6250 RosegardenMainWindow::leaveActionState(QString stateName)
6251 {
6252 if (stateName == "not_playing") {
6253 m_notPlaying = false;
6254
6255 QSettings settings;
6256 settings.beginGroup(GeneralOptionsConfigGroup);
6257 bool enableEditingDuringPlayback =
6258 settings.value("enableEditingDuringPlayback", false).toBool();
6259
6260 if (!enableEditingDuringPlayback)
6261 CommandHistory::getInstance()->enableUndo(false);
6262 }
6263
6264 if (stateName == "have_selection")
6265 m_haveSelection = false;
6266 if (stateName == "have_range")
6267 m_haveRange = false;
6268
6269 updateActions();
6270
6271 // Let baseclass take a shot as well.
6272 ActionFileClient::leaveActionState(stateName);
6273 }
6274
6275
6276 void
slotTestClipboard()6277 RosegardenMainWindow::slotTestClipboard()
6278 {
6279 // use qt4:
6280 //QClipboard *clipboard = QApplication::clipboard();
6281 //QString originalText = clipboard->text();
6282
6283 if (m_clipboard->isEmpty()) {
6284 leaveActionState("have_clipboard"); //@@@ JAS orig. KXMLGUIClient::StateReverse
6285 leaveActionState("have_clipboard_single_segment"); //@@@ JAS orig. KXMLGUIClient::StateReverse
6286 //leaveActionState("have_clipboard_can_paste_as_links");
6287 } else {
6288 enterActionState("have_clipboard");
6289 if (m_clipboard->isSingleSegment()) { //@@@ JAS orig. KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse
6290 enterActionState("have_clipboard_single_segment");
6291 } else {
6292 leaveActionState("have_clipboard_single_segment");
6293 }
6294 //if (m_clipboard->getCanPasteAsLinks()) {
6295 // enterActionState("have_clipboard_can_paste_as_links");
6296 //} else {
6297 // leaveActionState("have_clipboard_can_paste_as_links");
6298 //}
6299 }
6300 }
6301
6302 void
plugShortcuts(QWidget * widget,QShortcut *)6303 RosegardenMainWindow::plugShortcuts(QWidget *widget, QShortcut * /*acc*/)
6304 {
6305 // ??? This routine now does nothing other than set up the transport.
6306 // It shouldn't be called by anyone and it should be inlined into
6307 // createAndSetupTransport().
6308
6309 //
6310 // Shortcuts are now defined in *.rc files.
6311 //
6312 // new qt4:
6313 // QWidget* sc_parent = this;
6314 // QShortcut* sc_tmp;
6315
6316 // types:Qt::WidgetShortcut, Qt::ApplicationShortcut, Qt::WindowShortcut
6317
6318 /**
6319 * Shortcuts for showing/hiding Transport.
6320 */
6321 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_T), sc_parent, 0, 0, Qt::ApplicationShortcut);
6322 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotToggleTransportVisibility()));
6323
6324 /**
6325 * Shortcuts for playing.
6326 */
6327 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_MediaPlay), sc_parent, 0, 0, Qt::ApplicationShortcut);
6328 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotPlay()));
6329
6330 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_Enter), sc_parent, 0, 0, Qt::ApplicationShortcut);
6331 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotPlay()));
6332
6333 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_Return + Qt::CTRL), sc_parent, 0, 0, Qt::ApplicationShortcut);
6334 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotPlay()));
6335
6336 /**
6337 * Shortcuts for stopping.
6338 */
6339 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_MediaStop), sc_parent, 0, 0, Qt::ApplicationShortcut);
6340 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotStop()));
6341
6342 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_Insert), sc_parent, 0, 0, Qt::ApplicationShortcut);
6343 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotStop()));
6344
6345 /**
6346 * Shortcuts for fast forwarding.
6347 */
6348 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_MediaNext), sc_parent, 0, 0, Qt::ApplicationShortcut);
6349 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotFastforward()));
6350
6351 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_PageDown), sc_parent, 0, 0, Qt::ApplicationShortcut);
6352 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotFastforward()));
6353
6354 /**
6355 * Shortcuts for rewinding.
6356 */
6357 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_MediaPrevious), sc_parent, 0, 0, Qt::ApplicationShortcut);
6358 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotRewind()));
6359
6360 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_PageUp), sc_parent, 0, 0, Qt::ApplicationShortcut);
6361 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotRewind()));
6362
6363 /**
6364 * Shortcuts for fast forwarding to end.
6365 */
6366 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_End), sc_parent, 0, 0, Qt::ApplicationShortcut);
6367 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotFastForwardToEnd()));
6368
6369 /**
6370 * Shortcuts for rewinding to beginning.
6371 */
6372 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_Home), sc_parent, 0, 0, Qt::ApplicationShortcut);
6373 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotRewindToBeginning()));
6374
6375 /**
6376 * Shortcuts for recording.
6377 */
6378
6379 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_MediaRecord), sc_parent, 0, 0, Qt::ApplicationShortcut);
6380 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotToggleRecord()));
6381
6382 // sc_tmp = new QShortcut(QKeySequence(Qt::Key_Space), sc_parent, 0, 0, Qt::ApplicationShortcut);
6383 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotToggleRecord()));
6384
6385
6386
6387 TransportDialog *transport =
6388 dynamic_cast<TransportDialog*>(widget);
6389
6390 if (transport) {
6391
6392 // Should not use just Qt::Key_M, because it is used for "Open in MatrixView"
6393 // Are these needed, because we also have "Ctrl + 1" ... "Ctrl + 9" and "1" ... "9"
6394 // sc_tmp = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_M), sc_parent, 0, 0, Qt::ApplicationShortcut);
6395 //sc_tmp = m_jumpToQuickMarkerAction->shortcut();
6396 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotJumpToQuickMarker()));
6397
6398 // sc_tmp = new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M), sc_parent, 0, 0, Qt::ApplicationShortcut);
6399 //sc_tmp = m_setQuickMarkerAction->shortcut();
6400 // connect(sc_tmp, SIGNAL(activated()), this, SLOT(slotSetQuickMarker()));
6401
6402
6403 connect(transport->PlayButton(),
6404 &QAbstractButton::clicked,
6405 this,
6406 &RosegardenMainWindow::slotPlay);
6407
6408 connect(transport->StopButton(),
6409 &QAbstractButton::clicked,
6410 this,
6411 &RosegardenMainWindow::slotStop);
6412
6413 connect(transport->FfwdButton(),
6414 &QAbstractButton::clicked,
6415 this, &RosegardenMainWindow::slotFastforward);
6416
6417 connect(transport->RewindButton(),
6418 &QAbstractButton::clicked,
6419 this,
6420 &RosegardenMainWindow::slotRewind);
6421
6422 connect(transport->RecordButton(),
6423 &QAbstractButton::clicked,
6424 this,
6425 &RosegardenMainWindow::slotRecord);
6426
6427 connect(transport->RewindEndButton(),
6428 &QAbstractButton::clicked,
6429 this,
6430 &RosegardenMainWindow::slotRewindToBeginning);
6431
6432 connect(transport->FfwdEndButton(),
6433 &QAbstractButton::clicked,
6434 this,
6435 &RosegardenMainWindow::slotFastForwardToEnd);
6436
6437 connect(transport->MetronomeButton(),
6438 &QAbstractButton::clicked,
6439 this,
6440 &RosegardenMainWindow::slotToggleMetronome);
6441
6442 connect(transport->SoloButton(),
6443 &QAbstractButton::clicked,
6444 this,
6445 &RosegardenMainWindow::slotToggleSolo);
6446
6447 connect(transport->TimeDisplayButton(),
6448 &QAbstractButton::clicked,
6449 this,
6450 &RosegardenMainWindow::slotRefreshTimeDisplay);
6451
6452 connect(transport->ToEndButton(),
6453 &QAbstractButton::clicked,
6454 this, &RosegardenMainWindow::slotRefreshTimeDisplay);
6455 }
6456 }
6457
6458 QVector<QString>
createRecordAudioFiles(const QVector<InstrumentId> & recordInstruments)6459 RosegardenMainWindow::createRecordAudioFiles(const QVector<InstrumentId> &recordInstruments)
6460 {
6461 QVector<QString> qv;
6462
6463 // Refuse to start recording to "Untitled" for the user's own good. (Want
6464 // proof? Look at my 3.9G ~/rosegarden full of meaningless files not
6465 // associated with anything particular. The ones since 2005, at least I
6466 // know the date I recorded them, but that isn't very helpful.)
6467 //
6468 // I weighed the user inconvenience carefully, and decided to do this to
6469 // maximize the value of the new filenames that include the title and
6470 // instrument alias. It's worthelss if they all say "Untitled" and there's
6471 // no way to make "pick this and set that up before you hit this" intuitive,
6472 // so I've had to throw instructions in their face.
6473 if (m_doc->getTitle() == tr("Untitled")) {
6474 QMessageBox::information(this,
6475 tr("Rosegarden"),
6476 // TRANSLATOR: you may change "doc:audio-filename-en" to a page in your
6477 // language if you wish. The n in <i>n</i>.wav refers to an unknown
6478 // number, such as might be used in a mathematical equation
6479 tr("<qt><p>You must choose a filename for this composition before recording audio.</p><p>Audio files will be saved to <b>%1</b> as <b>rg-[<i>filename</i>]-[<i>instrument</i>]-<i>date</i>_<i>time</i>-<i>n</i>.wav</b>. You may wish to rename audio instruments before recording as well. For more information, please see the <a style=\"color:gold\" href=\"http://www.rosegardenmusic.com/wiki/doc:audio-filenames-en\">Rosegarden Wiki</a>.</p></qt>")
6480 .arg(m_doc->getAudioFileManager().getAudioPath()),
6481 QMessageBox::Ok,
6482 QMessageBox::Ok);
6483
6484 slotFileSave();
6485
6486 //!!!
6487 // We should return and cancel here, but the way all of this is strung
6488 // together makes that tricky. Let's see if we ever get a bug about "I
6489 // tried to record audio, and it told me I had to choose a new name, and
6490 // I decided to test my boundaries as a user and canceled the dialog,
6491 // and recording started anyway!" If so, this is the place! Anyway,
6492 // the aim here is to be helpful, not draconian.
6493 }
6494
6495 for (int i = 0; i < recordInstruments.size(); ++i) {
6496 AudioFile *aF = nullptr;
6497 try {
6498 std::string alias = "";
6499 Instrument *ins = m_doc->getStudio().getInstrumentById(recordInstruments[i]);
6500 if (ins) alias = ins->getAlias();
6501 aF = m_doc->getAudioFileManager().createRecordingAudioFile(m_doc->getTitle(),
6502 strtoqstr(alias));
6503 if (aF) {
6504 // createRecordingAudioFile doesn't actually write to the disk,
6505 // and in principle it shouldn't fail
6506 qv.push_back(aF->getFilename());
6507 m_doc->addRecordAudioSegment(recordInstruments[i],
6508 aF->getId());
6509 } else {
6510 RG_WARNING << "createRecordAudioFiles(): WARNING: Failed to create recording audio file";
6511 return qv;
6512 }
6513 } catch (const AudioFileManager::BadAudioPathException &e) {
6514 delete aF;
6515 RG_WARNING << "createRecordAudioFiles(): ERROR: Failed to create recording audio file: " << e.getMessage();
6516 return qv;
6517 }
6518 }
6519 return qv;
6520 }
6521
6522 QString
getAudioFilePath()6523 RosegardenMainWindow::getAudioFilePath()
6524 {
6525 return m_doc->getAudioFileManager().getAudioPath();
6526 }
6527
6528 QVector<InstrumentId>
getArmedInstruments()6529 RosegardenMainWindow::getArmedInstruments()
6530 {
6531 std::set
6532 <InstrumentId> iid;
6533
6534 const Composition::recordtrackcontainer &tr =
6535 m_doc->getComposition().getRecordTracks();
6536
6537 for (Composition::recordtrackcontainer::const_iterator i =
6538 tr.begin(); i != tr.end(); ++i) {
6539 TrackId tid = (*i);
6540 Track *track = m_doc->getComposition().getTrackById(tid);
6541 if (track) {
6542 iid.insert(track->getInstrument());
6543 } else {
6544 RG_WARNING << "getArmedInstruments(): Warning: Armed track " << tid << " not found in Composition";
6545 }
6546 }
6547
6548 QVector<InstrumentId> iv;
6549 for (std::set
6550 <InstrumentId>::iterator ii = iid.begin();
6551 ii != iid.end(); ++ii) {
6552 iv.push_back(*ii);
6553 }
6554 return iv;
6555 }
6556
6557 void
showError(QString error)6558 RosegardenMainWindow::showError(QString error)
6559 {
6560 StartupLogo::hideIfStillThere();
6561
6562 // This is principally used for return values from DSSI plugin
6563 // configure() calls. It seems some plugins return a string
6564 // telling you when everything's OK, as well as error strings, but
6565 // dssi.h does make it reasonably clear that configure() should
6566 // only return a string when there is actually a problem, so we're
6567 // going to stick with a warning dialog here rather than an
6568 // information one
6569
6570 QMessageBox::warning(nullptr, tr("Rosegarden"), error);
6571 }
6572
6573 void
slotAudioManager()6574 RosegardenMainWindow::slotAudioManager()
6575 {
6576 if (m_audioManagerDialog) {
6577 m_audioManagerDialog->show();
6578 m_audioManagerDialog->raise();
6579 m_audioManagerDialog->activateWindow();
6580 return ;
6581 }
6582
6583 m_audioManagerDialog =
6584 new AudioManagerDialog(this, m_doc);
6585
6586 connect(m_audioManagerDialog,
6587 SIGNAL(playAudioFile(AudioFileId,
6588 const RealTime &,
6589 const RealTime&)),
6590 SLOT(slotPlayAudioFile(AudioFileId,
6591 const RealTime &,
6592 const RealTime &)));
6593
6594 connect(m_audioManagerDialog,
6595 SIGNAL(addAudioFile(AudioFileId)),
6596 SLOT(slotAddAudioFile(AudioFileId)));
6597
6598 connect(m_audioManagerDialog,
6599 &AudioManagerDialog::deleteAudioFile,
6600 this, &RosegardenMainWindow::slotDeleteAudioFile);
6601
6602 //
6603 // Sync segment selection between audio man. dialog and main window
6604 //
6605
6606 // from dialog to us...
6607 connect(m_audioManagerDialog,
6608 &AudioManagerDialog::segmentsSelected,
6609 m_view,
6610 &RosegardenMainViewWidget::slotPropagateSegmentSelection);
6611
6612 // and from us to dialog
6613 connect(this, &RosegardenMainWindow::segmentsSelected,
6614 m_audioManagerDialog,
6615 &AudioManagerDialog::slotSegmentSelection);
6616
6617
6618 connect(m_audioManagerDialog,
6619 &AudioManagerDialog::deleteSegments,
6620 this, &RosegardenMainWindow::slotDeleteSegments);
6621
6622 connect(m_audioManagerDialog,
6623 SIGNAL(insertAudioSegment(AudioFileId,
6624 const RealTime&,
6625 const RealTime&)),
6626 m_view,
6627 SLOT(slotAddAudioSegmentDefaultPosition(AudioFileId,
6628 const RealTime&,
6629 const RealTime&)));
6630 connect(m_audioManagerDialog,
6631 &AudioManagerDialog::cancelPlayingAudioFile,
6632 this, &RosegardenMainWindow::slotCancelAudioPlayingFile);
6633
6634 connect(m_audioManagerDialog,
6635 &AudioManagerDialog::deleteAllAudioFiles,
6636 this, &RosegardenMainWindow::slotDeleteAllAudioFiles);
6637
6638 // Make sure we know when the audio man. dialog is closing
6639 //
6640 connect(m_audioManagerDialog,
6641 &AudioManagerDialog::closing,
6642 this, &RosegardenMainWindow::slotAudioManagerClosed);
6643
6644 // And that it goes away when the current document is changing
6645 //
6646 connect(this, &RosegardenMainWindow::documentAboutToChange,
6647 m_audioManagerDialog, &QWidget::close);
6648
6649 m_audioManagerDialog->setAudioSubsystemStatus(
6650 m_seqManager->getSoundDriverStatus() & AUDIO_OK);
6651
6652 plugShortcuts(m_audioManagerDialog,
6653 m_audioManagerDialog->getShortcuts());
6654
6655 m_audioManagerDialog->show();
6656 }
6657
6658 void
slotPlayAudioFile(unsigned int id,const RealTime & startTime,const RealTime & duration)6659 RosegardenMainWindow::slotPlayAudioFile(unsigned int id,
6660 const RealTime &startTime,
6661 const RealTime &duration)
6662 {
6663 AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
6664
6665 if (aF == nullptr)
6666 return ;
6667
6668 MappedEvent mE(m_doc->getStudio().
6669 getAudioPreviewInstrument(),
6670 id,
6671 RealTime(-120, 0),
6672 duration, // duration
6673 startTime); // start index
6674
6675 StudioControl::sendMappedEvent(mE);
6676
6677 }
6678
6679 void
slotAddAudioFile(unsigned int id)6680 RosegardenMainWindow::slotAddAudioFile(unsigned int id)
6681 {
6682 AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
6683
6684 if (aF == nullptr) return;
6685
6686 int result = RosegardenSequencer::getInstance()->
6687 addAudioFile(aF->getFilename(), aF->getId());
6688
6689 if (!result) {
6690 QMessageBox::critical(this, tr("Rosegarden"), tr("Sequencer failed to add audio file %1").arg(aF->getFilename()));
6691 }
6692 }
6693
6694 void
slotDeleteAudioFile(unsigned int id)6695 RosegardenMainWindow::slotDeleteAudioFile(unsigned int id)
6696 {
6697 if (m_doc->getAudioFileManager().removeFile(id) == false)
6698 return ;
6699
6700 int result = RosegardenSequencer::getInstance()->removeAudioFile(id);
6701
6702 if (!result) {
6703 QMessageBox::critical(this, tr("Rosegarden"), tr("Sequencer failed to remove audio file id %1").arg(id));
6704 }
6705 }
6706
6707 void
slotDeleteSegments(const SegmentSelection & selection)6708 RosegardenMainWindow::slotDeleteSegments(const SegmentSelection &selection)
6709 {
6710 m_view->slotPropagateSegmentSelection(selection);
6711 slotDeleteSelectedSegments();
6712 }
6713
6714 void
slotCancelAudioPlayingFile(AudioFileId id)6715 RosegardenMainWindow::slotCancelAudioPlayingFile(AudioFileId id)
6716 {
6717 AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
6718
6719 if (aF == nullptr)
6720 return ;
6721
6722 MappedEvent mE(m_doc->getStudio().
6723 getAudioPreviewInstrument(),
6724 MappedEvent::AudioCancel,
6725 id);
6726
6727 StudioControl::sendMappedEvent(mE);
6728 }
6729
6730 void
slotDeleteAllAudioFiles()6731 RosegardenMainWindow::slotDeleteAllAudioFiles()
6732 {
6733 m_doc->getAudioFileManager().clear();
6734
6735 // Clear at the sequencer
6736 //
6737 RosegardenSequencer::getInstance()->clearAllAudioFiles();
6738 }
6739
6740 void
slotRepeatingSegments()6741 RosegardenMainWindow::slotRepeatingSegments()
6742 {
6743 m_view->getTrackEditor()->turnRepeatingSegmentToRealCopies();
6744 }
6745
6746 void
slotLinksToCopies()6747 RosegardenMainWindow::slotLinksToCopies()
6748 {
6749 m_view->getTrackEditor()->turnLinkedSegmentsToRealCopies();
6750 }
6751
6752 void
slotRelabelSegments()6753 RosegardenMainWindow::slotRelabelSegments()
6754 {
6755 if (!m_view->haveSelection())
6756 return ;
6757
6758 SegmentSelection selection(m_view->getSelection());
6759 QString editLabel;
6760
6761 if (selection.size() == 0)
6762 return ;
6763 else if (selection.size() == 1)
6764 editLabel = tr("Modify Segment label");
6765 else
6766 editLabel = tr("Modify Segments label");
6767
6768 TmpStatusMsg msg(tr("Relabelling selection..."), this);
6769
6770 // Generate label
6771 QString label = strtoqstr((*selection.begin())->getLabel());
6772
6773 for (SegmentSelection::iterator i = selection.begin();
6774 i != selection.end(); ++i) {
6775 if (strtoqstr((*i)->getLabel()) != label)
6776 label = "";
6777 }
6778
6779 bool ok = false;
6780
6781 QString newLabel = InputDialog::getText(dynamic_cast<QWidget*>(this), tr("Input"), tr("Enter new label"), LineEdit::Normal, QString(), &ok);
6782
6783 if (ok) {
6784 CommandHistory::getInstance()->addCommand
6785 (new SegmentLabelCommand(selection, newLabel));
6786 m_view->getTrackEditor()->getCompositionView()->slotUpdateAll();
6787 }
6788 }
6789
6790 void
slotTransposeSegments()6791 RosegardenMainWindow::slotTransposeSegments()
6792 {
6793 if (!m_view->haveSelection()) return ;
6794
6795 IntervalDialog intervalDialog(this, true, true);
6796 int ok = intervalDialog.exec();
6797
6798 int semitones = intervalDialog.getChromaticDistance();
6799 int steps = intervalDialog.getDiatonicDistance();
6800
6801 if (!ok || (semitones == 0 && steps == 0)) return;
6802
6803 CommandHistory::getInstance()->addCommand(
6804 new SegmentTransposeCommand(
6805 m_view->getSelection(),
6806 intervalDialog.getChangeKey(),
6807 steps,
6808 semitones,
6809 intervalDialog.getTransposeSegmentBack()));
6810 }
6811
6812 void
slotTransposeSemitones()6813 RosegardenMainWindow::slotTransposeSemitones()
6814 {
6815 QSettings settings;
6816 settings.beginGroup(GeneralOptionsConfigGroup);
6817
6818 int lastTranspose = settings.value("main_last_transpose", 0).toInt() ;
6819
6820 bool ok = false;
6821 int semitones = QInputDialog::getInt(
6822 this, // parent
6823 tr("Transpose"), // title
6824 tr("By number of semitones: "), // label
6825 lastTranspose, // value
6826 -127, // minValue
6827 127, // maxValue
6828 1, // step
6829 &ok);
6830
6831 if (!ok || semitones == 0)
6832 return;
6833
6834 settings.setValue("main_last_transpose", semitones);
6835
6836 SegmentSelection selection = m_view->getSelection();
6837
6838 MacroCommand *command = new MacroCommand(TransposeCommand::getGlobalName());
6839
6840 // for each selected Segment
6841 for (SegmentSelection::iterator i = selection.begin();
6842 i != selection.end();
6843 ++i) {
6844
6845 Segment &segment = **i;
6846
6847 // Create an EventSelection with all events in the Segment.
6848 // ??? MEMORY LEAK (confirmed) TransposeCommand stores a pointer
6849 // to this, so we can't delete it. Annoying.
6850 // Make TransposeCommand take a QSharedPointer<EventSelection>.
6851 // Then it could hang on to it as long as it likes. It can even
6852 // call reset() when it is really finished if it wants.
6853 EventSelection *eventSelection = new EventSelection(
6854 segment,
6855 segment.getStartTime(),
6856 segment.getEndMarkerTime());
6857
6858 command->addCommand(new TransposeCommand(semitones, *eventSelection));
6859
6860 }
6861
6862 m_view->slotAddCommandToHistory(command);
6863 }
6864
6865 void
slotChangeCompositionLength()6866 RosegardenMainWindow::slotChangeCompositionLength()
6867 {
6868 CompositionLengthDialog dialog(this, &m_doc->getComposition());
6869
6870 if (dialog.exec() == QDialog::Accepted) {
6871 ChangeCompositionLengthCommand *command
6872 = new ChangeCompositionLengthCommand(
6873 &m_doc->getComposition(),
6874 dialog.getStartMarker(),
6875 dialog.getEndMarker(),
6876 dialog.autoExpandEnabled());
6877
6878 m_view->getTrackEditor()->getCompositionView()->deleteCachedPreviews();
6879 CommandHistory::getInstance()->addCommand(command);
6880
6881 // If you change the composition length to 50 while the pointer is still
6882 // at 100 and save, the pointer position is saved, and the document
6883 // re-extends itself to 100 when you reload. I was going to bother to
6884 // do some nice "if the pointer is beyond the new end then move the
6885 // pointer" logic here, but it's more than a oneliner, and so we'll just
6886 // rewind the damn cursor to the beginning as insurance against this,
6887 // and dust off our hands.
6888 slotRewindToBeginning();
6889 }
6890 }
6891
6892 void
slotManageMIDIDevices()6893 RosegardenMainWindow::slotManageMIDIDevices()
6894 {
6895 if (m_deviceManager) {
6896 m_deviceManager->show();
6897 m_deviceManager->raise();
6898 m_deviceManager->activateWindow();
6899 return;
6900 }
6901 if (!m_deviceManager) {
6902
6903 m_deviceManager = new DeviceManagerDialog(this, getDocument());
6904
6905 connect(m_deviceManager, SIGNAL(editBanks(DeviceId)),
6906 this, SLOT(slotEditBanks(DeviceId)));
6907
6908 connect(m_deviceManager.data(), &DeviceManagerDialog::editControllers,
6909 this, &RosegardenMainWindow::slotEditControlParameters);
6910
6911 connect(this, &RosegardenMainWindow::documentAboutToChange,
6912 m_deviceManager.data(), &QWidget::close);
6913
6914 if (m_midiMixer) {
6915 connect(m_deviceManager.data(), &DeviceManagerDialog::deviceNamesChanged,
6916 m_midiMixer, &MidiMixerWindow::slotSynchronise);
6917 }
6918
6919 connect(m_deviceManager.data(), &DeviceManagerDialog::deviceNamesChanged,
6920 m_trackParameterBox, &TrackParameterBox::devicesChanged);
6921 }
6922
6923 QToolButton *tb = findChild<QToolButton*>("manage_midi_devices");
6924 if(tb){
6925 tb->setDown(true);
6926 }
6927
6928 // Prevent the device manager from being resized larger or smaller than the
6929 // size it first renders at. I tried to correct this in Designer, but long
6930 // story short, the only way I could get the form preview to behave properly
6931 // was to fix its size in the .ui file. That can't possibly work well, so
6932 // we'll try fixing its size here, after it has been calculated against the
6933 // user environment.
6934 QSize renderedSize = m_deviceManager->size();
6935 m_deviceManager->setMinimumSize(renderedSize);
6936 m_deviceManager->setMaximumSize(renderedSize);
6937 m_deviceManager->show();
6938 }
6939
6940 void
slotManageSynths()6941 RosegardenMainWindow::slotManageSynths()
6942 {
6943 if (m_synthManager) {
6944 m_synthManager->show();
6945 m_synthManager->raise();
6946 m_synthManager->activateWindow();
6947 return ;
6948 }
6949
6950 m_synthManager = new SynthPluginManagerDialog(this, m_doc, m_pluginGUIManager);
6951
6952 connect(m_synthManager, &SynthPluginManagerDialog::closing,
6953 this, &RosegardenMainWindow::slotSynthPluginManagerClosed);
6954
6955 connect(this, &RosegardenMainWindow::documentAboutToChange,
6956 m_synthManager, &QWidget::close);
6957
6958 connect(m_synthManager,
6959 &SynthPluginManagerDialog::pluginSelected,
6960 this,
6961 &RosegardenMainWindow::slotPluginSelected);
6962
6963 connect(m_synthManager,
6964 &SynthPluginManagerDialog::showPluginDialog,
6965 this,
6966 &RosegardenMainWindow::slotShowPluginDialog);
6967
6968 connect(m_synthManager,
6969 &SynthPluginManagerDialog::showPluginGUI,
6970 this,
6971 &RosegardenMainWindow::slotShowPluginGUI);
6972
6973 m_synthManager->show();
6974 }
6975
6976 void
slotOpenAudioMixer()6977 RosegardenMainWindow::slotOpenAudioMixer()
6978 {
6979 if (m_audioMixerWindow2) {
6980 m_audioMixerWindow2->activateWindow();
6981 m_audioMixerWindow2->raise();
6982 return;
6983 }
6984
6985 m_audioMixerWindow2 = new AudioMixerWindow2(this);
6986
6987 return;
6988 }
6989
6990 void
slotOpenMidiMixer()6991 RosegardenMainWindow::slotOpenMidiMixer()
6992 {
6993 if (m_midiMixer) {
6994 m_midiMixer->show();
6995 m_midiMixer->raise();
6996 m_midiMixer->activateWindow();
6997 return ;
6998 }
6999
7000 m_midiMixer = new MidiMixerWindow(this, m_doc);
7001
7002 connect(m_midiMixer, &MixerWindow::closing,
7003 this, &RosegardenMainWindow::slotMidiMixerClosed);
7004
7005 connect(this, &RosegardenMainWindow::documentAboutToChange,
7006 m_midiMixer, &QWidget::close);
7007
7008 connect(m_midiMixer, &MidiMixerWindow::play,
7009 this, &RosegardenMainWindow::slotPlay);
7010 connect(m_midiMixer, &MidiMixerWindow::stop,
7011 this, &RosegardenMainWindow::slotStop);
7012 connect(m_midiMixer, &MidiMixerWindow::fastForwardPlayback,
7013 this, &RosegardenMainWindow::slotFastforward);
7014 connect(m_midiMixer, &MidiMixerWindow::rewindPlayback,
7015 this, &RosegardenMainWindow::slotRewind);
7016 connect(m_midiMixer, &MidiMixerWindow::fastForwardPlaybackToEnd,
7017 this, &RosegardenMainWindow::slotFastForwardToEnd);
7018 connect(m_midiMixer, &MidiMixerWindow::rewindPlaybackToBeginning,
7019 this, &RosegardenMainWindow::slotRewindToBeginning);
7020 connect(m_midiMixer, &MidiMixerWindow::record,
7021 this, &RosegardenMainWindow::slotRecord);
7022 connect(m_midiMixer, &MidiMixerWindow::panic,
7023 this, &RosegardenMainWindow::slotPanic);
7024
7025 plugShortcuts(m_midiMixer, m_midiMixer->getShortcuts());
7026
7027 m_midiMixer->show();
7028 }
7029
7030 void
slotEditControlParameters(DeviceId device)7031 RosegardenMainWindow::slotEditControlParameters(DeviceId device)
7032 {
7033 for (std::set
7034 <ControlEditorDialog *>::iterator i = m_controlEditors.begin();
7035 i != m_controlEditors.end(); ++i) {
7036 if ((*i)->getDevice() == device) {
7037 (*i)->show();
7038 (*i)->raise();
7039 (*i)->activateWindow();
7040 return ;
7041 }
7042 }
7043
7044 ControlEditorDialog *controlEditor = new ControlEditorDialog(this, m_doc,
7045 device);
7046 m_controlEditors.insert(controlEditor);
7047
7048 RG_DEBUG << "inserting control editor dialog, have " << m_controlEditors.size() << " now";
7049
7050 connect(controlEditor, &ControlEditorDialog::closing,
7051 this, &RosegardenMainWindow::slotControlEditorClosed);
7052
7053 connect(this, &RosegardenMainWindow::documentAboutToChange,
7054 controlEditor, &QWidget::close);
7055
7056 connect(m_doc, SIGNAL(devicesResyncd()),
7057 controlEditor, SLOT(slotUpdate()));
7058
7059
7060 controlEditor->resize(780, 360);
7061 controlEditor->move(50, 80);
7062 controlEditor->show();
7063 }
7064
7065 void
slotEditBanks()7066 RosegardenMainWindow::slotEditBanks()
7067 {
7068 slotEditBanks(Device::NO_DEVICE);
7069 }
7070
7071 void
slotEditBanks(DeviceId device)7072 RosegardenMainWindow::slotEditBanks(DeviceId device)
7073 {
7074 if (m_bankEditor) {
7075 if (device != Device::NO_DEVICE)
7076 m_bankEditor->setCurrentDevice(device);
7077 m_bankEditor->show();
7078 m_bankEditor->raise();
7079 m_bankEditor->activateWindow();
7080 return ;
7081 }
7082
7083 m_bankEditor = new BankEditorDialog(this, m_doc, device);
7084
7085 connect(m_bankEditor, &BankEditorDialog::closing,
7086 this, &RosegardenMainWindow::slotBankEditorClosed);
7087
7088 connect(this, &RosegardenMainWindow::documentAboutToChange,
7089 m_bankEditor, &BankEditorDialog::slotFileClose);
7090
7091 // Cheating way of updating the track/instrument list
7092 //
7093 connect(m_bankEditor, &BankEditorDialog::deviceNamesChanged,
7094 m_view, &RosegardenMainViewWidget::slotSynchroniseWithComposition);
7095
7096 // Assume a m_device manager at this point, but check
7097 // Need to refresh the device manager as well
7098 connect(m_bankEditor, &BankEditorDialog::deviceNamesChanged,
7099 m_deviceManager.data(), &DeviceManagerDialog::slotResyncDevicesReceived);
7100 m_bankEditor->show();
7101
7102 connect(m_bankEditor, &BankEditorDialog::deviceNamesChanged,
7103 m_trackParameterBox, &TrackParameterBox::devicesChanged);
7104 }
7105
7106 void
slotManageTriggerSegments()7107 RosegardenMainWindow::slotManageTriggerSegments()
7108 {
7109 if (m_triggerSegmentManager) {
7110 m_triggerSegmentManager->show();
7111 m_triggerSegmentManager->raise();
7112 m_triggerSegmentManager->activateWindow();
7113 return ;
7114 }
7115
7116 m_triggerSegmentManager = new TriggerSegmentManager(this, m_doc);
7117
7118 connect(m_triggerSegmentManager, &TriggerSegmentManager::closing,
7119 this, &RosegardenMainWindow::slotTriggerManagerClosed);
7120
7121 connect(m_triggerSegmentManager, &TriggerSegmentManager::editTriggerSegment,
7122 m_view, &RosegardenMainViewWidget::slotEditTriggerSegment);
7123
7124 m_triggerSegmentManager->show();
7125 }
7126
7127 void
slotTriggerManagerClosed()7128 RosegardenMainWindow::slotTriggerManagerClosed()
7129 {
7130 RG_DEBUG << "slotTriggerManagerClosed()";
7131
7132 m_triggerSegmentManager = nullptr;
7133 }
7134
7135 void
slotEditMarkers()7136 RosegardenMainWindow::slotEditMarkers()
7137 {
7138 if (m_markerEditor) {
7139 m_markerEditor->show();
7140 m_markerEditor->raise();
7141 m_markerEditor->activateWindow();
7142 return ;
7143 }
7144
7145 m_markerEditor = new MarkerEditor(this, m_doc);
7146
7147 connect(m_markerEditor, &MarkerEditor::closing,
7148 this, &RosegardenMainWindow::slotMarkerEditorClosed);
7149
7150 connect(m_markerEditor, &MarkerEditor::jumpToMarker,
7151 m_doc, &RosegardenDocument::slotSetPointerPosition);
7152
7153 plugShortcuts(m_markerEditor, m_markerEditor->getShortcuts());
7154
7155 m_markerEditor->show();
7156 }
7157
7158 void
slotMarkerEditorClosed()7159 RosegardenMainWindow::slotMarkerEditorClosed()
7160 {
7161 RG_DEBUG << "slotMarkerEditorClosed()";
7162
7163 m_markerEditor = nullptr;
7164 }
7165
7166 void
slotEditTempos(timeT t)7167 RosegardenMainWindow::slotEditTempos(timeT t)
7168 {
7169 if (m_tempoView) {
7170 m_tempoView->show();
7171 m_tempoView->raise();
7172 m_tempoView->activateWindow();
7173 return ;
7174 }
7175
7176 m_tempoView = new TempoView(m_doc, getView(), m_editTempoController, t);
7177
7178 connect(m_tempoView, &TempoView::closing,
7179 this, &RosegardenMainWindow::slotTempoViewClosed);
7180
7181 connect(m_tempoView, &EditViewBase::saveFile, this, &RosegardenMainWindow::slotFileSave);
7182
7183 plugShortcuts(m_tempoView, m_tempoView->getShortcuts());
7184
7185 m_tempoView->show();
7186 }
7187
7188 void
slotTempoViewClosed()7189 RosegardenMainWindow::slotTempoViewClosed()
7190 {
7191 RG_DEBUG << "slotTempoViewClosed()";
7192
7193 m_tempoView = nullptr;
7194 }
7195
7196 void
slotControlEditorClosed()7197 RosegardenMainWindow::slotControlEditorClosed()
7198 {
7199 RG_DEBUG << "slotControlEditorClosed()";
7200
7201 // Make sure changes get to the UI.
7202 uiUpdateKludge();
7203
7204 const QObject *s = sender();
7205
7206 for (std::set<ControlEditorDialog *>::iterator i = m_controlEditors.begin();
7207 i != m_controlEditors.end(); ++i) {
7208 if (*i == s) {
7209 m_controlEditors.erase(i);
7210 RG_DEBUG << "removed control editor dialog, have " << m_controlEditors.size() << " left";
7211 return ;
7212 }
7213 }
7214
7215 RG_WARNING << "WARNING: control editor " << s << " closed, but couldn't find it in our control editor list (we have " << m_controlEditors.size() << " editors)";
7216 }
7217
7218 void
slotShowPluginDialog(QWidget * parent,InstrumentId instrumentId,int index)7219 RosegardenMainWindow::slotShowPluginDialog(QWidget *parent,
7220 InstrumentId instrumentId,
7221 int index)
7222 {
7223 RG_DEBUG << "slotShowPluginDialog(" << parent << ", " << instrumentId << ", " << index << ")";
7224
7225 // ??? AudioPluginDialog should be simplified to the point where this
7226 // routine is only a single line launching it. Don't hold on
7227 // to the dialogs (AudioPluginDialog should do this). Don't connect
7228 // all these signals (AudioPluginDialog should take care of itself).
7229 // Etc... Then let all who are connected to this slot launch
7230 // AudioPluginDialog directly on their own. Get rid of this routine.
7231
7232 if (!parent)
7233 parent = this;
7234
7235 int key = (index << 16) + instrumentId;
7236 RG_DEBUG << " key:" << key;
7237
7238 // If we already have a dialog for this plugin, show it.
7239 if (m_pluginDialogs[key]) {
7240 m_pluginDialogs[key]->show();
7241 m_pluginDialogs[key]->raise();
7242 m_pluginDialogs[key]->activateWindow();
7243 return ;
7244 }
7245
7246 PluginContainer *container = nullptr;
7247
7248 container = m_doc->getStudio().getContainerById(instrumentId);
7249 if (!container) {
7250 RG_DEBUG << "slotShowPluginDialog - "
7251 << "no instrument or buss of id " << instrumentId;
7252 return ;
7253 }
7254
7255 // only create a dialog if we've got a plugin instance
7256 AudioPluginInstance *inst =
7257 container->getPlugin(index);
7258
7259 if (!inst) {
7260 RG_DEBUG << "slotShowPluginDialog - "
7261 << "no AudioPluginInstance found for index "
7262 << index;
7263 return ;
7264 }
7265
7266 // Create the plugin dialog
7267 //
7268 AudioPluginDialog *dialog =
7269 new AudioPluginDialog(parent,
7270 m_doc->getPluginManager(),
7271 m_pluginGUIManager,
7272 container,
7273 index);
7274
7275 // Plug the new dialog into the standard keyboard shortcuts so
7276 // that we can use them still while the plugin has focus.
7277 //
7278 plugShortcuts(dialog, dialog->getShortcuts());
7279
7280 connect(dialog,
7281 &AudioPluginDialog::pluginSelected,
7282 this,
7283 &RosegardenMainWindow::slotPluginSelected);
7284
7285 connect(dialog,
7286 &AudioPluginDialog::pluginPortChanged,
7287 this,
7288 &RosegardenMainWindow::slotPluginPortChanged);
7289
7290 connect(dialog,
7291 &AudioPluginDialog::pluginProgramChanged,
7292 this,
7293 &RosegardenMainWindow::slotPluginProgramChanged);
7294
7295 connect(dialog,
7296 &AudioPluginDialog::changePluginConfiguration,
7297 this,
7298 &RosegardenMainWindow::slotChangePluginConfiguration);
7299
7300 connect(dialog,
7301 &AudioPluginDialog::showPluginGUI,
7302 this,
7303 &RosegardenMainWindow::slotShowPluginGUI);
7304
7305 connect(dialog,
7306 &AudioPluginDialog::stopPluginGUI,
7307 this,
7308 &RosegardenMainWindow::slotStopPluginGUI);
7309
7310 connect(dialog,
7311 &AudioPluginDialog::bypassed,
7312 this,
7313 &RosegardenMainWindow::slotPluginBypassed);
7314
7315 connect(dialog,
7316 SIGNAL(destroyed(InstrumentId, int)),
7317 this,
7318 SLOT(slotPluginDialogDestroyed(InstrumentId, int)));
7319
7320 connect(this, &RosegardenMainWindow::documentAboutToChange, dialog, &QWidget::close);
7321
7322 // Hold onto this dialog so we don't have to create it again.
7323 m_pluginDialogs[key] = dialog;
7324 m_pluginDialogs[key]->show();
7325
7326 // Set modified
7327 m_doc->slotDocumentModified();
7328 }
7329
7330 void
slotPluginSelected(InstrumentId instrumentId,int index,int plugin)7331 RosegardenMainWindow::slotPluginSelected(InstrumentId instrumentId,
7332 int index, int plugin)
7333 {
7334 const QObject *s = sender();
7335
7336 bool fromSynthMgr = (s == m_synthManager);
7337
7338 // It's assumed that ports etc will already have been set up on
7339 // the AudioPluginInstance before this is invoked.
7340
7341 PluginContainer *container = nullptr;
7342
7343 container = m_doc->getStudio().getContainerById(instrumentId);
7344 if (!container) {
7345 RG_DEBUG << "slotPluginSelected - "
7346 << "no instrument or buss of id " << instrumentId;
7347 return ;
7348 }
7349
7350 AudioPluginInstance *inst =
7351 container->getPlugin(index);
7352
7353 if (!inst) {
7354 RG_DEBUG << "slotPluginSelected - "
7355 << "got index of unknown plugin!";
7356 return ;
7357 }
7358
7359 if (plugin == -1) {
7360 // Destroy plugin instance
7361 //!!! seems iffy -- why can't we just unassign it?
7362
7363 if (StudioControl::
7364 destroyStudioObject(inst->getMappedId())) {
7365 RG_DEBUG << "slotPluginSelected - "
7366 << "cannot destroy Studio object "
7367 << inst->getMappedId();
7368 }
7369
7370 inst->setAssigned(false);
7371 } else {
7372 // If unassigned then create a sequencer instance of this
7373 // AudioPluginInstance.
7374 //
7375 if (inst->isAssigned()) {
7376 RG_DEBUG << "slotPluginSelected - "
7377 << " setting identifier for mapper id " << inst->getMappedId()
7378 << " to " << strtoqstr(inst->getIdentifier());
7379
7380 StudioControl::setStudioObjectProperty(inst->getMappedId(),
7381 MappedPluginSlot::Identifier,
7382 strtoqstr(inst->getIdentifier()));
7383 } else {
7384 // create a studio object at the sequencer
7385 MappedObjectId newId =
7386 StudioControl::createStudioObject
7387 (MappedObject::PluginSlot);
7388
7389 RG_DEBUG << "slotPluginSelected - "
7390 << " new MappedObjectId = " << newId;
7391
7392 // set the new Mapped ID and that this instance
7393 // is assigned
7394 inst->setMappedId(newId);
7395 inst->setAssigned(true);
7396
7397 // set the instrument id
7398 StudioControl::setStudioObjectProperty
7399 (newId,
7400 MappedObject::Instrument,
7401 MappedObjectValue(instrumentId));
7402
7403 // set the position
7404 StudioControl::setStudioObjectProperty
7405 (newId,
7406 MappedObject::Position,
7407 MappedObjectValue(index));
7408
7409 // set the plugin id
7410 StudioControl::setStudioObjectProperty
7411 (newId,
7412 MappedPluginSlot::Identifier,
7413 strtoqstr(inst->getIdentifier()));
7414 }
7415 }
7416
7417 int pluginMappedId = inst->getMappedId();
7418
7419 //!!! much code duplicated here from RosegardenDocument::initialiseStudio
7420
7421 inst->setConfigurationValue
7422 (qstrtostr(PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY),
7423 qstrtostr(m_doc->getAudioFileManager().getAudioPath()));
7424
7425 // Set opaque string configuration data (e.g. for DSSI plugin)
7426 //
7427 MappedObjectPropertyList config;
7428 for (AudioPluginInstance::ConfigMap::const_iterator
7429 i = inst->getConfiguration().begin();
7430 i != inst->getConfiguration().end(); ++i) {
7431 config.push_back(strtoqstr(i->first));
7432 config.push_back(strtoqstr(i->second));
7433 }
7434
7435 QString error = StudioControl::setStudioObjectPropertyList
7436 (pluginMappedId,
7437 MappedPluginSlot::Configuration,
7438 config);
7439
7440 if (error != "") showError(error);
7441
7442 // Set the bypass
7443 //
7444 StudioControl::setStudioObjectProperty
7445 (pluginMappedId,
7446 MappedPluginSlot::Bypassed,
7447 MappedObjectValue(inst->isBypassed()));
7448
7449 // Set the program
7450 //
7451 if (inst->getProgram() != "") {
7452 StudioControl::setStudioObjectProperty
7453 (pluginMappedId,
7454 MappedPluginSlot::Program,
7455 strtoqstr(inst->getProgram()));
7456 }
7457
7458 // Set all the port values
7459 //
7460 PortInstanceIterator portIt;
7461
7462 for (portIt = inst->begin();
7463 portIt != inst->end(); ++portIt) {
7464 StudioControl::setStudioPluginPort
7465 (pluginMappedId,
7466 (*portIt)->number,
7467 (*portIt)->value);
7468 }
7469
7470 if (fromSynthMgr) {
7471 int key = (index << 16) + instrumentId;
7472 if (m_pluginDialogs[key]) {
7473 m_pluginDialogs[key]->updatePlugin(plugin);
7474 }
7475 } else if (m_synthManager) {
7476 m_synthManager->updatePlugin(instrumentId, plugin);
7477 }
7478
7479 emit pluginSelected(instrumentId, index, plugin);
7480
7481 // Set modified
7482 m_doc->slotDocumentModified();
7483 }
7484
7485 void
slotChangePluginPort(InstrumentId instrumentId,int pluginIndex,int portIndex,float value)7486 RosegardenMainWindow::slotChangePluginPort(InstrumentId instrumentId,
7487 int pluginIndex,
7488 int portIndex,
7489 float value)
7490 {
7491 PluginContainer *container = nullptr;
7492
7493 container = m_doc->getStudio().getContainerById(instrumentId);
7494 if (!container) {
7495 RG_DEBUG << "slotChangePluginPort - "
7496 << "no instrument or buss of id " << instrumentId;
7497 return ;
7498 }
7499
7500 AudioPluginInstance *inst = container->getPlugin(pluginIndex);
7501 if (!inst) {
7502 RG_DEBUG << "slotChangePluginPort - "
7503 << "no plugin at index " << pluginIndex << " on " << instrumentId;
7504 return ;
7505 }
7506
7507 PluginPortInstance *port = inst->getPort(portIndex);
7508 if (!port) {
7509 RG_DEBUG << "slotChangePluginPort - no port "
7510 << portIndex;
7511 return ;
7512 }
7513
7514 RG_DEBUG << "slotPluginPortChanged - "
7515 << "setting plugin port (" << inst->getMappedId()
7516 << ", " << portIndex << ") from " << port->value
7517 << " to " << value;
7518
7519 port->setValue(value);
7520
7521 StudioControl::setStudioPluginPort(inst->getMappedId(),
7522 portIndex, port->value);
7523
7524 m_doc->slotDocumentModified();
7525
7526 // This modification came from The Outside!
7527 int key = (pluginIndex << 16) + instrumentId;
7528 if (m_pluginDialogs[key]) {
7529 m_pluginDialogs[key]->updatePluginPortControl(portIndex);
7530 }
7531 }
7532
7533 void
slotPluginPortChanged(InstrumentId instrumentId,int pluginIndex,int portIndex)7534 RosegardenMainWindow::slotPluginPortChanged(InstrumentId instrumentId,
7535 int pluginIndex,
7536 int portIndex)
7537 {
7538 PluginContainer *container = nullptr;
7539
7540 container = m_doc->getStudio().getContainerById(instrumentId);
7541 if (!container) {
7542 RG_DEBUG << "slotPluginPortChanged - "
7543 << "no instrument or buss of id " << instrumentId;
7544 return ;
7545 }
7546
7547 AudioPluginInstance *inst = container->getPlugin(pluginIndex);
7548 if (!inst) {
7549 RG_DEBUG << "slotPluginPortChanged - "
7550 << "no plugin at index " << pluginIndex << " on " << instrumentId;
7551 return ;
7552 }
7553
7554 PluginPortInstance *port = inst->getPort(portIndex);
7555 if (!port) {
7556 RG_DEBUG << "slotPluginPortChanged - no port "
7557 << portIndex;
7558 return ;
7559 }
7560
7561 RG_DEBUG << "slotPluginPortChanged - "
7562 << "setting plugin port (" << inst->getMappedId()
7563 << ", " << portIndex << ") to " << port->value;
7564
7565 StudioControl::setStudioPluginPort(inst->getMappedId(),
7566 portIndex, port->value);
7567
7568 m_doc->slotDocumentModified();
7569
7570 // This modification came from our own plugin dialog, so update
7571 // any external GUIs
7572 if (m_pluginGUIManager) {
7573 m_pluginGUIManager->updatePort(instrumentId,
7574 pluginIndex,
7575 portIndex);
7576 }
7577 }
7578
7579 void
slotChangePluginProgram(InstrumentId instrumentId,int pluginIndex,QString program)7580 RosegardenMainWindow::slotChangePluginProgram(InstrumentId instrumentId,
7581 int pluginIndex,
7582 QString program)
7583 {
7584 PluginContainer *container = nullptr;
7585
7586 container = m_doc->getStudio().getContainerById(instrumentId);
7587 if (!container) {
7588 RG_DEBUG << "slotChangePluginProgram - "
7589 << "no instrument or buss of id " << instrumentId;
7590 return ;
7591 }
7592
7593 AudioPluginInstance *inst = container->getPlugin(pluginIndex);
7594 if (!inst) {
7595 RG_DEBUG << "slotChangePluginProgram - "
7596 << "no plugin at index " << pluginIndex << " on " << instrumentId;
7597 return ;
7598 }
7599
7600 RG_DEBUG << "slotChangePluginProgram - "
7601 << "setting plugin program ("
7602 << inst->getMappedId() << ") from " << strtoqstr(inst->getProgram())
7603 << " to " << program;
7604
7605 inst->setProgram(qstrtostr(program));
7606
7607 StudioControl::
7608 setStudioObjectProperty(inst->getMappedId(),
7609 MappedPluginSlot::Program,
7610 program);
7611
7612 PortInstanceIterator portIt;
7613
7614 for (portIt = inst->begin();
7615 portIt != inst->end(); ++portIt) {
7616 float value = StudioControl::getStudioPluginPort
7617 (inst->getMappedId(),
7618 (*portIt)->number);
7619 (*portIt)->value = value;
7620 }
7621
7622 // Set modified
7623 m_doc->slotDocumentModified();
7624
7625 int key = (pluginIndex << 16) + instrumentId;
7626 if (m_pluginDialogs[key]) {
7627 m_pluginDialogs[key]->updatePluginProgramControl();
7628 }
7629 }
7630
7631 void
slotPluginProgramChanged(InstrumentId instrumentId,int pluginIndex)7632 RosegardenMainWindow::slotPluginProgramChanged(InstrumentId instrumentId,
7633 int pluginIndex)
7634 {
7635 PluginContainer *container = nullptr;
7636
7637 container = m_doc->getStudio().getContainerById(instrumentId);
7638 if (!container) {
7639 RG_DEBUG << "slotPluginProgramChanged - "
7640 << "no instrument or buss of id " << instrumentId;
7641 return ;
7642 }
7643
7644 AudioPluginInstance *inst = container->getPlugin(pluginIndex);
7645 if (!inst) {
7646 RG_DEBUG << "slotPluginProgramChanged - "
7647 << "no plugin at index " << pluginIndex << " on " << instrumentId;
7648 return ;
7649 }
7650
7651 QString program = strtoqstr(inst->getProgram());
7652
7653 RG_DEBUG << "slotPluginProgramChanged - "
7654 << "setting plugin program ("
7655 << inst->getMappedId() << ") to " << program;
7656
7657 StudioControl::
7658 setStudioObjectProperty(inst->getMappedId(),
7659 MappedPluginSlot::Program,
7660 program);
7661
7662 PortInstanceIterator portIt;
7663
7664 for (portIt = inst->begin();
7665 portIt != inst->end(); ++portIt) {
7666 float value = StudioControl::getStudioPluginPort
7667 (inst->getMappedId(),
7668 (*portIt)->number);
7669 (*portIt)->value = value;
7670 }
7671
7672 // Set modified
7673 m_doc->slotDocumentModified();
7674
7675 if (m_pluginGUIManager)
7676 m_pluginGUIManager->updateProgram(instrumentId,
7677 pluginIndex);
7678 }
7679
7680 void
slotChangePluginConfiguration(InstrumentId instrumentId,int index,bool global,QString key,QString value)7681 RosegardenMainWindow::slotChangePluginConfiguration(InstrumentId instrumentId,
7682 int index,
7683 bool global,
7684 QString key,
7685 QString value)
7686 {
7687 PluginContainer *container = nullptr;
7688
7689 container = m_doc->getStudio().getContainerById(instrumentId);
7690 if (!container) {
7691 RG_DEBUG << "slotChangePluginConfiguration - "
7692 << "no instrument or buss of id " << instrumentId;
7693 return ;
7694 }
7695
7696 AudioPluginInstance *inst = container->getPlugin(index);
7697
7698 if (global && inst) {
7699
7700 // Set the same configuration on other plugins in the same
7701 // instance group
7702
7703 QSharedPointer<AudioPlugin> pl =
7704 m_pluginManager->getPluginByIdentifier(strtoqstr(inst->getIdentifier()));
7705
7706 if (pl && pl->isGrouped()) {
7707
7708 InstrumentList il =
7709 m_doc->getStudio().getAllInstruments();
7710
7711 for (InstrumentList::iterator i = il.begin();
7712 i != il.end(); ++i) {
7713
7714 for (AudioPluginVector::iterator pli =
7715 (*i)->beginPlugins();
7716 pli != (*i)->endPlugins(); ++pli) {
7717
7718 if (*pli && (*pli)->isAssigned() &&
7719 (*pli)->getIdentifier() == inst->getIdentifier() &&
7720 (*pli) != inst) {
7721
7722 slotChangePluginConfiguration
7723 ((*i)->getId(), (*pli)->getPosition(),
7724 false, key, value);
7725
7726 m_pluginGUIManager->updateConfiguration
7727 ((*i)->getId(), (*pli)->getPosition(), key);
7728 }
7729 }
7730 }
7731 }
7732 }
7733
7734 if (inst) {
7735
7736 inst->setConfigurationValue(qstrtostr(key), qstrtostr(value));
7737
7738 MappedObjectPropertyList config;
7739 for (AudioPluginInstance::ConfigMap::const_iterator
7740 i = inst->getConfiguration().begin();
7741 i != inst->getConfiguration().end(); ++i) {
7742 config.push_back(strtoqstr(i->first));
7743 config.push_back(strtoqstr(i->second));
7744 }
7745
7746 RG_DEBUG << "slotChangePluginConfiguration: setting new config on mapped id " << inst->getMappedId();
7747
7748 QString error = StudioControl::setStudioObjectPropertyList
7749 (inst->getMappedId(),
7750 MappedPluginSlot::Configuration,
7751 config);
7752
7753 if (error != "") showError(error);
7754
7755 // Set modified
7756 m_doc->slotDocumentModified();
7757
7758 int key = (index << 16) + instrumentId;
7759 if (m_pluginDialogs[key]) {
7760 m_pluginDialogs[key]->updatePluginProgramList();
7761 }
7762 }
7763 }
7764
7765 void
slotPluginDialogDestroyed(InstrumentId instrumentId,int index)7766 RosegardenMainWindow::slotPluginDialogDestroyed(InstrumentId instrumentId,
7767 int index)
7768 {
7769 RG_DEBUG << "slotPluginDialogDestroyed()";
7770
7771 int key = (index << 16) + instrumentId;
7772
7773 RG_DEBUG << " key:" << key;
7774
7775 // We can't simply call erase() here to prevent the map from
7776 // growing. We would also need to change every check for
7777 // m_pluginDialogs[key] to use find() instead.
7778 m_pluginDialogs[key] = nullptr;
7779 }
7780
7781 void
slotPluginBypassed(InstrumentId instrumentId,int pluginIndex,bool bp)7782 RosegardenMainWindow::slotPluginBypassed(InstrumentId instrumentId,
7783 int pluginIndex, bool bp)
7784 {
7785 PluginContainer *container = nullptr;
7786
7787 container = m_doc->getStudio().getContainerById(instrumentId);
7788 if (!container) {
7789 RG_DEBUG << "slotPluginBypassed - "
7790 << "no instrument or buss of id " << instrumentId;
7791 return ;
7792 }
7793
7794 AudioPluginInstance *inst = container->getPlugin(pluginIndex);
7795
7796 if (inst) {
7797 StudioControl::setStudioObjectProperty
7798 (inst->getMappedId(),
7799 MappedPluginSlot::Bypassed,
7800 MappedObjectValue(bp));
7801
7802 // Set the bypass on the instance
7803 //
7804 inst->setBypass(bp);
7805
7806 // Set modified
7807 m_doc->slotDocumentModified();
7808 }
7809
7810 emit pluginBypassed(instrumentId, pluginIndex, bp);
7811 }
7812
7813 void
slotShowPluginGUI(InstrumentId instrument,int index)7814 RosegardenMainWindow::slotShowPluginGUI(InstrumentId instrument,
7815 int index)
7816 {
7817 m_pluginGUIManager->showGUI(instrument, index);
7818 }
7819
7820 void
slotStopPluginGUI(InstrumentId instrument,int index)7821 RosegardenMainWindow::slotStopPluginGUI(InstrumentId instrument,
7822 int index)
7823 {
7824 m_pluginGUIManager->stopGUI(instrument, index);
7825 }
7826
7827 void
slotPluginGUIExited(InstrumentId instrument,int index)7828 RosegardenMainWindow::slotPluginGUIExited(InstrumentId instrument,
7829 int index)
7830 {
7831 int key = (index << 16) + instrument;
7832 if (m_pluginDialogs[key]) {
7833 m_pluginDialogs[key]->guiExited();
7834 }
7835 }
7836
7837 void
slotPlayList()7838 RosegardenMainWindow::slotPlayList()
7839 {
7840 if (!m_playList) {
7841 m_playList = new PlayListDialog(tr("Play List"), this);
7842 connect(m_playList, &PlayListDialog::closing, this, &RosegardenMainWindow::slotPlayListClosed);
7843
7844 connect(m_playList->getPlayList(), &PlayList::play, this, &RosegardenMainWindow::slotPlayListPlay);
7845
7846 }
7847
7848 m_playList->show();
7849 }
7850
7851 void
slotPlayListPlay(QString url)7852 RosegardenMainWindow::slotPlayListPlay(QString url)
7853 {
7854 // RG_DEBUG << "slotPlayListPlay() - called with: " << url;
7855 slotStop();
7856 openURL(url);
7857 slotPlay();
7858 }
7859
7860 void
slotPlayListClosed()7861 RosegardenMainWindow::slotPlayListClosed()
7862 {
7863 RG_DEBUG << "slotPlayListClosed()\n";
7864 if( m_playList ){
7865 // delete m_playList;
7866 m_playList = nullptr;
7867 }
7868 }
7869
7870 void
slotHelp()7871 RosegardenMainWindow::slotHelp()
7872 {
7873 // TRANSLATORS: if the manual is translated into your language, you can
7874 // change the two-letter language code in this URL to point to your language
7875 // version, eg. "http://rosegardenmusic.com/wiki/doc:manual-es" for the
7876 // Spanish version. If your language doesn't yet have a translation, feel
7877 // free to create one.
7878 QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:manual-en");
7879 QDesktopServices::openUrl(QUrl(helpURL));
7880 }
7881
7882 void
slotTutorial()7883 RosegardenMainWindow::slotTutorial()
7884 {
7885 QString tutorialURL = tr("http://rosegardenmusic.com/tutorials/");
7886 QDesktopServices::openUrl(QUrl(tutorialURL));
7887 }
7888
7889 void
slotBugGuidelines()7890 RosegardenMainWindow::slotBugGuidelines()
7891 {
7892 QString glURL = tr("http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html");
7893 QDesktopServices::openUrl(QUrl(glURL));
7894 }
7895
7896 void
slotHelpAbout()7897 RosegardenMainWindow::slotHelpAbout()
7898 {
7899 new AboutDialog(this);
7900 }
7901
7902 void
slotHelpAboutQt()7903 RosegardenMainWindow::slotHelpAboutQt()
7904 {
7905 QMessageBox::aboutQt(this, tr("Rosegarden"));
7906 }
7907
7908 void
slotDonate()7909 RosegardenMainWindow::slotDonate()
7910 {
7911 QDesktopServices::openUrl(QUrl(
7912 "https://www.rosegardenmusic.com/wiki/donations"));
7913 }
7914
7915
7916 void
slotBankEditorClosed()7917 RosegardenMainWindow::slotBankEditorClosed()
7918 {
7919 RG_DEBUG << "slotBankEditorClosed()";
7920
7921 if (m_doc->isModified()) {
7922 if (m_view) {
7923 // ??? Can't just remove this when the time comes.
7924 // ??? I suspect this call is being made because the Track and
7925 // Instrument Parameters boxes need to be updated. But
7926 // they should have already updated in response to the
7927 // RosegardenDocument::documentModified() signal(s) sent
7928 // while modifying the banks.
7929 m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
7930 }
7931 }
7932
7933 m_bankEditor = nullptr;
7934 }
7935
7936 void
slotSynthPluginManagerClosed()7937 RosegardenMainWindow::slotSynthPluginManagerClosed()
7938 {
7939 RG_DEBUG << "slotSynthPluginManagerClosed()\n";
7940
7941 m_synthManager = nullptr;
7942 }
7943
7944 void
slotMidiMixerClosed()7945 RosegardenMainWindow::slotMidiMixerClosed()
7946 {
7947 RG_DEBUG << "slotMidiMixerClosed()\n";
7948
7949 m_midiMixer = nullptr;
7950 }
7951
7952 void
slotAudioManagerClosed()7953 RosegardenMainWindow::slotAudioManagerClosed()
7954 {
7955 // File > Manage Audio Files...
7956 RG_DEBUG << "slotAudioManagerClosed()";
7957
7958 if (m_doc->isModified()) {
7959 if (m_view) {
7960 // ??? Can't just remove this when the time comes.
7961 // ??? This is needed to make sure newly added audio segments
7962 // are selected. It may also update the parameter boxes.
7963 // We'll need to devise an appropriate way to handle this
7964 // before removing this call.
7965 m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
7966 }
7967 }
7968
7969 m_audioManagerDialog = nullptr;
7970 }
7971
7972 void
slotPanic()7973 RosegardenMainWindow::slotPanic()
7974 {
7975 if (!m_seqManager)
7976 return;
7977
7978 // Stop the transport before we send a panic as the
7979 // playback goes all to hell anyway.
7980 //
7981 slotStop();
7982
7983 m_seqManager->panic();
7984 }
7985
7986 void
slotPopulateTrackInstrumentPopup()7987 RosegardenMainWindow::slotPopulateTrackInstrumentPopup()
7988 {
7989 RG_DEBUG << "slotSetTrackInstrument\n";
7990 Composition &comp = m_doc->getComposition();
7991 Track *track = comp.getTrackById(comp.getSelectedTrack());
7992
7993 if (!track) {
7994 RG_DEBUG << "Weird: no track available for instrument popup!";
7995 return ;
7996 }
7997
7998 Instrument* instrument = m_doc->getStudio().getInstrumentById(track->getInstrument());
7999
8000 // QPopupMenu* popup = dynamic_cast<QPopupMenu*>(factory()->container("set_track_instrument", this));
8001 QMenu* popup = this->findChild<QMenu*>("set_track_instrument");
8002
8003 m_view->getTrackEditor()->getTrackButtons()->populateInstrumentPopup(instrument, popup);
8004 }
8005
8006 void
slotRemapInstruments()8007 RosegardenMainWindow::slotRemapInstruments()
8008 {
8009 RG_DEBUG << "slotRemapInstruments\n";
8010 RemapInstrumentDialog dialog(this, m_doc);
8011
8012 connect(&dialog, &RemapInstrumentDialog::applyClicked,
8013 m_view->getTrackEditor()->getTrackButtons(),
8014 &TrackButtons::slotSynchroniseWithComposition);
8015
8016 if (dialog.exec() == QDialog::Accepted) {
8017 RG_DEBUG << "slotRemapInstruments - accepted\n";
8018 }
8019
8020 }
8021
8022 void
slotSaveDefaultStudio()8023 RosegardenMainWindow::slotSaveDefaultStudio()
8024 {
8025 RG_DEBUG << "slotSaveDefaultStudio\n";
8026
8027 int reply = QMessageBox::warning
8028 (this, tr("Rosegarden"), tr("Are you sure you want to save this as your default studio?"),
8029 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
8030
8031 if (reply != QMessageBox::Yes)
8032 return ;
8033
8034 TmpStatusMsg msg(tr("Saving current document as default studio..."), this);
8035
8036 QString autoloadFile = ResourceFinder().getAutoloadSavePath();
8037
8038 RG_DEBUG << "slotSaveDefaultStudio : saving studio in "
8039 << autoloadFile;
8040
8041 SetWaitCursor waitCursor;
8042 QString errMsg;
8043 bool res = m_doc->saveDocument(autoloadFile, errMsg);
8044 if (!res) {
8045 if (!errMsg.isEmpty())
8046 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not auto-save document at %1\nError was : %2")
8047 .arg(autoloadFile).arg(errMsg));
8048 else
8049 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not auto-save document at %1")
8050 .arg(autoloadFile));
8051
8052 }
8053 }
8054
8055 void
slotImportDefaultStudio()8056 RosegardenMainWindow::slotImportDefaultStudio()
8057 {
8058 int reply = QMessageBox::warning
8059 (this, tr("Rosegarden"), tr("Are you sure you want to import your default studio and lose the current one?"), QMessageBox::Yes | QMessageBox::No);
8060
8061 if (reply != QMessageBox::Yes)
8062 return ;
8063
8064 QString autoloadFile = ResourceFinder().getAutoloadPath();
8065
8066 QFileInfo autoloadFileInfo(autoloadFile);
8067
8068 if (!autoloadFileInfo.isReadable()) {
8069 RG_DEBUG << "RosegardenDocument::slotImportDefaultStudio - "
8070 << "can't find autoload file - defaulting";
8071 return ;
8072 }
8073
8074 slotImportStudioFromFile(autoloadFile);
8075 }
8076
8077 void
slotImportStudio()8078 RosegardenMainWindow::slotImportStudio()
8079 {
8080 RG_DEBUG << "slotImportStudio()\n";
8081
8082 QSettings settings;
8083 settings.beginGroup(LastUsedPathsConfigGroup);
8084 QString directory = settings.value("import_studio", ResourceFinder().getResourceDir("library")).toString();
8085
8086 const QString file = FileDialog::getOpenFileName(this, tr("Import Studio from File"), directory,
8087 tr("All supported files") + " (*.rg *.RG *.rgt *.RGT *.rgp *.RGP)" + ";;" +
8088 tr("All files") + " (*)", nullptr);
8089
8090 if (file.isEmpty())
8091 return ;
8092
8093 QDir d = QFileInfo(file).dir();
8094 directory = d.canonicalPath();
8095 settings.setValue("import_studio", directory);
8096 settings.endGroup();
8097
8098 slotImportStudioFromFile(file);
8099 }
8100
8101 void
slotImportStudioFromFile(const QString & file)8102 RosegardenMainWindow::slotImportStudioFromFile(const QString &file)
8103 {
8104 // We're only using this document temporarily, so we don't want to let it
8105 // obliterate the command history!
8106 bool clearCommandHistory = false, skipAutoload = true;
8107 RosegardenDocument *doc = new RosegardenDocument(this, {}, skipAutoload, clearCommandHistory, m_useSequencer);
8108
8109 Studio &oldStudio = m_doc->getStudio();
8110 Studio &newStudio = doc->getStudio();
8111
8112 // Add some dummy devices for when we open the document. We guess
8113 // that the file won't have more than 32 devices.
8114 //
8115 // for (unsigned int i = 0; i < 32; i++) {
8116 // newStudio.addDevice("", i, Device::Midi);
8117 // }
8118
8119 //!DEVPUSH review this
8120
8121 if (doc->openDocument(file, true)) { // true because we actually
8122 // do want to create devices
8123 // on the sequencer here
8124
8125 MacroCommand *command = new MacroCommand(tr("Import Studio"));
8126
8127 // We actually only copy across MIDI play devices... for now
8128 std::vector<DeviceId> midiPlayDevices;
8129
8130 for (DeviceList::const_iterator i =
8131 oldStudio.begin(); i != oldStudio.end(); ++i) {
8132
8133 MidiDevice *md = dynamic_cast<MidiDevice *>(*i);
8134
8135 if (md && (md->getDirection() == MidiDevice::Play)) {
8136 midiPlayDevices.push_back((*i)->getId());
8137 }
8138 }
8139
8140 std::vector<DeviceId>::iterator di(midiPlayDevices.begin());
8141
8142 for (DeviceList::const_iterator i = newStudio.begin();
8143 i != newStudio.end(); ++i) {
8144
8145 MidiDevice *md = dynamic_cast<MidiDevice *>(*i);
8146
8147 if (md && (md->getDirection() == MidiDevice::Play)) {
8148 if (di != midiPlayDevices.end()) {
8149 MidiDevice::VariationType variation
8150 (md->getVariationType());
8151 BankList bl(md->getBanks());
8152 ProgramList pl(md->getPrograms());
8153 ControlList cl(md->getControlParameters());
8154
8155 ModifyDeviceCommand* mdCommand =
8156 new ModifyDeviceCommand(&oldStudio,
8157 *di,
8158 md->getName(),
8159 md->getLibrarianName(),
8160 md->getLibrarianEmail());
8161 mdCommand->setVariation(variation);
8162 mdCommand->setBankList(bl);
8163 mdCommand->setProgramList(pl);
8164 mdCommand->setControlList(cl);
8165 mdCommand->setOverwrite(true);
8166 mdCommand->setRename(md->getName() != "");
8167
8168 command->addCommand(mdCommand);
8169 ++di;
8170 }
8171 }
8172 }
8173
8174 while (di != midiPlayDevices.end()) {
8175 command->addCommand(new CreateOrDeleteDeviceCommand
8176 (&oldStudio,
8177 *di));
8178 ++di;
8179 }
8180
8181 oldStudio.setMIDIThruFilter(newStudio.getMIDIThruFilter());
8182 oldStudio.setMIDIRecordFilter(newStudio.getMIDIRecordFilter());
8183
8184 CommandHistory::getInstance()->addCommand(command);
8185 m_doc->initialiseStudio(); // The other document will have reset it
8186
8187 if (m_view) {
8188 // ??? Can't just remove this when the time comes.
8189 // ??? I suspect this call is being made because the Track and
8190 // Instrument Parameters boxes need to be updated. It
8191 // would be better if the various boxes responded to
8192 // RosegardenDocument::documentModified() which should
8193 // have already been emitted.
8194 m_view->slotSelectTrackSegments
8195 (m_doc->getComposition().getSelectedTrack());
8196 }
8197 }
8198 delete doc;
8199 }
8200
8201 void
slotResetMidiNetwork()8202 RosegardenMainWindow::slotResetMidiNetwork()
8203 {
8204 if (!m_doc)
8205 return;
8206
8207 // Send out BS/PC/CCs for each Track.
8208 m_doc->sendChannelSetups(false); // do not send resets
8209 }
8210
8211 void
slotModifyMIDIFilters()8212 RosegardenMainWindow::slotModifyMIDIFilters()
8213 {
8214 RG_DEBUG << "slotModifyMIDIFilters";
8215
8216 MidiFilterDialog dialog(this, m_doc);
8217
8218 if (dialog.exec() == QDialog::Accepted) {
8219 RG_DEBUG << "slotModifyMIDIFilters - accepted";
8220 }
8221 }
8222
8223 void
slotManageMetronome()8224 RosegardenMainWindow::slotManageMetronome()
8225 {
8226 RG_DEBUG << "slotManageMetronome";
8227
8228 ManageMetronomeDialog dialog(this, m_doc);
8229
8230 if (dialog.exec() == QDialog::Accepted) {
8231 RG_DEBUG << "slotManageMetronome - accepted";
8232 }
8233 }
8234
8235 void
slotAutoSave()8236 RosegardenMainWindow::slotAutoSave()
8237 {
8238 if (!m_seqManager ||
8239 m_seqManager->getTransportStatus() == PLAYING ||
8240 m_seqManager->getTransportStatus() == RECORDING)
8241 return ;
8242
8243 QSettings settings;
8244 settings.beginGroup(GeneralOptionsConfigGroup);
8245
8246 if (! qStrToBool(settings.value("autosave", "true"))) {
8247 settings.endGroup();
8248 return ;
8249 }
8250 m_doc->slotAutoSave();
8251
8252 settings.endGroup();
8253 }
8254
8255 void
slotUpdateAutoSaveInterval(unsigned int interval)8256 RosegardenMainWindow::slotUpdateAutoSaveInterval(unsigned int interval)
8257 {
8258 RG_DEBUG << "slotUpdateAutoSaveInterval - "
8259 << "changed interval to " << interval;
8260 m_autoSaveTimer->setInterval(int(interval) * 1000);
8261 }
8262
8263 void
slotShowTip()8264 RosegardenMainWindow::slotShowTip()
8265 {
8266 RG_DEBUG << "slotShowTip";
8267 // KTipDialog::showTip(this, locate("data", "rosegarden/tips"), true); //&&& showTip dialog deactivated.
8268 }
8269
slotShowToolHelp(const QString & s)8270 void RosegardenMainWindow::slotShowToolHelp(const QString &s)
8271 {
8272 QString msg = s;
8273 if (msg != "") msg = " " + msg;
8274 slotStatusMsg(msg);
8275 }
8276
getTransport()8277 TransportDialog* RosegardenMainWindow::getTransport()
8278 {
8279 if (m_transport == nullptr)
8280 createAndSetupTransport();
8281
8282 return m_transport;
8283 }
8284
getDocument() const8285 RosegardenDocument *RosegardenMainWindow::getDocument() const
8286 {
8287 return m_doc;
8288 }
8289
8290 void
awaitDialogClearance() const8291 RosegardenMainWindow::awaitDialogClearance() const
8292 {
8293 bool haveDialog = true;
8294
8295 while (haveDialog) {
8296
8297 QList<QDialog *> childList = findChildren<QDialog *>();
8298
8299 haveDialog = false;
8300
8301 // For each child dialog...
8302 for (int i = 0; i < childList.size(); ++i) {
8303 QDialog *child = childList.at(i);
8304 // If it's visible and it's not the TransportDialog, we need
8305 // to keep waiting.
8306 if (child->isVisible() &&
8307 child->objectName() != "Rosegarden Transport") {
8308 haveDialog = true;
8309 break;
8310 }
8311 }
8312
8313 if (haveDialog)
8314 qApp->processEvents(QEventLoop::AllEvents, 300);
8315 }
8316 }
8317
8318 void
slotNewerVersionAvailable(QString v)8319 RosegardenMainWindow::slotNewerVersionAvailable(QString v)
8320 {
8321 QString text(tr("<h3>Newer version available</h3>"));
8322 QString informativeText(tr("<p>You are using version %1. Version %2 is now available.</p><p>Please consult the <a style=\"color:gold\" href=\"http://www.rosegardenmusic.com/getting/\">Rosegarden website</a> for more information.</p>").arg(VERSION).arg(v));
8323 slotDisplayWarning(WarningWidget::Info, text, informativeText);
8324 }
8325
8326 void
slotAddMarker2()8327 RosegardenMainWindow::slotAddMarker2()
8328 {
8329 AddMarkerCommand *command =
8330 new AddMarkerCommand(&m_doc->getComposition(),
8331 m_doc->getComposition().getPosition(),
8332 "new marker",
8333 "no description");
8334
8335 m_view->slotAddCommandToHistory(command);
8336 }
8337
8338 void
slotPreviousMarker()8339 RosegardenMainWindow::slotPreviousMarker()
8340 {
8341 const Composition::markercontainer &markers =
8342 m_doc->getComposition().getMarkers();
8343
8344 timeT currentTime = m_doc->getComposition().getPosition();
8345 timeT time = currentTime;
8346
8347 // For each marker...
8348 for (const Marker *marker : markers) {
8349 if (marker->getTime() >= currentTime)
8350 break;
8351 time = marker->getTime();
8352 }
8353
8354 // If a jump is needed, jump.
8355 if (time != currentTime)
8356 m_doc->slotSetPointerPosition(time);
8357 }
8358
8359 void
slotNextMarker()8360 RosegardenMainWindow::slotNextMarker()
8361 {
8362 const Composition::markercontainer &markers =
8363 m_doc->getComposition().getMarkers();
8364
8365 timeT currentTime = m_doc->getComposition().getPosition();
8366 timeT time = currentTime;
8367
8368 // For each marker...
8369 for (const Marker *marker : markers) {
8370 if (marker->getTime() > currentTime) {
8371 time = marker->getTime();
8372 break;
8373 }
8374 }
8375
8376 // If a jump is needed, jump.
8377 if (time != currentTime)
8378 m_doc->slotSetPointerPosition(time);
8379 }
8380
8381 void
slotSetQuickMarker()8382 RosegardenMainWindow::slotSetQuickMarker()
8383 {
8384 RG_DEBUG << "slotSetQuickMarker()";
8385
8386 m_doc->setQuickMarker();
8387 getView()->getTrackEditor()->updateRulers();
8388 }
8389
8390 void
slotJumpToQuickMarker()8391 RosegardenMainWindow::slotJumpToQuickMarker()
8392 {
8393 RG_DEBUG << "slotJumpToQuickMarker()";
8394
8395 m_doc->jumpToQuickMarker();
8396 }
8397
8398 void
slotDisplayWarning(int type,QString text,QString informativeText)8399 RosegardenMainWindow::slotDisplayWarning(int type,
8400 QString text,
8401 QString informativeText)
8402 {
8403 RG_WARNING << "slotDisplayWarning(): MAIN WINDOW DISPLAY WARNING: type " << type << " text" << text;
8404
8405 // I'll need a hack way to make it look like my system isn't broken, for
8406 // screenshots, even though in reality Ubuntu 9.04 is totally hopeless
8407 //#define PEACHY_HACK
8408 #ifdef PEACHY_HACK
8409 return;
8410 #endif
8411
8412 // queue up the message, which trips the warning or info icon in so doing
8413 m_warningWidget->queueMessage(type, text, informativeText);
8414
8415 // set up the error state for the appropriate icon... this should probably
8416 // be managed some other way, but that's organic growth for you
8417 switch (type) {
8418 case WarningWidget::Midi: m_warningWidget->setMidiWarning(true); break;
8419 case WarningWidget::Audio: m_warningWidget->setAudioWarning(true); break;
8420 case WarningWidget::Timer: m_warningWidget->setTimerWarning(true); break;
8421 case WarningWidget::Other:
8422 case WarningWidget::Info:
8423 default: break;
8424 }
8425
8426 }
8427
8428 void
slotSwitchPreset()8429 RosegardenMainWindow::slotSwitchPreset()
8430 {
8431 if (!m_view->haveSelection()) return ;
8432
8433 // Code pasted from NotationView.cpp with very coarse adaptation performed.
8434 // Really, I think both places need some more thought about what "selected
8435 // segments" and "all segments on this track" mean. Definitely more work
8436 // could be done here. TODO
8437 PresetHandlerDialog dialog(this, true);
8438
8439 if (dialog.exec() != QDialog::Accepted) return;
8440
8441 if (dialog.getConvertAllSegments()) {
8442 // get all segments for this track and convert them.
8443 Composition& comp = getDocument()->getComposition();
8444 TrackId selectedTrack = comp.getSelectedTrack();
8445
8446 // satisfy #1885251 the way that seems most reasonble to me at the
8447 // moment, only changing track parameters when acting on all segments on
8448 // this track from the notation view
8449 //
8450 //!!! This won't be undoable, and I'm not sure if that's seriously
8451 // wrong, or just mildly wrong, but I'm betting somebody will tell me
8452 // about it if this was inappropriate
8453 Track *track = comp.getTrackById(selectedTrack);
8454 track->setPresetLabel( qstrtostr(dialog.getName()) );
8455 track->setClef(dialog.getClef());
8456 track->setTranspose(dialog.getTranspose());
8457 track->setLowestPlayable(dialog.getLowRange());
8458 track->setHighestPlayable(dialog.getHighRange());
8459
8460 CommandHistory::getInstance()->addCommand(new SegmentSyncCommand(
8461 comp.getSegments(), selectedTrack,
8462 dialog.getTranspose(),
8463 dialog.getLowRange(),
8464 dialog.getHighRange(),
8465 clefIndexToClef(dialog.getClef())));
8466
8467 comp.notifyTrackChanged(track);
8468
8469 } else {
8470 CommandHistory::getInstance()->addCommand(new SegmentSyncCommand(
8471 m_view->getSelection(),
8472 dialog.getTranspose(),
8473 dialog.getLowRange(),
8474 dialog.getHighRange(),
8475 clefIndexToClef(dialog.getClef())));
8476 }
8477
8478 m_doc->slotDocumentModified();
8479 }
8480
8481 void
checkAudioPath()8482 RosegardenMainWindow::checkAudioPath()
8483 {
8484 QString audioPath = m_doc->getAudioFileManager().getAudioPath();
8485 QDir dir(audioPath);
8486 QString text(tr("<h3>Invalid audio path</h3>"));
8487 QString correctThis(tr("<p>You will not be able to record audio or drag and drop audio files onto Rosegarden until you correct this in <b>View -> Document Properties -> Audio</b>.</p>"));
8488
8489 if (!dir.exists()) {
8490
8491 text = tr("<h3>Created audio path</h3>");
8492 QString informativeText(tr("<qt><p>Rosegarden created the audio path \"%1\" to use for audio recording, and to receive dropped audio files.</p><p>If you wish to use a different path, change this in <b>View -> Document Properties -> Audio</b>.</p></qt>").arg(audioPath));
8493 slotDisplayWarning(WarningWidget::Info, text, informativeText);
8494
8495 if (!dir.mkpath(audioPath)) {
8496 RG_DEBUG << "RosegardenDocument::testAudioPath() - audio path did not exist. Tried to create it, and failed.";
8497
8498 QString informativeText(tr("<qt><p>The audio path \"%1\" did not exist, and could not be created.</p>%2</qt>").arg(audioPath).arg(correctThis));
8499 slotDisplayWarning(WarningWidget::Audio, text, informativeText);
8500 }
8501 } else {
8502 QTemporaryFile tmp(audioPath);
8503 QString informativeText(tr("<qt><p>The audio path \"%1\" exists, but is not writable.</p>%2</qt>").arg(audioPath).arg(correctThis));
8504 bool showError = false;
8505 if (tmp.open()) {
8506 if (tmp.write("0", 1) == -1) {
8507 std::cout << "could not write file" << std::endl;
8508 showError = true;
8509 }
8510 } else {
8511 showError = true;
8512 }
8513
8514 if (showError) {
8515 slotDisplayWarning(WarningWidget::Audio, text, informativeText);
8516 }
8517
8518 if (tmp.isOpen()) tmp.close();
8519 }
8520
8521 // This is all more convenient than intentionally breaking things in my system
8522 // to trigger warning conditions. It's not a real test of function, but it
8523 // serves to test form.
8524 //#define WARNING_WIDGET_WORKOUT
8525 #ifdef WARNING_WIDGET_WORKOUT
8526 slotDisplayWarning(WarningWidget::Audio, "Audio warning!", "Informative audio warning!");
8527 slotDisplayWarning(WarningWidget::Midi, "MIDI warning!", "Informative MIDI warning!");
8528 slotDisplayWarning(WarningWidget::Timer, "Timer warning!", "Informative timer warning!");
8529 slotDisplayWarning(WarningWidget::Other, "Misc. warning!", "Informative misc. warning!");
8530 slotDisplayWarning(WarningWidget::Info, "Information", "Informative information!");
8531 #endif
8532 }
8533
saveIfModified()8534 bool RosegardenMainWindow::saveIfModified()
8535 {
8536 //RG_DEBUG << "saveIfModified()";
8537
8538 // If the current document hasn't been modified, it's ok to continue.
8539 if (!m_doc->isModified())
8540 return true;
8541
8542 // The current document has been modified.
8543
8544 bool completed = true;
8545
8546 // Ask the user if they want to save changes to the current document.
8547 int wantSave = QMessageBox::warning( this, tr("Rosegarden - Warning"),
8548 tr("<qt><p>The current file has been modified.</p><p>Do you want to save it?</p></qt>"),
8549 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel );
8550
8551 //RG_DEBUG << "saveIfModified(): wantSave = " << wantSave;
8552
8553 switch (wantSave) {
8554
8555 case QMessageBox::Yes:
8556
8557 if (!m_doc->isRegularDotRGFile()) {
8558
8559 //RG_DEBUG << "saveIfModified() : new or imported file";
8560
8561 completed = slotFileSaveAs();
8562
8563 } else {
8564
8565 //RG_DEBUG << "saveIfModified() : regular file";
8566
8567 QString errMsg;
8568 completed = m_doc->saveDocument(m_doc->getAbsFilePath(), errMsg);
8569
8570 if (!completed) {
8571 if (!errMsg.isEmpty()) {
8572 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1\n(%2)").arg(m_doc->getAbsFilePath()).arg(errMsg));
8573 } else {
8574 QMessageBox::critical(this, tr("Rosegarden"), tr("Could not save document at %1").arg(m_doc->getAbsFilePath()));
8575 }
8576 }
8577 }
8578
8579 break;
8580
8581 case QMessageBox::No:
8582 // delete the autosave file so it won't annoy
8583 // the user when reloading the file.
8584 m_doc->deleteAutoSaveFile();
8585
8586 completed = true;
8587 break;
8588
8589 case QMessageBox::Cancel:
8590 completed = false;
8591 break;
8592
8593 default:
8594 completed = false;
8595 break;
8596 }
8597
8598 // Clean up audio files.
8599 if (completed) {
8600 completed = m_doc->deleteOrphanedAudioFiles(wantSave == QMessageBox::No);
8601 if (completed) {
8602 m_doc->getAudioFileManager().resetRecentlyCreatedFiles();
8603 }
8604 }
8605
8606 // If all's well, mark the document as ok to toss.
8607 if (completed)
8608 m_doc->clearModifiedStatus();
8609
8610 return completed;
8611 }
8612
8613
8614 void
uiUpdateKludge()8615 RosegardenMainWindow::uiUpdateKludge()
8616 {
8617 // Force an update to the UI.
8618 // ??? This is a kludge. Callers to this should be using
8619 // RosegardenDocument::documentModified() to cause a
8620 // refresh of the UI.
8621 m_view->slotSelectTrackSegments(
8622 m_doc->getComposition().getSelectedTrack());
8623 }
8624
newDocument(bool skipAutoload)8625 RosegardenDocument *RosegardenMainWindow::newDocument(bool skipAutoload)
8626 {
8627 return new RosegardenDocument(this, m_pluginManager, skipAutoload,
8628 true, /*clear command history*/
8629 m_useSequencer);
8630 }
8631
8632 void
changeEvent(QEvent * event)8633 RosegardenMainWindow::changeEvent(QEvent *event)
8634 {
8635 // Let baseclass handle first.
8636 QWidget::changeEvent(event);
8637
8638 // We only care about this if the external controller port is
8639 // in Rosegarden native mode.
8640 if (!ExternalController::self().isNative())
8641 return;
8642
8643 // We only care about activation changes.
8644 if (event->type() != QEvent::ActivationChange)
8645 return;
8646
8647 // If we aren't the active window, bail.
8648 if (!isActiveWindow())
8649 return;
8650
8651 ExternalController::self().activeWindow =
8652 ExternalController::Main;
8653
8654 // Send CCs for current Track to external controller.
8655
8656 // Get the selected Track's Instrument.
8657 InstrumentId instrumentId =
8658 m_doc->getComposition().getSelectedInstrumentId();
8659
8660 if (instrumentId == NoInstrument)
8661 return;
8662
8663 Instrument *instrument =
8664 m_doc->getStudio().getInstrumentById(instrumentId);
8665
8666 if (!instrument)
8667 return;
8668
8669 ExternalController::sendAllCCs(instrument, 0);
8670
8671 // Clear out channels 1-15 for external controller.
8672 // ??? Why not provide the next 15 tracks here? Then the user can
8673 // always have control of 16 out of the full set of tracks.
8674 // Sounds potentially handy.
8675 for (MidiByte channel = 1; channel < 16; ++channel) {
8676 ExternalController::send(channel, MIDI_CONTROLLER_VOLUME, 0);
8677 ExternalController::send(
8678 channel, MIDI_CONTROLLER_PAN, MidiMidValue);
8679 }
8680 }
8681
8682 void
openWindow(ExternalController::Window window)8683 RosegardenMainWindow::openWindow(ExternalController::Window window)
8684 {
8685 switch (window) {
8686 case ExternalController::Main:
8687 show();
8688 activateWindow();
8689 raise();
8690 break;
8691
8692 case ExternalController::AudioMixer:
8693 slotOpenAudioMixer();
8694 break;
8695
8696 case ExternalController::MidiMixer:
8697 slotOpenMidiMixer();
8698 break;
8699
8700 default:
8701 RG_WARNING << "openwindow(): Unexpected window.";
8702 break;
8703 }
8704 }
8705
8706 void
customEvent(QEvent * event)8707 RosegardenMainWindow::customEvent(QEvent *event)
8708 {
8709 if (event->type() == PreviousTrack) {
8710 slotSelectPreviousTrack();
8711 return;
8712 }
8713
8714 if (event->type() == NextTrack) {
8715 slotSelectNextTrack();
8716 return;
8717 }
8718
8719 if (event->type() == Loop) {
8720 toggleLoop();
8721 return;
8722 }
8723
8724 if (event->type() == Stop) {
8725 slotStop();
8726 return;
8727 }
8728
8729 if (event->type() == Rewind) {
8730 ButtonEvent *buttonEvent = dynamic_cast<ButtonEvent *>(event);
8731 if (!buttonEvent)
8732 return;
8733
8734 m_rewindTypematic.press(buttonEvent->pressed);
8735
8736 return;
8737 }
8738 if (event->type() == FastForward) {
8739 ButtonEvent *buttonEvent = dynamic_cast<ButtonEvent *>(event);
8740 if (!buttonEvent)
8741 return;
8742
8743 m_fastForwardTypematic.press(buttonEvent->pressed);
8744
8745 return;
8746 }
8747
8748 if (event->type() == AddMarker) {
8749 slotAddMarker2();
8750 return;
8751 }
8752
8753 if (event->type() == PreviousMarker) {
8754 slotPreviousMarker();
8755 return;
8756 }
8757
8758 if (event->type() == NextMarker) {
8759 slotNextMarker();
8760 return;
8761 }
8762 }
8763
8764
8765 RosegardenMainWindow *RosegardenMainWindow::m_myself = nullptr;
8766
8767
8768 }// end namespace Rosegarden
8769
8770