16 #include "../version.h" 21 #include "view/Pane.h" 22 #include "view/PaneStack.h" 23 #include "data/model/WaveFileModel.h" 24 #include "data/model/SparseOneDimensionalModel.h" 25 #include "data/model/RangeSummarisableTimeValueModel.h" 26 #include "data/model/NoteModel.h" 27 #include "data/model/AggregateWaveModel.h" 28 #include "data/model/Labeller.h" 29 #include "data/osc/OSCQueue.h" 30 #include "framework/Document.h" 31 #include "framework/TransformUserConfigurator.h" 32 #include "view/ViewManager.h" 33 #include "base/Preferences.h" 34 #include "base/ResourceFinder.h" 35 #include "base/RecordDirectory.h" 36 #include "layer/WaveformLayer.h" 37 #include "layer/TimeRulerLayer.h" 38 #include "layer/TimeInstantLayer.h" 39 #include "layer/TimeValueLayer.h" 40 #include "layer/NoteLayer.h" 41 #include "layer/Colour3DPlotLayer.h" 42 #include "layer/SliceLayer.h" 43 #include "layer/SliceableLayer.h" 44 #include "layer/ImageLayer.h" 45 #include "layer/RegionLayer.h" 46 #include "view/Overview.h" 47 #include "widgets/PropertyBox.h" 48 #include "widgets/PropertyStack.h" 49 #include "widgets/AudioDial.h" 50 #include "widgets/LevelPanWidget.h" 51 #include "widgets/LevelPanToolButton.h" 52 #include "widgets/IconLoader.h" 53 #include "widgets/LayerTreeDialog.h" 54 #include "widgets/ListInputDialog.h" 55 #include "widgets/SubdividingMenu.h" 56 #include "widgets/NotifyingPushButton.h" 57 #include "widgets/KeyReference.h" 58 #include "widgets/TransformFinder.h" 59 #include "widgets/LabelCounterInputDialog.h" 60 #include "widgets/ActivityLog.h" 61 #include "widgets/UnitConverter.h" 62 #include "widgets/ProgressDialog.h" 63 #include "widgets/CSVAudioFormatDialog.h" 64 #include "audio/AudioCallbackPlaySource.h" 65 #include "audio/AudioCallbackRecordTarget.h" 66 #include "audio/PlaySpeedRangeMapper.h" 67 #include "data/fileio/DataFileReaderFactory.h" 68 #include "data/fileio/PlaylistFileReader.h" 69 #include "data/fileio/WavFileWriter.h" 70 #include "data/fileio/CSVFileWriter.h" 71 #include "data/fileio/MIDIFileWriter.h" 72 #include "data/fileio/BZipFileDevice.h" 73 #include "data/fileio/FileSource.h" 74 #include "data/midi/MIDIInput.h" 75 #include "base/RecentFiles.h" 76 #include "plugin/PluginScan.h" 77 #include "transform/TransformFactory.h" 78 #include "transform/ModelTransformerFactory.h" 79 #include "base/XmlExportable.h" 80 #include "widgets/CommandHistory.h" 81 #include "base/Profiler.h" 82 #include "base/Clipboard.h" 83 #include "base/UnitDatabase.h" 84 #include "layer/ColourDatabase.h" 85 #include "widgets/ModelDataTableDialog.h" 86 #include "widgets/CSVExportDialog.h" 87 #include "widgets/MenuTitle.h" 88 #include "rdf/PluginRDFIndexer.h" 92 #include "framework/VersionTester.h" 95 #include <vamp/vamp.h> 96 #include <vamp-hostsdk/PluginBase.h> 97 #include "plugin/api/ladspa.h" 98 #include "plugin/api/dssi.h" 100 #include <bqaudioio/SystemPlaybackTarget.h> 101 #include <bqaudioio/SystemAudioIO.h> 103 #include <QApplication> 104 #include <QMessageBox> 105 #include <QGridLayout> 110 #include <QInputDialog> 111 #include <QStatusBar> 116 #include <QTextStream> 117 #include <QTextCodec> 125 #include <QScrollArea> 126 #include <QCloseEvent> 127 #include <QDialogButtonBox> 128 #include <QFileSystemWatcher> 130 #include <QWidgetAction> 142 MainWindowBase(audioMode, midiMode, int(PaneStack::Option::Default)),
144 m_mainMenusCreated(false),
146 m_layerMenu(nullptr),
147 m_transformsMenu(nullptr),
148 m_playbackMenu(nullptr),
149 m_existingLayersMenu(nullptr),
150 m_sliceMenu(nullptr),
151 m_recentFilesMenu(nullptr),
152 m_recentTransformsMenu(nullptr),
153 m_templatesMenu(nullptr),
154 m_rightButtonMenu(nullptr),
155 m_rightButtonLayerMenu(nullptr),
156 m_rightButtonTransformsMenu(nullptr),
157 m_rightButtonPlaybackMenu(nullptr),
158 m_lastRightButtonPropertyMenu(nullptr),
159 m_soloAction(nullptr),
160 m_rwdStartAction(nullptr),
161 m_rwdSimilarAction(nullptr),
162 m_rwdAction(nullptr),
163 m_ffwdAction(nullptr),
164 m_ffwdSimilarAction(nullptr),
165 m_ffwdEndAction(nullptr),
166 m_playAction(nullptr),
167 m_recordAction(nullptr),
168 m_playSelectionAction(nullptr),
169 m_playLoopAction(nullptr),
170 m_soloModified(false),
172 m_playControlsSpacer(nullptr),
173 m_playControlsWidth(0),
174 m_preferencesDialog(nullptr),
175 m_layerTreeDialog(nullptr),
176 m_activityLog(new ActivityLog()),
177 m_unitConverter(new UnitConverter()),
178 m_keyReference(new KeyReference()),
179 m_templateWatcher(nullptr),
180 m_transformPopulater(nullptr)
182 Profiler profiler(
"MainWindow::MainWindow");
186 setWindowTitle(QApplication::applicationName());
188 UnitDatabase *udb = UnitDatabase::getInstance();
189 udb->registerUnit(
"Hz");
190 udb->registerUnit(
"dB");
191 udb->registerUnit(
"s");
193 ColourDatabase *cdb = ColourDatabase::getInstance();
194 cdb->addColour(Qt::black, tr(
"Black"));
195 cdb->addColour(Qt::darkRed, tr(
"Red"));
196 cdb->addColour(Qt::darkBlue, tr(
"Blue"));
197 cdb->addColour(Qt::darkGreen, tr(
"Green"));
198 cdb->addColour(QColor(200, 50, 255), tr(
"Purple"));
199 cdb->addColour(QColor(255, 150, 50), tr(
"Orange"));
200 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr(
"White")),
true);
201 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr(
"Bright Red")),
true);
202 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr(
"Bright Blue")),
true);
203 cdb->setUseDarkBackground(cdb->addColour(QColor(20, 255, 90), tr(
"Bright Green")),
true);
204 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr(
"Bright Purple")),
true);
205 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr(
"Bright Orange")),
true);
207 SVDEBUG <<
"MainWindow: Creating main user interface layout" << endl;
209 QFrame *frame =
new QFrame;
210 setCentralWidget(frame);
212 QGridLayout *layout =
new QGridLayout;
218 m_mainScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
225 int overviewHeight = m_viewManager->scalePixelSize(35);
226 if (overviewHeight < 40) overviewHeight = 40;
232 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
234 connect(
m_overview, SIGNAL(contextHelpChanged(
const QString &)),
235 this, SLOT(contextHelpChanged(
const QString &)));
238 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
253 m_playSpeed->setRangeMapper(
new PlaySpeedRangeMapper);
271 layout->setSpacing(m_viewManager->scalePixelSize(4));
282 + 2 + layout->spacing());
285 layout->setColumnStretch(0, 10);
290 frame->setLayout(layout);
292 SVDEBUG <<
"MainWindow: Creating menus and toolbars" << endl;
296 QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
297 setIconsVisibleInMenus(
false);
310 connect(m_viewManager, SIGNAL(activity(QString)),
312 connect(m_playSource, SIGNAL(activity(QString)),
314 connect(CommandHistory::getInstance(), SIGNAL(activity(QString)),
316 connect(
this, SIGNAL(activity(QString)),
324 setAudioRecordMode(RecordCreateAdditionalModel);
326 SVDEBUG <<
"MainWindow: Creating new session" << endl;
330 connect(m_midiInput, SIGNAL(eventsAvailable()),
333 SVDEBUG <<
"MainWindow: Creating network permission tester" << endl;
337 if (networkPermission) {
338 SVDEBUG <<
"MainWindow: Starting uninstalled-transform population thread" << endl;
339 TransformFactory::getInstance()->startPopulationThread();
343 #ifdef WITH_FEEDBACK_REQUEST 344 SVDEBUG <<
"MainWindow: Feedback request enabled" << endl;
345 if (QDate::currentDate().year() > 2020) {
346 SVDEBUG <<
"MainWindow: Feedback request expired, not running it" << endl;
348 SVDEBUG <<
"MainWindow: Creating surveyer" << endl;
350 config.
hostname =
"sonicvisualiser.org";
351 config.
testPath =
"feedback41-present.txt";
355 config.
title =
"Sonic Visualiser - Can you help?";
356 config.
text =
"<h3>Sonic Visualiser: Can you help?</h3><p>" 357 "<p>Are you using Sonic Visualiser in academic research or for commercial purposes? Or do you intend to do so?</p>" 358 "<p>If so, would you be interested in telling us something about your work? We are gathering case studies to gauge our impact and inform our future actions.</p><p>Anything you tell us will be used solely to guide research and development at the Centre for Digital Music, Queen Mary University of London.</p>";
366 SVDEBUG <<
"MainWindow: Creating version tester" << endl;
368 (
"sonicvisualiser.org",
"latest-version.txt", SV_VERSION);
376 if (withOSCSupport && networkPermission) {
377 SVDEBUG <<
"MainWindow: Creating OSC queue with network port" 381 SVDEBUG <<
"MainWindow: Creating internal-only OSC queue without port" 383 startOSCQueue(
false);
388 SVDEBUG <<
"MainWindow: Constructor done" << endl;
407 Profiles::getInstance()->dump();
414 SVDEBUG <<
"MainWindow::setupMenus" << endl;
425 menuBar()->setNativeMenuBar(
false);
470 SVDEBUG <<
"MainWindow::setupMenus: done" << endl;
476 if (m_viewManager->getZoomWheelsEnabled()) {
482 ps->setParent(
nullptr);
486 sc =
new QShortcut(QKeySequence(
"Esc"), ps);
487 connect(sc, SIGNAL(activated()),
this, SLOT(
endFullScreen()));
489 sc =
new QShortcut(QKeySequence(
"F11"), ps);
490 connect(sc, SIGNAL(activated()),
this, SLOT(
endFullScreen()));
497 for (
int i = 0; i < int(
sizeof(acts)/
sizeof(acts[0])); ++i) {
498 sc =
new QShortcut(acts[i]->shortcut(), ps);
499 connect(sc, SIGNAL(activated()), acts[i], SLOT(trigger()));
502 ps->showFullScreen();
509 QObjectList cl = m_paneStack->children();
510 foreach (QObject *o, cl) {
511 QShortcut *sc = qobject_cast<QShortcut *>(o);
515 m_paneStack->showNormal();
522 SVDEBUG <<
"MainWindow::setupFileMenu" << endl;
526 QMenu *menu = menuBar()->addMenu(tr(
"&File"));
527 menu->setTearOffEnabled(
true);
528 QToolBar *toolbar = addToolBar(tr(
"File Toolbar"));
534 QIcon icon = il.load(
"filenew");
535 QAction *action =
new QAction(icon, tr(
"&New Session"),
this);
536 action->setShortcut(tr(
"Ctrl+N"));
537 action->setStatusTip(tr(
"Abandon the current %1 session and start a new one").arg(QApplication::applicationName()));
538 connect(action, SIGNAL(triggered()),
this, SLOT(
newSession()));
540 menu->addAction(action);
541 toolbar->addAction(action);
543 icon = il.load(
"fileopen");
544 action =
new QAction(icon, tr(
"&Open..."),
this);
545 action->setShortcut(tr(
"Ctrl+O"));
546 action->setStatusTip(tr(
"Open a session file, audio file, or layer"));
547 connect(action, SIGNAL(triggered()),
this, SLOT(
openSomething()));
549 toolbar->addAction(action);
550 menu->addAction(action);
554 QAction *iaction =
new QAction(tr(
"&Import More Audio..."),
this);
555 iaction->setShortcut(tr(
"Ctrl+I"));
556 iaction->setStatusTip(tr(
"Import an extra audio file into a new pane"));
558 connect(
this, SIGNAL(canImportMoreAudio(
bool)), iaction, SLOT(setEnabled(
bool)));
563 QAction *raction =
new QAction(tr(
"Replace &Main Audio..."),
this);
564 raction->setStatusTip(tr(
"Replace the main audio file of the session with a different file"));
566 connect(
this, SIGNAL(canReplaceMainAudio(
bool)), raction, SLOT(setEnabled(
bool)));
568 action =
new QAction(tr(
"Open Lo&cation..."),
this);
569 action->setShortcut(tr(
"Ctrl+Shift+O"));
570 action->setStatusTip(tr(
"Open or import a file from a remote URL"));
571 connect(action, SIGNAL(triggered()),
this, SLOT(
openLocation()));
573 menu->addAction(action);
578 connect(&m_recentFiles, SIGNAL(recentChanged()),
581 menu->addSeparator();
583 icon = il.load(
"filesave");
584 action =
new QAction(icon, tr(
"&Save Session"),
this);
585 action->setShortcut(tr(
"Ctrl+S"));
586 action->setStatusTip(tr(
"Save the current session into a %1 session file").arg(QApplication::applicationName()));
587 connect(action, SIGNAL(triggered()),
this, SLOT(
saveSession()));
588 connect(
this, SIGNAL(canSave(
bool)), action, SLOT(setEnabled(
bool)));
590 menu->addAction(action);
591 toolbar->addAction(action);
593 icon = il.load(
"filesaveas");
594 action =
new QAction(icon, tr(
"Save Session &As..."),
this);
595 action->setShortcut(tr(
"Ctrl+Shift+S"));
596 action->setStatusTip(tr(
"Save the current session into a new %1 session file").arg(QApplication::applicationName()));
597 connect(action, SIGNAL(triggered()),
this, SLOT(
saveSessionAs()));
598 menu->addAction(action);
599 toolbar->addAction(action);
601 menu->addSeparator();
604 menu->addAction(raction);
607 menu->addAction(iaction);
609 action =
new QAction(tr(
"&Export Audio File..."),
this);
610 action->setStatusTip(tr(
"Export selection as an audio file"));
611 connect(action, SIGNAL(triggered()),
this, SLOT(
exportAudio()));
612 connect(
this, SIGNAL(canExportAudio(
bool)), action, SLOT(setEnabled(
bool)));
613 menu->addAction(action);
615 menu->addSeparator();
617 action =
new QAction(tr(
"Import Annotation &Layer..."),
this);
618 action->setShortcut(tr(
"Ctrl+L"));
619 action->setStatusTip(tr(
"Import layer data from an existing file"));
620 connect(action, SIGNAL(triggered()),
this, SLOT(
importLayer()));
621 connect(
this, SIGNAL(canImportLayer(
bool)), action, SLOT(setEnabled(
bool)));
623 menu->addAction(action);
625 action =
new QAction(tr(
"Export Annotation La&yer..."),
this);
626 action->setShortcut(tr(
"Ctrl+Y"));
627 action->setStatusTip(tr(
"Export layer data to a file"));
628 connect(action, SIGNAL(triggered()),
this, SLOT(
exportLayer()));
629 connect(
this, SIGNAL(canExportLayer(
bool)), action, SLOT(setEnabled(
bool)));
631 menu->addAction(action);
633 menu->addSeparator();
635 action =
new QAction(tr(
"Convert Audio from Data File..."),
this);
636 action->setStatusTip(tr(
"Convert and import audio sample values from a CSV data file"));
637 connect(action, SIGNAL(triggered()),
this, SLOT(
convertAudio()));
638 menu->addAction(action);
640 action =
new QAction(tr(
"Export Audio to Data File..."),
this);
641 action->setStatusTip(tr(
"Export audio from selection into a CSV data file"));
643 connect(
this, SIGNAL(canExportAudio(
bool)), action, SLOT(setEnabled(
bool)));
644 menu->addAction(action);
646 menu->addSeparator();
648 action =
new QAction(tr(
"Export Image File..."),
this);
649 action->setStatusTip(tr(
"Export a single pane to an image file"));
650 connect(action, SIGNAL(triggered()),
this, SLOT(
exportImage()));
651 connect(
this, SIGNAL(canExportImage(
bool)), action, SLOT(setEnabled(
bool)));
652 menu->addAction(action);
654 action =
new QAction(tr(
"Export SVG File..."),
this);
655 action->setStatusTip(tr(
"Export a single pane to a scalable SVG image file"));
656 connect(action, SIGNAL(triggered()),
this, SLOT(
exportSVG()));
657 connect(
this, SIGNAL(canExportImage(
bool)), action, SLOT(setEnabled(
bool)));
658 menu->addAction(action);
660 menu->addSeparator();
662 action =
new QAction(tr(
"Browse Recorded and Converted Audio"),
this);
663 action->setStatusTip(tr(
"Open the Recorded Audio folder in the system file browser"));
665 menu->addAction(action);
667 menu->addSeparator();
669 QString templatesMenuLabel = tr(
"Apply Session Template");
674 connect(
this, SIGNAL(canExportAudio(
bool)),
m_templatesMenu, SLOT(setEnabled(
bool)));
678 action =
new QAction(tr(
"Export Session as Template..."),
this);
682 connect(
this, SIGNAL(canExportAudio(
bool)), action, SLOT(setEnabled(
bool)));
683 menu->addAction(action);
691 action =
new QAction(tr(
"&Preferences..."),
this);
692 action->setStatusTip(tr(
"Adjust the application preferences"));
693 connect(action, SIGNAL(triggered()),
this, SLOT(
preferences()));
694 menu->addAction(action);
696 menu->addSeparator();
697 action =
new QAction(il.load(
"exit"), tr(
"&Quit"),
this);
698 action->setShortcut(tr(
"Ctrl+Q"));
699 action->setStatusTip(tr(
"Exit %1").arg(QApplication::applicationName()));
700 connect(action, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
702 menu->addAction(action);
708 SVDEBUG <<
"MainWindow::setupEditMenu" << endl;
712 QMenu *menu = menuBar()->addMenu(tr(
"&Edit"));
713 menu->setTearOffEnabled(
true);
714 CommandHistory::getInstance()->registerMenu(menu);
718 menu->addSeparator();
722 QAction *action =
new QAction(il.load(
"editcut"),
724 action->setShortcut(tr(
"Ctrl+X"));
725 action->setStatusTip(tr(
"Cut the selection from the current layer to the clipboard"));
726 connect(action, SIGNAL(triggered()),
this, SLOT(cut()));
727 connect(
this, SIGNAL(canEditSelection(
bool)), action, SLOT(setEnabled(
bool)));
729 menu->addAction(action);
732 action =
new QAction(il.load(
"editcopy"),
734 action->setShortcut(tr(
"Ctrl+C"));
735 action->setStatusTip(tr(
"Copy the selection from the current layer to the clipboard"));
736 connect(action, SIGNAL(triggered()),
this, SLOT(copy()));
737 connect(
this, SIGNAL(canEditSelection(
bool)), action, SLOT(setEnabled(
bool)));
739 menu->addAction(action);
742 action =
new QAction(il.load(
"editpaste"),
744 action->setShortcut(tr(
"Ctrl+V"));
745 action->setStatusTip(tr(
"Paste from the clipboard to the current layer"));
746 connect(action, SIGNAL(triggered()),
this, SLOT(paste()));
747 connect(
this, SIGNAL(canPaste(
bool)), action, SLOT(setEnabled(
bool)));
749 menu->addAction(action);
752 action =
new QAction(tr(
"Paste at Playback Position"),
this);
753 action->setShortcut(tr(
"Ctrl+Shift+V"));
754 action->setStatusTip(tr(
"Paste from the clipboard to the current layer, placing the first item at the playback position"));
755 connect(action, SIGNAL(triggered()),
this, SLOT(pasteAtPlaybackPosition()));
756 connect(
this, SIGNAL(canPaste(
bool)), action, SLOT(setEnabled(
bool)));
758 menu->addAction(action);
770 menu->addSeparator();
775 action =
new QAction(tr(
"Select &All"),
this);
776 action->setShortcut(tr(
"Ctrl+A"));
777 action->setStatusTip(tr(
"Select the whole duration of the current session"));
778 connect(action, SIGNAL(triggered()),
this, SLOT(selectAll()));
779 connect(
this, SIGNAL(canSelect(
bool)), action, SLOT(setEnabled(
bool)));
781 menu->addAction(action);
784 action =
new QAction(tr(
"Select &Visible Range"),
this);
785 action->setShortcut(tr(
"Ctrl+Shift+A"));
786 action->setStatusTip(tr(
"Select the time range corresponding to the current window width"));
787 connect(action, SIGNAL(triggered()),
this, SLOT(selectVisible()));
788 connect(
this, SIGNAL(canSelect(
bool)), action, SLOT(setEnabled(
bool)));
790 menu->addAction(action);
792 action =
new QAction(tr(
"Select to &Start"),
this);
793 action->setShortcut(tr(
"Shift+Left"));
794 action->setStatusTip(tr(
"Select from the start of the session to the current playback position"));
795 connect(action, SIGNAL(triggered()),
this, SLOT(selectToStart()));
796 connect(
this, SIGNAL(canSelect(
bool)), action, SLOT(setEnabled(
bool)));
798 menu->addAction(action);
800 action =
new QAction(tr(
"Select to &End"),
this);
801 action->setShortcut(tr(
"Shift+Right"));
802 action->setStatusTip(tr(
"Select from the current playback position to the end of the session"));
803 connect(action, SIGNAL(triggered()),
this, SLOT(selectToEnd()));
804 connect(
this, SIGNAL(canSelect(
bool)), action, SLOT(setEnabled(
bool)));
806 menu->addAction(action);
808 action =
new QAction(tr(
"C&lear Selection"),
this);
809 action->setShortcut(tr(
"Esc"));
810 action->setStatusTip(tr(
"Clear the selection"));
811 connect(action, SIGNAL(triggered()),
this, SLOT(clearSelection()));
812 connect(
this, SIGNAL(canClearSelection(
bool)), action, SLOT(setEnabled(
bool)));
814 menu->addAction(action);
817 menu->addSeparator();
821 action =
new QAction(tr(
"&Insert Instant at Playback Position"),
this);
822 action->setShortcut(tr(
";"));
823 action->setStatusTip(tr(
"Insert a new time instant at the current playback position, in a new layer if necessary"));
824 connect(action, SIGNAL(triggered()),
this, SLOT(insertInstant()));
825 connect(
this, SIGNAL(canInsertInstant(
bool)), action, SLOT(setEnabled(
bool)));
827 menu->addAction(action);
835 QString shortcut(tr(
"Enter"));
836 connect(
new QShortcut(shortcut,
this), SIGNAL(activated()),
837 this, SLOT(insertInstant()));
840 action =
new QAction(tr(
"Insert Instants at Selection &Boundaries"),
this);
841 action->setShortcut(tr(
"Shift+;"));
842 action->setStatusTip(tr(
"Insert new time instants at the start and end of the current selected regions, in a new layer if necessary"));
843 connect(action, SIGNAL(triggered()),
this, SLOT(insertInstantsAtBoundaries()));
844 connect(
this, SIGNAL(canInsertInstantsAtBoundaries(
bool)), action, SLOT(setEnabled(
bool)));
846 menu->addAction(action);
848 shortcut = QString(tr(
"Shift+Enter"));
849 connect(
new QShortcut(shortcut,
this), SIGNAL(activated()),
850 this, SLOT(insertInstantsAtBoundaries()));
858 action =
new QAction(tr(
"Insert Item at Selection"),
this);
859 action->setShortcut(tr(
"Ctrl+Shift+;"));
860 action->setStatusTip(tr(
"Insert a new note or region item corresponding to the current selection"));
861 connect(action, SIGNAL(triggered()),
this, SLOT(insertItemAtSelection()));
862 connect(
this, SIGNAL(canInsertItemAtSelection(
bool)), action, SLOT(setEnabled(
bool)));
864 menu->addAction(action);
866 shortcut = QString(tr(
"Ctrl+Shift+Enter"));
867 connect(
new QShortcut(shortcut,
this), SIGNAL(activated()),
868 this, SLOT(insertItemAtSelection()));
871 shortcut = QString(tr(
"Ctrl+Shift+Return"));
872 connect(
new QShortcut(shortcut,
this), SIGNAL(activated()),
873 this, SLOT(insertItemAtSelection()));
878 menu->addSeparator();
880 QMenu *numberingMenu = menu->addMenu(tr(
"Number New Instants with"));
881 numberingMenu->setTearOffEnabled(
true);
882 QActionGroup *numberingGroup =
new QActionGroup(
this);
885 Labeller::TypeNameMap types = m_labeller->getTypeNames();
886 for (Labeller::TypeNameMap::iterator i = types.begin(); i != types.end(); ++i) {
888 if (i->first == Labeller::ValueFromLabel ||
889 i->first == Labeller::ValueFromExistingNeighbour)
continue;
891 action =
new QAction(i->second,
this);
893 action->setCheckable(
true);
894 action->setChecked(m_labeller->getType() == i->first);
895 numberingGroup->addAction(action);
896 numberingMenu->addAction(action);
899 if (i->first == Labeller::ValueFromTwoLevelCounter) {
901 QMenu *cycleMenu = numberingMenu->addMenu(tr(
"Cycle size"));
902 QActionGroup *cycleGroup =
new QActionGroup(
this);
904 int cycles[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16 };
905 for (
int i = 0; i < int(
sizeof(cycles)/
sizeof(cycles[0])); ++i) {
906 action =
new QAction(QString(
"%1").arg(cycles[i]),
this);
908 action->setCheckable(
true);
909 action->setChecked(cycles[i] == m_labeller->getCounterCycleSize());
910 cycleGroup->addAction(action);
911 cycleMenu->addAction(action);
915 if (i->first == Labeller::ValueNone ||
916 i->first == Labeller::ValueFromTwoLevelCounter ||
917 i->first == Labeller::ValueFromRealTime) {
918 numberingMenu->addSeparator();
922 action =
new QAction(tr(
"Reset Numbering Counters"),
this);
923 action->setStatusTip(tr(
"Reset to 1 all the counters used for counter-based labelling"));
925 connect(
this, SIGNAL(replacedDocument()), action, SLOT(trigger()));
926 menu->addAction(action);
928 action =
new QAction(tr(
"Set Numbering Counters..."),
this);
929 action->setStatusTip(tr(
"Set the counters used for counter-based labelling"));
931 menu->addAction(action);
933 action =
new QAction(tr(
"Renumber Selected Instants"),
this);
934 action->setStatusTip(tr(
"Renumber the selected instants using the current labelling scheme"));
935 connect(action, SIGNAL(triggered()),
this, SLOT(renumberInstants()));
936 connect(
this, SIGNAL(canRenumberInstants(
bool)), action, SLOT(setEnabled(
bool)));
938 menu->addAction(action);
940 menu->addSeparator();
942 action =
new QAction(tr(
"Subdivide Selected Instants..."),
this);
943 action->setStatusTip(tr(
"Add new instants at regular intervals between the selected instants"));
945 connect(
this, SIGNAL(canSubdivideInstants(
bool)), action, SLOT(setEnabled(
bool)));
946 menu->addAction(action);
948 action =
new QAction(tr(
"Winnow Selected Instants..."),
this);
949 action->setStatusTip(tr(
"Remove subdivisions, leaving only every Nth instant"));
950 connect(action, SIGNAL(triggered()),
this, SLOT(
winnowInstants()));
951 connect(
this, SIGNAL(canWinnowInstants(
bool)), action, SLOT(setEnabled(
bool)));
952 menu->addAction(action);
958 SVDEBUG <<
"MainWindow::setupViewMenu" << endl;
964 QAction *action =
nullptr;
968 QMenu *menu = menuBar()->addMenu(tr(
"&View"));
969 menu->setTearOffEnabled(
true);
986 action =
new QAction(tr(
"&Jump Left"),
this);
987 action->setShortcut(tr(
"Ctrl+Left"));
988 action->setStatusTip(tr(
"Scroll the current pane a big step to the left"));
989 connect(action, SIGNAL(triggered()),
this, SLOT(jumpLeft()));
990 connect(
this, SIGNAL(canScroll(
bool)), action, SLOT(setEnabled(
bool)));
992 menu->addAction(action);
994 action =
new QAction(tr(
"J&ump Right"),
this);
995 action->setShortcut(tr(
"Ctrl+Right"));
996 action->setStatusTip(tr(
"Scroll the current pane a big step to the right"));
997 connect(action, SIGNAL(triggered()),
this, SLOT(jumpRight()));
998 connect(
this, SIGNAL(canScroll(
bool)), action, SLOT(setEnabled(
bool)));
1000 menu->addAction(action);
1002 action =
new QAction(tr(
"Peek Left"),
this);
1003 action->setShortcut(tr(
"Alt+Left"));
1004 action->setStatusTip(tr(
"Scroll the current pane to the left without moving the playback cursor or other panes"));
1005 connect(action, SIGNAL(triggered()),
this, SLOT(peekLeft()));
1006 connect(
this, SIGNAL(canScroll(
bool)), action, SLOT(setEnabled(
bool)));
1008 menu->addAction(action);
1010 action =
new QAction(tr(
"Peek Right"),
this);
1011 action->setShortcut(tr(
"Alt+Right"));
1012 action->setStatusTip(tr(
"Scroll the current pane to the right without moving the playback cursor or other panes"));
1013 connect(action, SIGNAL(triggered()),
this, SLOT(peekRight()));
1014 connect(
this, SIGNAL(canScroll(
bool)), action, SLOT(setEnabled(
bool)));
1016 menu->addAction(action);
1018 menu->addSeparator();
1023 tr(
"Zoom &In"),
this);
1026 connect(
m_zoomInAction, SIGNAL(triggered()),
this, SLOT(zoomIn()));
1027 connect(
this, SIGNAL(canZoom(
bool)),
m_zoomInAction, SLOT(setEnabled(
bool)));
1032 tr(
"Zoom &Out"),
this);
1036 connect(
this, SIGNAL(canZoom(
bool)),
m_zoomOutAction, SLOT(setEnabled(
bool)));
1040 action =
new QAction(tr(
"Restore &Default Zoom"),
this);
1041 action->setStatusTip(tr(
"Restore the zoom level to the default"));
1042 connect(action, SIGNAL(triggered()),
this, SLOT(zoomDefault()));
1043 connect(
this, SIGNAL(canZoom(
bool)), action, SLOT(setEnabled(
bool)));
1044 menu->addAction(action);
1047 tr(
"Zoom to &Fit"),
this);
1050 connect(
m_zoomFitAction, SIGNAL(triggered()),
this, SLOT(zoomToFit()));
1051 connect(
this, SIGNAL(canZoom(
bool)),
m_zoomFitAction, SLOT(setEnabled(
bool)));
1055 menu->addSeparator();
1059 action =
new QAction(tr(
"Show &Centre Line"),
this);
1060 action->setShortcut(tr(
"'"));
1061 action->setStatusTip(tr(
"Show or hide the centre line"));
1062 connect(action, SIGNAL(triggered()),
this, SLOT(toggleCentreLine()));
1063 action->setCheckable(
true);
1064 action->setChecked(m_viewManager->shouldShowCentreLine());
1066 menu->addAction(action);
1068 action =
new QAction(tr(
"Toggle All Time Rulers"),
this);
1069 action->setShortcut(tr(
"#"));
1070 action->setStatusTip(tr(
"Show or hide all time rulers"));
1071 connect(action, SIGNAL(triggered()),
this, SLOT(toggleTimeRulers()));
1073 menu->addAction(action);
1075 menu->addSeparator();
1077 QActionGroup *overlayGroup =
new QActionGroup(
this);
1079 ViewManager::OverlayMode mode = m_viewManager->getOverlayMode();
1081 action =
new QAction(tr(
"Show &No Overlays"),
this);
1082 action->setShortcut(tr(
"0"));
1083 action->setStatusTip(tr(
"Hide times, layer names, and scale"));
1084 connect(action, SIGNAL(triggered()),
this, SLOT(showNoOverlays()));
1085 action->setCheckable(
true);
1086 action->setChecked(mode == ViewManager::NoOverlays);
1087 overlayGroup->addAction(action);
1089 menu->addAction(action);
1091 action =
new QAction(tr(
"Show &Minimal Overlays"),
this);
1092 action->setShortcut(tr(
"9"));
1093 action->setStatusTip(tr(
"Show times and basic scale"));
1094 connect(action, SIGNAL(triggered()),
this, SLOT(showMinimalOverlays()));
1095 action->setCheckable(
true);
1096 action->setChecked(mode == ViewManager::StandardOverlays);
1097 overlayGroup->addAction(action);
1099 menu->addAction(action);
1101 action =
new QAction(tr(
"Show &All Overlays"),
this);
1102 action->setShortcut(tr(
"8"));
1103 action->setStatusTip(tr(
"Show times, layer names, and scale"));
1104 connect(action, SIGNAL(triggered()),
this, SLOT(showAllOverlays()));
1105 action->setCheckable(
true);
1106 action->setChecked(mode == ViewManager::AllOverlays);
1107 overlayGroup->addAction(action);
1109 menu->addAction(action);
1111 menu->addSeparator();
1113 action =
new QAction(tr(
"Show &Zoom Wheels"),
this);
1114 action->setShortcut(tr(
"Z"));
1115 action->setStatusTip(tr(
"Show thumbwheels for zooming horizontally and vertically"));
1116 connect(action, SIGNAL(triggered()),
this, SLOT(toggleZoomWheels()));
1117 action->setCheckable(
true);
1118 action->setChecked(m_viewManager->getZoomWheelsEnabled());
1120 menu->addAction(action);
1131 action =
new QAction(tr(
"Show Status &Bar"),
this);
1132 action->setStatusTip(tr(
"Show context help information in the status bar at the bottom of the window"));
1133 connect(action, SIGNAL(triggered()),
this, SLOT(toggleStatusBar()));
1134 action->setCheckable(
true);
1135 action->setChecked(
true);
1136 menu->addAction(action);
1139 settings.beginGroup(
"MainWindow");
1140 bool sb = settings.value(
"showstatusbar",
true).toBool();
1142 action->setChecked(
false);
1143 statusBar()->hide();
1145 settings.endGroup();
1147 menu->addSeparator();
1149 action =
new QAction(tr(
"Show La&yer Summary"),
this);
1150 action->setShortcut(tr(
"Y"));
1151 action->setStatusTip(tr(
"Open a window displaying the hierarchy of panes and layers in this session"));
1152 connect(action, SIGNAL(triggered()),
this, SLOT(
showLayerTree()));
1154 menu->addAction(action);
1156 action =
new QAction(tr(
"Show Acti&vity Log"),
this);
1157 action->setStatusTip(tr(
"Open a window listing interactions and other events"));
1159 menu->addAction(action);
1161 action =
new QAction(tr(
"Show &Unit Converter"),
this);
1162 action->setStatusTip(tr(
"Open a window of pitch and timing conversion utilities"));
1164 menu->addAction(action);
1166 menu->addSeparator();
1171 action =
new QAction(tr(
"Go Full-Screen"),
this);
1172 action->setShortcut(tr(
"F11"));
1173 action->setStatusTip(tr(
"Expand the pane area to the whole screen"));
1174 connect(action, SIGNAL(triggered()),
this, SLOT(
goFullScreen()));
1176 menu->addAction(action);
1183 QString shortcutText;
1186 #pragma GCC diagnostic ignored "-Wswitch-enum" 1190 case LayerFactory::Waveform:
1192 shortcutText = tr(
"W");
1194 shortcutText = tr(
"Shift+W");
1198 case LayerFactory::Spectrogram:
1200 shortcutText = tr(
"G");
1202 shortcutText = tr(
"Shift+G");
1206 case LayerFactory::MelodicRangeSpectrogram:
1208 shortcutText = tr(
"M");
1210 shortcutText = tr(
"Shift+M");
1214 case LayerFactory::PeakFrequencySpectrogram:
1216 shortcutText = tr(
"K");
1218 shortcutText = tr(
"Shift+K");
1222 case LayerFactory::Spectrum:
1224 shortcutText = tr(
"U");
1226 shortcutText = tr(
"Shift+U");
1234 return shortcutText;
1240 SVDEBUG <<
"MainWindow::setupPaneAndLayerMenus" << endl;
1242 Profiler profiler(
"MainWindow::setupPaneAndLayerMenus");
1249 m_paneActions.clear();
1251 m_paneMenu = menuBar()->addMenu(tr(
"&Pane"));
1268 m_layerActions.clear();
1283 QAction *action =
new QAction(il.load(
"pane"), tr(
"Add &New Pane"),
this);
1284 action->setShortcut(tr(
"N"));
1285 action->setStatusTip(tr(
"Add a new pane containing only a time ruler"));
1286 connect(action, SIGNAL(triggered()),
this, SLOT(
addPane()));
1287 connect(
this, SIGNAL(canAddPane(
bool)), action, SLOT(setEnabled(
bool)));
1290 menu->addAction(action);
1292 menu->addSeparator();
1296 LayerFactory::LayerTypeSet emptyLayerTypes =
1297 LayerFactory::getInstance()->getValidEmptyLayerTypes();
1299 for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin();
1300 i != emptyLayerTypes.end(); ++i) {
1303 QString mainText, tipText, channelText;
1304 LayerFactory::LayerType type = *i;
1305 QString name = LayerFactory::getInstance()->getLayerPresentationName(type);
1307 icon = il.load(LayerFactory::getInstance()->getLayerIconName(type));
1309 mainText = tr(
"Add New %1 Layer").arg(name);
1310 tipText = tr(
"Add a new empty layer of type %1").arg(name);
1312 action =
new QAction(icon, mainText,
this);
1313 action->setStatusTip(tipText);
1315 if (type == LayerFactory::Text) {
1316 action->setShortcut(tr(
"T"));
1320 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
1321 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
1323 menu->addAction(action);
1328 menu->addSeparator();
1330 LayerFactory::LayerType backgroundTypes[] = {
1331 LayerFactory::Waveform,
1332 LayerFactory::Spectrogram,
1333 LayerFactory::MelodicRangeSpectrogram,
1334 LayerFactory::PeakFrequencySpectrogram,
1335 LayerFactory::Spectrum
1337 int backgroundTypeCount = int(
sizeof(backgroundTypes) /
1338 sizeof(backgroundTypes[0]));
1340 std::vector<ModelId> models;
1341 if (m_document) models = m_document->getTransformInputModels();
1342 bool plural = (models.size() > 1);
1343 if (models.empty()) {
1344 models.push_back(getMainModelId());
1347 for (
int i = 0; i < backgroundTypeCount; ++i) {
1349 const int paneMenuType = 0, layerMenuType = 1;
1351 for (
int menuType = paneMenuType; menuType <= layerMenuType; ++menuType) {
1353 if (menuType == paneMenuType) menu =
m_paneMenu;
1356 QMenu *submenu =
nullptr;
1359 QString mainText, tipText, channelText;
1360 LayerFactory::LayerType type = backgroundTypes[i];
1363 QString shortcutText =
shortcutFor(type, menuType == paneMenuType);
1369 #pragma GCC diagnostic ignored "-Wswitch-enum" 1374 case LayerFactory::Waveform:
1375 icon = il.load(
"waveform");
1376 mainText = tr(
"Add &Waveform");
1377 if (menuType == paneMenuType) {
1378 tipText = tr(
"Add a new pane showing a waveform view");
1380 tipText = tr(
"Add a new layer showing a waveform view");
1385 case LayerFactory::Spectrogram:
1386 icon = il.load(
"spectrogram");
1387 mainText = tr(
"Add Spectro&gram");
1388 if (menuType == paneMenuType) {
1389 tipText = tr(
"Add a new pane showing a spectrogram");
1391 tipText = tr(
"Add a new layer showing a spectrogram");
1395 case LayerFactory::MelodicRangeSpectrogram:
1396 icon = il.load(
"spectrogram");
1397 mainText = tr(
"Add &Melodic Range Spectrogram");
1398 if (menuType == paneMenuType) {
1399 tipText = tr(
"Add a new pane showing a spectrogram set up for an overview of note pitches");
1401 tipText = tr(
"Add a new layer showing a spectrogram set up for an overview of note pitches");
1405 case LayerFactory::PeakFrequencySpectrogram:
1406 icon = il.load(
"spectrogram");
1407 mainText = tr(
"Add Pea&k Frequency Spectrogram");
1408 if (menuType == paneMenuType) {
1409 tipText = tr(
"Add a new pane showing a spectrogram set up for tracking frequencies");
1411 tipText = tr(
"Add a new layer showing a spectrogram set up for tracking frequencies");
1415 case LayerFactory::Spectrum:
1416 icon = il.load(
"spectrum");
1417 mainText = tr(
"Add Spectr&um");
1418 if (menuType == paneMenuType) {
1419 tipText = tr(
"Add a new pane showing a frequency spectrum");
1421 tipText = tr(
"Add a new layer showing a frequency spectrum");
1428 std::vector<ModelId> candidateModels = models;
1429 if (candidateModels.empty()) {
1430 throw std::logic_error(
"candidateModels should not be empty");
1433 for (
auto modelId: candidateModels) {
1435 auto model = ModelById::get(modelId);
1439 if (
auto dtvm = ModelById::getAs<DenseTimeValueModel>
1441 channels = dtvm->getChannelCount();
1444 if (channels < 1 && getMainModel()) {
1445 channels = getMainModel()->getChannelCount();
1447 if (channels < 1) channels = 1;
1449 for (
int c = 0; c <= channels; ++c) {
1451 if (c == 1 && channels == 1)
continue;
1452 bool isDefault = (c == 0);
1453 bool isOnly = (isDefault && (channels == 1));
1455 if (isOnly && !plural) {
1457 action =
new QAction(icon, mainText,
this);
1459 action->setShortcut(shortcutText);
1460 action->setStatusTip(tipText);
1461 if (menuType == paneMenuType) {
1462 connect(action, SIGNAL(triggered()),
1464 connect(
this, SIGNAL(canAddPane(
bool)),
1465 action, SLOT(setEnabled(
bool)));
1469 connect(action, SIGNAL(triggered()),
1471 connect(
this, SIGNAL(canAddLayer(
bool)),
1472 action, SLOT(setEnabled(
bool)));
1476 if (shortcutText !=
"") {
1479 menu->addAction(action);
1484 submenu = menu->addMenu(mainText);
1485 submenu->setTearOffEnabled(
true);
1486 }
else if (isDefault) {
1487 submenu->addSeparator();
1493 actionText = tr(
"&All Channels Mixed");
1495 actionText = tr(
"&All Channels");
1498 actionText = tr(
"Channel &%1").arg(c);
1502 actionText = tr(
"%1: %2")
1503 .arg(model->objectName())
1508 action =
new QAction(icon, actionText,
this);
1509 if (!model || modelId == getMainModelId()) {
1516 action->setShortcut(shortcutText);
1519 action =
new QAction(actionText,
this);
1522 action->setStatusTip(tipText);
1524 if (menuType == paneMenuType) {
1525 connect(action, SIGNAL(triggered()),
1527 connect(
this, SIGNAL(canAddPane(
bool)),
1528 action, SLOT(setEnabled(
bool)));
1532 connect(action, SIGNAL(triggered()),
1534 connect(
this, SIGNAL(canAddLayer(
bool)),
1535 action, SLOT(setEnabled(
bool)));
1540 submenu->addAction(action);
1543 if (isDefault && menuType == layerMenuType &&
1544 modelId == *candidateModels.begin()) {
1548 action =
new QAction(icon, mainText,
this);
1549 action->setStatusTip(tipText);
1550 connect(action, SIGNAL(triggered()),
1552 connect(
this, SIGNAL(canAddLayer(
bool)),
1553 action, SLOT(setEnabled(
bool)));
1566 menu->addSeparator();
1568 action =
new QAction(tr(
"Switch to Previous Pane"),
this);
1569 action->setShortcut(tr(
"["));
1570 action->setStatusTip(tr(
"Make the next pane up in the pane stack current"));
1571 connect(action, SIGNAL(triggered()),
this, SLOT(previousPane()));
1572 connect(
this, SIGNAL(canSelectPreviousPane(
bool)), action, SLOT(setEnabled(
bool)));
1574 menu->addAction(action);
1576 action =
new QAction(tr(
"Switch to Next Pane"),
this);
1577 action->setShortcut(tr(
"]"));
1578 action->setStatusTip(tr(
"Make the next pane down in the pane stack current"));
1579 connect(action, SIGNAL(triggered()),
this, SLOT(nextPane()));
1580 connect(
this, SIGNAL(canSelectNextPane(
bool)), action, SLOT(setEnabled(
bool)));
1582 menu->addAction(action);
1584 menu->addSeparator();
1586 action =
new QAction(il.load(
"editdelete"), tr(
"&Delete Pane"),
this);
1587 action->setShortcut(tr(
"Ctrl+Shift+D"));
1588 action->setStatusTip(tr(
"Delete the currently active pane"));
1589 connect(action, SIGNAL(triggered()),
this, SLOT(deleteCurrentPane()));
1590 connect(
this, SIGNAL(canDeleteCurrentPane(
bool)), action, SLOT(setEnabled(
bool)));
1592 menu->addAction(action);
1596 action =
new QAction(il.load(
"timeruler"), tr(
"Add &Time Ruler"),
this);
1597 action->setStatusTip(tr(
"Add a new layer showing a time ruler"));
1598 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
1599 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
1601 menu->addAction(action);
1603 menu->addSeparator();
1609 m_sliceMenu = menu->addMenu(tr(
"Add S&lice of Layer"));
1615 menu->addSeparator();
1617 action =
new QAction(tr(
"Switch to Previous Layer"),
this);
1618 action->setShortcut(tr(
"{"));
1619 action->setStatusTip(tr(
"Make the previous layer in the pane current"));
1620 connect(action, SIGNAL(triggered()),
this, SLOT(previousLayer()));
1621 connect(
this, SIGNAL(canSelectPreviousLayer(
bool)), action, SLOT(setEnabled(
bool)));
1623 menu->addAction(action);
1625 action =
new QAction(tr(
"Switch to Next Layer"),
this);
1626 action->setShortcut(tr(
"}"));
1627 action->setStatusTip(tr(
"Make the next layer in the pane current"));
1628 connect(action, SIGNAL(triggered()),
this, SLOT(nextLayer()));
1629 connect(
this, SIGNAL(canSelectNextLayer(
bool)), action, SLOT(setEnabled(
bool)));
1631 menu->addAction(action);
1634 menu->addSeparator();
1636 QAction *raction =
new QAction(tr(
"&Rename Layer..."),
this);
1637 raction->setShortcut(tr(
"R"));
1638 raction->setStatusTip(tr(
"Rename the currently active layer"));
1640 connect(
this, SIGNAL(canRenameLayer(
bool)), raction, SLOT(setEnabled(
bool)));
1641 menu->addAction(raction);
1644 QAction *eaction =
new QAction(tr(
"Edit Layer Data"),
this);
1645 eaction->setShortcut(tr(
"E"));
1646 eaction->setStatusTip(tr(
"Edit the currently active layer as a data grid"));
1647 connect(eaction, SIGNAL(triggered()),
this, SLOT(editCurrentLayer()));
1648 connect(
this, SIGNAL(canEditLayerTabular(
bool)), eaction, SLOT(setEnabled(
bool)));
1649 menu->addAction(eaction);
1652 action =
new QAction(il.load(
"editdelete"), tr(
"&Delete Layer"),
this);
1653 action->setShortcut(tr(
"Ctrl+D"));
1654 action->setStatusTip(tr(
"Delete the currently active layer"));
1655 connect(action, SIGNAL(triggered()),
this, SLOT(deleteCurrentLayer()));
1656 connect(
this, SIGNAL(canDeleteCurrentLayer(
bool)), action, SLOT(setEnabled(
bool)));
1658 menu->addAction(action);
1674 set<LayerFactory::LayerType> seen;
1677 if (a.second.sourceModel.isNone()) {
1680 auto type = a.second.layer;
1681 if (a.second.sourceModel == modelId && seen.find(type) == seen.end()) {
1685 a.first->setShortcut(QString());
1692 if (a.second.sourceModel.isNone()) {
1695 auto type = a.second.layer;
1696 if (a.second.sourceModel == modelId && seen.find(type) == seen.end()) {
1700 a.first->setShortcut(QString());
1708 SVDEBUG <<
"MainWindow::prepareTransformsMenu" << endl;
1719 pending->setEnabled(
false);
1721 SVDEBUG <<
"MainWindow::prepareTransformsMenu: Starting installed-transform population thread" << endl;
1731 TransformFactory *tf = TransformFactory::getInstance();
1734 connect(tf, SIGNAL(transformsPopulated()),
1737 SVDEBUG <<
"MainWindow::TransformPopulater::run: scanning" << endl;
1739 PluginScan::getInstance()->scan();
1741 SVDEBUG <<
"MainWindow::TransformPopulater::run: populating" << endl;
1743 (void)tf->haveTransform({});
1745 SVDEBUG <<
"MainWindow::TransformPopulater::run: done" << endl;
1751 SVDEBUG <<
"MainWindow::populateTransformsMenu" << endl;
1767 TransformFactory *factory = TransformFactory::getInstance();
1769 TransformList transforms = factory->getAllTransformDescriptions();
1785 QString warning = factory->getStartupFailureReport();
1786 if (warning !=
"") {
1787 SVDEBUG <<
"MainWindow::populateTransformsMenu: Transform population yielded errors" << endl;
1790 warning = PluginScan::getInstance()->getStartupFailureReport();
1791 if (warning !=
"") {
1792 SVDEBUG <<
"MainWindow::populateTransformsMenu: Plugin scan yielded errors" << endl;
1797 vector<TransformDescription::Type> types = factory->getAllTransformTypes();
1799 map<TransformDescription::Type, map<QString, SubdividingMenu *> > categoryMenus;
1800 map<TransformDescription::Type, map<QString, SubdividingMenu *> > makerMenus;
1802 map<TransformDescription::Type, SubdividingMenu *> byPluginNameMenus;
1803 map<TransformDescription::Type, map<QString, QMenu *> > pluginNameMenus;
1805 set<SubdividingMenu *> pendingMenus;
1810 connect(&m_recentTransforms, SIGNAL(recentChanged()),
1816 for (vector<TransformDescription::Type>::iterator i = types.begin();
1817 i != types.end(); ++i) {
1819 if (i != types.begin()) {
1824 QString byCategoryLabel = tr(
"%1 by Category")
1825 .arg(factory->getTransformTypeName(*i));
1826 SubdividingMenu *byCategoryMenu =
new SubdividingMenu(byCategoryLabel,
1828 byCategoryMenu->setTearOffEnabled(
true);
1831 pendingMenus.insert(byCategoryMenu);
1833 vector<QString> categories = factory->getTransformCategories(*i);
1835 for (vector<QString>::iterator j = categories.begin();
1836 j != categories.end(); ++j) {
1838 QString category = *j;
1839 if (category ==
"") category = tr(
"Unclassified");
1841 if (categories.size() < 2) {
1842 categoryMenus[*i][category] = byCategoryMenu;
1846 QStringList components = category.split(
" > ");
1849 for (QStringList::iterator k = components.begin();
1850 k != components.end(); ++k) {
1852 QString parentKey = key;
1853 if (key !=
"") key +=
" > ";
1856 if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) {
1857 SubdividingMenu *m =
new SubdividingMenu(*k, 20, 40);
1858 m->setTearOffEnabled(
true);
1859 pendingMenus.insert(m);
1860 categoryMenus[*i][key] = m;
1861 if (parentKey ==
"") {
1862 byCategoryMenu->addMenu(m);
1864 categoryMenus[*i][parentKey]->addMenu(m);
1870 QString byPluginNameLabel = tr(
"%1 by Plugin Name")
1871 .arg(factory->getTransformTypeName(*i));
1872 byPluginNameMenus[*i] =
new SubdividingMenu(byPluginNameLabel);
1873 byPluginNameMenus[*i]->setTearOffEnabled(
true);
1876 pendingMenus.insert(byPluginNameMenus[*i]);
1878 QString byMakerLabel = tr(
"%1 by Maker")
1879 .arg(factory->getTransformTypeName(*i));
1880 SubdividingMenu *byMakerMenu =
new SubdividingMenu(byMakerLabel, 20, 40);
1881 byMakerMenu->setTearOffEnabled(
true);
1884 pendingMenus.insert(byMakerMenu);
1886 vector<QString> makers = factory->getTransformMakers(*i);
1888 for (vector<QString>::iterator j = makers.begin();
1889 j != makers.end(); ++j) {
1892 if (maker ==
"") maker = tr(
"Unknown");
1893 maker.replace(QRegExp(tr(
" [\\(<].*$")),
"");
1895 makerMenus[*i][maker] =
new SubdividingMenu(maker, 30, 40);
1896 makerMenus[*i][maker]->setTearOffEnabled(
true);
1897 byMakerMenu->addMenu(makerMenus[*i][maker]);
1898 pendingMenus.insert(makerMenus[*i][maker]);
1905 std::map<QString, QString> idNameSonameMap;
1906 std::set<QString> seenNames, duplicateNames;
1907 for (
int i = 0; in_range_for(transforms, i); ++i) {
1908 QString name = transforms[i].name;
1909 if (seenNames.find(name) != seenNames.end()) {
1910 duplicateNames.insert(name);
1912 seenNames.insert(name);
1919 for (
int i = 0; in_range_for(transforms, i); ++i) {
1921 QString name = transforms[i].name;
1922 if (name ==
"") name = transforms[i].identifier;
1926 TransformDescription::Type type = transforms[i].type;
1927 QString typeStr = factory->getTransformTypeName(type);
1929 QString category = transforms[i].category;
1930 if (category ==
"") category = tr(
"Unclassified");
1932 QString maker = transforms[i].maker;
1933 if (maker ==
"") maker = tr(
"Unknown");
1934 maker.replace(QRegExp(tr(
" [\\(<].*$")),
"");
1936 QString pluginName = name.section(
": ", 0, 0);
1937 QString output = name.section(
": ", 1);
1939 if (duplicateNames.find(pluginName) != duplicateNames.end()) {
1940 pluginName = QString(
"%1 <%2>")
1942 .arg(transforms[i].identifier.section(
':', 1, 1));
1943 if (output ==
"") name = pluginName;
1944 else name = QString(
"%1: %2")
1949 QAction *action =
new QAction(tr(
"%1...").arg(name),
this);
1950 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
1953 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
1955 action->setStatusTip(transforms[i].longDescription);
1957 if (categoryMenus[type].find(category) == categoryMenus[type].end()) {
1958 cerr <<
"WARNING: MainWindow::setupMenus: Internal error: " 1959 <<
"No category menu for transform \"" 1960 << name <<
"\" (category = \"" 1961 << category <<
"\")" << endl;
1963 categoryMenus[type][category]->addAction(action);
1966 if (makerMenus[type].find(maker) == makerMenus[type].end()) {
1967 cerr <<
"WARNING: MainWindow::setupMenus: Internal error: " 1968 <<
"No maker menu for transform \"" 1969 << name <<
"\" (maker = \"" 1970 << maker <<
"\")" << endl;
1972 makerMenus[type][maker]->addAction(action);
1975 action =
new QAction(tr(
"%1...").arg(output ==
"" ? pluginName : output),
this);
1976 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
1978 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
1979 action->setStatusTip(transforms[i].longDescription);
1983 if (pluginNameMenus[type].find(pluginName) ==
1984 pluginNameMenus[type].end()) {
1986 SubdividingMenu *parentMenu = byPluginNameMenus[type];
1987 parentMenu->setTearOffEnabled(
true);
1990 parentMenu->addAction(pluginName, action);
1992 pluginNameMenus[type][pluginName] =
1993 parentMenu->addMenu(pluginName);
1994 connect(
this, SIGNAL(canAddLayer(
bool)),
1995 pluginNameMenus[type][pluginName],
1996 SLOT(setEnabled(
bool)));
2000 if (pluginNameMenus[type].find(pluginName) !=
2001 pluginNameMenus[type].end()) {
2002 pluginNameMenus[type][pluginName]->addAction(action);
2006 for (set<SubdividingMenu *>::iterator i = pendingMenus.begin();
2007 i != pendingMenus.end(); ++i) {
2008 (*i)->entriesAdded();
2014 QAction *action =
new QAction(tr(
"Find a Transform..."),
this);
2015 action->setStatusTip(tr(
"Search for a transform from the installed plugins, by name or description"));
2016 action->setShortcut(tr(
"Ctrl+M"));
2017 connect(action, SIGNAL(triggered()),
this, SLOT(
findTransform()));
2031 SVDEBUG <<
"MainWindow::setupHelpMenu" << endl;
2033 QMenu *menu = menuBar()->addMenu(tr(
"&Help"));
2034 menu->setTearOffEnabled(
true);
2040 QString name = QApplication::applicationName();
2042 QAction *action =
new QAction(il.load(
"help"),
2043 tr(
"&Help Reference"),
this);
2044 action->setShortcut(tr(
"F1"));
2045 action->setStatusTip(tr(
"Open the %1 reference manual").arg(name));
2046 connect(action, SIGNAL(triggered()),
this, SLOT(
help()));
2048 menu->addAction(action);
2050 action =
new QAction(tr(
"&Key and Mouse Reference"),
this);
2051 action->setShortcut(tr(
"F2"));
2052 action->setStatusTip(tr(
"Open a window showing the keystrokes you can use in %1").arg(name));
2053 connect(action, SIGNAL(triggered()),
this, SLOT(
keyReference()));
2055 menu->addAction(action);
2057 action =
new QAction(tr(
"What's &New In This Release?"),
this);
2058 action->setStatusTip(tr(
"List the changes in this release (and every previous release) of %1").arg(name));
2059 connect(action, SIGNAL(triggered()),
this, SLOT(
whatsNew()));
2060 menu->addAction(action);
2062 action =
new QAction(tr(
"&About %1").arg(name),
this);
2063 action->setStatusTip(tr(
"Show information about %1").arg(name));
2064 connect(action, SIGNAL(triggered()),
this, SLOT(
about()));
2065 menu->addAction(action);
2071 SVDEBUG <<
"MainWindow::setupRecentFilesMenu" << endl;
2074 vector<QString> files = m_recentFiles.getRecent();
2075 for (
size_t i = 0; i < files.size(); ++i) {
2076 QString path = files[i];
2078 action->setObjectName(path);
2079 connect(action, SIGNAL(triggered()),
this, SLOT(
openRecentFile()));
2081 action->setShortcut(tr(
"Ctrl+R"));
2084 action->shortcut().toString(),
2085 tr(
"Re-open the current or most recently opened file"));
2093 SVDEBUG <<
"MainWindow::setupTemplatesMenu" << endl;
2097 QAction *defaultAction =
m_templatesMenu->addAction(tr(
"Standard Waveform"));
2098 defaultAction->setObjectName(
"default");
2099 connect(defaultAction, SIGNAL(triggered()),
this, SLOT(
applyTemplate()));
2103 QAction *action =
nullptr;
2105 QStringList templates = ResourceFinder().getResourceFiles(
"templates",
"svt");
2107 bool havePersonal =
false;
2110 std::set<QString> byName;
2111 foreach (QString t, templates) {
2112 if (!t.startsWith(
":")) havePersonal =
true;
2113 byName.insert(QFileInfo(t).baseName());
2116 foreach (QString t, byName) {
2117 if (t.toLower() ==
"default")
continue;
2119 connect(action, SIGNAL(triggered()),
this, SLOT(
applyTemplate()));
2133 QAction *setDefaultAction =
m_templatesMenu->addAction(tr(
"Choose Default Template..."));
2134 setDefaultAction->setObjectName(
"set_default_template");
2135 connect(setDefaultAction, SIGNAL(triggered()),
this, SLOT(
preferences()));
2144 SVDEBUG <<
"MainWindow::setupRecentTransformsMenu" << endl;
2147 vector<QString> transforms = m_recentTransforms.getRecent();
2148 for (
size_t i = 0; i < transforms.size(); ++i) {
2149 TransformActionReverseMap::iterator ti =
2152 cerr <<
"WARNING: MainWindow::setupRecentTransformsMenu: " 2153 <<
"Unknown transform \"" << transforms[i]
2154 <<
"\" in recent transforms list" << endl;
2158 ti->second->setShortcut(tr(
"Ctrl+T"));
2160 (tr(
"Repeat Transform"),
2161 ti->second->shortcut().toString(),
2162 tr(
"Re-select the most recently run transform"));
2164 ti->second->setShortcut(QString(
""));
2173 SVDEBUG <<
"MainWindow::setupExistingLayersMenus" << endl;
2179 Profiler profiler1(
"MainWindow::setupExistingLayersMenu");
2185 m_existingLayerActions.clear();
2191 m_sliceActions.clear();
2195 vector<Layer *> orderedLayers;
2196 set<Layer *> observedLayers;
2197 set<Layer *> sliceableLayers;
2199 LayerFactory *factory = LayerFactory::getInstance();
2201 for (
int i = 0; i < m_paneStack->getPaneCount(); ++i) {
2203 Pane *pane = m_paneStack->getPane(i);
2204 if (!pane)
continue;
2206 for (
int j = 0; j < pane->getLayerCount(); ++j) {
2208 Layer *layer = pane->getLayer(j);
2209 if (!layer)
continue;
2210 if (observedLayers.find(layer) != observedLayers.end()) {
2218 orderedLayers.push_back(layer);
2219 observedLayers.insert(layer);
2221 if (factory->isLayerSliceable(layer)) {
2222 sliceableLayers.insert(layer);
2227 Profiler profiler3(
"MainWindow::setupExistingLayersMenu: after sorting");
2229 map<QString, int> observedNames;
2231 for (
size_t i = 0; i < orderedLayers.size(); ++i) {
2233 Layer *layer = orderedLayers[i];
2235 QString name = layer->getLayerPresentationName();
2236 int n = ++observedNames[name];
2237 if (n > 1) name = QString(
"%1 <%2>").arg(name).arg(n);
2239 QIcon icon = il.load(factory->getLayerIconName
2240 (factory->getLayerType(layer)));
2242 QAction *action =
new QAction(icon, name,
this);
2243 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
2244 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
2245 m_existingLayerActions.push_back({ action, layer });
2249 if (sliceableLayers.find(layer) != sliceableLayers.end()) {
2250 action =
new QAction(icon, name,
this);
2251 connect(action, SIGNAL(triggered()),
this, SLOT(
addLayer()));
2252 connect(
this, SIGNAL(canAddLayer(
bool)), action, SLOT(setEnabled(
bool)));
2253 m_sliceActions.push_back({ action, layer });
2264 SVDEBUG <<
"MainWindow::setupToolbars" << endl;
2266 m_keyReference->setCategory(tr(
"Playback and Transport Controls"));
2270 QMenu *menu =
m_playbackMenu = menuBar()->addMenu(tr(
"Play&back"));
2271 menu->setTearOffEnabled(
true);
2275 QToolBar *toolbar = addToolBar(tr(
"Playback Toolbar"));
2278 tr(
"Rewind to Start"));
2282 connect(
this, SIGNAL(canPlay(
bool)),
m_rwdStartAction, SLOT(setEnabled(
bool)));
2284 m_rwdAction = toolbar->addAction(il.load(
"rewind"), tr(
"Rewind"));
2286 m_rwdAction->setStatusTip(tr(
"Rewind to the previous time instant or time ruler notch"));
2287 connect(
m_rwdAction, SIGNAL(triggered()),
this, SLOT(rewind()));
2288 connect(
this, SIGNAL(canRewind(
bool)),
m_rwdAction, SLOT(setEnabled(
bool)));
2292 m_rwdSimilarAction->setStatusTip(tr(
"Rewind to the previous similarly valued time instant"));
2296 m_playAction = toolbar->addAction(il.load(
"playpause"),
2297 tr(
"Play / Pause"));
2307 m_playAction->setStatusTip(tr(
"Start or stop playback from the current position"));
2308 connect(
m_playAction, SIGNAL(triggered()),
this, SLOT(play()));
2313 connect(
this, SIGNAL(canPlay(
bool)),
m_playAction, SLOT(setEnabled(
bool)));
2316 tr(
"Fast Forward"));
2318 m_ffwdAction->setStatusTip(tr(
"Fast-forward to the next time instant or time ruler notch"));
2319 connect(
m_ffwdAction, SIGNAL(triggered()),
this, SLOT(ffwd()));
2320 connect(
this, SIGNAL(canFfwd(
bool)),
m_ffwdAction, SLOT(setEnabled(
bool)));
2324 m_ffwdSimilarAction->setStatusTip(tr(
"Fast-forward to the next similarly valued time instant"));
2329 tr(
"Fast Forward to End"));
2333 connect(
this, SIGNAL(canPlay(
bool)),
m_ffwdEndAction, SLOT(setEnabled(
bool)));
2340 connect(
m_recordAction, SIGNAL(triggered()),
this, SLOT(record()));
2341 connect(m_recordTarget, SIGNAL(recordStatusChanged(
bool)),
2343 connect(
this, SIGNAL(canRecord(
bool)),
2346 toolbar = addToolBar(tr(
"Play Mode Toolbar"));
2349 tr(
"Constrain Playback to Selection"));
2354 connect(m_viewManager, SIGNAL(playSelectionModeChanged(
bool)),
2360 tr(
"Loop Playback"));
2365 connect(m_viewManager, SIGNAL(playLoopModeChanged(
bool)),
2367 connect(
m_playLoopAction, SIGNAL(triggered()),
this, SLOT(playLoopToggled()));
2368 connect(
this, SIGNAL(canPlay(
bool)),
m_playLoopAction, SLOT(setEnabled(
bool)));
2371 tr(
"Solo Current Pane"));
2373 m_soloAction->setChecked(m_viewManager->getPlaySoloMode());
2374 m_prevSolo = m_viewManager->getPlaySoloMode();
2376 m_soloAction->setStatusTip(tr(
"Solo the current pane during playback"));
2377 connect(m_viewManager, SIGNAL(playSoloModeChanged(
bool)),
2382 QAction *alAction = toolbar->addAction(il.load(
"align"),
2383 tr(
"Align File Timelines"));
2384 alAction->setCheckable(
true);
2385 alAction->setChecked(m_viewManager->getAlignMode());
2386 alAction->setStatusTip(tr(
"Treat multiple audio files as versions of the same work, and align their timelines"));
2387 alAction->setEnabled(
false);
2388 connect(m_viewManager, SIGNAL(alignModeChanged(
bool)),
2389 alAction, SLOT(setChecked(
bool)));
2390 connect(alAction, SIGNAL(triggered()),
this, SLOT(
alignToggled()));
2391 connect(
this, SIGNAL(
canAlign(
bool)), alAction, SLOT(setEnabled(
bool)));
2411 menu->addAction(alAction);
2412 menu->addSeparator();
2415 menu->addSeparator();
2418 menu->addSeparator();
2421 menu->addSeparator();
2423 menu->addSeparator();
2440 QAction *fastAction = menu->addAction(tr(
"Speed Up"));
2441 fastAction->setShortcut(tr(
"Ctrl+PgUp"));
2442 fastAction->setStatusTip(tr(
"Time-stretch playback to speed it up without changing pitch"));
2443 connect(fastAction, SIGNAL(triggered()),
this, SLOT(
speedUpPlayback()));
2444 connect(
this, SIGNAL(canSpeedUpPlayback(
bool)), fastAction, SLOT(setEnabled(
bool)));
2446 QAction *slowAction = menu->addAction(tr(
"Slow Down"));
2447 slowAction->setShortcut(tr(
"Ctrl+PgDown"));
2448 slowAction->setStatusTip(tr(
"Time-stretch playback to slow it down without changing pitch"));
2450 connect(
this, SIGNAL(canSlowDownPlayback(
bool)), slowAction, SLOT(setEnabled(
bool)));
2452 QAction *normalAction = menu->addAction(tr(
"Restore Normal Speed"));
2453 normalAction->setShortcut(tr(
"Ctrl+Home"));
2454 normalAction->setStatusTip(tr(
"Restore non-time-stretched playback"));
2456 connect(
this, SIGNAL(canChangePlaybackSpeed(
bool)), normalAction, SLOT(setEnabled(
bool)));
2466 toolbar = addToolBar(tr(
"Edit Toolbar"));
2467 CommandHistory::getInstance()->registerToolbar(toolbar);
2469 toolbar = addToolBar(tr(
"Tools Toolbar"));
2470 QActionGroup *group =
new QActionGroup(
this);
2474 QAction *action = toolbar->addAction(il.load(
"navigate"), tr(
"Navigate"));
2475 action->setCheckable(
true);
2476 action->setChecked(
true);
2477 action->setShortcut(tr(
"1"));
2478 action->setStatusTip(tr(
"Navigate"));
2480 connect(
this, SIGNAL(replacedDocument()), action, SLOT(trigger()));
2481 group->addAction(action);
2483 m_toolActions.push_back({ ViewManager::NavigateMode, action });
2486 (tr(
"Navigate Tool Mouse Actions"));
2488 (tr(
"Navigate"), tr(
"Left"),
2489 tr(
"Click left button and drag to move around"));
2491 (tr(
"Zoom to Area"), tr(
"Shift+Left"),
2492 tr(
"Shift-click left button and drag to zoom to a rectangular area"));
2494 (tr(
"Relocate"), tr(
"Double-Click Left"),
2495 tr(
"Double-click left button to jump to clicked location"));
2497 (tr(
"Edit"), tr(
"Double-Click Left"),
2498 tr(
"Double-click left button on an item to edit it"));
2501 action = toolbar->addAction(il.load(
"select"), tr(
"Select"));
2502 action->setCheckable(
true);
2503 action->setShortcut(tr(
"2"));
2504 action->setStatusTip(tr(
"Select ranges"));
2506 group->addAction(action);
2508 m_toolActions.push_back({ ViewManager::SelectMode, action });
2511 (tr(
"Select Tool Mouse Actions"));
2513 (tr(
"Select"), tr(
"Left"),
2514 tr(
"Click left button and drag to select region; drag region edge to resize"));
2517 (tr(
"Multi Select"), tr(
"Ctrl+Left"),
2518 tr(
"Cmd-click left button and drag to select an additional region"));
2521 (tr(
"Multi Select"), tr(
"Ctrl+Left"),
2522 tr(
"Ctrl-click left button and drag to select an additional region"));
2525 (tr(
"Fine Select"), tr(
"Shift+Left"),
2526 tr(
"Shift-click left button and drag to select without snapping to items or grid"));
2529 action = toolbar->addAction(il.load(
"move"), tr(
"Edit"));
2530 action->setCheckable(
true);
2531 action->setShortcut(tr(
"3"));
2532 action->setStatusTip(tr(
"Edit items in layer"));
2534 connect(
this, SIGNAL(canEditLayer(
bool)), action, SLOT(setEnabled(
bool)));
2535 group->addAction(action);
2540 (tr(
"Edit Tool Mouse Actions"));
2542 (tr(
"Move"), tr(
"Left"),
2543 tr(
"Click left button on an item or selected region and drag to move"));
2545 (tr(
"Edit"), tr(
"Double-Click Left"),
2546 tr(
"Double-click left button on an item to edit it"));
2549 action = toolbar->addAction(il.load(
"draw"), tr(
"Draw"));
2550 action->setCheckable(
true);
2551 action->setShortcut(tr(
"4"));
2552 action->setStatusTip(tr(
"Draw new items in layer"));
2554 connect(
this, SIGNAL(canEditLayer(
bool)), action, SLOT(setEnabled(
bool)));
2555 group->addAction(action);
2560 (tr(
"Draw Tool Mouse Actions"));
2562 (tr(
"Draw"), tr(
"Left"),
2563 tr(
"Click left button and drag to create new item"));
2566 action = toolbar->addAction(il.load(
"erase"), tr(
"Erase"));
2567 action->setCheckable(
true);
2568 action->setShortcut(tr(
"5"));
2569 action->setStatusTip(tr(
"Erase items from layer"));
2571 connect(
this, SIGNAL(canEditLayer(
bool)), action, SLOT(setEnabled(
bool)));
2572 group->addAction(action);
2574 m_toolActions.push_back({ ViewManager::EraseMode, action });
2577 (tr(
"Erase Tool Mouse Actions"));
2579 (tr(
"Erase"), tr(
"Left"),
2580 tr(
"Click left button on an item to remove it from the layer"));
2583 action = toolbar->addAction(il.load(
"measure"), tr(
"Measure"));
2584 action->setCheckable(
true);
2585 action->setShortcut(tr(
"6"));
2586 action->setStatusTip(tr(
"Make measurements in layer"));
2588 connect(
this, SIGNAL(canMeasureLayer(
bool)), action, SLOT(setEnabled(
bool)));
2589 group->addAction(action);
2591 m_toolActions.push_back({ ViewManager::MeasureMode, action });
2594 (tr(
"Measure Tool Mouse Actions"));
2596 (tr(
"Measure Area"), tr(
"Left"),
2597 tr(
"Click left button and drag to measure a rectangular area"));
2599 (tr(
"Measure Item"), tr(
"Double-Click Left"),
2600 tr(
"Click left button and drag to measure extents of an item or shape"));
2602 (tr(
"Zoom to Area"), tr(
"Shift+Left"),
2603 tr(
"Shift-click left button and drag to zoom to a rectangular area"));
2613 MainWindowBase::connectLayerEditDialog(dialog);
2614 QToolBar *toolbar = dialog->getPlayToolbar();
2627 SVDEBUG <<
"MainWindow::updateMenuStates" << endl;
2629 MainWindowBase::updateMenuStates();
2631 Pane *currentPane =
nullptr;
2632 Layer *currentLayer =
nullptr;
2634 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
2635 if (currentPane) currentLayer = currentPane->getSelectedLayer();
2637 bool haveCurrentPane =
2638 (currentPane !=
nullptr);
2639 bool haveCurrentLayer =
2641 (currentLayer !=
nullptr));
2642 bool havePlayTarget =
2643 (m_playTarget !=
nullptr || m_audioIO !=
nullptr);
2644 bool haveSelection =
2646 !m_viewManager->getSelections().empty());
2647 bool haveCurrentEditableLayer =
2648 (haveCurrentLayer &&
2649 currentLayer->isLayerEditable());
2650 bool haveCurrentTimeInstantsLayer =
2651 (haveCurrentLayer &&
2652 dynamic_cast<TimeInstantLayer *
>(currentLayer));
2653 bool haveCurrentTimeValueLayer =
2654 (haveCurrentLayer &&
2655 dynamic_cast<TimeValueLayer *
>(currentLayer));
2657 bool alignMode = m_viewManager && m_viewManager->getAlignMode();
2660 if (TransformFactory::getInstance()->havePopulated()) {
2661 emit
canAlign(havePlayTarget && m_document && m_document->canAlign());
2664 emit canChangePlaybackSpeed(
true);
2666 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
2667 emit canSlowDownPlayback(v >
m_playSpeed->minimum());
2669 if (m_viewManager &&
2670 (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
2671 emit canDeleteSelection(haveCurrentLayer);
2675 emit canDeleteSelection(haveSelection && haveCurrentEditableLayer);
2681 if (haveCurrentTimeInstantsLayer) {
2682 m_ffwdAction->setText(tr(
"Fast Forward to Next Instant"));
2683 m_ffwdAction->setStatusTip(tr(
"Fast forward to the next time instant in the current layer"));
2684 m_rwdAction->setText(tr(
"Rewind to Previous Instant"));
2685 m_rwdAction->setStatusTip(tr(
"Rewind to the previous time instant in the current layer"));
2686 }
else if (haveCurrentTimeValueLayer) {
2687 m_ffwdAction->setText(tr(
"Fast Forward to Next Point"));
2688 m_ffwdAction->setStatusTip(tr(
"Fast forward to the next point in the current layer"));
2689 m_rwdAction->setText(tr(
"Rewind to Previous Point"));
2690 m_rwdAction->setStatusTip(tr(
"Rewind to the previous point in the current layer"));
2703 if (!getMainModel()) {
2708 QString description;
2712 sv_samplerate_t ssr = getMainModel()->getSampleRate();
2713 sv_samplerate_t tsr = ssr;
2714 if (m_playSource) tsr = m_playSource->getDeviceSampleRate();
2717 description = tr(
"%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
2719 description = QString(
"%1Hz").arg(ssr);
2722 description = QString(
"%1 - %2")
2723 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
2724 .toText(
false).c_str())
2734 MainWindowBase::documentModified();
2741 MainWindowBase::documentRestored();
2747 m_viewManager->setToolMode(ViewManager::NavigateMode);
2753 m_viewManager->setToolMode(ViewManager::SelectMode);
2759 m_viewManager->setToolMode(ViewManager::EditMode);
2765 m_viewManager->setToolMode(ViewManager::DrawMode);
2771 m_viewManager->setToolMode(ViewManager::EraseMode);
2777 m_viewManager->setToolMode(ViewManager::MeasureMode);
2783 QString path = getOpenFileName(FileFinder::AudioFile);
2786 if (openAudio(path, ReplaceSession) == FileOpenFailed) {
2788 QMessageBox::critical(
this, tr(
"Failed to open file"),
2789 tr(
"<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
2797 QString path = getOpenFileName(FileFinder::AudioFile);
2800 if (openAudio(path, CreateAdditionalModel) == FileOpenFailed) {
2802 QMessageBox::critical(
this, tr(
"Failed to open file"),
2803 tr(
"<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
2811 QString path = getOpenFileName(FileFinder::AudioFile);
2814 if (openAudio(path, ReplaceMainModel) == FileOpenFailed) {
2816 QMessageBox::critical(
this, tr(
"Failed to open file"),
2817 tr(
"<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
2837 auto modelId = getMainModelId();
2838 if (modelId.isNone())
return;
2840 std::set<ModelId> otherModelIds;
2841 ModelId current = modelId;
2844 for (
int i = 0; i < m_paneStack->getPaneCount(); ++i) {
2845 Pane *pane = m_paneStack->getPane(i);
2846 if (!pane)
continue;
2847 for (
int j = 0; j < pane->getLayerCount(); ++j) {
2848 Layer *layer = pane->getLayer(j);
2849 if (!layer)
continue;
2850 cerr <<
"layer = " << layer->objectName() << endl;
2851 ModelId m = layer->getModel();
2852 if (ModelById::isa<RangeSummarisableTimeValueModel>(m)) {
2853 otherModelIds.insert(m);
2854 if (pane == m_paneStack->getCurrentPane()) {
2861 if (!otherModelIds.empty()) {
2862 std::map<QString, ModelId> m;
2863 QString unnamed = tr(
"<unnamed>");
2864 QString oname = unnamed;
2865 if (
auto mp = ModelById::get(modelId)) {
2866 oname = mp->objectName();
2868 m[tr(
"1. %2").arg(oname)] = modelId;
2871 for (
auto otherModelId: otherModelIds) {
2872 if (otherModelId == modelId)
continue;
2874 if (
auto mp = ModelById::get(otherModelId)) {
2875 oname = mp->objectName();
2877 m[tr(
"%1. %2").arg(n).arg(oname)] = otherModelId;
2879 if (otherModelId == current) c = n-1;
2885 if (items.size() > 1) {
2887 QString item = QInputDialog::getItem
2888 (
this, tr(
"Select audio file to export"),
2889 tr(
"Which audio file do you want to export from?"),
2890 items, c,
false, &ok);
2891 if (!ok || item.isEmpty())
return;
2892 if (m.find(item) == m.end()) {
2893 SVCERR <<
"WARNING: Model " << item
2894 <<
" not found in list!" << endl;
2901 auto model = ModelById::getAs<DenseTimeValueModel>(modelId);
2903 SVCERR <<
"ERROR: Chosen model is not a DenseTimeValueModel!" << endl;
2909 path = getSaveFileName(FileFinder::CSVFile);
2911 path = getSaveFileName(FileFinder::AudioFile);
2913 if (path ==
"")
return;
2918 MultiSelection ms = m_viewManager->getSelection();
2919 MultiSelection::SelectionList selections = m_viewManager->getSelections();
2921 bool multiple =
false;
2923 MultiSelection *selectionToWrite =
nullptr;
2925 if (selections.size() == 1) {
2928 items << tr(
"Export the selected region only")
2929 << tr(
"Export the whole audio file");
2932 QString item = ListInputDialog::getItem
2933 (
this, tr(
"Select region to export"),
2934 tr(
"Which region from the original audio file do you want to export?"),
2937 if (!ok || item.isEmpty())
return;
2939 if (item == items[0]) selectionToWrite = &ms;
2941 }
else if (selections.size() > 1) {
2946 items << tr(
"Export the selected regions into a single file")
2947 << tr(
"Export the selected regions into separate files")
2948 << tr(
"Export the whole file");
2950 QString item = ListInputDialog::getItem
2951 (
this, tr(
"Select region to export"),
2952 tr(
"Multiple regions of the original audio file are selected.\nWhat do you want to export?"),
2955 if (!ok || item.isEmpty())
return;
2957 if (item == items[0]) {
2958 selectionToWrite = &ms;
2959 }
else if (item == items[1]) {
2964 selectionToWrite = &ms;
2970 QString base = path;
2971 base.replace(
".wav",
"");
2973 for (MultiSelection::SelectionList::iterator i = selections.begin();
2974 i != selections.end(); ++i) {
2976 MultiSelection subms;
2977 subms.setSelection(*i);
2979 QString subpath = QString(
"%1.%2.wav").arg(base).arg(n);
2982 if (QFileInfo(subpath).exists()) {
2983 error = tr(
"Fragment file %1 already exists, aborting").arg(subpath);
2987 WavFileWriter subwriter(subpath,
2988 model->getSampleRate(),
2989 model->getChannelCount(),
2990 WavFileWriter::WriteToTemporary);
2991 subwriter.writeModel(model.get(), &subms);
2992 ok = subwriter.isOK();
2995 error = subwriter.getError();
3005 ProgressDialog dialog {
3006 QObject::tr(
"Exporting audio data..."),
3010 Qt::ApplicationModal
3012 CSVFileWriter writer(path, model.get(), &dialog,
3013 ((QFileInfo(path).suffix() ==
"csv") ?
3015 if (selectionToWrite) {
3016 writer.writeSelection(*selectionToWrite);
3021 error = writer.getError();
3023 WavFileWriter writer(path,
3024 model->getSampleRate(),
3025 model->getChannelCount(),
3026 WavFileWriter::WriteToTemporary);
3027 writer.writeModel(model.get(), selectionToWrite);
3029 error = writer.getError();
3035 emit activity(tr(
"Export multiple audio files"));
3037 emit activity(tr(
"Export audio to \"%1\"").arg(path));
3038 m_recentFiles.addFile(path);
3041 QMessageBox::critical(
this, tr(
"Failed to write file"), error);
3048 QString path = getOpenFileName(FileFinder::CSVFile);
3049 if (path ==
"")
return;
3051 sv_samplerate_t defaultRate = 44100;
3053 CSVFormat format(path);
3054 format.setModelType(CSVFormat::WaveFileModel);
3055 format.setTimingType(CSVFormat::ImplicitTiming);
3056 format.setTimeUnits(CSVFormat::TimeAudioFrames);
3057 format.setSampleRate(defaultRate);
3060 CSVAudioFormatDialog *dialog =
new CSVAudioFormatDialog(
this, format);
3061 if (dialog->exec() != QDialog::Accepted) {
3065 format = dialog->getFormat();
3069 FileOpenStatus status = FileOpenSucceeded;
3071 ProgressDialog *progress =
new ProgressDialog
3072 (tr(
"Converting audio data..."),
true, 0,
this, Qt::ApplicationModal);
3074 WaveFileModel *model = qobject_cast<WaveFileModel *>
3075 (DataFileReaderFactory::loadCSV
3077 getMainModel() ? getMainModel()->getSampleRate() : defaultRate,
3080 if (progress->wasCancelled()) {
3083 status = FileOpenCancelled;
3085 }
else if (!model || !model->isOK()) {
3088 status = FileOpenFailed;
3092 auto modelId = ModelById::add(std::shared_ptr<Model>(model));
3094 status = addOpenedAudioModel(path,
3096 CreateAdditionalModel,
3097 getDefaultSessionTemplate(),
3103 if (status == FileOpenFailed) {
3105 QMessageBox::critical(
this, tr(
"Failed to open file"),
3106 tr(
"<b>File open failed</b><p>Audio data file %1 could not be opened.").arg(path));
3113 Pane *pane = m_paneStack->getCurrentPane();
3117 cerr <<
"WARNING: MainWindow::importLayer: no current pane" << endl;
3121 if (!getMainModel()) {
3123 cerr <<
"WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << endl;
3127 QString path = getOpenFileName(FileFinder::LayerFile);
3131 FileOpenStatus status = openLayer(path);
3133 if (status == FileOpenFailed) {
3135 QMessageBox::critical(
this, tr(
"Failed to open file"),
3136 tr(
"<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
3138 }
else if (status == FileOpenWrongMode) {
3140 QMessageBox::critical(
this, tr(
"Failed to open file"),
3141 tr(
"<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(path));
3149 Pane *pane = m_paneStack->getCurrentPane();
3152 Layer *layer = pane->getSelectedLayer();
3155 ModelId modelId = layer->getModel();
3156 if (modelId.isNone())
return;
3158 FileFinder::FileType type = FileFinder::LayerFileNoMidi;
3159 if (ModelById::isa<NoteModel>(modelId)) type = FileFinder::LayerFile;
3160 QString path = getSaveFileName(type);
3162 if (path ==
"")
return;
3164 QString suffix = QFileInfo(path).suffix().toLower();
3165 if (suffix ==
"") suffix =
"csv";
3167 bool canWriteSelection =
3168 ! (suffix ==
"xml" || suffix ==
"svl" ||
3169 suffix ==
"n3" || suffix ==
"ttl");
3172 ! (suffix ==
"xml" || suffix ==
"svl" ||
3173 suffix ==
"mid" || suffix ==
"midi" ||
3174 suffix ==
"n3" || suffix ==
"ttl");
3176 if (!ModelById::isa<NoteModel>(modelId) &&
3177 (suffix ==
"mid" || suffix ==
"midi")) {
3178 QMessageBox::critical
3179 (
this, tr(
"Failed to export layer"),
3180 tr(
"Only note layers may be exported to MIDI files."));
3184 if (ModelById::isa<DenseTimeValueModel>(modelId) &&
3187 QMessageBox::critical
3188 (
this, tr(
"Failed to export layer"),
3189 tr(
"Cannot export this layer to this file type. Only delimited column formats such as CSV are supported."));
3193 MultiSelection ms = m_viewManager->getSelection();
3194 bool haveSelection = !ms.getSelections().empty();
3196 MultiSelection *selectionToWrite =
nullptr;
3197 LayerGeometryProvider *provider = pane;
3199 DataExportOptions options = DataExportDefaults;
3200 QString delimiter =
",";
3204 CSVExportDialog::Configuration config;
3205 config.layerName = layer->getLayerPresentationName();
3206 config.fileExtension = suffix;
3207 config.isDense =
false;
3208 if (
auto m = ModelById::get(modelId)) {
3209 config.isDense = !m->isSparse();
3211 config.haveView =
true;
3212 config.haveSelection = canWriteSelection && haveSelection;
3214 CSVExportDialog dialog(config,
this);
3215 if (dialog.exec() != QDialog::Accepted) {
3219 if (dialog.shouldConstrainToSelection()) {
3220 selectionToWrite = &ms;
3223 if (!dialog.shouldConstrainToViewHeight()) {
3227 delimiter = dialog.getDelimiter();
3229 if (dialog.shouldIncludeHeader()) {
3230 options |= DataExportIncludeHeader;
3233 if (dialog.shouldIncludeTimestamps()) {
3234 options |= DataExportAlwaysIncludeTimestamp;
3237 if (dialog.shouldWriteTimeInFrames()) {
3238 options |= DataExportWriteTimeInFrames;
3241 }
else if (canWriteSelection && haveSelection) {
3244 items << tr(
"Export the content of the selected area")
3245 << tr(
"Export the whole layer");
3248 QString item = ListInputDialog::getItem
3249 (
this, tr(
"Select region to export"),
3250 tr(
"Which region of the layer do you want to export?"),
3253 if (!ok || item.isEmpty()) {
3257 if (item == items[0]) {
3258 selectionToWrite = &ms;
3264 bool result =
false;
3266 if (suffix ==
"xml" || suffix ==
"svl") {
3267 result = exportLayerToSVL(layer, path, error);
3268 }
else if (suffix ==
"mid" || suffix ==
"midi") {
3269 result = exportLayerToMIDI(layer, selectionToWrite, path, error);
3270 }
else if (suffix ==
"ttl" || suffix ==
"n3") {
3271 result = exportLayerToRDF(layer, path, error);
3273 result = exportLayerToCSV(layer, provider, selectionToWrite,
3274 delimiter, options, path, error);
3278 QMessageBox::critical(
this, tr(
"Failed to write file"), error);
3280 m_recentFiles.addFile(path);
3281 emit activity(tr(
"Export layer to \"%1\"").arg(path));
3288 Pane *pane = m_paneStack->getCurrentPane();
3291 QString path = getSaveFileName(FileFinder::ImageFile);
3292 if (path ==
"")
return;
3293 if (QFileInfo(path).suffix() ==
"") path +=
".png";
3295 bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty();
3297 QSize total, visible, selected;
3298 total = pane->getRenderedImageSize();
3299 visible = pane->getRenderedPartImageSize(pane->getFirstVisibleFrame(),
3300 pane->getLastVisibleFrame());
3302 sv_frame_t sf0 = 0, sf1 = 0;
3304 if (haveSelection) {
3305 MultiSelection::SelectionList selections = m_viewManager->getSelections();
3306 sf0 = selections.begin()->getStartFrame();
3307 MultiSelection::SelectionList::iterator e = selections.end();
3309 sf1 = e->getEndFrame();
3310 selected = pane->getRenderedPartImageSize(sf0, sf1);
3314 items << tr(
"Export the whole pane (%1x%2 pixels)")
3315 .arg(total.width()).arg(total.height());
3316 items << tr(
"Export the visible area only (%1x%2 pixels)")
3317 .arg(visible.width()).arg(visible.height());
3318 if (haveSelection) {
3319 items << tr(
"Export the selection extent (%1x%2 pixels)")
3320 .arg(selected.width()).arg(selected.height());
3322 items << tr(
"Export the selection extent");
3326 settings.beginGroup(
"MainWindow");
3327 int deflt = settings.value(
"lastimageexportregion", 0).toInt();
3328 if (deflt == 2 && !haveSelection) deflt = 1;
3329 if (deflt == 0 && total.width() > 32767) deflt = 1;
3331 ListInputDialog *lid =
new ListInputDialog
3332 (
this, tr(
"Select region to export"),
3333 tr(
"Which region of the current pane do you want to export as an image?"),
3336 if (!haveSelection) {
3337 lid->setItemAvailability(2,
false);
3339 if (total.width() > 32767) {
3340 lid->setItemAvailability(0,
false);
3341 lid->setFootnote(tr(
"Note: the whole pane is too wide to be exported as a single image."));
3344 bool ok = lid->exec();
3345 QString item = lid->getCurrentString();
3348 if (!ok || item.isEmpty())
return;
3350 settings.setValue(
"lastimageexportregion", deflt);
3352 QImage *image =
nullptr;
3354 if (item == items[0]) {
3355 image = pane->renderToNewImage();
3356 }
else if (item == items[1]) {
3357 image = pane->renderPartToNewImage(pane->getFirstVisibleFrame(),
3358 pane->getLastVisibleFrame());
3359 }
else if (haveSelection) {
3360 image = pane->renderPartToNewImage(sf0, sf1);
3365 if (!image->save(path,
"PNG")) {
3366 QMessageBox::critical(
this, tr(
"Failed to save image file"),
3367 tr(
"Failed to save image file %1").arg(path));
3376 Pane *pane = m_paneStack->getCurrentPane();
3379 QString path = getSaveFileName(FileFinder::SVGFile);
3380 if (path ==
"")
return;
3381 if (QFileInfo(path).suffix() ==
"") path +=
".svg";
3383 bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty();
3385 sv_frame_t sf0 = 0, sf1 = 0;
3387 if (haveSelection) {
3388 MultiSelection::SelectionList selections = m_viewManager->getSelections();
3389 sf0 = selections.begin()->getStartFrame();
3390 MultiSelection::SelectionList::iterator e = selections.end();
3392 sf1 = e->getEndFrame();
3396 items << tr(
"Export the whole pane");
3397 items << tr(
"Export the visible area only");
3398 items << tr(
"Export the selection extent");
3401 settings.beginGroup(
"MainWindow");
3402 int deflt = settings.value(
"lastsvgexportregion", 0).toInt();
3403 if (deflt == 2 && !haveSelection) deflt = 1;
3405 ListInputDialog *lid =
new ListInputDialog
3406 (
this, tr(
"Select region to export"),
3407 tr(
"Which region of the current pane do you want to export as a scalable SVG image?"),
3410 if (!haveSelection) {
3411 lid->setItemAvailability(2,
false);
3414 bool ok = lid->exec();
3415 QString item = lid->getCurrentString();
3418 if (!ok || item.isEmpty())
return;
3420 settings.setValue(
"lastsvgexportregion", deflt);
3422 bool result =
false;
3424 if (item == items[0]) {
3425 result = pane->renderToSvgFile(path);
3426 }
else if (item == items[1]) {
3427 result = pane->renderPartToSvgFile(path,
3428 pane->getFirstVisibleFrame(),
3429 pane->getLastVisibleFrame());
3430 }
else if (haveSelection) {
3431 result = pane->renderPartToSvgFile(path, sf0, sf1);
3435 QMessageBox::critical(
this, tr(
"Failed to save SVG file"),
3436 tr(
"Failed to save SVG file %1").arg(path));
3443 QString path = RecordDirectory::getRecordContainerDirectory();
3444 if (path ==
"") path = RecordDirectory::getRecordDirectory();
3445 if (path ==
"")
return;
3447 openLocalFolder(path);
3459 Pane *pane = m_paneStack->addPane();
3461 connect(pane, SIGNAL(contextHelpChanged(
const QString &)),
3462 this, SLOT(contextHelpChanged(
const QString &)));
3464 if (!m_timeRulerLayer) {
3465 m_timeRulerLayer = m_document->createMainModelLayer
3466 (LayerFactory::TimeRuler);
3469 m_document->addLayerToView(pane, m_timeRulerLayer);
3471 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
3472 m_document->addLayerToView(pane, waveform);
3476 CommandHistory::getInstance()->clear();
3477 CommandHistory::getInstance()->documentSaved();
3486 connect(m_document, SIGNAL(activity(QString)),
3496 while (m_paneStack->getPaneCount() > 0) {
3498 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
3500 while (pane->getLayerCount() > 0) {
3501 m_document->removeLayerFromView
3502 (pane, pane->getLayer(pane->getLayerCount() - 1));
3506 m_paneStack->deletePane(pane);
3509 while (m_paneStack->getHiddenPaneCount() > 0) {
3511 Pane *pane = m_paneStack->getHiddenPane
3512 (m_paneStack->getHiddenPaneCount() - 1);
3514 while (pane->getLayerCount() > 0) {
3515 m_document->removeLayerFromView
3516 (pane, pane->getLayer(pane->getLayerCount() - 1));
3520 m_paneStack->deletePane(pane);
3531 m_document =
nullptr;
3532 m_viewManager->clearSelections();
3533 m_timeRulerLayer =
nullptr;
3536 m_originalLocation =
"";
3537 setWindowTitle(QApplication::applicationName());
3539 CommandHistory::getInstance()->clear();
3540 CommandHistory::getInstance()->documentSaved();
3547 QString orig = m_audioFile;
3548 if (orig ==
"") orig =
".";
3549 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
3551 QString path = getOpenFileName(FileFinder::AnyFile);
3553 if (path.isEmpty())
return;
3555 FileOpenStatus status = openPath(path, ReplaceSession);
3557 if (status == FileOpenFailed) {
3559 QMessageBox::critical(
this, tr(
"Failed to open file"),
3560 tr(
"<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
3561 }
else if (status == FileOpenWrongMode) {
3563 QMessageBox::critical(
this, tr(
"Failed to open file"),
3564 tr(
"<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(path));
3572 settings.beginGroup(
"MainWindow");
3573 QString lastLocation = settings.value(
"lastremote",
"").toString();
3576 QString text = QInputDialog::getText
3577 (
this, tr(
"Open Location"),
3578 tr(
"Please enter the URL of the location to open:"),
3579 QLineEdit::Normal, lastLocation, &ok);
3583 settings.setValue(
"lastremote", text);
3585 if (text.isEmpty())
return;
3587 FileOpenStatus status = openPath(text, AskUser);
3589 if (status == FileOpenFailed) {
3591 QMessageBox::critical(
this, tr(
"Failed to open location"),
3592 tr(
"<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
3593 }
else if (status == FileOpenWrongMode) {
3595 QMessageBox::critical(
this, tr(
"Failed to open location"),
3596 tr(
"<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(text));
3603 QObject *obj = sender();
3604 QAction *action =
dynamic_cast<QAction *
>(obj);
3607 cerr <<
"WARNING: MainWindow::openRecentFile: sender is not an action" 3612 QString path = action->objectName();
3615 cerr <<
"WARNING: MainWindow::openRecentFile: action incorrectly named" 3620 FileOpenStatus status = openPath(path, ReplaceSession);
3622 if (status == FileOpenFailed) {
3624 QMessageBox::critical(
this, tr(
"Failed to open location"),
3625 tr(
"<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
3626 }
else if (status == FileOpenWrongMode) {
3628 QMessageBox::critical(
this, tr(
"Failed to open location"),
3629 tr(
"<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(path));
3636 QObject *s = sender();
3637 QAction *action = qobject_cast<QAction *>(s);
3640 cerr <<
"WARNING: MainWindow::applyTemplate: sender is not an action" 3645 QString n = action->objectName();
3646 if (n ==
"") n = action->text();
3649 cerr <<
"WARNING: MainWindow::applyTemplate: sender has no name" 3654 QString mainModelLocation;
3655 auto mm = getMainModel();
3656 if (mm) mainModelLocation = mm->getLocation();
3657 if (mainModelLocation !=
"") {
3658 openAudio(mainModelLocation, ReplaceSession, n);
3660 openSessionTemplate(n);
3667 QDialog *d =
new QDialog(
this);
3668 d->setWindowTitle(tr(
"Enter template name"));
3670 QGridLayout *layout =
new QGridLayout;
3671 d->setLayout(layout);
3673 layout->addWidget(
new QLabel(tr(
"Please enter a name for the saved template:")),
3675 QLineEdit *lineEdit =
new QLineEdit;
3676 layout->addWidget(lineEdit, 1, 0);
3677 QCheckBox *makeDefault =
new QCheckBox(tr(
"Set as default template for future audio files"));
3678 layout->addWidget(makeDefault, 2, 0);
3680 QDialogButtonBox *bb =
new QDialogButtonBox(QDialogButtonBox::Ok |
3681 QDialogButtonBox::Cancel);
3682 layout->addWidget(bb, 3, 0);
3683 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
3684 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
3685 connect(bb, SIGNAL(rejected()), d, SLOT(reject()));
3687 if (d->exec() == QDialog::Accepted) {
3689 QString name = lineEdit->text();
3690 name.replace(QRegExp(
"[^\\w\\s\\.\"'-]"),
"_");
3693 QString dir = rf.getResourceSaveDir(
"templates");
3694 QString filename = QString(
"%1/%2.svt").arg(dir).arg(name);
3695 if (QFile(filename).exists()) {
3696 if (QMessageBox::warning(
this,
3697 tr(
"Template file exists"),
3698 tr(
"<b>Template file exists</b><p>The template \"%1\" already exists.<br>Overwrite it?").arg(name),
3699 QMessageBox::Ok | QMessageBox::Cancel,
3700 QMessageBox::Cancel) != QMessageBox::Ok) {
3706 if (saveSessionTemplate(filename)) {
3707 if (makeDefault->isChecked()) {
3708 setDefaultSessionTemplate(name);
3720 openLocalFolder(rf.getResourceSaveDir(
"templates"));
3728 connect(pane, SIGNAL(cancelButtonPressed(Layer *)),
3748 Pane *pane = qobject_cast<Pane *>(sender());
3750 if (pane && layer) {
3751 for (
int i = 0; i < pane->getLayerCount(); ++i) {
3752 if (pane->getLayer(i) == layer) {
3759 SVDEBUG <<
"MainWindow::paneCancelButtonPressed: Unknown layer in pane" 3764 SVDEBUG <<
"MainWindow::paneCancelButtonPressed: Layer " << layer << endl;
3777 m_document->setModel(layer, {});
3778 m_document->removeLayerFromView(pane, layer);
3791 if (pane) m_paneStack->setCurrentPane(pane);
3793 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
3795 FileOpenStatus status;
3797 if (i == uriList.begin()) {
3798 status = openPath(*i, ReplaceCurrentPane);
3800 status = openPath(*i, CreateAdditionalModel);
3803 if (status == FileOpenFailed) {
3805 QMessageBox::critical(
this, tr(
"Failed to open dropped URL"),
3806 tr(
"<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
3808 }
else if (status == FileOpenWrongMode) {
3810 QMessageBox::critical(
this, tr(
"Failed to open dropped URL"),
3811 tr(
"<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(*i));
3813 }
else if (status == FileOpenCancelled) {
3822 if (pane) m_paneStack->setCurrentPane(pane);
3825 if (testUrl.scheme() ==
"file" ||
3826 testUrl.scheme() ==
"http" ||
3827 testUrl.scheme() ==
"ftp") {
3829 list.push_back(text);
3841 SVDEBUG <<
"MainWindow::closeEvent" << endl;
3843 if (m_openingAudioFile) {
3844 SVCERR <<
"Busy - ignoring close event" << endl;
3850 SVCERR <<
"Close refused by user - ignoring close event" << endl;
3856 settings.beginGroup(
"MainWindow");
3857 settings.setValue(
"maximised", isMaximized());
3858 if (!isMaximized()) {
3859 settings.setValue(
"size", size());
3860 settings.setValue(
"position", pos());
3862 settings.endGroup();
3894 if (!m_documentModified)
return true;
3900 QString svDirBase =
".sv1";
3901 QString svDir = QDir::home().filePath(svDirBase);
3903 if (!QFileInfo(svDir).exists()) {
3904 if (!QDir::home().mkdir(svDirBase))
return false;
3906 if (!QFileInfo(svDir).isDir())
return false;
3911 QString fname = QString(
"tmp-%1-%2.sv")
3912 .arg(QDateTime::currentDateTime().toString(
"yyyyMMddhhmmsszzz"))
3913 .arg(QProcess().pid());
3915 QString fname = QString(
"tmp-%1.sv")
3916 .arg(QDateTime::currentDateTime().toString(
"yyyyMMddhhmmsszzz"));
3918 QString fpath = QDir(svDir).filePath(fname);
3919 if (saveSessionFile(fpath)) {
3920 m_recentFiles.addFile(fpath);
3921 emit activity(tr(
"Export image to \"%1\"").arg(fpath));
3936 if (!m_documentModified)
return true;
3941 QMessageBox::warning(
this,
3942 tr(
"Session modified"),
3943 tr(
"<b>Session modified</b><p>The current session has been modified.<br>Do you want to save it?"),
3944 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
3947 if (button == QMessageBox::Yes) {
3949 if (m_documentModified) {
3954 }
else if (button == QMessageBox::No) {
3955 m_documentModified =
false;
3970 settings.beginGroup(
"MainWindow");
3971 bool prevNewSession = settings.value(
"newsessionforrdfaudio",
true).toBool();
3972 settings.endGroup();
3976 items << tr(
"Close the current session and create a new one")
3977 << tr(
"Add this data to the current session");
3980 QString item = ListInputDialog::getItem
3981 (
this, tr(
"Select target for import"),
3982 tr(
"<b>Select a target for import</b><p>This RDF document refers to one or more audio files.<br>You already have an audio waveform loaded.<br>What would you like to do with the new data?"),
3983 items, prevNewSession ? 0 : 1, &ok);
3985 if (!ok || item.isEmpty()) {
3990 newSession = (item == items[0]);
3991 settings.beginGroup(
"MainWindow");
3992 settings.setValue(
"newsessionforrdfaudio", newSession);
3993 settings.endGroup();
3995 if (newSession)
return true;
4002 if (m_sessionFile !=
"") {
4003 if (!saveSessionFile(m_sessionFile)) {
4004 QMessageBox::critical(
this, tr(
"Failed to save file"),
4005 tr(
"<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(m_sessionFile));
4007 CommandHistory::getInstance()->documentSaved();
4018 QString orig = m_audioFile;
4019 if (orig ==
"") orig =
".";
4020 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
4022 QString path = getSaveFileName(FileFinder::SessionFile);
4024 if (path ==
"")
return;
4026 if (!saveSessionFile(path)) {
4027 QMessageBox::critical(
this, tr(
"Failed to save file"),
4028 tr(
"<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(path));
4030 setWindowTitle(tr(
"%1: %2")
4031 .arg(QApplication::applicationName())
4032 .arg(QFileInfo(path).fileName()));
4033 m_sessionFile = path;
4034 CommandHistory::getInstance()->documentSaved();
4036 m_recentFiles.addFile(path);
4037 emit activity(tr(
"Save session as \"%1\"").arg(path));
4044 MainWindowBase::preferenceChanged(name);
4046 if (name ==
"Background Mode") {
4055 settings.beginGroup(
"Preferences");
4057 bool haveDarkBackground = (m_viewManager &&
4058 m_viewManager->getGlobalDarkBackground());
4059 QColor highlight = QApplication::palette().color(QPalette::Highlight);
4060 ColourDatabase *cdb = ColourDatabase::getInstance();
4061 int nearestIndex = cdb->getNearbyColourIndex
4063 haveDarkBackground ?
4064 ColourDatabase::WithDarkBackground :
4065 ColourDatabase::WithLightBackground);
4066 QString defaultColourName = cdb->getColourName(nearestIndex);
4068 QColor colour = QColor
4069 (settings.value(
"overview-colour",
4070 cdb->getColour(defaultColourName).name()).toString());
4071 settings.endGroup();
4073 int index = cdb->getColourIndex(colour);
4075 SVDEBUG <<
"MainWindow::coloursChanged: haveDarkBackground = " << haveDarkBackground <<
", highlight = " << highlight.name() <<
", nearestIndex = " << nearestIndex <<
", defaultColourName = " << defaultColourName <<
", colour = " << colour.name() <<
", index = " << index << endl;
4099 QObject *s = sender();
4100 QAction *action =
dynamic_cast<QAction *
>(s);
4102 cerr <<
"addPane: sender is " << s <<
", action is " << action <<
", name " << action->text() << endl;
4105 cerr <<
"WARNING: MainWindow::addPane: sender is not an action" 4112 if (i->first == action)
break;
4117 cerr <<
"WARNING: MainWindow::addPane: unknown action " 4118 << action->objectName() << endl;
4119 cerr <<
"known actions are:" << endl;
4122 cerr << i->first <<
", name " << i->first->text() << endl;
4127 addPane(i->second, action->text());
4133 CommandHistory::getInstance()->startCompoundOperation(text,
true);
4135 AddPaneCommand *command =
new AddPaneCommand(
this);
4136 CommandHistory::getInstance()->addCommand(command);
4138 Pane *pane = command->getPane();
4140 if (configuration.
layer == LayerFactory::Spectrum) {
4141 pane->setPlaybackFollow(PlaybackScrollContinuous);
4142 pane->setFollowGlobalZoom(
false);
4143 pane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, 512));
4146 if (configuration.
layer != LayerFactory::TimeRuler &&
4147 configuration.
layer != LayerFactory::Spectrum) {
4149 if (!m_timeRulerLayer) {
4151 m_timeRulerLayer = m_document->createMainModelLayer
4152 (LayerFactory::TimeRuler);
4157 m_document->addLayerToView(pane, m_timeRulerLayer);
4160 Layer *newLayer = m_document->createLayer(configuration.
layer);
4162 ModelId suggestedModelId = configuration.
sourceModel;
4165 if (!suggestedModelId.isNone()) {
4168 std::vector<ModelId> inputModels = m_document->getTransformInputModels();
4169 for (
auto im: inputModels) {
4170 if (im == suggestedModelId) {
4171 modelId = suggestedModelId;
4175 if (modelId.isNone()) {
4176 cerr <<
"WARNING: Model " << modelId
4177 <<
" appears in pane action map, but is not reported " 4178 <<
"by document as a valid transform source" << endl;
4182 if (modelId.isNone()) {
4183 modelId = m_document->getMainModel();
4186 m_document->setModel(newLayer, modelId);
4188 m_document->setChannel(newLayer, configuration.
channel);
4189 m_document->addLayerToView(pane, newLayer);
4191 m_paneStack->setCurrentPane(pane);
4192 m_paneStack->setCurrentLayer(pane, newLayer);
4198 CommandHistory::getInstance()->endCompoundOperation();
4206 QObject *s = sender();
4207 QAction *action =
dynamic_cast<QAction *
>(s);
4210 cerr <<
"WARNING: MainWindow::addLayer: sender is not an action" 4215 Pane *pane = m_paneStack->getCurrentPane();
4218 cerr <<
"WARNING: MainWindow::addLayer: no current pane" << endl;
4224 if (ei->first == action)
break;
4229 Layer *newLayer = ei->second;
4230 m_document->addLayerToView(pane, newLayer);
4231 m_paneStack->setCurrentLayer(pane, newLayer);
4237 if (ei->first == action)
break;
4242 Layer *newLayer = m_document->createLayer(LayerFactory::Slice);
4244 SliceableLayer *source =
dynamic_cast<SliceableLayer *
>(ei->second);
4245 SliceLayer *dest =
dynamic_cast<SliceLayer *
>(newLayer);
4246 if (source && dest) {
4248 dest->setSliceableModel(source->getSliceableModel());
4249 connect(source, SIGNAL(sliceableModelReplaced(
const Model *,
const Model *)),
4250 dest, SLOT(sliceableModelReplaced(
const Model *,
const Model *)));
4251 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
4252 dest, SLOT(modelAboutToBeDeleted(Model *)));
4254 m_document->addLayerToView(pane, newLayer);
4255 m_paneStack->setCurrentLayer(pane, newLayer);
4261 if (i->first == action)
break;
4269 if (i->first == action)
break;
4274 cerr <<
"WARNING: MainWindow::addLayer: unknown action " 4275 << action->objectName() << endl;
4279 LayerFactory::LayerType type = i->second.layer;
4281 LayerFactory::LayerTypeSet emptyTypes =
4282 LayerFactory::getInstance()->getValidEmptyLayerTypes();
4284 Layer *newLayer =
nullptr;
4286 bool isNewEmptyLayer =
false;
4288 if (emptyTypes.find(type) != emptyTypes.end()) {
4290 newLayer = m_document->createEmptyLayer(type);
4292 isNewEmptyLayer =
true;
4297 ModelId modelId = i->second.sourceModel;
4299 if (modelId.isNone()) {
4300 if (type == LayerFactory::TimeRuler) {
4301 newLayer = m_document->createMainModelLayer(type);
4307 Pane::ModelSet ms = pane->getModels();
4308 for (ModelId m: ms) {
4309 if (ModelById::isa<RangeSummarisableTimeValueModel>(m)) {
4313 if (modelId.isNone()) {
4314 modelId = getMainModelId();
4319 if (!modelId.isNone()) {
4320 newLayer = m_document->createLayer(type);
4321 if (m_document->isKnownModel(modelId)) {
4322 m_document->setChannel(newLayer, i->second.channel);
4323 m_document->setModel(newLayer, modelId);
4325 SVCERR <<
"WARNING: MainWindow::addLayer: unknown model " 4326 << modelId <<
" in layer action map" << endl;
4331 if (isNewEmptyLayer) {
4336 if (pane->getVisibleExtentsForAnyUnit(min, max, log, unit)) {
4337 newLayer->adoptExtents(min, max, unit);
4341 if (a.first == ViewManager::DrawMode) {
4342 a.second->trigger();
4349 m_document->addLayerToView(pane, newLayer);
4350 m_paneStack->setCurrentLayer(pane, newLayer);
4374 QString transformId = i->second;
4382 Pane *pane = m_paneStack->getCurrentPane();
4384 cerr <<
"WARNING: MainWindow::addLayer: no current pane" << endl;
4388 Transform transform;
4390 transform = TransformFactory::getInstance()->
4391 getDefaultTransformFor(transformId);
4392 }
catch (std::exception &e) {
4393 QMessageBox::critical
4394 (
this, tr(
"Failed to query transform attributes"),
4395 tr(
"<b>Failed to query transform attributes</b><p>Plugin or server error: %1</p>")
4400 std::vector<ModelId> candidateInputModels =
4401 m_document->getTransformInputModels();
4403 ModelId defaultInputModelId;
4405 for (
int j = 0; j < pane->getLayerCount(); ++j) {
4407 Layer *layer = pane->getLayer(j);
4408 if (!layer)
continue;
4410 if (LayerFactory::getInstance()->getLayerType(layer) !=
4411 LayerFactory::Waveform &&
4412 !layer->isLayerOpaque()) {
4416 ModelId modelId = layer->getModel();
4417 if (modelId.isNone())
continue;
4419 for (ModelId candidateId: candidateInputModels) {
4420 if (candidateId == modelId) {
4421 defaultInputModelId = modelId;
4426 if (!defaultInputModelId.isNone())
break;
4431 if (candidateInputModels.size() > 1) {
4433 AggregateWaveModel::ChannelSpecList sl;
4434 for (ModelId mid: candidateInputModels) {
4435 if (ModelById::isa<RangeSummarisableTimeValueModel>(mid)) {
4436 sl.push_back(AggregateWaveModel::ModelChannelSpec(mid, -1));
4440 auto aggregate = std::make_shared<AggregateWaveModel>(sl);
4441 aggregate->setObjectName(tr(
"Multiplex all of the above"));
4442 candidateInputModels.push_back(ModelById::add(aggregate));
4446 sv_frame_t startFrame = 0, duration = 0;
4447 sv_frame_t endFrame = 0;
4448 m_viewManager->getSelection().getExtents(startFrame, endFrame);
4449 if (endFrame > startFrame) duration = endFrame - startFrame;
4450 else startFrame = 0;
4452 TransformUserConfigurator configurator;
4454 ModelTransformer::Input input = ModelTransformerFactory::getInstance()->
4455 getConfigurationForTransform
4457 candidateInputModels,
4458 defaultInputModelId,
4464 if (!aggregate.isNone()) {
4465 if (input.getModel() == aggregate) {
4466 if (
auto aggregateModel = ModelById::get(aggregate)) {
4467 aggregateModel->setObjectName(tr(
"Multiplexed audio"));
4469 m_document->addNonDerivedModel(aggregate);
4471 ModelById::release(aggregate);
4475 if (input.getModel().isNone())
return;
4480 Layer *newLayer = m_document->createDerivedLayer(transform, input);
4482 m_document->addLayerToView(pane, newLayer);
4483 m_document->setChannel(newLayer, input.getChannel());
4484 m_recentTransforms.add(transformId);
4485 m_paneStack->setCurrentLayer(pane, newLayer);
4487 }
catch (std::exception &e) {
4488 QMessageBox::critical
4489 (
this, tr(
"Transform failed"),
4490 tr(
"<b>Failed to run transform</b><p>Plugin or server error: %1</p>")
4501 Pane *pane = m_paneStack->getCurrentPane();
4504 Layer *layer = pane->getSelectedLayer();
4508 QString newName = QInputDialog::getText
4509 (
this, tr(
"Rename Layer"),
4510 tr(
"New name for this layer:"),
4511 QLineEdit::Normal, layer->objectName(), &ok);
4514 bool existingNameSet = layer->isPresentationNameSet();
4515 QString existingName = layer->getLayerPresentationName();
4517 CommandHistory::getInstance()->addCommand
4519 (tr(
"Rename Layer"),
4521 layer->setPresentationName(newName);
4525 layer->setPresentationName(existingNameSet ? existingName :
"");
4533 TransformFinder *finder =
new TransformFinder(
this);
4534 if (!finder->exec()) {
4538 TransformId transform = finder->getTransform();
4541 if (getMainModel() !=
nullptr && m_paneStack->getCurrentPane() !=
nullptr) {
4549 MainWindowBase::playSoloToggled();
4556 QAction *action =
dynamic_cast<QAction *
>(sender());
4558 if (!m_viewManager)
return;
4561 m_viewManager->setAlignMode(action->isChecked());
4563 m_viewManager->setAlignMode(!m_viewManager->getAlignMode());
4566 if (m_viewManager->getAlignMode()) {
4570 MainWindowBase::playSoloToggled();
4574 m_document->alignModels();
4575 m_document->setAutoAlignment(
true);
4580 MainWindowBase::playSoloToggled();
4584 m_document->setAutoAlignment(
false);
4587 for (
int i = 0; i < m_paneStack->getPaneCount(); ++i) {
4589 Pane *pane = m_paneStack->getPane(i);
4590 if (!pane)
continue;
4599 PlaySpeedRangeMapper mapper;
4602 double factor = mapper.getFactorForValue(percent);
4614 if (position == centre) {
4615 contextHelpChanged(tr(
"Playback speed: Normal"));
4616 }
else if (position < centre) {
4617 sprintf(pcbuf,
"%.1f", percent);
4618 sprintf(facbuf,
"%.3g", 1.0 / factor);
4619 contextHelpChanged(tr(
"Playback speed: %1% (%2x slower)")
4623 sprintf(pcbuf,
"%.0f", percent);
4624 sprintf(facbuf,
"%.3g", factor);
4625 contextHelpChanged(tr(
"Playback speed: %1% (%2x faster)")
4630 m_playSource->setTimeStretch(1.0 / factor);
4649 if (value < m_playSpeed->minimum()) value =
m_playSpeed->minimum();
4662 MainWindowBase::currentPaneChanged(pane);
4670 bool containsMainModel =
false;
4671 for (
int i = pane->getLayerCount(); i > 0; ) {
4673 Layer *layer = pane->getLayer(i);
4675 LayerFactory::getInstance()->getLayerType(layer) ==
4676 LayerFactory::Waveform &&
4677 layer->getModel() == getMainModelId()) {
4678 containsMainModel =
true;
4683 bool panLayerSet =
false;
4685 for (
int i = pane->getLayerCount(); i > 0; ) {
4687 Layer *layer = pane->getLayer(i);
4688 ModelId modelId = layer->getModel();
4689 if (ModelById::isa<RangeSummarisableTimeValueModel>(modelId)) {
4690 auto type = LayerFactory::getInstance()->getLayerType(layer);
4691 if (type != LayerFactory::TimeRuler) {
4694 if (type == LayerFactory::Waveform) {
4702 if (containsMainModel && !panLayerSet) {
4710 sv_samplerate_t sampleRate = 0;
4711 if (
auto mm = getMainModel()) {
4712 sampleRate = mm->getSampleRate();
4720 bool haveSelection =
false;
4721 sv_frame_t startFrame = 0, endFrame = 0;
4723 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
4725 bool exclusive =
false;
4726 Selection s = m_viewManager->getInProgressSelection(exclusive);
4729 haveSelection =
true;
4730 startFrame = s.getStartFrame();
4731 endFrame = s.getEndFrame();
4735 if (!haveSelection) {
4736 startFrame = p->getFirstVisibleFrame();
4737 endFrame = p->getLastVisibleFrame();
4740 RealTime start = RealTime::frame2RealTime(startFrame, sampleRate);
4741 RealTime end = RealTime::frame2RealTime(endFrame, sampleRate);
4742 RealTime duration = end - start;
4744 QString startStr, endStr, durationStr;
4745 startStr = start.toText(
true).c_str();
4746 endStr = end.toText(
true).c_str();
4747 durationStr = duration.toText(
true).c_str();
4749 if (haveSelection) {
4750 m_myStatusMessage = tr(
"Selection: %1 to %2 (duration %3)")
4751 .arg(startStr).arg(endStr).arg(durationStr);
4753 m_myStatusMessage = tr(
"Visible: %1 to %2 (duration %3)")
4754 .arg(startStr).arg(endStr).arg(durationStr);
4757 if (getStatusLabel()->text() != m_myStatusMessage) {
4758 getStatusLabel()->setText(m_myStatusMessage);
4767 if (!statusBar()->isVisible())
return;
4769 Pane *pane =
nullptr;
4770 sv_frame_t frame = m_viewManager->getPlaybackFrame();
4772 if (m_paneStack) pane = m_paneStack->getCurrentPane();
4775 int layers = pane->getLayerCount();
4778 for (
int i = layers-1; i >= 0; --i) {
4779 Layer *layer = pane->getLayer(i);
4780 if (!layer)
continue;
4781 if (!layer->isLayerEditable())
continue;
4782 QString label = layer->getLabelPreceding
4783 (pane->alignFromReference(frame));
4797 sv_samplerate_t actual,
4800 if (!willResample) {
4802 QMessageBox::information
4803 (
this, tr(
"Sample rate mismatch"),
4804 tr(
"<b>Wrong sample rate</b><p>The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).<p>The file will play at the wrong speed and pitch.<p>Change the <i>Resample mismatching files on import</i> option under <i>File</i> -> <i>Preferences</i> if you want to alter this behaviour.")
4805 .arg(requested).arg(actual));
4814 QMessageBox::information
4815 (
this, tr(
"Audio processing overload"),
4816 tr(
"<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
4822 QMessageBox::information
4823 (
this, tr(
"Beta release"),
4824 tr(
"<b>This is a beta release of %1</b><p>Please see the \"What's New\" option in the Help menu for a list of changes since the last proper release.</p>").arg(QApplication::applicationName()));
4832 box.setWindowTitle(tr(
"Problems loading plugins"));
4833 box.setText(tr(
"<b>Failed to load plugins</b>"));
4834 box.setInformativeText(warning);
4835 box.setIcon(QMessageBox::Warning);
4836 box.setStandardButtons(QMessageBox::Ok);
4843 Pane *currentPane =
nullptr;
4844 NoteLayer *currentNoteLayer =
nullptr;
4845 TimeValueLayer *currentTimeValueLayer =
nullptr;
4848 currentPane = m_paneStack->getCurrentPane();
4852 currentNoteLayer =
dynamic_cast<NoteLayer *
> 4853 (currentPane->getSelectedLayer());
4854 currentTimeValueLayer =
dynamic_cast<TimeValueLayer *
> 4855 (currentPane->getSelectedLayer());
4858 while (m_midiInput->getEventsAvailable() > 0) {
4859 (void)m_midiInput->readEvent();
4869 while (m_midiInput->getEventsAvailable() > 0) {
4871 MIDIEvent ev(m_midiInput->readEvent());
4873 sv_frame_t frame = currentPane->alignFromReference(ev.getTime());
4875 bool noteOn = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
4876 ev.getVelocity() > 0);
4878 bool noteOff = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_OFF ||
4879 (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
4880 ev.getVelocity() == 0));
4882 if (currentNoteLayer) {
4884 if (!m_playSource || !m_playSource->isPlaying())
continue;
4888 currentNoteLayer->addNoteOn(frame,
4892 }
else if (noteOff) {
4894 currentNoteLayer->addNoteOff(frame,
4902 if (currentTimeValueLayer) {
4904 if (!noteOn)
continue;
4906 if (!m_playSource || !m_playSource->isPlaying())
continue;
4908 ModelId modelId = currentTimeValueLayer->getModel();
4909 if (ModelById::isa<SparseTimeValueModel>(modelId)) {
4910 Event point(frame,
float(ev.getPitch() % 12),
"");
4911 AddEventCommand *command =
new AddEventCommand
4912 (modelId.untyped, point, tr(
"Add Point"));
4913 CommandHistory::getInstance()->addCommand(command);
4923 if (!noteOn)
continue;
4924 insertInstantAt(ev.getTime());
4931 Pane *currentPane =
nullptr;
4932 NoteLayer *currentNoteLayer =
nullptr;
4934 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
4936 currentNoteLayer =
dynamic_cast<NoteLayer *
>(currentPane->getSelectedLayer());
4939 if (currentNoteLayer) {
4940 currentNoteLayer->abandonNoteOns();
4947 Profiler profiler(
"MainWindow::layerRemoved");
4949 MainWindowBase::layerRemoved(layer);
4956 MainWindowBase::layerInAView(layer, inAView);
4962 MainWindowBase::modelAdded(modelId);
4963 if (ModelById::isa<DenseTimeValueModel>(modelId)) {
4973 MainWindowBase::mainModelChanged(modelId);
4975 if (m_playTarget || m_audioIO) {
4987 m_playTarget->setOutputGain(gain);
4988 }
else if (m_audioIO) {
4989 m_audioIO->setOutputGain(gain);
4998 m_playTarget->setOutputBalance(balance);
4999 }
else if (m_audioIO) {
5000 m_audioIO->setOutputBalance(balance);
5007 QAction *a =
dynamic_cast<QAction *
>(sender());
5012 if (ai.first == a) type = ai.second;
5015 if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
5018 settings.beginGroup(
"MainWindow");
5019 settings.setValue(
"labellertype", type);
5020 settings.endGroup();
5026 QAction *a =
dynamic_cast<QAction *
>(sender());
5029 int cycle = a->text().toInt();
5030 if (cycle == 0)
return;
5032 if (m_labeller) m_labeller->setCounterCycleSize(cycle);
5035 settings.beginGroup(
"MainWindow");
5036 settings.setValue(
"labellercycle", cycle);
5037 settings.endGroup();
5043 LabelCounterInputDialog dialog(m_labeller,
this);
5044 dialog.setWindowTitle(tr(
"Reset Counters"));
5051 if (m_labeller) m_labeller->resetCounters();
5058 settings.beginGroup(
"MainWindow");
5059 int n = settings.value(
"subdivisions", 4).toInt();
5063 n = QInputDialog::getInt(
this,
5064 tr(
"Subdivide instants"),
5065 tr(
"Number of subdivisions:"),
5069 settings.setValue(
"subdivisions", n);
5070 subdivideInstantsBy(n);
5073 settings.endGroup();
5080 settings.beginGroup(
"MainWindow");
5081 int n = settings.value(
"winnow-subdivisions", 4).toInt();
5085 n = QInputDialog::getInt(
this,
5086 tr(
"Winnow instants"),
5087 tr(
"Remove all instants apart from multiples of:"),
5091 settings.setValue(
"winnow-subdivisions", n);
5092 winnowInstantsBy(n);
5095 settings.endGroup();
5104 if (transformName !=
"") {
5105 quoted = QString(
"\"%1\" ").arg(transformName);
5108 if (message !=
"") {
5110 QMessageBox::warning
5112 tr(
"Failed to generate layer"),
5113 tr(
"<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform %1failed:<p>%2")
5114 .arg(quoted).arg(message),
5117 QMessageBox::warning
5119 tr(
"Failed to generate layer"),
5120 tr(
"<b>Layer generation failed</b><p>Failed to generate a derived layer.<p>The layer transform %1failed.<p>No error information is available.")
5131 QMessageBox::warning
5132 (
this, tr(
"Warning"), message, QMessageBox::Ok);
5137 QString transformName, QString message)
5141 if (message !=
"") {
5143 QMessageBox::warning
5145 tr(
"Failed to regenerate layer"),
5146 tr(
"<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed:<p>%3")
5147 .arg(layerName).arg(transformName).arg(message),
5150 QMessageBox::warning
5152 tr(
"Failed to regenerate layer"),
5153 tr(
"<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed.<p>No error information is available.")
5154 .arg(layerName).arg(transformName),
5166 QMessageBox::warning
5167 (
this, tr(
"Warning"), tr(
"<b>Warning when regenerating layer</b><p>When regenerating the derived layer \"%1\" using new data model as input:<p>%2").arg(layerName).arg(message), QMessageBox::Ok);
5173 QMessageBox::warning
5175 tr(
"Failed to calculate alignment"),
5176 tr(
"<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
5184 m_paneStack->setCurrentPane(pane);
5195 QMenu *m =
new QMenu;
5198 MenuTitle::addTitle(m, tr(
"Pane"));
5200 m_paneStack->setCurrentLayer(pane,
nullptr);
5202 m->addAction(il.load(
"editdelete"), tr(
"&Delete Pane"),
5203 this, SLOT(deleteCurrentPane()));
5216 QMenu *m =
new QMenu;
5219 MenuTitle::addTitle(m, layer->getLayerPresentationName());
5221 m_paneStack->setCurrentLayer(pane, layer);
5223 m->addAction(tr(
"&Rename Layer..."),
5226 m->addAction(tr(
"Edit Layer Data"),
5227 this, SLOT(editCurrentLayer()))
5228 ->setEnabled(layer->isLayerEditable());
5230 m->addAction(il.load(
"editdelete"), tr(
"&Delete Layer"),
5231 this, SLOT(deleteCurrentLayer()));
5269 bool goToTemplateTab =
5270 (sender() && sender()->objectName() ==
"set_default_template");
5275 if (goToTemplateTab) {
5284 this, SLOT(recreateAudioIO()));
5297 if (goToTemplateTab) {
5305 QWidget *w =
dynamic_cast<QWidget *
>(sender());
5308 QString mainText, editText;
5311 mainText = tr(
"Adjust the master playback level and pan");
5312 editText = tr(
"click then drag to adjust, ctrl+click to reset");
5314 mainText = tr(
"Adjust the master playback speed");
5315 editText = tr(
"drag up/down to adjust, ctrl+click to reset");
5318 if (mainText !=
"") {
5319 contextHelpChanged(tr(
"%1: %2").arg(mainText).arg(editText));
5326 contextHelpChanged(
"");
5332 openHelpUrl(tr(
"http://www.sonicvisualiser.org/"));
5338 openHelpUrl(tr(
"http://www.sonicvisualiser.org/doc/reference/%1/en/").arg(SV_VERSION));
5344 QFile changelog(
":CHANGELOG");
5345 changelog.open(QFile::ReadOnly);
5346 QByteArray content = changelog.readAll();
5347 QString text = QString::fromUtf8(content);
5349 QDialog *d =
new QDialog(
this);
5350 d->setWindowTitle(tr(
"What's New"));
5352 QGridLayout *layout =
new QGridLayout;
5353 d->setLayout(layout);
5357 QLabel *iconLabel =
new QLabel;
5358 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
5359 layout->addWidget(iconLabel, row, 0);
5362 (
new QLabel(tr(
"<h3>What's New in %1</h3>")
5363 .arg(QApplication::applicationName())),
5365 layout->setColumnStretch(2, 10);
5367 QTextEdit *textEdit =
new QTextEdit;
5368 layout->addWidget(textEdit, row++, 1, 1, 2);
5371 layout->addWidget(
new QLabel(tr(
"<b>Note:</b> A newer version of Sonic Visualiser is available.<br>(Version %1 is available; you are using version %2)").arg(
m_newerVersionIs).arg(SV_VERSION)), row++, 1, 1, 2);
5374 QDialogButtonBox *bb =
new QDialogButtonBox(QDialogButtonBox::Ok);
5375 layout->addWidget(bb, row++, 0, 1, 3);
5376 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
5379 text.replace(
'\r',
"");
5383 text.replace(QRegExp(
"(.)\n +(.)"),
"\\1 \\2");
5386 text.replace(QRegExp(
"\n - ([^\n]+)"),
"\n<li>\\1</li>");
5389 text.replace(QRegExp(
": *\n"),
":\n<ul>\n");
5392 text.replace(QRegExp(
"</li>\n\\s*\n"),
"</li>\n</ul>\n\n");
5395 text.replace(QRegExp(
"\n(\\w[^:\n]+:)"),
"\n<p><b>\\1</b></p>");
5397 textEdit->setHtml(text);
5398 textEdit->setReadOnly(
true);
5400 d->setMinimumSize(m_viewManager->scalePixelSize(520),
5401 m_viewManager->scalePixelSize(450));
5412 QString version =
"(unknown version)";
5416 #endif // BUILD_DEBUG 5419 version = tr(
"Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
5421 version = tr(
"Release %1").arg(SV_VERSION);
5423 #else // !SV_VERSION 5425 version = tr(
"Unreleased : Revision %1").arg(SVNREV);
5427 #endif // SV_VERSION 5429 return tr(
"%1 : %2 configuration, %3-bit build")
5431 .arg(debug ? tr(
"Debug") : tr(
"Release"))
5432 .arg(
sizeof(
void *) * 8);
5440 aboutText += tr(
"<h3>About Sonic Visualiser</h3>");
5441 aboutText += tr(
"<p>Sonic Visualiser is a program for viewing and exploring audio data for semantic music analysis and annotation.<br><a href=\"http://www.sonicvisualiser.org/\">http://www.sonicvisualiser.org/</a></p>");
5442 aboutText += QString(
"<p><small>%1</small></p>").arg(
getReleaseText());
5444 if (m_oscQueue && m_oscQueue->isOK()) {
5445 aboutText += tr(
"</small><p><small>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
5448 aboutText +=
"</small><p><small>";
5450 aboutText += tr(
"With Qt v%1 © The Qt Company").arg(QT_VERSION_STR);
5452 aboutText +=
"</small><small>";
5456 aboutText += tr(
"<br>With JACK audio output library v%1 © Paul Davis and Jack O'Quin").arg(JACK_VERSION);
5457 #else // !JACK_VERSION 5458 aboutText += tr(
"<br>With JACK audio output library © Paul Davis and Jack O'Quin");
5459 #endif // JACK_VERSION 5461 #ifdef HAVE_PORTAUDIO 5462 aboutText += tr(
"<br>With PortAudio audio output library © Ross Bencina and Phil Burk");
5463 #endif // HAVE_PORTAUDIO 5464 #ifdef HAVE_LIBPULSE 5465 #ifdef LIBPULSE_VERSION 5466 aboutText += tr(
"<br>With PulseAudio audio output library v%1 © Lennart Poettering and Pierre Ossman").arg(LIBPULSE_VERSION);
5467 #else // !LIBPULSE_VERSION 5468 aboutText += tr(
"<br>With PulseAudio audio output library © Lennart Poettering and Pierre Ossman");
5469 #endif // LIBPULSE_VERSION 5470 #endif // HAVE_LIBPULSE 5473 aboutText += tr(
"<br>With Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
5474 #else // !OGGZ_VERSION 5475 aboutText += tr(
"<br>With Ogg file decoder © CSIRO Australia");
5476 #endif // OGGZ_VERSION 5479 aboutText += tr(
"<br>With Opus decoder © Xiph.Org Foundation");
5483 aboutText += tr(
"<br>With MAD mp3 decoder v%1 © Underbit Technologies Inc").arg(MAD_VERSION);
5484 #else // !MAD_VERSION 5485 aboutText += tr(
"<br>With MAD mp3 decoder © Underbit Technologies Inc");
5486 #endif // MAD_VERSION 5488 #ifdef HAVE_SAMPLERATE 5489 #ifdef SAMPLERATE_VERSION 5490 aboutText += tr(
"<br>With libsamplerate v%1 © Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
5491 #else // !SAMPLERATE_VERSION 5492 aboutText += tr(
"<br>With libsamplerate © Erik de Castro Lopo");
5493 #endif // SAMPLERATE_VERSION 5494 #endif // HAVE_SAMPLERATE 5496 #ifdef SNDFILE_VERSION 5497 aboutText += tr(
"<br>With libsndfile v%1 © Erik de Castro Lopo").arg(SNDFILE_VERSION);
5498 #else // !SNDFILE_VERSION 5499 aboutText += tr(
"<br>With libsndfile © Erik de Castro Lopo");
5500 #endif // SNDFILE_VERSION 5501 #endif // HAVE_SNDFILE 5503 #ifdef FFTW3_VERSION 5504 aboutText += tr(
"<br>With FFTW3 v%1 © Matteo Frigo and MIT").arg(FFTW3_VERSION);
5505 #else // !FFTW3_VERSION 5506 aboutText += tr(
"<br>With FFTW3 © Matteo Frigo and MIT");
5507 #endif // FFTW3_VERSION 5508 #endif // HAVE_FFTW3F 5509 #ifdef HAVE_RUBBERBAND 5510 #ifdef RUBBERBAND_VERSION 5511 aboutText += tr(
"<br>With Rubber Band Library v%1 © Particular Programs Ltd").arg(RUBBERBAND_VERSION);
5512 #else // !RUBBERBAND_VERSION 5513 aboutText += tr(
"<br>With Rubber Band Library © Particular Programs Ltd");
5514 #endif // RUBBERBAND_VERSION 5515 #endif // HAVE_RUBBERBAND 5516 aboutText += tr(
"<br>With Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam and QMUL").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION);
5517 aboutText += tr(
"<br>With Piper Vamp protocol bridge © QMUL");
5518 aboutText += tr(
"<br>With LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
5519 aboutText += tr(
"<br>With DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
5520 #ifdef REDLAND_VERSION 5521 aboutText += tr(
"<br>With Redland RDF datastore v%1 © Dave Beckett and the University of Bristol").arg(REDLAND_VERSION);
5522 #else // !REDLAND_VERSION 5523 aboutText += tr(
"<br>With Redland RDF datastore © Dave Beckett and the University of Bristol");
5524 #endif // REDLAND_VERSION 5525 aboutText += tr(
"<br>With Serd and Sord RDF parser and store © David Robillard");
5526 aboutText += tr(
"<br>With Dataquay Qt/RDF library © Particular Programs Ltd");
5527 aboutText += tr(
"<br>With Cap'n Proto serialisation © Sandstorm Development Group");
5528 aboutText += tr(
"<br>With RtMidi © Gary P. Scavone");
5531 #ifdef LIBLO_VERSION 5532 aboutText += tr(
"<br>With liblo Lite OSC library v%1 © Steve Harris").arg(LIBLO_VERSION);
5533 #else // !LIBLO_VERSION 5534 aboutText += tr(
"<br>With liblo Lite OSC library © Steve Harris");
5535 #endif // LIBLO_VERSION 5537 aboutText +=
"</small></p>";
5538 #endif // HAVE_LIBLO 5540 aboutText +=
"<p><small>";
5541 aboutText += tr(
"Russian UI translation contributed by Alexandre Prokoudine.");
5542 aboutText +=
"<br>";
5543 aboutText += tr(
"Czech UI translation contributed by Pavel Fric.");
5544 aboutText +=
"</small></p>";
5547 "<p><small>Sonic Visualiser Copyright © 2005–2020 Chris Cannam and " 5548 "Queen Mary, University of London.</small></p>";
5551 "<p><small>This program is free software; you can redistribute it and/or " 5552 "modify it under the terms of the GNU General Public License as " 5553 "published by the Free Software Foundation; either version 2 of the " 5554 "License, or (at your option) any later version.<br>See the file " 5555 "COPYING included with this distribution for more information.</small></p>";
5559 QDialog *d =
new QDialog(
this);
5561 d->setWindowTitle(tr(
"About %1").arg(QApplication::applicationName()));
5563 QGridLayout *layout =
new QGridLayout;
5564 d->setLayout(layout);
5568 QLabel *iconLabel =
new QLabel;
5569 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
5570 layout->addWidget(iconLabel, row, 0, Qt::AlignTop);
5572 QLabel *mainText =
new QLabel();
5573 layout->addWidget(mainText, row, 1, 1, 2);
5575 layout->setRowStretch(row, 10);
5576 layout->setColumnStretch(1, 10);
5580 QDialogButtonBox *bb =
new QDialogButtonBox(QDialogButtonBox::Ok);
5581 layout->addWidget(bb, row++, 0, 1, 3);
5582 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
5586 mainText->setWordWrap(
true);
5587 mainText->setOpenExternalLinks(
true);
5588 mainText->setText(aboutText);
5590 d->setMinimumSize(m_viewManager->scalePixelSize(420),
5591 m_viewManager->scalePixelSize(200));
5626 settings.beginGroup(
"NewerVersionWarning");
5627 QString tag = QString(
"version-%1-available-show").arg(version);
5628 if (settings.value(tag,
true).toBool()) {
5629 QString title(tr(
"Newer version available"));
5630 QString text(tr(
"<h3>Newer version available</h3><p>You are using version %1 of Sonic Visualiser, but version %2 is now available.</p><p>Please see the <a href=\"http://sonicvisualiser.org/\">Sonic Visualiser website</a> for more information.</p>").arg(SV_VERSION).arg(version));
5631 QMessageBox::information(
this, title, text);
5632 settings.setValue(tag,
false);
5634 settings.endGroup();
virtual void openRecentFile()
virtual void showActivityLog()
void modelRegenerationFailed(QString, QString, QString) override
virtual void toolEraseSelected()
QFileSystemWatcher * m_templateWatcher
virtual void importMoreAudio()
QMenu * m_recentTransformsMenu
virtual void coloursChanged()
virtual void convertAudio()
virtual void preferences()
ExistingLayerActions m_existingLayerActions
QAction * m_playLoopAction
QAction * m_zoomOutAction
void setupMenus() override
void paneCancelButtonPressed(Layer *)
void paneRightButtonMenuRequested(Pane *, QPoint point) override
QString shortcutFor(LayerFactory::LayerType, bool isPaneMenu)
bool checkSaveModified() override
virtual void midiEventsAvailable()
void populateTransformsMenu()
void layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint point) override
virtual void setupRecentFilesMenu()
virtual void openSomething()
virtual void saveSession()
virtual void pluginPopulationWarning(QString text)
QPointer< PreferencesDialog > m_preferencesDialog
virtual void findTransform()
virtual void showUnitConverter()
QMenu * m_rightButtonPlaybackMenu
void monitoringLevelsChanged(float, float) override
virtual void resetInstantsCounters()
QAction * m_showPropertyBoxesAction
QAction * m_deleteSelectedAction
ToolActions m_toolActions
void prepareTransformsMenu()
virtual void importAudio()
virtual void manageSavedTemplates()
void modelGenerationFailed(QString, QString) override
virtual void keyReference()
LayerActions m_layerActions
virtual void setInstantsCounters()
virtual void importLayer()
virtual void mainModelPanChanged(float)
virtual void toolMeasureSelected()
virtual void exportImage()
void updateVisibleRangeDisplay(Pane *p) const override
VersionTester * m_versionTester
void playSoloToggled() override
void documentRestored() override
virtual void applyTemplate()
virtual void winnowInstants()
QScrollArea * m_mainScroll
void setupExistingLayersMenus()
virtual void slowDownPlayback()
void panePropertiesRightButtonMenuRequested(Pane *, QPoint point) override
KeyReference * m_keyReference
virtual void openLocation()
virtual void subdivideInstants()
virtual void setInstantsNumbering()
void modelRegenerationWarning(QString, QString, QString) override
void setupPaneAndLayerMenus()
PaneActions m_paneActions
TransformActions m_transformActions
virtual void exportAudio()
NumberingActions m_numberingActions
void currentPaneChanged(Pane *) override
virtual void restoreNormalPlayback()
void closeEvent(QCloseEvent *e) override
virtual void mouseEnteredWidget()
virtual void replaceMainAudio()
virtual void playStatusChanged(bool)
UnitConverter * m_unitConverter
QPointer< LayerTreeDialog > m_layerTreeDialog
void closeSession() override
void updateDescriptionLabel() override
TransformActionReverseMap m_transformActionsReverse
void connectLayerEditDialog(ModelDataTableDialog *) override
virtual void saveSessionAsTemplate()
LevelPanToolButton * m_mainLevelPan
virtual void toolEditSelected()
QAction * m_manageTemplatesAction
void newerVersionAvailable(QString) override
virtual void toolSelectSelected()
QAction * m_scrollLeftAction
QMenu * m_rightButtonMenu
void sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool) override
QAction * m_rwdSimilarAction
void modelGenerationWarning(QString, QString) override
QMenu * m_rightButtonLayerMenu
void documentModified() override
QLabel * m_descriptionLabel
QMenu * m_recentFilesMenu
virtual void speedUpPlayback()
virtual void betaReleaseWarning()
ActivityLog * m_activityLog
void mainModelChanged(ModelId) override
void updatePositionStatusDisplays() const override
QMenu * m_rightButtonTransformsMenu
void audioOverloadPluginDisabled() override
void paneAboutToBeDeleted(Pane *) override
virtual void setupRecentTransformsMenu()
QMenu * m_lastRightButtonPropertyMenu
QString getReleaseText() const
virtual void saveSessionAs()
ExistingLayerActions m_sliceActions
void updateLayerShortcutsFor(ModelId)
virtual void exportLayer()
virtual void mouseLeftWidget()
virtual void propertyStacksResized(int)
virtual void exportAudioData()
void preferenceChanged(PropertyContainer::PropertyName) override
virtual void documentReplaced()
void modelAdded(ModelId) override
virtual void playSpeedChanged(int)
void alignmentFailed(ModelId, QString) override
TransformPopulater * m_transformPopulater
void paneAdded(Pane *) override
QAction * m_ffwdEndAction
LayerFactory::LayerType layer
void updateMenuStates() override
virtual bool commitData(bool mayAskUser)
virtual void toolNavigateSelected()
MainWindow(AudioMode audioMode, MIDIMode midiMode, bool withOSCSupport)
virtual void setInstantsCounterCycle()
QAction * m_zoomFitAction
QAction * m_playSelectionAction
virtual void newSession()
QAction * m_rwdStartAction
virtual void showLayerTree()
virtual void browseRecordedAudio()
virtual void alignToggled()
virtual void toolDrawSelected()
virtual void mainModelGainChanged(float)
bool shouldCreateNewSessionForRDFAudio(bool *cancel) override
virtual void setupTemplatesMenu()
QFrame * m_playControlsSpacer
void paneHidden(Pane *) override
void paneDropAccepted(Pane *, QStringList) override
QAction * m_ffwdSimilarAction
QMenu * m_existingLayersMenu
virtual void renameCurrentLayer()
void layerRemoved(Layer *) override
WaveformLayer * m_panLayer
void layerInAView(Layer *, bool) override
QAction * m_scrollRightAction