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