Mercurial > hg > svapp
view framework/MainWindowBase.cpp @ 60:7b71da2d0631
* Some work on correct alignment when moving panes during playback
* Overhaul alignment for playback frame values (view manager now always
refers to reference-timeline values, only the play source deals in
playback model timeline values)
* When making a selection, ensure the selection regions shown in other
panes (and used for playback constraints if appropriate) are aligned
correctly. This may be the coolest feature ever implemented in any
program ever.
author | Chris Cannam |
---|---|
date | Thu, 22 Nov 2007 14:17:19 +0000 |
parents | eb596ef12041 |
children | 716e9d2f91c7 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006-2007 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "MainWindowBase.h" #include "Document.h" #include "view/Pane.h" #include "view/PaneStack.h" #include "data/model/WaveFileModel.h" #include "data/model/SparseOneDimensionalModel.h" #include "data/model/NoteModel.h" #include "data/model/Labeller.h" #include "view/ViewManager.h" #include "layer/WaveformLayer.h" #include "layer/TimeRulerLayer.h" #include "layer/TimeInstantLayer.h" #include "layer/TimeValueLayer.h" #include "layer/Colour3DPlotLayer.h" #include "layer/SliceLayer.h" #include "layer/SliceableLayer.h" #include "layer/ImageLayer.h" #include "widgets/ListInputDialog.h" #include "audioio/AudioCallbackPlaySource.h" #include "audioio/AudioCallbackPlayTarget.h" #include "audioio/AudioTargetFactory.h" #include "audioio/PlaySpeedRangeMapper.h" #include "data/fileio/DataFileReaderFactory.h" #include "data/fileio/PlaylistFileReader.h" #include "data/fileio/WavFileWriter.h" #include "data/fileio/CSVFileWriter.h" #include "data/fileio/MIDIFileWriter.h" #include "data/fileio/BZipFileDevice.h" #include "data/fileio/FileSource.h" #include "data/fft/FFTDataServer.h" #include "base/RecentFiles.h" #include "base/PlayParameterRepository.h" #include "base/XmlExportable.h" #include "base/CommandHistory.h" #include "base/Profiler.h" #include "base/Preferences.h" #include "data/osc/OSCQueue.h" #include <QApplication> #include <QMessageBox> #include <QGridLayout> #include <QLabel> #include <QAction> #include <QMenuBar> #include <QToolBar> #include <QInputDialog> #include <QStatusBar> #include <QTreeView> #include <QFile> #include <QFileInfo> #include <QDir> #include <QTextStream> #include <QProcess> #include <QShortcut> #include <QSettings> #include <QDateTime> #include <QProcess> #include <QCheckBox> #include <QRegExp> #include <QScrollArea> #include <iostream> #include <cstdio> #include <errno.h> using std::cerr; using std::endl; using std::vector; using std::map; using std::set; MainWindowBase::MainWindowBase(bool withAudioOutput, bool withOSCSupport) : m_document(0), m_paneStack(0), m_viewManager(0), m_timeRulerLayer(0), m_audioOutput(withAudioOutput), m_playSource(0), m_playTarget(0), m_oscQueue(withOSCSupport ? new OSCQueue() : 0), m_recentFiles("RecentFiles", 20), m_recentTransforms("RecentTransforms", 20), m_documentModified(false), m_openingAudioFile(false), m_abandoning(false), m_labeller(0) { connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), this, SLOT(documentModified())); connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), this, SLOT(documentRestored())); m_viewManager = new ViewManager(); connect(m_viewManager, SIGNAL(selectionChanged()), this, SLOT(updateMenuStates())); connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), this, SLOT(inProgressSelectionChanged())); Preferences::BackgroundMode mode = Preferences::getInstance()->getBackgroundMode(); m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); if (mode != Preferences::BackgroundFromTheme) { m_viewManager->setGlobalDarkBackground (mode == Preferences::DarkBackground); } m_paneStack = new PaneStack(0, m_viewManager); connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), this, SLOT(currentPaneChanged(Pane *))); connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), this, SLOT(currentLayerChanged(Pane *, Layer *))); connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), this, SLOT(contextHelpChanged(const QString &))); connect(m_paneStack, SIGNAL(paneAdded(Pane *)), this, SLOT(paneAdded(Pane *))); connect(m_paneStack, SIGNAL(paneHidden(Pane *)), this, SLOT(paneHidden(Pane *))); connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)), this, SLOT(paneAboutToBeDeleted(Pane *))); connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)), this, SLOT(paneDropAccepted(Pane *, QStringList))); connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)), this, SLOT(paneDropAccepted(Pane *, QString))); connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), this, SLOT(paneDeleteButtonClicked(Pane *))); m_playSource = new AudioCallbackPlaySource(m_viewManager, QApplication::applicationName()); connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), this, SLOT(sampleRateMismatch(size_t, size_t, bool))); connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), this, SLOT(audioOverloadPluginDisabled())); connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), this, SLOT(outputLevelsChanged(float, float))); connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), this, SLOT(playbackFrameChanged(unsigned long))); connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), this, SLOT(globalCentreFrameChanged(unsigned long))); connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), this, SLOT(viewCentreFrameChanged(View *, unsigned long))); connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); connect(Preferences::getInstance(), SIGNAL(propertyChanged(PropertyContainer::PropertyName)), this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); if (m_oscQueue && m_oscQueue->isOK()) { connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); QTimer *oscTimer = new QTimer(this); connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); oscTimer->start(1000); } Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; QSettings settings; settings.beginGroup("MainWindow"); labellerType = (Labeller::ValueType) settings.value("labellertype", (int)labellerType).toInt(); int cycle = settings.value("labellercycle", 4).toInt(); settings.endGroup(); m_labeller = new Labeller(labellerType); m_labeller->setCounterCycleSize(cycle); } MainWindowBase::~MainWindowBase() { delete m_playTarget; delete m_playSource; delete m_viewManager; delete m_oscQueue; Profiles::getInstance()->dump(); } QString MainWindowBase::getOpenFileName(FileFinder::FileType type) { FileFinder *ff = FileFinder::getInstance(); switch (type) { case FileFinder::SessionFile: return ff->getOpenFileName(type, m_sessionFile); case FileFinder::AudioFile: return ff->getOpenFileName(type, m_audioFile); case FileFinder::LayerFile: return ff->getOpenFileName(type, m_sessionFile); case FileFinder::LayerFileNoMidi: return ff->getOpenFileName(type, m_sessionFile); case FileFinder::SessionOrAudioFile: return ff->getOpenFileName(type, m_sessionFile); case FileFinder::ImageFile: return ff->getOpenFileName(type, m_sessionFile); case FileFinder::AnyFile: if (getMainModel() != 0 && m_paneStack != 0 && m_paneStack->getCurrentPane() != 0) { // can import a layer return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); } else { return ff->getOpenFileName(FileFinder::SessionOrAudioFile, m_sessionFile); } } return ""; } QString MainWindowBase::getSaveFileName(FileFinder::FileType type) { FileFinder *ff = FileFinder::getInstance(); switch (type) { case FileFinder::SessionFile: return ff->getSaveFileName(type, m_sessionFile); case FileFinder::AudioFile: return ff->getSaveFileName(type, m_audioFile); case FileFinder::LayerFile: return ff->getSaveFileName(type, m_sessionFile); case FileFinder::LayerFileNoMidi: return ff->getSaveFileName(type, m_sessionFile); case FileFinder::SessionOrAudioFile: return ff->getSaveFileName(type, m_sessionFile); case FileFinder::ImageFile: return ff->getSaveFileName(type, m_sessionFile); case FileFinder::AnyFile: return ff->getSaveFileName(type, m_sessionFile); } return ""; } void MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path) { FileFinder *ff = FileFinder::getInstance(); ff->registerLastOpenedFilePath(type, path); } void MainWindowBase::updateMenuStates() { Pane *currentPane = 0; Layer *currentLayer = 0; if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentLayer = currentPane->getSelectedLayer(); bool haveCurrentPane = (currentPane != 0); bool haveCurrentLayer = (haveCurrentPane && (currentLayer != 0)); bool haveMainModel = (getMainModel() != 0); bool havePlayTarget = (m_playTarget != 0); bool haveSelection = (m_viewManager && !m_viewManager->getSelections().empty()); bool haveCurrentEditableLayer = (haveCurrentLayer && currentLayer->isLayerEditable()); bool haveCurrentTimeInstantsLayer = (haveCurrentLayer && dynamic_cast<TimeInstantLayer *>(currentLayer)); bool haveCurrentColour3DPlot = (haveCurrentLayer && dynamic_cast<Colour3DPlotLayer *>(currentLayer)); bool haveClipboardContents = (m_viewManager && !m_viewManager->getClipboard().empty()); emit canAddPane(haveMainModel); emit canDeleteCurrentPane(haveCurrentPane); emit canZoom(haveMainModel && haveCurrentPane); emit canScroll(haveMainModel && haveCurrentPane); emit canAddLayer(haveMainModel && haveCurrentPane); emit canImportMoreAudio(haveMainModel); emit canImportLayer(haveMainModel && haveCurrentPane); emit canExportAudio(haveMainModel); emit canExportLayer(haveMainModel && (haveCurrentEditableLayer || haveCurrentColour3DPlot)); emit canExportImage(haveMainModel && haveCurrentPane); emit canDeleteCurrentLayer(haveCurrentLayer); emit canRenameLayer(haveCurrentLayer); emit canEditLayer(haveCurrentEditableLayer); emit canMeasureLayer(haveCurrentLayer); emit canSelect(haveMainModel && haveCurrentPane); emit canPlay(havePlayTarget); emit canFfwd(true); emit canRewind(true); emit canPaste(haveCurrentEditableLayer && haveClipboardContents); emit canInsertInstant(haveCurrentPane); emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); emit canClearSelection(haveSelection); emit canEditSelection(haveSelection && haveCurrentEditableLayer); emit canSave(m_sessionFile != "" && m_documentModified); } void MainWindowBase::documentModified() { // std::cerr << "MainWindowBase::documentModified" << std::endl; if (!m_documentModified) { //!!! this in subclass implementation? setWindowTitle(tr("%1 (modified)").arg(windowTitle())); } m_documentModified = true; updateMenuStates(); } void MainWindowBase::documentRestored() { // std::cerr << "MainWindowBase::documentRestored" << std::endl; if (m_documentModified) { //!!! this in subclass implementation? QString wt(windowTitle()); wt.replace(tr(" (modified)"), ""); setWindowTitle(wt); } m_documentModified = false; updateMenuStates(); } void MainWindowBase::playLoopToggled() { QAction *action = dynamic_cast<QAction *>(sender()); if (action) { m_viewManager->setPlayLoopMode(action->isChecked()); } else { m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); } } void MainWindowBase::playSelectionToggled() { QAction *action = dynamic_cast<QAction *>(sender()); if (action) { m_viewManager->setPlaySelectionMode(action->isChecked()); } else { m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); } } void MainWindowBase::playSoloToggled() { QAction *action = dynamic_cast<QAction *>(sender()); if (action) { m_viewManager->setPlaySoloMode(action->isChecked()); } else { m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); } if (m_viewManager->getPlaySoloMode()) { currentPaneChanged(m_paneStack->getCurrentPane()); } else { m_viewManager->setPlaybackModel(0); if (m_playSource) { m_playSource->clearSoloModelSet(); } } } void MainWindowBase::currentPaneChanged(Pane *p) { updateMenuStates(); updateVisibleRangeDisplay(p); if (!p) return; if (!(m_viewManager && m_playSource && m_viewManager->getPlaySoloMode())) { if (m_viewManager) m_viewManager->setPlaybackModel(0); return; } Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); int frame = m_playSource->getCurrentPlayingFrame(); std::cerr << "playing frame (in ref model) = " << frame << std::endl; View::ModelSet soloModels = p->getModels(); View::ModelSet sources; for (View::ModelSet::iterator mi = soloModels.begin(); mi != soloModels.end(); ++mi) { if (*mi && (*mi)->getSourceModel()) { sources.insert((*mi)->getSourceModel()); } } for (View::ModelSet::iterator mi = sources.begin(); mi != sources.end(); ++mi) { soloModels.insert(*mi); } //!!! Need an "atomic" way of telling the play source that the //playback model has changed, and changing it on ViewManager -- //the play source should be making the setPlaybackModel call to //ViewManager for (View::ModelSet::iterator mi = soloModels.begin(); mi != soloModels.end(); ++mi) { if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) { m_viewManager->setPlaybackModel(*mi); } } RangeSummarisableTimeValueModel *a = dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); RangeSummarisableTimeValueModel *b = dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> getPlaybackModel()); m_playSource->setSoloModelSet(soloModels); if (a && b && (a != b)) { /*!!! int rframe = a->alignToReference(frame); int bframe = b->alignFromReference(rframe); if (m_playSource->isPlaying()) m_playSource->play(bframe); */ if (m_playSource->isPlaying()) m_playSource->play(frame); } } void MainWindowBase::currentLayerChanged(Pane *p, Layer *) { updateMenuStates(); updateVisibleRangeDisplay(p); } void MainWindowBase::selectAll() { if (!getMainModel()) return; m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), getMainModel()->getEndFrame())); } void MainWindowBase::selectToStart() { if (!getMainModel()) return; m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), m_viewManager->getGlobalCentreFrame())); } void MainWindowBase::selectToEnd() { if (!getMainModel()) return; m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), getMainModel()->getEndFrame())); } void MainWindowBase::selectVisible() { Model *model = getMainModel(); if (!model) return; Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; size_t startFrame, endFrame; if (currentPane->getStartFrame() < 0) startFrame = 0; else startFrame = currentPane->getStartFrame(); if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); else endFrame = currentPane->getEndFrame(); m_viewManager->setSelection(Selection(startFrame, endFrame)); } void MainWindowBase::clearSelection() { m_viewManager->clearSelections(); } void MainWindowBase::cut() { Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; Layer *layer = currentPane->getSelectedLayer(); if (!layer) return; Clipboard &clipboard = m_viewManager->getClipboard(); clipboard.clear(); MultiSelection::SelectionList selections = m_viewManager->getSelections(); CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); for (MultiSelection::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { layer->copy(*i, clipboard); layer->deleteSelection(*i); } CommandHistory::getInstance()->endCompoundOperation(); } void MainWindowBase::copy() { Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; Layer *layer = currentPane->getSelectedLayer(); if (!layer) return; Clipboard &clipboard = m_viewManager->getClipboard(); clipboard.clear(); MultiSelection::SelectionList selections = m_viewManager->getSelections(); for (MultiSelection::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { layer->copy(*i, clipboard); } } void MainWindowBase::paste() { Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; //!!! if we have no current layer, we should create one of the most // appropriate type Layer *layer = currentPane->getSelectedLayer(); if (!layer) return; Clipboard &clipboard = m_viewManager->getClipboard(); Clipboard::PointList contents = clipboard.getPoints(); /* long minFrame = 0; bool have = false; for (int i = 0; i < contents.size(); ++i) { if (!contents[i].haveFrame()) continue; if (!have || contents[i].getFrame() < minFrame) { minFrame = contents[i].getFrame(); have = true; } } long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; layer->paste(clipboard, frameOffset); */ layer->paste(clipboard, 0, true); } void MainWindowBase::deleteSelected() { if (m_paneStack->getCurrentPane() && m_paneStack->getCurrentPane()->getSelectedLayer()) { Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); if (m_viewManager && (m_viewManager->getToolMode() == ViewManager::MeasureMode)) { layer->deleteCurrentMeasureRect(); } else { MultiSelection::SelectionList selections = m_viewManager->getSelections(); for (MultiSelection::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { layer->deleteSelection(*i); } } } } void MainWindowBase::insertInstant() { int frame = m_viewManager->getPlaybackFrame(); insertInstantAt(frame); } void MainWindowBase::insertInstantsAtBoundaries() { MultiSelection::SelectionList selections = m_viewManager->getSelections(); for (MultiSelection::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { size_t start = i->getStartFrame(); size_t end = i->getEndFrame(); if (start != end) { insertInstantAt(i->getStartFrame()); insertInstantAt(i->getEndFrame()); } } } void MainWindowBase::insertInstantAt(size_t frame) { Pane *pane = m_paneStack->getCurrentPane(); if (!pane) { return; } Layer *layer = dynamic_cast<TimeInstantLayer *> (pane->getSelectedLayer()); if (!layer) { for (int i = pane->getLayerCount(); i > 0; --i) { layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); if (layer) break; } if (!layer) { CommandHistory::getInstance()->startCompoundOperation (tr("Add Point"), true); layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); if (layer) { m_document->addLayerToView(pane, layer); m_paneStack->setCurrentLayer(pane, layer); } CommandHistory::getInstance()->endCompoundOperation(); } } if (layer) { Model *model = layer->getModel(); SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> (model); if (sodm) { SparseOneDimensionalModel::Point point(frame, ""); SparseOneDimensionalModel::Point prevPoint(0); bool havePrevPoint = false; SparseOneDimensionalModel::EditCommand *command = new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); if (m_labeller->actingOnPrevPoint()) { SparseOneDimensionalModel::PointList prevPoints = sodm->getPreviousPoints(frame); if (!prevPoints.empty()) { prevPoint = *prevPoints.begin(); havePrevPoint = true; } } if (m_labeller) { m_labeller->setSampleRate(sodm->getSampleRate()); if (havePrevPoint) { command->deletePoint(prevPoint); } m_labeller->label<SparseOneDimensionalModel::Point> (point, havePrevPoint ? &prevPoint : 0); if (havePrevPoint) { command->addPoint(prevPoint); } } command->addPoint(point); command->setName(tr("Add Point at %1 s") .arg(RealTime::frame2RealTime (frame, sodm->getSampleRate()) .toText(false).c_str())); command->finish(); } } } void MainWindowBase::renumberInstants() { Pane *pane = m_paneStack->getCurrentPane(); if (!pane) return; Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer()); if (!layer) return; MultiSelection ms(m_viewManager->getSelection()); Model *model = layer->getModel(); SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> (model); if (!sodm) return; if (!m_labeller) return; Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); // This uses a command labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); } MainWindowBase::FileOpenStatus MainWindowBase::open(QString fileOrUrl, AudioFileOpenMode mode) { return open(FileSource(fileOrUrl, true), mode); } MainWindowBase::FileOpenStatus MainWindowBase::open(FileSource source, AudioFileOpenMode mode) { FileOpenStatus status; if (!source.isAvailable()) return FileOpenFailed; source.waitForData(); bool canImportLayer = (getMainModel() != 0 && m_paneStack != 0 && m_paneStack->getCurrentPane() != 0); if ((status = openAudio(source, mode)) != FileOpenFailed) { return status; } else if ((status = openSession(source)) != FileOpenFailed) { return status; } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) { return status; } else if (!canImportLayer) { return FileOpenWrongMode; } else if ((status = openImage(source)) != FileOpenFailed) { return status; } else if ((status = openLayer(source)) != FileOpenFailed) { return status; } else { return FileOpenFailed; } } MainWindowBase::FileOpenStatus MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode) { std::cerr << "MainWindowBase::openAudio(" << source.getLocation().toStdString() << ")" << std::endl; if (!source.isAvailable()) return FileOpenFailed; source.waitForData(); m_openingAudioFile = true; size_t rate = 0; if (Preferences::getInstance()->getResampleOnLoad()) { rate = m_playSource->getSourceSampleRate(); } WaveFileModel *newModel = new WaveFileModel(source, rate); if (!newModel->isOK()) { delete newModel; m_openingAudioFile = false; return FileOpenFailed; } std::cerr << "mode = " << mode << std::endl; if (mode == AskUser) { if (getMainModel()) { static bool prevSetAsMain = true; bool setAsMain = true; QStringList items; items << tr("Replace the existing main waveform") << tr("Load this file into a new waveform pane"); bool ok = false; QString item = ListInputDialog::getItem (this, tr("Select target for import"), tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), items, prevSetAsMain ? 0 : 1, &ok); if (!ok || item.isEmpty()) { delete newModel; m_openingAudioFile = false; return FileOpenCancelled; } setAsMain = (item == items[0]); prevSetAsMain = setAsMain; if (setAsMain) mode = ReplaceMainModel; else mode = CreateAdditionalModel; } else { mode = ReplaceMainModel; } } if (mode == ReplaceCurrentPane) { Pane *pane = m_paneStack->getCurrentPane(); if (pane) { if (getMainModel()) { View::ModelSet models(pane->getModels()); if (models.find(getMainModel()) != models.end()) { mode = ReplaceMainModel; } } else { mode = ReplaceMainModel; } } else { mode = CreateAdditionalModel; } } if (mode == CreateAdditionalModel && !getMainModel()) { mode = ReplaceMainModel; } if (mode == ReplaceMainModel) { Model *prevMain = getMainModel(); if (prevMain) { m_playSource->removeModel(prevMain); PlayParameterRepository::getInstance()->removeModel(prevMain); } PlayParameterRepository::getInstance()->addModel(newModel); m_document->setMainModel(newModel); setupMenus(); if (m_sessionFile == "") { //!!! shouldn't be dealing directly with title from here -- call a method setWindowTitle(tr("%1: %2") .arg(QApplication::applicationName()) .arg(source.getLocation())); CommandHistory::getInstance()->clear(); CommandHistory::getInstance()->documentSaved(); m_documentModified = false; } else { setWindowTitle(tr("%1: %2 [%3]") .arg(QApplication::applicationName()) .arg(QFileInfo(m_sessionFile).fileName()) .arg(source.getLocation())); if (m_documentModified) { m_documentModified = false; documentModified(); // so as to restore "(modified)" window title } } if (!source.isRemote()) m_audioFile = source.getLocalFilename(); } else if (mode == CreateAdditionalModel) { CommandHistory::getInstance()->startCompoundOperation (tr("Import \"%1\"").arg(source.getLocation()), true); m_document->addImportedModel(newModel); AddPaneCommand *command = new AddPaneCommand(this); CommandHistory::getInstance()->addCommand(command); Pane *pane = command->getPane(); if (m_timeRulerLayer) { m_document->addLayerToView(pane, m_timeRulerLayer); } Layer *newLayer = m_document->createImportedLayer(newModel); if (newLayer) { m_document->addLayerToView(pane, newLayer); } CommandHistory::getInstance()->endCompoundOperation(); } else if (mode == ReplaceCurrentPane) { // We know there is a current pane, otherwise we would have // reset the mode to CreateAdditionalModel above; and we know // the current pane does not contain the main model, otherwise // we would have reset it to ReplaceMainModel. But we don't // know whether the pane contains a waveform model at all. Pane *pane = m_paneStack->getCurrentPane(); Layer *replace = 0; for (int i = 0; i < pane->getLayerCount(); ++i) { Layer *layer = pane->getLayer(i); if (dynamic_cast<WaveformLayer *>(layer)) { replace = layer; break; } } CommandHistory::getInstance()->startCompoundOperation (tr("Import \"%1\"").arg(source.getLocation()), true); m_document->addImportedModel(newModel); if (replace) { m_document->removeLayerFromView(pane, replace); } Layer *newLayer = m_document->createImportedLayer(newModel); if (newLayer) { m_document->addLayerToView(pane, newLayer); } CommandHistory::getInstance()->endCompoundOperation(); } updateMenuStates(); m_recentFiles.addFile(source.getLocation()); if (!source.isRemote()) { // for file dialog registerLastOpenedFilePath(FileFinder::AudioFile, source.getLocalFilename()); } m_openingAudioFile = false; currentPaneChanged(m_paneStack->getCurrentPane()); return FileOpenSucceeded; } MainWindowBase::FileOpenStatus MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode) { std::set<QString> extensions; PlaylistFileReader::getSupportedExtensions(extensions); QString extension = source.getExtension(); if (extensions.find(extension) == extensions.end()) return FileOpenFailed; if (!source.isAvailable()) return FileOpenFailed; source.waitForData(); PlaylistFileReader reader(source.getLocalFilename()); if (!reader.isOK()) return FileOpenFailed; PlaylistFileReader::Playlist playlist = reader.load(); bool someSuccess = false; for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); i != playlist.end(); ++i) { FileOpenStatus status = openAudio(*i, mode); if (status == FileOpenCancelled) { return FileOpenCancelled; } if (status == FileOpenSucceeded) { someSuccess = true; mode = CreateAdditionalModel; } } if (someSuccess) return FileOpenSucceeded; else return FileOpenFailed; } MainWindowBase::FileOpenStatus MainWindowBase::openLayer(FileSource source) { Pane *pane = m_paneStack->getCurrentPane(); if (!pane) { // shouldn't happen, as the menu action should have been disabled std::cerr << "WARNING: MainWindowBase::openLayer: no current pane" << std::endl; return FileOpenWrongMode; } if (!getMainModel()) { // shouldn't happen, as the menu action should have been disabled std::cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << std::endl; return FileOpenWrongMode; } if (!source.isAvailable()) return FileOpenFailed; source.waitForData(); QString path = source.getLocalFilename(); if (source.getExtension() == "svl" || source.getExtension() == "xml") { PaneCallback callback(this); QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { std::cerr << "ERROR: MainWindowBase::openLayer(" << source.getLocation().toStdString() << "): Failed to open file for reading" << std::endl; return FileOpenFailed; } SVFileReader reader(m_document, callback, source.getLocation()); reader.setCurrentPane(pane); QXmlInputSource inputSource(&file); reader.parse(inputSource); if (!reader.isOK()) { std::cerr << "ERROR: MainWindowBase::openLayer(" << source.getLocation().toStdString() << "): Failed to read XML file: " << reader.getErrorString().toStdString() << std::endl; return FileOpenFailed; } m_recentFiles.addFile(source.getLocation()); if (!source.isRemote()) { registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog } } else { try { Model *model = DataFileReaderFactory::load (path, getMainModel()->getSampleRate()); if (model) { std::cerr << "MainWindowBase::openLayer: Have model" << std::endl; Layer *newLayer = m_document->createImportedLayer(model); if (newLayer) { m_document->addLayerToView(pane, newLayer); m_recentFiles.addFile(source.getLocation()); if (!source.isRemote()) { registerLastOpenedFilePath (FileFinder::LayerFile, path); // for file dialog } return FileOpenSucceeded; } } } catch (DataFileReaderFactory::Exception e) { if (e == DataFileReaderFactory::ImportCancelled) { return FileOpenCancelled; } } } source.setLeaveLocalFile(true); return FileOpenFailed; } MainWindowBase::FileOpenStatus MainWindowBase::openImage(FileSource source) { Pane *pane = m_paneStack->getCurrentPane(); if (!pane) { // shouldn't happen, as the menu action should have been disabled std::cerr << "WARNING: MainWindowBase::openImage: no current pane" << std::endl; return FileOpenWrongMode; } if (!m_document->getMainModel()) { return FileOpenWrongMode; } bool newLayer = false; ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer()); if (!il) { for (int i = pane->getLayerCount()-1; i >= 0; --i) { il = dynamic_cast<ImageLayer *>(pane->getLayer(i)); if (il) break; } } if (!il) { il = dynamic_cast<ImageLayer *> (m_document->createEmptyLayer(LayerFactory::Image)); if (!il) return FileOpenFailed; newLayer = true; } // We don't put the image file in Recent Files std::cerr << "openImage: trying location \"" << source.getLocation().toStdString() << "\" in image layer" << std::endl; if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) { if (newLayer) { m_document->deleteLayer(il); // also releases its model } return FileOpenFailed; } else { if (newLayer) { m_document->addLayerToView(pane, il); } m_paneStack->setCurrentLayer(pane, il); } return FileOpenSucceeded; } MainWindowBase::FileOpenStatus MainWindowBase::openSessionFile(QString fileOrUrl) { return openSession(FileSource(fileOrUrl, true)); } MainWindowBase::FileOpenStatus MainWindowBase::openSession(FileSource source) { if (!source.isAvailable()) return FileOpenFailed; if (source.getExtension() != "sv") return FileOpenFailed; source.waitForData(); BZipFileDevice bzFile(source.getLocalFilename()); if (!bzFile.open(QIODevice::ReadOnly)) return FileOpenFailed; if (!checkSaveModified()) return FileOpenCancelled; QString error; closeSession(); createDocument(); PaneCallback callback(this); m_viewManager->clearSelections(); SVFileReader reader(m_document, callback, source.getLocation()); QXmlInputSource inputSource(&bzFile); reader.parse(inputSource); if (!reader.isOK()) { error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); } bzFile.close(); bool ok = (error == ""); if (ok) { setWindowTitle(tr("%1: %2") .arg(QApplication::applicationName()) .arg(source.getLocation())); if (!source.isRemote()) m_sessionFile = source.getLocalFilename(); setupMenus(); CommandHistory::getInstance()->clear(); CommandHistory::getInstance()->documentSaved(); m_documentModified = false; updateMenuStates(); m_recentFiles.addFile(source.getLocation()); if (!source.isRemote()) { // for file dialog registerLastOpenedFilePath(FileFinder::SessionFile, source.getLocalFilename()); } } else { setWindowTitle(QApplication::applicationName()); } return ok ? FileOpenSucceeded : FileOpenFailed; } void MainWindowBase::createPlayTarget() { if (m_playTarget) return; m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); if (!m_playTarget) { QMessageBox::warning (this, tr("Couldn't open audio device"), tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Audio playback will not be available during this session."), QMessageBox::Ok); } } WaveFileModel * MainWindowBase::getMainModel() { if (!m_document) return 0; return m_document->getMainModel(); } const WaveFileModel * MainWindowBase::getMainModel() const { if (!m_document) return 0; return m_document->getMainModel(); } void MainWindowBase::createDocument() { m_document = new Document; connect(m_document, SIGNAL(layerAdded(Layer *)), this, SLOT(layerAdded(Layer *))); connect(m_document, SIGNAL(layerRemoved(Layer *)), this, SLOT(layerRemoved(Layer *))); connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), this, SLOT(layerAboutToBeDeleted(Layer *))); connect(m_document, SIGNAL(layerInAView(Layer *, bool)), this, SLOT(layerInAView(Layer *, bool))); connect(m_document, SIGNAL(modelAdded(Model *)), this, SLOT(modelAdded(Model *))); connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), this, SLOT(mainModelChanged(WaveFileModel *))); connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), this, SLOT(modelAboutToBeDeleted(Model *))); connect(m_document, SIGNAL(modelGenerationFailed(QString)), this, SLOT(modelGenerationFailed(QString))); connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), this, SLOT(modelRegenerationFailed(QString, QString))); } bool MainWindowBase::saveSessionFile(QString path) { BZipFileDevice bzFile(path); if (!bzFile.open(QIODevice::WriteOnly)) { std::cerr << "Failed to open session file \"" << path.toStdString() << "\" for writing: " << bzFile.errorString().toStdString() << std::endl; return false; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QTextStream out(&bzFile); toXml(out); out.flush(); QApplication::restoreOverrideCursor(); if (!bzFile.isOK()) { QMessageBox::critical(this, tr("Failed to write file"), tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") .arg(path).arg(bzFile.errorString())); bzFile.close(); return false; } bzFile.close(); return true; } void MainWindowBase::toXml(QTextStream &out) { QString indent(" "); out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; out << "<!DOCTYPE sonic-visualiser>\n"; out << "<sv>\n"; m_document->toXml(out, "", ""); out << "<display>\n"; out << QString(" <window width=\"%1\" height=\"%2\"/>\n") .arg(width()).arg(height()); for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Pane *pane = m_paneStack->getPane(i); if (pane) { pane->toXml(out, indent); } } out << "</display>\n"; m_viewManager->getSelection().toXml(out); out << "</sv>\n"; } Pane * MainWindowBase::addPaneToStack() { AddPaneCommand *command = new AddPaneCommand(this); CommandHistory::getInstance()->addCommand(command); Pane *pane = command->getPane(); return pane; } void MainWindowBase::zoomIn() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->zoom(true); } void MainWindowBase::zoomOut() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->zoom(false); } void MainWindowBase::zoomToFit() { Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; Model *model = getMainModel(); if (!model) return; size_t start = model->getStartFrame(); size_t end = model->getEndFrame(); if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame()); size_t pixels = currentPane->width(); size_t sw = currentPane->getVerticalScaleWidth(); if (pixels > sw * 2) pixels -= sw * 2; else pixels = 1; if (pixels > 4) pixels -= 4; size_t zoomLevel = (end - start) / pixels; currentPane->setZoomLevel(zoomLevel); currentPane->setCentreFrame((start + end) / 2); } void MainWindowBase::zoomDefault() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->setZoomLevel(1024); } void MainWindowBase::scrollLeft() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->scroll(false, false); } void MainWindowBase::jumpLeft() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->scroll(false, true); } void MainWindowBase::scrollRight() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->scroll(true, false); } void MainWindowBase::jumpRight() { Pane *currentPane = m_paneStack->getCurrentPane(); if (currentPane) currentPane->scroll(true, true); } void MainWindowBase::showNoOverlays() { m_viewManager->setOverlayMode(ViewManager::NoOverlays); } void MainWindowBase::showMinimalOverlays() { m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); } void MainWindowBase::showStandardOverlays() { m_viewManager->setOverlayMode(ViewManager::StandardOverlays); } void MainWindowBase::showAllOverlays() { m_viewManager->setOverlayMode(ViewManager::AllOverlays); } void MainWindowBase::toggleZoomWheels() { if (m_viewManager->getZoomWheelsEnabled()) { m_viewManager->setZoomWheelsEnabled(false); } else { m_viewManager->setZoomWheelsEnabled(true); } } void MainWindowBase::togglePropertyBoxes() { if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { if (Preferences::getInstance()->getPropertyBoxLayout() == Preferences::VerticallyStacked) { m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); } else { m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); } } else { m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); } } void MainWindowBase::toggleStatusBar() { QSettings settings; settings.beginGroup("MainWindow"); bool sb = settings.value("showstatusbar", true).toBool(); if (sb) { statusBar()->hide(); } else { statusBar()->show(); } settings.setValue("showstatusbar", !sb); settings.endGroup(); } void MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name) { if (name == "Property Box Layout") { if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { if (Preferences::getInstance()->getPropertyBoxLayout() == Preferences::VerticallyStacked) { m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); } else { m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); } } } else if (name == "Background Mode" && m_viewManager) { Preferences::BackgroundMode mode = Preferences::getInstance()->getBackgroundMode(); if (mode == Preferences::BackgroundFromTheme) { m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); } else if (mode == Preferences::DarkBackground) { m_viewManager->setGlobalDarkBackground(true); } else { m_viewManager->setGlobalDarkBackground(false); } } } void MainWindowBase::play() { if (m_playSource->isPlaying()) { stop(); } else { playbackFrameChanged(m_viewManager->getPlaybackFrame()); m_playSource->play(m_viewManager->getPlaybackFrame()); } } void MainWindowBase::ffwd() { if (!getMainModel()) return; int frame = m_viewManager->getPlaybackFrame(); ++frame; Layer *layer = getSnapLayer(); size_t sr = getMainModel()->getSampleRate(); if (!layer) { frame = RealTime::realTime2Frame (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr); if (frame > int(getMainModel()->getEndFrame())) { frame = getMainModel()->getEndFrame(); } } else { size_t resolution = 0; if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), frame, resolution, Layer::SnapRight)) { frame = getMainModel()->getEndFrame(); } } if (frame < 0) frame = 0; if (m_viewManager->getPlaySelectionMode()) { frame = m_viewManager->constrainFrameToSelection(size_t(frame)); } m_viewManager->setPlaybackFrame(frame); } void MainWindowBase::ffwdEnd() { if (!getMainModel()) return; size_t frame = getMainModel()->getEndFrame(); if (m_viewManager->getPlaySelectionMode()) { frame = m_viewManager->constrainFrameToSelection(frame); } m_viewManager->setPlaybackFrame(frame); } void MainWindowBase::rewind() { if (!getMainModel()) return; int frame = m_viewManager->getPlaybackFrame(); if (frame > 0) --frame; Layer *layer = getSnapLayer(); size_t sr = getMainModel()->getSampleRate(); // when rewinding during playback, we want to allow a period // following a rewind target point at which the rewind will go to // the prior point instead of the immediately neighbouring one if (m_playSource && m_playSource->isPlaying()) { RealTime ct = RealTime::frame2RealTime(frame, sr); ct = ct - RealTime::fromSeconds(0.25); if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; // std::cerr << "rewind: frame " << frame << " -> "; frame = RealTime::realTime2Frame(ct, sr); // std::cerr << frame << std::endl; } if (!layer) { frame = RealTime::realTime2Frame (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr); if (frame < int(getMainModel()->getStartFrame())) { frame = getMainModel()->getStartFrame(); } } else { size_t resolution = 0; if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), frame, resolution, Layer::SnapLeft)) { frame = getMainModel()->getStartFrame(); } } if (frame < 0) frame = 0; if (m_viewManager->getPlaySelectionMode()) { frame = m_viewManager->constrainFrameToSelection(size_t(frame)); } m_viewManager->setPlaybackFrame(frame); } void MainWindowBase::rewindStart() { if (!getMainModel()) return; size_t frame = getMainModel()->getStartFrame(); if (m_viewManager->getPlaySelectionMode()) { frame = m_viewManager->constrainFrameToSelection(frame); } m_viewManager->setPlaybackFrame(frame); } Layer * MainWindowBase::getSnapLayer() const { Pane *pane = m_paneStack->getCurrentPane(); if (!pane) return 0; Layer *layer = pane->getSelectedLayer(); if (!dynamic_cast<TimeInstantLayer *>(layer) && !dynamic_cast<TimeValueLayer *>(layer) && !dynamic_cast<TimeRulerLayer *>(layer)) { layer = 0; for (int i = pane->getLayerCount(); i > 0; --i) { Layer *l = pane->getLayer(i-1); if (dynamic_cast<TimeRulerLayer *>(l)) { layer = l; break; } } } return layer; } void MainWindowBase::stop() { m_playSource->stop(); if (m_paneStack && m_paneStack->getCurrentPane()) { updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); } else { m_myStatusMessage = ""; statusBar()->showMessage(""); } } MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) : m_mw(mw), m_pane(0), m_prevCurrentPane(0), m_added(false) { } MainWindowBase::AddPaneCommand::~AddPaneCommand() { if (m_pane && !m_added) { m_mw->m_paneStack->deletePane(m_pane); } } QString MainWindowBase::AddPaneCommand::getName() const { return tr("Add Pane"); } void MainWindowBase::AddPaneCommand::execute() { if (!m_pane) { m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); m_pane = m_mw->m_paneStack->addPane(); connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), m_mw, SLOT(contextHelpChanged(const QString &))); } else { m_mw->m_paneStack->showPane(m_pane); } m_mw->m_paneStack->setCurrentPane(m_pane); m_added = true; } void MainWindowBase::AddPaneCommand::unexecute() { m_mw->m_paneStack->hidePane(m_pane); m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); m_added = false; } MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) : m_mw(mw), m_pane(pane), m_added(true) { } MainWindowBase::RemovePaneCommand::~RemovePaneCommand() { if (m_pane && !m_added) { m_mw->m_paneStack->deletePane(m_pane); } } QString MainWindowBase::RemovePaneCommand::getName() const { return tr("Remove Pane"); } void MainWindowBase::RemovePaneCommand::execute() { m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); m_mw->m_paneStack->hidePane(m_pane); m_added = false; } void MainWindowBase::RemovePaneCommand::unexecute() { m_mw->m_paneStack->showPane(m_pane); m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); m_added = true; } void MainWindowBase::deleteCurrentPane() { CommandHistory::getInstance()->startCompoundOperation (tr("Delete Pane"), true); Pane *pane = m_paneStack->getCurrentPane(); if (pane) { while (pane->getLayerCount() > 0) { Layer *layer = pane->getLayer(0); if (layer) { m_document->removeLayerFromView(pane, layer); } else { break; } } RemovePaneCommand *command = new RemovePaneCommand(this, pane); CommandHistory::getInstance()->addCommand(command); } CommandHistory::getInstance()->endCompoundOperation(); updateMenuStates(); } void MainWindowBase::deleteCurrentLayer() { Pane *pane = m_paneStack->getCurrentPane(); if (pane) { Layer *layer = pane->getSelectedLayer(); if (layer) { m_document->removeLayerFromView(pane, layer); } } updateMenuStates(); } void MainWindowBase::playbackFrameChanged(unsigned long frame) { if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; RealTime now = RealTime::frame2RealTime (frame, getMainModel()->getSampleRate()); if (now.sec == m_lastPlayStatusSec) return; RealTime then = RealTime::frame2RealTime (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); QString nowStr; QString thenStr; QString remainingStr; if (then.sec > 10) { nowStr = now.toSecText().c_str(); thenStr = then.toSecText().c_str(); remainingStr = (then - now).toSecText().c_str(); m_lastPlayStatusSec = now.sec; } else { nowStr = now.toText(true).c_str(); thenStr = then.toText(true).c_str(); remainingStr = (then - now).toText(true).c_str(); } m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") .arg(nowStr).arg(thenStr).arg(remainingStr); statusBar()->showMessage(m_myStatusMessage); } void MainWindowBase::globalCentreFrameChanged(unsigned long ) { if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Pane *p = 0; if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; if (!p->getFollowGlobalPan()) return; updateVisibleRangeDisplay(p); } void MainWindowBase::viewCentreFrameChanged(View *v, unsigned long ) { if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Pane *p = 0; if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; if (v == p) updateVisibleRangeDisplay(p); } void MainWindowBase::viewZoomLevelChanged(View *v, unsigned long , bool ) { if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; Pane *p = 0; if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; if (v == p) updateVisibleRangeDisplay(p); } void MainWindowBase::layerAdded(Layer *) { // std::cerr << "MainWindowBase::layerAdded(" << layer << ")" << std::endl; updateMenuStates(); } void MainWindowBase::layerRemoved(Layer *) { // std::cerr << "MainWindowBase::layerRemoved(" << layer << ")" << std::endl; updateMenuStates(); } void MainWindowBase::layerAboutToBeDeleted(Layer *layer) { // std::cerr << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << std::endl; if (m_timeRulerLayer && (layer == m_timeRulerLayer)) { // std::cerr << "(this is the time ruler layer)" << std::endl; m_timeRulerLayer = 0; } } void MainWindowBase::layerInAView(Layer *layer, bool inAView) { // std::cerr << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << std::endl; // Check whether we need to add or remove model from play source Model *model = layer->getModel(); if (model) { if (inAView) { m_playSource->addModel(model); } else { bool found = false; for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Pane *pane = m_paneStack->getPane(i); if (!pane) continue; for (int j = 0; j < pane->getLayerCount(); ++j) { Layer *pl = pane->getLayer(j); if (pl && pl->getModel() == model) { found = true; break; } } if (found) break; } if (!found) m_playSource->removeModel(model); } } updateMenuStates(); } void MainWindowBase::modelAdded(Model *model) { // std::cerr << "MainWindowBase::modelAdded(" << model << ")" << std::endl; m_playSource->addModel(model); } void MainWindowBase::mainModelChanged(WaveFileModel *model) { // std::cerr << "MainWindowBase::mainModelChanged(" << model << ")" << std::endl; updateDescriptionLabel(); if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); if (model && !m_playTarget && m_audioOutput) createPlayTarget(); } void MainWindowBase::modelAboutToBeDeleted(Model *model) { // std::cerr << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << std::endl; if (model == m_viewManager->getPlaybackModel()) { m_viewManager->setPlaybackModel(0); } m_playSource->removeModel(model); FFTDataServer::modelAboutToBeDeleted(model); } void MainWindowBase::paneDeleteButtonClicked(Pane *pane) { bool found = false; for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { if (m_paneStack->getPane(i) == pane) { found = true; break; } } if (!found) { std::cerr << "MainWindowBase::paneDeleteButtonClicked: Unknown pane " << pane << std::endl; return; } CommandHistory::getInstance()->startCompoundOperation (tr("Delete Pane"), true); while (pane->getLayerCount() > 0) { Layer *layer = pane->getLayer(0); if (layer) { m_document->removeLayerFromView(pane, layer); } else { break; } } RemovePaneCommand *command = new RemovePaneCommand(this, pane); CommandHistory::getInstance()->addCommand(command); CommandHistory::getInstance()->endCompoundOperation(); updateMenuStates(); } void MainWindowBase::pollOSC() { if (!m_oscQueue || m_oscQueue->isEmpty()) return; std::cerr << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; if (m_openingAudioFile) return; OSCMessage message = m_oscQueue->readMessage(); if (message.getTarget() != 0) { return; //!!! for now -- this class is target 0, others not handled yet } handleOSCMessage(message); } void MainWindowBase::inProgressSelectionChanged() { Pane *currentPane = 0; if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); if (currentPane) updateVisibleRangeDisplay(currentPane); } void MainWindowBase::contextHelpChanged(const QString &s) { if (s == "" && m_myStatusMessage != "") { statusBar()->showMessage(m_myStatusMessage); return; } statusBar()->showMessage(s); } void MainWindowBase::openHelpUrl(QString url) { // This method mostly lifted from Qt Assistant source code QProcess *process = new QProcess(this); connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList args; #ifdef Q_OS_MAC args.append(url); process->start("open", args); #else #ifdef Q_OS_WIN32 QString pf(getenv("ProgramFiles")); QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); args.append(url); process->start(command, args); #else #ifdef Q_WS_X11 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { args.append("exec"); args.append(url); process->start("kfmclient", args); } else if (!qgetenv("BROWSER").isEmpty()) { args.append(url); process->start(qgetenv("BROWSER"), args); } else { args.append(url); process->start("firefox", args); } #endif #endif #endif }