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