| Chris@45 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@45 | 2 | 
| Chris@45 | 3 /* | 
| Chris@45 | 4     Sonic Visualiser | 
| Chris@45 | 5     An audio file viewer and annotation editor. | 
| Chris@45 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@45 | 7     This file copyright 2006-2007 Chris Cannam and QMUL. | 
| Chris@45 | 8 | 
| Chris@45 | 9     This program is free software; you can redistribute it and/or | 
| Chris@45 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@45 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@45 | 12     License, or (at your option) any later version.  See the file | 
| Chris@45 | 13     COPYING included with this distribution for more information. | 
| Chris@45 | 14 */ | 
| Chris@45 | 15 | 
| Chris@45 | 16 #include "MainWindowBase.h" | 
| Chris@46 | 17 #include "Document.h" | 
| Chris@45 | 18 | 
| Chris@45 | 19 | 
| Chris@45 | 20 #include "view/Pane.h" | 
| Chris@45 | 21 #include "view/PaneStack.h" | 
| Chris@45 | 22 #include "data/model/WaveFileModel.h" | 
| Chris@45 | 23 #include "data/model/SparseOneDimensionalModel.h" | 
| Chris@45 | 24 #include "data/model/NoteModel.h" | 
| Chris@45 | 25 #include "data/model/Labeller.h" | 
| Chris@45 | 26 #include "view/ViewManager.h" | 
| Chris@45 | 27 | 
| Chris@45 | 28 #include "layer/WaveformLayer.h" | 
| Chris@45 | 29 #include "layer/TimeRulerLayer.h" | 
| Chris@45 | 30 #include "layer/TimeInstantLayer.h" | 
| Chris@45 | 31 #include "layer/TimeValueLayer.h" | 
| Chris@45 | 32 #include "layer/Colour3DPlotLayer.h" | 
| Chris@45 | 33 #include "layer/SliceLayer.h" | 
| Chris@45 | 34 #include "layer/SliceableLayer.h" | 
| Chris@45 | 35 #include "layer/ImageLayer.h" | 
| Chris@45 | 36 | 
| Chris@45 | 37 #include "widgets/ListInputDialog.h" | 
| Chris@105 | 38 #include "widgets/CommandHistory.h" | 
| Chris@109 | 39 #include "widgets/ProgressDialog.h" | 
| Chris@109 | 40 #include "widgets/MIDIFileImportDialog.h" | 
| Chris@109 | 41 #include "widgets/CSVFormatDialog.h" | 
| Chris@45 | 42 | 
| Chris@45 | 43 #include "audioio/AudioCallbackPlaySource.h" | 
| Chris@45 | 44 #include "audioio/AudioCallbackPlayTarget.h" | 
| Chris@45 | 45 #include "audioio/AudioTargetFactory.h" | 
| Chris@45 | 46 #include "audioio/PlaySpeedRangeMapper.h" | 
| Chris@45 | 47 #include "data/fileio/DataFileReaderFactory.h" | 
| Chris@45 | 48 #include "data/fileio/PlaylistFileReader.h" | 
| Chris@45 | 49 #include "data/fileio/WavFileWriter.h" | 
| Chris@45 | 50 #include "data/fileio/CSVFileWriter.h" | 
| Chris@45 | 51 #include "data/fileio/MIDIFileWriter.h" | 
| Chris@45 | 52 #include "data/fileio/BZipFileDevice.h" | 
| Chris@45 | 53 #include "data/fileio/FileSource.h" | 
| Chris@45 | 54 | 
| Chris@45 | 55 #include "data/fft/FFTDataServer.h" | 
| Chris@45 | 56 | 
| Chris@45 | 57 #include "base/RecentFiles.h" | 
| Chris@45 | 58 | 
| Chris@45 | 59 #include "base/PlayParameterRepository.h" | 
| Chris@45 | 60 #include "base/XmlExportable.h" | 
| Chris@45 | 61 #include "base/Profiler.h" | 
| Chris@45 | 62 #include "base/Preferences.h" | 
| Chris@45 | 63 | 
| Chris@45 | 64 #include "data/osc/OSCQueue.h" | 
| Chris@45 | 65 | 
| Chris@45 | 66 #include <QApplication> | 
| Chris@45 | 67 #include <QMessageBox> | 
| Chris@45 | 68 #include <QGridLayout> | 
| Chris@45 | 69 #include <QLabel> | 
| Chris@45 | 70 #include <QAction> | 
| Chris@45 | 71 #include <QMenuBar> | 
| Chris@45 | 72 #include <QToolBar> | 
| Chris@45 | 73 #include <QInputDialog> | 
| Chris@45 | 74 #include <QStatusBar> | 
| Chris@45 | 75 #include <QTreeView> | 
| Chris@45 | 76 #include <QFile> | 
| Chris@45 | 77 #include <QFileInfo> | 
| Chris@45 | 78 #include <QDir> | 
| Chris@45 | 79 #include <QTextStream> | 
| Chris@45 | 80 #include <QProcess> | 
| Chris@45 | 81 #include <QShortcut> | 
| Chris@45 | 82 #include <QSettings> | 
| Chris@45 | 83 #include <QDateTime> | 
| Chris@45 | 84 #include <QProcess> | 
| Chris@45 | 85 #include <QCheckBox> | 
| Chris@45 | 86 #include <QRegExp> | 
| Chris@45 | 87 #include <QScrollArea> | 
| Chris@45 | 88 | 
| Chris@45 | 89 #include <iostream> | 
| Chris@45 | 90 #include <cstdio> | 
| Chris@45 | 91 #include <errno.h> | 
| Chris@45 | 92 | 
| Chris@45 | 93 using std::cerr; | 
| Chris@45 | 94 using std::endl; | 
| Chris@45 | 95 | 
| Chris@45 | 96 using std::vector; | 
| Chris@45 | 97 using std::map; | 
| Chris@45 | 98 using std::set; | 
| Chris@45 | 99 | 
| Chris@45 | 100 | 
| Chris@45 | 101 MainWindowBase::MainWindowBase(bool withAudioOutput, bool withOSCSupport) : | 
| Chris@45 | 102     m_document(0), | 
| Chris@45 | 103     m_paneStack(0), | 
| Chris@45 | 104     m_viewManager(0), | 
| Chris@45 | 105     m_timeRulerLayer(0), | 
| Chris@45 | 106     m_audioOutput(withAudioOutput), | 
| Chris@45 | 107     m_playSource(0), | 
| Chris@45 | 108     m_playTarget(0), | 
| Chris@45 | 109     m_oscQueue(withOSCSupport ? new OSCQueue() : 0), | 
| Chris@45 | 110     m_recentFiles("RecentFiles", 20), | 
| Chris@54 | 111     m_recentTransforms("RecentTransforms", 20), | 
| Chris@45 | 112     m_documentModified(false), | 
| Chris@45 | 113     m_openingAudioFile(false), | 
| Chris@45 | 114     m_abandoning(false), | 
| Chris@45 | 115     m_labeller(0) | 
| Chris@45 | 116 { | 
| Chris@45 | 117     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), | 
| Chris@45 | 118 	    this, SLOT(documentModified())); | 
| Chris@45 | 119     connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), | 
| Chris@45 | 120 	    this, SLOT(documentRestored())); | 
| Chris@45 | 121 | 
| Chris@45 | 122     m_viewManager = new ViewManager(); | 
| Chris@45 | 123     connect(m_viewManager, SIGNAL(selectionChanged()), | 
| Chris@45 | 124 	    this, SLOT(updateMenuStates())); | 
| Chris@45 | 125     connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), | 
| Chris@45 | 126 	    this, SLOT(inProgressSelectionChanged())); | 
| Chris@45 | 127 | 
| Chris@105 | 128     // set a sensible default font size for views -- cannot do this | 
| Chris@105 | 129     // in Preferences, which is in base and not supposed to use QtGui | 
| Chris@105 | 130     int viewFontSize = QApplication::font().pointSize() * 0.9; | 
| Chris@105 | 131     QSettings settings; | 
| Chris@105 | 132     settings.beginGroup("Preferences"); | 
| Chris@105 | 133     viewFontSize = settings.value("view-font-size", viewFontSize).toInt(); | 
| Chris@105 | 134     settings.setValue("view-font-size", viewFontSize); | 
| Chris@105 | 135     settings.endGroup(); | 
| Chris@105 | 136 | 
| Chris@45 | 137     Preferences::BackgroundMode mode = | 
| Chris@45 | 138         Preferences::getInstance()->getBackgroundMode(); | 
| Chris@45 | 139     m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); | 
| Chris@45 | 140     if (mode != Preferences::BackgroundFromTheme) { | 
| Chris@45 | 141         m_viewManager->setGlobalDarkBackground | 
| Chris@45 | 142             (mode == Preferences::DarkBackground); | 
| Chris@45 | 143     } | 
| Chris@45 | 144 | 
| Chris@45 | 145     m_paneStack = new PaneStack(0, m_viewManager); | 
| Chris@45 | 146     connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), | 
| Chris@45 | 147 	    this, SLOT(currentPaneChanged(Pane *))); | 
| Chris@45 | 148     connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), | 
| Chris@45 | 149 	    this, SLOT(currentLayerChanged(Pane *, Layer *))); | 
| Chris@45 | 150     connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), | 
| Chris@45 | 151             this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); | 
| Chris@45 | 152     connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@45 | 153             this, SLOT(contextHelpChanged(const QString &))); | 
| Chris@45 | 154     connect(m_paneStack, SIGNAL(paneAdded(Pane *)), | 
| Chris@45 | 155             this, SLOT(paneAdded(Pane *))); | 
| Chris@45 | 156     connect(m_paneStack, SIGNAL(paneHidden(Pane *)), | 
| Chris@45 | 157             this, SLOT(paneHidden(Pane *))); | 
| Chris@45 | 158     connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)), | 
| Chris@45 | 159             this, SLOT(paneAboutToBeDeleted(Pane *))); | 
| Chris@45 | 160     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)), | 
| Chris@45 | 161             this, SLOT(paneDropAccepted(Pane *, QStringList))); | 
| Chris@45 | 162     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)), | 
| Chris@45 | 163             this, SLOT(paneDropAccepted(Pane *, QString))); | 
| Chris@55 | 164     connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), | 
| Chris@55 | 165             this, SLOT(paneDeleteButtonClicked(Pane *))); | 
| Chris@45 | 166 | 
| Chris@57 | 167     m_playSource = new AudioCallbackPlaySource(m_viewManager, | 
| Chris@57 | 168                                                QApplication::applicationName()); | 
| Chris@45 | 169 | 
| Chris@45 | 170     connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), | 
| Chris@45 | 171 	    this,           SLOT(sampleRateMismatch(size_t, size_t, bool))); | 
| Chris@45 | 172     connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), | 
| Chris@45 | 173             this,           SLOT(audioOverloadPluginDisabled())); | 
| Chris@45 | 174 | 
| Chris@45 | 175     connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), | 
| Chris@45 | 176 	    this, SLOT(outputLevelsChanged(float, float))); | 
| Chris@45 | 177 | 
| Chris@45 | 178     connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), | 
| Chris@45 | 179             this, SLOT(playbackFrameChanged(unsigned long))); | 
| Chris@45 | 180 | 
| Chris@45 | 181     connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), | 
| Chris@45 | 182             this, SLOT(globalCentreFrameChanged(unsigned long))); | 
| Chris@45 | 183 | 
| Chris@45 | 184     connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), | 
| Chris@45 | 185             this, SLOT(viewCentreFrameChanged(View *, unsigned long))); | 
| Chris@45 | 186 | 
| Chris@45 | 187     connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), | 
| Chris@45 | 188             this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); | 
| Chris@45 | 189 | 
| Chris@45 | 190     connect(Preferences::getInstance(), | 
| Chris@45 | 191             SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@45 | 192             this, | 
| Chris@45 | 193             SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@45 | 194 | 
| Chris@45 | 195     if (m_oscQueue && m_oscQueue->isOK()) { | 
| Chris@45 | 196         connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); | 
| Chris@45 | 197         QTimer *oscTimer = new QTimer(this); | 
| Chris@45 | 198         connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); | 
| Chris@45 | 199         oscTimer->start(1000); | 
| Chris@45 | 200     } | 
| Chris@45 | 201 | 
| Chris@45 | 202     Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; | 
| Chris@45 | 203     settings.beginGroup("MainWindow"); | 
| Chris@45 | 204     labellerType = (Labeller::ValueType) | 
| Chris@45 | 205         settings.value("labellertype", (int)labellerType).toInt(); | 
| Chris@45 | 206     int cycle = settings.value("labellercycle", 4).toInt(); | 
| Chris@45 | 207     settings.endGroup(); | 
| Chris@45 | 208 | 
| Chris@45 | 209     m_labeller = new Labeller(labellerType); | 
| Chris@45 | 210     m_labeller->setCounterCycleSize(cycle); | 
| Chris@45 | 211 } | 
| Chris@45 | 212 | 
| Chris@45 | 213 MainWindowBase::~MainWindowBase() | 
| Chris@45 | 214 { | 
| Chris@70 | 215     if (m_playTarget) m_playTarget->shutdown(); | 
| Chris@70 | 216 //    delete m_playTarget; | 
| Chris@45 | 217     delete m_playSource; | 
| Chris@45 | 218     delete m_viewManager; | 
| Chris@45 | 219     delete m_oscQueue; | 
| Chris@45 | 220     Profiles::getInstance()->dump(); | 
| Chris@45 | 221 } | 
| Chris@45 | 222 | 
| Chris@45 | 223 QString | 
| Chris@45 | 224 MainWindowBase::getOpenFileName(FileFinder::FileType type) | 
| Chris@45 | 225 { | 
| Chris@45 | 226     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@45 | 227     switch (type) { | 
| Chris@45 | 228     case FileFinder::SessionFile: | 
| Chris@45 | 229         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@45 | 230     case FileFinder::AudioFile: | 
| Chris@45 | 231         return ff->getOpenFileName(type, m_audioFile); | 
| Chris@45 | 232     case FileFinder::LayerFile: | 
| Chris@45 | 233         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@45 | 234     case FileFinder::LayerFileNoMidi: | 
| Chris@45 | 235         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@45 | 236     case FileFinder::SessionOrAudioFile: | 
| Chris@45 | 237         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@45 | 238     case FileFinder::ImageFile: | 
| Chris@45 | 239         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@45 | 240     case FileFinder::AnyFile: | 
| Chris@45 | 241         if (getMainModel() != 0 && | 
| Chris@45 | 242             m_paneStack != 0 && | 
| Chris@45 | 243             m_paneStack->getCurrentPane() != 0) { // can import a layer | 
| Chris@45 | 244             return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); | 
| Chris@45 | 245         } else { | 
| Chris@45 | 246             return ff->getOpenFileName(FileFinder::SessionOrAudioFile, | 
| Chris@45 | 247                                        m_sessionFile); | 
| Chris@45 | 248         } | 
| Chris@45 | 249     } | 
| Chris@45 | 250     return ""; | 
| Chris@45 | 251 } | 
| Chris@45 | 252 | 
| Chris@45 | 253 QString | 
| Chris@45 | 254 MainWindowBase::getSaveFileName(FileFinder::FileType type) | 
| Chris@45 | 255 { | 
| Chris@45 | 256     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@45 | 257     switch (type) { | 
| Chris@45 | 258     case FileFinder::SessionFile: | 
| Chris@45 | 259         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 260     case FileFinder::AudioFile: | 
| Chris@45 | 261         return ff->getSaveFileName(type, m_audioFile); | 
| Chris@45 | 262     case FileFinder::LayerFile: | 
| Chris@45 | 263         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 264     case FileFinder::LayerFileNoMidi: | 
| Chris@45 | 265         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 266     case FileFinder::SessionOrAudioFile: | 
| Chris@45 | 267         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 268     case FileFinder::ImageFile: | 
| Chris@45 | 269         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 270     case FileFinder::AnyFile: | 
| Chris@45 | 271         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@45 | 272     } | 
| Chris@45 | 273     return ""; | 
| Chris@45 | 274 } | 
| Chris@45 | 275 | 
| Chris@45 | 276 void | 
| Chris@45 | 277 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path) | 
| Chris@45 | 278 { | 
| Chris@45 | 279     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@45 | 280     ff->registerLastOpenedFilePath(type, path); | 
| Chris@45 | 281 } | 
| Chris@45 | 282 | 
| Chris@45 | 283 void | 
| Chris@45 | 284 MainWindowBase::updateMenuStates() | 
| Chris@45 | 285 { | 
| Chris@45 | 286     Pane *currentPane = 0; | 
| Chris@45 | 287     Layer *currentLayer = 0; | 
| Chris@45 | 288 | 
| Chris@45 | 289     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 290     if (currentPane) currentLayer = currentPane->getSelectedLayer(); | 
| Chris@45 | 291 | 
| Chris@73 | 292     bool havePrevPane = false, haveNextPane = false; | 
| Chris@73 | 293     bool havePrevLayer = false, haveNextLayer = false; | 
| Chris@73 | 294 | 
| Chris@73 | 295     if (currentPane) { | 
| Chris@73 | 296         for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 297             if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 298                 if (i > 0) havePrevPane = true; | 
| Chris@73 | 299                 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true; | 
| Chris@73 | 300                 break; | 
| Chris@73 | 301             } | 
| Chris@73 | 302         } | 
| Chris@73 | 303         if (currentLayer) { | 
| Chris@73 | 304             for (int i = 0; i < currentPane->getLayerCount(); ++i) { | 
| Chris@73 | 305                 if (currentPane->getLayer(i) == currentLayer) { | 
| Chris@73 | 306                     if (i > 0) havePrevLayer = true; | 
| Chris@73 | 307                     if (i < currentPane->getLayerCount()-1) haveNextLayer = true; | 
| Chris@73 | 308                     break; | 
| Chris@73 | 309                 } | 
| Chris@73 | 310             } | 
| Chris@73 | 311         } | 
| Chris@73 | 312     } | 
| Chris@73 | 313 | 
| Chris@45 | 314     bool haveCurrentPane = | 
| Chris@45 | 315         (currentPane != 0); | 
| Chris@45 | 316     bool haveCurrentLayer = | 
| Chris@45 | 317         (haveCurrentPane && | 
| Chris@45 | 318          (currentLayer != 0)); | 
| Chris@45 | 319     bool haveMainModel = | 
| Chris@45 | 320 	(getMainModel() != 0); | 
| Chris@45 | 321     bool havePlayTarget = | 
| Chris@45 | 322 	(m_playTarget != 0); | 
| Chris@45 | 323     bool haveSelection = | 
| Chris@45 | 324 	(m_viewManager && | 
| Chris@45 | 325 	 !m_viewManager->getSelections().empty()); | 
| Chris@45 | 326     bool haveCurrentEditableLayer = | 
| Chris@45 | 327 	(haveCurrentLayer && | 
| Chris@45 | 328 	 currentLayer->isLayerEditable()); | 
| Chris@45 | 329     bool haveCurrentTimeInstantsLayer = | 
| Chris@45 | 330 	(haveCurrentLayer && | 
| Chris@45 | 331 	 dynamic_cast<TimeInstantLayer *>(currentLayer)); | 
| Chris@45 | 332     bool haveCurrentColour3DPlot = | 
| Chris@45 | 333         (haveCurrentLayer && | 
| Chris@45 | 334          dynamic_cast<Colour3DPlotLayer *>(currentLayer)); | 
| Chris@45 | 335     bool haveClipboardContents = | 
| Chris@45 | 336         (m_viewManager && | 
| Chris@45 | 337          !m_viewManager->getClipboard().empty()); | 
| Chris@45 | 338 | 
| Chris@45 | 339     emit canAddPane(haveMainModel); | 
| Chris@45 | 340     emit canDeleteCurrentPane(haveCurrentPane); | 
| Chris@45 | 341     emit canZoom(haveMainModel && haveCurrentPane); | 
| Chris@45 | 342     emit canScroll(haveMainModel && haveCurrentPane); | 
| Chris@45 | 343     emit canAddLayer(haveMainModel && haveCurrentPane); | 
| Chris@45 | 344     emit canImportMoreAudio(haveMainModel); | 
| Chris@45 | 345     emit canImportLayer(haveMainModel && haveCurrentPane); | 
| Chris@45 | 346     emit canExportAudio(haveMainModel); | 
| Chris@45 | 347     emit canExportLayer(haveMainModel && | 
| Chris@45 | 348                         (haveCurrentEditableLayer || haveCurrentColour3DPlot)); | 
| Chris@45 | 349     emit canExportImage(haveMainModel && haveCurrentPane); | 
| Chris@45 | 350     emit canDeleteCurrentLayer(haveCurrentLayer); | 
| Chris@45 | 351     emit canRenameLayer(haveCurrentLayer); | 
| Chris@45 | 352     emit canEditLayer(haveCurrentEditableLayer); | 
| Chris@45 | 353     emit canMeasureLayer(haveCurrentLayer); | 
| Chris@45 | 354     emit canSelect(haveMainModel && haveCurrentPane); | 
| Chris@45 | 355     emit canPlay(havePlayTarget); | 
| Chris@45 | 356     emit canFfwd(true); | 
| Chris@45 | 357     emit canRewind(true); | 
| Chris@87 | 358     emit canPaste(haveClipboardContents); | 
| Chris@45 | 359     emit canInsertInstant(haveCurrentPane); | 
| Chris@45 | 360     emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); | 
| Chris@45 | 361     emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); | 
| Chris@45 | 362     emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); | 
| Chris@45 | 363     emit canClearSelection(haveSelection); | 
| Chris@45 | 364     emit canEditSelection(haveSelection && haveCurrentEditableLayer); | 
| Chris@45 | 365     emit canSave(m_sessionFile != "" && m_documentModified); | 
| Chris@73 | 366     emit canSelectPreviousPane(havePrevPane); | 
| Chris@73 | 367     emit canSelectNextPane(haveNextPane); | 
| Chris@73 | 368     emit canSelectPreviousLayer(havePrevLayer); | 
| Chris@73 | 369     emit canSelectNextLayer(haveNextLayer); | 
| Chris@45 | 370 } | 
| Chris@45 | 371 | 
| Chris@45 | 372 void | 
| Chris@45 | 373 MainWindowBase::documentModified() | 
| Chris@45 | 374 { | 
| Chris@45 | 375 //    std::cerr << "MainWindowBase::documentModified" << std::endl; | 
| Chris@45 | 376 | 
| Chris@45 | 377     if (!m_documentModified) { | 
| Chris@45 | 378         //!!! this in subclass implementation? | 
| Chris@45 | 379 	setWindowTitle(tr("%1 (modified)").arg(windowTitle())); | 
| Chris@45 | 380     } | 
| Chris@45 | 381 | 
| Chris@45 | 382     m_documentModified = true; | 
| Chris@45 | 383     updateMenuStates(); | 
| Chris@45 | 384 } | 
| Chris@45 | 385 | 
| Chris@45 | 386 void | 
| Chris@45 | 387 MainWindowBase::documentRestored() | 
| Chris@45 | 388 { | 
| Chris@45 | 389 //    std::cerr << "MainWindowBase::documentRestored" << std::endl; | 
| Chris@45 | 390 | 
| Chris@45 | 391     if (m_documentModified) { | 
| Chris@45 | 392         //!!! this in subclass implementation? | 
| Chris@45 | 393 	QString wt(windowTitle()); | 
| Chris@45 | 394 	wt.replace(tr(" (modified)"), ""); | 
| Chris@45 | 395 	setWindowTitle(wt); | 
| Chris@45 | 396     } | 
| Chris@45 | 397 | 
| Chris@45 | 398     m_documentModified = false; | 
| Chris@45 | 399     updateMenuStates(); | 
| Chris@45 | 400 } | 
| Chris@45 | 401 | 
| Chris@45 | 402 void | 
| Chris@45 | 403 MainWindowBase::playLoopToggled() | 
| Chris@45 | 404 { | 
| Chris@45 | 405     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 406 | 
| Chris@45 | 407     if (action) { | 
| Chris@45 | 408 	m_viewManager->setPlayLoopMode(action->isChecked()); | 
| Chris@45 | 409     } else { | 
| Chris@45 | 410 	m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); | 
| Chris@45 | 411     } | 
| Chris@45 | 412 } | 
| Chris@45 | 413 | 
| Chris@45 | 414 void | 
| Chris@45 | 415 MainWindowBase::playSelectionToggled() | 
| Chris@45 | 416 { | 
| Chris@45 | 417     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 418 | 
| Chris@45 | 419     if (action) { | 
| Chris@45 | 420 	m_viewManager->setPlaySelectionMode(action->isChecked()); | 
| Chris@45 | 421     } else { | 
| Chris@45 | 422 	m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); | 
| Chris@45 | 423     } | 
| Chris@45 | 424 } | 
| Chris@45 | 425 | 
| Chris@45 | 426 void | 
| Chris@45 | 427 MainWindowBase::playSoloToggled() | 
| Chris@45 | 428 { | 
| Chris@45 | 429     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 430 | 
| Chris@45 | 431     if (action) { | 
| Chris@45 | 432 	m_viewManager->setPlaySoloMode(action->isChecked()); | 
| Chris@45 | 433     } else { | 
| Chris@45 | 434 	m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); | 
| Chris@45 | 435     } | 
| Chris@45 | 436 | 
| Chris@45 | 437     if (m_viewManager->getPlaySoloMode()) { | 
| Chris@45 | 438         currentPaneChanged(m_paneStack->getCurrentPane()); | 
| Chris@45 | 439     } else { | 
| Chris@45 | 440         m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 441         if (m_playSource) { | 
| Chris@45 | 442             m_playSource->clearSoloModelSet(); | 
| Chris@45 | 443         } | 
| Chris@45 | 444     } | 
| Chris@45 | 445 } | 
| Chris@45 | 446 | 
| Chris@45 | 447 void | 
| Chris@45 | 448 MainWindowBase::currentPaneChanged(Pane *p) | 
| Chris@45 | 449 { | 
| Chris@45 | 450     updateMenuStates(); | 
| Chris@45 | 451     updateVisibleRangeDisplay(p); | 
| Chris@45 | 452 | 
| Chris@45 | 453     if (!p) return; | 
| Chris@45 | 454 | 
| Chris@45 | 455     if (!(m_viewManager && | 
| Chris@45 | 456           m_playSource && | 
| Chris@45 | 457           m_viewManager->getPlaySoloMode())) { | 
| Chris@45 | 458         if (m_viewManager) m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 459         return; | 
| Chris@45 | 460     } | 
| Chris@45 | 461 | 
| Chris@45 | 462     Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); | 
| Chris@60 | 463 | 
| Chris@93 | 464     // What we want here is not the currently playing frame (unless we | 
| Chris@93 | 465     // are about to clear out the audio playback buffers -- which may | 
| Chris@93 | 466     // or may not be possible, depending on the audio driver).  What | 
| Chris@93 | 467     // we want is the frame that was last committed to the soundcard | 
| Chris@93 | 468     // buffers, as the audio driver will continue playing up to that | 
| Chris@93 | 469     // frame before switching to whichever one we decide we want to | 
| Chris@93 | 470     // switch to, regardless of our efforts. | 
| Chris@93 | 471 | 
| Chris@93 | 472     int frame = m_playSource->getCurrentBufferedFrame(); | 
| Chris@93 | 473 | 
| Chris@93 | 474 //    std::cerr << "currentPaneChanged: current frame (in ref model) = " << frame << std::endl; | 
| Chris@45 | 475 | 
| Chris@45 | 476     View::ModelSet soloModels = p->getModels(); | 
| Chris@45 | 477 | 
| Chris@57 | 478     View::ModelSet sources; | 
| Chris@57 | 479     for (View::ModelSet::iterator mi = soloModels.begin(); | 
| Chris@57 | 480          mi != soloModels.end(); ++mi) { | 
| Chris@57 | 481         if (*mi && (*mi)->getSourceModel()) { | 
| Chris@57 | 482             sources.insert((*mi)->getSourceModel()); | 
| Chris@57 | 483         } | 
| Chris@57 | 484     } | 
| Chris@57 | 485     for (View::ModelSet::iterator mi = sources.begin(); | 
| Chris@57 | 486          mi != sources.end(); ++mi) { | 
| Chris@57 | 487         soloModels.insert(*mi); | 
| Chris@57 | 488     } | 
| Chris@57 | 489 | 
| Chris@60 | 490     //!!! Need an "atomic" way of telling the play source that the | 
| Chris@60 | 491     //playback model has changed, and changing it on ViewManager -- | 
| Chris@60 | 492     //the play source should be making the setPlaybackModel call to | 
| Chris@60 | 493     //ViewManager | 
| Chris@60 | 494 | 
| Chris@45 | 495     for (View::ModelSet::iterator mi = soloModels.begin(); | 
| Chris@45 | 496          mi != soloModels.end(); ++mi) { | 
| Chris@45 | 497         if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) { | 
| Chris@45 | 498             m_viewManager->setPlaybackModel(*mi); | 
| Chris@45 | 499         } | 
| Chris@45 | 500     } | 
| Chris@45 | 501 | 
| Chris@45 | 502     RangeSummarisableTimeValueModel *a = | 
| Chris@45 | 503         dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); | 
| Chris@45 | 504     RangeSummarisableTimeValueModel *b = | 
| Chris@45 | 505         dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> | 
| Chris@45 | 506                                                         getPlaybackModel()); | 
| Chris@45 | 507 | 
| Chris@45 | 508     m_playSource->setSoloModelSet(soloModels); | 
| Chris@45 | 509 | 
| Chris@45 | 510     if (a && b && (a != b)) { | 
| Chris@60 | 511         if (m_playSource->isPlaying()) m_playSource->play(frame); | 
| Chris@45 | 512     } | 
| Chris@45 | 513 } | 
| Chris@45 | 514 | 
| Chris@45 | 515 void | 
| Chris@45 | 516 MainWindowBase::currentLayerChanged(Pane *p, Layer *) | 
| Chris@45 | 517 { | 
| Chris@45 | 518     updateMenuStates(); | 
| Chris@45 | 519     updateVisibleRangeDisplay(p); | 
| Chris@45 | 520 } | 
| Chris@45 | 521 | 
| Chris@45 | 522 void | 
| Chris@45 | 523 MainWindowBase::selectAll() | 
| Chris@45 | 524 { | 
| Chris@45 | 525     if (!getMainModel()) return; | 
| Chris@45 | 526     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@45 | 527 					  getMainModel()->getEndFrame())); | 
| Chris@45 | 528 } | 
| Chris@45 | 529 | 
| Chris@45 | 530 void | 
| Chris@45 | 531 MainWindowBase::selectToStart() | 
| Chris@45 | 532 { | 
| Chris@45 | 533     if (!getMainModel()) return; | 
| Chris@45 | 534     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@45 | 535 					  m_viewManager->getGlobalCentreFrame())); | 
| Chris@45 | 536 } | 
| Chris@45 | 537 | 
| Chris@45 | 538 void | 
| Chris@45 | 539 MainWindowBase::selectToEnd() | 
| Chris@45 | 540 { | 
| Chris@45 | 541     if (!getMainModel()) return; | 
| Chris@45 | 542     m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), | 
| Chris@45 | 543 					  getMainModel()->getEndFrame())); | 
| Chris@45 | 544 } | 
| Chris@45 | 545 | 
| Chris@45 | 546 void | 
| Chris@45 | 547 MainWindowBase::selectVisible() | 
| Chris@45 | 548 { | 
| Chris@45 | 549     Model *model = getMainModel(); | 
| Chris@45 | 550     if (!model) return; | 
| Chris@45 | 551 | 
| Chris@45 | 552     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 553     if (!currentPane) return; | 
| Chris@45 | 554 | 
| Chris@45 | 555     size_t startFrame, endFrame; | 
| Chris@45 | 556 | 
| Chris@45 | 557     if (currentPane->getStartFrame() < 0) startFrame = 0; | 
| Chris@45 | 558     else startFrame = currentPane->getStartFrame(); | 
| Chris@45 | 559 | 
| Chris@45 | 560     if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); | 
| Chris@45 | 561     else endFrame = currentPane->getEndFrame(); | 
| Chris@45 | 562 | 
| Chris@45 | 563     m_viewManager->setSelection(Selection(startFrame, endFrame)); | 
| Chris@45 | 564 } | 
| Chris@45 | 565 | 
| Chris@45 | 566 void | 
| Chris@45 | 567 MainWindowBase::clearSelection() | 
| Chris@45 | 568 { | 
| Chris@45 | 569     m_viewManager->clearSelections(); | 
| Chris@45 | 570 } | 
| Chris@45 | 571 | 
| Chris@45 | 572 void | 
| Chris@45 | 573 MainWindowBase::cut() | 
| Chris@45 | 574 { | 
| Chris@45 | 575     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 576     if (!currentPane) return; | 
| Chris@45 | 577 | 
| Chris@45 | 578     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 579     if (!layer) return; | 
| Chris@45 | 580 | 
| Chris@45 | 581     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@45 | 582     clipboard.clear(); | 
| Chris@45 | 583 | 
| Chris@45 | 584     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 585 | 
| Chris@45 | 586     CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); | 
| Chris@45 | 587 | 
| Chris@45 | 588     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 589          i != selections.end(); ++i) { | 
| Chris@86 | 590         layer->copy(currentPane, *i, clipboard); | 
| Chris@45 | 591         layer->deleteSelection(*i); | 
| Chris@45 | 592     } | 
| Chris@45 | 593 | 
| Chris@45 | 594     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 595 } | 
| Chris@45 | 596 | 
| Chris@45 | 597 void | 
| Chris@45 | 598 MainWindowBase::copy() | 
| Chris@45 | 599 { | 
| Chris@45 | 600     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 601     if (!currentPane) return; | 
| Chris@45 | 602 | 
| Chris@45 | 603     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 604     if (!layer) return; | 
| Chris@45 | 605 | 
| Chris@45 | 606     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@45 | 607     clipboard.clear(); | 
| Chris@45 | 608 | 
| Chris@45 | 609     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 610 | 
| Chris@45 | 611     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 612          i != selections.end(); ++i) { | 
| Chris@86 | 613         layer->copy(currentPane, *i, clipboard); | 
| Chris@45 | 614     } | 
| Chris@45 | 615 } | 
| Chris@45 | 616 | 
| Chris@45 | 617 void | 
| Chris@45 | 618 MainWindowBase::paste() | 
| Chris@45 | 619 { | 
| Chris@45 | 620     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 621     if (!currentPane) return; | 
| Chris@45 | 622 | 
| Chris@45 | 623     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 624 | 
| Chris@45 | 625     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@87 | 626 //    Clipboard::PointList contents = clipboard.getPoints(); | 
| Chris@87 | 627 | 
| Chris@98 | 628     bool inCompound = false; | 
| Chris@87 | 629 | 
| Chris@87 | 630     if (!layer || !layer->isLayerEditable()) { | 
| Chris@87 | 631 | 
| Chris@87 | 632         CommandHistory::getInstance()->startCompoundOperation | 
| Chris@87 | 633             (tr("Paste"), true); | 
| Chris@87 | 634 | 
| Chris@87 | 635         // no suitable current layer: create one of the most | 
| Chris@87 | 636         // appropriate sort | 
| Chris@87 | 637         LayerFactory::LayerType type = | 
| Chris@87 | 638             LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard); | 
| Chris@87 | 639         layer = m_document->createEmptyLayer(type); | 
| Chris@87 | 640 | 
| Chris@87 | 641         if (!layer) { | 
| Chris@87 | 642             CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@87 | 643             return; | 
| Chris@45 | 644         } | 
| Chris@87 | 645 | 
| Chris@87 | 646         m_document->addLayerToView(currentPane, layer); | 
| Chris@87 | 647         m_paneStack->setCurrentLayer(currentPane, layer); | 
| Chris@87 | 648 | 
| Chris@87 | 649         inCompound = true; | 
| Chris@45 | 650     } | 
| Chris@45 | 651 | 
| Chris@87 | 652     layer->paste(currentPane, clipboard, 0, true); | 
| Chris@45 | 653 | 
| Chris@87 | 654     if (inCompound) CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 655 } | 
| Chris@45 | 656 | 
| Chris@45 | 657 void | 
| Chris@45 | 658 MainWindowBase::deleteSelected() | 
| Chris@45 | 659 { | 
| Chris@45 | 660     if (m_paneStack->getCurrentPane() && | 
| Chris@45 | 661 	m_paneStack->getCurrentPane()->getSelectedLayer()) { | 
| Chris@45 | 662 | 
| Chris@45 | 663         Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); | 
| Chris@45 | 664 | 
| Chris@45 | 665         if (m_viewManager && | 
| Chris@45 | 666             (m_viewManager->getToolMode() == ViewManager::MeasureMode)) { | 
| Chris@45 | 667 | 
| Chris@45 | 668             layer->deleteCurrentMeasureRect(); | 
| Chris@45 | 669 | 
| Chris@45 | 670         } else { | 
| Chris@45 | 671 | 
| Chris@45 | 672             MultiSelection::SelectionList selections = | 
| Chris@45 | 673                 m_viewManager->getSelections(); | 
| Chris@45 | 674 | 
| Chris@45 | 675             for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 676                  i != selections.end(); ++i) { | 
| Chris@45 | 677                 layer->deleteSelection(*i); | 
| Chris@45 | 678             } | 
| Chris@45 | 679 	} | 
| Chris@45 | 680     } | 
| Chris@45 | 681 } | 
| Chris@45 | 682 | 
| Chris@45 | 683 void | 
| Chris@45 | 684 MainWindowBase::insertInstant() | 
| Chris@45 | 685 { | 
| Chris@45 | 686     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@45 | 687     insertInstantAt(frame); | 
| Chris@45 | 688 } | 
| Chris@45 | 689 | 
| Chris@45 | 690 void | 
| Chris@45 | 691 MainWindowBase::insertInstantsAtBoundaries() | 
| Chris@45 | 692 { | 
| Chris@45 | 693     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 694     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 695          i != selections.end(); ++i) { | 
| Chris@45 | 696         size_t start = i->getStartFrame(); | 
| Chris@45 | 697         size_t end = i->getEndFrame(); | 
| Chris@45 | 698         if (start != end) { | 
| Chris@45 | 699             insertInstantAt(i->getStartFrame()); | 
| Chris@45 | 700             insertInstantAt(i->getEndFrame()); | 
| Chris@45 | 701         } | 
| Chris@45 | 702     } | 
| Chris@45 | 703 } | 
| Chris@45 | 704 | 
| Chris@45 | 705 void | 
| Chris@45 | 706 MainWindowBase::insertInstantAt(size_t frame) | 
| Chris@45 | 707 { | 
| Chris@45 | 708     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 709     if (!pane) { | 
| Chris@45 | 710         return; | 
| Chris@45 | 711     } | 
| Chris@45 | 712 | 
| Chris@74 | 713     frame = pane->alignFromReference(frame); | 
| Chris@74 | 714 | 
| Chris@45 | 715     Layer *layer = dynamic_cast<TimeInstantLayer *> | 
| Chris@45 | 716         (pane->getSelectedLayer()); | 
| Chris@45 | 717 | 
| Chris@45 | 718     if (!layer) { | 
| Chris@45 | 719         for (int i = pane->getLayerCount(); i > 0; --i) { | 
| Chris@45 | 720             layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); | 
| Chris@45 | 721             if (layer) break; | 
| Chris@45 | 722         } | 
| Chris@45 | 723 | 
| Chris@45 | 724         if (!layer) { | 
| Chris@45 | 725             CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 726                 (tr("Add Point"), true); | 
| Chris@45 | 727             layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); | 
| Chris@45 | 728             if (layer) { | 
| Chris@45 | 729                 m_document->addLayerToView(pane, layer); | 
| Chris@45 | 730                 m_paneStack->setCurrentLayer(pane, layer); | 
| Chris@45 | 731             } | 
| Chris@45 | 732             CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 733         } | 
| Chris@45 | 734     } | 
| Chris@45 | 735 | 
| Chris@45 | 736     if (layer) { | 
| Chris@45 | 737 | 
| Chris@45 | 738         Model *model = layer->getModel(); | 
| Chris@45 | 739         SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 
| Chris@45 | 740             (model); | 
| Chris@45 | 741 | 
| Chris@45 | 742         if (sodm) { | 
| Chris@45 | 743             SparseOneDimensionalModel::Point point(frame, ""); | 
| Chris@45 | 744 | 
| Chris@45 | 745             SparseOneDimensionalModel::Point prevPoint(0); | 
| Chris@45 | 746             bool havePrevPoint = false; | 
| Chris@45 | 747 | 
| Chris@45 | 748             SparseOneDimensionalModel::EditCommand *command = | 
| Chris@45 | 749                 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); | 
| Chris@45 | 750 | 
| Chris@74 | 751             if (m_labeller->requiresPrevPoint()) { | 
| Chris@45 | 752 | 
| Chris@45 | 753                 SparseOneDimensionalModel::PointList prevPoints = | 
| Chris@45 | 754                     sodm->getPreviousPoints(frame); | 
| Chris@45 | 755 | 
| Chris@45 | 756                 if (!prevPoints.empty()) { | 
| Chris@45 | 757                     prevPoint = *prevPoints.begin(); | 
| Chris@45 | 758                     havePrevPoint = true; | 
| Chris@45 | 759                 } | 
| Chris@45 | 760             } | 
| Chris@45 | 761 | 
| Chris@45 | 762             if (m_labeller) { | 
| Chris@45 | 763 | 
| Chris@45 | 764                 m_labeller->setSampleRate(sodm->getSampleRate()); | 
| Chris@45 | 765 | 
| Chris@74 | 766                 if (m_labeller->actingOnPrevPoint()) { | 
| Chris@45 | 767                     command->deletePoint(prevPoint); | 
| Chris@45 | 768                 } | 
| Chris@45 | 769 | 
| Chris@45 | 770                 m_labeller->label<SparseOneDimensionalModel::Point> | 
| Chris@45 | 771                     (point, havePrevPoint ? &prevPoint : 0); | 
| Chris@45 | 772 | 
| Chris@74 | 773                 if (m_labeller->actingOnPrevPoint()) { | 
| Chris@45 | 774                     command->addPoint(prevPoint); | 
| Chris@45 | 775                 } | 
| Chris@45 | 776             } | 
| Chris@45 | 777 | 
| Chris@45 | 778             command->addPoint(point); | 
| Chris@45 | 779 | 
| Chris@45 | 780             command->setName(tr("Add Point at %1 s") | 
| Chris@45 | 781                              .arg(RealTime::frame2RealTime | 
| Chris@45 | 782                                   (frame, | 
| Chris@45 | 783                                    sodm->getSampleRate()) | 
| Chris@45 | 784                                   .toText(false).c_str())); | 
| Chris@45 | 785 | 
| Chris@108 | 786             Command *c = command->finish(); | 
| Chris@108 | 787             if (c) CommandHistory::getInstance()->addCommand(c, false); | 
| Chris@45 | 788         } | 
| Chris@45 | 789     } | 
| Chris@45 | 790 } | 
| Chris@45 | 791 | 
| Chris@45 | 792 void | 
| Chris@45 | 793 MainWindowBase::renumberInstants() | 
| Chris@45 | 794 { | 
| Chris@45 | 795     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 796     if (!pane) return; | 
| Chris@45 | 797 | 
| Chris@45 | 798     Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer()); | 
| Chris@45 | 799     if (!layer) return; | 
| Chris@45 | 800 | 
| Chris@45 | 801     MultiSelection ms(m_viewManager->getSelection()); | 
| Chris@45 | 802 | 
| Chris@45 | 803     Model *model = layer->getModel(); | 
| Chris@45 | 804     SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 
| Chris@45 | 805         (model); | 
| Chris@45 | 806     if (!sodm) return; | 
| Chris@45 | 807 | 
| Chris@45 | 808     if (!m_labeller) return; | 
| Chris@45 | 809 | 
| Chris@45 | 810     Labeller labeller(*m_labeller); | 
| Chris@45 | 811     labeller.setSampleRate(sodm->getSampleRate()); | 
| Chris@45 | 812 | 
| Chris@45 | 813     // This uses a command | 
| Chris@45 | 814 | 
| Chris@45 | 815     labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); | 
| Chris@45 | 816 } | 
| Chris@45 | 817 | 
| Chris@45 | 818 MainWindowBase::FileOpenStatus | 
| Chris@45 | 819 MainWindowBase::open(QString fileOrUrl, AudioFileOpenMode mode) | 
| Chris@45 | 820 { | 
| Chris@109 | 821     ProgressDialog dialog(tr("Opening file or URL..."), true, 2000); | 
| Chris@109 | 822     return open(FileSource(fileOrUrl, &dialog), mode); | 
| Chris@45 | 823 } | 
| Chris@45 | 824 | 
| Chris@45 | 825 MainWindowBase::FileOpenStatus | 
| Chris@45 | 826 MainWindowBase::open(FileSource source, AudioFileOpenMode mode) | 
| Chris@45 | 827 { | 
| Chris@45 | 828     FileOpenStatus status; | 
| Chris@45 | 829 | 
| Chris@45 | 830     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 831     source.waitForData(); | 
| Chris@45 | 832 | 
| Chris@45 | 833     bool canImportLayer = (getMainModel() != 0 && | 
| Chris@45 | 834                            m_paneStack != 0 && | 
| Chris@45 | 835                            m_paneStack->getCurrentPane() != 0); | 
| Chris@45 | 836 | 
| Chris@45 | 837     if ((status = openAudio(source, mode)) != FileOpenFailed) { | 
| Chris@45 | 838         return status; | 
| Chris@45 | 839     } else if ((status = openSession(source)) != FileOpenFailed) { | 
| Chris@45 | 840 	return status; | 
| Chris@45 | 841     } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) { | 
| Chris@45 | 842         return status; | 
| Chris@45 | 843     } else if (!canImportLayer) { | 
| Chris@45 | 844         return FileOpenWrongMode; | 
| Chris@45 | 845     } else if ((status = openImage(source)) != FileOpenFailed) { | 
| Chris@45 | 846         return status; | 
| Chris@45 | 847     } else if ((status = openLayer(source)) != FileOpenFailed) { | 
| Chris@45 | 848         return status; | 
| Chris@45 | 849     } else { | 
| Chris@45 | 850 	return FileOpenFailed; | 
| Chris@45 | 851     } | 
| Chris@45 | 852 } | 
| Chris@45 | 853 | 
| Chris@45 | 854 MainWindowBase::FileOpenStatus | 
| Chris@45 | 855 MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode) | 
| Chris@45 | 856 { | 
| Chris@45 | 857     std::cerr << "MainWindowBase::openAudio(" << source.getLocation().toStdString() << ")" << std::endl; | 
| Chris@45 | 858 | 
| Chris@45 | 859     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 860     source.waitForData(); | 
| Chris@45 | 861 | 
| Chris@45 | 862     m_openingAudioFile = true; | 
| Chris@45 | 863 | 
| Chris@45 | 864     size_t rate = 0; | 
| Chris@45 | 865 | 
| Chris@45 | 866     if (Preferences::getInstance()->getResampleOnLoad()) { | 
| Chris@45 | 867         rate = m_playSource->getSourceSampleRate(); | 
| Chris@45 | 868     } | 
| Chris@45 | 869 | 
| Chris@45 | 870     WaveFileModel *newModel = new WaveFileModel(source, rate); | 
| Chris@45 | 871 | 
| Chris@45 | 872     if (!newModel->isOK()) { | 
| Chris@45 | 873 	delete newModel; | 
| Chris@45 | 874         m_openingAudioFile = false; | 
| Chris@45 | 875 	return FileOpenFailed; | 
| Chris@45 | 876     } | 
| Chris@45 | 877 | 
| Chris@45 | 878     std::cerr << "mode = " << mode << std::endl; | 
| Chris@45 | 879 | 
| Chris@45 | 880     if (mode == AskUser) { | 
| Chris@45 | 881         if (getMainModel()) { | 
| Chris@45 | 882 | 
| Chris@45 | 883             static bool prevSetAsMain = true; | 
| Chris@45 | 884             bool setAsMain = true; | 
| Chris@45 | 885 | 
| Chris@45 | 886             QStringList items; | 
| Chris@45 | 887             items << tr("Replace the existing main waveform") | 
| Chris@45 | 888                   << tr("Load this file into a new waveform pane"); | 
| Chris@45 | 889 | 
| Chris@45 | 890             bool ok = false; | 
| Chris@45 | 891             QString item = ListInputDialog::getItem | 
| Chris@45 | 892                 (this, tr("Select target for import"), | 
| Chris@45 | 893                  tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), | 
| Chris@45 | 894                  items, prevSetAsMain ? 0 : 1, &ok); | 
| Chris@45 | 895 | 
| Chris@45 | 896             if (!ok || item.isEmpty()) { | 
| Chris@45 | 897                 delete newModel; | 
| Chris@45 | 898                 m_openingAudioFile = false; | 
| Chris@45 | 899                 return FileOpenCancelled; | 
| Chris@45 | 900             } | 
| Chris@45 | 901 | 
| Chris@45 | 902             setAsMain = (item == items[0]); | 
| Chris@45 | 903             prevSetAsMain = setAsMain; | 
| Chris@45 | 904 | 
| Chris@45 | 905             if (setAsMain) mode = ReplaceMainModel; | 
| Chris@45 | 906             else mode = CreateAdditionalModel; | 
| Chris@45 | 907 | 
| Chris@45 | 908         } else { | 
| Chris@45 | 909             mode = ReplaceMainModel; | 
| Chris@45 | 910         } | 
| Chris@45 | 911     } | 
| Chris@45 | 912 | 
| Chris@45 | 913     if (mode == ReplaceCurrentPane) { | 
| Chris@45 | 914 | 
| Chris@45 | 915         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 916         if (pane) { | 
| Chris@45 | 917             if (getMainModel()) { | 
| Chris@45 | 918                 View::ModelSet models(pane->getModels()); | 
| Chris@45 | 919                 if (models.find(getMainModel()) != models.end()) { | 
| Chris@45 | 920                     mode = ReplaceMainModel; | 
| Chris@45 | 921                 } | 
| Chris@45 | 922             } else { | 
| Chris@45 | 923                 mode = ReplaceMainModel; | 
| Chris@45 | 924             } | 
| Chris@45 | 925         } else { | 
| Chris@45 | 926             mode = CreateAdditionalModel; | 
| Chris@45 | 927         } | 
| Chris@45 | 928     } | 
| Chris@45 | 929 | 
| Chris@45 | 930     if (mode == CreateAdditionalModel && !getMainModel()) { | 
| Chris@45 | 931         mode = ReplaceMainModel; | 
| Chris@45 | 932     } | 
| Chris@45 | 933 | 
| Chris@45 | 934     if (mode == ReplaceMainModel) { | 
| Chris@45 | 935 | 
| Chris@45 | 936         Model *prevMain = getMainModel(); | 
| Chris@45 | 937         if (prevMain) { | 
| Chris@45 | 938             m_playSource->removeModel(prevMain); | 
| Chris@108 | 939             PlayParameterRepository::getInstance()->removePlayable(prevMain); | 
| Chris@45 | 940         } | 
| Chris@108 | 941         PlayParameterRepository::getInstance()->addPlayable(newModel); | 
| Chris@45 | 942 | 
| Chris@45 | 943 	m_document->setMainModel(newModel); | 
| Chris@45 | 944 | 
| Chris@45 | 945 	setupMenus(); | 
| Chris@45 | 946 | 
| Chris@45 | 947 	if (m_sessionFile == "") { | 
| Chris@45 | 948             //!!! shouldn't be dealing directly with title from here -- call a method | 
| Chris@57 | 949 	    setWindowTitle(tr("%1: %2") | 
| Chris@57 | 950                            .arg(QApplication::applicationName()) | 
| Chris@45 | 951                            .arg(source.getLocation())); | 
| Chris@45 | 952 	    CommandHistory::getInstance()->clear(); | 
| Chris@45 | 953 	    CommandHistory::getInstance()->documentSaved(); | 
| Chris@45 | 954 	    m_documentModified = false; | 
| Chris@45 | 955 	} else { | 
| Chris@57 | 956 	    setWindowTitle(tr("%1: %2 [%3]") | 
| Chris@57 | 957                            .arg(QApplication::applicationName()) | 
| Chris@45 | 958 			   .arg(QFileInfo(m_sessionFile).fileName()) | 
| Chris@45 | 959 			   .arg(source.getLocation())); | 
| Chris@45 | 960 	    if (m_documentModified) { | 
| Chris@45 | 961 		m_documentModified = false; | 
| Chris@45 | 962 		documentModified(); // so as to restore "(modified)" window title | 
| Chris@45 | 963 	    } | 
| Chris@45 | 964 	} | 
| Chris@45 | 965 | 
| Chris@45 | 966         if (!source.isRemote()) m_audioFile = source.getLocalFilename(); | 
| Chris@45 | 967 | 
| Chris@45 | 968     } else if (mode == CreateAdditionalModel) { | 
| Chris@45 | 969 | 
| Chris@45 | 970 	CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 971 	    (tr("Import \"%1\"").arg(source.getLocation()), true); | 
| Chris@45 | 972 | 
| Chris@45 | 973 	m_document->addImportedModel(newModel); | 
| Chris@45 | 974 | 
| Chris@45 | 975 	AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@45 | 976 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@45 | 977 | 
| Chris@45 | 978 	Pane *pane = command->getPane(); | 
| Chris@45 | 979 | 
| Chris@47 | 980         if (m_timeRulerLayer) { | 
| Chris@47 | 981             m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@47 | 982         } | 
| Chris@45 | 983 | 
| Chris@45 | 984 	Layer *newLayer = m_document->createImportedLayer(newModel); | 
| Chris@45 | 985 | 
| Chris@45 | 986 	if (newLayer) { | 
| Chris@45 | 987 	    m_document->addLayerToView(pane, newLayer); | 
| Chris@45 | 988 	} | 
| Chris@45 | 989 | 
| Chris@45 | 990 	CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 991 | 
| Chris@45 | 992     } else if (mode == ReplaceCurrentPane) { | 
| Chris@45 | 993 | 
| Chris@45 | 994         // We know there is a current pane, otherwise we would have | 
| Chris@45 | 995         // reset the mode to CreateAdditionalModel above; and we know | 
| Chris@45 | 996         // the current pane does not contain the main model, otherwise | 
| Chris@45 | 997         // we would have reset it to ReplaceMainModel.  But we don't | 
| Chris@45 | 998         // know whether the pane contains a waveform model at all. | 
| Chris@45 | 999 | 
| Chris@45 | 1000         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1001         Layer *replace = 0; | 
| Chris@45 | 1002 | 
| Chris@45 | 1003         for (int i = 0; i < pane->getLayerCount(); ++i) { | 
| Chris@45 | 1004             Layer *layer = pane->getLayer(i); | 
| Chris@45 | 1005             if (dynamic_cast<WaveformLayer *>(layer)) { | 
| Chris@45 | 1006                 replace = layer; | 
| Chris@45 | 1007                 break; | 
| Chris@45 | 1008             } | 
| Chris@45 | 1009         } | 
| Chris@45 | 1010 | 
| Chris@45 | 1011 	CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 1012 	    (tr("Import \"%1\"").arg(source.getLocation()), true); | 
| Chris@45 | 1013 | 
| Chris@45 | 1014 	m_document->addImportedModel(newModel); | 
| Chris@45 | 1015 | 
| Chris@45 | 1016         if (replace) { | 
| Chris@45 | 1017             m_document->removeLayerFromView(pane, replace); | 
| Chris@45 | 1018         } | 
| Chris@45 | 1019 | 
| Chris@45 | 1020 	Layer *newLayer = m_document->createImportedLayer(newModel); | 
| Chris@45 | 1021 | 
| Chris@45 | 1022 	if (newLayer) { | 
| Chris@45 | 1023 	    m_document->addLayerToView(pane, newLayer); | 
| Chris@45 | 1024 	} | 
| Chris@45 | 1025 | 
| Chris@45 | 1026 	CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 1027     } | 
| Chris@45 | 1028 | 
| Chris@45 | 1029     updateMenuStates(); | 
| Chris@45 | 1030     m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1031     if (!source.isRemote()) { | 
| Chris@45 | 1032         // for file dialog | 
| Chris@45 | 1033         registerLastOpenedFilePath(FileFinder::AudioFile, | 
| Chris@45 | 1034                                    source.getLocalFilename()); | 
| Chris@45 | 1035     } | 
| Chris@45 | 1036     m_openingAudioFile = false; | 
| Chris@45 | 1037 | 
| Chris@45 | 1038     currentPaneChanged(m_paneStack->getCurrentPane()); | 
| Chris@45 | 1039 | 
| Chris@45 | 1040     return FileOpenSucceeded; | 
| Chris@45 | 1041 } | 
| Chris@45 | 1042 | 
| Chris@45 | 1043 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1044 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode) | 
| Chris@45 | 1045 { | 
| Chris@45 | 1046     std::set<QString> extensions; | 
| Chris@45 | 1047     PlaylistFileReader::getSupportedExtensions(extensions); | 
| Chris@45 | 1048     QString extension = source.getExtension(); | 
| Chris@45 | 1049     if (extensions.find(extension) == extensions.end()) return FileOpenFailed; | 
| Chris@45 | 1050 | 
| Chris@45 | 1051     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1052     source.waitForData(); | 
| Chris@45 | 1053 | 
| Chris@45 | 1054     PlaylistFileReader reader(source.getLocalFilename()); | 
| Chris@45 | 1055     if (!reader.isOK()) return FileOpenFailed; | 
| Chris@45 | 1056 | 
| Chris@45 | 1057     PlaylistFileReader::Playlist playlist = reader.load(); | 
| Chris@45 | 1058 | 
| Chris@45 | 1059     bool someSuccess = false; | 
| Chris@45 | 1060 | 
| Chris@45 | 1061     for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); | 
| Chris@45 | 1062          i != playlist.end(); ++i) { | 
| Chris@45 | 1063 | 
| Chris@109 | 1064         ProgressDialog dialog(tr("Opening playlist..."), true, 2000); | 
| Chris@109 | 1065         FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode); | 
| Chris@45 | 1066 | 
| Chris@45 | 1067         if (status == FileOpenCancelled) { | 
| Chris@45 | 1068             return FileOpenCancelled; | 
| Chris@45 | 1069         } | 
| Chris@45 | 1070 | 
| Chris@45 | 1071         if (status == FileOpenSucceeded) { | 
| Chris@45 | 1072             someSuccess = true; | 
| Chris@45 | 1073             mode = CreateAdditionalModel; | 
| Chris@45 | 1074         } | 
| Chris@45 | 1075     } | 
| Chris@45 | 1076 | 
| Chris@45 | 1077     if (someSuccess) return FileOpenSucceeded; | 
| Chris@45 | 1078     else return FileOpenFailed; | 
| Chris@45 | 1079 } | 
| Chris@45 | 1080 | 
| Chris@45 | 1081 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1082 MainWindowBase::openLayer(FileSource source) | 
| Chris@45 | 1083 { | 
| Chris@45 | 1084     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1085 | 
| Chris@45 | 1086     if (!pane) { | 
| Chris@45 | 1087 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@45 | 1088 	std::cerr << "WARNING: MainWindowBase::openLayer: no current pane" << std::endl; | 
| Chris@45 | 1089 	return FileOpenWrongMode; | 
| Chris@45 | 1090     } | 
| Chris@45 | 1091 | 
| Chris@45 | 1092     if (!getMainModel()) { | 
| Chris@45 | 1093 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@45 | 1094 	std::cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << std::endl; | 
| Chris@45 | 1095 	return FileOpenWrongMode; | 
| Chris@45 | 1096     } | 
| Chris@45 | 1097 | 
| Chris@45 | 1098     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1099     source.waitForData(); | 
| Chris@45 | 1100 | 
| Chris@45 | 1101     QString path = source.getLocalFilename(); | 
| Chris@45 | 1102 | 
| Chris@45 | 1103     if (source.getExtension() == "svl" || source.getExtension() == "xml") { | 
| Chris@45 | 1104 | 
| Chris@45 | 1105         PaneCallback callback(this); | 
| Chris@45 | 1106         QFile file(path); | 
| Chris@45 | 1107 | 
| Chris@45 | 1108         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | 
| Chris@45 | 1109             std::cerr << "ERROR: MainWindowBase::openLayer(" | 
| Chris@45 | 1110                       << source.getLocation().toStdString() | 
| Chris@45 | 1111                       << "): Failed to open file for reading" << std::endl; | 
| Chris@45 | 1112             return FileOpenFailed; | 
| Chris@45 | 1113         } | 
| Chris@45 | 1114 | 
| Chris@45 | 1115         SVFileReader reader(m_document, callback, source.getLocation()); | 
| Chris@79 | 1116         connect | 
| Chris@79 | 1117             (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), | 
| Chris@79 | 1118              this, SLOT(modelRegenerationFailed(QString, QString, QString))); | 
| Chris@79 | 1119         connect | 
| Chris@79 | 1120             (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@79 | 1121              this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@45 | 1122         reader.setCurrentPane(pane); | 
| Chris@45 | 1123 | 
| Chris@45 | 1124         QXmlInputSource inputSource(&file); | 
| Chris@45 | 1125         reader.parse(inputSource); | 
| Chris@45 | 1126 | 
| Chris@45 | 1127         if (!reader.isOK()) { | 
| Chris@45 | 1128             std::cerr << "ERROR: MainWindowBase::openLayer(" | 
| Chris@45 | 1129                       << source.getLocation().toStdString() | 
| Chris@45 | 1130                       << "): Failed to read XML file: " | 
| Chris@45 | 1131                       << reader.getErrorString().toStdString() << std::endl; | 
| Chris@45 | 1132             return FileOpenFailed; | 
| Chris@45 | 1133         } | 
| Chris@45 | 1134 | 
| Chris@45 | 1135         m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1136 | 
| Chris@45 | 1137         if (!source.isRemote()) { | 
| Chris@45 | 1138             registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog | 
| Chris@45 | 1139         } | 
| Chris@45 | 1140 | 
| Chris@75 | 1141         return FileOpenSucceeded; | 
| Chris@75 | 1142 | 
| Chris@45 | 1143     } else { | 
| Chris@45 | 1144 | 
| Chris@45 | 1145         try { | 
| Chris@45 | 1146 | 
| Chris@109 | 1147             MIDIFileImportDialog midiDlg(this); | 
| Chris@109 | 1148 | 
| Chris@109 | 1149             Model *model = DataFileReaderFactory::loadNonCSV | 
| Chris@109 | 1150                 (path, &midiDlg, getMainModel()->getSampleRate()); | 
| Chris@45 | 1151 | 
| Chris@109 | 1152             if (!model) { | 
| Chris@109 | 1153                 CSVFormatDialog *dialog = new CSVFormatDialog | 
| Chris@109 | 1154                     (this, CSVFormat(path), getMainModel()->getSampleRate()); | 
| Chris@109 | 1155                 if (dialog->exec() == QDialog::Accepted) { | 
| Chris@109 | 1156                     model = DataFileReaderFactory::loadCSV | 
| Chris@109 | 1157                         (path, dialog->getFormat(), | 
| Chris@109 | 1158                          getMainModel()->getSampleRate()); | 
| Chris@109 | 1159                 } | 
| Chris@109 | 1160             } | 
| Chris@109 | 1161 | 
| Chris@45 | 1162             if (model) { | 
| Chris@45 | 1163 | 
| Chris@45 | 1164                 std::cerr << "MainWindowBase::openLayer: Have model" << std::endl; | 
| Chris@45 | 1165 | 
| Chris@45 | 1166                 Layer *newLayer = m_document->createImportedLayer(model); | 
| Chris@45 | 1167 | 
| Chris@45 | 1168                 if (newLayer) { | 
| Chris@45 | 1169 | 
| Chris@45 | 1170                     m_document->addLayerToView(pane, newLayer); | 
| Chris@88 | 1171                     m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@88 | 1172 | 
| Chris@45 | 1173                     m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1174 | 
| Chris@45 | 1175                     if (!source.isRemote()) { | 
| Chris@45 | 1176                         registerLastOpenedFilePath | 
| Chris@45 | 1177                             (FileFinder::LayerFile, | 
| Chris@45 | 1178                              path); // for file dialog | 
| Chris@45 | 1179                     } | 
| Chris@88 | 1180 | 
| Chris@45 | 1181                     return FileOpenSucceeded; | 
| Chris@45 | 1182                 } | 
| Chris@45 | 1183             } | 
| Chris@45 | 1184         } catch (DataFileReaderFactory::Exception e) { | 
| Chris@45 | 1185             if (e == DataFileReaderFactory::ImportCancelled) { | 
| Chris@45 | 1186                 return FileOpenCancelled; | 
| Chris@45 | 1187             } | 
| Chris@45 | 1188         } | 
| Chris@45 | 1189     } | 
| Chris@45 | 1190 | 
| Chris@45 | 1191     return FileOpenFailed; | 
| Chris@45 | 1192 } | 
| Chris@45 | 1193 | 
| Chris@45 | 1194 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1195 MainWindowBase::openImage(FileSource source) | 
| Chris@45 | 1196 { | 
| Chris@45 | 1197     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1198 | 
| Chris@45 | 1199     if (!pane) { | 
| Chris@45 | 1200 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@45 | 1201 	std::cerr << "WARNING: MainWindowBase::openImage: no current pane" << std::endl; | 
| Chris@45 | 1202 	return FileOpenWrongMode; | 
| Chris@45 | 1203     } | 
| Chris@45 | 1204 | 
| Chris@45 | 1205     if (!m_document->getMainModel()) { | 
| Chris@45 | 1206         return FileOpenWrongMode; | 
| Chris@45 | 1207     } | 
| Chris@45 | 1208 | 
| Chris@45 | 1209     bool newLayer = false; | 
| Chris@45 | 1210 | 
| Chris@45 | 1211     ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer()); | 
| Chris@45 | 1212     if (!il) { | 
| Chris@45 | 1213         for (int i = pane->getLayerCount()-1; i >= 0; --i) { | 
| Chris@45 | 1214             il = dynamic_cast<ImageLayer *>(pane->getLayer(i)); | 
| Chris@45 | 1215             if (il) break; | 
| Chris@45 | 1216         } | 
| Chris@45 | 1217     } | 
| Chris@45 | 1218     if (!il) { | 
| Chris@45 | 1219         il = dynamic_cast<ImageLayer *> | 
| Chris@45 | 1220             (m_document->createEmptyLayer(LayerFactory::Image)); | 
| Chris@45 | 1221         if (!il) return FileOpenFailed; | 
| Chris@45 | 1222         newLayer = true; | 
| Chris@45 | 1223     } | 
| Chris@45 | 1224 | 
| Chris@45 | 1225     // We don't put the image file in Recent Files | 
| Chris@45 | 1226 | 
| Chris@45 | 1227     std::cerr << "openImage: trying location \"" << source.getLocation().toStdString() << "\" in image layer" << std::endl; | 
| Chris@45 | 1228 | 
| Chris@45 | 1229     if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) { | 
| Chris@45 | 1230         if (newLayer) { | 
| Chris@52 | 1231             m_document->deleteLayer(il); // also releases its model | 
| Chris@45 | 1232         } | 
| Chris@45 | 1233         return FileOpenFailed; | 
| Chris@45 | 1234     } else { | 
| Chris@45 | 1235         if (newLayer) { | 
| Chris@45 | 1236             m_document->addLayerToView(pane, il); | 
| Chris@45 | 1237         } | 
| Chris@45 | 1238         m_paneStack->setCurrentLayer(pane, il); | 
| Chris@45 | 1239     } | 
| Chris@45 | 1240 | 
| Chris@45 | 1241     return FileOpenSucceeded; | 
| Chris@45 | 1242 } | 
| Chris@45 | 1243 | 
| Chris@45 | 1244 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1245 MainWindowBase::openSessionFile(QString fileOrUrl) | 
| Chris@45 | 1246 { | 
| Chris@109 | 1247     ProgressDialog dialog(tr("Opening session..."), true, 2000); | 
| Chris@109 | 1248     return openSession(FileSource(fileOrUrl, &dialog)); | 
| Chris@45 | 1249 } | 
| Chris@45 | 1250 | 
| Chris@45 | 1251 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1252 MainWindowBase::openSession(FileSource source) | 
| Chris@45 | 1253 { | 
| Chris@45 | 1254     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1255     if (source.getExtension() != "sv") return FileOpenFailed; | 
| Chris@45 | 1256     source.waitForData(); | 
| Chris@45 | 1257 | 
| Chris@45 | 1258     BZipFileDevice bzFile(source.getLocalFilename()); | 
| Chris@45 | 1259     if (!bzFile.open(QIODevice::ReadOnly)) return FileOpenFailed; | 
| Chris@45 | 1260 | 
| Chris@45 | 1261     if (!checkSaveModified()) return FileOpenCancelled; | 
| Chris@45 | 1262 | 
| Chris@45 | 1263     QString error; | 
| Chris@45 | 1264     closeSession(); | 
| Chris@45 | 1265     createDocument(); | 
| Chris@45 | 1266 | 
| Chris@45 | 1267     PaneCallback callback(this); | 
| Chris@45 | 1268     m_viewManager->clearSelections(); | 
| Chris@45 | 1269 | 
| Chris@45 | 1270     SVFileReader reader(m_document, callback, source.getLocation()); | 
| Chris@79 | 1271     connect | 
| Chris@79 | 1272         (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), | 
| Chris@79 | 1273          this, SLOT(modelRegenerationFailed(QString, QString, QString))); | 
| Chris@79 | 1274     connect | 
| Chris@79 | 1275         (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@79 | 1276          this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@45 | 1277     QXmlInputSource inputSource(&bzFile); | 
| Chris@45 | 1278     reader.parse(inputSource); | 
| Chris@45 | 1279 | 
| Chris@45 | 1280     if (!reader.isOK()) { | 
| Chris@45 | 1281         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); | 
| Chris@45 | 1282     } | 
| Chris@45 | 1283 | 
| Chris@45 | 1284     bzFile.close(); | 
| Chris@45 | 1285 | 
| Chris@45 | 1286     bool ok = (error == ""); | 
| Chris@45 | 1287 | 
| Chris@45 | 1288     if (ok) { | 
| Chris@45 | 1289 | 
| Chris@57 | 1290 	setWindowTitle(tr("%1: %2") | 
| Chris@57 | 1291                        .arg(QApplication::applicationName()) | 
| Chris@45 | 1292 		       .arg(source.getLocation())); | 
| Chris@45 | 1293 | 
| Chris@45 | 1294 	if (!source.isRemote()) m_sessionFile = source.getLocalFilename(); | 
| Chris@45 | 1295 | 
| Chris@45 | 1296 	setupMenus(); | 
| Chris@45 | 1297 | 
| Chris@45 | 1298 	CommandHistory::getInstance()->clear(); | 
| Chris@45 | 1299 	CommandHistory::getInstance()->documentSaved(); | 
| Chris@45 | 1300 	m_documentModified = false; | 
| Chris@45 | 1301 	updateMenuStates(); | 
| Chris@45 | 1302 | 
| Chris@45 | 1303         m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1304 | 
| Chris@45 | 1305         if (!source.isRemote()) { | 
| Chris@45 | 1306             // for file dialog | 
| Chris@45 | 1307             registerLastOpenedFilePath(FileFinder::SessionFile, | 
| Chris@45 | 1308                                         source.getLocalFilename()); | 
| Chris@45 | 1309         } | 
| Chris@45 | 1310 | 
| Chris@45 | 1311     } else { | 
| Chris@57 | 1312 	setWindowTitle(QApplication::applicationName()); | 
| Chris@45 | 1313     } | 
| Chris@45 | 1314 | 
| Chris@45 | 1315     return ok ? FileOpenSucceeded : FileOpenFailed; | 
| Chris@45 | 1316 } | 
| Chris@45 | 1317 | 
| Chris@45 | 1318 void | 
| Chris@45 | 1319 MainWindowBase::createPlayTarget() | 
| Chris@45 | 1320 { | 
| Chris@45 | 1321     if (m_playTarget) return; | 
| Chris@45 | 1322 | 
| Chris@45 | 1323     m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); | 
| Chris@45 | 1324     if (!m_playTarget) { | 
| Chris@104 | 1325         emit hideSplash(); | 
| Chris@45 | 1326 	QMessageBox::warning | 
| Chris@45 | 1327 	    (this, tr("Couldn't open audio device"), | 
| Chris@45 | 1328 	     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."), | 
| Chris@45 | 1329 	     QMessageBox::Ok); | 
| Chris@45 | 1330     } | 
| Chris@45 | 1331 } | 
| Chris@45 | 1332 | 
| Chris@45 | 1333 WaveFileModel * | 
| Chris@45 | 1334 MainWindowBase::getMainModel() | 
| Chris@45 | 1335 { | 
| Chris@45 | 1336     if (!m_document) return 0; | 
| Chris@45 | 1337     return m_document->getMainModel(); | 
| Chris@45 | 1338 } | 
| Chris@45 | 1339 | 
| Chris@45 | 1340 const WaveFileModel * | 
| Chris@45 | 1341 MainWindowBase::getMainModel() const | 
| Chris@45 | 1342 { | 
| Chris@45 | 1343     if (!m_document) return 0; | 
| Chris@45 | 1344     return m_document->getMainModel(); | 
| Chris@45 | 1345 } | 
| Chris@45 | 1346 | 
| Chris@45 | 1347 void | 
| Chris@45 | 1348 MainWindowBase::createDocument() | 
| Chris@45 | 1349 { | 
| Chris@45 | 1350     m_document = new Document; | 
| Chris@45 | 1351 | 
| Chris@45 | 1352     connect(m_document, SIGNAL(layerAdded(Layer *)), | 
| Chris@45 | 1353 	    this, SLOT(layerAdded(Layer *))); | 
| Chris@45 | 1354     connect(m_document, SIGNAL(layerRemoved(Layer *)), | 
| Chris@45 | 1355 	    this, SLOT(layerRemoved(Layer *))); | 
| Chris@45 | 1356     connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), | 
| Chris@45 | 1357 	    this, SLOT(layerAboutToBeDeleted(Layer *))); | 
| Chris@45 | 1358     connect(m_document, SIGNAL(layerInAView(Layer *, bool)), | 
| Chris@45 | 1359 	    this, SLOT(layerInAView(Layer *, bool))); | 
| Chris@45 | 1360 | 
| Chris@45 | 1361     connect(m_document, SIGNAL(modelAdded(Model *)), | 
| Chris@45 | 1362 	    this, SLOT(modelAdded(Model *))); | 
| Chris@45 | 1363     connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), | 
| Chris@45 | 1364 	    this, SLOT(mainModelChanged(WaveFileModel *))); | 
| Chris@45 | 1365     connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), | 
| Chris@45 | 1366 	    this, SLOT(modelAboutToBeDeleted(Model *))); | 
| Chris@45 | 1367 | 
| Chris@78 | 1368     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), | 
| Chris@78 | 1369             this, SLOT(modelGenerationFailed(QString, QString))); | 
| Chris@78 | 1370     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@78 | 1371             this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@78 | 1372     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), | 
| Chris@78 | 1373             this, SLOT(modelGenerationFailed(QString, QString))); | 
| Chris@78 | 1374     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@78 | 1375             this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@78 | 1376     connect(m_document, SIGNAL(alignmentFailed(QString, QString)), | 
| Chris@78 | 1377             this, SLOT(alignmentFailed(QString, QString))); | 
| Chris@45 | 1378 } | 
| Chris@45 | 1379 | 
| Chris@45 | 1380 bool | 
| Chris@45 | 1381 MainWindowBase::saveSessionFile(QString path) | 
| Chris@45 | 1382 { | 
| Chris@45 | 1383     BZipFileDevice bzFile(path); | 
| Chris@45 | 1384     if (!bzFile.open(QIODevice::WriteOnly)) { | 
| Chris@45 | 1385         std::cerr << "Failed to open session file \"" << path.toStdString() | 
| Chris@45 | 1386                   << "\" for writing: " | 
| Chris@45 | 1387                   << bzFile.errorString().toStdString() << std::endl; | 
| Chris@45 | 1388         return false; | 
| Chris@45 | 1389     } | 
| Chris@45 | 1390 | 
| Chris@45 | 1391     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); | 
| Chris@45 | 1392 | 
| Chris@45 | 1393     QTextStream out(&bzFile); | 
| Chris@45 | 1394     toXml(out); | 
| Chris@45 | 1395     out.flush(); | 
| Chris@45 | 1396 | 
| Chris@45 | 1397     QApplication::restoreOverrideCursor(); | 
| Chris@45 | 1398 | 
| Chris@45 | 1399     if (!bzFile.isOK()) { | 
| Chris@45 | 1400 	QMessageBox::critical(this, tr("Failed to write file"), | 
| Chris@45 | 1401 			      tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") | 
| Chris@45 | 1402 			      .arg(path).arg(bzFile.errorString())); | 
| Chris@45 | 1403         bzFile.close(); | 
| Chris@45 | 1404 	return false; | 
| Chris@45 | 1405     } | 
| Chris@45 | 1406 | 
| Chris@45 | 1407     bzFile.close(); | 
| Chris@45 | 1408     return true; | 
| Chris@45 | 1409 } | 
| Chris@45 | 1410 | 
| Chris@45 | 1411 void | 
| Chris@45 | 1412 MainWindowBase::toXml(QTextStream &out) | 
| Chris@45 | 1413 { | 
| Chris@45 | 1414     QString indent("  "); | 
| Chris@45 | 1415 | 
| Chris@45 | 1416     out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | 
| Chris@45 | 1417     out << "<!DOCTYPE sonic-visualiser>\n"; | 
| Chris@45 | 1418     out << "<sv>\n"; | 
| Chris@45 | 1419 | 
| Chris@45 | 1420     m_document->toXml(out, "", ""); | 
| Chris@45 | 1421 | 
| Chris@45 | 1422     out << "<display>\n"; | 
| Chris@45 | 1423 | 
| Chris@45 | 1424     out << QString("  <window width=\"%1\" height=\"%2\"/>\n") | 
| Chris@45 | 1425 	.arg(width()).arg(height()); | 
| Chris@45 | 1426 | 
| Chris@45 | 1427     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@45 | 1428 | 
| Chris@45 | 1429 	Pane *pane = m_paneStack->getPane(i); | 
| Chris@45 | 1430 | 
| Chris@45 | 1431 	if (pane) { | 
| Chris@45 | 1432             pane->toXml(out, indent); | 
| Chris@45 | 1433 	} | 
| Chris@45 | 1434     } | 
| Chris@45 | 1435 | 
| Chris@45 | 1436     out << "</display>\n"; | 
| Chris@45 | 1437 | 
| Chris@45 | 1438     m_viewManager->getSelection().toXml(out); | 
| Chris@45 | 1439 | 
| Chris@45 | 1440     out << "</sv>\n"; | 
| Chris@45 | 1441 } | 
| Chris@45 | 1442 | 
| Chris@45 | 1443 Pane * | 
| Chris@45 | 1444 MainWindowBase::addPaneToStack() | 
| Chris@45 | 1445 { | 
| Chris@45 | 1446     AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@45 | 1447     CommandHistory::getInstance()->addCommand(command); | 
| Chris@57 | 1448     Pane *pane = command->getPane(); | 
| Chris@57 | 1449     return pane; | 
| Chris@45 | 1450 } | 
| Chris@45 | 1451 | 
| Chris@45 | 1452 void | 
| Chris@45 | 1453 MainWindowBase::zoomIn() | 
| Chris@45 | 1454 { | 
| Chris@45 | 1455     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1456     if (currentPane) currentPane->zoom(true); | 
| Chris@45 | 1457 } | 
| Chris@45 | 1458 | 
| Chris@45 | 1459 void | 
| Chris@45 | 1460 MainWindowBase::zoomOut() | 
| Chris@45 | 1461 { | 
| Chris@45 | 1462     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1463     if (currentPane) currentPane->zoom(false); | 
| Chris@45 | 1464 } | 
| Chris@45 | 1465 | 
| Chris@45 | 1466 void | 
| Chris@45 | 1467 MainWindowBase::zoomToFit() | 
| Chris@45 | 1468 { | 
| Chris@45 | 1469     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1470     if (!currentPane) return; | 
| Chris@45 | 1471 | 
| Chris@45 | 1472     Model *model = getMainModel(); | 
| Chris@45 | 1473     if (!model) return; | 
| Chris@45 | 1474 | 
| Chris@45 | 1475     size_t start = model->getStartFrame(); | 
| Chris@45 | 1476     size_t end = model->getEndFrame(); | 
| Chris@60 | 1477     if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame()); | 
| Chris@45 | 1478     size_t pixels = currentPane->width(); | 
| Chris@45 | 1479 | 
| Chris@45 | 1480     size_t sw = currentPane->getVerticalScaleWidth(); | 
| Chris@45 | 1481     if (pixels > sw * 2) pixels -= sw * 2; | 
| Chris@45 | 1482     else pixels = 1; | 
| Chris@45 | 1483     if (pixels > 4) pixels -= 4; | 
| Chris@45 | 1484 | 
| Chris@45 | 1485     size_t zoomLevel = (end - start) / pixels; | 
| Chris@45 | 1486 | 
| Chris@45 | 1487     currentPane->setZoomLevel(zoomLevel); | 
| Chris@45 | 1488     currentPane->setCentreFrame((start + end) / 2); | 
| Chris@45 | 1489 } | 
| Chris@45 | 1490 | 
| Chris@45 | 1491 void | 
| Chris@45 | 1492 MainWindowBase::zoomDefault() | 
| Chris@45 | 1493 { | 
| Chris@45 | 1494     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1495     if (currentPane) currentPane->setZoomLevel(1024); | 
| Chris@45 | 1496 } | 
| Chris@45 | 1497 | 
| Chris@45 | 1498 void | 
| Chris@45 | 1499 MainWindowBase::scrollLeft() | 
| Chris@45 | 1500 { | 
| Chris@45 | 1501     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1502     if (currentPane) currentPane->scroll(false, false); | 
| Chris@45 | 1503 } | 
| Chris@45 | 1504 | 
| Chris@45 | 1505 void | 
| Chris@45 | 1506 MainWindowBase::jumpLeft() | 
| Chris@45 | 1507 { | 
| Chris@45 | 1508     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1509     if (currentPane) currentPane->scroll(false, true); | 
| Chris@45 | 1510 } | 
| Chris@45 | 1511 | 
| Chris@45 | 1512 void | 
| Chris@45 | 1513 MainWindowBase::scrollRight() | 
| Chris@45 | 1514 { | 
| Chris@45 | 1515     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1516     if (currentPane) currentPane->scroll(true, false); | 
| Chris@45 | 1517 } | 
| Chris@45 | 1518 | 
| Chris@45 | 1519 void | 
| Chris@45 | 1520 MainWindowBase::jumpRight() | 
| Chris@45 | 1521 { | 
| Chris@45 | 1522     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1523     if (currentPane) currentPane->scroll(true, true); | 
| Chris@45 | 1524 } | 
| Chris@45 | 1525 | 
| Chris@45 | 1526 void | 
| Chris@45 | 1527 MainWindowBase::showNoOverlays() | 
| Chris@45 | 1528 { | 
| Chris@45 | 1529     m_viewManager->setOverlayMode(ViewManager::NoOverlays); | 
| Chris@45 | 1530 } | 
| Chris@45 | 1531 | 
| Chris@45 | 1532 void | 
| Chris@45 | 1533 MainWindowBase::showMinimalOverlays() | 
| Chris@45 | 1534 { | 
| Chris@45 | 1535     m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); | 
| Chris@45 | 1536 } | 
| Chris@45 | 1537 | 
| Chris@45 | 1538 void | 
| Chris@45 | 1539 MainWindowBase::showStandardOverlays() | 
| Chris@45 | 1540 { | 
| Chris@45 | 1541     m_viewManager->setOverlayMode(ViewManager::StandardOverlays); | 
| Chris@45 | 1542 } | 
| Chris@45 | 1543 | 
| Chris@45 | 1544 void | 
| Chris@45 | 1545 MainWindowBase::showAllOverlays() | 
| Chris@45 | 1546 { | 
| Chris@45 | 1547     m_viewManager->setOverlayMode(ViewManager::AllOverlays); | 
| Chris@45 | 1548 } | 
| Chris@45 | 1549 | 
| Chris@45 | 1550 void | 
| Chris@45 | 1551 MainWindowBase::toggleZoomWheels() | 
| Chris@45 | 1552 { | 
| Chris@45 | 1553     if (m_viewManager->getZoomWheelsEnabled()) { | 
| Chris@45 | 1554         m_viewManager->setZoomWheelsEnabled(false); | 
| Chris@45 | 1555     } else { | 
| Chris@45 | 1556         m_viewManager->setZoomWheelsEnabled(true); | 
| Chris@45 | 1557     } | 
| Chris@45 | 1558 } | 
| Chris@45 | 1559 | 
| Chris@45 | 1560 void | 
| Chris@45 | 1561 MainWindowBase::togglePropertyBoxes() | 
| Chris@45 | 1562 { | 
| Chris@45 | 1563     if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { | 
| Chris@45 | 1564         if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@45 | 1565             Preferences::VerticallyStacked) { | 
| Chris@45 | 1566             m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@45 | 1567         } else { | 
| Chris@45 | 1568             m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@45 | 1569         } | 
| Chris@45 | 1570     } else { | 
| Chris@45 | 1571         m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); | 
| Chris@45 | 1572     } | 
| Chris@45 | 1573 } | 
| Chris@45 | 1574 | 
| Chris@45 | 1575 void | 
| Chris@45 | 1576 MainWindowBase::toggleStatusBar() | 
| Chris@45 | 1577 { | 
| Chris@45 | 1578     QSettings settings; | 
| Chris@45 | 1579     settings.beginGroup("MainWindow"); | 
| Chris@45 | 1580     bool sb = settings.value("showstatusbar", true).toBool(); | 
| Chris@45 | 1581 | 
| Chris@45 | 1582     if (sb) { | 
| Chris@45 | 1583         statusBar()->hide(); | 
| Chris@45 | 1584     } else { | 
| Chris@45 | 1585         statusBar()->show(); | 
| Chris@45 | 1586     } | 
| Chris@45 | 1587 | 
| Chris@45 | 1588     settings.setValue("showstatusbar", !sb); | 
| Chris@45 | 1589 | 
| Chris@45 | 1590     settings.endGroup(); | 
| Chris@45 | 1591 } | 
| Chris@45 | 1592 | 
| Chris@45 | 1593 void | 
| Chris@45 | 1594 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name) | 
| Chris@45 | 1595 { | 
| Chris@45 | 1596     if (name == "Property Box Layout") { | 
| Chris@45 | 1597         if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { | 
| Chris@45 | 1598             if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@45 | 1599                 Preferences::VerticallyStacked) { | 
| Chris@45 | 1600                 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@45 | 1601             } else { | 
| Chris@45 | 1602                 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@45 | 1603             } | 
| Chris@45 | 1604         } | 
| Chris@45 | 1605     } else if (name == "Background Mode" && m_viewManager) { | 
| Chris@45 | 1606         Preferences::BackgroundMode mode = | 
| Chris@45 | 1607             Preferences::getInstance()->getBackgroundMode(); | 
| Chris@45 | 1608         if (mode == Preferences::BackgroundFromTheme) { | 
| Chris@45 | 1609             m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); | 
| Chris@45 | 1610         } else if (mode == Preferences::DarkBackground) { | 
| Chris@45 | 1611             m_viewManager->setGlobalDarkBackground(true); | 
| Chris@45 | 1612         } else { | 
| Chris@45 | 1613             m_viewManager->setGlobalDarkBackground(false); | 
| Chris@45 | 1614         } | 
| Chris@45 | 1615     } | 
| Chris@45 | 1616 } | 
| Chris@45 | 1617 | 
| Chris@45 | 1618 void | 
| Chris@45 | 1619 MainWindowBase::play() | 
| Chris@45 | 1620 { | 
| Chris@45 | 1621     if (m_playSource->isPlaying()) { | 
| Chris@45 | 1622         stop(); | 
| Chris@45 | 1623     } else { | 
| Chris@45 | 1624         playbackFrameChanged(m_viewManager->getPlaybackFrame()); | 
| Chris@45 | 1625 	m_playSource->play(m_viewManager->getPlaybackFrame()); | 
| Chris@45 | 1626     } | 
| Chris@45 | 1627 } | 
| Chris@45 | 1628 | 
| Chris@45 | 1629 void | 
| Chris@45 | 1630 MainWindowBase::ffwd() | 
| Chris@45 | 1631 { | 
| Chris@45 | 1632     if (!getMainModel()) return; | 
| Chris@45 | 1633 | 
| Chris@45 | 1634     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@45 | 1635     ++frame; | 
| Chris@45 | 1636 | 
| Chris@85 | 1637     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1638     Layer *layer = getSnapLayer(); | 
| Chris@45 | 1639     size_t sr = getMainModel()->getSampleRate(); | 
| Chris@45 | 1640 | 
| Chris@45 | 1641     if (!layer) { | 
| Chris@45 | 1642 | 
| Chris@45 | 1643         frame = RealTime::realTime2Frame | 
| Chris@45 | 1644             (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr); | 
| Chris@45 | 1645         if (frame > int(getMainModel()->getEndFrame())) { | 
| Chris@45 | 1646             frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 1647         } | 
| Chris@45 | 1648 | 
| Chris@45 | 1649     } else { | 
| Chris@45 | 1650 | 
| Chris@45 | 1651         size_t resolution = 0; | 
| Chris@85 | 1652         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), | 
| Chris@85 | 1653                                       frame, resolution, Layer::SnapRight)) { | 
| Chris@85 | 1654             if (pane) frame = pane->alignToReference(frame); | 
| Chris@85 | 1655         } else { | 
| Chris@45 | 1656             frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 1657         } | 
| Chris@45 | 1658     } | 
| Chris@45 | 1659 | 
| Chris@45 | 1660     if (frame < 0) frame = 0; | 
| Chris@45 | 1661 | 
| Chris@45 | 1662     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 1663         frame = m_viewManager->constrainFrameToSelection(size_t(frame)); | 
| Chris@45 | 1664     } | 
| Chris@45 | 1665 | 
| Chris@45 | 1666     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 1667 } | 
| Chris@45 | 1668 | 
| Chris@45 | 1669 void | 
| Chris@45 | 1670 MainWindowBase::ffwdEnd() | 
| Chris@45 | 1671 { | 
| Chris@45 | 1672     if (!getMainModel()) return; | 
| Chris@45 | 1673 | 
| Chris@45 | 1674     size_t frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 1675 | 
| Chris@45 | 1676     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 1677         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 1678     } | 
| Chris@45 | 1679 | 
| Chris@45 | 1680     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 1681 } | 
| Chris@45 | 1682 | 
| Chris@45 | 1683 void | 
| Chris@45 | 1684 MainWindowBase::rewind() | 
| Chris@45 | 1685 { | 
| Chris@45 | 1686     if (!getMainModel()) return; | 
| Chris@45 | 1687 | 
| Chris@45 | 1688     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@45 | 1689     if (frame > 0) --frame; | 
| Chris@45 | 1690 | 
| Chris@85 | 1691     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1692     Layer *layer = getSnapLayer(); | 
| Chris@45 | 1693     size_t sr = getMainModel()->getSampleRate(); | 
| Chris@45 | 1694 | 
| Chris@45 | 1695     // when rewinding during playback, we want to allow a period | 
| Chris@45 | 1696     // following a rewind target point at which the rewind will go to | 
| Chris@45 | 1697     // the prior point instead of the immediately neighbouring one | 
| Chris@45 | 1698     if (m_playSource && m_playSource->isPlaying()) { | 
| Chris@45 | 1699         RealTime ct = RealTime::frame2RealTime(frame, sr); | 
| Chris@45 | 1700         ct = ct - RealTime::fromSeconds(0.25); | 
| Chris@45 | 1701         if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; | 
| Chris@45 | 1702 //        std::cerr << "rewind: frame " << frame << " -> "; | 
| Chris@45 | 1703         frame = RealTime::realTime2Frame(ct, sr); | 
| Chris@45 | 1704 //        std::cerr << frame << std::endl; | 
| Chris@45 | 1705     } | 
| Chris@45 | 1706 | 
| Chris@45 | 1707     if (!layer) { | 
| Chris@45 | 1708 | 
| Chris@45 | 1709         frame = RealTime::realTime2Frame | 
| Chris@45 | 1710             (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr); | 
| Chris@45 | 1711         if (frame < int(getMainModel()->getStartFrame())) { | 
| Chris@45 | 1712             frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 1713         } | 
| Chris@45 | 1714 | 
| Chris@45 | 1715     } else { | 
| Chris@45 | 1716 | 
| Chris@45 | 1717         size_t resolution = 0; | 
| Chris@85 | 1718         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), | 
| Chris@85 | 1719                                       frame, resolution, Layer::SnapLeft)) { | 
| Chris@85 | 1720 | 
| Chris@85 | 1721             if (pane) frame = pane->alignToReference(frame); | 
| Chris@85 | 1722         } else { | 
| Chris@45 | 1723             frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 1724         } | 
| Chris@45 | 1725     } | 
| Chris@45 | 1726 | 
| Chris@45 | 1727     if (frame < 0) frame = 0; | 
| Chris@45 | 1728 | 
| Chris@45 | 1729     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 1730         frame = m_viewManager->constrainFrameToSelection(size_t(frame)); | 
| Chris@45 | 1731     } | 
| Chris@45 | 1732 | 
| Chris@45 | 1733     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 1734 } | 
| Chris@45 | 1735 | 
| Chris@45 | 1736 void | 
| Chris@45 | 1737 MainWindowBase::rewindStart() | 
| Chris@45 | 1738 { | 
| Chris@45 | 1739     if (!getMainModel()) return; | 
| Chris@45 | 1740 | 
| Chris@45 | 1741     size_t frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 1742 | 
| Chris@45 | 1743     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 1744         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 1745     } | 
| Chris@45 | 1746 | 
| Chris@45 | 1747     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 1748 } | 
| Chris@45 | 1749 | 
| Chris@45 | 1750 Layer * | 
| Chris@45 | 1751 MainWindowBase::getSnapLayer() const | 
| Chris@45 | 1752 { | 
| Chris@45 | 1753     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1754     if (!pane) return 0; | 
| Chris@45 | 1755 | 
| Chris@45 | 1756     Layer *layer = pane->getSelectedLayer(); | 
| Chris@45 | 1757 | 
| Chris@45 | 1758     if (!dynamic_cast<TimeInstantLayer *>(layer) && | 
| Chris@45 | 1759         !dynamic_cast<TimeValueLayer *>(layer) && | 
| Chris@45 | 1760         !dynamic_cast<TimeRulerLayer *>(layer)) { | 
| Chris@45 | 1761 | 
| Chris@45 | 1762         layer = 0; | 
| Chris@45 | 1763 | 
| Chris@45 | 1764         for (int i = pane->getLayerCount(); i > 0; --i) { | 
| Chris@45 | 1765             Layer *l = pane->getLayer(i-1); | 
| Chris@45 | 1766             if (dynamic_cast<TimeRulerLayer *>(l)) { | 
| Chris@45 | 1767                 layer = l; | 
| Chris@45 | 1768                 break; | 
| Chris@45 | 1769             } | 
| Chris@45 | 1770         } | 
| Chris@45 | 1771     } | 
| Chris@45 | 1772 | 
| Chris@45 | 1773     return layer; | 
| Chris@45 | 1774 } | 
| Chris@45 | 1775 | 
| Chris@45 | 1776 void | 
| Chris@45 | 1777 MainWindowBase::stop() | 
| Chris@45 | 1778 { | 
| Chris@45 | 1779     m_playSource->stop(); | 
| Chris@45 | 1780 | 
| Chris@45 | 1781     if (m_paneStack && m_paneStack->getCurrentPane()) { | 
| Chris@45 | 1782         updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); | 
| Chris@45 | 1783     } else { | 
| Chris@45 | 1784         m_myStatusMessage = ""; | 
| Chris@45 | 1785         statusBar()->showMessage(""); | 
| Chris@45 | 1786     } | 
| Chris@45 | 1787 } | 
| Chris@45 | 1788 | 
| Chris@45 | 1789 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) : | 
| Chris@45 | 1790     m_mw(mw), | 
| Chris@45 | 1791     m_pane(0), | 
| Chris@45 | 1792     m_prevCurrentPane(0), | 
| Chris@45 | 1793     m_added(false) | 
| Chris@45 | 1794 { | 
| Chris@45 | 1795 } | 
| Chris@45 | 1796 | 
| Chris@45 | 1797 MainWindowBase::AddPaneCommand::~AddPaneCommand() | 
| Chris@45 | 1798 { | 
| Chris@45 | 1799     if (m_pane && !m_added) { | 
| Chris@45 | 1800 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@45 | 1801     } | 
| Chris@45 | 1802 } | 
| Chris@45 | 1803 | 
| Chris@45 | 1804 QString | 
| Chris@45 | 1805 MainWindowBase::AddPaneCommand::getName() const | 
| Chris@45 | 1806 { | 
| Chris@45 | 1807     return tr("Add Pane"); | 
| Chris@45 | 1808 } | 
| Chris@45 | 1809 | 
| Chris@45 | 1810 void | 
| Chris@45 | 1811 MainWindowBase::AddPaneCommand::execute() | 
| Chris@45 | 1812 { | 
| Chris@45 | 1813     if (!m_pane) { | 
| Chris@45 | 1814 	m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@45 | 1815 	m_pane = m_mw->m_paneStack->addPane(); | 
| Chris@45 | 1816 | 
| Chris@45 | 1817         connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@45 | 1818                 m_mw, SLOT(contextHelpChanged(const QString &))); | 
| Chris@45 | 1819     } else { | 
| Chris@45 | 1820 	m_mw->m_paneStack->showPane(m_pane); | 
| Chris@45 | 1821     } | 
| Chris@45 | 1822 | 
| Chris@45 | 1823     m_mw->m_paneStack->setCurrentPane(m_pane); | 
| Chris@45 | 1824     m_added = true; | 
| Chris@45 | 1825 } | 
| Chris@45 | 1826 | 
| Chris@45 | 1827 void | 
| Chris@45 | 1828 MainWindowBase::AddPaneCommand::unexecute() | 
| Chris@45 | 1829 { | 
| Chris@45 | 1830     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@45 | 1831     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@45 | 1832     m_added = false; | 
| Chris@45 | 1833 } | 
| Chris@45 | 1834 | 
| Chris@45 | 1835 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) : | 
| Chris@45 | 1836     m_mw(mw), | 
| Chris@45 | 1837     m_pane(pane), | 
| Chris@45 | 1838     m_added(true) | 
| Chris@45 | 1839 { | 
| Chris@45 | 1840 } | 
| Chris@45 | 1841 | 
| Chris@45 | 1842 MainWindowBase::RemovePaneCommand::~RemovePaneCommand() | 
| Chris@45 | 1843 { | 
| Chris@45 | 1844     if (m_pane && !m_added) { | 
| Chris@45 | 1845 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@45 | 1846     } | 
| Chris@45 | 1847 } | 
| Chris@45 | 1848 | 
| Chris@45 | 1849 QString | 
| Chris@45 | 1850 MainWindowBase::RemovePaneCommand::getName() const | 
| Chris@45 | 1851 { | 
| Chris@45 | 1852     return tr("Remove Pane"); | 
| Chris@45 | 1853 } | 
| Chris@45 | 1854 | 
| Chris@45 | 1855 void | 
| Chris@45 | 1856 MainWindowBase::RemovePaneCommand::execute() | 
| Chris@45 | 1857 { | 
| Chris@45 | 1858     m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@45 | 1859     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@45 | 1860     m_added = false; | 
| Chris@45 | 1861 } | 
| Chris@45 | 1862 | 
| Chris@45 | 1863 void | 
| Chris@45 | 1864 MainWindowBase::RemovePaneCommand::unexecute() | 
| Chris@45 | 1865 { | 
| Chris@45 | 1866     m_mw->m_paneStack->showPane(m_pane); | 
| Chris@45 | 1867     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@45 | 1868     m_added = true; | 
| Chris@45 | 1869 } | 
| Chris@45 | 1870 | 
| Chris@45 | 1871 void | 
| Chris@45 | 1872 MainWindowBase::deleteCurrentPane() | 
| Chris@45 | 1873 { | 
| Chris@45 | 1874     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 1875 	(tr("Delete Pane"), true); | 
| Chris@45 | 1876 | 
| Chris@45 | 1877     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1878     if (pane) { | 
| Chris@45 | 1879 	while (pane->getLayerCount() > 0) { | 
| Chris@45 | 1880 	    Layer *layer = pane->getLayer(0); | 
| Chris@45 | 1881 	    if (layer) { | 
| Chris@45 | 1882 		m_document->removeLayerFromView(pane, layer); | 
| Chris@45 | 1883 	    } else { | 
| Chris@45 | 1884 		break; | 
| Chris@45 | 1885 	    } | 
| Chris@45 | 1886 	} | 
| Chris@45 | 1887 | 
| Chris@45 | 1888 	RemovePaneCommand *command = new RemovePaneCommand(this, pane); | 
| Chris@45 | 1889 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@45 | 1890     } | 
| Chris@45 | 1891 | 
| Chris@45 | 1892     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 1893 | 
| Chris@45 | 1894     updateMenuStates(); | 
| Chris@45 | 1895 } | 
| Chris@45 | 1896 | 
| Chris@45 | 1897 void | 
| Chris@45 | 1898 MainWindowBase::deleteCurrentLayer() | 
| Chris@45 | 1899 { | 
| Chris@45 | 1900     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1901     if (pane) { | 
| Chris@45 | 1902 	Layer *layer = pane->getSelectedLayer(); | 
| Chris@45 | 1903 	if (layer) { | 
| Chris@45 | 1904 	    m_document->removeLayerFromView(pane, layer); | 
| Chris@45 | 1905 	} | 
| Chris@45 | 1906     } | 
| Chris@45 | 1907     updateMenuStates(); | 
| Chris@45 | 1908 } | 
| Chris@45 | 1909 | 
| Chris@45 | 1910 void | 
| Chris@73 | 1911 MainWindowBase::previousPane() | 
| Chris@73 | 1912 { | 
| Chris@73 | 1913     if (!m_paneStack) return; | 
| Chris@73 | 1914 | 
| Chris@73 | 1915     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 1916     if (!currentPane) return; | 
| Chris@73 | 1917 | 
| Chris@73 | 1918     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 1919         if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 1920             if (i == 0) return; | 
| Chris@73 | 1921             m_paneStack->setCurrentPane(m_paneStack->getPane(i-1)); | 
| Chris@73 | 1922             updateMenuStates(); | 
| Chris@73 | 1923             return; | 
| Chris@73 | 1924         } | 
| Chris@73 | 1925     } | 
| Chris@73 | 1926 } | 
| Chris@73 | 1927 | 
| Chris@73 | 1928 void | 
| Chris@73 | 1929 MainWindowBase::nextPane() | 
| Chris@73 | 1930 { | 
| Chris@73 | 1931     if (!m_paneStack) return; | 
| Chris@73 | 1932 | 
| Chris@73 | 1933     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 1934     if (!currentPane) return; | 
| Chris@73 | 1935 | 
| Chris@73 | 1936     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 1937         if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 1938             if (i == m_paneStack->getPaneCount()-1) return; | 
| Chris@73 | 1939             m_paneStack->setCurrentPane(m_paneStack->getPane(i+1)); | 
| Chris@73 | 1940             updateMenuStates(); | 
| Chris@73 | 1941             return; | 
| Chris@73 | 1942         } | 
| Chris@73 | 1943     } | 
| Chris@73 | 1944 } | 
| Chris@73 | 1945 | 
| Chris@73 | 1946 void | 
| Chris@73 | 1947 MainWindowBase::previousLayer() | 
| Chris@73 | 1948 { | 
| Chris@73 | 1949     //!!! Not right -- pane lists layers in stacking order | 
| Chris@73 | 1950 | 
| Chris@73 | 1951     if (!m_paneStack) return; | 
| Chris@73 | 1952 | 
| Chris@73 | 1953     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 1954     if (!currentPane) return; | 
| Chris@73 | 1955 | 
| Chris@73 | 1956     Layer *currentLayer = currentPane->getSelectedLayer(); | 
| Chris@73 | 1957     if (!currentLayer) return; | 
| Chris@73 | 1958 | 
| Chris@73 | 1959     for (int i = 0; i < currentPane->getLayerCount(); ++i) { | 
| Chris@73 | 1960         if (currentPane->getLayer(i) == currentLayer) { | 
| Chris@73 | 1961             if (i == 0) return; | 
| Chris@73 | 1962             m_paneStack->setCurrentLayer(currentPane, | 
| Chris@73 | 1963                                          currentPane->getLayer(i-1)); | 
| Chris@73 | 1964             updateMenuStates(); | 
| Chris@73 | 1965             return; | 
| Chris@73 | 1966         } | 
| Chris@73 | 1967     } | 
| Chris@73 | 1968 } | 
| Chris@73 | 1969 | 
| Chris@73 | 1970 void | 
| Chris@73 | 1971 MainWindowBase::nextLayer() | 
| Chris@73 | 1972 { | 
| Chris@73 | 1973     //!!! Not right -- pane lists layers in stacking order | 
| Chris@73 | 1974 | 
| Chris@73 | 1975     if (!m_paneStack) return; | 
| Chris@73 | 1976 | 
| Chris@73 | 1977     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 1978     if (!currentPane) return; | 
| Chris@73 | 1979 | 
| Chris@73 | 1980     Layer *currentLayer = currentPane->getSelectedLayer(); | 
| Chris@73 | 1981     if (!currentLayer) return; | 
| Chris@73 | 1982 | 
| Chris@73 | 1983     for (int i = 0; i < currentPane->getLayerCount(); ++i) { | 
| Chris@73 | 1984         if (currentPane->getLayer(i) == currentLayer) { | 
| Chris@73 | 1985             if (i == currentPane->getLayerCount()-1) return; | 
| Chris@73 | 1986             m_paneStack->setCurrentLayer(currentPane, | 
| Chris@73 | 1987                                          currentPane->getLayer(i+1)); | 
| Chris@73 | 1988             updateMenuStates(); | 
| Chris@73 | 1989             return; | 
| Chris@73 | 1990         } | 
| Chris@73 | 1991     } | 
| Chris@73 | 1992 } | 
| Chris@73 | 1993 | 
| Chris@73 | 1994 void | 
| Chris@45 | 1995 MainWindowBase::playbackFrameChanged(unsigned long frame) | 
| Chris@45 | 1996 { | 
| Chris@45 | 1997     if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 1998 | 
| Chris@45 | 1999     RealTime now = RealTime::frame2RealTime | 
| Chris@45 | 2000         (frame, getMainModel()->getSampleRate()); | 
| Chris@45 | 2001 | 
| Chris@45 | 2002     if (now.sec == m_lastPlayStatusSec) return; | 
| Chris@45 | 2003 | 
| Chris@45 | 2004     RealTime then = RealTime::frame2RealTime | 
| Chris@45 | 2005         (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); | 
| Chris@45 | 2006 | 
| Chris@45 | 2007     QString nowStr; | 
| Chris@45 | 2008     QString thenStr; | 
| Chris@45 | 2009     QString remainingStr; | 
| Chris@45 | 2010 | 
| Chris@45 | 2011     if (then.sec > 10) { | 
| Chris@45 | 2012         nowStr = now.toSecText().c_str(); | 
| Chris@45 | 2013         thenStr = then.toSecText().c_str(); | 
| Chris@45 | 2014         remainingStr = (then - now).toSecText().c_str(); | 
| Chris@45 | 2015         m_lastPlayStatusSec = now.sec; | 
| Chris@45 | 2016     } else { | 
| Chris@45 | 2017         nowStr = now.toText(true).c_str(); | 
| Chris@45 | 2018         thenStr = then.toText(true).c_str(); | 
| Chris@45 | 2019         remainingStr = (then - now).toText(true).c_str(); | 
| Chris@45 | 2020     } | 
| Chris@45 | 2021 | 
| Chris@45 | 2022     m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") | 
| Chris@45 | 2023         .arg(nowStr).arg(thenStr).arg(remainingStr); | 
| Chris@45 | 2024 | 
| Chris@45 | 2025     statusBar()->showMessage(m_myStatusMessage); | 
| Chris@45 | 2026 } | 
| Chris@45 | 2027 | 
| Chris@45 | 2028 void | 
| Chris@45 | 2029 MainWindowBase::globalCentreFrameChanged(unsigned long ) | 
| Chris@45 | 2030 { | 
| Chris@45 | 2031     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 2032     Pane *p = 0; | 
| Chris@45 | 2033     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 2034     if (!p->getFollowGlobalPan()) return; | 
| Chris@45 | 2035     updateVisibleRangeDisplay(p); | 
| Chris@45 | 2036 } | 
| Chris@45 | 2037 | 
| Chris@45 | 2038 void | 
| Chris@45 | 2039 MainWindowBase::viewCentreFrameChanged(View *v, unsigned long ) | 
| Chris@45 | 2040 { | 
| Chris@45 | 2041     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 2042     Pane *p = 0; | 
| Chris@45 | 2043     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 2044     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@45 | 2045 } | 
| Chris@45 | 2046 | 
| Chris@45 | 2047 void | 
| Chris@45 | 2048 MainWindowBase::viewZoomLevelChanged(View *v, unsigned long , bool ) | 
| Chris@45 | 2049 { | 
| Chris@45 | 2050     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 2051     Pane *p = 0; | 
| Chris@45 | 2052     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 2053     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@45 | 2054 } | 
| Chris@45 | 2055 | 
| Chris@45 | 2056 void | 
| Chris@45 | 2057 MainWindowBase::layerAdded(Layer *) | 
| Chris@45 | 2058 { | 
| Chris@45 | 2059 //    std::cerr << "MainWindowBase::layerAdded(" << layer << ")" << std::endl; | 
| Chris@45 | 2060     updateMenuStates(); | 
| Chris@45 | 2061 } | 
| Chris@45 | 2062 | 
| Chris@45 | 2063 void | 
| Chris@45 | 2064 MainWindowBase::layerRemoved(Layer *) | 
| Chris@45 | 2065 { | 
| Chris@45 | 2066 //    std::cerr << "MainWindowBase::layerRemoved(" << layer << ")" << std::endl; | 
| Chris@45 | 2067     updateMenuStates(); | 
| Chris@45 | 2068 } | 
| Chris@45 | 2069 | 
| Chris@45 | 2070 void | 
| Chris@45 | 2071 MainWindowBase::layerAboutToBeDeleted(Layer *layer) | 
| Chris@45 | 2072 { | 
| Chris@45 | 2073 //    std::cerr << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << std::endl; | 
| Chris@47 | 2074     if (m_timeRulerLayer && (layer == m_timeRulerLayer)) { | 
| Chris@45 | 2075 //	std::cerr << "(this is the time ruler layer)" << std::endl; | 
| Chris@45 | 2076 	m_timeRulerLayer = 0; | 
| Chris@45 | 2077     } | 
| Chris@45 | 2078 } | 
| Chris@45 | 2079 | 
| Chris@45 | 2080 void | 
| Chris@45 | 2081 MainWindowBase::layerInAView(Layer *layer, bool inAView) | 
| Chris@45 | 2082 { | 
| Chris@45 | 2083 //    std::cerr << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << std::endl; | 
| Chris@45 | 2084 | 
| Chris@45 | 2085     // Check whether we need to add or remove model from play source | 
| Chris@45 | 2086     Model *model = layer->getModel(); | 
| Chris@45 | 2087     if (model) { | 
| Chris@45 | 2088         if (inAView) { | 
| Chris@45 | 2089             m_playSource->addModel(model); | 
| Chris@45 | 2090         } else { | 
| Chris@45 | 2091             bool found = false; | 
| Chris@45 | 2092             for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@45 | 2093                 Pane *pane = m_paneStack->getPane(i); | 
| Chris@45 | 2094                 if (!pane) continue; | 
| Chris@45 | 2095                 for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@45 | 2096                     Layer *pl = pane->getLayer(j); | 
| Chris@45 | 2097                     if (pl && pl->getModel() == model) { | 
| Chris@45 | 2098                         found = true; | 
| Chris@45 | 2099                         break; | 
| Chris@45 | 2100                     } | 
| Chris@45 | 2101                 } | 
| Chris@45 | 2102                 if (found) break; | 
| Chris@45 | 2103             } | 
| Chris@45 | 2104             if (!found) m_playSource->removeModel(model); | 
| Chris@45 | 2105         } | 
| Chris@45 | 2106     } | 
| Chris@45 | 2107 | 
| Chris@45 | 2108     updateMenuStates(); | 
| Chris@45 | 2109 } | 
| Chris@45 | 2110 | 
| Chris@45 | 2111 void | 
| Chris@45 | 2112 MainWindowBase::modelAdded(Model *model) | 
| Chris@45 | 2113 { | 
| Chris@45 | 2114 //    std::cerr << "MainWindowBase::modelAdded(" << model << ")" << std::endl; | 
| Chris@45 | 2115     m_playSource->addModel(model); | 
| Chris@45 | 2116 } | 
| Chris@45 | 2117 | 
| Chris@45 | 2118 void | 
| Chris@45 | 2119 MainWindowBase::mainModelChanged(WaveFileModel *model) | 
| Chris@45 | 2120 { | 
| Chris@45 | 2121 //    std::cerr << "MainWindowBase::mainModelChanged(" << model << ")" << std::endl; | 
| Chris@45 | 2122     updateDescriptionLabel(); | 
| Chris@45 | 2123     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); | 
| Chris@45 | 2124     if (model && !m_playTarget && m_audioOutput) createPlayTarget(); | 
| Chris@45 | 2125 } | 
| Chris@45 | 2126 | 
| Chris@45 | 2127 void | 
| Chris@45 | 2128 MainWindowBase::modelAboutToBeDeleted(Model *model) | 
| Chris@45 | 2129 { | 
| Chris@45 | 2130 //    std::cerr << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << std::endl; | 
| Chris@45 | 2131     if (model == m_viewManager->getPlaybackModel()) { | 
| Chris@45 | 2132         m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 2133     } | 
| Chris@45 | 2134     m_playSource->removeModel(model); | 
| Chris@45 | 2135     FFTDataServer::modelAboutToBeDeleted(model); | 
| Chris@45 | 2136 } | 
| Chris@45 | 2137 | 
| Chris@45 | 2138 void | 
| Chris@55 | 2139 MainWindowBase::paneDeleteButtonClicked(Pane *pane) | 
| Chris@55 | 2140 { | 
| Chris@55 | 2141     bool found = false; | 
| Chris@55 | 2142     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@55 | 2143         if (m_paneStack->getPane(i) == pane) { | 
| Chris@55 | 2144             found = true; | 
| Chris@55 | 2145             break; | 
| Chris@55 | 2146         } | 
| Chris@55 | 2147     } | 
| Chris@55 | 2148     if (!found) { | 
| Chris@55 | 2149         std::cerr << "MainWindowBase::paneDeleteButtonClicked: Unknown pane " | 
| Chris@55 | 2150                   << pane << std::endl; | 
| Chris@55 | 2151         return; | 
| Chris@55 | 2152     } | 
| Chris@55 | 2153 | 
| Chris@55 | 2154     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@55 | 2155 	(tr("Delete Pane"), true); | 
| Chris@55 | 2156 | 
| Chris@55 | 2157     while (pane->getLayerCount() > 0) { | 
| Chris@55 | 2158         Layer *layer = pane->getLayer(0); | 
| Chris@55 | 2159         if (layer) { | 
| Chris@55 | 2160             m_document->removeLayerFromView(pane, layer); | 
| Chris@55 | 2161         } else { | 
| Chris@55 | 2162             break; | 
| Chris@55 | 2163         } | 
| Chris@55 | 2164     } | 
| Chris@55 | 2165 | 
| Chris@55 | 2166     RemovePaneCommand *command = new RemovePaneCommand(this, pane); | 
| Chris@55 | 2167     CommandHistory::getInstance()->addCommand(command); | 
| Chris@55 | 2168 | 
| Chris@55 | 2169     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@55 | 2170 | 
| Chris@55 | 2171     updateMenuStates(); | 
| Chris@55 | 2172 } | 
| Chris@55 | 2173 | 
| Chris@55 | 2174 void | 
| Chris@45 | 2175 MainWindowBase::pollOSC() | 
| Chris@45 | 2176 { | 
| Chris@45 | 2177     if (!m_oscQueue || m_oscQueue->isEmpty()) return; | 
| Chris@45 | 2178     std::cerr << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; | 
| Chris@45 | 2179 | 
| Chris@45 | 2180     if (m_openingAudioFile) return; | 
| Chris@45 | 2181 | 
| Chris@45 | 2182     OSCMessage message = m_oscQueue->readMessage(); | 
| Chris@45 | 2183 | 
| Chris@45 | 2184     if (message.getTarget() != 0) { | 
| Chris@45 | 2185         return; //!!! for now -- this class is target 0, others not handled yet | 
| Chris@45 | 2186     } | 
| Chris@45 | 2187 | 
| Chris@45 | 2188     handleOSCMessage(message); | 
| Chris@45 | 2189 } | 
| Chris@45 | 2190 | 
| Chris@45 | 2191 void | 
| Chris@45 | 2192 MainWindowBase::inProgressSelectionChanged() | 
| Chris@45 | 2193 { | 
| Chris@45 | 2194     Pane *currentPane = 0; | 
| Chris@45 | 2195     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2196     if (currentPane) updateVisibleRangeDisplay(currentPane); | 
| Chris@45 | 2197 } | 
| Chris@45 | 2198 | 
| Chris@45 | 2199 void | 
| Chris@45 | 2200 MainWindowBase::contextHelpChanged(const QString &s) | 
| Chris@45 | 2201 { | 
| Chris@45 | 2202     if (s == "" && m_myStatusMessage != "") { | 
| Chris@45 | 2203         statusBar()->showMessage(m_myStatusMessage); | 
| Chris@45 | 2204         return; | 
| Chris@45 | 2205     } | 
| Chris@45 | 2206     statusBar()->showMessage(s); | 
| Chris@45 | 2207 } | 
| Chris@45 | 2208 | 
| Chris@45 | 2209 void | 
| Chris@45 | 2210 MainWindowBase::openHelpUrl(QString url) | 
| Chris@45 | 2211 { | 
| Chris@45 | 2212     // This method mostly lifted from Qt Assistant source code | 
| Chris@45 | 2213 | 
| Chris@45 | 2214     QProcess *process = new QProcess(this); | 
| Chris@45 | 2215     connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); | 
| Chris@45 | 2216 | 
| Chris@45 | 2217     QStringList args; | 
| Chris@45 | 2218 | 
| Chris@45 | 2219 #ifdef Q_OS_MAC | 
| Chris@45 | 2220     args.append(url); | 
| Chris@45 | 2221     process->start("open", args); | 
| Chris@45 | 2222 #else | 
| Chris@45 | 2223 #ifdef Q_OS_WIN32 | 
| Chris@45 | 2224 | 
| Chris@45 | 2225 	QString pf(getenv("ProgramFiles")); | 
| Chris@45 | 2226 	QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); | 
| Chris@45 | 2227 | 
| Chris@45 | 2228 	args.append(url); | 
| Chris@45 | 2229 	process->start(command, args); | 
| Chris@45 | 2230 | 
| Chris@45 | 2231 #else | 
| Chris@45 | 2232 #ifdef Q_WS_X11 | 
| Chris@45 | 2233     if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { | 
| Chris@45 | 2234         args.append("exec"); | 
| Chris@45 | 2235         args.append(url); | 
| Chris@45 | 2236         process->start("kfmclient", args); | 
| Chris@45 | 2237     } else if (!qgetenv("BROWSER").isEmpty()) { | 
| Chris@45 | 2238         args.append(url); | 
| Chris@45 | 2239         process->start(qgetenv("BROWSER"), args); | 
| Chris@45 | 2240     } else { | 
| Chris@45 | 2241         args.append(url); | 
| Chris@45 | 2242         process->start("firefox", args); | 
| Chris@45 | 2243     } | 
| Chris@45 | 2244 #endif | 
| Chris@45 | 2245 #endif | 
| Chris@45 | 2246 #endif | 
| Chris@45 | 2247 } | 
| Chris@45 | 2248 |