Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: Sonic Visualiser Chris@0: An audio file viewer and annotation editor. Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@77: This file copyright 2006 Chris Cannam and QMUL. Chris@0: Chris@0: This program is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU General Public License as Chris@0: published by the Free Software Foundation; either version 2 of the Chris@0: License, or (at your option) any later version. See the file Chris@0: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@144: #include "../version.h" Chris@0: Chris@0: #include "MainWindow.h" Chris@1: #include "document/Document.h" Chris@0: #include "PreferencesDialog.h" Chris@0: Chris@1: #include "view/Pane.h" Chris@1: #include "view/PaneStack.h" Chris@1: #include "data/model/WaveFileModel.h" Chris@1: #include "data/model/SparseOneDimensionalModel.h" Chris@185: #include "data/model/NoteModel.h" Chris@189: #include "data/model/Labeller.h" Chris@1: #include "view/ViewManager.h" Chris@0: #include "base/Preferences.h" Chris@0: #include "layer/WaveformLayer.h" Chris@0: #include "layer/TimeRulerLayer.h" Chris@0: #include "layer/TimeInstantLayer.h" Chris@0: #include "layer/TimeValueLayer.h" Chris@0: #include "layer/Colour3DPlotLayer.h" Chris@95: #include "layer/SliceLayer.h" Chris@95: #include "layer/SliceableLayer.h" Chris@0: #include "widgets/Fader.h" Chris@65: #include "view/Overview.h" Chris@0: #include "widgets/PropertyBox.h" Chris@0: #include "widgets/PropertyStack.h" Chris@0: #include "widgets/AudioDial.h" Chris@168: #include "widgets/IconLoader.h" Chris@0: #include "widgets/LayerTree.h" Chris@0: #include "widgets/ListInputDialog.h" Chris@36: #include "widgets/SubdividingMenu.h" Chris@90: #include "widgets/NotifyingPushButton.h" Chris@162: #include "widgets/KeyReference.h" Chris@0: #include "audioio/AudioCallbackPlaySource.h" Chris@0: #include "audioio/AudioCallbackPlayTarget.h" Chris@0: #include "audioio/AudioTargetFactory.h" Chris@59: #include "audioio/PlaySpeedRangeMapper.h" Chris@1: #include "data/fileio/DataFileReaderFactory.h" Chris@180: #include "data/fileio/PlaylistFileReader.h" Chris@1: #include "data/fileio/WavFileWriter.h" Chris@1: #include "data/fileio/CSVFileWriter.h" Chris@185: #include "data/fileio/MIDIFileWriter.h" Chris@1: #include "data/fileio/BZipFileDevice.h" Chris@85: #include "data/fileio/RemoteFile.h" Chris@91: #include "data/fft/FFTDataServer.h" Chris@1: #include "base/RecentFiles.h" Chris@0: #include "transform/TransformFactory.h" Chris@0: #include "base/PlayParameterRepository.h" Chris@0: #include "base/XmlExportable.h" Chris@0: #include "base/CommandHistory.h" Chris@0: #include "base/Profiler.h" Chris@0: #include "base/Clipboard.h" Chris@165: #include "base/UnitDatabase.h" Chris@165: #include "base/ColourDatabase.h" Chris@69: #include "osc/OSCQueue.h" Chris@0: Chris@180: //!!! Chris@180: #include "data/model/AggregateWaveModel.h" Chris@180: Chris@0: // For version information Chris@0: #include "vamp/vamp.h" Chris@0: #include "vamp-sdk/PluginBase.h" Chris@0: #include "plugin/api/ladspa.h" Chris@0: #include "plugin/api/dssi.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@88: #include Chris@88: #include Chris@0: #include Chris@0: #include Chris@7: #include Chris@5: #include Chris@11: #include Chris@11: #include Chris@16: #include Chris@55: #include Chris@158: #include Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: using std::cerr; Chris@0: using std::endl; Chris@0: Chris@33: using std::vector; Chris@33: using std::map; Chris@33: using std::set; Chris@33: Chris@0: Chris@70: MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : Chris@0: m_document(0), Chris@0: m_paneStack(0), Chris@0: m_viewManager(0), Chris@65: m_overview(0), Chris@0: m_timeRulerLayer(0), Chris@46: m_audioOutput(withAudioOutput), Chris@0: m_playSource(0), Chris@0: m_playTarget(0), Chris@70: m_oscQueue(withOSCSupport ? new OSCQueue() : 0), Chris@81: m_recentFiles("RecentFiles", 20), Chris@49: m_recentTransforms("RecentTransforms", 20), Chris@0: m_mainMenusCreated(false), Chris@0: m_paneMenu(0), Chris@0: m_layerMenu(0), Chris@34: m_transformsMenu(0), Chris@155: m_playbackMenu(0), Chris@0: m_existingLayersMenu(0), Chris@95: m_sliceMenu(0), Chris@34: m_recentFilesMenu(0), Chris@34: m_recentTransformsMenu(0), Chris@0: m_rightButtonMenu(0), Chris@0: m_rightButtonLayerMenu(0), Chris@34: m_rightButtonTransformsMenu(0), Chris@155: m_rightButtonPlaybackMenu(0), Chris@155: m_ffwdAction(0), Chris@155: m_rwdAction(0), Chris@0: m_documentModified(false), Chris@70: m_openingAudioFile(false), Chris@70: m_abandoning(false), Chris@189: m_labeller(0), Chris@162: m_preferencesDialog(0), Chris@177: m_layerTreeView(0), Chris@162: m_keyReference(new KeyReference()) Chris@0: { Chris@0: setWindowTitle(tr("Sonic Visualiser")); Chris@0: Chris@165: UnitDatabase *udb = UnitDatabase::getInstance(); Chris@165: udb->registerUnit("Hz"); Chris@165: udb->registerUnit("dB"); Chris@165: udb->registerUnit("s"); Chris@165: Chris@165: ColourDatabase *cdb = ColourDatabase::getInstance(); Chris@165: cdb->addColour(Qt::black, tr("Black")); Chris@165: cdb->addColour(Qt::darkRed, tr("Red")); Chris@165: cdb->addColour(Qt::darkBlue, tr("Blue")); Chris@165: cdb->addColour(Qt::darkGreen, tr("Green")); Chris@165: cdb->addColour(QColor(200, 50, 255), tr("Purple")); Chris@165: cdb->addColour(QColor(255, 150, 50), tr("Orange")); Chris@166: cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true); Chris@166: cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true); Chris@166: cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true); Chris@166: cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true); Chris@166: cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true); Chris@166: cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true); Chris@0: Chris@0: connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), Chris@0: this, SLOT(documentModified())); Chris@0: connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), Chris@0: this, SLOT(documentRestored())); Chris@0: Chris@0: QFrame *frame = new QFrame; Chris@0: setCentralWidget(frame); Chris@0: Chris@0: QGridLayout *layout = new QGridLayout; Chris@0: Chris@0: m_viewManager = new ViewManager(); Chris@0: connect(m_viewManager, SIGNAL(selectionChanged()), Chris@0: this, SLOT(updateMenuStates())); Chris@117: connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), Chris@117: this, SLOT(inProgressSelectionChanged())); Chris@0: Chris@180: Preferences::BackgroundMode mode = Chris@180: Preferences::getInstance()->getBackgroundMode(); Chris@180: m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); Chris@180: if (mode != Preferences::BackgroundFromTheme) { Chris@180: m_viewManager->setGlobalDarkBackground Chris@180: (mode == Preferences::DarkBackground); Chris@180: } Chris@180: Chris@0: m_descriptionLabel = new QLabel; Chris@0: Chris@158: QScrollArea *scroll = new QScrollArea(frame); Chris@158: scroll->setWidgetResizable(true); Chris@158: scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); Chris@159: scroll->setFrameShape(QFrame::NoFrame); Chris@159: Chris@159: m_paneStack = new PaneStack(scroll, m_viewManager); Chris@0: connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), Chris@0: this, SLOT(currentPaneChanged(Pane *))); Chris@0: connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), Chris@0: this, SLOT(currentLayerChanged(Pane *, Layer *))); Chris@0: connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), Chris@0: this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); Chris@73: connect(m_paneStack, SIGNAL(propertyStacksResized()), Chris@73: this, SLOT(propertyStacksResized())); Chris@90: connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), Chris@116: this, SLOT(contextHelpChanged(const QString &))); Chris@0: Chris@159: scroll->setWidget(m_paneStack); Chris@159: Chris@65: m_overview = new Overview(frame); Chris@65: m_overview->setViewManager(m_viewManager); Chris@65: m_overview->setFixedHeight(40); Chris@144: #ifndef _WIN32 Chris@144: // For some reason, the contents of the overview never appear if we Chris@144: // make this setting on Windows. I have no inclination at the moment Chris@144: // to track down the reason why. Chris@129: m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); Chris@144: #endif Chris@90: connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), Chris@116: this, SLOT(contextHelpChanged(const QString &))); Chris@0: Chris@0: m_panLayer = new WaveformLayer; Chris@0: m_panLayer->setChannelMode(WaveformLayer::MergeChannels); Chris@0: m_panLayer->setAggressiveCacheing(true); Chris@65: m_overview->addLayer(m_panLayer); Chris@174: Chris@174: if (m_viewManager->getGlobalDarkBackground()) { Chris@174: m_panLayer->setBaseColour Chris@174: (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); Chris@174: } else { Chris@174: m_panLayer->setBaseColour Chris@174: (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); Chris@174: } Chris@0: Chris@0: m_playSource = new AudioCallbackPlaySource(m_viewManager); Chris@0: Chris@0: connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), Chris@0: this, SLOT(sampleRateMismatch(size_t, size_t, bool))); Chris@42: connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), Chris@42: this, SLOT(audioOverloadPluginDisabled())); Chris@0: Chris@0: m_fader = new Fader(frame, false); Chris@90: connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@90: connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@0: Chris@0: m_playSpeed = new AudioDial(frame); Chris@12: m_playSpeed->setMinimum(0); Chris@48: m_playSpeed->setMaximum(200); Chris@25: m_playSpeed->setValue(100); Chris@0: m_playSpeed->setFixedWidth(24); Chris@0: m_playSpeed->setFixedHeight(24); Chris@0: m_playSpeed->setNotchesVisible(true); Chris@25: m_playSpeed->setPageStep(10); Chris@60: m_playSpeed->setObjectName(tr("Playback Speedup")); Chris@25: m_playSpeed->setDefaultValue(100); Chris@59: m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200)); Chris@60: m_playSpeed->setShowToolTip(true); Chris@0: connect(m_playSpeed, SIGNAL(valueChanged(int)), Chris@0: this, SLOT(playSpeedChanged(int))); Chris@90: connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@90: connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@90: Chris@168: IconLoader il; Chris@168: Chris@90: m_playSharpen = new NotifyingPushButton(frame); Chris@17: m_playSharpen->setToolTip(tr("Sharpen percussive transients")); Chris@26: m_playSharpen->setFixedSize(20, 20); Chris@16: m_playSharpen->setEnabled(false); Chris@26: m_playSharpen->setCheckable(true); Chris@26: m_playSharpen->setChecked(false); Chris@168: m_playSharpen->setIcon(il.load("sharpen")); Chris@26: connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled())); Chris@90: connect(m_playSharpen, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@90: connect(m_playSharpen, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@90: Chris@90: m_playMono = new NotifyingPushButton(frame); Chris@26: m_playMono->setToolTip(tr("Run time stretcher in mono only")); Chris@26: m_playMono->setFixedSize(20, 20); Chris@26: m_playMono->setEnabled(false); Chris@26: m_playMono->setCheckable(true); Chris@26: m_playMono->setChecked(false); Chris@168: m_playMono->setIcon(il.load("mono")); Chris@26: connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled())); Chris@90: connect(m_playMono, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@90: connect(m_playMono, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@21: Chris@21: QSettings settings; Chris@21: settings.beginGroup("MainWindow"); Chris@24: m_playSharpen->setChecked(settings.value("playsharpen", true).toBool()); Chris@26: m_playMono->setChecked(settings.value("playmono", false).toBool()); Chris@21: settings.endGroup(); Chris@21: Chris@129: layout->setSpacing(4); Chris@159: layout->addWidget(scroll, 0, 0, 1, 5); Chris@65: layout->addWidget(m_overview, 1, 0); Chris@0: layout->addWidget(m_fader, 1, 1); Chris@0: layout->addWidget(m_playSpeed, 1, 2); Chris@16: layout->addWidget(m_playSharpen, 1, 3); Chris@26: layout->addWidget(m_playMono, 1, 4); Chris@16: Chris@129: m_paneStack->setPropertyStackMinWidth Chris@129: (m_fader->width() + m_playSpeed->width() + m_playSharpen->width() + Chris@129: m_playMono->width() + layout->spacing() * 4); Chris@129: Chris@16: layout->setColumnStretch(0, 10); Chris@16: Chris@0: frame->setLayout(layout); Chris@0: Chris@0: connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), Chris@0: this, SLOT(outputLevelsChanged(float, float))); Chris@0: Chris@116: connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), Chris@116: this, SLOT(playbackFrameChanged(unsigned long))); Chris@116: Chris@116: connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), Chris@116: this, SLOT(globalCentreFrameChanged(unsigned long))); Chris@116: Chris@116: connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), Chris@116: this, SLOT(viewCentreFrameChanged(View *, unsigned long))); Chris@116: Chris@116: connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), Chris@116: this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); Chris@116: Chris@0: connect(Preferences::getInstance(), Chris@0: SIGNAL(propertyChanged(PropertyContainer::PropertyName)), Chris@0: this, Chris@0: SLOT(preferenceChanged(PropertyContainer::PropertyName))); Chris@0: Chris@98: // preferenceChanged("Property Box Layout"); Chris@98: Chris@70: if (m_oscQueue && m_oscQueue->isOK()) { Chris@69: connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); Chris@69: QTimer *oscTimer = new QTimer(this); Chris@69: connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); Chris@69: oscTimer->start(1000); Chris@69: } Chris@69: Chris@189: Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; Chris@189: settings.beginGroup("MainWindow"); Chris@189: labellerType = (Labeller::ValueType) Chris@189: settings.value("labellertype", (int)labellerType).toInt(); Chris@189: int cycle = settings.value("labellercycle", 4).toInt(); Chris@189: settings.endGroup(); Chris@189: Chris@189: m_labeller = new Labeller(labellerType); Chris@189: m_labeller->setCounterCycleSize(cycle); Chris@189: Chris@0: setupMenus(); Chris@0: setupToolbars(); Chris@155: setupHelpMenu(); Chris@0: Chris@90: statusBar(); Chris@0: Chris@0: newSession(); Chris@0: } Chris@0: Chris@0: MainWindow::~MainWindow() Chris@0: { Chris@137: // std::cerr << "MainWindow::~MainWindow()" << std::endl; Chris@118: Chris@70: if (!m_abandoning) { Chris@70: closeSession(); Chris@70: } Chris@0: delete m_playTarget; Chris@0: delete m_playSource; Chris@0: delete m_viewManager; Chris@69: delete m_oscQueue; Chris@162: delete m_keyReference; Chris@163: delete m_preferencesDialog; Chris@177: delete m_layerTreeView; Chris@0: Profiles::getInstance()->dump(); Chris@0: } Chris@0: Chris@81: QString Chris@88: MainWindow::getOpenFileName(FileFinder::FileType type) Chris@81: { Chris@88: FileFinder *ff = FileFinder::getInstance(); Chris@81: switch (type) { Chris@88: case FileFinder::SessionFile: Chris@88: return ff->getOpenFileName(type, m_sessionFile); Chris@88: case FileFinder::AudioFile: Chris@88: return ff->getOpenFileName(type, m_audioFile); Chris@88: case FileFinder::LayerFile: Chris@88: return ff->getOpenFileName(type, m_sessionFile); Chris@185: case FileFinder::LayerFileNoMidi: Chris@185: return ff->getOpenFileName(type, m_sessionFile); Chris@88: case FileFinder::SessionOrAudioFile: Chris@88: return ff->getOpenFileName(type, m_sessionFile); Chris@121: case FileFinder::ImageFile: Chris@121: return ff->getOpenFileName(type, m_sessionFile); Chris@88: case FileFinder::AnyFile: Chris@88: if (getMainModel() != 0 && Chris@88: m_paneStack != 0 && Chris@88: m_paneStack->getCurrentPane() != 0) { // can import a layer Chris@88: return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); Chris@81: } else { Chris@88: return ff->getOpenFileName(FileFinder::SessionOrAudioFile, Chris@88: m_sessionFile); Chris@81: } Chris@81: } Chris@88: return ""; Chris@88: } Chris@88: Chris@88: QString Chris@88: MainWindow::getSaveFileName(FileFinder::FileType type) Chris@88: { Chris@88: FileFinder *ff = FileFinder::getInstance(); Chris@88: switch (type) { Chris@88: case FileFinder::SessionFile: Chris@88: return ff->getSaveFileName(type, m_sessionFile); Chris@88: case FileFinder::AudioFile: Chris@88: return ff->getSaveFileName(type, m_audioFile); Chris@88: case FileFinder::LayerFile: Chris@88: return ff->getSaveFileName(type, m_sessionFile); Chris@185: case FileFinder::LayerFileNoMidi: Chris@185: return ff->getSaveFileName(type, m_sessionFile); Chris@88: case FileFinder::SessionOrAudioFile: Chris@88: return ff->getSaveFileName(type, m_sessionFile); Chris@121: case FileFinder::ImageFile: Chris@121: return ff->getSaveFileName(type, m_sessionFile); Chris@88: case FileFinder::AnyFile: Chris@88: return ff->getSaveFileName(type, m_sessionFile); Chris@81: } Chris@88: return ""; Chris@81: } Chris@81: Chris@88: void Chris@88: MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path) Chris@81: { Chris@88: FileFinder *ff = FileFinder::getInstance(); Chris@88: ff->registerLastOpenedFilePath(type, path); Chris@81: } Chris@81: Chris@81: void Chris@0: MainWindow::setupMenus() Chris@0: { Chris@0: if (!m_mainMenusCreated) { Chris@0: m_rightButtonMenu = new QMenu(); Chris@104: Chris@104: // No -- we don't want tear-off enabled on the right-button Chris@104: // menu. If it is enabled, then simply right-clicking and Chris@104: // releasing will pop up the menu, activate the tear-off, and Chris@104: // leave the torn-off menu window in front of the main window. Chris@104: // That isn't desirable. I'm not sure it ever would be, in a Chris@104: // context menu -- perhaps technically a Qt bug? Chris@104: // m_rightButtonMenu->setTearOffEnabled(true); Chris@0: } Chris@0: Chris@0: if (m_rightButtonLayerMenu) { Chris@0: m_rightButtonLayerMenu->clear(); Chris@0: } else { Chris@0: m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer")); Chris@97: m_rightButtonLayerMenu->setTearOffEnabled(true); Chris@0: m_rightButtonMenu->addSeparator(); Chris@0: } Chris@0: Chris@34: if (m_rightButtonTransformsMenu) { Chris@34: m_rightButtonTransformsMenu->clear(); Chris@34: } else { Chris@34: m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform")); Chris@97: m_rightButtonTransformsMenu->setTearOffEnabled(true); Chris@34: m_rightButtonMenu->addSeparator(); Chris@34: } Chris@34: Chris@0: if (!m_mainMenusCreated) { Chris@0: CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); Chris@0: m_rightButtonMenu->addSeparator(); Chris@66: } Chris@66: Chris@66: setupFileMenu(); Chris@66: setupEditMenu(); Chris@66: setupViewMenu(); Chris@66: setupPaneAndLayerMenus(); Chris@66: setupTransformsMenu(); Chris@66: Chris@66: m_mainMenusCreated = true; Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupFileMenu() Chris@66: { Chris@66: if (m_mainMenusCreated) return; Chris@66: Chris@66: QMenu *menu = menuBar()->addMenu(tr("&File")); Chris@97: menu->setTearOffEnabled(true); Chris@66: QToolBar *toolbar = addToolBar(tr("File Toolbar")); Chris@66: Chris@162: m_keyReference->setCategory(tr("File and Session Management")); Chris@162: Chris@168: IconLoader il; Chris@168: Chris@168: QIcon icon = il.load("filenew"); Chris@168: icon.addPixmap(il.loadPixmap("filenew-22")); Chris@66: QAction *action = new QAction(icon, tr("&New Session"), this); Chris@66: action->setShortcut(tr("Ctrl+N")); Chris@90: action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(newSession())); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: toolbar->addAction(action); Chris@138: Chris@168: icon = il.load("fileopensession"); Chris@66: action = new QAction(icon, tr("&Open Session..."), this); Chris@66: action->setShortcut(tr("Ctrl+O")); Chris@66: action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(openSession())); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@168: icon = il.load("fileopen"); Chris@168: icon.addPixmap(il.loadPixmap("fileopen-22")); Chris@138: Chris@66: action = new QAction(icon, tr("&Open..."), this); Chris@66: action->setStatusTip(tr("Open a session file, audio file, or layer")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); Chris@66: toolbar->addAction(action); Chris@66: Chris@168: icon = il.load("filesave"); Chris@168: icon.addPixmap(il.loadPixmap("filesave-22")); Chris@66: action = new QAction(icon, tr("&Save Session"), this); Chris@66: action->setShortcut(tr("Ctrl+S")); Chris@66: action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(saveSession())); Chris@66: connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: toolbar->addAction(action); Chris@0: Chris@168: icon = il.load("filesaveas"); Chris@168: icon.addPixmap(il.loadPixmap("filesaveas-22")); Chris@66: action = new QAction(icon, tr("Save Session &As..."), this); Chris@66: action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs())); Chris@66: menu->addAction(action); Chris@66: toolbar->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@168: icon = il.load("fileopenaudio"); Chris@138: action = new QAction(icon, tr("&Import Audio File..."), this); Chris@66: action->setShortcut(tr("Ctrl+I")); Chris@66: action->setStatusTip(tr("Import an existing audio file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: action = new QAction(tr("Import Secondary Audio File..."), this); Chris@66: action->setShortcut(tr("Ctrl+Shift+I")); Chris@66: action->setStatusTip(tr("Import an extra audio file as a separate layer")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio())); Chris@66: connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: action = new QAction(tr("&Export Audio File..."), this); Chris@66: action->setStatusTip(tr("Export selection as an audio file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(exportAudio())); Chris@66: connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); Chris@66: menu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@66: action = new QAction(tr("Import Annotation &Layer..."), this); Chris@66: action->setShortcut(tr("Ctrl+L")); Chris@66: action->setStatusTip(tr("Import layer data from an existing file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(importLayer())); Chris@66: connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: action = new QAction(tr("Export Annotation Layer..."), this); Chris@66: action->setStatusTip(tr("Export layer data to a file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(exportLayer())); Chris@66: connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); Chris@66: menu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@86: Chris@121: action = new QAction(tr("Export Image File..."), this); Chris@121: action->setStatusTip(tr("Export a single pane to an image file")); Chris@121: connect(action, SIGNAL(triggered()), this, SLOT(exportImage())); Chris@121: connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool))); Chris@121: menu->addAction(action); Chris@121: Chris@121: menu->addSeparator(); Chris@121: Chris@86: action = new QAction(tr("Open Lo&cation..."), this); Chris@86: action->setShortcut(tr("Ctrl+Shift+O")); Chris@86: action->setStatusTip(tr("Open or import a file from a remote URL")); Chris@86: connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); Chris@162: m_keyReference->registerShortcut(action); Chris@86: menu->addAction(action); Chris@86: Chris@86: menu->addSeparator(); Chris@86: Chris@66: m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); Chris@97: m_recentFilesMenu->setTearOffEnabled(true); Chris@66: setupRecentFilesMenu(); Chris@66: connect(&m_recentFiles, SIGNAL(recentChanged()), Chris@66: this, SLOT(setupRecentFilesMenu())); Chris@66: Chris@66: menu->addSeparator(); Chris@66: action = new QAction(tr("&Preferences..."), this); Chris@66: action->setStatusTip(tr("Adjust the application preferences")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(preferences())); Chris@66: menu->addAction(action); Chris@0: Chris@66: menu->addSeparator(); Chris@168: action = new QAction(il.load("exit"), Chris@66: tr("&Quit"), this); Chris@66: action->setShortcut(tr("Ctrl+Q")); Chris@90: action->setStatusTip(tr("Exit Sonic Visualiser")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(close())); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupEditMenu() Chris@66: { Chris@66: if (m_mainMenusCreated) return; Chris@66: Chris@66: QMenu *menu = menuBar()->addMenu(tr("&Edit")); Chris@97: menu->setTearOffEnabled(true); Chris@66: CommandHistory::getInstance()->registerMenu(menu); Chris@66: Chris@162: m_keyReference->setCategory(tr("Editing")); Chris@162: Chris@66: menu->addSeparator(); Chris@66: Chris@168: IconLoader il; Chris@168: Chris@168: QAction *action = new QAction(il.load("editcut"), Chris@66: tr("Cu&t"), this); Chris@66: action->setShortcut(tr("Ctrl+X")); Chris@90: action->setStatusTip(tr("Cut the selection from the current layer to the clipboard")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(cut())); Chris@66: connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonMenu->addAction(action); Chris@66: Chris@168: action = new QAction(il.load("editcopy"), Chris@66: tr("&Copy"), this); Chris@66: action->setShortcut(tr("Ctrl+C")); Chris@90: action->setStatusTip(tr("Copy the selection from the current layer to the clipboard")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(copy())); Chris@66: connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonMenu->addAction(action); Chris@66: Chris@168: action = new QAction(il.load("editpaste"), Chris@66: tr("&Paste"), this); Chris@66: action->setShortcut(tr("Ctrl+V")); Chris@90: action->setStatusTip(tr("Paste from the clipboard to the current layer")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(paste())); Chris@66: connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonMenu->addAction(action); Chris@66: Chris@164: m_deleteSelectedAction = new QAction(tr("&Delete Selected Items"), this); Chris@164: m_deleteSelectedAction->setShortcut(tr("Del")); Chris@164: m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer")); Chris@164: connect(m_deleteSelectedAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); Chris@164: connect(this, SIGNAL(canDeleteSelection(bool)), m_deleteSelectedAction, SLOT(setEnabled(bool))); Chris@164: m_keyReference->registerShortcut(m_deleteSelectedAction); Chris@164: menu->addAction(m_deleteSelectedAction); Chris@164: m_rightButtonMenu->addAction(m_deleteSelectedAction); Chris@66: Chris@66: menu->addSeparator(); Chris@66: m_rightButtonMenu->addSeparator(); Chris@162: Chris@162: m_keyReference->setCategory(tr("Selection")); Chris@162: Chris@66: action = new QAction(tr("Select &All"), this); Chris@66: action->setShortcut(tr("Ctrl+A")); Chris@90: action->setStatusTip(tr("Select the whole duration of the current session")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); Chris@66: connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonMenu->addAction(action); Chris@0: Chris@66: action = new QAction(tr("Select &Visible Range"), this); Chris@66: action->setShortcut(tr("Ctrl+Shift+A")); Chris@90: action->setStatusTip(tr("Select the time range corresponding to the current window width")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(selectVisible())); Chris@66: connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@66: action = new QAction(tr("Select to &Start"), this); Chris@66: action->setShortcut(tr("Shift+Left")); Chris@90: action->setStatusTip(tr("Select from the start of the session to the current playback position")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(selectToStart())); Chris@66: connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@66: action = new QAction(tr("Select to &End"), this); Chris@66: action->setShortcut(tr("Shift+Right")); Chris@90: action->setStatusTip(tr("Select from the current playback position to the end of the session")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd())); Chris@66: connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: action = new QAction(tr("C&lear Selection"), this); Chris@66: action->setShortcut(tr("Esc")); Chris@90: action->setStatusTip(tr("Clear the selection")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(clearSelection())); Chris@66: connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonMenu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@162: m_keyReference->setCategory(tr("Tapping Time Instants")); Chris@162: Chris@66: action = new QAction(tr("&Insert Instant at Playback Position"), this); Chris@66: action->setShortcut(tr("Enter")); Chris@90: action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(insertInstant())); Chris@66: connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@162: // Laptop shortcut (no keypad Enter key) Chris@162: QString shortcut(tr(";")); Chris@162: connect(new QShortcut(shortcut, this), SIGNAL(activated()), Chris@162: this, SLOT(insertInstant())); Chris@162: m_keyReference->registerAlternativeShortcut(action, shortcut); Chris@162: Chris@81: action = new QAction(tr("Insert Instants at Selection &Boundaries"), this); Chris@81: action->setShortcut(tr("Shift+Enter")); Chris@163: action->setStatusTip(tr("Insert new time instants at the start and end of the current selected regions, in a new layer if necessary")); Chris@81: connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries())); Chris@81: connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@81: menu->addAction(action); Chris@189: Chris@189: QMenu *numberingMenu = menu->addMenu(tr("Set Instant Numbering")); Chris@189: QActionGroup *numberingGroup = new QActionGroup(this); Chris@189: Chris@189: Labeller::TypeNameMap types = m_labeller->getTypeNames(); Chris@189: for (Labeller::TypeNameMap::iterator i = types.begin(); i != types.end(); ++i) { Chris@189: if (i->first == Labeller::ValueFromLabel) continue; Chris@189: action = new QAction(i->second, this); Chris@189: connect(action, SIGNAL(triggered()), this, SLOT(setInstantsNumbering())); Chris@189: action->setCheckable(true); Chris@189: action->setChecked(m_labeller->getType() == i->first); Chris@189: numberingGroup->addAction(action); Chris@189: numberingMenu->addAction(action); Chris@189: m_numberingActions[action] = (int)i->first; Chris@189: } Chris@189: Chris@189: QMenu *cycleMenu = menu->addMenu(tr("Set Instant Counter Cycle")); Chris@189: QActionGroup *cycleGroup = new QActionGroup(this); Chris@189: Chris@189: int cycles[] = { 2, 3, 4, 5, 6, 7, 8, 10, 12, 16 }; Chris@189: for (int i = 0; i < sizeof(cycles)/sizeof(cycles[0]); ++i) { Chris@189: action = new QAction(QString("%1").arg(cycles[i]), this); Chris@189: connect(action, SIGNAL(triggered()), this, SLOT(setInstantsCounterCycle())); Chris@189: action->setCheckable(true); Chris@189: action->setChecked(cycles[i] == m_labeller->getCounterCycleSize()); Chris@189: cycleGroup->addAction(action); Chris@189: cycleMenu->addAction(action); Chris@189: } Chris@189: Chris@189: action = new QAction(tr("Re-Number Selected Instants"), this); Chris@189: action->setStatusTip(tr("Re-number the selected instants using the current labelling scheme")); Chris@189: connect(action, SIGNAL(triggered()), this, SLOT(renumberInstants())); Chris@189: connect(this, SIGNAL(canRenumberInstants(bool)), action, SLOT(setEnabled(bool))); Chris@189: // m_keyReference->registerShortcut(action); Chris@189: menu->addAction(action); Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupViewMenu() Chris@66: { Chris@66: if (m_mainMenusCreated) return; Chris@66: Chris@168: IconLoader il; Chris@168: Chris@90: QAction *action = 0; Chris@90: Chris@162: m_keyReference->setCategory(tr("Panning and Navigation")); Chris@162: Chris@66: QMenu *menu = menuBar()->addMenu(tr("&View")); Chris@97: menu->setTearOffEnabled(true); Chris@66: action = new QAction(tr("Scroll &Left"), this); Chris@66: action->setShortcut(tr("Left")); Chris@66: action->setStatusTip(tr("Scroll the current pane to the left")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft())); Chris@66: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@66: action = new QAction(tr("Scroll &Right"), this); Chris@66: action->setShortcut(tr("Right")); Chris@66: action->setStatusTip(tr("Scroll the current pane to the right")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(scrollRight())); Chris@66: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@90: action = new QAction(tr("&Jump Left"), this); Chris@66: action->setShortcut(tr("Ctrl+Left")); Chris@66: action->setStatusTip(tr("Scroll the current pane a big step to the left")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft())); Chris@66: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@90: action = new QAction(tr("J&ump Right"), this); Chris@66: action->setShortcut(tr("Ctrl+Right")); Chris@66: action->setStatusTip(tr("Scroll the current pane a big step to the right")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(jumpRight())); Chris@66: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@162: m_keyReference->setCategory(tr("Zoom")); Chris@162: Chris@168: action = new QAction(il.load("zoom-in"), Chris@66: tr("Zoom &In"), this); Chris@66: action->setShortcut(tr("Up")); Chris@66: action->setStatusTip(tr("Increase the zoom level")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(zoomIn())); Chris@66: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@168: action = new QAction(il.load("zoom-out"), Chris@66: tr("Zoom &Out"), this); Chris@66: action->setShortcut(tr("Down")); Chris@66: action->setStatusTip(tr("Decrease the zoom level")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(zoomOut())); Chris@66: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@0: Chris@66: action = new QAction(tr("Restore &Default Zoom"), this); Chris@90: action->setStatusTip(tr("Restore the zoom level to the default")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault())); Chris@66: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@66: menu->addAction(action); Chris@66: Chris@168: action = new QAction(il.load("zoom-fit"), Chris@138: tr("Zoom to &Fit"), this); Chris@155: action->setShortcut(tr("F")); Chris@66: action->setStatusTip(tr("Zoom to show the whole file")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit())); Chris@66: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@90: Chris@90: menu->addSeparator(); Chris@90: Chris@162: m_keyReference->setCategory(tr("Display Features")); Chris@162: Chris@90: QActionGroup *overlayGroup = new QActionGroup(this); Chris@90: Chris@90: action = new QAction(tr("Show &No Overlays"), this); Chris@90: action->setShortcut(tr("0")); Chris@90: action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale")); Chris@90: connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays())); Chris@90: action->setCheckable(true); Chris@90: action->setChecked(false); Chris@90: overlayGroup->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@90: menu->addAction(action); Chris@90: Chris@90: action = new QAction(tr("Show &Minimal Overlays"), this); Chris@90: action->setShortcut(tr("9")); Chris@90: action->setStatusTip(tr("Show centre indicator only")); Chris@90: connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays())); Chris@90: action->setCheckable(true); Chris@90: action->setChecked(false); Chris@90: overlayGroup->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@90: menu->addAction(action); Chris@90: Chris@90: action = new QAction(tr("Show &Standard Overlays"), this); Chris@90: action->setShortcut(tr("8")); Chris@90: action->setStatusTip(tr("Show centre indicator, frame times and scale")); Chris@90: connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays())); Chris@90: action->setCheckable(true); Chris@90: action->setChecked(true); Chris@90: overlayGroup->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@90: menu->addAction(action); Chris@90: Chris@90: action = new QAction(tr("Show &All Overlays"), this); Chris@90: action->setShortcut(tr("7")); Chris@90: action->setStatusTip(tr("Show all texts and scale")); Chris@90: connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays())); Chris@90: action->setCheckable(true); Chris@90: action->setChecked(false); Chris@90: overlayGroup->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@90: menu->addAction(action); Chris@7: Chris@72: menu->addSeparator(); Chris@72: Chris@66: action = new QAction(tr("Show &Zoom Wheels"), this); Chris@66: action->setShortcut(tr("Z")); Chris@66: action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels())); Chris@66: action->setCheckable(true); Chris@66: action->setChecked(m_viewManager->getZoomWheelsEnabled()); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@72: Chris@72: action = new QAction(tr("Show Property Bo&xes"), this); Chris@72: action->setShortcut(tr("X")); Chris@72: action->setStatusTip(tr("Show the layer property boxes at the side of the main window")); Chris@72: connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes())); Chris@72: action->setCheckable(true); Chris@72: action->setChecked(true); Chris@162: m_keyReference->registerShortcut(action); Chris@72: menu->addAction(action); Chris@0: Chris@90: action = new QAction(tr("Show Status &Bar"), this); Chris@90: action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window")); Chris@90: connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar())); Chris@90: action->setCheckable(true); Chris@90: action->setChecked(true); Chris@90: menu->addAction(action); Chris@90: Chris@90: QSettings settings; Chris@90: settings.beginGroup("MainWindow"); Chris@90: bool sb = settings.value("showstatusbar", true).toBool(); Chris@90: if (!sb) { Chris@90: action->setChecked(false); Chris@90: statusBar()->hide(); Chris@90: } Chris@90: settings.endGroup(); Chris@90: Chris@66: menu->addSeparator(); Chris@66: Chris@90: action = new QAction(tr("Show La&yer Hierarchy"), this); Chris@155: action->setShortcut(tr("H")); Chris@90: action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupPaneAndLayerMenus() Chris@66: { Chris@0: if (m_paneMenu) { Chris@0: m_paneActions.clear(); Chris@0: m_paneMenu->clear(); Chris@0: } else { Chris@0: m_paneMenu = menuBar()->addMenu(tr("&Pane")); Chris@97: m_paneMenu->setTearOffEnabled(true); Chris@0: } Chris@0: Chris@0: if (m_layerMenu) { Chris@0: m_layerActions.clear(); Chris@0: m_layerMenu->clear(); Chris@0: } else { Chris@0: m_layerMenu = menuBar()->addMenu(tr("&Layer")); Chris@97: m_layerMenu->setTearOffEnabled(true); Chris@0: } Chris@0: Chris@66: QMenu *menu = m_paneMenu; Chris@66: Chris@168: IconLoader il; Chris@168: Chris@162: m_keyReference->setCategory(tr("Managing Panes and Layers")); Chris@162: Chris@168: QAction *action = new QAction(il.load("pane"), tr("Add &New Pane"), this); Chris@155: action->setShortcut(tr("N")); Chris@66: action->setStatusTip(tr("Add a new pane containing only a time ruler")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(addPane())); Chris@66: connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); Chris@66: m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@66: menu = m_layerMenu; Chris@66: Chris@66: // menu->addSeparator(); Chris@66: Chris@66: LayerFactory::LayerTypeSet emptyLayerTypes = Chris@66: LayerFactory::getInstance()->getValidEmptyLayerTypes(); Chris@66: Chris@66: for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin(); Chris@66: i != emptyLayerTypes.end(); ++i) { Chris@66: Chris@66: QIcon icon; Chris@66: QString mainText, tipText, channelText; Chris@66: LayerFactory::LayerType type = *i; Chris@66: QString name = LayerFactory::getInstance()->getLayerPresentationName(type); Chris@66: Chris@168: icon = il.load(LayerFactory::getInstance()->getLayerIconName(type)); Chris@66: Chris@66: mainText = tr("Add New %1 Layer").arg(name); Chris@66: tipText = tr("Add a new empty layer of type %1").arg(name); Chris@66: Chris@66: action = new QAction(icon, mainText, this); Chris@66: action->setStatusTip(tipText); Chris@66: Chris@66: if (type == LayerFactory::Text) { Chris@155: action->setShortcut(tr("T")); Chris@162: m_keyReference->registerShortcut(action); Chris@66: } Chris@66: Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@66: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@66: m_layerActions[action] = type; Chris@66: menu->addAction(action); Chris@66: m_rightButtonLayerMenu->addAction(action); Chris@66: } Chris@66: Chris@66: m_rightButtonLayerMenu->addSeparator(); Chris@66: menu->addSeparator(); Chris@66: Chris@66: LayerFactory::LayerType backgroundTypes[] = { Chris@66: LayerFactory::Waveform, Chris@66: LayerFactory::Spectrogram, Chris@66: LayerFactory::MelodicRangeSpectrogram, Chris@66: LayerFactory::PeakFrequencySpectrogram, Chris@66: LayerFactory::Spectrum Chris@66: }; Chris@66: Chris@66: std::vector models; Chris@66: if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this! Chris@66: bool plural = (models.size() > 1); Chris@66: if (models.empty()) { Chris@67: models.push_back(getMainModel()); // probably 0 Chris@66: } Chris@66: Chris@66: for (unsigned int i = 0; Chris@66: i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) { Chris@66: Chris@66: for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer Chris@66: Chris@66: if (menuType == 0) menu = m_paneMenu; Chris@66: else menu = m_layerMenu; Chris@66: Chris@66: QMenu *submenu = 0; Chris@66: Chris@66: QIcon icon; Chris@66: QString mainText, shortcutText, tipText, channelText; Chris@66: LayerFactory::LayerType type = backgroundTypes[i]; Chris@66: bool mono = true; Chris@66: Chris@66: switch (type) { Chris@66: Chris@66: case LayerFactory::Waveform: Chris@168: icon = il.load("waveform"); Chris@66: mainText = tr("Add &Waveform"); Chris@66: if (menuType == 0) { Chris@155: shortcutText = tr("W"); Chris@66: tipText = tr("Add a new pane showing a waveform view"); Chris@66: } else { Chris@66: tipText = tr("Add a new layer showing a waveform view"); Chris@66: } Chris@66: mono = false; Chris@66: break; Chris@66: Chris@66: case LayerFactory::Spectrogram: Chris@168: icon = il.load("spectrogram"); Chris@161: mainText = tr("Add Spectro&gram"); Chris@66: if (menuType == 0) { Chris@155: shortcutText = tr("G"); Chris@90: tipText = tr("Add a new pane showing a spectrogram"); Chris@66: } else { Chris@90: tipText = tr("Add a new layer showing a spectrogram"); Chris@66: } Chris@66: break; Chris@66: Chris@66: case LayerFactory::MelodicRangeSpectrogram: Chris@168: icon = il.load("spectrogram"); Chris@66: mainText = tr("Add &Melodic Range Spectrogram"); Chris@66: if (menuType == 0) { Chris@155: shortcutText = tr("M"); Chris@90: tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches"); Chris@66: } else { Chris@90: tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches"); Chris@66: } Chris@66: break; Chris@66: Chris@66: case LayerFactory::PeakFrequencySpectrogram: Chris@168: icon = il.load("spectrogram"); Chris@155: mainText = tr("Add Pea&k Frequency Spectrogram"); Chris@66: if (menuType == 0) { Chris@155: shortcutText = tr("K"); Chris@66: tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies"); Chris@66: } else { Chris@66: tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies"); Chris@66: } Chris@66: break; Chris@66: Chris@66: case LayerFactory::Spectrum: Chris@168: icon = il.load("spectrum"); Chris@66: mainText = tr("Add Spectr&um"); Chris@66: if (menuType == 0) { Chris@155: shortcutText = tr("U"); Chris@66: tipText = tr("Add a new pane showing a frequency spectrum"); Chris@66: } else { Chris@66: tipText = tr("Add a new layer showing a frequency spectrum"); Chris@66: } Chris@66: break; Chris@66: Chris@66: default: break; Chris@66: } Chris@66: Chris@66: std::vector candidateModels; Chris@66: if (menuType == 0) { Chris@66: candidateModels = models; Chris@66: } else { Chris@66: candidateModels.push_back(0); Chris@66: } Chris@66: Chris@66: for (std::vector::iterator mi = Chris@66: candidateModels.begin(); Chris@66: mi != candidateModels.end(); ++mi) { Chris@66: Chris@66: Model *model = *mi; Chris@66: Chris@66: int channels = 0; Chris@66: if (model) { Chris@66: DenseTimeValueModel *dtvm = Chris@66: dynamic_cast(model); Chris@66: if (dtvm) channels = dtvm->getChannelCount(); Chris@66: } Chris@66: if (channels < 1 && getMainModel()) { Chris@66: channels = getMainModel()->getChannelCount(); Chris@66: } Chris@66: if (channels < 1) channels = 1; Chris@66: Chris@66: for (int c = 0; c <= channels; ++c) { Chris@66: Chris@66: if (c == 1 && channels == 1) continue; Chris@66: bool isDefault = (c == 0); Chris@66: bool isOnly = (isDefault && (channels == 1)); Chris@66: Chris@66: if (menuType == 1) { Chris@66: if (isDefault) isOnly = true; Chris@66: else continue; Chris@66: } Chris@66: Chris@66: if (isOnly && (!plural || menuType == 1)) { Chris@66: Chris@138: if (menuType == 1 && type != LayerFactory::Waveform) { Chris@67: action = new QAction(mainText, this); Chris@67: } else { Chris@67: action = new QAction(icon, mainText, this); Chris@67: } Chris@67: Chris@66: action->setShortcut(shortcutText); Chris@66: action->setStatusTip(tipText); Chris@66: if (menuType == 0) { Chris@66: connect(action, SIGNAL(triggered()), Chris@66: this, SLOT(addPane())); Chris@66: connect(this, SIGNAL(canAddPane(bool)), Chris@66: action, SLOT(setEnabled(bool))); Chris@66: m_paneActions[action] = PaneConfiguration(type); Chris@66: } else { Chris@66: connect(action, SIGNAL(triggered()), Chris@66: this, SLOT(addLayer())); Chris@66: connect(this, SIGNAL(canAddLayer(bool)), Chris@66: action, SLOT(setEnabled(bool))); Chris@66: m_layerActions[action] = type; Chris@66: } Chris@162: if (shortcutText != "") { Chris@162: m_keyReference->registerShortcut(action); Chris@162: } Chris@66: menu->addAction(action); Chris@66: Chris@66: } else { Chris@66: Chris@66: if (!submenu) { Chris@66: submenu = menu->addMenu(mainText); Chris@97: submenu->setTearOffEnabled(true); Chris@67: } else if (isDefault) { Chris@67: submenu->addSeparator(); Chris@66: } Chris@66: Chris@66: QString actionText; Chris@66: if (c == 0) { Chris@66: if (mono) { Chris@66: actionText = tr("&All Channels Mixed"); Chris@66: } else { Chris@66: actionText = tr("&All Channels"); Chris@66: } Chris@66: } else { Chris@66: actionText = tr("Channel &%1").arg(c); Chris@66: } Chris@66: Chris@66: if (model) { Chris@66: actionText = tr("%1: %2") Chris@66: .arg(model->objectName()) Chris@66: .arg(actionText); Chris@66: } Chris@67: Chris@67: if (isDefault) { Chris@67: action = new QAction(icon, actionText, this); Chris@67: if (!model || model == getMainModel()) { Chris@162: action->setShortcut(shortcutText); Chris@67: } Chris@67: } else { Chris@67: action = new QAction(actionText, this); Chris@67: } Chris@67: Chris@66: action->setStatusTip(tipText); Chris@66: Chris@66: if (menuType == 0) { Chris@66: connect(action, SIGNAL(triggered()), Chris@66: this, SLOT(addPane())); Chris@66: connect(this, SIGNAL(canAddPane(bool)), Chris@66: action, SLOT(setEnabled(bool))); Chris@66: m_paneActions[action] = Chris@66: PaneConfiguration(type, model, c - 1); Chris@66: } else { Chris@66: connect(action, SIGNAL(triggered()), Chris@66: this, SLOT(addLayer())); Chris@66: connect(this, SIGNAL(canAddLayer(bool)), Chris@66: action, SLOT(setEnabled(bool))); Chris@66: m_layerActions[action] = type; Chris@66: } Chris@66: Chris@66: submenu->addAction(action); Chris@66: } Chris@66: } Chris@66: } Chris@66: } Chris@66: } Chris@66: Chris@66: menu = m_paneMenu; Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@168: action = new QAction(il.load("editdelete"), tr("&Delete Pane"), this); Chris@155: action->setShortcut(tr("Ctrl+Shift+D")); Chris@90: action->setStatusTip(tr("Delete the currently active pane")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane())); Chris@66: connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: Chris@66: menu = m_layerMenu; Chris@66: Chris@168: action = new QAction(il.load("timeruler"), tr("Add &Time Ruler"), this); Chris@66: action->setStatusTip(tr("Add a new layer showing a time ruler")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@66: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@66: m_layerActions[action] = LayerFactory::TimeRuler; Chris@66: menu->addAction(action); Chris@66: Chris@66: menu->addSeparator(); Chris@66: Chris@66: m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer")); Chris@97: m_existingLayersMenu->setTearOffEnabled(true); Chris@66: m_rightButtonLayerMenu->addMenu(m_existingLayersMenu); Chris@95: Chris@95: m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer")); Chris@97: m_sliceMenu->setTearOffEnabled(true); Chris@95: m_rightButtonLayerMenu->addMenu(m_sliceMenu); Chris@95: Chris@95: setupExistingLayersMenus(); Chris@66: Chris@66: m_rightButtonLayerMenu->addSeparator(); Chris@66: menu->addSeparator(); Chris@66: Chris@163: QAction *raction = new QAction(tr("&Rename Layer..."), this); Chris@163: raction->setShortcut(tr("R")); Chris@163: raction->setStatusTip(tr("Rename the currently active layer")); Chris@163: connect(raction, SIGNAL(triggered()), this, SLOT(renameCurrentLayer())); Chris@163: connect(this, SIGNAL(canRenameLayer(bool)), raction, SLOT(setEnabled(bool))); Chris@163: menu->addAction(raction); Chris@163: m_rightButtonLayerMenu->addAction(raction); Chris@66: Chris@168: action = new QAction(il.load("editdelete"), tr("&Delete Layer"), this); Chris@155: action->setShortcut(tr("Ctrl+D")); Chris@66: action->setStatusTip(tr("Delete the currently active layer")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer())); Chris@66: connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool))); Chris@162: m_keyReference->registerShortcut(action); Chris@66: menu->addAction(action); Chris@66: m_rightButtonLayerMenu->addAction(action); Chris@163: Chris@163: m_keyReference->registerShortcut(raction); // rename after delete, so delete layer goes next to delete pane Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupTransformsMenu() Chris@66: { Chris@34: if (m_transformsMenu) { Chris@34: m_transformActions.clear(); Chris@34: m_transformActionsReverse.clear(); Chris@34: m_transformsMenu->clear(); Chris@34: } else { Chris@97: m_transformsMenu = menuBar()->addMenu(tr("&Transform")); Chris@97: m_transformsMenu->setTearOffEnabled(true); Chris@97: } Chris@34: Chris@0: TransformFactory::TransformList transforms = Chris@0: TransformFactory::getInstance()->getAllTransforms(); Chris@0: Chris@33: vector types = Chris@0: TransformFactory::getInstance()->getAllTransformTypes(); Chris@0: Chris@37: map > categoryMenus; Chris@37: map > makerMenus; Chris@33: Chris@36: map byPluginNameMenus; Chris@33: map > pluginNameMenus; Chris@33: Chris@37: set pendingMenus; Chris@37: Chris@34: m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms")); Chris@97: m_recentTransformsMenu->setTearOffEnabled(true); Chris@34: m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu); Chris@34: connect(&m_recentTransforms, SIGNAL(recentChanged()), Chris@34: this, SLOT(setupRecentTransformsMenu())); Chris@34: Chris@34: m_transformsMenu->addSeparator(); Chris@34: m_rightButtonTransformsMenu->addSeparator(); Chris@34: Chris@33: for (vector::iterator i = types.begin(); i != types.end(); ++i) { Chris@33: Chris@33: if (i != types.begin()) { Chris@34: m_transformsMenu->addSeparator(); Chris@34: m_rightButtonTransformsMenu->addSeparator(); Chris@33: } Chris@33: Chris@33: QString byCategoryLabel = tr("%1 by Category").arg(*i); Chris@37: SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel, Chris@37: 20, 40); Chris@97: byCategoryMenu->setTearOffEnabled(true); Chris@37: m_transformsMenu->addMenu(byCategoryMenu); Chris@34: m_rightButtonTransformsMenu->addMenu(byCategoryMenu); Chris@37: pendingMenus.insert(byCategoryMenu); Chris@33: Chris@33: vector categories = Chris@33: TransformFactory::getInstance()->getTransformCategories(*i); Chris@33: Chris@33: for (vector::iterator j = categories.begin(); Chris@33: j != categories.end(); ++j) { Chris@33: Chris@33: QString category = *j; Chris@33: if (category == "") category = tr("Unclassified"); Chris@33: Chris@33: if (categories.size() < 2) { Chris@33: categoryMenus[*i][category] = byCategoryMenu; Chris@33: continue; Chris@33: } Chris@33: Chris@33: QStringList components = category.split(" > "); Chris@33: QString key; Chris@33: Chris@33: for (QStringList::iterator k = components.begin(); Chris@33: k != components.end(); ++k) { Chris@33: Chris@33: QString parentKey = key; Chris@33: if (key != "") key += " > "; Chris@33: key += *k; Chris@33: Chris@33: if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) { Chris@37: SubdividingMenu *m = new SubdividingMenu(*k, 20, 40); Chris@97: m->setTearOffEnabled(true); Chris@37: pendingMenus.insert(m); Chris@37: categoryMenus[*i][key] = m; Chris@33: if (parentKey == "") { Chris@37: byCategoryMenu->addMenu(m); Chris@33: } else { Chris@37: categoryMenus[*i][parentKey]->addMenu(m); Chris@33: } Chris@33: } Chris@33: } Chris@33: } Chris@33: Chris@34: QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); Chris@36: byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel); Chris@97: byPluginNameMenus[*i]->setTearOffEnabled(true); Chris@36: m_transformsMenu->addMenu(byPluginNameMenus[*i]); Chris@34: m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]); Chris@37: pendingMenus.insert(byPluginNameMenus[*i]); Chris@34: Chris@33: QString byMakerLabel = tr("%1 by Maker").arg(*i); Chris@37: SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40); Chris@97: byMakerMenu->setTearOffEnabled(true); Chris@37: m_transformsMenu->addMenu(byMakerMenu); Chris@34: m_rightButtonTransformsMenu->addMenu(byMakerMenu); Chris@37: pendingMenus.insert(byMakerMenu); Chris@33: Chris@33: vector makers = Chris@33: TransformFactory::getInstance()->getTransformMakers(*i); Chris@37: Chris@33: for (vector::iterator j = makers.begin(); Chris@33: j != makers.end(); ++j) { Chris@33: Chris@33: QString maker = *j; Chris@33: if (maker == "") maker = tr("Unknown"); Chris@55: maker.replace(QRegExp(tr(" [\\(<].*$")), ""); Chris@55: Chris@37: makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40); Chris@97: makerMenus[*i][maker]->setTearOffEnabled(true); Chris@37: byMakerMenu->addMenu(makerMenus[*i][maker]); Chris@37: pendingMenus.insert(makerMenus[*i][maker]); Chris@33: } Chris@0: } Chris@0: Chris@0: for (unsigned int i = 0; i < transforms.size(); ++i) { Chris@0: Chris@107: QString name = transforms[i].name; Chris@107: if (name == "") name = transforms[i].identifier; Chris@107: Chris@107: // std::cerr << "Plugin Name: " << name.toStdString() << std::endl; Chris@80: Chris@33: QString type = transforms[i].type; Chris@33: Chris@33: QString category = transforms[i].category; Chris@33: if (category == "") category = tr("Unclassified"); Chris@33: Chris@33: QString maker = transforms[i].maker; Chris@33: if (maker == "") maker = tr("Unknown"); Chris@55: maker.replace(QRegExp(tr(" [\\(<].*$")), ""); Chris@33: Chris@107: QString pluginName = name.section(": ", 0, 0); Chris@107: QString output = name.section(": ", 1); Chris@107: Chris@107: QAction *action = new QAction(tr("%1...").arg(name), this); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@107: m_transformActions[action] = transforms[i].identifier; Chris@107: m_transformActionsReverse[transforms[i].identifier] = action; Chris@0: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@33: Chris@108: action->setStatusTip(transforms[i].description); Chris@90: Chris@33: if (categoryMenus[type].find(category) == categoryMenus[type].end()) { Chris@33: std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " Chris@33: << "No category menu for transform \"" Chris@107: << name.toStdString() << "\" (category = \"" Chris@33: << category.toStdString() << "\")" << std::endl; Chris@33: } else { Chris@33: categoryMenus[type][category]->addAction(action); Chris@33: } Chris@33: Chris@33: if (makerMenus[type].find(maker) == makerMenus[type].end()) { Chris@33: std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " Chris@33: << "No maker menu for transform \"" Chris@107: << name.toStdString() << "\" (maker = \"" Chris@33: << maker.toStdString() << "\")" << std::endl; Chris@33: } else { Chris@80: makerMenus[type][maker]->addAction(action); Chris@33: } Chris@33: Chris@33: action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this); Chris@33: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@107: m_transformActions[action] = transforms[i].identifier; Chris@33: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@108: action->setStatusTip(transforms[i].description); Chris@33: Chris@34: // cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl; Chris@34: Chris@33: if (pluginNameMenus[type].find(pluginName) == Chris@33: pluginNameMenus[type].end()) { Chris@33: Chris@36: SubdividingMenu *parentMenu = byPluginNameMenus[type]; Chris@97: parentMenu->setTearOffEnabled(true); Chris@34: Chris@33: if (output == "") { Chris@36: parentMenu->addAction(pluginName, action); Chris@33: } else { Chris@34: pluginNameMenus[type][pluginName] = Chris@34: parentMenu->addMenu(pluginName); Chris@33: connect(this, SIGNAL(canAddLayer(bool)), Chris@33: pluginNameMenus[type][pluginName], Chris@33: SLOT(setEnabled(bool))); Chris@33: } Chris@33: } Chris@33: Chris@33: if (pluginNameMenus[type].find(pluginName) != Chris@33: pluginNameMenus[type].end()) { Chris@33: pluginNameMenus[type][pluginName]->addAction(action); Chris@33: } Chris@0: } Chris@0: Chris@37: for (set::iterator i = pendingMenus.begin(); Chris@37: i != pendingMenus.end(); ++i) { Chris@37: (*i)->entriesAdded(); Chris@37: } Chris@37: Chris@34: setupRecentTransformsMenu(); Chris@66: } Chris@66: Chris@66: void Chris@66: MainWindow::setupHelpMenu() Chris@66: { Chris@66: QMenu *menu = menuBar()->addMenu(tr("&Help")); Chris@97: menu->setTearOffEnabled(true); Chris@66: Chris@162: m_keyReference->setCategory(tr("Help")); Chris@162: Chris@168: IconLoader il; Chris@168: Chris@168: QAction *action = new QAction(il.load("help"), Chris@138: tr("&Help Reference"), this); Chris@162: action->setShortcut(tr("F1")); Chris@66: action->setStatusTip(tr("Open the Sonic Visualiser reference manual")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(help())); Chris@162: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@162: Chris@163: action = new QAction(tr("&Key and Mouse Reference"), this); Chris@162: action->setShortcut(tr("F2")); Chris@162: action->setStatusTip(tr("Open a window showing the keystrokes you can use in Sonic Visualiser")); Chris@162: connect(action, SIGNAL(triggered()), this, SLOT(keyReference())); Chris@162: m_keyReference->registerShortcut(action); Chris@162: menu->addAction(action); Chris@66: Chris@164: action = new QAction(tr("Sonic Visualiser on the &Web"), this); Chris@164: action->setStatusTip(tr("Open the Sonic Visualiser website")); Chris@164: connect(action, SIGNAL(triggered()), this, SLOT(website())); Chris@164: menu->addAction(action); Chris@164: Chris@66: action = new QAction(tr("&About Sonic Visualiser"), this); Chris@66: action->setStatusTip(tr("Show information about Sonic Visualiser")); Chris@66: connect(action, SIGNAL(triggered()), this, SLOT(about())); Chris@0: menu->addAction(action); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupRecentFilesMenu() Chris@0: { Chris@0: m_recentFilesMenu->clear(); Chris@34: vector files = m_recentFiles.getRecent(); Chris@0: for (size_t i = 0; i < files.size(); ++i) { Chris@0: QAction *action = new QAction(files[i], this); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); Chris@162: if (i == 0) { Chris@162: action->setShortcut(tr("Ctrl+R")); Chris@162: m_keyReference->registerShortcut Chris@163: (tr("Re-open"), Chris@163: action->shortcut(), Chris@163: tr("Re-open the current or most recently opened file")); Chris@162: } Chris@0: m_recentFilesMenu->addAction(action); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@34: MainWindow::setupRecentTransformsMenu() Chris@34: { Chris@34: m_recentTransformsMenu->clear(); Chris@34: vector transforms = m_recentTransforms.getRecent(); Chris@34: for (size_t i = 0; i < transforms.size(); ++i) { Chris@34: TransformActionReverseMap::iterator ti = Chris@34: m_transformActionsReverse.find(transforms[i]); Chris@34: if (ti == m_transformActionsReverse.end()) { Chris@34: std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: " Chris@34: << "Unknown transform \"" << transforms[i].toStdString() Chris@34: << "\" in recent transforms list" << std::endl; Chris@34: continue; Chris@34: } Chris@162: if (i == 0) { Chris@162: ti->second->setShortcut(tr("Ctrl+T")); Chris@162: m_keyReference->registerShortcut Chris@163: (tr("Repeat Transform"), Chris@162: ti->second->shortcut(), Chris@163: tr("Re-select the most recently run transform")); Chris@162: } Chris@34: m_recentTransformsMenu->addAction(ti->second); Chris@34: } Chris@34: } Chris@34: Chris@34: void Chris@95: MainWindow::setupExistingLayersMenus() Chris@0: { Chris@0: if (!m_existingLayersMenu) return; // should have been created by setupMenus Chris@0: Chris@0: // std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl; Chris@0: Chris@0: m_existingLayersMenu->clear(); Chris@0: m_existingLayerActions.clear(); Chris@0: Chris@95: m_sliceMenu->clear(); Chris@95: m_sliceActions.clear(); Chris@95: Chris@168: IconLoader il; Chris@168: Chris@33: vector orderedLayers; Chris@33: set observedLayers; Chris@95: set sliceableLayers; Chris@95: Chris@95: LayerFactory *factory = LayerFactory::getInstance(); Chris@0: Chris@0: for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Chris@0: Chris@0: Pane *pane = m_paneStack->getPane(i); Chris@0: if (!pane) continue; Chris@0: Chris@0: for (int j = 0; j < pane->getLayerCount(); ++j) { Chris@0: Chris@0: Layer *layer = pane->getLayer(j); Chris@0: if (!layer) continue; Chris@0: if (observedLayers.find(layer) != observedLayers.end()) { Chris@137: // std::cerr << "found duplicate layer " << layer << std::endl; Chris@0: continue; Chris@0: } Chris@0: Chris@0: // std::cerr << "found new layer " << layer << " (name = " Chris@0: // << layer->getLayerPresentationName().toStdString() << ")" << std::endl; Chris@0: Chris@0: orderedLayers.push_back(layer); Chris@0: observedLayers.insert(layer); Chris@95: Chris@95: if (factory->isLayerSliceable(layer)) { Chris@95: sliceableLayers.insert(layer); Chris@95: } Chris@0: } Chris@0: } Chris@0: Chris@33: map observedNames; Chris@0: Chris@137: for (size_t i = 0; i < orderedLayers.size(); ++i) { Chris@0: Chris@95: Layer *layer = orderedLayers[i]; Chris@95: Chris@95: QString name = layer->getLayerPresentationName(); Chris@0: int n = ++observedNames[name]; Chris@0: if (n > 1) name = QString("%1 <%2>").arg(name).arg(n); Chris@0: Chris@168: QIcon icon = il.load(factory->getLayerIconName Chris@168: (factory->getLayerType(layer))); Chris@95: Chris@95: QAction *action = new QAction(icon, name, this); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@0: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@95: m_existingLayerActions[action] = layer; Chris@0: Chris@0: m_existingLayersMenu->addAction(action); Chris@95: Chris@95: if (sliceableLayers.find(layer) != sliceableLayers.end()) { Chris@95: action = new QAction(icon, name, this); Chris@95: connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); Chris@95: connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); Chris@95: m_sliceActions[action] = layer; Chris@95: m_sliceMenu->addAction(action); Chris@95: } Chris@0: } Chris@95: Chris@95: m_sliceMenu->setEnabled(!m_sliceActions.empty()); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupToolbars() Chris@0: { Chris@162: m_keyReference->setCategory(tr("Playback and Transport Controls")); Chris@162: Chris@168: IconLoader il; Chris@168: Chris@155: QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back")); Chris@155: menu->setTearOffEnabled(true); Chris@155: m_rightButtonMenu->addSeparator(); Chris@155: m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback")); Chris@155: Chris@155: QToolBar *toolbar = addToolBar(tr("Playback Toolbar")); Chris@155: Chris@168: QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"), Chris@155: tr("Rewind to Start")); Chris@155: rwdStartAction->setShortcut(tr("Home")); Chris@155: rwdStartAction->setStatusTip(tr("Rewind to the start")); Chris@155: connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart())); Chris@155: connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool))); Chris@155: Chris@168: QAction *m_rwdAction = toolbar->addAction(il.load("rewind"), Chris@155: tr("Rewind")); Chris@155: m_rwdAction->setShortcut(tr("PgUp")); Chris@163: m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch")); Chris@155: connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind())); Chris@155: connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool))); Chris@155: Chris@168: QAction *playAction = toolbar->addAction(il.load("playpause"), Chris@155: tr("Play / Pause")); Chris@155: playAction->setCheckable(true); Chris@155: playAction->setShortcut(tr("Space")); Chris@155: playAction->setStatusTip(tr("Start or stop playback from the current position")); Chris@155: connect(playAction, SIGNAL(triggered()), this, SLOT(play())); Chris@0: connect(m_playSource, SIGNAL(playStatusChanged(bool)), Chris@155: playAction, SLOT(setChecked(bool))); Chris@155: connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool))); Chris@155: Chris@168: m_ffwdAction = toolbar->addAction(il.load("ffwd"), Chris@155: tr("Fast Forward")); Chris@155: m_ffwdAction->setShortcut(tr("PgDown")); Chris@163: m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch")); Chris@155: connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd())); Chris@155: connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool))); Chris@155: Chris@168: QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"), Chris@155: tr("Fast Forward to End")); Chris@155: ffwdEndAction->setShortcut(tr("End")); Chris@155: ffwdEndAction->setStatusTip(tr("Fast-forward to the end")); Chris@155: connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd())); Chris@155: connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: toolbar = addToolBar(tr("Play Mode Toolbar")); Chris@0: Chris@168: QAction *psAction = toolbar->addAction(il.load("playselection"), Chris@155: tr("Constrain Playback to Selection")); Chris@155: psAction->setCheckable(true); Chris@155: psAction->setChecked(m_viewManager->getPlaySelectionMode()); Chris@155: psAction->setShortcut(tr("s")); Chris@163: psAction->setStatusTip(tr("Constrain playback to the selected regions")); Chris@69: connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), Chris@155: psAction, SLOT(setChecked(bool))); Chris@155: connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); Chris@155: connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool))); Chris@155: Chris@168: QAction *plAction = toolbar->addAction(il.load("playloop"), Chris@155: tr("Loop Playback")); Chris@155: plAction->setCheckable(true); Chris@155: plAction->setChecked(m_viewManager->getPlayLoopMode()); Chris@155: plAction->setShortcut(tr("l")); Chris@155: plAction->setStatusTip(tr("Loop playback")); Chris@69: connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), Chris@155: plAction, SLOT(setChecked(bool))); Chris@155: connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled())); Chris@155: connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool))); Chris@155: Chris@180: QAction *soAction = toolbar->addAction(il.load("solo"), Chris@180: tr("Solo Current Pane")); Chris@180: soAction->setCheckable(true); Chris@180: soAction->setChecked(m_viewManager->getPlaySoloMode()); Chris@180: soAction->setShortcut(tr("o")); Chris@180: soAction->setStatusTip(tr("Solo the current pane during playback")); Chris@180: connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)), Chris@180: soAction, SLOT(setChecked(bool))); Chris@180: connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled())); Chris@180: connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool))); Chris@180: Chris@162: m_keyReference->registerShortcut(playAction); Chris@162: m_keyReference->registerShortcut(psAction); Chris@162: m_keyReference->registerShortcut(plAction); Chris@180: m_keyReference->registerShortcut(soAction); Chris@162: m_keyReference->registerShortcut(m_rwdAction); Chris@162: m_keyReference->registerShortcut(m_ffwdAction); Chris@162: m_keyReference->registerShortcut(rwdStartAction); Chris@162: m_keyReference->registerShortcut(ffwdEndAction); Chris@162: Chris@155: menu->addAction(playAction); Chris@155: menu->addAction(psAction); Chris@155: menu->addAction(plAction); Chris@180: menu->addAction(soAction); Chris@155: menu->addSeparator(); Chris@155: menu->addAction(m_rwdAction); Chris@155: menu->addAction(m_ffwdAction); Chris@155: menu->addSeparator(); Chris@155: menu->addAction(rwdStartAction); Chris@155: menu->addAction(ffwdEndAction); Chris@155: menu->addSeparator(); Chris@155: Chris@155: m_rightButtonPlaybackMenu->addAction(playAction); Chris@155: m_rightButtonPlaybackMenu->addAction(psAction); Chris@155: m_rightButtonPlaybackMenu->addAction(plAction); Chris@180: m_rightButtonPlaybackMenu->addAction(soAction); Chris@155: m_rightButtonPlaybackMenu->addSeparator(); Chris@155: m_rightButtonPlaybackMenu->addAction(m_rwdAction); Chris@155: m_rightButtonPlaybackMenu->addAction(m_ffwdAction); Chris@155: m_rightButtonPlaybackMenu->addSeparator(); Chris@155: m_rightButtonPlaybackMenu->addAction(rwdStartAction); Chris@155: m_rightButtonPlaybackMenu->addAction(ffwdEndAction); Chris@155: m_rightButtonPlaybackMenu->addSeparator(); Chris@155: Chris@155: QAction *fastAction = menu->addAction(tr("Speed Up")); Chris@155: fastAction->setShortcut(tr("Ctrl+PgUp")); Chris@163: fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch")); Chris@155: connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback())); Chris@155: connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool))); Chris@155: Chris@155: QAction *slowAction = menu->addAction(tr("Slow Down")); Chris@155: slowAction->setShortcut(tr("Ctrl+PgDown")); Chris@163: slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch")); Chris@155: connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback())); Chris@155: connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool))); Chris@155: Chris@155: QAction *normalAction = menu->addAction(tr("Restore Normal Speed")); Chris@155: normalAction->setShortcut(tr("Ctrl+Home")); Chris@163: normalAction->setStatusTip(tr("Restore non-time-stretched playback")); Chris@155: connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback())); Chris@155: connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool))); Chris@155: Chris@162: m_keyReference->registerShortcut(fastAction); Chris@162: m_keyReference->registerShortcut(slowAction); Chris@162: m_keyReference->registerShortcut(normalAction); Chris@162: Chris@155: m_rightButtonPlaybackMenu->addAction(fastAction); Chris@155: m_rightButtonPlaybackMenu->addAction(slowAction); Chris@155: m_rightButtonPlaybackMenu->addAction(normalAction); Chris@0: Chris@0: toolbar = addToolBar(tr("Edit Toolbar")); Chris@0: CommandHistory::getInstance()->registerToolbar(toolbar); Chris@0: Chris@162: m_keyReference->setCategory(tr("Tool Selection")); Chris@162: Chris@0: toolbar = addToolBar(tr("Tools Toolbar")); Chris@0: QActionGroup *group = new QActionGroup(this); Chris@0: Chris@168: QAction *action = toolbar->addAction(il.load("navigate"), Chris@155: tr("Navigate")); Chris@0: action->setCheckable(true); Chris@0: action->setChecked(true); Chris@0: action->setShortcut(tr("1")); Chris@90: action->setStatusTip(tr("Navigate")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); Chris@0: group->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@0: m_toolActions[ViewManager::NavigateMode] = action; Chris@0: Chris@168: action = toolbar->addAction(il.load("select"), Chris@0: tr("Select")); Chris@0: action->setCheckable(true); Chris@0: action->setShortcut(tr("2")); Chris@90: action->setStatusTip(tr("Select ranges")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected())); Chris@0: group->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@0: m_toolActions[ViewManager::SelectMode] = action; Chris@0: Chris@168: action = toolbar->addAction(il.load("move"), Chris@0: tr("Edit")); Chris@0: action->setCheckable(true); Chris@0: action->setShortcut(tr("3")); Chris@90: action->setStatusTip(tr("Edit items in layer")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); Chris@0: connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); Chris@0: group->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@0: m_toolActions[ViewManager::EditMode] = action; Chris@0: Chris@168: action = toolbar->addAction(il.load("draw"), Chris@0: tr("Draw")); Chris@0: action->setCheckable(true); Chris@0: action->setShortcut(tr("4")); Chris@90: action->setStatusTip(tr("Draw new items in layer")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected())); Chris@0: connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); Chris@0: group->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@0: m_toolActions[ViewManager::DrawMode] = action; Chris@0: Chris@168: action = toolbar->addAction(il.load("measure"), Chris@151: tr("Measure")); Chris@151: action->setCheckable(true); Chris@151: action->setShortcut(tr("5")); Chris@151: action->setStatusTip(tr("Make measurements in layer")); Chris@151: connect(action, SIGNAL(triggered()), this, SLOT(toolMeasureSelected())); Chris@169: connect(this, SIGNAL(canMeasureLayer(bool)), action, SLOT(setEnabled(bool))); Chris@151: group->addAction(action); Chris@162: m_keyReference->registerShortcut(action); Chris@151: m_toolActions[ViewManager::MeasureMode] = action; Chris@151: Chris@168: // action = toolbar->addAction(il.load("text"), Chris@0: // tr("Text")); Chris@0: // action->setCheckable(true); Chris@0: // action->setShortcut(tr("5")); Chris@0: // connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected())); Chris@0: // group->addAction(action); Chris@0: // m_toolActions[ViewManager::TextMode] = action; Chris@0: Chris@0: toolNavigateSelected(); Chris@163: Chris@163: Pane::registerShortcuts(*m_keyReference); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::updateMenuStates() Chris@0: { Chris@117: Pane *currentPane = 0; Chris@117: Layer *currentLayer = 0; Chris@117: Chris@117: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@117: if (currentPane) currentLayer = currentPane->getSelectedLayer(); Chris@117: Chris@0: bool haveCurrentPane = Chris@117: (currentPane != 0); Chris@0: bool haveCurrentLayer = Chris@117: (haveCurrentPane && Chris@117: (currentLayer != 0)); Chris@0: bool haveMainModel = Chris@0: (getMainModel() != 0); Chris@0: bool havePlayTarget = Chris@0: (m_playTarget != 0); Chris@0: bool haveSelection = Chris@0: (m_viewManager && Chris@0: !m_viewManager->getSelections().empty()); Chris@0: bool haveCurrentEditableLayer = Chris@0: (haveCurrentLayer && Chris@117: currentLayer->isLayerEditable()); Chris@0: bool haveCurrentTimeInstantsLayer = Chris@0: (haveCurrentLayer && Chris@117: dynamic_cast(currentLayer)); Chris@0: bool haveCurrentTimeValueLayer = Chris@0: (haveCurrentLayer && Chris@117: dynamic_cast(currentLayer)); Chris@0: bool haveCurrentColour3DPlot = Chris@0: (haveCurrentLayer && Chris@117: dynamic_cast(currentLayer)); Chris@0: bool haveClipboardContents = Chris@0: (m_viewManager && Chris@0: !m_viewManager->getClipboard().empty()); Chris@0: Chris@0: emit canAddPane(haveMainModel); Chris@0: emit canDeleteCurrentPane(haveCurrentPane); Chris@0: emit canZoom(haveMainModel && haveCurrentPane); Chris@0: emit canScroll(haveMainModel && haveCurrentPane); Chris@0: emit canAddLayer(haveMainModel && haveCurrentPane); Chris@0: emit canImportMoreAudio(haveMainModel); Chris@0: emit canImportLayer(haveMainModel && haveCurrentPane); Chris@0: emit canExportAudio(haveMainModel); Chris@0: emit canExportLayer(haveMainModel && Chris@0: (haveCurrentEditableLayer || haveCurrentColour3DPlot)); Chris@121: emit canExportImage(haveMainModel && haveCurrentPane); Chris@0: emit canDeleteCurrentLayer(haveCurrentLayer); Chris@0: emit canRenameLayer(haveCurrentLayer); Chris@0: emit canEditLayer(haveCurrentEditableLayer); Chris@169: emit canMeasureLayer(haveCurrentLayer); Chris@0: emit canSelect(haveMainModel && haveCurrentPane); Chris@155: emit canPlay(havePlayTarget); Chris@155: emit canFfwd(true); Chris@155: emit canRewind(true); Chris@0: emit canPaste(haveCurrentEditableLayer && haveClipboardContents); Chris@0: emit canInsertInstant(haveCurrentPane); Chris@81: emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); Chris@189: emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); Chris@0: emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); Chris@0: emit canClearSelection(haveSelection); Chris@0: emit canEditSelection(haveSelection && haveCurrentEditableLayer); Chris@0: emit canSave(m_sessionFile != "" && m_documentModified); Chris@155: Chris@164: if (m_viewManager && Chris@164: (m_viewManager->getToolMode() == ViewManager::MeasureMode)) { Chris@164: emit canDeleteSelection(haveCurrentLayer); Chris@164: m_deleteSelectedAction->setText(tr("&Delete Current Measurement")); Chris@164: m_deleteSelectedAction->setStatusTip(tr("Delete the measurement currently under the mouse pointer")); Chris@164: } else { Chris@164: emit canDeleteSelection(haveSelection && haveCurrentEditableLayer); Chris@164: m_deleteSelectedAction->setText(tr("&Delete Selected Items")); Chris@164: m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer")); Chris@164: } Chris@164: Chris@155: emit canChangePlaybackSpeed(true); Chris@155: int v = m_playSpeed->value(); Chris@155: emit canSpeedUpPlayback(v < m_playSpeed->maximum()); Chris@155: emit canSlowDownPlayback(v > m_playSpeed->minimum()); Chris@155: Chris@155: if (m_ffwdAction && m_rwdAction) { Chris@155: if (haveCurrentTimeInstantsLayer) { Chris@155: m_ffwdAction->setText(tr("Fast Forward to Next Instant")); Chris@155: m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer")); Chris@155: m_rwdAction->setText(tr("Rewind to Previous Instant")); Chris@155: m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer")); Chris@155: } else if (haveCurrentTimeValueLayer) { Chris@155: m_ffwdAction->setText(tr("Fast Forward to Next Point")); Chris@155: m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer")); Chris@155: m_rwdAction->setText(tr("Rewind to Previous Point")); Chris@155: m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer")); Chris@155: } else { Chris@155: m_ffwdAction->setText(tr("Fast Forward")); Chris@155: m_ffwdAction->setStatusTip(tr("Fast forward")); Chris@155: m_rwdAction->setText(tr("Rewind")); Chris@155: m_rwdAction->setStatusTip(tr("Rewind")); Chris@155: } Chris@155: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::updateDescriptionLabel() Chris@0: { Chris@0: if (!getMainModel()) { Chris@0: m_descriptionLabel->setText(tr("No audio file loaded.")); Chris@0: return; Chris@0: } Chris@0: Chris@0: QString description; Chris@0: Chris@0: size_t ssr = getMainModel()->getSampleRate(); Chris@0: size_t tsr = ssr; Chris@0: if (m_playSource) tsr = m_playSource->getTargetSampleRate(); Chris@0: Chris@0: if (ssr != tsr) { Chris@0: description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr); Chris@0: } else { Chris@0: description = QString("%1Hz").arg(ssr); Chris@0: } Chris@0: Chris@0: description = QString("%1 - %2") Chris@0: .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr) Chris@0: .toText(false).c_str()) Chris@0: .arg(description); Chris@0: Chris@0: m_descriptionLabel->setText(description); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::documentModified() Chris@0: { Chris@0: // std::cerr << "MainWindow::documentModified" << std::endl; Chris@0: Chris@0: if (!m_documentModified) { Chris@0: setWindowTitle(tr("%1 (modified)").arg(windowTitle())); Chris@0: } Chris@0: Chris@0: m_documentModified = true; Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::documentRestored() Chris@0: { Chris@0: // std::cerr << "MainWindow::documentRestored" << std::endl; Chris@0: Chris@0: if (m_documentModified) { Chris@0: QString wt(windowTitle()); Chris@0: wt.replace(tr(" (modified)"), ""); Chris@0: setWindowTitle(wt); Chris@0: } Chris@0: Chris@0: m_documentModified = false; Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::playLoopToggled() Chris@0: { Chris@0: QAction *action = dynamic_cast(sender()); Chris@0: Chris@0: if (action) { Chris@0: m_viewManager->setPlayLoopMode(action->isChecked()); Chris@0: } else { Chris@0: m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::playSelectionToggled() Chris@0: { Chris@0: QAction *action = dynamic_cast(sender()); Chris@0: Chris@0: if (action) { Chris@0: m_viewManager->setPlaySelectionMode(action->isChecked()); Chris@0: } else { Chris@0: m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@180: MainWindow::playSoloToggled() Chris@180: { Chris@180: QAction *action = dynamic_cast(sender()); Chris@180: Chris@180: if (action) { Chris@180: m_viewManager->setPlaySoloMode(action->isChecked()); Chris@180: } else { Chris@180: m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); Chris@180: } Chris@180: Chris@184: if (m_viewManager->getPlaySoloMode()) { Chris@184: currentPaneChanged(m_paneStack->getCurrentPane()); Chris@184: } else { Chris@180: m_viewManager->setPlaybackModel(0); Chris@180: if (m_playSource) { Chris@180: m_playSource->clearSoloModelSet(); Chris@180: } Chris@180: } Chris@180: } Chris@180: Chris@180: void Chris@116: MainWindow::currentPaneChanged(Pane *p) Chris@0: { Chris@0: updateMenuStates(); Chris@116: updateVisibleRangeDisplay(p); Chris@180: Chris@180: if (!p) return; Chris@180: Chris@180: if (!(m_viewManager && Chris@180: m_playSource && Chris@180: m_viewManager->getPlaySoloMode())) { Chris@180: if (m_viewManager) m_viewManager->setPlaybackModel(0); Chris@180: return; Chris@180: } Chris@180: Chris@180: Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); Chris@180: Chris@180: std::set soloModels; Chris@180: Chris@180: for (int i = 0; i < p->getLayerCount(); ++i) { Chris@180: Layer *layer = p->getLayer(i); Chris@180: if (dynamic_cast(layer)) { Chris@180: continue; Chris@180: } Chris@180: if (layer && layer->getModel()) { Chris@180: Model *model = layer->getModel(); Chris@180: if (dynamic_cast(model)) { Chris@180: m_viewManager->setPlaybackModel(model); Chris@180: } Chris@180: soloModels.insert(model); Chris@180: } Chris@180: } Chris@180: Chris@180: RangeSummarisableTimeValueModel *a = Chris@180: dynamic_cast(prevPlaybackModel); Chris@180: RangeSummarisableTimeValueModel *b = Chris@180: dynamic_cast(m_viewManager-> Chris@180: getPlaybackModel()); Chris@180: Chris@180: m_playSource->setSoloModelSet(soloModels); Chris@180: Chris@180: if (a && b && (a != b)) { Chris@180: int frame = m_playSource->getCurrentPlayingFrame(); Chris@180: //!!! I don't really believe that these functions are the right way around Chris@180: int rframe = a->alignFromReference(frame); Chris@180: int bframe = b->alignToReference(rframe); Chris@180: if (m_playSource->isPlaying()) m_playSource->play(bframe); Chris@180: } Chris@0: } Chris@0: Chris@0: void Chris@116: MainWindow::currentLayerChanged(Pane *p, Layer *) Chris@0: { Chris@0: updateMenuStates(); Chris@116: updateVisibleRangeDisplay(p); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::toolNavigateSelected() Chris@0: { Chris@0: m_viewManager->setToolMode(ViewManager::NavigateMode); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::toolSelectSelected() Chris@0: { Chris@0: m_viewManager->setToolMode(ViewManager::SelectMode); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::toolEditSelected() Chris@0: { Chris@0: m_viewManager->setToolMode(ViewManager::EditMode); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::toolDrawSelected() Chris@0: { Chris@0: m_viewManager->setToolMode(ViewManager::DrawMode); Chris@0: } Chris@0: Chris@151: void Chris@151: MainWindow::toolMeasureSelected() Chris@151: { Chris@151: m_viewManager->setToolMode(ViewManager::MeasureMode); Chris@151: } Chris@151: Chris@0: //void Chris@0: //MainWindow::toolTextSelected() Chris@0: //{ Chris@0: // m_viewManager->setToolMode(ViewManager::TextMode); Chris@0: //} Chris@0: Chris@0: void Chris@0: MainWindow::selectAll() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), Chris@0: getMainModel()->getEndFrame())); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::selectToStart() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), Chris@0: m_viewManager->getGlobalCentreFrame())); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::selectToEnd() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), Chris@0: getMainModel()->getEndFrame())); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::selectVisible() Chris@0: { Chris@0: Model *model = getMainModel(); Chris@0: if (!model) return; Chris@0: Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (!currentPane) return; Chris@0: Chris@0: size_t startFrame, endFrame; Chris@0: Chris@0: if (currentPane->getStartFrame() < 0) startFrame = 0; Chris@0: else startFrame = currentPane->getStartFrame(); Chris@0: Chris@0: if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); Chris@0: else endFrame = currentPane->getEndFrame(); Chris@0: Chris@0: m_viewManager->setSelection(Selection(startFrame, endFrame)); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::clearSelection() Chris@0: { Chris@0: m_viewManager->clearSelections(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::cut() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (!currentPane) return; Chris@0: Chris@0: Layer *layer = currentPane->getSelectedLayer(); Chris@0: if (!layer) return; Chris@0: Chris@0: Clipboard &clipboard = m_viewManager->getClipboard(); Chris@0: clipboard.clear(); Chris@0: Chris@0: MultiSelection::SelectionList selections = m_viewManager->getSelections(); Chris@0: Chris@0: CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); Chris@0: Chris@0: for (MultiSelection::SelectionList::iterator i = selections.begin(); Chris@0: i != selections.end(); ++i) { Chris@0: layer->copy(*i, clipboard); Chris@0: layer->deleteSelection(*i); Chris@0: } Chris@0: Chris@0: CommandHistory::getInstance()->endCompoundOperation(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::copy() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (!currentPane) return; Chris@0: Chris@0: Layer *layer = currentPane->getSelectedLayer(); Chris@0: if (!layer) return; Chris@0: Chris@0: Clipboard &clipboard = m_viewManager->getClipboard(); Chris@0: clipboard.clear(); Chris@0: Chris@0: MultiSelection::SelectionList selections = m_viewManager->getSelections(); Chris@0: Chris@0: for (MultiSelection::SelectionList::iterator i = selections.begin(); Chris@0: i != selections.end(); ++i) { Chris@0: layer->copy(*i, clipboard); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paste() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (!currentPane) return; Chris@0: Chris@0: //!!! if we have no current layer, we should create one of the most Chris@0: // appropriate type Chris@0: Chris@0: Layer *layer = currentPane->getSelectedLayer(); Chris@0: if (!layer) return; Chris@0: Chris@0: Clipboard &clipboard = m_viewManager->getClipboard(); Chris@0: Clipboard::PointList contents = clipboard.getPoints(); Chris@0: /* Chris@0: long minFrame = 0; Chris@0: bool have = false; Chris@0: for (int i = 0; i < contents.size(); ++i) { Chris@0: if (!contents[i].haveFrame()) continue; Chris@0: if (!have || contents[i].getFrame() < minFrame) { Chris@0: minFrame = contents[i].getFrame(); Chris@0: have = true; Chris@0: } Chris@0: } Chris@0: Chris@0: long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; Chris@0: Chris@0: layer->paste(clipboard, frameOffset); Chris@0: */ Chris@0: layer->paste(clipboard, 0, true); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::deleteSelected() Chris@0: { Chris@0: if (m_paneStack->getCurrentPane() && Chris@0: m_paneStack->getCurrentPane()->getSelectedLayer()) { Chris@164: Chris@164: Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); Chris@164: Chris@164: if (m_viewManager && Chris@164: (m_viewManager->getToolMode() == ViewManager::MeasureMode)) { Chris@164: Chris@164: layer->deleteCurrentMeasureRect(); Chris@164: Chris@164: } else { Chris@164: Chris@164: MultiSelection::SelectionList selections = Chris@164: m_viewManager->getSelections(); Chris@164: Chris@164: for (MultiSelection::SelectionList::iterator i = selections.begin(); Chris@164: i != selections.end(); ++i) { Chris@164: layer->deleteSelection(*i); Chris@164: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::insertInstant() Chris@0: { Chris@0: int frame = m_viewManager->getPlaybackFrame(); Chris@81: insertInstantAt(frame); Chris@81: } Chris@81: Chris@81: void Chris@81: MainWindow::insertInstantsAtBoundaries() Chris@81: { Chris@81: MultiSelection::SelectionList selections = m_viewManager->getSelections(); Chris@81: for (MultiSelection::SelectionList::iterator i = selections.begin(); Chris@81: i != selections.end(); ++i) { Chris@81: size_t start = i->getStartFrame(); Chris@81: size_t end = i->getEndFrame(); Chris@81: if (start != end) { Chris@81: insertInstantAt(i->getStartFrame()); Chris@81: insertInstantAt(i->getEndFrame()); Chris@81: } Chris@81: } Chris@81: } Chris@81: Chris@81: void Chris@81: MainWindow::insertInstantAt(size_t frame) Chris@81: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (!pane) { Chris@0: return; Chris@0: } Chris@0: Chris@0: Layer *layer = dynamic_cast Chris@0: (pane->getSelectedLayer()); Chris@0: Chris@0: if (!layer) { Chris@0: for (int i = pane->getLayerCount(); i > 0; --i) { Chris@0: layer = dynamic_cast(pane->getLayer(i - 1)); Chris@0: if (layer) break; Chris@0: } Chris@0: Chris@0: if (!layer) { Chris@0: CommandHistory::getInstance()->startCompoundOperation Chris@0: (tr("Add Point"), true); Chris@0: layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); Chris@0: if (layer) { Chris@0: m_document->addLayerToView(pane, layer); Chris@0: m_paneStack->setCurrentLayer(pane, layer); Chris@0: } Chris@0: CommandHistory::getInstance()->endCompoundOperation(); Chris@0: } Chris@0: } Chris@0: Chris@0: if (layer) { Chris@0: Chris@0: Model *model = layer->getModel(); Chris@0: SparseOneDimensionalModel *sodm = dynamic_cast Chris@0: (model); Chris@0: Chris@0: if (sodm) { Chris@189: SparseOneDimensionalModel::Point point(frame, ""); Chris@189: Chris@189: SparseOneDimensionalModel::Point prevPoint(0); Chris@189: bool havePrevPoint = false; Chris@189: Chris@189: SparseOneDimensionalModel::EditCommand *command = Chris@189: new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); Chris@189: Chris@189: if (m_labeller->actingOnPrevPoint()) { Chris@189: Chris@189: SparseOneDimensionalModel::PointList prevPoints = Chris@189: sodm->getPreviousPoints(frame); Chris@189: Chris@189: if (!prevPoints.empty()) { Chris@189: prevPoint = *prevPoints.begin(); Chris@189: havePrevPoint = true; Chris@189: } Chris@189: } Chris@189: Chris@189: if (m_labeller) { Chris@189: Chris@189: m_labeller->setSampleRate(sodm->getSampleRate()); Chris@189: Chris@189: if (havePrevPoint) { Chris@189: command->deletePoint(prevPoint); Chris@189: } Chris@189: Chris@189: m_labeller->label Chris@189: (point, havePrevPoint ? &prevPoint : 0); Chris@189: Chris@189: if (havePrevPoint) { Chris@189: command->addPoint(prevPoint); Chris@189: } Chris@189: } Chris@189: Chris@189: command->addPoint(point); Chris@189: Chris@189: command->setName(tr("Add Point at %1 s") Chris@189: .arg(RealTime::frame2RealTime Chris@189: (frame, Chris@189: sodm->getSampleRate()) Chris@189: .toText(false).c_str())); Chris@189: Chris@189: command->finish(); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@189: MainWindow::setInstantsNumbering() Chris@189: { Chris@189: QAction *a = dynamic_cast(sender()); Chris@189: if (!a) return; Chris@189: Chris@189: int type = m_numberingActions[a]; Chris@189: Chris@189: if (m_labeller) m_labeller->setType(Labeller::ValueType(type)); Chris@189: Chris@189: QSettings settings; Chris@189: settings.beginGroup("MainWindow"); Chris@189: settings.setValue("labellertype", type); Chris@189: settings.endGroup(); Chris@189: } Chris@189: Chris@189: void Chris@189: MainWindow::setInstantsCounterCycle() Chris@189: { Chris@189: QAction *a = dynamic_cast(sender()); Chris@189: if (!a) return; Chris@189: Chris@189: int cycle = a->text().toInt(); Chris@189: if (cycle == 0) return; Chris@189: Chris@189: if (m_labeller) m_labeller->setCounterCycleSize(cycle); Chris@189: Chris@189: Chris@189: QSettings settings; Chris@189: settings.beginGroup("MainWindow"); Chris@189: settings.setValue("labellercycle", cycle); Chris@189: settings.endGroup(); Chris@189: } Chris@189: Chris@189: void Chris@189: MainWindow::renumberInstants() Chris@189: { Chris@189: Pane *pane = m_paneStack->getCurrentPane(); Chris@189: if (!pane) return; Chris@189: Chris@189: Layer *layer = dynamic_cast(pane->getSelectedLayer()); Chris@189: if (!layer) return; Chris@189: Chris@189: MultiSelection ms(m_viewManager->getSelection()); Chris@189: Chris@189: Model *model = layer->getModel(); Chris@189: SparseOneDimensionalModel *sodm = dynamic_cast Chris@189: (model); Chris@189: if (!sodm) return; Chris@189: Chris@189: if (!m_labeller) return; Chris@189: Chris@189: Labeller labeller(*m_labeller); Chris@189: labeller.setSampleRate(sodm->getSampleRate()); Chris@189: Chris@189: // This uses a command Chris@189: Chris@189: labeller.labelAll(*sodm, &ms); Chris@189: } Chris@189: Chris@189: void Chris@0: MainWindow::importAudio() Chris@0: { Chris@88: QString path = getOpenFileName(FileFinder::AudioFile); Chris@0: Chris@0: if (path != "") { Chris@82: if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Audio file \"%1\" could not be opened").arg(path)); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::importMoreAudio() Chris@0: { Chris@88: QString path = getOpenFileName(FileFinder::AudioFile); Chris@0: Chris@0: if (path != "") { Chris@82: if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Audio file \"%1\" could not be opened").arg(path)); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::exportAudio() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: Chris@88: QString path = getSaveFileName(FileFinder::AudioFile); Chris@0: Chris@0: if (path == "") return; Chris@0: Chris@0: bool ok = false; Chris@0: QString error; Chris@0: Chris@0: MultiSelection ms = m_viewManager->getSelection(); Chris@0: MultiSelection::SelectionList selections = m_viewManager->getSelections(); Chris@0: Chris@0: bool multiple = false; Chris@0: Chris@38: MultiSelection *selectionToWrite = 0; Chris@38: Chris@38: if (selections.size() == 1) { Chris@0: Chris@0: QStringList items; Chris@0: items << tr("Export the selected region only") Chris@0: << tr("Export the whole audio file"); Chris@0: Chris@0: bool ok = false; Chris@0: QString item = ListInputDialog::getItem Chris@0: (this, tr("Select region to export"), Chris@0: tr("Which region from the original audio file do you want to export?"), Chris@0: items, 0, &ok); Chris@0: Chris@0: if (!ok || item.isEmpty()) return; Chris@0: Chris@38: if (item == items[0]) selectionToWrite = &ms; Chris@38: Chris@38: } else if (selections.size() > 1) { Chris@0: Chris@0: QStringList items; Chris@0: items << tr("Export the selected regions into a single audio file") Chris@0: << tr("Export the selected regions into separate files") Chris@0: << tr("Export the whole audio file"); Chris@0: Chris@0: QString item = ListInputDialog::getItem Chris@0: (this, tr("Select region to export"), Chris@0: tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"), Chris@0: items, 0, &ok); Chris@0: Chris@0: if (!ok || item.isEmpty()) return; Chris@0: Chris@0: if (item == items[0]) { Chris@0: Chris@38: selectionToWrite = &ms; Chris@38: Chris@38: } else if (item == items[1]) { Chris@0: Chris@0: multiple = true; Chris@0: Chris@0: int n = 1; Chris@0: QString base = path; Chris@0: base.replace(".wav", ""); Chris@0: Chris@0: for (MultiSelection::SelectionList::iterator i = selections.begin(); Chris@0: i != selections.end(); ++i) { Chris@0: Chris@0: MultiSelection subms; Chris@0: subms.setSelection(*i); Chris@0: Chris@0: QString subpath = QString("%1.%2.wav").arg(base).arg(n); Chris@0: ++n; Chris@0: Chris@0: if (QFileInfo(subpath).exists()) { Chris@0: error = tr("Fragment file %1 already exists, aborting").arg(subpath); Chris@0: break; Chris@0: } Chris@0: Chris@38: WavFileWriter subwriter(subpath, Chris@38: getMainModel()->getSampleRate(), Chris@38: getMainModel()->getChannelCount()); Chris@38: subwriter.writeModel(getMainModel(), &subms); Chris@0: ok = subwriter.isOK(); Chris@0: Chris@0: if (!ok) { Chris@0: error = subwriter.getError(); Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@38: if (!multiple) { Chris@38: WavFileWriter writer(path, Chris@38: getMainModel()->getSampleRate(), Chris@38: getMainModel()->getChannelCount()); Chris@38: writer.writeModel(getMainModel(), selectionToWrite); Chris@38: ok = writer.isOK(); Chris@38: error = writer.getError(); Chris@0: } Chris@0: Chris@0: if (ok) { Chris@0: if (!multiple) { Chris@34: m_recentFiles.addFile(path); Chris@0: } Chris@0: } else { Chris@0: QMessageBox::critical(this, tr("Failed to write file"), error); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::importLayer() Chris@0: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: Chris@0: if (!pane) { Chris@0: // shouldn't happen, as the menu action should have been disabled Chris@0: std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: if (!getMainModel()) { Chris@0: // shouldn't happen, as the menu action should have been disabled Chris@0: std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@88: QString path = getOpenFileName(FileFinder::LayerFile); Chris@0: Chris@0: if (path != "") { Chris@0: Chris@82: if (openLayerFile(path) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("File %1 could not be opened.").arg(path)); Chris@0: return; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@82: MainWindow::FileOpenStatus Chris@0: MainWindow::openLayerFile(QString path) Chris@0: { Chris@86: return openLayerFile(path, path); Chris@86: } Chris@86: Chris@86: MainWindow::FileOpenStatus Chris@86: MainWindow::openLayerFile(QString path, QString location) Chris@86: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: Chris@0: if (!pane) { Chris@0: // shouldn't happen, as the menu action should have been disabled Chris@0: std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@0: if (!getMainModel()) { Chris@0: // shouldn't happen, as the menu action should have been disabled Chris@0: std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@86: bool realFile = (location == path); Chris@86: Chris@0: if (path.endsWith(".svl") || path.endsWith(".xml")) { Chris@0: Chris@0: PaneCallback callback(this); Chris@0: QFile file(path); Chris@0: Chris@0: if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { Chris@0: std::cerr << "ERROR: MainWindow::openLayerFile(" Chris@86: << location.toStdString() Chris@0: << "): Failed to open file for reading" << std::endl; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@87: SVFileReader reader(m_document, callback, location); Chris@0: reader.setCurrentPane(pane); Chris@0: Chris@0: QXmlInputSource inputSource(&file); Chris@0: reader.parse(inputSource); Chris@0: Chris@0: if (!reader.isOK()) { Chris@0: std::cerr << "ERROR: MainWindow::openLayerFile(" Chris@86: << location.toStdString() Chris@0: << "): Failed to read XML file: " Chris@0: << reader.getErrorString().toStdString() << std::endl; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@86: m_recentFiles.addFile(location); Chris@86: Chris@86: if (realFile) { Chris@88: registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog Chris@86: } Chris@86: Chris@82: return FileOpenSucceeded; Chris@0: Chris@0: } else { Chris@0: Chris@0: Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate()); Chris@0: Chris@0: if (model) { Chris@86: Chris@0: Layer *newLayer = m_document->createImportedLayer(model); Chris@86: Chris@0: if (newLayer) { Chris@86: Chris@0: m_document->addLayerToView(pane, newLayer); Chris@86: m_recentFiles.addFile(location); Chris@86: Chris@86: if (realFile) { Chris@88: registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog Chris@86: } Chris@86: Chris@82: return FileOpenSucceeded; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::exportLayer() Chris@0: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (!pane) return; Chris@0: Chris@0: Layer *layer = pane->getSelectedLayer(); Chris@0: if (!layer) return; Chris@0: Chris@0: Model *model = layer->getModel(); Chris@0: if (!model) return; Chris@0: Chris@185: FileFinder::FileType type = FileFinder::LayerFileNoMidi; Chris@185: Chris@185: if (dynamic_cast(model)) type = FileFinder::LayerFile; Chris@185: Chris@185: QString path = getSaveFileName(type); Chris@0: Chris@0: if (path == "") return; Chris@0: Chris@0: if (QFileInfo(path).suffix() == "") path += ".svl"; Chris@0: Chris@185: QString suffix = QFileInfo(path).suffix().toLower(); Chris@185: Chris@0: QString error; Chris@0: Chris@185: if (suffix == "xml" || suffix == "svl") { Chris@0: Chris@0: QFile file(path); Chris@0: if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { Chris@0: error = tr("Failed to open file %1 for writing").arg(path); Chris@0: } else { Chris@0: QTextStream out(&file); Chris@0: out << "\n" Chris@0: << "\n" Chris@0: << "\n" Chris@0: << " \n"; Chris@0: Chris@0: model->toXml(out, " "); Chris@0: Chris@0: out << " \n" Chris@0: << " \n"; Chris@0: Chris@0: layer->toXml(out, " "); Chris@0: Chris@0: out << " \n" Chris@0: << "\n"; Chris@0: } Chris@0: Chris@185: } else if (suffix == "mid" || suffix == "midi") { Chris@185: Chris@185: NoteModel *nm = dynamic_cast(model); Chris@185: Chris@185: if (!nm) { Chris@185: error = tr("Can't export non-note layers to MIDI"); Chris@185: } else { Chris@185: MIDIFileWriter writer(path, nm); Chris@185: writer.write(); Chris@185: if (!writer.isOK()) { Chris@185: error = writer.getError(); Chris@185: } Chris@185: } Chris@185: Chris@0: } else { Chris@0: Chris@0: CSVFileWriter writer(path, model, Chris@185: ((suffix == "csv") ? "," : "\t")); Chris@0: writer.write(); Chris@0: Chris@0: if (!writer.isOK()) { Chris@0: error = writer.getError(); Chris@0: } Chris@0: } Chris@0: Chris@0: if (error != "") { Chris@0: QMessageBox::critical(this, tr("Failed to write file"), error); Chris@0: } else { Chris@34: m_recentFiles.addFile(path); Chris@0: } Chris@0: } Chris@0: Chris@121: void Chris@121: MainWindow::exportImage() Chris@121: { Chris@121: Pane *pane = m_paneStack->getCurrentPane(); Chris@121: if (!pane) return; Chris@121: Chris@121: QString path = getSaveFileName(FileFinder::ImageFile); Chris@121: Chris@121: if (path == "") return; Chris@121: Chris@121: if (QFileInfo(path).suffix() == "") path += ".png"; Chris@121: Chris@123: bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty(); Chris@123: Chris@123: QSize total, visible, selected; Chris@123: total = pane->getImageSize(); Chris@123: visible = pane->getImageSize(pane->getFirstVisibleFrame(), Chris@123: pane->getLastVisibleFrame()); Chris@123: Chris@123: size_t sf0 = 0, sf1 = 0; Chris@123: Chris@123: if (haveSelection) { Chris@123: MultiSelection::SelectionList selections = m_viewManager->getSelections(); Chris@123: sf0 = selections.begin()->getStartFrame(); Chris@123: MultiSelection::SelectionList::iterator e = selections.end(); Chris@123: --e; Chris@123: sf1 = e->getEndFrame(); Chris@123: selected = pane->getImageSize(sf0, sf1); Chris@123: } Chris@123: Chris@123: QStringList items; Chris@125: items << tr("Export the whole pane (%1x%2 pixels)") Chris@123: .arg(total.width()).arg(total.height()); Chris@123: items << tr("Export the visible area only (%1x%2 pixels)") Chris@123: .arg(visible.width()).arg(visible.height()); Chris@123: if (haveSelection) { Chris@123: items << tr("Export the selection extent (%1x%2 pixels)") Chris@123: .arg(selected.width()).arg(selected.height()); Chris@124: } else { Chris@124: items << tr("Export the selection extent"); Chris@123: } Chris@123: Chris@123: QSettings settings; Chris@123: settings.beginGroup("MainWindow"); Chris@123: int deflt = settings.value("lastimageexportregion", 0).toInt(); Chris@123: if (deflt == 2 && !haveSelection) deflt = 1; Chris@124: if (deflt == 0 && total.width() > 32767) deflt = 1; Chris@124: Chris@124: ListInputDialog *lid = new ListInputDialog Chris@123: (this, tr("Select region to export"), Chris@123: tr("Which region of the current pane do you want to export as an image?"), Chris@124: items, deflt); Chris@124: Chris@124: if (!haveSelection) { Chris@124: lid->setItemAvailability(2, false); Chris@124: } Chris@124: if (total.width() > 32767) { // appears to be the limit of a QImage Chris@124: lid->setItemAvailability(0, false); Chris@124: lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image.")); Chris@124: } Chris@124: Chris@124: bool ok = lid->exec(); Chris@124: QString item = lid->getCurrentString(); Chris@124: delete lid; Chris@123: Chris@123: if (!ok || item.isEmpty()) return; Chris@123: Chris@123: settings.setValue("lastimageexportregion", deflt); Chris@123: Chris@123: QImage *image = 0; Chris@123: Chris@123: if (item == items[0]) { Chris@123: image = pane->toNewImage(); Chris@123: } else if (item == items[1]) { Chris@123: image = pane->toNewImage(pane->getFirstVisibleFrame(), Chris@123: pane->getLastVisibleFrame()); Chris@123: } else if (haveSelection) { Chris@123: image = pane->toNewImage(sf0, sf1); Chris@123: } Chris@123: Chris@121: if (!image) return; Chris@121: Chris@121: if (!image->save(path, "PNG")) { Chris@121: QMessageBox::critical(this, tr("Failed to save image file"), Chris@121: tr("Failed to save image file %1").arg(path)); Chris@121: } Chris@121: Chris@121: delete image; Chris@121: } Chris@121: Chris@82: MainWindow::FileOpenStatus Chris@0: MainWindow::openAudioFile(QString path, AudioFileOpenMode mode) Chris@0: { Chris@86: return openAudioFile(path, path, mode); Chris@86: } Chris@86: Chris@86: MainWindow::FileOpenStatus Chris@86: MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode) Chris@86: { Chris@0: if (!(QFileInfo(path).exists() && Chris@0: QFileInfo(path).isFile() && Chris@0: QFileInfo(path).isReadable())) { Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@70: m_openingAudioFile = true; Chris@70: Chris@180: size_t rate = 0; Chris@180: Chris@180: if (Preferences::getInstance()->getResampleOnLoad()) { Chris@180: rate = m_playSource->getSourceSampleRate(); Chris@180: } Chris@180: Chris@180: WaveFileModel *newModel = new WaveFileModel(path, location, rate); Chris@0: Chris@0: if (!newModel->isOK()) { Chris@0: delete newModel; Chris@70: m_openingAudioFile = false; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@0: bool setAsMain = true; Chris@0: static bool prevSetAsMain = true; Chris@0: Chris@86: bool realFile = (location == path); Chris@86: Chris@0: if (mode == CreateAdditionalModel) setAsMain = false; Chris@0: else if (mode == AskUser) { Chris@0: if (m_document->getMainModel()) { Chris@0: Chris@0: QStringList items; Chris@0: items << tr("Replace the existing main waveform") Chris@0: << tr("Load this file into a new waveform pane"); Chris@0: Chris@0: bool ok = false; Chris@0: QString item = ListInputDialog::getItem Chris@0: (this, tr("Select target for import"), Chris@0: tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), Chris@0: items, prevSetAsMain ? 0 : 1, &ok); Chris@0: Chris@0: if (!ok || item.isEmpty()) { Chris@0: delete newModel; Chris@70: m_openingAudioFile = false; Chris@82: return FileOpenCancelled; Chris@0: } Chris@0: Chris@0: setAsMain = (item == items[0]); Chris@0: prevSetAsMain = setAsMain; Chris@0: } Chris@0: } Chris@0: Chris@0: if (setAsMain) { Chris@0: Chris@0: Model *prevMain = getMainModel(); Chris@118: if (prevMain) { Chris@118: m_playSource->removeModel(prevMain); Chris@118: PlayParameterRepository::getInstance()->removeModel(prevMain); Chris@118: } Chris@118: Chris@118: PlayParameterRepository::getInstance()->addModel(newModel); Chris@0: Chris@0: m_document->setMainModel(newModel); Chris@0: setupMenus(); Chris@0: Chris@0: if (m_sessionFile == "") { Chris@0: setWindowTitle(tr("Sonic Visualiser: %1") Chris@86: .arg(QFileInfo(location).fileName())); Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: m_documentModified = false; Chris@0: } else { Chris@0: setWindowTitle(tr("Sonic Visualiser: %1 [%2]") Chris@0: .arg(QFileInfo(m_sessionFile).fileName()) Chris@86: .arg(QFileInfo(location).fileName())); Chris@0: if (m_documentModified) { Chris@0: m_documentModified = false; Chris@0: documentModified(); // so as to restore "(modified)" window title Chris@0: } Chris@0: } Chris@0: Chris@86: if (realFile) m_audioFile = path; Chris@0: Chris@0: } else { // !setAsMain Chris@0: Chris@0: CommandHistory::getInstance()->startCompoundOperation Chris@86: (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true); Chris@0: Chris@0: m_document->addImportedModel(newModel); Chris@0: Chris@0: AddPaneCommand *command = new AddPaneCommand(this); Chris@0: CommandHistory::getInstance()->addCommand(command); Chris@0: Chris@0: Pane *pane = command->getPane(); Chris@0: Chris@0: if (!m_timeRulerLayer) { Chris@0: m_timeRulerLayer = m_document->createMainModelLayer Chris@0: (LayerFactory::TimeRuler); Chris@0: } Chris@0: Chris@0: m_document->addLayerToView(pane, m_timeRulerLayer); Chris@0: Chris@0: Layer *newLayer = m_document->createImportedLayer(newModel); Chris@0: Chris@0: if (newLayer) { Chris@0: m_document->addLayerToView(pane, newLayer); Chris@0: } Chris@0: Chris@0: CommandHistory::getInstance()->endCompoundOperation(); Chris@0: } Chris@0: Chris@0: updateMenuStates(); Chris@86: m_recentFiles.addFile(location); Chris@86: if (realFile) { Chris@88: registerLastOpenedFilePath(FileFinder::AudioFile, path); // for file dialog Chris@86: } Chris@70: m_openingAudioFile = false; Chris@0: Chris@180: currentPaneChanged(m_paneStack->getCurrentPane()); Chris@180: Chris@82: return FileOpenSucceeded; Chris@0: } Chris@0: Chris@180: MainWindow::FileOpenStatus Chris@180: MainWindow::openPlaylistFile(QString path, AudioFileOpenMode mode) Chris@180: { Chris@180: return openPlaylistFile(path, path, mode); Chris@180: } Chris@180: Chris@180: MainWindow::FileOpenStatus Chris@180: MainWindow::openPlaylistFile(QString path, QString location, AudioFileOpenMode mode) Chris@180: { Chris@180: if (!(QFileInfo(path).exists() && Chris@180: QFileInfo(path).isFile() && Chris@180: QFileInfo(path).isReadable())) { Chris@180: return FileOpenFailed; Chris@180: } Chris@180: Chris@180: std::set extensions; Chris@180: PlaylistFileReader::getSupportedExtensions(extensions); Chris@180: QString extension = QFileInfo(path).suffix(); Chris@180: if (extensions.find(extension) == extensions.end()) return FileOpenFailed; Chris@180: Chris@180: PlaylistFileReader reader(path); Chris@180: if (!reader.isOK()) return FileOpenFailed; Chris@180: Chris@180: PlaylistFileReader::Playlist playlist = reader.load(); Chris@180: Chris@180: bool someSuccess = false; Chris@180: Chris@180: for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); Chris@180: i != playlist.end(); ++i) { Chris@180: Chris@180: FileOpenStatus status = openURL(*i, mode); Chris@180: Chris@180: if (status == FileOpenCancelled) { Chris@180: return FileOpenCancelled; Chris@180: } Chris@180: Chris@180: if (status == FileOpenSucceeded) { Chris@180: someSuccess = true; Chris@180: mode = CreateAdditionalModel; Chris@180: } Chris@180: } Chris@180: Chris@180: if (someSuccess) return FileOpenSucceeded; Chris@180: else return FileOpenFailed; Chris@180: } Chris@180: Chris@0: void Chris@0: MainWindow::createPlayTarget() Chris@0: { Chris@0: if (m_playTarget) return; Chris@0: Chris@0: m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); Chris@0: if (!m_playTarget) { Chris@0: QMessageBox::warning Chris@0: (this, tr("Couldn't open audio device"), Chris@0: tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"), Chris@165: QMessageBox::Ok); Chris@0: } Chris@0: connect(m_fader, SIGNAL(valueChanged(float)), Chris@0: m_playTarget, SLOT(setOutputGain(float))); Chris@0: } Chris@0: Chris@0: WaveFileModel * Chris@0: MainWindow::getMainModel() Chris@0: { Chris@0: if (!m_document) return 0; Chris@0: return m_document->getMainModel(); Chris@0: } Chris@0: Chris@116: const WaveFileModel * Chris@116: MainWindow::getMainModel() const Chris@116: { Chris@116: if (!m_document) return 0; Chris@116: return m_document->getMainModel(); Chris@116: } Chris@116: Chris@0: void Chris@0: MainWindow::newSession() Chris@0: { Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@0: closeSession(); Chris@0: createDocument(); Chris@0: Chris@0: Pane *pane = m_paneStack->addPane(); Chris@0: Chris@90: connect(pane, SIGNAL(contextHelpChanged(const QString &)), Chris@116: this, SLOT(contextHelpChanged(const QString &))); Chris@90: Chris@0: if (!m_timeRulerLayer) { Chris@0: m_timeRulerLayer = m_document->createMainModelLayer Chris@0: (LayerFactory::TimeRuler); Chris@0: } Chris@0: Chris@0: m_document->addLayerToView(pane, m_timeRulerLayer); Chris@0: Chris@0: Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); Chris@0: m_document->addLayerToView(pane, waveform); Chris@0: Chris@65: m_overview->registerView(pane); Chris@0: Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::createDocument() Chris@0: { Chris@0: m_document = new Document; Chris@0: Chris@0: connect(m_document, SIGNAL(layerAdded(Layer *)), Chris@0: this, SLOT(layerAdded(Layer *))); Chris@0: connect(m_document, SIGNAL(layerRemoved(Layer *)), Chris@0: this, SLOT(layerRemoved(Layer *))); Chris@0: connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), Chris@0: this, SLOT(layerAboutToBeDeleted(Layer *))); Chris@0: connect(m_document, SIGNAL(layerInAView(Layer *, bool)), Chris@0: this, SLOT(layerInAView(Layer *, bool))); Chris@0: Chris@0: connect(m_document, SIGNAL(modelAdded(Model *)), Chris@0: this, SLOT(modelAdded(Model *))); Chris@0: connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), Chris@0: this, SLOT(mainModelChanged(WaveFileModel *))); Chris@0: connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), Chris@0: this, SLOT(modelAboutToBeDeleted(Model *))); Chris@0: Chris@0: connect(m_document, SIGNAL(modelGenerationFailed(QString)), Chris@0: this, SLOT(modelGenerationFailed(QString))); Chris@63: connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), Chris@63: this, SLOT(modelRegenerationFailed(QString, QString))); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::closeSession() Chris@0: { Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@0: while (m_paneStack->getPaneCount() > 0) { Chris@0: Chris@0: Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1); Chris@0: Chris@0: while (pane->getLayerCount() > 0) { Chris@0: m_document->removeLayerFromView Chris@0: (pane, pane->getLayer(pane->getLayerCount() - 1)); Chris@0: } Chris@0: Chris@65: m_overview->unregisterView(pane); Chris@0: m_paneStack->deletePane(pane); Chris@0: } Chris@0: Chris@0: while (m_paneStack->getHiddenPaneCount() > 0) { Chris@0: Chris@0: Pane *pane = m_paneStack->getHiddenPane Chris@0: (m_paneStack->getHiddenPaneCount() - 1); Chris@0: Chris@0: while (pane->getLayerCount() > 0) { Chris@0: m_document->removeLayerFromView Chris@0: (pane, pane->getLayer(pane->getLayerCount() - 1)); Chris@0: } Chris@0: Chris@65: m_overview->unregisterView(pane); Chris@0: m_paneStack->deletePane(pane); Chris@0: } Chris@0: Chris@0: delete m_document; Chris@0: m_document = 0; Chris@0: m_viewManager->clearSelections(); Chris@0: m_timeRulerLayer = 0; // document owned this Chris@0: Chris@0: m_sessionFile = ""; Chris@0: setWindowTitle(tr("Sonic Visualiser")); Chris@0: Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openSession() Chris@0: { Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@0: QString orig = m_audioFile; Chris@0: if (orig == "") orig = "."; Chris@0: else orig = QFileInfo(orig).absoluteDir().canonicalPath(); Chris@0: Chris@88: QString path = getOpenFileName(FileFinder::SessionFile); Chris@0: Chris@0: if (path.isEmpty()) return; Chris@0: Chris@82: if (openSessionFile(path) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Session file \"%1\" could not be opened").arg(path)); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openSomething() Chris@0: { Chris@0: QString orig = m_audioFile; Chris@0: if (orig == "") orig = "."; Chris@0: else orig = QFileInfo(orig).absoluteDir().canonicalPath(); Chris@0: Chris@0: bool canImportLayer = (getMainModel() != 0 && Chris@0: m_paneStack != 0 && Chris@0: m_paneStack->getCurrentPane() != 0); Chris@0: Chris@88: QString path = getOpenFileName(FileFinder::AnyFile); Chris@0: Chris@0: if (path.isEmpty()) return; Chris@0: Chris@0: if (path.endsWith(".sv")) { Chris@0: Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@82: if (openSessionFile(path) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Session file \"%1\" could not be opened").arg(path)); Chris@0: } Chris@0: Chris@0: } else { Chris@0: Chris@180: if (openPlaylistFile(path, AskUser) == FileOpenFailed) { Chris@180: Chris@180: if (openAudioFile(path, AskUser) == FileOpenFailed) { Chris@180: Chris@180: if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { Chris@180: Chris@180: QMessageBox::critical(this, tr("Failed to open file"), Chris@180: tr("File \"%1\" could not be opened").arg(path)); Chris@180: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@86: MainWindow::openLocation() Chris@86: { Chris@103: QSettings settings; Chris@103: settings.beginGroup("MainWindow"); Chris@103: QString lastLocation = settings.value("lastremote", "").toString(); Chris@103: Chris@86: bool ok = false; Chris@86: QString text = QInputDialog::getText Chris@86: (this, tr("Open Location"), Chris@86: tr("Please enter the URL of the location to open:"), Chris@103: QLineEdit::Normal, lastLocation, &ok); Chris@103: Chris@103: if (!ok) return; Chris@103: Chris@103: settings.setValue("lastremote", text); Chris@103: Chris@103: if (text.isEmpty()) return; Chris@86: Chris@86: if (openURL(QUrl(text)) == FileOpenFailed) { Chris@86: QMessageBox::critical(this, tr("Failed to open location"), Chris@86: tr("URL \"%1\" could not be opened").arg(text)); Chris@86: } Chris@86: } Chris@86: Chris@86: void Chris@0: MainWindow::openRecentFile() Chris@0: { Chris@0: QObject *obj = sender(); Chris@0: QAction *action = dynamic_cast(obj); Chris@0: Chris@0: if (!action) { Chris@0: std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action" Chris@0: << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: QString path = action->text(); Chris@0: if (path == "") return; Chris@0: Chris@86: QUrl url(path); Chris@86: if (RemoteFile::canHandleScheme(url)) { Chris@86: openURL(url); Chris@86: return; Chris@86: } Chris@86: Chris@0: if (path.endsWith("sv")) { Chris@0: Chris@103: if (!checkSaveModified()) return; Chris@0: Chris@82: if (openSessionFile(path) == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Session file \"%1\" could not be opened").arg(path)); Chris@0: } Chris@0: Chris@0: } else { Chris@0: Chris@180: if (openPlaylistFile(path, AskUser) == FileOpenFailed) { Chris@180: Chris@180: if (openAudioFile(path, AskUser) == FileOpenFailed) { Chris@180: Chris@180: bool canImportLayer = (getMainModel() != 0 && Chris@180: m_paneStack != 0 && Chris@180: m_paneStack->getCurrentPane() != 0); Chris@180: Chris@180: if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { Chris@180: Chris@180: QMessageBox::critical(this, tr("Failed to open file"), Chris@180: tr("File \"%1\" could not be opened").arg(path)); Chris@180: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@82: MainWindow::FileOpenStatus Chris@180: MainWindow::openURL(QUrl url, AudioFileOpenMode mode) Chris@85: { Chris@85: if (url.scheme().toLower() == "file") { Chris@180: Chris@180: return openSomeFile(url.toLocalFile(), mode); Chris@180: Chris@86: } else if (!RemoteFile::canHandleScheme(url)) { Chris@180: Chris@85: QMessageBox::critical(this, tr("Unsupported scheme in URL"), Chris@85: tr("The URL scheme \"%1\" is not supported") Chris@85: .arg(url.scheme())); Chris@85: return FileOpenFailed; Chris@180: Chris@85: } else { Chris@85: RemoteFile rf(url); Chris@85: rf.wait(); Chris@85: if (!rf.isOK()) { Chris@85: QMessageBox::critical(this, tr("File download failed"), Chris@85: tr("Failed to download URL \"%1\": %2") Chris@85: .arg(url.toString()).arg(rf.getErrorString())); Chris@85: return FileOpenFailed; Chris@85: } Chris@88: FileOpenStatus status; Chris@180: if ((status = openSomeFile(rf.getLocalFilename(), url.toString(), Chris@180: mode)) != Chris@88: FileOpenSucceeded) { Chris@88: rf.deleteLocalFile(); Chris@88: } Chris@88: return status; Chris@85: } Chris@85: } Chris@85: Chris@85: MainWindow::FileOpenStatus Chris@180: MainWindow::openURL(QString ustr, AudioFileOpenMode mode) Chris@180: { Chris@180: // This function is used when we don't know whether the string is Chris@180: // an encoded or human-readable url Chris@180: Chris@180: QUrl url(ustr); Chris@180: Chris@180: if (url.scheme().toLower() == "file") { Chris@180: Chris@180: return openSomeFile(url.toLocalFile(), mode); Chris@180: Chris@180: } else if (!RemoteFile::canHandleScheme(url)) { Chris@180: Chris@180: QMessageBox::critical(this, tr("Unsupported scheme in URL"), Chris@180: tr("The URL scheme \"%1\" is not supported") Chris@180: .arg(url.scheme())); Chris@180: return FileOpenFailed; Chris@180: Chris@180: } else { Chris@180: RemoteFile rf(url); Chris@180: rf.wait(); Chris@180: if (!rf.isOK()) { Chris@180: // rf was created on the assumption that ustr was Chris@180: // human-readable. Let's try again, this time assuming it Chris@180: // was already encoded. Chris@180: std::cerr << "MainWindow::openURL: Failed to retrieve URL \"" Chris@180: << ustr.toStdString() << "\" as human-readable URL; " Chris@180: << "trying again treating it as encoded URL" Chris@180: << std::endl; Chris@180: url.setEncodedUrl(ustr.toAscii()); Chris@180: return openURL(url, mode); Chris@180: } Chris@180: Chris@180: FileOpenStatus status; Chris@180: if ((status = openSomeFile(rf.getLocalFilename(), ustr, mode)) != Chris@180: FileOpenSucceeded) { Chris@180: rf.deleteLocalFile(); Chris@180: } Chris@180: return status; Chris@180: } Chris@180: } Chris@180: Chris@180: MainWindow::FileOpenStatus Chris@54: MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) Chris@0: { Chris@86: return openSomeFile(path, path, mode); Chris@86: } Chris@86: Chris@86: MainWindow::FileOpenStatus Chris@86: MainWindow::openSomeFile(QString path, QString location, Chris@86: AudioFileOpenMode mode) Chris@86: { Chris@82: FileOpenStatus status; Chris@82: Chris@85: bool canImportLayer = (getMainModel() != 0 && Chris@85: m_paneStack != 0 && Chris@85: m_paneStack->getCurrentPane() != 0); Chris@85: Chris@180: if ((status = openPlaylistFile(path, location, mode)) != FileOpenFailed) { Chris@180: return status; Chris@180: } else if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { Chris@82: return status; Chris@86: } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { Chris@82: return status; Chris@85: } else if (!canImportLayer) { Chris@85: return FileOpenFailed; Chris@86: } else if ((status = openLayerFile(path, location)) != FileOpenFailed) { Chris@85: return status; Chris@0: } else { Chris@82: return FileOpenFailed; Chris@0: } Chris@0: } Chris@0: Chris@82: MainWindow::FileOpenStatus Chris@0: MainWindow::openSessionFile(QString path) Chris@0: { Chris@86: return openSessionFile(path, path); Chris@86: } Chris@86: Chris@86: MainWindow::FileOpenStatus Chris@86: MainWindow::openSessionFile(QString path, QString location) Chris@86: { Chris@0: BZipFileDevice bzFile(path); Chris@0: if (!bzFile.open(QIODevice::ReadOnly)) { Chris@86: std::cerr << "Failed to open session file \"" << location.toStdString() Chris@0: << "\": " << bzFile.errorString().toStdString() << std::endl; Chris@82: return FileOpenFailed; Chris@0: } Chris@0: Chris@103: if (!checkSaveModified()) return FileOpenCancelled; Chris@103: Chris@0: QString error; Chris@0: closeSession(); Chris@0: createDocument(); Chris@0: Chris@0: PaneCallback callback(this); Chris@0: m_viewManager->clearSelections(); Chris@0: Chris@87: SVFileReader reader(m_document, callback, location); Chris@0: QXmlInputSource inputSource(&bzFile); Chris@0: reader.parse(inputSource); Chris@0: Chris@0: if (!reader.isOK()) { Chris@0: error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); Chris@0: } Chris@0: Chris@0: bzFile.close(); Chris@0: Chris@0: bool ok = (error == ""); Chris@86: Chris@86: bool realFile = (location == path); Chris@0: Chris@0: if (ok) { Chris@86: Chris@0: setWindowTitle(tr("Sonic Visualiser: %1") Chris@86: .arg(QFileInfo(location).fileName())); Chris@86: Chris@86: if (realFile) m_sessionFile = path; Chris@86: Chris@0: setupMenus(); Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: m_documentModified = false; Chris@0: updateMenuStates(); Chris@86: Chris@86: m_recentFiles.addFile(location); Chris@86: Chris@86: if (realFile) { Chris@88: registerLastOpenedFilePath(FileFinder::SessionFile, path); // for file dialog Chris@86: } Chris@86: Chris@0: } else { Chris@0: setWindowTitle(tr("Sonic Visualiser")); Chris@0: } Chris@0: Chris@82: return ok ? FileOpenSucceeded : FileOpenFailed; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::closeEvent(QCloseEvent *e) Chris@0: { Chris@137: // std::cerr << "MainWindow::closeEvent" << std::endl; Chris@118: Chris@136: if (m_openingAudioFile) { Chris@137: // std::cerr << "Busy - ignoring close event" << std::endl; Chris@136: e->ignore(); Chris@136: return; Chris@136: } Chris@136: Chris@70: if (!m_abandoning && !checkSaveModified()) { Chris@137: // std::cerr << "Ignoring close event" << std::endl; Chris@0: e->ignore(); Chris@0: return; Chris@0: } Chris@0: Chris@5: QSettings settings; Chris@5: settings.beginGroup("MainWindow"); Chris@5: settings.setValue("size", size()); Chris@5: settings.setValue("position", pos()); Chris@5: settings.endGroup(); Chris@5: Chris@163: delete m_keyReference; Chris@163: m_keyReference = 0; Chris@163: Chris@163: if (m_preferencesDialog && Chris@163: m_preferencesDialog->isVisible()) { Chris@164: closeSession(); // otherwise we'll have to wait for prefs changes Chris@163: m_preferencesDialog->applicationClosing(false); Chris@163: } Chris@163: Chris@177: if (m_layerTreeView && Chris@177: m_layerTreeView->isVisible()) { Chris@177: delete m_layerTreeView; Chris@177: } Chris@177: Chris@0: e->accept(); Chris@0: return; Chris@0: } Chris@0: Chris@0: bool Chris@11: MainWindow::commitData(bool mayAskUser) Chris@11: { Chris@11: if (mayAskUser) { Chris@163: bool rv = checkSaveModified(); Chris@163: if (rv) { Chris@163: if (m_preferencesDialog && Chris@163: m_preferencesDialog->isVisible()) { Chris@163: m_preferencesDialog->applicationClosing(false); Chris@163: } Chris@163: } Chris@163: return rv; Chris@11: } else { Chris@163: if (m_preferencesDialog && Chris@163: m_preferencesDialog->isVisible()) { Chris@163: m_preferencesDialog->applicationClosing(true); Chris@163: } Chris@11: if (!m_documentModified) return true; Chris@11: Chris@11: // If we can't check with the user first, then we can't save Chris@11: // to the original session file (even if we have it) -- have Chris@11: // to use a temporary file Chris@11: Chris@11: QString svDirBase = ".sv1"; Chris@11: QString svDir = QDir::home().filePath(svDirBase); Chris@11: Chris@11: if (!QFileInfo(svDir).exists()) { Chris@11: if (!QDir::home().mkdir(svDirBase)) return false; Chris@11: } else { Chris@11: if (!QFileInfo(svDir).isDir()) return false; Chris@11: } Chris@11: Chris@11: // This name doesn't have to be unguessable Chris@93: #ifndef _WIN32 Chris@11: QString fname = QString("tmp-%1-%2.sv") Chris@11: .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")) Chris@11: .arg(QProcess().pid()); Chris@93: #else Chris@93: QString fname = QString("tmp-%1.sv") Chris@93: .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")); Chris@93: #endif Chris@11: QString fpath = QDir(svDir).filePath(fname); Chris@11: if (saveSessionFile(fpath)) { Chris@34: m_recentFiles.addFile(fpath); Chris@11: return true; Chris@11: } else { Chris@11: return false; Chris@11: } Chris@11: } Chris@11: } Chris@11: Chris@11: bool Chris@0: MainWindow::checkSaveModified() Chris@0: { Chris@0: // Called before some destructive operation (e.g. new session, Chris@0: // exit program). Return true if we can safely proceed, false to Chris@0: // cancel. Chris@0: Chris@0: if (!m_documentModified) return true; Chris@0: Chris@0: int button = Chris@0: QMessageBox::warning(this, Chris@0: tr("Session modified"), Chris@0: tr("The current session has been modified.\nDo you want to save it?"), Chris@165: QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, Chris@165: QMessageBox::Yes); Chris@0: Chris@0: if (button == QMessageBox::Yes) { Chris@0: saveSession(); Chris@0: if (m_documentModified) { // save failed -- don't proceed! Chris@0: return false; Chris@0: } else { Chris@0: return true; // saved, so it's safe to continue now Chris@0: } Chris@0: } else if (button == QMessageBox::No) { Chris@0: m_documentModified = false; // so we know to abandon it Chris@0: return true; Chris@0: } Chris@0: Chris@0: // else cancel Chris@0: return false; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::saveSession() Chris@0: { Chris@0: if (m_sessionFile != "") { Chris@0: if (!saveSessionFile(m_sessionFile)) { Chris@0: QMessageBox::critical(this, tr("Failed to save file"), Chris@0: tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); Chris@0: } else { Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@0: } Chris@0: } else { Chris@0: saveSessionAs(); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::saveSessionAs() Chris@0: { Chris@0: QString orig = m_audioFile; Chris@0: if (orig == "") orig = "."; Chris@0: else orig = QFileInfo(orig).absoluteDir().canonicalPath(); Chris@0: Chris@88: QString path = getSaveFileName(FileFinder::SessionFile); Chris@81: Chris@81: if (path == "") return; Chris@0: Chris@0: if (!saveSessionFile(path)) { Chris@0: QMessageBox::critical(this, tr("Failed to save file"), Chris@79: tr("Session file \"%1\" could not be saved.").arg(path)); Chris@0: } else { Chris@0: setWindowTitle(tr("Sonic Visualiser: %1") Chris@0: .arg(QFileInfo(path).fileName())); Chris@0: m_sessionFile = path; Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@34: m_recentFiles.addFile(path); Chris@0: } Chris@0: } Chris@0: Chris@0: bool Chris@0: MainWindow::saveSessionFile(QString path) Chris@0: { Chris@0: BZipFileDevice bzFile(path); Chris@0: if (!bzFile.open(QIODevice::WriteOnly)) { Chris@0: std::cerr << "Failed to open session file \"" << path.toStdString() Chris@0: << "\" for writing: " Chris@0: << bzFile.errorString().toStdString() << std::endl; Chris@0: return false; Chris@0: } Chris@0: Chris@0: QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); Chris@0: Chris@0: QTextStream out(&bzFile); Chris@0: toXml(out); Chris@0: out.flush(); Chris@0: Chris@0: QApplication::restoreOverrideCursor(); Chris@0: Chris@84: if (!bzFile.isOK()) { Chris@0: QMessageBox::critical(this, tr("Failed to write file"), Chris@0: tr("Failed to write to file \"%1\": %2") Chris@0: .arg(path).arg(bzFile.errorString())); Chris@0: bzFile.close(); Chris@0: return false; Chris@0: } Chris@0: Chris@0: bzFile.close(); Chris@0: return true; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::toXml(QTextStream &out) Chris@0: { Chris@0: QString indent(" "); Chris@0: Chris@0: out << "\n"; Chris@0: out << "\n"; Chris@0: out << "\n"; Chris@0: Chris@0: m_document->toXml(out, "", ""); Chris@0: Chris@0: out << "\n"; Chris@0: Chris@0: out << QString(" \n") Chris@0: .arg(width()).arg(height()); Chris@0: Chris@0: for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Chris@0: Chris@0: Pane *pane = m_paneStack->getPane(i); Chris@0: Chris@0: if (pane) { Chris@0: pane->toXml(out, indent); Chris@0: } Chris@0: } Chris@0: Chris@0: out << "\n"; Chris@0: Chris@0: m_viewManager->getSelection().toXml(out); Chris@0: Chris@0: out << "\n"; Chris@0: } Chris@0: Chris@0: Pane * Chris@0: MainWindow::addPaneToStack() Chris@0: { Chris@0: AddPaneCommand *command = new AddPaneCommand(this); Chris@0: CommandHistory::getInstance()->addCommand(command); Chris@0: return command->getPane(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::zoomIn() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->zoom(true); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::zoomOut() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->zoom(false); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::zoomToFit() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (!currentPane) return; Chris@0: Chris@0: Model *model = getMainModel(); Chris@0: if (!model) return; Chris@0: Chris@0: size_t start = model->getStartFrame(); Chris@0: size_t end = model->getEndFrame(); Chris@0: size_t pixels = currentPane->width(); Chris@159: Chris@158: size_t sw = currentPane->getVerticalScaleWidth(); Chris@158: if (pixels > sw * 2) pixels -= sw * 2; Chris@158: else pixels = 1; Chris@159: if (pixels > 4) pixels -= 4; Chris@159: Chris@0: size_t zoomLevel = (end - start) / pixels; Chris@0: Chris@0: currentPane->setZoomLevel(zoomLevel); Chris@159: currentPane->setCentreFrame((start + end) / 2); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::zoomDefault() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->setZoomLevel(1024); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::scrollLeft() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->scroll(false, false); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::jumpLeft() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->scroll(false, true); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::scrollRight() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->scroll(true, false); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::jumpRight() Chris@0: { Chris@0: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentPane->scroll(true, true); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::showNoOverlays() Chris@0: { Chris@0: m_viewManager->setOverlayMode(ViewManager::NoOverlays); Chris@0: } Chris@0: Chris@0: void Chris@90: MainWindow::showMinimalOverlays() Chris@0: { Chris@90: m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); Chris@0: } Chris@0: Chris@0: void Chris@90: MainWindow::showStandardOverlays() Chris@90: { Chris@90: m_viewManager->setOverlayMode(ViewManager::StandardOverlays); Chris@90: } Chris@90: Chris@90: void Chris@90: MainWindow::showAllOverlays() Chris@0: { Chris@0: m_viewManager->setOverlayMode(ViewManager::AllOverlays); Chris@0: } Chris@0: Chris@0: void Chris@7: MainWindow::toggleZoomWheels() Chris@7: { Chris@7: if (m_viewManager->getZoomWheelsEnabled()) { Chris@7: m_viewManager->setZoomWheelsEnabled(false); Chris@7: } else { Chris@7: m_viewManager->setZoomWheelsEnabled(true); Chris@7: } Chris@7: } Chris@7: Chris@7: void Chris@72: MainWindow::togglePropertyBoxes() Chris@72: { Chris@72: if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { Chris@72: if (Preferences::getInstance()->getPropertyBoxLayout() == Chris@72: Preferences::VerticallyStacked) { Chris@72: m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); Chris@72: } else { Chris@72: m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); Chris@72: } Chris@72: } else { Chris@72: m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); Chris@72: } Chris@72: } Chris@72: Chris@72: void Chris@90: MainWindow::toggleStatusBar() Chris@90: { Chris@90: QSettings settings; Chris@90: settings.beginGroup("MainWindow"); Chris@90: bool sb = settings.value("showstatusbar", true).toBool(); Chris@90: Chris@90: if (sb) { Chris@90: statusBar()->hide(); Chris@90: } else { Chris@90: statusBar()->show(); Chris@90: } Chris@90: Chris@90: settings.setValue("showstatusbar", !sb); Chris@90: Chris@90: settings.endGroup(); Chris@90: } Chris@90: Chris@90: void Chris@72: MainWindow::preferenceChanged(PropertyContainer::PropertyName name) Chris@72: { Chris@72: if (name == "Property Box Layout") { Chris@72: if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { Chris@72: if (Preferences::getInstance()->getPropertyBoxLayout() == Chris@72: Preferences::VerticallyStacked) { Chris@72: m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); Chris@72: } else { Chris@72: m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); Chris@72: } Chris@72: } Chris@180: } else if (name == "Background Mode" && m_viewManager) { Chris@180: Preferences::BackgroundMode mode = Chris@180: Preferences::getInstance()->getBackgroundMode(); Chris@180: if (mode == Preferences::BackgroundFromTheme) { Chris@180: m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); Chris@180: } else if (mode == Preferences::DarkBackground) { Chris@180: m_viewManager->setGlobalDarkBackground(true); Chris@180: } else { Chris@180: m_viewManager->setGlobalDarkBackground(false); Chris@180: } Chris@180: if (m_viewManager->getGlobalDarkBackground()) { Chris@180: m_panLayer->setBaseColour Chris@180: (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); Chris@180: } else { Chris@180: m_panLayer->setBaseColour Chris@180: (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); Chris@180: } Chris@180: } Chris@72: } Chris@72: Chris@72: void Chris@0: MainWindow::play() Chris@0: { Chris@0: if (m_playSource->isPlaying()) { Chris@116: stop(); Chris@0: } else { Chris@116: playbackFrameChanged(m_viewManager->getPlaybackFrame()); Chris@0: m_playSource->play(m_viewManager->getPlaybackFrame()); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::ffwd() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: Chris@0: int frame = m_viewManager->getPlaybackFrame(); Chris@0: ++frame; Chris@0: Chris@155: Layer *layer = getSnapLayer(); Chris@155: size_t sr = getMainModel()->getSampleRate(); Chris@155: Chris@155: if (!layer) { Chris@155: Chris@155: frame = RealTime::realTime2Frame Chris@155: (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr); Chris@155: if (frame > int(getMainModel()->getEndFrame())) { Chris@155: frame = getMainModel()->getEndFrame(); Chris@155: } Chris@155: Chris@155: } else { Chris@155: Chris@155: size_t resolution = 0; Chris@155: if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), Chris@155: frame, resolution, Layer::SnapRight)) { Chris@155: frame = getMainModel()->getEndFrame(); Chris@155: } Chris@0: } Chris@155: Chris@155: if (frame < 0) frame = 0; Chris@147: Chris@147: if (m_viewManager->getPlaySelectionMode()) { Chris@155: frame = m_viewManager->constrainFrameToSelection(size_t(frame)); Chris@147: } Chris@0: Chris@0: m_viewManager->setPlaybackFrame(frame); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::ffwdEnd() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@147: Chris@147: size_t frame = getMainModel()->getEndFrame(); Chris@147: Chris@147: if (m_viewManager->getPlaySelectionMode()) { Chris@155: frame = m_viewManager->constrainFrameToSelection(frame); Chris@147: } Chris@147: Chris@147: m_viewManager->setPlaybackFrame(frame); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::rewind() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@0: Chris@0: int frame = m_viewManager->getPlaybackFrame(); Chris@0: if (frame > 0) --frame; Chris@0: Chris@155: Layer *layer = getSnapLayer(); Chris@155: size_t sr = getMainModel()->getSampleRate(); Chris@156: Chris@156: // when rewinding during playback, we want to allow a period Chris@156: // following a rewind target point at which the rewind will go to Chris@156: // the prior point instead of the immediately neighbouring one Chris@156: if (m_playSource && m_playSource->isPlaying()) { Chris@156: RealTime ct = RealTime::frame2RealTime(frame, sr); Chris@156: ct = ct - RealTime::fromSeconds(0.25); Chris@156: if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; Chris@156: // std::cerr << "rewind: frame " << frame << " -> "; Chris@156: frame = RealTime::realTime2Frame(ct, sr); Chris@156: // std::cerr << frame << std::endl; Chris@156: } Chris@155: Chris@155: if (!layer) { Chris@155: Chris@155: frame = RealTime::realTime2Frame Chris@155: (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr); Chris@155: if (frame < int(getMainModel()->getStartFrame())) { Chris@155: frame = getMainModel()->getStartFrame(); Chris@155: } Chris@155: Chris@155: } else { Chris@155: Chris@155: size_t resolution = 0; Chris@155: if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), Chris@155: frame, resolution, Layer::SnapLeft)) { Chris@155: frame = getMainModel()->getStartFrame(); Chris@155: } Chris@0: } Chris@147: Chris@155: if (frame < 0) frame = 0; Chris@155: Chris@147: if (m_viewManager->getPlaySelectionMode()) { Chris@155: frame = m_viewManager->constrainFrameToSelection(size_t(frame)); Chris@147: } Chris@147: Chris@0: m_viewManager->setPlaybackFrame(frame); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::rewindStart() Chris@0: { Chris@0: if (!getMainModel()) return; Chris@147: Chris@147: size_t frame = getMainModel()->getStartFrame(); Chris@147: Chris@147: if (m_viewManager->getPlaySelectionMode()) { Chris@155: frame = m_viewManager->constrainFrameToSelection(frame); Chris@155: } Chris@155: Chris@155: m_viewManager->setPlaybackFrame(frame); Chris@155: } Chris@155: Chris@155: Layer * Chris@155: MainWindow::getSnapLayer() const Chris@155: { Chris@155: Pane *pane = m_paneStack->getCurrentPane(); Chris@155: if (!pane) return 0; Chris@155: Chris@155: Layer *layer = pane->getSelectedLayer(); Chris@155: Chris@155: if (!dynamic_cast(layer) && Chris@155: !dynamic_cast(layer) && Chris@155: !dynamic_cast(layer)) { Chris@155: Chris@155: layer = 0; Chris@155: Chris@155: for (int i = pane->getLayerCount(); i > 0; --i) { Chris@155: Layer *l = pane->getLayer(i-1); Chris@155: if (dynamic_cast(l)) { Chris@155: layer = l; Chris@155: break; Chris@155: } Chris@147: } Chris@147: } Chris@147: Chris@155: return layer; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::stop() Chris@0: { Chris@0: m_playSource->stop(); Chris@116: Chris@116: if (m_paneStack && m_paneStack->getCurrentPane()) { Chris@116: updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); Chris@116: } else { Chris@116: m_myStatusMessage = ""; Chris@116: statusBar()->showMessage(""); Chris@116: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::addPane() Chris@0: { Chris@0: QObject *s = sender(); Chris@0: QAction *action = dynamic_cast(s); Chris@0: Chris@0: if (!action) { Chris@0: std::cerr << "WARNING: MainWindow::addPane: sender is not an action" Chris@0: << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: PaneActionMap::iterator i = m_paneActions.find(action); Chris@0: Chris@0: if (i == m_paneActions.end()) { Chris@0: std::cerr << "WARNING: MainWindow::addPane: unknown action " Chris@0: << action->objectName().toStdString() << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@69: addPane(i->second, action->text()); Chris@69: } Chris@69: Chris@69: void Chris@69: MainWindow::addPane(const PaneConfiguration &configuration, QString text) Chris@69: { Chris@69: CommandHistory::getInstance()->startCompoundOperation(text, true); Chris@0: Chris@0: AddPaneCommand *command = new AddPaneCommand(this); Chris@0: CommandHistory::getInstance()->addCommand(command); Chris@0: Chris@0: Pane *pane = command->getPane(); Chris@0: Chris@69: if (configuration.layer == LayerFactory::Spectrum) { Chris@109: pane->setPlaybackFollow(PlaybackScrollContinuous); Chris@110: pane->setFollowGlobalZoom(false); Chris@112: pane->setZoomLevel(512); Chris@7: } Chris@7: Chris@69: if (configuration.layer != LayerFactory::TimeRuler && Chris@69: configuration.layer != LayerFactory::Spectrum) { Chris@8: Chris@0: if (!m_timeRulerLayer) { Chris@0: // std::cerr << "no time ruler layer, creating one" << std::endl; Chris@0: m_timeRulerLayer = m_document->createMainModelLayer Chris@0: (LayerFactory::TimeRuler); Chris@0: } Chris@0: Chris@0: // std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl; Chris@0: Chris@0: m_document->addLayerToView(pane, m_timeRulerLayer); Chris@0: } Chris@0: Chris@69: Layer *newLayer = m_document->createLayer(configuration.layer); Chris@69: Chris@69: Model *suggestedModel = configuration.sourceModel; Chris@66: Model *model = 0; Chris@66: Chris@66: if (suggestedModel) { Chris@66: Chris@66: // check its validity Chris@66: std::vector inputModels = m_document->getTransformInputModels(); Chris@66: for (size_t j = 0; j < inputModels.size(); ++j) { Chris@66: if (inputModels[j] == suggestedModel) { Chris@66: model = suggestedModel; Chris@66: break; Chris@66: } Chris@66: } Chris@66: Chris@66: if (!model) { Chris@66: std::cerr << "WARNING: Model " << (void *)suggestedModel Chris@66: << " appears in pane action map, but is not reported " Chris@66: << "by document as a valid transform source" << std::endl; Chris@66: } Chris@66: } Chris@66: Chris@66: if (!model) model = m_document->getMainModel(); Chris@66: Chris@66: m_document->setModel(newLayer, model); Chris@66: Chris@69: m_document->setChannel(newLayer, configuration.channel); Chris@0: m_document->addLayerToView(pane, newLayer); Chris@0: Chris@0: m_paneStack->setCurrentPane(pane); Chris@70: m_paneStack->setCurrentLayer(pane, newLayer); Chris@0: Chris@130: // std::cerr << "MainWindow::addPane: global centre frame is " Chris@130: // << m_viewManager->getGlobalCentreFrame() << std::endl; Chris@130: // pane->setCentreFrame(m_viewManager->getGlobalCentreFrame()); Chris@73: Chris@0: CommandHistory::getInstance()->endCompoundOperation(); Chris@0: Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) : Chris@0: m_mw(mw), Chris@0: m_pane(0), Chris@0: m_prevCurrentPane(0), Chris@0: m_added(false) Chris@0: { Chris@0: } Chris@0: Chris@0: MainWindow::AddPaneCommand::~AddPaneCommand() Chris@0: { Chris@0: if (m_pane && !m_added) { Chris@0: m_mw->m_paneStack->deletePane(m_pane); Chris@0: } Chris@0: } Chris@0: Chris@0: QString Chris@0: MainWindow::AddPaneCommand::getName() const Chris@0: { Chris@0: return tr("Add Pane"); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::AddPaneCommand::execute() Chris@0: { Chris@0: if (!m_pane) { Chris@0: m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); Chris@0: m_pane = m_mw->m_paneStack->addPane(); Chris@90: Chris@90: connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), Chris@116: m_mw, SLOT(contextHelpChanged(const QString &))); Chris@0: } else { Chris@0: m_mw->m_paneStack->showPane(m_pane); Chris@0: } Chris@0: Chris@0: m_mw->m_paneStack->setCurrentPane(m_pane); Chris@65: m_mw->m_overview->registerView(m_pane); Chris@0: m_added = true; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::AddPaneCommand::unexecute() Chris@0: { Chris@0: m_mw->m_paneStack->hidePane(m_pane); Chris@0: m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); Chris@65: m_mw->m_overview->unregisterView(m_pane); Chris@0: m_added = false; Chris@0: } Chris@0: Chris@0: MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) : Chris@0: m_mw(mw), Chris@0: m_pane(pane), Chris@0: m_added(true) Chris@0: { Chris@0: } Chris@0: Chris@0: MainWindow::RemovePaneCommand::~RemovePaneCommand() Chris@0: { Chris@0: if (m_pane && !m_added) { Chris@0: m_mw->m_paneStack->deletePane(m_pane); Chris@0: } Chris@0: } Chris@0: Chris@0: QString Chris@0: MainWindow::RemovePaneCommand::getName() const Chris@0: { Chris@0: return tr("Remove Pane"); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::RemovePaneCommand::execute() Chris@0: { Chris@0: m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); Chris@0: m_mw->m_paneStack->hidePane(m_pane); Chris@65: m_mw->m_overview->unregisterView(m_pane); Chris@0: m_added = false; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::RemovePaneCommand::unexecute() Chris@0: { Chris@0: m_mw->m_paneStack->showPane(m_pane); Chris@0: m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); Chris@65: m_mw->m_overview->registerView(m_pane); Chris@0: m_added = true; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::addLayer() Chris@0: { Chris@0: QObject *s = sender(); Chris@0: QAction *action = dynamic_cast(s); Chris@0: Chris@0: if (!action) { Chris@0: std::cerr << "WARNING: MainWindow::addLayer: sender is not an action" Chris@0: << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: Chris@0: if (!pane) { Chris@0: std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action); Chris@0: Chris@0: if (ei != m_existingLayerActions.end()) { Chris@0: Layer *newLayer = ei->second; Chris@0: m_document->addLayerToView(pane, newLayer); Chris@0: m_paneStack->setCurrentLayer(pane, newLayer); Chris@0: return; Chris@0: } Chris@0: Chris@95: ei = m_sliceActions.find(action); Chris@95: Chris@95: if (ei != m_sliceActions.end()) { Chris@95: Layer *newLayer = m_document->createLayer(LayerFactory::Slice); Chris@95: // document->setModel(newLayer, ei->second->getModel()); Chris@95: SliceableLayer *source = dynamic_cast(ei->second); Chris@95: SliceLayer *dest = dynamic_cast(newLayer); Chris@95: if (source && dest) { Chris@95: dest->setSliceableModel(source->getSliceableModel()); Chris@95: connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)), Chris@95: dest, SLOT(sliceableModelReplaced(const Model *, const Model *))); Chris@95: connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), Chris@95: dest, SLOT(modelAboutToBeDeleted(Model *))); Chris@95: } Chris@95: m_document->addLayerToView(pane, newLayer); Chris@95: m_paneStack->setCurrentLayer(pane, newLayer); Chris@95: return; Chris@95: } Chris@95: Chris@34: TransformActionMap::iterator i = m_transformActions.find(action); Chris@34: Chris@34: if (i == m_transformActions.end()) { Chris@0: Chris@0: LayerActionMap::iterator i = m_layerActions.find(action); Chris@0: Chris@0: if (i == m_layerActions.end()) { Chris@0: std::cerr << "WARNING: MainWindow::addLayer: unknown action " Chris@0: << action->objectName().toStdString() << std::endl; Chris@0: return; Chris@0: } Chris@0: Chris@0: LayerFactory::LayerType type = i->second; Chris@0: Chris@0: LayerFactory::LayerTypeSet emptyTypes = Chris@0: LayerFactory::getInstance()->getValidEmptyLayerTypes(); Chris@0: Chris@0: Layer *newLayer; Chris@0: Chris@0: if (emptyTypes.find(type) != emptyTypes.end()) { Chris@0: Chris@0: newLayer = m_document->createEmptyLayer(type); Chris@0: m_toolActions[ViewManager::DrawMode]->trigger(); Chris@0: Chris@0: } else { Chris@0: Chris@0: newLayer = m_document->createMainModelLayer(type); Chris@0: } Chris@0: Chris@0: m_document->addLayerToView(pane, newLayer); Chris@0: m_paneStack->setCurrentLayer(pane, newLayer); Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@107: TransformId transform = i->second; Chris@0: TransformFactory *factory = TransformFactory::getInstance(); Chris@0: Chris@0: QString configurationXml; Chris@0: Chris@0: int channel = -1; Chris@0: // pick up the default channel from any existing layers on the same pane Chris@0: for (int j = 0; j < pane->getLayerCount(); ++j) { Chris@0: int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j)); Chris@0: if (c != -1) { Chris@0: channel = c; Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@33: // We always ask for configuration, even if the plugin isn't Chris@33: // supposed to be configurable, because we need to let the user Chris@33: // change the execution context (block size etc). Chris@0: Chris@27: PluginTransform::ExecutionContext context(channel); Chris@27: Chris@66: std::vector candidateInputModels = Chris@66: m_document->getTransformInputModels(); Chris@53: Chris@184: size_t startFrame = 0, duration = 0; Chris@184: size_t endFrame = 0; Chris@184: m_viewManager->getSelection().getExtents(startFrame, endFrame); Chris@184: if (endFrame > startFrame) duration = endFrame - startFrame; Chris@184: else startFrame = 0; Chris@184: Chris@54: Model *inputModel = factory->getConfigurationForTransform(transform, Chris@54: candidateInputModels, Chris@54: context, Chris@54: configurationXml, Chris@184: m_playSource, Chris@184: startFrame, Chris@184: duration); Chris@54: if (!inputModel) return; Chris@54: Chris@135: // std::cerr << "MainWindow::addLayer: Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl; Chris@0: Chris@0: Layer *newLayer = m_document->createDerivedLayer(transform, Chris@54: inputModel, Chris@27: context, Chris@0: configurationXml); Chris@0: Chris@0: if (newLayer) { Chris@0: m_document->addLayerToView(pane, newLayer); Chris@27: m_document->setChannel(newLayer, context.channel); Chris@34: m_recentTransforms.add(transform); Chris@70: m_paneStack->setCurrentLayer(pane, newLayer); Chris@0: } Chris@0: Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::deleteCurrentPane() Chris@0: { Chris@0: CommandHistory::getInstance()->startCompoundOperation Chris@0: (tr("Delete Pane"), true); Chris@0: Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (pane) { Chris@0: while (pane->getLayerCount() > 0) { Chris@0: Layer *layer = pane->getLayer(0); Chris@0: if (layer) { Chris@0: m_document->removeLayerFromView(pane, layer); Chris@0: } else { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: RemovePaneCommand *command = new RemovePaneCommand(this, pane); Chris@0: CommandHistory::getInstance()->addCommand(command); Chris@0: } Chris@0: Chris@0: CommandHistory::getInstance()->endCompoundOperation(); Chris@0: Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::deleteCurrentLayer() Chris@0: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (pane) { Chris@0: Layer *layer = pane->getSelectedLayer(); Chris@0: if (layer) { Chris@0: m_document->removeLayerFromView(pane, layer); Chris@0: } Chris@0: } Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::renameCurrentLayer() Chris@0: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (pane) { Chris@0: Layer *layer = pane->getSelectedLayer(); Chris@0: if (layer) { Chris@0: bool ok = false; Chris@0: QString newName = QInputDialog::getText Chris@0: (this, tr("Rename Layer"), Chris@0: tr("New name for this layer:"), Chris@0: QLineEdit::Normal, layer->objectName(), &ok); Chris@0: if (ok) { Chris@0: layer->setObjectName(newName); Chris@95: setupExistingLayersMenus(); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@59: MainWindow::playSpeedChanged(int position) Chris@0: { Chris@59: PlaySpeedRangeMapper mapper(0, 200); Chris@60: Chris@60: float percent = m_playSpeed->mappedValue(); Chris@60: float factor = mapper.getFactorForValue(percent); Chris@60: Chris@60: std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl; Chris@60: Chris@59: bool something = (position != 100); Chris@155: Chris@59: int pc = lrintf(percent); Chris@21: Chris@155: if (!something) { Chris@155: contextHelpChanged(tr("Playback speed: Normal")); Chris@155: } else { Chris@155: contextHelpChanged(tr("Playback speed: %1%2%") Chris@155: .arg(position > 100 ? "+" : "") Chris@155: .arg(pc)); Chris@155: } Chris@155: Chris@25: m_playSharpen->setEnabled(something); Chris@26: m_playMono->setEnabled(something); Chris@25: bool sharpen = (something && m_playSharpen->isChecked()); Chris@26: bool mono = (something && m_playMono->isChecked()); Chris@26: m_playSource->setTimeStretch(factor, sharpen, mono); Chris@155: Chris@155: updateMenuStates(); Chris@16: } Chris@16: Chris@16: void Chris@16: MainWindow::playSharpenToggled() Chris@16: { Chris@21: QSettings settings; Chris@21: settings.beginGroup("MainWindow"); Chris@21: settings.setValue("playsharpen", m_playSharpen->isChecked()); Chris@21: settings.endGroup(); Chris@21: Chris@16: playSpeedChanged(m_playSpeed->value()); Chris@0: } Chris@0: Chris@0: void Chris@26: MainWindow::playMonoToggled() Chris@26: { Chris@26: QSettings settings; Chris@26: settings.beginGroup("MainWindow"); Chris@26: settings.setValue("playmono", m_playMono->isChecked()); Chris@26: settings.endGroup(); Chris@26: Chris@26: playSpeedChanged(m_playSpeed->value()); Chris@26: } Chris@26: Chris@26: void Chris@155: MainWindow::speedUpPlayback() Chris@155: { Chris@155: int value = m_playSpeed->value(); Chris@155: value = value + m_playSpeed->pageStep(); Chris@155: if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum(); Chris@155: m_playSpeed->setValue(value); Chris@155: } Chris@155: Chris@155: void Chris@155: MainWindow::slowDownPlayback() Chris@155: { Chris@155: int value = m_playSpeed->value(); Chris@155: value = value - m_playSpeed->pageStep(); Chris@155: if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum(); Chris@155: m_playSpeed->setValue(value); Chris@155: } Chris@155: Chris@155: void Chris@155: MainWindow::restoreNormalPlayback() Chris@155: { Chris@155: m_playSpeed->setValue(m_playSpeed->defaultValue()); Chris@155: } Chris@155: Chris@155: void Chris@116: MainWindow::playbackFrameChanged(unsigned long frame) Chris@116: { Chris@116: if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Chris@116: Chris@116: RealTime now = RealTime::frame2RealTime Chris@116: (frame, getMainModel()->getSampleRate()); Chris@116: Chris@116: if (now.sec == m_lastPlayStatusSec) return; Chris@116: Chris@116: RealTime then = RealTime::frame2RealTime Chris@116: (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); Chris@116: Chris@116: QString nowStr; Chris@116: QString thenStr; Chris@116: QString remainingStr; Chris@116: Chris@116: if (then.sec > 10) { Chris@116: nowStr = now.toSecText().c_str(); Chris@116: thenStr = then.toSecText().c_str(); Chris@116: remainingStr = (then - now).toSecText().c_str(); Chris@116: m_lastPlayStatusSec = now.sec; Chris@116: } else { Chris@116: nowStr = now.toText(true).c_str(); Chris@116: thenStr = then.toText(true).c_str(); Chris@116: remainingStr = (then - now).toText(true).c_str(); Chris@116: } Chris@116: Chris@116: m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") Chris@116: .arg(nowStr).arg(thenStr).arg(remainingStr); Chris@116: Chris@116: statusBar()->showMessage(m_myStatusMessage); Chris@116: } Chris@116: Chris@116: void Chris@137: MainWindow::globalCentreFrameChanged(unsigned long ) Chris@116: { Chris@116: if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Chris@116: Pane *p = 0; Chris@116: if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; Chris@116: if (!p->getFollowGlobalPan()) return; Chris@116: updateVisibleRangeDisplay(p); Chris@116: } Chris@116: Chris@116: void Chris@137: MainWindow::viewCentreFrameChanged(View *v, unsigned long ) Chris@116: { Chris@116: if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Chris@116: Pane *p = 0; Chris@116: if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; Chris@116: if (v == p) updateVisibleRangeDisplay(p); Chris@116: } Chris@116: Chris@116: void Chris@137: MainWindow::viewZoomLevelChanged(View *v, unsigned long , bool ) Chris@116: { Chris@116: if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Chris@116: Pane *p = 0; Chris@116: if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; Chris@116: if (v == p) updateVisibleRangeDisplay(p); Chris@116: } Chris@116: Chris@116: void Chris@116: MainWindow::updateVisibleRangeDisplay(Pane *p) const Chris@116: { Chris@116: if (!getMainModel() || !p) { Chris@116: return; Chris@116: } Chris@116: Chris@117: bool haveSelection = false; Chris@117: size_t startFrame = 0, endFrame = 0; Chris@117: Chris@117: if (m_viewManager && m_viewManager->haveInProgressSelection()) { Chris@117: Chris@117: bool exclusive = false; Chris@117: Selection s = m_viewManager->getInProgressSelection(exclusive); Chris@117: Chris@117: if (!s.isEmpty()) { Chris@117: haveSelection = true; Chris@117: startFrame = s.getStartFrame(); Chris@117: endFrame = s.getEndFrame(); Chris@117: } Chris@117: } Chris@117: Chris@117: if (!haveSelection) { Chris@117: startFrame = p->getFirstVisibleFrame(); Chris@117: endFrame = p->getLastVisibleFrame(); Chris@117: } Chris@117: Chris@116: RealTime start = RealTime::frame2RealTime Chris@117: (startFrame, getMainModel()->getSampleRate()); Chris@116: Chris@116: RealTime end = RealTime::frame2RealTime Chris@117: (endFrame, getMainModel()->getSampleRate()); Chris@116: Chris@116: RealTime duration = end - start; Chris@116: Chris@116: QString startStr, endStr, durationStr; Chris@116: startStr = start.toText(true).c_str(); Chris@116: endStr = end.toText(true).c_str(); Chris@116: durationStr = duration.toText(true).c_str(); Chris@116: Chris@117: if (haveSelection) { Chris@117: m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)") Chris@117: .arg(startStr).arg(endStr).arg(durationStr); Chris@117: } else { Chris@117: m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)") Chris@117: .arg(startStr).arg(endStr).arg(durationStr); Chris@117: } Chris@116: Chris@116: statusBar()->showMessage(m_myStatusMessage); Chris@116: } Chris@116: Chris@116: void Chris@0: MainWindow::outputLevelsChanged(float left, float right) Chris@0: { Chris@0: m_fader->setPeakLeft(left); Chris@0: m_fader->setPeakRight(right); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::sampleRateMismatch(size_t requested, size_t actual, Chris@0: bool willResample) Chris@0: { Chris@0: if (!willResample) { Chris@0: //!!! more helpful message needed Chris@0: QMessageBox::information Chris@0: (this, tr("Sample rate mismatch"), Chris@118: tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed and pitch.") Chris@0: .arg(requested).arg(actual)); Chris@0: } Chris@0: Chris@0: updateDescriptionLabel(); Chris@0: } Chris@0: Chris@0: void Chris@42: MainWindow::audioOverloadPluginDisabled() Chris@42: { Chris@42: QMessageBox::information Chris@42: (this, tr("Audio processing overload"), Chris@42: tr("Audio effects plugin auditioning has been disabled\ndue to a processing overload.")); Chris@42: } Chris@42: Chris@42: void Chris@137: MainWindow::layerAdded(Layer *) Chris@0: { Chris@0: // std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl; Chris@0: // setupExistingLayersMenu(); Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@137: MainWindow::layerRemoved(Layer *) Chris@0: { Chris@0: // std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl; Chris@95: setupExistingLayersMenus(); Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::layerAboutToBeDeleted(Layer *layer) Chris@0: { Chris@0: // std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl; Chris@0: if (layer == m_timeRulerLayer) { Chris@0: // std::cerr << "(this is the time ruler layer)" << std::endl; Chris@0: m_timeRulerLayer = 0; Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::layerInAView(Layer *layer, bool inAView) Chris@0: { Chris@0: // std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl; Chris@0: Chris@0: // Check whether we need to add or remove model from play source Chris@0: Model *model = layer->getModel(); Chris@0: if (model) { Chris@0: if (inAView) { Chris@0: m_playSource->addModel(model); Chris@0: } else { Chris@0: bool found = false; Chris@0: for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Chris@0: Pane *pane = m_paneStack->getPane(i); Chris@0: if (!pane) continue; Chris@0: for (int j = 0; j < pane->getLayerCount(); ++j) { Chris@0: Layer *pl = pane->getLayer(j); Chris@0: if (pl && pl->getModel() == model) { Chris@0: found = true; Chris@0: break; Chris@0: } Chris@0: } Chris@0: if (found) break; Chris@0: } Chris@0: if (!found) m_playSource->removeModel(model); Chris@0: } Chris@0: } Chris@0: Chris@95: setupExistingLayersMenus(); Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelAdded(Model *model) Chris@0: { Chris@0: // std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl; Chris@0: m_playSource->addModel(model); Chris@66: if (dynamic_cast(model)) { Chris@66: setupPaneAndLayerMenus(); Chris@66: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::mainModelChanged(WaveFileModel *model) Chris@0: { Chris@0: // std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl; Chris@0: updateDescriptionLabel(); Chris@0: m_panLayer->setModel(model); Chris@0: if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); Chris@46: if (model && !m_playTarget && m_audioOutput) createPlayTarget(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelAboutToBeDeleted(Model *model) Chris@0: { Chris@0: // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; Chris@180: if (model == m_viewManager->getPlaybackModel()) { Chris@180: m_viewManager->setPlaybackModel(0); Chris@180: } Chris@0: m_playSource->removeModel(model); Chris@91: FFTDataServer::modelAboutToBeDeleted(model); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelGenerationFailed(QString transformName) Chris@0: { Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to generate layer"), Chris@34: tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.") Chris@0: .arg(transformName), Chris@165: QMessageBox::Ok); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelRegenerationFailed(QString layerName, QString transformName) Chris@0: { Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to regenerate layer"), Chris@34: tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.") Chris@0: .arg(layerName).arg(transformName), Chris@165: QMessageBox::Ok); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position) Chris@0: { Chris@0: // std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl; Chris@0: m_paneStack->setCurrentPane(pane); Chris@0: m_rightButtonMenu->popup(position); Chris@0: } Chris@0: Chris@0: void Chris@73: MainWindow::propertyStacksResized() Chris@73: { Chris@73: /* Chris@73: std::cerr << "MainWindow::propertyStacksResized" << std::endl; Chris@73: Pane *pane = m_paneStack->getCurrentPane(); Chris@73: if (pane && m_overview) { Chris@73: std::cerr << "Fixed width: " << pane->width() << std::endl; Chris@73: m_overview->setFixedWidth(pane->width()); Chris@73: } Chris@73: */ Chris@73: } Chris@73: Chris@73: void Chris@0: MainWindow::showLayerTree() Chris@0: { Chris@177: if (!m_layerTreeView.isNull()) { Chris@177: m_layerTreeView->show(); Chris@177: m_layerTreeView->raise(); Chris@177: return; Chris@177: } Chris@177: Chris@177: //!!! should use an actual dialog class Chris@177: Chris@177: m_layerTreeView = new QTreeView(); Chris@0: LayerTreeModel *tree = new LayerTreeModel(m_paneStack); Chris@177: m_layerTreeView->resize(500, 300); //!!! Chris@177: m_layerTreeView->setModel(tree); Chris@177: m_layerTreeView->expandAll(); Chris@177: m_layerTreeView->show(); Chris@0: } Chris@0: Chris@0: void Chris@69: MainWindow::pollOSC() Chris@69: { Chris@70: if (!m_oscQueue || m_oscQueue->isEmpty()) return; Chris@69: std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; Chris@69: Chris@70: if (m_openingAudioFile) return; Chris@70: Chris@69: OSCMessage message = m_oscQueue->readMessage(); Chris@69: Chris@69: if (message.getTarget() != 0) { Chris@69: return; //!!! for now -- this class is target 0, others not handled yet Chris@69: } Chris@69: Chris@69: handleOSCMessage(message); Chris@69: } Chris@69: Chris@69: void Chris@69: MainWindow::handleOSCMessage(const OSCMessage &message) Chris@69: { Chris@70: std::cerr << "MainWindow::handleOSCMessage: thread id = " Chris@70: << QThread::currentThreadId() << std::endl; Chris@70: Chris@69: // This large function should really be abstracted out. Chris@69: Chris@69: if (message.getMethod() == "open") { Chris@69: Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: QString path = message.getArg(0).toString(); Chris@82: if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) { Chris@69: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@69: << path.toStdString() << "\"" << std::endl; Chris@69: } Chris@69: //!!! we really need to spin here and not return until the Chris@69: // file has been completely decoded... Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "openadditional") { Chris@69: Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: QString path = message.getArg(0).toString(); Chris@82: if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) { Chris@69: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@69: << path.toStdString() << "\"" << std::endl; Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "recent" || Chris@69: message.getMethod() == "last") { Chris@69: Chris@69: int n = 0; Chris@69: if (message.getMethod() == "recent" && Chris@69: message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::Int)) { Chris@69: n = message.getArg(0).toInt() - 1; Chris@69: } Chris@69: std::vector recent = m_recentFiles.getRecent(); Chris@137: if (n >= 0 && n < int(recent.size())) { Chris@82: if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) { Chris@69: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@69: << recent[n].toStdString() << "\"" << std::endl; Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "save") { Chris@69: Chris@69: QString path; Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: path = message.getArg(0).toString(); Chris@69: if (QFileInfo(path).exists()) { Chris@69: std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl; Chris@69: } else { Chris@69: saveSessionFile(path); Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "export") { Chris@69: Chris@69: QString path; Chris@69: if (getMainModel()) { Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: path = message.getArg(0).toString(); Chris@69: if (QFileInfo(path).exists()) { Chris@69: std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl; Chris@69: } else { Chris@69: WavFileWriter writer(path, Chris@69: getMainModel()->getSampleRate(), Chris@69: getMainModel()->getChannelCount()); Chris@69: MultiSelection ms = m_viewManager->getSelection(); Chris@69: if (!ms.getSelections().empty()) { Chris@69: writer.writeModel(getMainModel(), &ms); Chris@69: } else { Chris@69: writer.writeModel(getMainModel()); Chris@69: } Chris@69: } Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "jump" || Chris@69: message.getMethod() == "play") { Chris@69: Chris@69: if (getMainModel()) { Chris@69: Chris@69: unsigned long frame = m_viewManager->getPlaybackFrame(); Chris@69: bool selection = false; Chris@69: bool play = (message.getMethod() == "play"); Chris@69: Chris@69: if (message.getArgCount() == 1) { Chris@69: Chris@69: if (message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(0).toString() == "selection") { Chris@69: Chris@69: selection = true; Chris@69: Chris@69: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(0).toString() == "end") { Chris@69: Chris@69: frame = getMainModel()->getEndFrame(); Chris@69: Chris@69: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@69: Chris@69: double time = message.getArg(0).toDouble(); Chris@69: if (time < 0.0) time = 0.0; Chris@69: Chris@69: frame = lrint(time * getMainModel()->getSampleRate()); Chris@69: } Chris@69: } Chris@69: Chris@69: if (frame > getMainModel()->getEndFrame()) { Chris@69: frame = getMainModel()->getEndFrame(); Chris@69: } Chris@69: Chris@69: if (play) { Chris@69: m_viewManager->setPlaySelectionMode(selection); Chris@69: } Chris@69: Chris@69: if (selection) { Chris@69: MultiSelection::SelectionList sl = m_viewManager->getSelections(); Chris@69: if (!sl.empty()) { Chris@69: frame = sl.begin()->getStartFrame(); Chris@69: } Chris@69: } Chris@69: Chris@69: m_viewManager->setPlaybackFrame(frame); Chris@69: Chris@69: if (play && !m_playSource->isPlaying()) { Chris@69: m_playSource->play(frame); Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "stop") { Chris@69: Chris@69: if (m_playSource->isPlaying()) m_playSource->stop(); Chris@69: Chris@69: } else if (message.getMethod() == "loop") { Chris@69: Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: Chris@69: QString str = message.getArg(0).toString(); Chris@69: if (str == "on") { Chris@69: m_viewManager->setPlayLoopMode(true); Chris@69: } else if (str == "off") { Chris@69: m_viewManager->setPlayLoopMode(false); Chris@69: } Chris@69: } Chris@69: Chris@180: } else if (message.getMethod() == "solo") { Chris@180: Chris@180: if (message.getArgCount() == 1 && Chris@180: message.getArg(0).canConvert(QVariant::String)) { Chris@180: Chris@180: QString str = message.getArg(0).toString(); Chris@180: if (str == "on") { Chris@180: m_viewManager->setPlaySoloMode(true); Chris@180: } else if (str == "off") { Chris@180: m_viewManager->setPlaySoloMode(false); Chris@180: } Chris@180: } Chris@180: Chris@69: } else if (message.getMethod() == "select" || Chris@69: message.getMethod() == "addselect") { Chris@69: Chris@69: if (getMainModel()) { Chris@69: Chris@73: int f0 = getMainModel()->getStartFrame(); Chris@73: int f1 = getMainModel()->getEndFrame(); Chris@69: Chris@69: bool done = false; Chris@69: Chris@69: if (message.getArgCount() == 2 && Chris@69: message.getArg(0).canConvert(QVariant::Double) && Chris@69: message.getArg(1).canConvert(QVariant::Double)) { Chris@69: Chris@69: double t0 = message.getArg(0).toDouble(); Chris@69: double t1 = message.getArg(1).toDouble(); Chris@69: if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } Chris@69: if (t0 < 0.0) t0 = 0.0; Chris@69: if (t1 < 0.0) t1 = 0.0; Chris@69: Chris@69: f0 = lrint(t0 * getMainModel()->getSampleRate()); Chris@69: f1 = lrint(t1 * getMainModel()->getSampleRate()); Chris@73: Chris@73: Pane *pane = m_paneStack->getCurrentPane(); Chris@73: Layer *layer = 0; Chris@73: if (pane) layer = pane->getSelectedLayer(); Chris@73: if (layer) { Chris@73: size_t resolution; Chris@73: layer->snapToFeatureFrame(pane, f0, resolution, Chris@73: Layer::SnapLeft); Chris@73: layer->snapToFeatureFrame(pane, f1, resolution, Chris@73: Layer::SnapRight); Chris@73: } Chris@69: Chris@69: } else if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: Chris@69: QString str = message.getArg(0).toString(); Chris@69: if (str == "none") { Chris@69: m_viewManager->clearSelections(); Chris@69: done = true; Chris@69: } Chris@69: } Chris@69: Chris@69: if (!done) { Chris@69: if (message.getMethod() == "select") { Chris@69: m_viewManager->setSelection(Selection(f0, f1)); Chris@69: } else { Chris@69: m_viewManager->addSelection(Selection(f0, f1)); Chris@69: } Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "add") { Chris@69: Chris@69: if (getMainModel()) { Chris@69: Chris@69: if (message.getArgCount() >= 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: Chris@69: int channel = -1; Chris@69: if (message.getArgCount() == 2 && Chris@69: message.getArg(0).canConvert(QVariant::Int)) { Chris@69: channel = message.getArg(0).toInt(); Chris@69: if (channel < -1 || Chris@137: channel > int(getMainModel()->getChannelCount())) { Chris@69: std::cerr << "WARNING: MainWindow::handleOSCMessage: channel " Chris@69: << channel << " out of range" << std::endl; Chris@69: channel = -1; Chris@69: } Chris@69: } Chris@69: Chris@69: QString str = message.getArg(0).toString(); Chris@69: Chris@69: LayerFactory::LayerType type = Chris@69: LayerFactory::getInstance()->getLayerTypeForName(str); Chris@69: Chris@69: if (type == LayerFactory::UnknownLayer) { Chris@69: std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer " Chris@69: << "type " << str.toStdString() << std::endl; Chris@69: } else { Chris@69: Chris@69: PaneConfiguration configuration(type, Chris@69: getMainModel(), Chris@69: channel); Chris@69: Chris@69: addPane(configuration, Chris@69: tr("Add %1 Pane") Chris@69: .arg(LayerFactory::getInstance()-> Chris@69: getLayerPresentationName(type))); Chris@69: } Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "undo") { Chris@69: Chris@69: CommandHistory::getInstance()->undo(); Chris@69: Chris@69: } else if (message.getMethod() == "redo") { Chris@69: Chris@69: CommandHistory::getInstance()->redo(); Chris@69: Chris@69: } else if (message.getMethod() == "set") { Chris@69: Chris@70: if (message.getArgCount() == 2 && Chris@69: message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(1).canConvert(QVariant::Double)) { Chris@69: Chris@69: QString property = message.getArg(0).toString(); Chris@69: float value = (float)message.getArg(1).toDouble(); Chris@69: Chris@69: if (property == "gain") { Chris@69: if (value < 0.0) value = 0.0; Chris@69: m_fader->setValue(value); Chris@69: if (m_playTarget) m_playTarget->setOutputGain(value); Chris@69: } else if (property == "speedup") { Chris@69: m_playSpeed->setMappedValue(value); Chris@69: } else if (property == "overlays") { Chris@69: if (value < 0.5) { Chris@69: m_viewManager->setOverlayMode(ViewManager::NoOverlays); Chris@69: } else if (value < 1.5) { Chris@90: m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); Chris@90: } else if (value < 2.5) { Chris@90: m_viewManager->setOverlayMode(ViewManager::StandardOverlays); Chris@69: } else { Chris@69: m_viewManager->setOverlayMode(ViewManager::AllOverlays); Chris@69: } Chris@70: } else if (property == "zoomwheels") { Chris@70: m_viewManager->setZoomWheelsEnabled(value > 0.5); Chris@72: } else if (property == "propertyboxes") { Chris@72: bool toggle = ((value < 0.5) != Chris@72: (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks)); Chris@72: if (toggle) togglePropertyBoxes(); Chris@70: } Chris@70: Chris@70: } else { Chris@70: PropertyContainer *container = 0; Chris@70: Pane *pane = m_paneStack->getCurrentPane(); Chris@70: if (pane && Chris@70: message.getArgCount() == 3 && Chris@70: message.getArg(0).canConvert(QVariant::String) && Chris@70: message.getArg(1).canConvert(QVariant::String) && Chris@70: message.getArg(2).canConvert(QVariant::String)) { Chris@70: if (message.getArg(0).toString() == "pane") { Chris@70: container = pane->getPropertyContainer(0); Chris@70: } else if (message.getArg(0).toString() == "layer") { Chris@70: container = pane->getSelectedLayer(); Chris@70: } Chris@70: } Chris@70: if (container) { Chris@70: QString nameString = message.getArg(1).toString(); Chris@70: QString valueString = message.getArg(2).toString(); Chris@70: container->setPropertyWithCommand(nameString, valueString); Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "setcurrent") { Chris@69: Chris@69: int paneIndex = -1, layerIndex = -1; Chris@69: bool wantLayer = false; Chris@69: Chris@69: if (message.getArgCount() >= 1 && Chris@69: message.getArg(0).canConvert(QVariant::Int)) { Chris@69: Chris@69: paneIndex = message.getArg(0).toInt() - 1; Chris@69: Chris@69: if (message.getArgCount() >= 2 && Chris@69: message.getArg(1).canConvert(QVariant::Int)) { Chris@69: wantLayer = true; Chris@69: layerIndex = message.getArg(1).toInt() - 1; Chris@69: } Chris@69: } Chris@69: Chris@69: if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { Chris@69: Pane *pane = m_paneStack->getPane(paneIndex); Chris@70: m_paneStack->setCurrentPane(pane); Chris@69: if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { Chris@69: Layer *layer = pane->getLayer(layerIndex); Chris@69: m_paneStack->setCurrentLayer(pane, layer); Chris@69: } else if (wantLayer && layerIndex == -1) { Chris@69: m_paneStack->setCurrentLayer(pane, 0); Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "delete") { Chris@69: Chris@69: if (message.getArgCount() == 1 && Chris@69: message.getArg(0).canConvert(QVariant::String)) { Chris@69: Chris@69: QString target = message.getArg(0).toString(); Chris@69: Chris@69: if (target == "pane") { Chris@69: Chris@69: deleteCurrentPane(); Chris@69: Chris@69: } else if (target == "layer") { Chris@69: Chris@69: deleteCurrentLayer(); Chris@69: Chris@69: } else { Chris@69: Chris@69: std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl; Chris@69: } Chris@69: } Chris@69: Chris@69: } else if (message.getMethod() == "zoom") { Chris@69: Chris@69: if (message.getArgCount() == 1) { Chris@69: if (message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(0).toString() == "in") { Chris@69: zoomIn(); Chris@69: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(0).toString() == "out") { Chris@69: zoomOut(); Chris@69: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@69: message.getArg(0).toString() == "default") { Chris@69: zoomDefault(); Chris@69: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@69: double level = message.getArg(0).toDouble(); Chris@69: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@69: if (level < 1.0) level = 1.0; Chris@69: if (currentPane) currentPane->setZoomLevel(lrint(level)); Chris@69: } Chris@69: } Chris@69: Chris@73: } else if (message.getMethod() == "zoomvertical") { Chris@73: Chris@73: Pane *pane = m_paneStack->getCurrentPane(); Chris@73: Layer *layer = 0; Chris@73: if (pane && pane->getLayerCount() > 0) { Chris@73: layer = pane->getLayer(pane->getLayerCount() - 1); Chris@73: } Chris@73: int defaultStep = 0; Chris@73: int steps = 0; Chris@73: if (layer) { Chris@73: steps = layer->getVerticalZoomSteps(defaultStep); Chris@73: if (message.getArgCount() == 1 && steps > 0) { Chris@73: if (message.getArg(0).canConvert(QVariant::String) && Chris@73: message.getArg(0).toString() == "in") { Chris@73: int step = layer->getCurrentVerticalZoomStep() + 1; Chris@73: if (step < steps) layer->setVerticalZoomStep(step); Chris@73: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@73: message.getArg(0).toString() == "out") { Chris@73: int step = layer->getCurrentVerticalZoomStep() - 1; Chris@73: if (step >= 0) layer->setVerticalZoomStep(step); Chris@73: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@73: message.getArg(0).toString() == "default") { Chris@73: layer->setVerticalZoomStep(defaultStep); Chris@73: } Chris@73: } else if (message.getArgCount() == 2) { Chris@73: if (message.getArg(0).canConvert(QVariant::Double) && Chris@73: message.getArg(1).canConvert(QVariant::Double)) { Chris@73: double min = message.getArg(0).toDouble(); Chris@73: double max = message.getArg(1).toDouble(); Chris@73: layer->setDisplayExtents(min, max); Chris@73: } Chris@73: } Chris@73: } Chris@73: Chris@70: } else if (message.getMethod() == "quit") { Chris@70: Chris@70: m_abandoning = true; Chris@70: close(); Chris@70: Chris@70: } else if (message.getMethod() == "resize") { Chris@70: Chris@70: if (message.getArgCount() == 2) { Chris@70: Chris@70: int width = 0, height = 0; Chris@70: Chris@70: if (message.getArg(1).canConvert(QVariant::Int)) { Chris@70: Chris@70: height = message.getArg(1).toInt(); Chris@70: Chris@70: if (message.getArg(0).canConvert(QVariant::String) && Chris@70: message.getArg(0).toString() == "pane") { Chris@70: Chris@70: Pane *pane = m_paneStack->getCurrentPane(); Chris@70: if (pane) pane->resize(pane->width(), height); Chris@70: Chris@70: } else if (message.getArg(0).canConvert(QVariant::Int)) { Chris@70: Chris@70: width = message.getArg(0).toInt(); Chris@70: resize(width, height); Chris@70: } Chris@70: } Chris@70: } Chris@70: Chris@70: } else if (message.getMethod() == "transform") { Chris@70: Chris@70: Pane *pane = m_paneStack->getCurrentPane(); Chris@70: Chris@70: if (getMainModel() && Chris@70: pane && Chris@70: message.getArgCount() == 1 && Chris@70: message.getArg(0).canConvert(QVariant::String)) { Chris@70: Chris@107: TransformId transform = message.getArg(0).toString(); Chris@70: Chris@70: Layer *newLayer = m_document->createDerivedLayer Chris@70: (transform, Chris@70: getMainModel(), Chris@73: TransformFactory::getInstance()->getDefaultContextForTransform Chris@73: (transform, getMainModel()), Chris@70: ""); Chris@70: Chris@70: if (newLayer) { Chris@70: m_document->addLayerToView(pane, newLayer); Chris@70: m_recentTransforms.add(transform); Chris@70: m_paneStack->setCurrentLayer(pane, newLayer); Chris@70: } Chris@70: } Chris@70: Chris@69: } else { Chris@69: std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported " Chris@69: << "method \"" << message.getMethod().toStdString() Chris@69: << "\"" << std::endl; Chris@69: } Chris@69: Chris@69: } Chris@69: Chris@69: void Chris@0: MainWindow::preferences() Chris@0: { Chris@0: if (!m_preferencesDialog.isNull()) { Chris@0: m_preferencesDialog->show(); Chris@0: m_preferencesDialog->raise(); Chris@0: return; Chris@0: } Chris@0: Chris@0: m_preferencesDialog = new PreferencesDialog(this); Chris@0: Chris@0: // DeleteOnClose is safe here, because m_preferencesDialog is a Chris@0: // QPointer that will be zeroed when the dialog is deleted. We Chris@0: // use it in preference to leaving the dialog lying around because Chris@0: // if you Cancel the dialog, it resets the preferences state Chris@0: // without resetting its own widgets, so its state will be Chris@0: // incorrect when next shown unless we construct it afresh Chris@0: m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); Chris@0: Chris@0: m_preferencesDialog->show(); Chris@0: } Chris@0: Chris@0: void Chris@90: MainWindow::mouseEnteredWidget() Chris@90: { Chris@90: QWidget *w = dynamic_cast(sender()); Chris@90: if (!w) return; Chris@90: Chris@90: if (w == m_fader) { Chris@116: contextHelpChanged(tr("Adjust the master playback level")); Chris@90: } else if (w == m_playSpeed) { Chris@116: contextHelpChanged(tr("Adjust the master playback speed")); Chris@90: } else if (w == m_playSharpen && w->isEnabled()) { Chris@116: contextHelpChanged(tr("Toggle transient sharpening for playback time scaling")); Chris@90: } else if (w == m_playMono && w->isEnabled()) { Chris@116: contextHelpChanged(tr("Toggle mono mode for playback time scaling")); Chris@90: } Chris@90: } Chris@90: Chris@90: void Chris@90: MainWindow::mouseLeftWidget() Chris@90: { Chris@116: contextHelpChanged(""); Chris@116: } Chris@116: Chris@116: void Chris@117: MainWindow::inProgressSelectionChanged() Chris@117: { Chris@117: Pane *currentPane = 0; Chris@117: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@117: if (currentPane) updateVisibleRangeDisplay(currentPane); Chris@117: } Chris@117: Chris@117: void Chris@116: MainWindow::contextHelpChanged(const QString &s) Chris@116: { Chris@116: if (s == "" && m_myStatusMessage != "") { Chris@116: statusBar()->showMessage(m_myStatusMessage); Chris@116: return; Chris@116: } Chris@116: statusBar()->showMessage(s); Chris@90: } Chris@90: Chris@90: void Chris@0: MainWindow::website() Chris@0: { Chris@0: openHelpUrl(tr("http://www.sonicvisualiser.org/")); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::help() Chris@0: { Chris@126: openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/")); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openHelpUrl(QString url) Chris@0: { Chris@0: // This method mostly lifted from Qt Assistant source code Chris@0: Chris@0: QProcess *process = new QProcess(this); Chris@0: connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); Chris@0: Chris@0: QStringList args; Chris@0: Chris@0: #ifdef Q_OS_MAC Chris@0: args.append(url); Chris@0: process->start("open", args); Chris@0: #else Chris@0: #ifdef Q_OS_WIN32 Chris@0: Chris@0: QString pf(getenv("ProgramFiles")); Chris@0: QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); Chris@0: Chris@0: args.append(url); Chris@0: process->start(command, args); Chris@0: Chris@0: #else Chris@0: #ifdef Q_WS_X11 Chris@0: if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { Chris@0: args.append("exec"); Chris@0: args.append(url); Chris@0: process->start("kfmclient", args); Chris@0: } else if (!qgetenv("BROWSER").isEmpty()) { Chris@0: args.append(url); Chris@0: process->start(qgetenv("BROWSER"), args); Chris@0: } else { Chris@0: args.append(url); Chris@0: process->start("firefox", args); Chris@0: } Chris@0: #endif Chris@0: #endif Chris@0: #endif Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::about() Chris@0: { Chris@0: bool debug = false; Chris@0: QString version = "(unknown version)"; Chris@0: Chris@0: #ifdef BUILD_DEBUG Chris@0: debug = true; Chris@0: #endif Chris@0: #ifdef SV_VERSION Chris@0: #ifdef SVNREV Chris@0: version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV); Chris@0: #else Chris@0: version = tr("Release %1").arg(SV_VERSION); Chris@0: #endif Chris@0: #else Chris@0: #ifdef SVNREV Chris@0: version = tr("Unreleased : Revision %1").arg(SVNREV); Chris@0: #endif Chris@0: #endif Chris@0: Chris@0: QString aboutText; Chris@0: Chris@0: aboutText += tr("

About Sonic Visualiser

"); Chris@90: aboutText += tr("

Sonic Visualiser is a program for viewing and exploring audio data for
semantic music analysis and annotation.

"); Chris@111: aboutText += tr("

%1 : %2 configuration

") Chris@0: .arg(version) Chris@0: .arg(debug ? tr("Debug") : tr("Release")); Chris@0: Chris@132: #ifndef BUILD_STATIC Chris@132: aboutText += tr("
Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); Chris@132: #else Chris@132: #ifdef QT_SHARED Chris@132: aboutText += tr("
Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); Chris@132: #endif Chris@132: #endif Chris@132: Chris@0: #ifdef BUILD_STATIC Chris@0: aboutText += tr("

Statically linked"); Chris@0: #ifndef QT_SHARED Chris@0: aboutText += tr("
With Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); Chris@0: #endif Chris@0: #ifdef HAVE_JACK Chris@93: #ifdef JACK_VERSION Chris@0: aboutText += tr("
With JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With JACK audio output © Paul Davis and Jack O'Quin"); Chris@93: #endif Chris@0: #endif Chris@0: #ifdef HAVE_PORTAUDIO Chris@0: aboutText += tr("
With PortAudio audio output © Ross Bencina and Phil Burk"); Chris@0: #endif Chris@0: #ifdef HAVE_OGGZ Chris@93: #ifdef OGGZ_VERSION Chris@0: aboutText += tr("
With Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With Ogg file decoder © CSIRO Australia"); Chris@93: #endif Chris@0: #endif Chris@0: #ifdef HAVE_MAD Chris@93: #ifdef MAD_VERSION Chris@0: aboutText += tr("
With MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With MAD mp3 decoder © Underbit Technologies Inc"); Chris@93: #endif Chris@0: #endif Chris@0: #ifdef HAVE_SAMPLERATE Chris@93: #ifdef SAMPLERATE_VERSION Chris@0: aboutText += tr("
With libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With libsamplerate © Erik de Castro Lopo"); Chris@93: #endif Chris@0: #endif Chris@0: #ifdef HAVE_SNDFILE Chris@93: #ifdef SNDFILE_VERSION Chris@0: aboutText += tr("
With libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With libsndfile © Erik de Castro Lopo"); Chris@93: #endif Chris@0: #endif Chris@127: #ifdef HAVE_FFTW3F Chris@93: #ifdef FFTW3_VERSION Chris@0: aboutText += tr("
With FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With FFTW3 © Matteo Frigo and MIT"); Chris@93: #endif Chris@0: #endif Chris@0: #ifdef HAVE_VAMP Chris@114: aboutText += tr("
With Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); Chris@0: #endif Chris@0: aboutText += tr("
With LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); Chris@0: aboutText += tr("
With DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); Chris@69: #ifdef HAVE_LIBLO Chris@93: #ifdef LIBLO_VERSION Chris@69: aboutText += tr("
With liblo Lite OSC library (v%1) © Steve Harris").arg(LIBLO_VERSION); Chris@93: #else Chris@93: aboutText += tr("
With liblo Lite OSC library © Steve Harris").arg(LIBLO_VERSION); Chris@93: #endif Chris@70: if (m_oscQueue && m_oscQueue->isOK()) { Chris@69: aboutText += tr("

The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL()); Chris@69: } Chris@69: #endif Chris@0: aboutText += "

"; Chris@0: #endif Chris@0: Chris@0: aboutText += Chris@90: "

Sonic Visualiser Copyright © 2005 - 2007 Chris Cannam and
" Chris@90: "Queen Mary, University of London.

" Chris@0: "

This program is free software; you can redistribute it and/or
" Chris@0: "modify it under the terms of the GNU General Public License as
" Chris@0: "published by the Free Software Foundation; either version 2 of the
" Chris@0: "License, or (at your option) any later version.
See the file " Chris@0: "COPYING included with this distribution for more information.

"; Chris@0: Chris@0: QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText); Chris@0: } Chris@0: Chris@162: void Chris@162: MainWindow::keyReference() Chris@162: { Chris@162: m_keyReference->show(); Chris@162: } Chris@162: