svapp
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006-2007 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "MainWindowBase.h" 00017 #include "Document.h" 00018 00019 00020 #include "view/Pane.h" 00021 #include "view/PaneStack.h" 00022 #include "data/model/WaveFileModel.h" 00023 #include "data/model/SparseOneDimensionalModel.h" 00024 #include "data/model/NoteModel.h" 00025 #include "data/model/FlexiNoteModel.h" 00026 #include "data/model/Labeller.h" 00027 #include "data/model/TabularModel.h" 00028 #include "view/ViewManager.h" 00029 00030 #include "layer/WaveformLayer.h" 00031 #include "layer/TimeRulerLayer.h" 00032 #include "layer/TimeInstantLayer.h" 00033 #include "layer/TimeValueLayer.h" 00034 #include "layer/Colour3DPlotLayer.h" 00035 #include "layer/SliceLayer.h" 00036 #include "layer/SliceableLayer.h" 00037 #include "layer/ImageLayer.h" 00038 #include "layer/NoteLayer.h" 00039 #include "layer/FlexiNoteLayer.h" 00040 #include "layer/RegionLayer.h" 00041 00042 #include "widgets/ListInputDialog.h" 00043 #include "widgets/CommandHistory.h" 00044 #include "widgets/ProgressDialog.h" 00045 #include "widgets/MIDIFileImportDialog.h" 00046 #include "widgets/CSVFormatDialog.h" 00047 #include "widgets/ModelDataTableDialog.h" 00048 #include "widgets/InteractiveFileFinder.h" 00049 00050 #include "audioio/AudioCallbackPlaySource.h" 00051 #include "audioio/AudioCallbackPlayTarget.h" 00052 #include "audioio/AudioTargetFactory.h" 00053 #include "audioio/PlaySpeedRangeMapper.h" 00054 #include "data/fileio/DataFileReaderFactory.h" 00055 #include "data/fileio/PlaylistFileReader.h" 00056 #include "data/fileio/WavFileWriter.h" 00057 #include "data/fileio/CSVFileWriter.h" 00058 #include "data/fileio/MIDIFileWriter.h" 00059 #include "data/fileio/BZipFileDevice.h" 00060 #include "data/fileio/FileSource.h" 00061 #include "data/fileio/AudioFileReaderFactory.h" 00062 #include "rdf/RDFImporter.h" 00063 00064 #include "data/fft/FFTDataServer.h" 00065 00066 #include "base/RecentFiles.h" 00067 00068 #include "base/PlayParameterRepository.h" 00069 #include "base/XmlExportable.h" 00070 #include "base/Profiler.h" 00071 #include "base/Preferences.h" 00072 #include "base/TempWriteFile.h" 00073 #include "base/Exceptions.h" 00074 #include "base/ResourceFinder.h" 00075 00076 #include "data/osc/OSCQueue.h" 00077 #include "data/midi/MIDIInput.h" 00078 00079 #include <QApplication> 00080 #include <QMessageBox> 00081 #include <QGridLayout> 00082 #include <QLabel> 00083 #include <QAction> 00084 #include <QMenuBar> 00085 #include <QToolBar> 00086 #include <QInputDialog> 00087 #include <QStatusBar> 00088 #include <QTreeView> 00089 #include <QFile> 00090 #include <QFileInfo> 00091 #include <QDir> 00092 #include <QTextStream> 00093 #include <QProcess> 00094 #include <QShortcut> 00095 #include <QSettings> 00096 #include <QDateTime> 00097 #include <QProcess> 00098 #include <QCheckBox> 00099 #include <QRegExp> 00100 #include <QScrollArea> 00101 #include <QDesktopWidget> 00102 #include <QSignalMapper> 00103 00104 #include <iostream> 00105 #include <cstdio> 00106 #include <errno.h> 00107 00108 00109 00110 00111 using std::vector; 00112 using std::map; 00113 using std::set; 00114 00115 #ifdef Q_WS_X11 00116 #define Window X11Window 00117 #include <X11/Xlib.h> 00118 #include <X11/Xutil.h> 00119 #include <X11/Xatom.h> 00120 #include <X11/SM/SMlib.h> 00121 00122 static int handle_x11_error(Display *dpy, XErrorEvent *err) 00123 { 00124 char errstr[256]; 00125 XGetErrorText(dpy, err->error_code, errstr, 256); 00126 if (err->error_code != BadWindow) { 00127 cerr << "Sonic Visualiser: X Error: " 00128 << errstr << " " << int(err->error_code) 00129 << "\nin major opcode: " 00130 << int(err->request_code) << endl; 00131 } 00132 return 0; 00133 } 00134 #undef Window 00135 #endif 00136 00137 MainWindowBase::MainWindowBase(bool withAudioOutput, 00138 bool withMIDIInput) : 00139 m_document(0), 00140 m_paneStack(0), 00141 m_viewManager(0), 00142 m_timeRulerLayer(0), 00143 m_audioOutput(withAudioOutput), 00144 m_playSource(0), 00145 m_playTarget(0), 00146 m_oscQueue(0), 00147 m_oscQueueStarter(0), 00148 m_midiInput(0), 00149 m_recentFiles("RecentFiles", 20), 00150 m_recentTransforms("RecentTransforms", 20), 00151 m_documentModified(false), 00152 m_openingAudioFile(false), 00153 m_abandoning(false), 00154 m_labeller(0), 00155 m_lastPlayStatusSec(0), 00156 m_initialDarkBackground(false), 00157 m_defaultFfwdRwdStep(2, 0), 00158 m_statusLabel(0), 00159 m_menuShortcutMapper(0) 00160 { 00161 Profiler profiler("MainWindowBase::MainWindowBase"); 00162 00163 #ifdef Q_WS_X11 00164 XSetErrorHandler(handle_x11_error); 00165 #endif 00166 00167 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), 00168 this, SLOT(documentModified())); 00169 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), 00170 this, SLOT(documentRestored())); 00171 00172 m_viewManager = new ViewManager(); 00173 connect(m_viewManager, SIGNAL(selectionChanged()), 00174 this, SLOT(updateMenuStates())); 00175 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), 00176 this, SLOT(inProgressSelectionChanged())); 00177 00178 // set a sensible default font size for views -- cannot do this 00179 // in Preferences, which is in base and not supposed to use QtGui 00180 int viewFontSize = QApplication::font().pointSize() * 0.9; 00181 QSettings settings; 00182 settings.beginGroup("Preferences"); 00183 viewFontSize = settings.value("view-font-size", viewFontSize).toInt(); 00184 settings.setValue("view-font-size", viewFontSize); 00185 settings.endGroup(); 00186 00187 Preferences::BackgroundMode mode = 00188 Preferences::getInstance()->getBackgroundMode(); 00189 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); 00190 if (mode != Preferences::BackgroundFromTheme) { 00191 m_viewManager->setGlobalDarkBackground 00192 (mode == Preferences::DarkBackground); 00193 } 00194 00195 m_paneStack = new PaneStack(0, m_viewManager); 00196 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), 00197 this, SLOT(currentPaneChanged(Pane *))); 00198 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), 00199 this, SLOT(currentLayerChanged(Pane *, Layer *))); 00200 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), 00201 this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); 00202 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), 00203 this, SLOT(contextHelpChanged(const QString &))); 00204 connect(m_paneStack, SIGNAL(paneAdded(Pane *)), 00205 this, SLOT(paneAdded(Pane *))); 00206 connect(m_paneStack, SIGNAL(paneHidden(Pane *)), 00207 this, SLOT(paneHidden(Pane *))); 00208 connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)), 00209 this, SLOT(paneAboutToBeDeleted(Pane *))); 00210 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)), 00211 this, SLOT(paneDropAccepted(Pane *, QStringList))); 00212 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)), 00213 this, SLOT(paneDropAccepted(Pane *, QString))); 00214 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), 00215 this, SLOT(paneDeleteButtonClicked(Pane *))); 00216 00217 m_playSource = new AudioCallbackPlaySource(m_viewManager, 00218 QApplication::applicationName()); 00219 00220 connect(m_playSource, SIGNAL(sampleRateMismatch(int, int, bool)), 00221 this, SLOT(sampleRateMismatch(int, int, bool))); 00222 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), 00223 this, SLOT(audioOverloadPluginDisabled())); 00224 connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()), 00225 this, SLOT(audioTimeStretchMultiChannelDisabled())); 00226 00227 connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), 00228 this, SLOT(outputLevelsChanged(float, float))); 00229 00230 connect(m_viewManager, SIGNAL(playbackFrameChanged(int)), 00231 this, SLOT(playbackFrameChanged(int))); 00232 00233 connect(m_viewManager, SIGNAL(globalCentreFrameChanged(int)), 00234 this, SLOT(globalCentreFrameChanged(int))); 00235 00236 connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, int)), 00237 this, SLOT(viewCentreFrameChanged(View *, int))); 00238 00239 connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, int, bool)), 00240 this, SLOT(viewZoomLevelChanged(View *, int, bool))); 00241 00242 connect(Preferences::getInstance(), 00243 SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 00244 this, 00245 SLOT(preferenceChanged(PropertyContainer::PropertyName))); 00246 00247 Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; 00248 settings.beginGroup("MainWindow"); 00249 00250 labellerType = (Labeller::ValueType) 00251 settings.value("labellertype", (int)labellerType).toInt(); 00252 int cycle = settings.value("labellercycle", 4).toInt(); 00253 00254 settings.endGroup(); 00255 00256 m_labeller = new Labeller(labellerType); 00257 m_labeller->setCounterCycleSize(cycle); 00258 00259 if (withMIDIInput) { 00260 m_midiInput = new MIDIInput(QApplication::applicationName(), this); 00261 } 00262 } 00263 00264 MainWindowBase::~MainWindowBase() 00265 { 00266 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl; 00267 if (m_playTarget) m_playTarget->shutdown(); 00268 // delete m_playTarget; 00269 delete m_playSource; 00270 delete m_viewManager; 00271 delete m_oscQueue; 00272 delete m_oscQueueStarter; 00273 delete m_midiInput; 00274 Profiles::getInstance()->dump(); 00275 } 00276 00277 void 00278 MainWindowBase::finaliseMenus() 00279 { 00280 delete m_menuShortcutMapper; 00281 m_menuShortcutMapper = 0; 00282 00283 foreach (QShortcut *sc, m_appShortcuts) { 00284 delete sc; 00285 } 00286 m_appShortcuts.clear(); 00287 00288 QMenuBar *mb = menuBar(); 00289 00290 // This used to find all children of QMenu type, and call 00291 // finaliseMenu on those. But it seems we are getting hold of some 00292 // menus that way that are not actually active in the menu bar and 00293 // are not returned in their parent menu's actions() list, and if 00294 // we finalise those, we end up with duplicate shortcuts in the 00295 // app shortcut mapper. So we should do this by descending the 00296 // menu tree through only those menus accessible via actions() 00297 // from their parents instead. 00298 00299 QList<QMenu *> menus = mb->findChildren<QMenu *> 00300 (QString(), Qt::FindDirectChildrenOnly); 00301 00302 foreach (QMenu *menu, menus) { 00303 if (menu) finaliseMenu(menu); 00304 } 00305 } 00306 00307 void 00308 MainWindowBase::finaliseMenu(QMenu * 00309 #ifdef Q_OS_MAC 00310 menu 00311 #endif 00312 ) 00313 { 00314 #ifdef Q_OS_MAC 00315 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and 00316 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 -- 00317 // single-key shortcuts that are associated only with a menu 00318 // action (and not with a toolbar button) do not work with Qt 5.x 00319 // under OS/X. 00320 // 00321 // Apparently Cocoa never handled them as a matter of course, but 00322 // earlier versions of Qt picked them up as widget shortcuts and 00323 // handled them anyway. That behaviour was removed to fix a crash 00324 // when invoking a menu while its window was overridden by a modal 00325 // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657). 00326 // 00327 // This workaround restores the single-key shortcut behaviour by 00328 // searching in menus for single-key shortcuts that are associated 00329 // only with the menu and not with a toolbar button, and 00330 // augmenting them with global application shortcuts that invoke 00331 // the relevant actions, testing whether the actions are enabled 00332 // on invocation. 00333 // 00334 // (Previously this acted on all single-key shortcuts in menus, 00335 // and it removed the shortcut from the action when it created 00336 // each new global one, in order to avoid an "ambiguous shortcut" 00337 // error in the case where the action was also associated with a 00338 // toolbar button. But that has the unwelcome side-effect of 00339 // removing the shortcut hint from the menu entry. So now we leave 00340 // the shortcut in the menu action as well as creating a global 00341 // one, and we only act on shortcuts that have no toolbar button, 00342 // i.e. that will not otherwise work. The downside is that if this 00343 // bug is fixed in a future Qt release, we will start getting 00344 // "ambiguous shortcut" errors from the menu entry actions and 00345 // will need to update the code.) 00346 00347 if (!m_menuShortcutMapper) { 00348 m_menuShortcutMapper = new QSignalMapper(this); 00349 connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)), 00350 this, SLOT(menuActionMapperInvoked(QObject *))); 00351 } 00352 00353 foreach (QAction *a, menu->actions()) { 00354 00355 if (a->isSeparator()) { 00356 continue; 00357 } else if (a->menu()) { 00358 finaliseMenu(a->menu()); 00359 } else { 00360 00361 QWidgetList ww = a->associatedWidgets(); 00362 bool hasButton = false; 00363 foreach (QWidget *w, ww) { 00364 if (qobject_cast<QAbstractButton *>(w)) { 00365 hasButton = true; 00366 break; 00367 } 00368 } 00369 if (hasButton) continue; 00370 QKeySequence sc = a->shortcut(); 00371 00372 // Note that the set of "single-key shortcuts" that aren't 00373 // working and that we need to handle here includes those 00374 // with the Shift modifier mask as well as those with no 00375 // modifier at all 00376 if (sc.count() == 1 && 00377 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier || 00378 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) { 00379 QShortcut *newSc = new QShortcut(sc, a->parentWidget()); 00380 QObject::connect(newSc, SIGNAL(activated()), 00381 m_menuShortcutMapper, SLOT(map())); 00382 m_menuShortcutMapper->setMapping(newSc, a); 00383 m_appShortcuts.push_back(newSc); 00384 } 00385 } 00386 } 00387 #endif 00388 } 00389 00390 void 00391 MainWindowBase::menuActionMapperInvoked(QObject *o) 00392 { 00393 QAction *a = qobject_cast<QAction *>(o); 00394 if (a && a->isEnabled()) { 00395 a->trigger(); 00396 } 00397 } 00398 00399 void 00400 MainWindowBase::resizeConstrained(QSize size) 00401 { 00402 QDesktopWidget *desktop = QApplication::desktop(); 00403 QRect available = desktop->availableGeometry(); 00404 QSize actual(std::min(size.width(), available.width()), 00405 std::min(size.height(), available.height())); 00406 resize(actual); 00407 } 00408 00409 void 00410 MainWindowBase::startOSCQueue() 00411 { 00412 m_oscQueueStarter = new OSCQueueStarter(this); 00413 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady())); 00414 m_oscQueueStarter->start(); 00415 } 00416 00417 void 00418 MainWindowBase::oscReady() 00419 { 00420 if (m_oscQueue && m_oscQueue->isOK()) { 00421 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); 00422 QTimer *oscTimer = new QTimer(this); 00423 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); 00424 oscTimer->start(1000); 00425 cerr << "Finished setting up OSC interface" << endl; 00426 } 00427 } 00428 00429 QString 00430 MainWindowBase::getOpenFileName(FileFinder::FileType type) 00431 { 00432 FileFinder *ff = FileFinder::getInstance(); 00433 00434 if (type == FileFinder::AnyFile) { 00435 if (getMainModel() != 0 && 00436 m_paneStack != 0 && 00437 m_paneStack->getCurrentPane() != 0) { // can import a layer 00438 return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); 00439 } else { 00440 return ff->getOpenFileName(FileFinder::SessionOrAudioFile, 00441 m_sessionFile); 00442 } 00443 } 00444 00445 QString lastPath = m_sessionFile; 00446 00447 if (type == FileFinder::AudioFile) { 00448 lastPath = m_audioFile; 00449 } 00450 00451 return ff->getOpenFileName(type, lastPath); 00452 } 00453 00454 QString 00455 MainWindowBase::getSaveFileName(FileFinder::FileType type) 00456 { 00457 QString lastPath = m_sessionFile; 00458 00459 if (type == FileFinder::AudioFile) { 00460 lastPath = m_audioFile; 00461 } 00462 00463 FileFinder *ff = FileFinder::getInstance(); 00464 return ff->getSaveFileName(type, lastPath); 00465 } 00466 00467 void 00468 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path) 00469 { 00470 FileFinder *ff = FileFinder::getInstance(); 00471 ff->registerLastOpenedFilePath(type, path); 00472 } 00473 00474 QString 00475 MainWindowBase::getDefaultSessionTemplate() const 00476 { 00477 QSettings settings; 00478 settings.beginGroup("MainWindow"); 00479 QString templateName = settings.value("sessiontemplate", "").toString(); 00480 if (templateName == "") templateName = "default"; 00481 return templateName; 00482 } 00483 00484 void 00485 MainWindowBase::setDefaultSessionTemplate(QString n) 00486 { 00487 QSettings settings; 00488 settings.beginGroup("MainWindow"); 00489 settings.setValue("sessiontemplate", n); 00490 } 00491 00492 void 00493 MainWindowBase::updateMenuStates() 00494 { 00495 Pane *currentPane = 0; 00496 Layer *currentLayer = 0; 00497 00498 if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); 00499 if (currentPane) currentLayer = currentPane->getSelectedLayer(); 00500 00501 bool havePrevPane = false, haveNextPane = false; 00502 bool havePrevLayer = false, haveNextLayer = false; 00503 00504 if (currentPane) { 00505 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 00506 if (m_paneStack->getPane(i) == currentPane) { 00507 if (i > 0) havePrevPane = true; 00508 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true; 00509 break; 00510 } 00511 } 00512 // the prev/next layer commands actually include the pane 00513 // itself as one of the selectables -- so we always have a 00514 // prev and next layer, as long as we have a pane with at 00515 // least one layer in it 00516 if (currentPane->getLayerCount() > 0) { 00517 havePrevLayer = true; 00518 haveNextLayer = true; 00519 } 00520 } 00521 00522 bool haveCurrentPane = 00523 (currentPane != 0); 00524 bool haveCurrentLayer = 00525 (haveCurrentPane && 00526 (currentLayer != 0)); 00527 bool haveMainModel = 00528 (getMainModel() != 0); 00529 bool havePlayTarget = 00530 (m_playTarget != 0); 00531 bool haveSelection = 00532 (m_viewManager && 00533 !m_viewManager->getSelections().empty()); 00534 bool haveCurrentEditableLayer = 00535 (haveCurrentLayer && 00536 currentLayer->isLayerEditable()); 00537 bool haveCurrentTimeInstantsLayer = 00538 (haveCurrentLayer && 00539 dynamic_cast<TimeInstantLayer *>(currentLayer)); 00540 bool haveCurrentDurationLayer = 00541 (haveCurrentLayer && 00542 (dynamic_cast<NoteLayer *>(currentLayer) || 00543 dynamic_cast<FlexiNoteLayer *>(currentLayer) || 00544 dynamic_cast<RegionLayer *>(currentLayer))); 00545 bool haveCurrentColour3DPlot = 00546 (haveCurrentLayer && 00547 dynamic_cast<Colour3DPlotLayer *>(currentLayer)); 00548 bool haveClipboardContents = 00549 (m_viewManager && 00550 !m_viewManager->getClipboard().empty()); 00551 bool haveTabularLayer = 00552 (haveCurrentLayer && 00553 dynamic_cast<TabularModel *>(currentLayer->getModel())); 00554 00555 emit canAddPane(haveMainModel); 00556 emit canDeleteCurrentPane(haveCurrentPane); 00557 emit canZoom(haveMainModel && haveCurrentPane); 00558 emit canScroll(haveMainModel && haveCurrentPane); 00559 emit canAddLayer(haveMainModel && haveCurrentPane); 00560 emit canImportMoreAudio(haveMainModel); 00561 emit canReplaceMainAudio(haveMainModel); 00562 emit canImportLayer(haveMainModel && haveCurrentPane); 00563 emit canExportAudio(haveMainModel); 00564 emit canChangeSessionTemplate(haveMainModel); 00565 emit canExportLayer(haveMainModel && 00566 (haveCurrentEditableLayer || haveCurrentColour3DPlot)); 00567 emit canExportImage(haveMainModel && haveCurrentPane); 00568 emit canDeleteCurrentLayer(haveCurrentLayer); 00569 emit canRenameLayer(haveCurrentLayer); 00570 emit canEditLayer(haveCurrentEditableLayer); 00571 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer); 00572 emit canMeasureLayer(haveCurrentLayer); 00573 emit canSelect(haveMainModel && haveCurrentPane); 00574 emit canPlay(haveMainModel && havePlayTarget); 00575 emit canFfwd(true); 00576 emit canRewind(true); 00577 emit canPaste(haveClipboardContents); 00578 emit canInsertInstant(haveCurrentPane); 00579 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); 00580 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer); 00581 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); 00582 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); 00583 emit canClearSelection(haveSelection); 00584 emit canEditSelection(haveSelection && haveCurrentEditableLayer); 00585 emit canSave(m_sessionFile != "" && m_documentModified); 00586 emit canSaveAs(haveMainModel); 00587 emit canSelectPreviousPane(havePrevPane); 00588 emit canSelectNextPane(haveNextPane); 00589 emit canSelectPreviousLayer(havePrevLayer); 00590 emit canSelectNextLayer(haveNextLayer); 00591 } 00592 00593 void 00594 MainWindowBase::documentModified() 00595 { 00596 // SVDEBUG << "MainWindowBase::documentModified" << endl; 00597 00598 if (!m_documentModified) { 00600 setWindowTitle(tr("%1 (modified)").arg(windowTitle())); 00601 } 00602 00603 m_documentModified = true; 00604 updateMenuStates(); 00605 } 00606 00607 void 00608 MainWindowBase::documentRestored() 00609 { 00610 // SVDEBUG << "MainWindowBase::documentRestored" << endl; 00611 00612 if (m_documentModified) { 00614 QString wt(windowTitle()); 00615 wt.replace(tr(" (modified)"), ""); 00616 setWindowTitle(wt); 00617 } 00618 00619 m_documentModified = false; 00620 updateMenuStates(); 00621 } 00622 00623 void 00624 MainWindowBase::playLoopToggled() 00625 { 00626 QAction *action = dynamic_cast<QAction *>(sender()); 00627 00628 if (action) { 00629 m_viewManager->setPlayLoopMode(action->isChecked()); 00630 } else { 00631 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); 00632 } 00633 } 00634 00635 void 00636 MainWindowBase::playSelectionToggled() 00637 { 00638 QAction *action = dynamic_cast<QAction *>(sender()); 00639 00640 if (action) { 00641 m_viewManager->setPlaySelectionMode(action->isChecked()); 00642 } else { 00643 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); 00644 } 00645 } 00646 00647 void 00648 MainWindowBase::playSoloToggled() 00649 { 00650 QAction *action = dynamic_cast<QAction *>(sender()); 00651 00652 if (action) { 00653 m_viewManager->setPlaySoloMode(action->isChecked()); 00654 } else { 00655 m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); 00656 } 00657 00658 if (m_viewManager->getPlaySoloMode()) { 00659 currentPaneChanged(m_paneStack->getCurrentPane()); 00660 } else { 00661 m_viewManager->setPlaybackModel(0); 00662 if (m_playSource) { 00663 m_playSource->clearSoloModelSet(); 00664 } 00665 } 00666 } 00667 00668 void 00669 MainWindowBase::currentPaneChanged(Pane *p) 00670 { 00671 updateMenuStates(); 00672 updateVisibleRangeDisplay(p); 00673 00674 if (!p) return; 00675 00676 if (!(m_viewManager && 00677 m_playSource && 00678 m_viewManager->getPlaySoloMode())) { 00679 if (m_viewManager) m_viewManager->setPlaybackModel(0); 00680 return; 00681 } 00682 00683 Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); 00684 00685 // What we want here is not the currently playing frame (unless we 00686 // are about to clear out the audio playback buffers -- which may 00687 // or may not be possible, depending on the audio driver). What 00688 // we want is the frame that was last committed to the soundcard 00689 // buffers, as the audio driver will continue playing up to that 00690 // frame before switching to whichever one we decide we want to 00691 // switch to, regardless of our efforts. 00692 00693 int frame = m_playSource->getCurrentBufferedFrame(); 00694 00695 cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl; 00696 00697 View::ModelSet soloModels = p->getModels(); 00698 00699 View::ModelSet sources; 00700 for (View::ModelSet::iterator mi = soloModels.begin(); 00701 mi != soloModels.end(); ++mi) { 00702 // If a model in this pane is derived from something else, 00703 // then we want to play that model as well -- if the model 00704 // that's derived from it is not something that is itself 00705 // individually playable (e.g. a waveform) 00706 if (*mi && 00707 !dynamic_cast<RangeSummarisableTimeValueModel *>(*mi) && 00708 (*mi)->getSourceModel()) { 00709 sources.insert((*mi)->getSourceModel()); 00710 } 00711 } 00712 for (View::ModelSet::iterator mi = sources.begin(); 00713 mi != sources.end(); ++mi) { 00714 soloModels.insert(*mi); 00715 } 00716 00718 //playback model has changed, and changing it on ViewManager -- 00719 //the play source should be making the setPlaybackModel call to 00720 //ViewManager 00721 00722 for (View::ModelSet::iterator mi = soloModels.begin(); 00723 mi != soloModels.end(); ++mi) { 00724 if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) { 00725 m_viewManager->setPlaybackModel(*mi); 00726 } 00727 } 00728 00729 RangeSummarisableTimeValueModel *a = 00730 dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); 00731 RangeSummarisableTimeValueModel *b = 00732 dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> 00733 getPlaybackModel()); 00734 00735 m_playSource->setSoloModelSet(soloModels); 00736 00737 if (a && b && (a != b)) { 00738 if (m_playSource->isPlaying()) m_playSource->play(frame); 00739 } 00740 } 00741 00742 void 00743 MainWindowBase::currentLayerChanged(Pane *p, Layer *) 00744 { 00745 updateMenuStates(); 00746 updateVisibleRangeDisplay(p); 00747 } 00748 00749 void 00750 MainWindowBase::selectAll() 00751 { 00752 if (!getMainModel()) return; 00753 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), 00754 getMainModel()->getEndFrame())); 00755 } 00756 00757 void 00758 MainWindowBase::selectToStart() 00759 { 00760 if (!getMainModel()) return; 00761 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), 00762 m_viewManager->getGlobalCentreFrame())); 00763 } 00764 00765 void 00766 MainWindowBase::selectToEnd() 00767 { 00768 if (!getMainModel()) return; 00769 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), 00770 getMainModel()->getEndFrame())); 00771 } 00772 00773 void 00774 MainWindowBase::selectVisible() 00775 { 00776 Model *model = getMainModel(); 00777 if (!model) return; 00778 00779 Pane *currentPane = m_paneStack->getCurrentPane(); 00780 if (!currentPane) return; 00781 00782 int startFrame, endFrame; 00783 00784 if (currentPane->getStartFrame() < 0) startFrame = 0; 00785 else startFrame = currentPane->getStartFrame(); 00786 00787 if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); 00788 else endFrame = currentPane->getEndFrame(); 00789 00790 m_viewManager->setSelection(Selection(startFrame, endFrame)); 00791 } 00792 00793 void 00794 MainWindowBase::clearSelection() 00795 { 00796 m_viewManager->clearSelections(); 00797 } 00798 00799 void 00800 MainWindowBase::cut() 00801 { 00802 Pane *currentPane = m_paneStack->getCurrentPane(); 00803 if (!currentPane) return; 00804 00805 Layer *layer = currentPane->getSelectedLayer(); 00806 if (!layer) return; 00807 00808 Clipboard &clipboard = m_viewManager->getClipboard(); 00809 clipboard.clear(); 00810 00811 MultiSelection::SelectionList selections = m_viewManager->getSelections(); 00812 00813 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); 00814 00815 for (MultiSelection::SelectionList::iterator i = selections.begin(); 00816 i != selections.end(); ++i) { 00817 layer->copy(currentPane, *i, clipboard); 00818 layer->deleteSelection(*i); 00819 } 00820 00821 CommandHistory::getInstance()->endCompoundOperation(); 00822 } 00823 00824 void 00825 MainWindowBase::copy() 00826 { 00827 Pane *currentPane = m_paneStack->getCurrentPane(); 00828 if (!currentPane) return; 00829 00830 Layer *layer = currentPane->getSelectedLayer(); 00831 if (!layer) return; 00832 00833 Clipboard &clipboard = m_viewManager->getClipboard(); 00834 clipboard.clear(); 00835 00836 MultiSelection::SelectionList selections = m_viewManager->getSelections(); 00837 00838 for (MultiSelection::SelectionList::iterator i = selections.begin(); 00839 i != selections.end(); ++i) { 00840 layer->copy(currentPane, *i, clipboard); 00841 } 00842 } 00843 00844 void 00845 MainWindowBase::paste() 00846 { 00847 pasteRelative(0); 00848 } 00849 00850 void 00851 MainWindowBase::pasteAtPlaybackPosition() 00852 { 00853 int pos = getFrame(); 00854 Clipboard &clipboard = m_viewManager->getClipboard(); 00855 if (!clipboard.empty()) { 00856 int firstEventFrame = clipboard.getPoints()[0].getFrame(); 00857 int offset = 0; 00858 if (firstEventFrame < 0) { 00859 offset = pos - firstEventFrame; 00860 } else if (firstEventFrame < pos) { 00861 offset = pos - firstEventFrame; 00862 } else { 00863 offset = -(firstEventFrame - pos); 00864 } 00865 pasteRelative(offset); 00866 } 00867 } 00868 00869 void 00870 MainWindowBase::pasteRelative(int offset) 00871 { 00872 Pane *currentPane = m_paneStack->getCurrentPane(); 00873 if (!currentPane) return; 00874 00875 Layer *layer = currentPane->getSelectedLayer(); 00876 00877 Clipboard &clipboard = m_viewManager->getClipboard(); 00878 00879 bool inCompound = false; 00880 00881 if (!layer || !layer->isLayerEditable()) { 00882 00883 CommandHistory::getInstance()->startCompoundOperation 00884 (tr("Paste"), true); 00885 00886 // no suitable current layer: create one of the most 00887 // appropriate sort 00888 LayerFactory::LayerType type = 00889 LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard); 00890 layer = m_document->createEmptyLayer(type); 00891 00892 if (!layer) { 00893 CommandHistory::getInstance()->endCompoundOperation(); 00894 return; 00895 } 00896 00897 m_document->addLayerToView(currentPane, layer); 00898 m_paneStack->setCurrentLayer(currentPane, layer); 00899 00900 inCompound = true; 00901 } 00902 00903 layer->paste(currentPane, clipboard, offset, true); 00904 00905 if (inCompound) CommandHistory::getInstance()->endCompoundOperation(); 00906 } 00907 00908 void 00909 MainWindowBase::deleteSelected() 00910 { 00911 if (m_paneStack->getCurrentPane() && 00912 m_paneStack->getCurrentPane()->getSelectedLayer()) { 00913 00914 Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); 00915 00916 if (m_viewManager) { 00917 00918 if (m_viewManager->getToolMode() == ViewManager::MeasureMode) { 00919 00920 layer->deleteCurrentMeasureRect(); 00921 00922 } else { 00923 00924 MultiSelection::SelectionList selections = 00925 m_viewManager->getSelections(); 00926 00927 for (MultiSelection::SelectionList::iterator i = selections.begin(); 00928 i != selections.end(); ++i) { 00929 layer->deleteSelection(*i); 00930 } 00931 } 00932 } 00933 } 00934 } 00935 00936 // FrameTimer method 00937 00938 int 00939 MainWindowBase::getFrame() const 00940 { 00941 if (m_playSource && m_playSource->isPlaying()) { 00942 return m_playSource->getCurrentPlayingFrame(); 00943 } else { 00944 return m_viewManager->getPlaybackFrame(); 00945 } 00946 } 00947 00948 void 00949 MainWindowBase::insertInstant() 00950 { 00951 insertInstantAt(getFrame()); 00952 } 00953 00954 void 00955 MainWindowBase::insertInstantsAtBoundaries() 00956 { 00957 MultiSelection::SelectionList selections = m_viewManager->getSelections(); 00958 for (MultiSelection::SelectionList::iterator i = selections.begin(); 00959 i != selections.end(); ++i) { 00960 int start = i->getStartFrame(); 00961 int end = i->getEndFrame(); 00962 if (start != end) { 00963 insertInstantAt(start); 00964 insertInstantAt(end); 00965 } 00966 } 00967 } 00968 00969 void 00970 MainWindowBase::insertInstantAt(int frame) 00971 { 00972 Pane *pane = m_paneStack->getCurrentPane(); 00973 if (!pane) { 00974 return; 00975 } 00976 00977 frame = pane->alignFromReference(frame); 00978 00979 Layer *layer = dynamic_cast<TimeInstantLayer *> 00980 (pane->getSelectedLayer()); 00981 00982 if (!layer) { 00983 for (int i = pane->getLayerCount(); i > 0; --i) { 00984 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); 00985 if (layer) break; 00986 } 00987 00988 if (!layer) { 00989 CommandHistory::getInstance()->startCompoundOperation 00990 (tr("Add Point"), true); 00991 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); 00992 if (layer) { 00993 m_document->addLayerToView(pane, layer); 00994 m_paneStack->setCurrentLayer(pane, layer); 00995 } 00996 CommandHistory::getInstance()->endCompoundOperation(); 00997 } 00998 } 00999 01000 if (layer) { 01001 01002 Model *model = layer->getModel(); 01003 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> 01004 (model); 01005 01006 if (sodm) { 01007 SparseOneDimensionalModel::Point point(frame, ""); 01008 01009 SparseOneDimensionalModel::Point prevPoint(0); 01010 bool havePrevPoint = false; 01011 01012 SparseOneDimensionalModel::EditCommand *command = 01013 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); 01014 01015 if (m_labeller) { 01016 01017 if (m_labeller->requiresPrevPoint()) { 01018 01019 SparseOneDimensionalModel::PointList prevPoints = 01020 sodm->getPreviousPoints(frame); 01021 01022 if (!prevPoints.empty()) { 01023 prevPoint = *prevPoints.begin(); 01024 havePrevPoint = true; 01025 } 01026 } 01027 01028 m_labeller->setSampleRate(sodm->getSampleRate()); 01029 01030 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { 01031 command->deletePoint(prevPoint); 01032 } 01033 01034 m_labeller->label<SparseOneDimensionalModel::Point> 01035 (point, havePrevPoint ? &prevPoint : 0); 01036 01037 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { 01038 command->addPoint(prevPoint); 01039 } 01040 } 01041 01042 command->addPoint(point); 01043 01044 command->setName(tr("Add Point at %1 s") 01045 .arg(RealTime::frame2RealTime 01046 (frame, 01047 sodm->getSampleRate()) 01048 .toText(false).c_str())); 01049 01050 Command *c = command->finish(); 01051 if (c) CommandHistory::getInstance()->addCommand(c, false); 01052 } 01053 } 01054 } 01055 01056 void 01057 MainWindowBase::insertItemAtSelection() 01058 { 01059 MultiSelection::SelectionList selections = m_viewManager->getSelections(); 01060 for (MultiSelection::SelectionList::iterator i = selections.begin(); 01061 i != selections.end(); ++i) { 01062 int start = i->getStartFrame(); 01063 int end = i->getEndFrame(); 01064 if (start < end) { 01065 insertItemAt(start, end - start); 01066 } 01067 } 01068 } 01069 01070 void 01071 MainWindowBase::insertItemAt(int frame, int duration) 01072 { 01073 Pane *pane = m_paneStack->getCurrentPane(); 01074 if (!pane) { 01075 return; 01076 } 01077 01078 // ugh! 01079 01080 int alignedStart = pane->alignFromReference(frame); 01081 int alignedEnd = pane->alignFromReference(frame + duration); 01082 if (alignedStart >= alignedEnd) return; 01083 int alignedDuration = alignedEnd - alignedStart; 01084 01085 Command *c = 0; 01086 01087 QString name = tr("Add Item at %1 s") 01088 .arg(RealTime::frame2RealTime 01089 (alignedStart, 01090 getMainModel()->getSampleRate()) 01091 .toText(false).c_str()); 01092 01093 Layer *layer = pane->getSelectedLayer(); 01094 if (!layer) return; 01095 01096 RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel()); 01097 if (rm) { 01098 RegionModel::Point point(alignedStart, 01099 rm->getValueMaximum() + 1, 01100 alignedDuration, 01101 ""); 01102 RegionModel::EditCommand *command = 01103 new RegionModel::EditCommand(rm, tr("Add Point")); 01104 command->addPoint(point); 01105 command->setName(name); 01106 c = command->finish(); 01107 } 01108 01109 if (c) { 01110 CommandHistory::getInstance()->addCommand(c, false); 01111 return; 01112 } 01113 01114 NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel()); 01115 if (nm) { 01116 NoteModel::Point point(alignedStart, 01117 nm->getValueMinimum(), 01118 alignedDuration, 01119 1.f, 01120 ""); 01121 NoteModel::EditCommand *command = 01122 new NoteModel::EditCommand(nm, tr("Add Point")); 01123 command->addPoint(point); 01124 command->setName(name); 01125 c = command->finish(); 01126 } 01127 01128 if (c) { 01129 CommandHistory::getInstance()->addCommand(c, false); 01130 return; 01131 } 01132 01133 FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel()); 01134 if (fnm) { 01135 FlexiNoteModel::Point point(alignedStart, 01136 fnm->getValueMinimum(), 01137 alignedDuration, 01138 1.f, 01139 ""); 01140 FlexiNoteModel::EditCommand *command = 01141 new FlexiNoteModel::EditCommand(fnm, tr("Add Point")); 01142 command->addPoint(point); 01143 command->setName(name); 01144 c = command->finish(); 01145 } 01146 01147 if (c) { 01148 CommandHistory::getInstance()->addCommand(c, false); 01149 return; 01150 } 01151 } 01152 01153 void 01154 MainWindowBase::renumberInstants() 01155 { 01156 Pane *pane = m_paneStack->getCurrentPane(); 01157 if (!pane) return; 01158 01159 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer()); 01160 if (!layer) return; 01161 01162 MultiSelection ms(m_viewManager->getSelection()); 01163 01164 Model *model = layer->getModel(); 01165 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> 01166 (model); 01167 if (!sodm) return; 01168 01169 if (!m_labeller) return; 01170 01171 Labeller labeller(*m_labeller); 01172 labeller.setSampleRate(sodm->getSampleRate()); 01173 01174 // This uses a command 01175 01176 labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); 01177 } 01178 01179 MainWindowBase::FileOpenStatus 01180 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode) 01181 { 01182 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this); 01183 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); 01184 return open(FileSource(fileOrUrl, &dialog), mode); 01185 } 01186 01187 MainWindowBase::FileOpenStatus 01188 MainWindowBase::open(FileSource source, AudioFileOpenMode mode) 01189 { 01190 FileOpenStatus status; 01191 01192 if (!source.isAvailable()) return FileOpenFailed; 01193 source.waitForData(); 01194 01195 bool canImportLayer = (getMainModel() != 0 && 01196 m_paneStack != 0 && 01197 m_paneStack->getCurrentPane() != 0); 01198 01199 bool rdf = (source.getExtension().toLower() == "rdf" || 01200 source.getExtension().toLower() == "n3" || 01201 source.getExtension().toLower() == "ttl"); 01202 01203 bool audio = AudioFileReaderFactory::getKnownExtensions().contains 01204 (source.getExtension().toLower()); 01205 01206 bool rdfSession = false; 01207 if (rdf) { 01208 RDFImporter::RDFDocumentType rdfType = 01209 RDFImporter::identifyDocumentType 01210 (QUrl::fromLocalFile(source.getLocalFilename()).toString()); 01211 if (rdfType == RDFImporter::AudioRefAndAnnotations || 01212 rdfType == RDFImporter::AudioRef) { 01213 rdfSession = true; 01214 } else if (rdfType == RDFImporter::NotRDF) { 01215 rdf = false; 01216 } 01217 } 01218 01219 if (rdf) { 01220 if (rdfSession) { 01221 bool cancel = false; 01222 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) { 01223 return openSession(source); 01224 } else if (cancel) { 01225 return FileOpenCancelled; 01226 } else { 01227 return openLayer(source); 01228 } 01229 } else { 01230 if ((status = openSession(source)) != FileOpenFailed) { 01231 return status; 01232 } else if (!canImportLayer) { 01233 return FileOpenWrongMode; 01234 } else if ((status = openLayer(source)) != FileOpenFailed) { 01235 return status; 01236 } else { 01237 return FileOpenFailed; 01238 } 01239 } 01240 } 01241 01242 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) { 01243 return status; 01244 } else if ((status = openSession(source)) != FileOpenFailed) { 01245 return status; 01246 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) { 01247 return status; 01248 } else if (!canImportLayer) { 01249 return FileOpenWrongMode; 01250 } else if ((status = openImage(source)) != FileOpenFailed) { 01251 return status; 01252 } else if ((status = openLayer(source)) != FileOpenFailed) { 01253 return status; 01254 } else { 01255 return FileOpenFailed; 01256 } 01257 } 01258 01259 MainWindowBase::FileOpenStatus 01260 MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode, 01261 QString templateName) 01262 { 01263 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl; 01264 01265 if (templateName == "") { 01266 templateName = getDefaultSessionTemplate(); 01267 } 01268 01269 // cerr << "template is: \"" << templateName << "\"" << endl; 01270 01271 if (!source.isAvailable()) { 01272 if (source.wasCancelled()) { 01273 return FileOpenCancelled; 01274 } else { 01275 return FileOpenFailed; 01276 } 01277 } 01278 01279 source.waitForData(); 01280 01281 m_openingAudioFile = true; 01282 01283 int rate = 0; 01284 01285 if (Preferences::getInstance()->getFixedSampleRate() != 0) { 01286 rate = Preferences::getInstance()->getFixedSampleRate(); 01287 } else if (Preferences::getInstance()->getResampleOnLoad()) { 01288 rate = m_playSource->getSourceSampleRate(); 01289 } 01290 01291 WaveFileModel *newModel = new WaveFileModel(source, rate); 01292 01293 if (!newModel->isOK()) { 01294 delete newModel; 01295 m_openingAudioFile = false; 01296 if (source.wasCancelled()) { 01297 return FileOpenCancelled; 01298 } else { 01299 return FileOpenFailed; 01300 } 01301 } 01302 01303 // cerr << "mode = " << mode << endl; 01304 01305 if (mode == AskUser) { 01306 if (getMainModel()) { 01307 01308 QSettings settings; 01309 settings.beginGroup("MainWindow"); 01310 int lastMode = settings.value("lastaudioopenmode", 0).toBool(); 01311 settings.endGroup(); 01312 int imode = 0; 01313 01314 QStringList items; 01315 items << tr("Close the current session and start a new one") 01316 << tr("Replace the main audio file in this session") 01317 << tr("Add the audio file to this session"); 01318 01319 bool ok = false; 01320 QString item = ListInputDialog::getItem 01321 (this, tr("Select target for import"), 01322 tr("<b>Select a target for import</b><p>You already have an audio file loaded.<br>What would you like to do with the new audio file?"), 01323 items, lastMode, &ok); 01324 01325 if (!ok || item.isEmpty()) { 01326 delete newModel; 01327 m_openingAudioFile = false; 01328 return FileOpenCancelled; 01329 } 01330 01331 for (int i = 0; i < items.size(); ++i) { 01332 if (item == items[i]) imode = i; 01333 } 01334 01335 settings.beginGroup("MainWindow"); 01336 settings.setValue("lastaudioopenmode", imode); 01337 settings.endGroup(); 01338 01339 mode = (AudioFileOpenMode)imode; 01340 01341 } else { 01342 // no main model: make a new session 01343 mode = ReplaceSession; 01344 } 01345 } 01346 01347 if (mode == ReplaceCurrentPane) { 01348 01349 Pane *pane = m_paneStack->getCurrentPane(); 01350 if (pane) { 01351 if (getMainModel()) { 01352 View::ModelSet models(pane->getModels()); 01353 if (models.find(getMainModel()) != models.end()) { 01354 // Current pane contains main model: replace that 01355 mode = ReplaceMainModel; 01356 } 01357 // Otherwise the current pane has a non-default model, 01358 // which we will deal with later 01359 } else { 01360 // We have no main model, so start a new session with 01361 // optional template 01362 mode = ReplaceSession; 01363 } 01364 } else { 01365 // We seem to have no current pane! Oh well 01366 mode = CreateAdditionalModel; 01367 } 01368 } 01369 01370 if (mode == CreateAdditionalModel && !getMainModel()) { 01371 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl; 01372 mode = ReplaceSession; 01373 } 01374 01375 bool loadedTemplate = false; 01376 01377 if (mode == ReplaceSession) { 01378 01379 if (!checkSaveModified()) return FileOpenCancelled; 01380 01381 SVDEBUG << "SV looking for template " << templateName << endl; 01382 if (templateName != "") { 01383 FileOpenStatus tplStatus = openSessionTemplate(templateName); 01384 if (tplStatus == FileOpenCancelled) { 01385 cerr << "Template load cancelled" << endl; 01386 return FileOpenCancelled; 01387 } 01388 if (tplStatus != FileOpenFailed) { 01389 cerr << "Template load succeeded" << endl; 01390 loadedTemplate = true; 01391 } 01392 } 01393 01394 if (!loadedTemplate) { 01395 SVDEBUG << "No template found: closing session, creating new empty document" << endl; 01396 closeSession(); 01397 createDocument(); 01398 } 01399 01400 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl; 01401 mode = ReplaceMainModel; 01402 } 01403 01404 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation())); 01405 01406 if (mode == ReplaceMainModel) { 01407 01408 Model *prevMain = getMainModel(); 01409 if (prevMain) { 01410 m_playSource->removeModel(prevMain); 01411 PlayParameterRepository::getInstance()->removePlayable(prevMain); 01412 } 01413 PlayParameterRepository::getInstance()->addPlayable(newModel); 01414 01415 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl; 01416 01417 m_document->setMainModel(newModel); 01418 01419 setupMenus(); 01420 01421 if (loadedTemplate || (m_sessionFile == "")) { 01423 setWindowTitle(tr("%1: %2") 01424 .arg(QApplication::applicationName()) 01425 .arg(source.getLocation())); 01426 CommandHistory::getInstance()->clear(); 01427 CommandHistory::getInstance()->documentSaved(); 01428 m_documentModified = false; 01429 } else { 01430 setWindowTitle(tr("%1: %2 [%3]") 01431 .arg(QApplication::applicationName()) 01432 .arg(QFileInfo(m_sessionFile).fileName()) 01433 .arg(source.getLocation())); 01434 if (m_documentModified) { 01435 m_documentModified = false; 01436 documentModified(); // so as to restore "(modified)" window title 01437 } 01438 } 01439 01440 if (!source.isRemote()) m_audioFile = source.getLocalFilename(); 01441 01442 } else if (mode == CreateAdditionalModel) { 01443 01444 CommandHistory::getInstance()->startCompoundOperation 01445 (tr("Import \"%1\"").arg(source.getBasename()), true); 01446 01447 m_document->addImportedModel(newModel); 01448 01449 AddPaneCommand *command = new AddPaneCommand(this); 01450 CommandHistory::getInstance()->addCommand(command); 01451 01452 Pane *pane = command->getPane(); 01453 01454 if (m_timeRulerLayer) { 01455 m_document->addLayerToView(pane, m_timeRulerLayer); 01456 } 01457 01458 Layer *newLayer = m_document->createImportedLayer(newModel); 01459 01460 if (newLayer) { 01461 m_document->addLayerToView(pane, newLayer); 01462 } 01463 01464 CommandHistory::getInstance()->endCompoundOperation(); 01465 01466 } else if (mode == ReplaceCurrentPane) { 01467 01468 // We know there is a current pane, otherwise we would have 01469 // reset the mode to CreateAdditionalModel above; and we know 01470 // the current pane does not contain the main model, otherwise 01471 // we would have reset it to ReplaceMainModel. But we don't 01472 // know whether the pane contains a waveform model at all. 01473 01474 Pane *pane = m_paneStack->getCurrentPane(); 01475 Layer *replace = 0; 01476 01477 for (int i = 0; i < pane->getLayerCount(); ++i) { 01478 Layer *layer = pane->getLayer(i); 01479 if (dynamic_cast<WaveformLayer *>(layer)) { 01480 replace = layer; 01481 break; 01482 } 01483 } 01484 01485 CommandHistory::getInstance()->startCompoundOperation 01486 (tr("Import \"%1\"").arg(source.getBasename()), true); 01487 01488 m_document->addImportedModel(newModel); 01489 01490 if (replace) { 01491 m_document->removeLayerFromView(pane, replace); 01492 } 01493 01494 Layer *newLayer = m_document->createImportedLayer(newModel); 01495 01496 if (newLayer) { 01497 m_document->addLayerToView(pane, newLayer); 01498 } 01499 01500 CommandHistory::getInstance()->endCompoundOperation(); 01501 } 01502 01503 updateMenuStates(); 01504 m_recentFiles.addFile(source.getLocation()); 01505 if (!source.isRemote()) { 01506 // for file dialog 01507 registerLastOpenedFilePath(FileFinder::AudioFile, 01508 source.getLocalFilename()); 01509 } 01510 m_openingAudioFile = false; 01511 01512 currentPaneChanged(m_paneStack->getCurrentPane()); 01513 01514 emit audioFileLoaded(); 01515 01516 return FileOpenSucceeded; 01517 } 01518 01519 MainWindowBase::FileOpenStatus 01520 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode) 01521 { 01522 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl; 01523 01524 std::set<QString> extensions; 01525 PlaylistFileReader::getSupportedExtensions(extensions); 01526 QString extension = source.getExtension().toLower(); 01527 if (extensions.find(extension) == extensions.end()) return FileOpenFailed; 01528 01529 if (!source.isAvailable()) return FileOpenFailed; 01530 source.waitForData(); 01531 01532 PlaylistFileReader reader(source.getLocalFilename()); 01533 if (!reader.isOK()) return FileOpenFailed; 01534 01535 PlaylistFileReader::Playlist playlist = reader.load(); 01536 01537 bool someSuccess = false; 01538 01539 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); 01540 i != playlist.end(); ++i) { 01541 01542 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this); 01543 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); 01544 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode); 01545 01546 if (status == FileOpenCancelled) { 01547 return FileOpenCancelled; 01548 } 01549 01550 if (status == FileOpenSucceeded) { 01551 someSuccess = true; 01552 mode = CreateAdditionalModel; 01553 } 01554 } 01555 01556 if (someSuccess) return FileOpenSucceeded; 01557 else return FileOpenFailed; 01558 } 01559 01560 MainWindowBase::FileOpenStatus 01561 MainWindowBase::openLayer(FileSource source) 01562 { 01563 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl; 01564 01565 Pane *pane = m_paneStack->getCurrentPane(); 01566 01567 if (!pane) { 01568 // shouldn't happen, as the menu action should have been disabled 01569 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl; 01570 return FileOpenWrongMode; 01571 } 01572 01573 if (!getMainModel()) { 01574 // shouldn't happen, as the menu action should have been disabled 01575 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl; 01576 return FileOpenWrongMode; 01577 } 01578 01579 if (!source.isAvailable()) return FileOpenFailed; 01580 source.waitForData(); 01581 01582 QString path = source.getLocalFilename(); 01583 01584 RDFImporter::RDFDocumentType rdfType = 01585 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString()); 01586 01587 // cerr << "RDF type: (in layer) " << (int) rdfType << endl; 01588 01589 if (rdfType != RDFImporter::NotRDF) { 01590 01591 return openLayersFromRDF(source); 01592 01593 } else if (source.getExtension().toLower() == "svl" || 01594 (source.getExtension().toLower() == "xml" && 01595 (SVFileReader::identifyXmlFile(source.getLocalFilename()) 01596 == SVFileReader::SVLayerFile))) { 01597 01598 PaneCallback callback(this); 01599 QFile file(path); 01600 01601 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 01602 cerr << "ERROR: MainWindowBase::openLayer(" 01603 << source.getLocation() 01604 << "): Failed to open file for reading" << endl; 01605 return FileOpenFailed; 01606 } 01607 01608 SVFileReader reader(m_document, callback, source.getLocation()); 01609 connect 01610 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), 01611 this, SLOT(modelRegenerationFailed(QString, QString, QString))); 01612 connect 01613 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 01614 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 01615 reader.setCurrentPane(pane); 01616 01617 QXmlInputSource inputSource(&file); 01618 reader.parse(inputSource); 01619 01620 if (!reader.isOK()) { 01621 cerr << "ERROR: MainWindowBase::openLayer(" 01622 << source.getLocation() 01623 << "): Failed to read XML file: " 01624 << reader.getErrorString() << endl; 01625 return FileOpenFailed; 01626 } 01627 01628 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation())); 01629 01630 m_recentFiles.addFile(source.getLocation()); 01631 01632 if (!source.isRemote()) { 01633 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog 01634 } 01635 01636 return FileOpenSucceeded; 01637 01638 } else { 01639 01640 try { 01641 01642 MIDIFileImportDialog midiDlg(this); 01643 01644 Model *model = DataFileReaderFactory::loadNonCSV 01645 (path, &midiDlg, getMainModel()->getSampleRate()); 01646 01647 if (!model) { 01648 CSVFormat format(path); 01649 format.setSampleRate(getMainModel()->getSampleRate()); 01650 CSVFormatDialog *dialog = new CSVFormatDialog(this, format); 01651 if (dialog->exec() == QDialog::Accepted) { 01652 model = DataFileReaderFactory::loadCSV 01653 (path, dialog->getFormat(), 01654 getMainModel()->getSampleRate()); 01655 } 01656 } 01657 01658 if (model) { 01659 01660 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl; 01661 01662 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation())); 01663 01664 Layer *newLayer = m_document->createImportedLayer(model); 01665 01666 if (newLayer) { 01667 01668 m_document->addLayerToView(pane, newLayer); 01669 m_paneStack->setCurrentLayer(pane, newLayer); 01670 01671 m_recentFiles.addFile(source.getLocation()); 01672 01673 if (!source.isRemote()) { 01674 registerLastOpenedFilePath 01675 (FileFinder::LayerFile, 01676 path); // for file dialog 01677 } 01678 01679 return FileOpenSucceeded; 01680 } 01681 } 01682 } catch (DataFileReaderFactory::Exception e) { 01683 if (e == DataFileReaderFactory::ImportCancelled) { 01684 return FileOpenCancelled; 01685 } 01686 } 01687 } 01688 01689 return FileOpenFailed; 01690 } 01691 01692 MainWindowBase::FileOpenStatus 01693 MainWindowBase::openImage(FileSource source) 01694 { 01695 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl; 01696 01697 Pane *pane = m_paneStack->getCurrentPane(); 01698 01699 if (!pane) { 01700 // shouldn't happen, as the menu action should have been disabled 01701 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl; 01702 return FileOpenWrongMode; 01703 } 01704 01705 if (!m_document->getMainModel()) { 01706 return FileOpenWrongMode; 01707 } 01708 01709 bool newLayer = false; 01710 01711 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer()); 01712 if (!il) { 01713 for (int i = pane->getLayerCount()-1; i >= 0; --i) { 01714 il = dynamic_cast<ImageLayer *>(pane->getLayer(i)); 01715 if (il) break; 01716 } 01717 } 01718 if (!il) { 01719 il = dynamic_cast<ImageLayer *> 01720 (m_document->createEmptyLayer(LayerFactory::Image)); 01721 if (!il) return FileOpenFailed; 01722 newLayer = true; 01723 } 01724 01725 // We don't put the image file in Recent Files 01726 01727 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl; 01728 01729 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) { 01730 if (newLayer) { 01731 m_document->deleteLayer(il); // also releases its model 01732 } 01733 return FileOpenFailed; 01734 } else { 01735 if (newLayer) { 01736 m_document->addLayerToView(pane, il); 01737 } 01738 m_paneStack->setCurrentLayer(pane, il); 01739 } 01740 01741 return FileOpenSucceeded; 01742 } 01743 01744 MainWindowBase::FileOpenStatus 01745 MainWindowBase::openSessionPath(QString fileOrUrl) 01746 { 01747 ProgressDialog dialog(tr("Opening session..."), true, 2000, this); 01748 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); 01749 return openSession(FileSource(fileOrUrl, &dialog)); 01750 } 01751 01752 MainWindowBase::FileOpenStatus 01753 MainWindowBase::openSession(FileSource source) 01754 { 01755 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl; 01756 01757 if (!source.isAvailable()) return FileOpenFailed; 01758 source.waitForData(); 01759 01760 QString sessionExt = 01761 InteractiveFileFinder::getInstance()->getApplicationSessionExtension(); 01762 01763 if (source.getExtension().toLower() != sessionExt) { 01764 01765 RDFImporter::RDFDocumentType rdfType = 01766 RDFImporter::identifyDocumentType 01767 (QUrl::fromLocalFile(source.getLocalFilename()).toString()); 01768 01769 // cerr << "RDF type: " << (int)rdfType << endl; 01770 01771 if (rdfType == RDFImporter::AudioRefAndAnnotations || 01772 rdfType == RDFImporter::AudioRef) { 01773 return openSessionFromRDF(source); 01774 } else if (rdfType != RDFImporter::NotRDF) { 01775 return FileOpenFailed; 01776 } 01777 01778 if (source.getExtension().toLower() == "xml") { 01779 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) == 01780 SVFileReader::SVSessionFile) { 01781 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl; 01782 } else { 01783 return FileOpenFailed; 01784 } 01785 } else { 01786 return FileOpenFailed; 01787 } 01788 } 01789 01790 QXmlInputSource *inputSource = 0; 01791 BZipFileDevice *bzFile = 0; 01792 QFile *rawFile = 0; 01793 01794 if (source.getExtension().toLower() == sessionExt) { 01795 bzFile = new BZipFileDevice(source.getLocalFilename()); 01796 if (!bzFile->open(QIODevice::ReadOnly)) { 01797 delete bzFile; 01798 return FileOpenFailed; 01799 } 01800 inputSource = new QXmlInputSource(bzFile); 01801 } else { 01802 rawFile = new QFile(source.getLocalFilename()); 01803 inputSource = new QXmlInputSource(rawFile); 01804 } 01805 01806 if (!checkSaveModified()) { 01807 if (bzFile) bzFile->close(); 01808 delete inputSource; 01809 delete bzFile; 01810 delete rawFile; 01811 return FileOpenCancelled; 01812 } 01813 01814 QString error; 01815 closeSession(); 01816 createDocument(); 01817 01818 PaneCallback callback(this); 01819 m_viewManager->clearSelections(); 01820 01821 SVFileReader reader(m_document, callback, source.getLocation()); 01822 connect 01823 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), 01824 this, SLOT(modelRegenerationFailed(QString, QString, QString))); 01825 connect 01826 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 01827 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 01828 01829 reader.parse(*inputSource); 01830 01831 if (!reader.isOK()) { 01832 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); 01833 } 01834 01835 if (bzFile) bzFile->close(); 01836 01837 delete inputSource; 01838 delete bzFile; 01839 delete rawFile; 01840 01841 bool ok = (error == ""); 01842 01843 if (ok) { 01844 01845 emit activity(tr("Import session file \"%1\"").arg(source.getLocation())); 01846 01847 setWindowTitle(tr("%1: %2") 01848 .arg(QApplication::applicationName()) 01849 .arg(source.getLocation())); 01850 01851 if (!source.isRemote()) m_sessionFile = source.getLocalFilename(); 01852 01853 setupMenus(); 01854 01855 CommandHistory::getInstance()->clear(); 01856 CommandHistory::getInstance()->documentSaved(); 01857 m_documentModified = false; 01858 updateMenuStates(); 01859 01860 m_recentFiles.addFile(source.getLocation()); 01861 01862 if (!source.isRemote()) { 01863 // for file dialog 01864 registerLastOpenedFilePath(FileFinder::SessionFile, 01865 source.getLocalFilename()); 01866 } 01867 01868 emit sessionLoaded(); 01869 01870 } else { 01871 setWindowTitle(QApplication::applicationName()); 01872 } 01873 01874 return ok ? FileOpenSucceeded : FileOpenFailed; 01875 } 01876 01877 MainWindowBase::FileOpenStatus 01878 MainWindowBase::openSessionTemplate(QString templateName) 01879 { 01880 // Template in the user's template directory takes 01881 // priority over a bundled one; we don't unbundle, but 01882 // open directly from the bundled file (where applicable) 01883 ResourceFinder rf; 01884 QString tfile = rf.getResourcePath("templates", templateName + ".svt"); 01885 if (tfile != "") { 01886 cerr << "SV loading template file " << tfile << endl; 01887 return openSessionTemplate(FileSource("file:" + tfile)); 01888 } else { 01889 return FileOpenFailed; 01890 } 01891 } 01892 01893 MainWindowBase::FileOpenStatus 01894 MainWindowBase::openSessionTemplate(FileSource source) 01895 { 01896 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl; 01897 01898 if (!source.isAvailable()) return FileOpenFailed; 01899 source.waitForData(); 01900 01901 QXmlInputSource *inputSource = 0; 01902 QFile *file = 0; 01903 01904 file = new QFile(source.getLocalFilename()); 01905 inputSource = new QXmlInputSource(file); 01906 01907 if (!checkSaveModified()) { 01908 delete inputSource; 01909 delete file; 01910 return FileOpenCancelled; 01911 } 01912 01913 QString error; 01914 closeSession(); 01915 createDocument(); 01916 01917 PaneCallback callback(this); 01918 m_viewManager->clearSelections(); 01919 01920 SVFileReader reader(m_document, callback, source.getLocation()); 01921 connect 01922 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), 01923 this, SLOT(modelRegenerationFailed(QString, QString, QString))); 01924 connect 01925 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 01926 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 01927 01928 reader.parse(*inputSource); 01929 01930 if (!reader.isOK()) { 01931 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); 01932 } 01933 01934 delete inputSource; 01935 delete file; 01936 01937 bool ok = (error == ""); 01938 01939 setWindowTitle(QApplication::applicationName()); 01940 01941 if (ok) { 01942 01943 emit activity(tr("Open session template \"%1\"").arg(source.getLocation())); 01944 01945 setupMenus(); 01946 01947 CommandHistory::getInstance()->clear(); 01948 CommandHistory::getInstance()->documentSaved(); 01949 m_documentModified = false; 01950 updateMenuStates(); 01951 01952 emit sessionLoaded(); 01953 } 01954 01955 return ok ? FileOpenSucceeded : FileOpenFailed; 01956 } 01957 01958 MainWindowBase::FileOpenStatus 01959 MainWindowBase::openSessionFromRDF(FileSource source) 01960 { 01961 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl; 01962 01963 if (!source.isAvailable()) return FileOpenFailed; 01964 source.waitForData(); 01965 01966 if (!checkSaveModified()) { 01967 return FileOpenCancelled; 01968 } 01969 01970 closeSession(); 01971 createDocument(); 01972 01973 FileOpenStatus status = openLayersFromRDF(source); 01974 01975 setupMenus(); 01976 01977 setWindowTitle(tr("%1: %2") 01978 .arg(QApplication::applicationName()) 01979 .arg(source.getLocation())); 01980 CommandHistory::getInstance()->clear(); 01981 CommandHistory::getInstance()->documentSaved(); 01982 m_documentModified = false; 01983 01984 emit sessionLoaded(); 01985 01986 return status; 01987 } 01988 01989 MainWindowBase::FileOpenStatus 01990 MainWindowBase::openLayersFromRDF(FileSource source) 01991 { 01992 int rate = 0; 01993 01994 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl; 01995 01996 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this); 01997 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); 01998 01999 if (getMainModel()) { 02000 rate = getMainModel()->getSampleRate(); 02001 } else if (Preferences::getInstance()->getResampleOnLoad()) { 02002 rate = m_playSource->getSourceSampleRate(); 02003 } 02004 02005 RDFImporter importer 02006 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate); 02007 02008 if (!importer.isOK()) { 02009 if (importer.getErrorString() != "") { 02010 QMessageBox::critical 02011 (this, tr("Failed to import RDF"), 02012 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>") 02013 .arg(source.getLocation()).arg(importer.getErrorString())); 02014 } 02015 return FileOpenFailed; 02016 } 02017 02018 std::vector<Model *> models = importer.getDataModels(&dialog); 02019 02020 dialog.setMessage(tr("Importing from RDF...")); 02021 02022 if (models.empty()) { 02023 QMessageBox::critical 02024 (this, tr("Failed to import RDF"), 02025 tr("<b>Failed to import RDF</b><p>No suitable data models found for import from RDF document at \"%1\"</p>").arg(source.getLocation())); 02026 return FileOpenFailed; 02027 } 02028 02029 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation())); 02030 02031 std::set<Model *> added; 02032 02033 for (int i = 0; i < (int)models.size(); ++i) { 02034 02035 Model *m = models[i]; 02036 WaveFileModel *w = dynamic_cast<WaveFileModel *>(m); 02037 02038 if (w) { 02039 02040 Pane *pane = addPaneToStack(); 02041 Layer *layer = 0; 02042 02043 if (m_timeRulerLayer) { 02044 m_document->addLayerToView(pane, m_timeRulerLayer); 02045 } 02046 02047 if (!getMainModel()) { 02048 m_document->setMainModel(w); 02049 layer = m_document->createMainModelLayer(LayerFactory::Waveform); 02050 } else { 02051 layer = m_document->createImportedLayer(w); 02052 } 02053 02054 m_document->addLayerToView(pane, layer); 02055 02056 added.insert(w); 02057 02058 for (int j = 0; j < (int)models.size(); ++j) { 02059 02060 Model *dm = models[j]; 02061 02062 if (dm == m) continue; 02063 if (dm->getSourceModel() != m) continue; 02064 02065 layer = m_document->createImportedLayer(dm); 02066 02067 if (layer->isLayerOpaque() || 02068 dynamic_cast<Colour3DPlotLayer *>(layer)) { 02069 02070 // these always go in a new pane, with nothing 02071 // else going in the same pane 02072 02073 Pane *singleLayerPane = addPaneToStack(); 02074 if (m_timeRulerLayer) { 02075 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer); 02076 } 02077 m_document->addLayerToView(singleLayerPane, layer); 02078 02079 } else if (layer->getLayerColourSignificance() == 02080 Layer::ColourHasMeaningfulValue) { 02081 02082 // these can go in a pane with something else, but 02083 // only if none of the something elses also have 02084 // this quality 02085 02086 bool needNewPane = false; 02087 for (int i = 0; i < pane->getLayerCount(); ++i) { 02088 Layer *otherLayer = pane->getLayer(i); 02089 if (otherLayer && 02090 (otherLayer->getLayerColourSignificance() == 02091 Layer::ColourHasMeaningfulValue)) { 02092 needNewPane = true; 02093 break; 02094 } 02095 } 02096 if (needNewPane) { 02097 pane = addPaneToStack(); 02098 } 02099 02100 m_document->addLayerToView(pane, layer); 02101 02102 } else { 02103 02104 if (pane->getLayerCount() > 4) { 02105 pane = addPaneToStack(); 02106 } 02107 02108 m_document->addLayerToView(pane, layer); 02109 } 02110 02111 added.insert(dm); 02112 } 02113 } 02114 } 02115 02116 for (int i = 0; i < (int)models.size(); ++i) { 02117 02118 Model *m = models[i]; 02119 02120 if (added.find(m) == added.end()) { 02121 02122 Layer *layer = m_document->createImportedLayer(m); 02123 if (!layer) return FileOpenFailed; 02124 02125 Pane *singleLayerPane = addPaneToStack(); 02126 if (m_timeRulerLayer) { 02127 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer); 02128 } 02129 m_document->addLayerToView(singleLayerPane, layer); 02130 } 02131 } 02132 02133 m_recentFiles.addFile(source.getLocation()); 02134 return FileOpenSucceeded; 02135 } 02136 02137 void 02138 MainWindowBase::createPlayTarget() 02139 { 02140 if (m_playTarget) return; 02141 02142 QSettings settings; 02143 settings.beginGroup("Preferences"); 02144 QString targetName = settings.value("audio-target", "").toString(); 02145 settings.endGroup(); 02146 02147 AudioTargetFactory *factory = AudioTargetFactory::getInstance(); 02148 02149 factory->setDefaultCallbackTarget(targetName); 02150 m_playTarget = factory->createCallbackTarget(m_playSource); 02151 02152 if (!m_playTarget) { 02153 emit hideSplash(); 02154 02155 if (factory->isAutoCallbackTarget(targetName)) { 02156 QMessageBox::warning 02157 (this, tr("Couldn't open audio device"), 02158 tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"), 02159 QMessageBox::Ok); 02160 } else { 02161 QMessageBox::warning 02162 (this, tr("Couldn't open audio device"), 02163 tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>") 02164 .arg(factory->getCallbackTargetDescription(targetName)), 02165 QMessageBox::Ok); 02166 } 02167 } 02168 } 02169 02170 WaveFileModel * 02171 MainWindowBase::getMainModel() 02172 { 02173 if (!m_document) return 0; 02174 return m_document->getMainModel(); 02175 } 02176 02177 const WaveFileModel * 02178 MainWindowBase::getMainModel() const 02179 { 02180 if (!m_document) return 0; 02181 return m_document->getMainModel(); 02182 } 02183 02184 void 02185 MainWindowBase::createDocument() 02186 { 02187 m_document = new Document; 02188 02189 connect(m_document, SIGNAL(layerAdded(Layer *)), 02190 this, SLOT(layerAdded(Layer *))); 02191 connect(m_document, SIGNAL(layerRemoved(Layer *)), 02192 this, SLOT(layerRemoved(Layer *))); 02193 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), 02194 this, SLOT(layerAboutToBeDeleted(Layer *))); 02195 connect(m_document, SIGNAL(layerInAView(Layer *, bool)), 02196 this, SLOT(layerInAView(Layer *, bool))); 02197 02198 connect(m_document, SIGNAL(modelAdded(Model *)), 02199 this, SLOT(modelAdded(Model *))); 02200 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), 02201 this, SLOT(mainModelChanged(WaveFileModel *))); 02202 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), 02203 this, SLOT(modelAboutToBeDeleted(Model *))); 02204 02205 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), 02206 this, SLOT(modelGenerationFailed(QString, QString))); 02207 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 02208 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 02209 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), 02210 this, SLOT(modelGenerationFailed(QString, QString))); 02211 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 02212 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 02213 connect(m_document, SIGNAL(alignmentFailed(QString, QString)), 02214 this, SLOT(alignmentFailed(QString, QString))); 02215 02216 emit replacedDocument(); 02217 } 02218 02219 bool 02220 MainWindowBase::saveSessionFile(QString path) 02221 { 02222 try { 02223 02224 TempWriteFile temp(path); 02225 02226 BZipFileDevice bzFile(temp.getTemporaryFilename()); 02227 if (!bzFile.open(QIODevice::WriteOnly)) { 02228 cerr << "Failed to open session file \"" 02229 << temp.getTemporaryFilename() 02230 << "\" for writing: " 02231 << bzFile.errorString() << endl; 02232 return false; 02233 } 02234 02235 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 02236 02237 QTextStream out(&bzFile); 02238 toXml(out, false); 02239 out.flush(); 02240 02241 QApplication::restoreOverrideCursor(); 02242 02243 if (!bzFile.isOK()) { 02244 QMessageBox::critical(this, tr("Failed to write file"), 02245 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") 02246 .arg(path).arg(bzFile.errorString())); 02247 bzFile.close(); 02248 return false; 02249 } 02250 02251 bzFile.close(); 02252 temp.moveToTarget(); 02253 return true; 02254 02255 } catch (FileOperationFailed &f) { 02256 02257 QMessageBox::critical(this, tr("Failed to write file"), 02258 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") 02259 .arg(path).arg(f.what())); 02260 return false; 02261 } 02262 } 02263 02264 bool 02265 MainWindowBase::saveSessionTemplate(QString path) 02266 { 02267 try { 02268 02269 TempWriteFile temp(path); 02270 02271 QFile file(temp.getTemporaryFilename()); 02272 if (!file.open(QIODevice::WriteOnly)) { 02273 cerr << "Failed to open session template file \"" 02274 << temp.getTemporaryFilename() 02275 << "\" for writing: " 02276 << file.errorString() << endl; 02277 return false; 02278 } 02279 02280 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 02281 02282 QTextStream out(&file); 02283 toXml(out, true); 02284 out.flush(); 02285 02286 QApplication::restoreOverrideCursor(); 02287 02288 file.close(); 02289 temp.moveToTarget(); 02290 return true; 02291 02292 } catch (FileOperationFailed &f) { 02293 02294 QMessageBox::critical(this, tr("Failed to write file"), 02295 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") 02296 .arg(path).arg(f.what())); 02297 return false; 02298 } 02299 } 02300 02301 void 02302 MainWindowBase::toXml(QTextStream &out, bool asTemplate) 02303 { 02304 QString indent(" "); 02305 02306 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 02307 out << "<!DOCTYPE sonic-visualiser>\n"; 02308 out << "<sv>\n"; 02309 02310 if (asTemplate) { 02311 m_document->toXmlAsTemplate(out, "", ""); 02312 } else { 02313 m_document->toXml(out, "", ""); 02314 } 02315 02316 out << "<display>\n"; 02317 02318 out << QString(" <window width=\"%1\" height=\"%2\"/>\n") 02319 .arg(width()).arg(height()); 02320 02321 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 02322 02323 Pane *pane = m_paneStack->getPane(i); 02324 02325 if (pane) { 02326 pane->toXml(out, indent); 02327 } 02328 } 02329 02330 out << "</display>\n"; 02331 02332 m_viewManager->getSelection().toXml(out); 02333 02334 out << "</sv>\n"; 02335 } 02336 02337 Pane * 02338 MainWindowBase::addPaneToStack() 02339 { 02340 cerr << "MainWindowBase::addPaneToStack()" << endl; 02341 AddPaneCommand *command = new AddPaneCommand(this); 02342 CommandHistory::getInstance()->addCommand(command); 02343 Pane *pane = command->getPane(); 02344 return pane; 02345 } 02346 02347 void 02348 MainWindowBase::zoomIn() 02349 { 02350 Pane *currentPane = m_paneStack->getCurrentPane(); 02351 if (currentPane) currentPane->zoom(true); 02352 } 02353 02354 void 02355 MainWindowBase::zoomOut() 02356 { 02357 Pane *currentPane = m_paneStack->getCurrentPane(); 02358 if (currentPane) currentPane->zoom(false); 02359 } 02360 02361 void 02362 MainWindowBase::zoomToFit() 02363 { 02364 Pane *currentPane = m_paneStack->getCurrentPane(); 02365 if (!currentPane) return; 02366 02367 Model *model = getMainModel(); 02368 if (!model) return; 02369 02370 int start = model->getStartFrame(); 02371 int end = model->getEndFrame(); 02372 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame()); 02373 int pixels = currentPane->width(); 02374 02375 int sw = currentPane->getVerticalScaleWidth(); 02376 if (pixels > sw * 2) pixels -= sw * 2; 02377 else pixels = 1; 02378 if (pixels > 4) pixels -= 4; 02379 02380 int zoomLevel = (end - start) / pixels; 02381 if (zoomLevel < 1) zoomLevel = 1; 02382 02383 currentPane->setZoomLevel(zoomLevel); 02384 currentPane->setCentreFrame((start + end) / 2); 02385 } 02386 02387 void 02388 MainWindowBase::zoomDefault() 02389 { 02390 Pane *currentPane = m_paneStack->getCurrentPane(); 02391 QSettings settings; 02392 settings.beginGroup("MainWindow"); 02393 int zoom = settings.value("zoom-default", 1024).toInt(); 02394 settings.endGroup(); 02395 if (currentPane) currentPane->setZoomLevel(zoom); 02396 } 02397 02398 void 02399 MainWindowBase::scrollLeft() 02400 { 02401 Pane *currentPane = m_paneStack->getCurrentPane(); 02402 if (currentPane) currentPane->scroll(false, false); 02403 } 02404 02405 void 02406 MainWindowBase::jumpLeft() 02407 { 02408 Pane *currentPane = m_paneStack->getCurrentPane(); 02409 if (currentPane) currentPane->scroll(false, true); 02410 } 02411 02412 void 02413 MainWindowBase::peekLeft() 02414 { 02415 Pane *currentPane = m_paneStack->getCurrentPane(); 02416 if (currentPane) currentPane->scroll(false, false, false); 02417 } 02418 02419 void 02420 MainWindowBase::scrollRight() 02421 { 02422 Pane *currentPane = m_paneStack->getCurrentPane(); 02423 if (currentPane) currentPane->scroll(true, false); 02424 } 02425 02426 void 02427 MainWindowBase::jumpRight() 02428 { 02429 Pane *currentPane = m_paneStack->getCurrentPane(); 02430 if (currentPane) currentPane->scroll(true, true); 02431 } 02432 02433 void 02434 MainWindowBase::peekRight() 02435 { 02436 Pane *currentPane = m_paneStack->getCurrentPane(); 02437 if (currentPane) currentPane->scroll(true, false, false); 02438 } 02439 02440 void 02441 MainWindowBase::showNoOverlays() 02442 { 02443 m_viewManager->setOverlayMode(ViewManager::NoOverlays); 02444 } 02445 02446 void 02447 MainWindowBase::showMinimalOverlays() 02448 { 02449 m_viewManager->setOverlayMode(ViewManager::StandardOverlays); 02450 } 02451 02452 void 02453 MainWindowBase::showAllOverlays() 02454 { 02455 m_viewManager->setOverlayMode(ViewManager::AllOverlays); 02456 } 02457 02458 void 02459 MainWindowBase::toggleTimeRulers() 02460 { 02461 bool haveRulers = false; 02462 bool someHidden = false; 02463 02464 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 02465 02466 Pane *pane = m_paneStack->getPane(i); 02467 if (!pane) continue; 02468 02469 for (int j = 0; j < pane->getLayerCount(); ++j) { 02470 02471 Layer *layer = pane->getLayer(j); 02472 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue; 02473 02474 haveRulers = true; 02475 if (layer->isLayerDormant(pane)) someHidden = true; 02476 } 02477 } 02478 02479 if (haveRulers) { 02480 02481 bool show = someHidden; 02482 02483 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 02484 02485 Pane *pane = m_paneStack->getPane(i); 02486 if (!pane) continue; 02487 02488 for (int j = 0; j < pane->getLayerCount(); ++j) { 02489 02490 Layer *layer = pane->getLayer(j); 02491 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue; 02492 02493 layer->showLayer(pane, show); 02494 } 02495 } 02496 } 02497 } 02498 02499 void 02500 MainWindowBase::toggleZoomWheels() 02501 { 02502 if (m_viewManager->getZoomWheelsEnabled()) { 02503 m_viewManager->setZoomWheelsEnabled(false); 02504 } else { 02505 m_viewManager->setZoomWheelsEnabled(true); 02506 } 02507 } 02508 02509 void 02510 MainWindowBase::togglePropertyBoxes() 02511 { 02512 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { 02513 if (Preferences::getInstance()->getPropertyBoxLayout() == 02514 Preferences::VerticallyStacked) { 02515 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); 02516 } else { 02517 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); 02518 } 02519 } else { 02520 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); 02521 } 02522 } 02523 02524 QLabel * 02525 MainWindowBase::getStatusLabel() const 02526 { 02527 if (!m_statusLabel) { 02528 m_statusLabel = new QLabel(); 02529 statusBar()->addWidget(m_statusLabel, 1); 02530 } 02531 02532 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>(); 02533 foreach (QFrame *f, frames) { 02534 f->setFrameStyle(QFrame::NoFrame); 02535 } 02536 02537 return m_statusLabel; 02538 } 02539 02540 void 02541 MainWindowBase::toggleStatusBar() 02542 { 02543 QSettings settings; 02544 settings.beginGroup("MainWindow"); 02545 bool sb = settings.value("showstatusbar", true).toBool(); 02546 02547 if (sb) { 02548 statusBar()->hide(); 02549 } else { 02550 statusBar()->show(); 02551 } 02552 02553 settings.setValue("showstatusbar", !sb); 02554 02555 settings.endGroup(); 02556 } 02557 02558 void 02559 MainWindowBase::toggleCentreLine() 02560 { 02561 if (m_viewManager->shouldShowCentreLine()) { 02562 m_viewManager->setShowCentreLine(false); 02563 } else { 02564 m_viewManager->setShowCentreLine(true); 02565 } 02566 } 02567 02568 void 02569 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name) 02570 { 02571 if (name == "Property Box Layout") { 02572 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { 02573 if (Preferences::getInstance()->getPropertyBoxLayout() == 02574 Preferences::VerticallyStacked) { 02575 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); 02576 } else { 02577 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); 02578 } 02579 } 02580 } else if (name == "Background Mode" && m_viewManager) { 02581 Preferences::BackgroundMode mode = 02582 Preferences::getInstance()->getBackgroundMode(); 02583 if (mode == Preferences::BackgroundFromTheme) { 02584 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); 02585 } else if (mode == Preferences::DarkBackground) { 02586 m_viewManager->setGlobalDarkBackground(true); 02587 } else { 02588 m_viewManager->setGlobalDarkBackground(false); 02589 } 02590 } 02591 } 02592 02593 void 02594 MainWindowBase::play() 02595 { 02596 if (m_playSource->isPlaying()) { 02597 stop(); 02598 } else { 02599 playbackFrameChanged(m_viewManager->getPlaybackFrame()); 02600 m_playSource->play(m_viewManager->getPlaybackFrame()); 02601 } 02602 } 02603 02604 void 02605 MainWindowBase::ffwd() 02606 { 02607 if (!getMainModel()) return; 02608 02609 int frame = m_viewManager->getPlaybackFrame(); 02610 ++frame; 02611 02612 Pane *pane = m_paneStack->getCurrentPane(); 02613 Layer *layer = getSnapLayer(); 02614 int sr = getMainModel()->getSampleRate(); 02615 02616 if (!layer) { 02617 02618 frame = RealTime::realTime2Frame 02619 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr); 02620 if (frame > int(getMainModel()->getEndFrame())) { 02621 frame = getMainModel()->getEndFrame(); 02622 } 02623 02624 } else { 02625 02626 int resolution = 0; 02627 if (pane) frame = pane->alignFromReference(frame); 02628 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), 02629 frame, resolution, Layer::SnapRight)) { 02630 if (pane) frame = pane->alignToReference(frame); 02631 } else { 02632 frame = getMainModel()->getEndFrame(); 02633 } 02634 } 02635 02636 if (frame < 0) frame = 0; 02637 02638 if (m_viewManager->getPlaySelectionMode()) { 02639 frame = m_viewManager->constrainFrameToSelection(int(frame)); 02640 } 02641 02642 m_viewManager->setPlaybackFrame(frame); 02643 02644 if (frame == (int)getMainModel()->getEndFrame() && 02645 m_playSource && 02646 m_playSource->isPlaying() && 02647 !m_viewManager->getPlayLoopMode()) { 02648 stop(); 02649 } 02650 } 02651 02652 void 02653 MainWindowBase::ffwdEnd() 02654 { 02655 if (!getMainModel()) return; 02656 02657 if (m_playSource && 02658 m_playSource->isPlaying() && 02659 !m_viewManager->getPlayLoopMode()) { 02660 stop(); 02661 } 02662 02663 int frame = getMainModel()->getEndFrame(); 02664 02665 if (m_viewManager->getPlaySelectionMode()) { 02666 frame = m_viewManager->constrainFrameToSelection(frame); 02667 } 02668 02669 m_viewManager->setPlaybackFrame(frame); 02670 } 02671 02672 void 02673 MainWindowBase::ffwdSimilar() 02674 { 02675 if (!getMainModel()) return; 02676 02677 Layer *layer = getSnapLayer(); 02678 if (!layer) { ffwd(); return; } 02679 02680 Pane *pane = m_paneStack->getCurrentPane(); 02681 int frame = m_viewManager->getPlaybackFrame(); 02682 02683 int resolution = 0; 02684 if (pane) frame = pane->alignFromReference(frame); 02685 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(), 02686 frame, resolution, Layer::SnapRight)) { 02687 if (pane) frame = pane->alignToReference(frame); 02688 } else { 02689 frame = getMainModel()->getEndFrame(); 02690 } 02691 02692 if (frame < 0) frame = 0; 02693 02694 if (m_viewManager->getPlaySelectionMode()) { 02695 frame = m_viewManager->constrainFrameToSelection(int(frame)); 02696 } 02697 02698 m_viewManager->setPlaybackFrame(frame); 02699 02700 if (frame == (int)getMainModel()->getEndFrame() && 02701 m_playSource && 02702 m_playSource->isPlaying() && 02703 !m_viewManager->getPlayLoopMode()) { 02704 stop(); 02705 } 02706 } 02707 02708 void 02709 MainWindowBase::rewind() 02710 { 02711 if (!getMainModel()) return; 02712 02713 int frame = m_viewManager->getPlaybackFrame(); 02714 if (frame > 0) --frame; 02715 02716 Pane *pane = m_paneStack->getCurrentPane(); 02717 Layer *layer = getSnapLayer(); 02718 int sr = getMainModel()->getSampleRate(); 02719 02720 // when rewinding during playback, we want to allow a period 02721 // following a rewind target point at which the rewind will go to 02722 // the prior point instead of the immediately neighbouring one 02723 if (m_playSource && m_playSource->isPlaying()) { 02724 RealTime ct = RealTime::frame2RealTime(frame, sr); 02725 ct = ct - RealTime::fromSeconds(0.15); 02726 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; 02727 frame = RealTime::realTime2Frame(ct, sr); 02728 } 02729 02730 if (!layer) { 02731 02732 frame = RealTime::realTime2Frame 02733 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr); 02734 if (frame < int(getMainModel()->getStartFrame())) { 02735 frame = getMainModel()->getStartFrame(); 02736 } 02737 02738 } else { 02739 02740 int resolution = 0; 02741 if (pane) frame = pane->alignFromReference(frame); 02742 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), 02743 frame, resolution, Layer::SnapLeft)) { 02744 if (pane) frame = pane->alignToReference(frame); 02745 } else { 02746 frame = getMainModel()->getStartFrame(); 02747 } 02748 } 02749 02750 if (frame < 0) frame = 0; 02751 02752 if (m_viewManager->getPlaySelectionMode()) { 02753 frame = m_viewManager->constrainFrameToSelection(int(frame)); 02754 } 02755 02756 m_viewManager->setPlaybackFrame(frame); 02757 } 02758 02759 void 02760 MainWindowBase::rewindStart() 02761 { 02762 if (!getMainModel()) return; 02763 02764 int frame = getMainModel()->getStartFrame(); 02765 02766 if (m_viewManager->getPlaySelectionMode()) { 02767 frame = m_viewManager->constrainFrameToSelection(frame); 02768 } 02769 02770 m_viewManager->setPlaybackFrame(frame); 02771 } 02772 02773 void 02774 MainWindowBase::rewindSimilar() 02775 { 02776 if (!getMainModel()) return; 02777 02778 Layer *layer = getSnapLayer(); 02779 if (!layer) { rewind(); return; } 02780 02781 Pane *pane = m_paneStack->getCurrentPane(); 02782 int frame = m_viewManager->getPlaybackFrame(); 02783 02784 int resolution = 0; 02785 if (pane) frame = pane->alignFromReference(frame); 02786 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(), 02787 frame, resolution, Layer::SnapLeft)) { 02788 if (pane) frame = pane->alignToReference(frame); 02789 } else { 02790 frame = getMainModel()->getStartFrame(); 02791 } 02792 02793 if (frame < 0) frame = 0; 02794 02795 if (m_viewManager->getPlaySelectionMode()) { 02796 frame = m_viewManager->constrainFrameToSelection(int(frame)); 02797 } 02798 02799 m_viewManager->setPlaybackFrame(frame); 02800 } 02801 02802 Layer * 02803 MainWindowBase::getSnapLayer() const 02804 { 02805 Pane *pane = m_paneStack->getCurrentPane(); 02806 if (!pane) return 0; 02807 02808 Layer *layer = pane->getSelectedLayer(); 02809 02810 if (!dynamic_cast<TimeInstantLayer *>(layer) && 02811 !dynamic_cast<TimeValueLayer *>(layer) && 02812 !dynamic_cast<RegionLayer *>(layer) && 02813 !dynamic_cast<TimeRulerLayer *>(layer)) { 02814 02815 layer = 0; 02816 02817 for (int i = pane->getLayerCount(); i > 0; --i) { 02818 Layer *l = pane->getLayer(i-1); 02819 if (dynamic_cast<TimeRulerLayer *>(l)) { 02820 layer = l; 02821 break; 02822 } 02823 } 02824 } 02825 02826 return layer; 02827 } 02828 02829 void 02830 MainWindowBase::stop() 02831 { 02832 m_playSource->stop(); 02833 02834 if (m_paneStack && m_paneStack->getCurrentPane()) { 02835 updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); 02836 } else { 02837 m_myStatusMessage = ""; 02838 getStatusLabel()->setText(""); 02839 } 02840 } 02841 02842 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) : 02843 m_mw(mw), 02844 m_pane(0), 02845 m_prevCurrentPane(0), 02846 m_added(false) 02847 { 02848 } 02849 02850 MainWindowBase::AddPaneCommand::~AddPaneCommand() 02851 { 02852 if (m_pane && !m_added) { 02853 m_mw->m_paneStack->deletePane(m_pane); 02854 } 02855 } 02856 02857 QString 02858 MainWindowBase::AddPaneCommand::getName() const 02859 { 02860 return tr("Add Pane"); 02861 } 02862 02863 void 02864 MainWindowBase::AddPaneCommand::execute() 02865 { 02866 if (!m_pane) { 02867 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); 02868 m_pane = m_mw->m_paneStack->addPane(); 02869 02870 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), 02871 m_mw, SLOT(contextHelpChanged(const QString &))); 02872 } else { 02873 m_mw->m_paneStack->showPane(m_pane); 02874 } 02875 02876 m_mw->m_paneStack->setCurrentPane(m_pane); 02877 m_added = true; 02878 } 02879 02880 void 02881 MainWindowBase::AddPaneCommand::unexecute() 02882 { 02883 m_mw->m_paneStack->hidePane(m_pane); 02884 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); 02885 m_added = false; 02886 } 02887 02888 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) : 02889 m_mw(mw), 02890 m_pane(pane), 02891 m_prevCurrentPane(0), 02892 m_added(true) 02893 { 02894 } 02895 02896 MainWindowBase::RemovePaneCommand::~RemovePaneCommand() 02897 { 02898 if (m_pane && !m_added) { 02899 m_mw->m_paneStack->deletePane(m_pane); 02900 } 02901 } 02902 02903 QString 02904 MainWindowBase::RemovePaneCommand::getName() const 02905 { 02906 return tr("Remove Pane"); 02907 } 02908 02909 void 02910 MainWindowBase::RemovePaneCommand::execute() 02911 { 02912 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); 02913 m_mw->m_paneStack->hidePane(m_pane); 02914 m_added = false; 02915 } 02916 02917 void 02918 MainWindowBase::RemovePaneCommand::unexecute() 02919 { 02920 m_mw->m_paneStack->showPane(m_pane); 02921 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); 02922 m_added = true; 02923 } 02924 02925 void 02926 MainWindowBase::deleteCurrentPane() 02927 { 02928 CommandHistory::getInstance()->startCompoundOperation 02929 (tr("Delete Pane"), true); 02930 02931 Pane *pane = m_paneStack->getCurrentPane(); 02932 if (pane) { 02933 while (pane->getLayerCount() > 0) { 02934 Layer *layer = pane->getLayer(0); 02935 if (layer) { 02936 m_document->removeLayerFromView(pane, layer); 02937 } else { 02938 break; 02939 } 02940 } 02941 02942 RemovePaneCommand *command = new RemovePaneCommand(this, pane); 02943 CommandHistory::getInstance()->addCommand(command); 02944 } 02945 02946 CommandHistory::getInstance()->endCompoundOperation(); 02947 02948 updateMenuStates(); 02949 } 02950 02951 void 02952 MainWindowBase::deleteCurrentLayer() 02953 { 02954 Pane *pane = m_paneStack->getCurrentPane(); 02955 if (pane) { 02956 Layer *layer = pane->getSelectedLayer(); 02957 if (layer) { 02958 m_document->removeLayerFromView(pane, layer); 02959 } 02960 } 02961 updateMenuStates(); 02962 } 02963 02964 void 02965 MainWindowBase::editCurrentLayer() 02966 { 02967 Layer *layer = 0; 02968 Pane *pane = m_paneStack->getCurrentPane(); 02969 if (pane) layer = pane->getSelectedLayer(); 02970 if (!layer) return; 02971 02972 Model *model = layer->getModel(); 02973 if (!model) return; 02974 02975 TabularModel *tabular = dynamic_cast<TabularModel *>(model); 02976 if (!tabular) { 02978 //appropriate model type? or will we ultimately support 02979 //tabular display for all editable models? 02980 SVDEBUG << "NOTE: Not a tabular model" << endl; 02981 return; 02982 } 02983 02984 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) { 02985 if (!m_layerDataDialogMap[layer].isNull()) { 02986 m_layerDataDialogMap[layer]->show(); 02987 m_layerDataDialogMap[layer]->raise(); 02988 return; 02989 } 02990 } 02991 02992 QString title = layer->getLayerPresentationName(); 02993 02994 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this); 02995 dialog->setAttribute(Qt::WA_DeleteOnClose); 02996 02997 connectLayerEditDialog(dialog); 02998 02999 m_layerDataDialogMap[layer] = dialog; 03000 m_viewDataDialogMap[pane].insert(dialog); 03001 03002 dialog->show(); 03003 } 03004 03005 void 03006 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog) 03007 { 03008 connect(m_viewManager, 03009 SIGNAL(globalCentreFrameChanged(int)), 03010 dialog, 03011 SLOT(userScrolledToFrame(int))); 03012 03013 connect(m_viewManager, 03014 SIGNAL(playbackFrameChanged(int)), 03015 dialog, 03016 SLOT(playbackScrolledToFrame(int))); 03017 03018 connect(dialog, 03019 SIGNAL(scrollToFrame(int)), 03020 m_viewManager, 03021 SLOT(setGlobalCentreFrame(int))); 03022 03023 connect(dialog, 03024 SIGNAL(scrollToFrame(int)), 03025 m_viewManager, 03026 SLOT(setPlaybackFrame(int))); 03027 } 03028 03029 void 03030 MainWindowBase::previousPane() 03031 { 03032 if (!m_paneStack) return; 03033 03034 Pane *currentPane = m_paneStack->getCurrentPane(); 03035 if (!currentPane) return; 03036 03037 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 03038 if (m_paneStack->getPane(i) == currentPane) { 03039 if (i == 0) return; 03040 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1)); 03041 updateMenuStates(); 03042 return; 03043 } 03044 } 03045 } 03046 03047 void 03048 MainWindowBase::nextPane() 03049 { 03050 if (!m_paneStack) return; 03051 03052 Pane *currentPane = m_paneStack->getCurrentPane(); 03053 if (!currentPane) return; 03054 03055 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 03056 if (m_paneStack->getPane(i) == currentPane) { 03057 if (i == m_paneStack->getPaneCount()-1) return; 03058 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1)); 03059 updateMenuStates(); 03060 return; 03061 } 03062 } 03063 } 03064 03065 void 03066 MainWindowBase::previousLayer() 03067 { 03068 if (!m_paneStack) return; 03069 03070 Pane *currentPane = m_paneStack->getCurrentPane(); 03071 if (!currentPane) return; 03072 03073 int count = currentPane->getLayerCount(); 03074 if (count == 0) return; 03075 03076 Layer *currentLayer = currentPane->getSelectedLayer(); 03077 03078 if (!currentLayer) { 03079 // The pane itself is current 03080 m_paneStack->setCurrentLayer 03081 (currentPane, currentPane->getFixedOrderLayer(count-1)); 03082 } else { 03083 for (int i = 0; i < count; ++i) { 03084 if (currentPane->getFixedOrderLayer(i) == currentLayer) { 03085 if (i == 0) { 03086 m_paneStack->setCurrentLayer 03087 (currentPane, 0); // pane 03088 } else { 03089 m_paneStack->setCurrentLayer 03090 (currentPane, currentPane->getFixedOrderLayer(i-1)); 03091 } 03092 break; 03093 } 03094 } 03095 } 03096 03097 updateMenuStates(); 03098 } 03099 03100 void 03101 MainWindowBase::nextLayer() 03102 { 03103 if (!m_paneStack) return; 03104 03105 Pane *currentPane = m_paneStack->getCurrentPane(); 03106 if (!currentPane) return; 03107 03108 int count = currentPane->getLayerCount(); 03109 if (count == 0) return; 03110 03111 Layer *currentLayer = currentPane->getSelectedLayer(); 03112 03113 if (!currentLayer) { 03114 // The pane itself is current 03115 m_paneStack->setCurrentLayer 03116 (currentPane, currentPane->getFixedOrderLayer(0)); 03117 } else { 03118 for (int i = 0; i < count; ++i) { 03119 if (currentPane->getFixedOrderLayer(i) == currentLayer) { 03120 if (i == currentPane->getLayerCount()-1) { 03121 m_paneStack->setCurrentLayer 03122 (currentPane, 0); // pane 03123 } else { 03124 m_paneStack->setCurrentLayer 03125 (currentPane, currentPane->getFixedOrderLayer(i+1)); 03126 } 03127 break; 03128 } 03129 } 03130 } 03131 03132 updateMenuStates(); 03133 } 03134 03135 void 03136 MainWindowBase::playbackFrameChanged(int frame) 03137 { 03138 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; 03139 03140 updatePositionStatusDisplays(); 03141 03142 RealTime now = RealTime::frame2RealTime 03143 (frame, getMainModel()->getSampleRate()); 03144 03145 if (now.sec == m_lastPlayStatusSec) return; 03146 03147 RealTime then = RealTime::frame2RealTime 03148 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); 03149 03150 QString nowStr; 03151 QString thenStr; 03152 QString remainingStr; 03153 03154 if (then.sec > 10) { 03155 nowStr = now.toSecText().c_str(); 03156 thenStr = then.toSecText().c_str(); 03157 remainingStr = (then - now).toSecText().c_str(); 03158 m_lastPlayStatusSec = now.sec; 03159 } else { 03160 nowStr = now.toText(true).c_str(); 03161 thenStr = then.toText(true).c_str(); 03162 remainingStr = (then - now).toText(true).c_str(); 03163 } 03164 03165 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") 03166 .arg(nowStr).arg(thenStr).arg(remainingStr); 03167 03168 getStatusLabel()->setText(m_myStatusMessage); 03169 } 03170 03171 void 03172 MainWindowBase::globalCentreFrameChanged(int ) 03173 { 03174 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; 03175 Pane *p = 0; 03176 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; 03177 if (!p->getFollowGlobalPan()) return; 03178 updateVisibleRangeDisplay(p); 03179 } 03180 03181 void 03182 MainWindowBase::viewCentreFrameChanged(View *v, int frame) 03183 { 03184 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl; 03185 03186 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) { 03187 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin(); 03188 i != m_viewDataDialogMap[v].end(); ++i) { 03189 (*i)->userScrolledToFrame(frame); 03190 } 03191 } 03192 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; 03193 Pane *p = 0; 03194 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; 03195 if (v == p) updateVisibleRangeDisplay(p); 03196 } 03197 03198 void 03199 MainWindowBase::viewZoomLevelChanged(View *v, int , bool ) 03200 { 03201 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; 03202 Pane *p = 0; 03203 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; 03204 if (v == p) updateVisibleRangeDisplay(p); 03205 } 03206 03207 void 03208 MainWindowBase::layerAdded(Layer *) 03209 { 03210 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl; 03211 updateMenuStates(); 03212 } 03213 03214 void 03215 MainWindowBase::layerRemoved(Layer *) 03216 { 03217 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl; 03218 updateMenuStates(); 03219 } 03220 03221 void 03222 MainWindowBase::layerAboutToBeDeleted(Layer *layer) 03223 { 03224 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl; 03225 03226 removeLayerEditDialog(layer); 03227 03228 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) { 03229 // cerr << "(this is the time ruler layer)" << endl; 03230 m_timeRulerLayer = 0; 03231 } 03232 } 03233 03234 void 03235 MainWindowBase::layerInAView(Layer *layer, bool inAView) 03236 { 03237 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl; 03238 03239 if (!inAView) removeLayerEditDialog(layer); 03240 03241 // Check whether we need to add or remove model from play source 03242 Model *model = layer->getModel(); 03243 if (model) { 03244 if (inAView) { 03245 m_playSource->addModel(model); 03246 } else { 03247 bool found = false; 03248 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 03249 Pane *pane = m_paneStack->getPane(i); 03250 if (!pane) continue; 03251 for (int j = 0; j < pane->getLayerCount(); ++j) { 03252 Layer *pl = pane->getLayer(j); 03253 if (pl && 03254 !dynamic_cast<TimeRulerLayer *>(pl) && 03255 (pl->getModel() == model)) { 03256 found = true; 03257 break; 03258 } 03259 } 03260 if (found) break; 03261 } 03262 if (!found) { 03263 m_playSource->removeModel(model); 03264 } 03265 } 03266 } 03267 03268 updateMenuStates(); 03269 } 03270 03271 void 03272 MainWindowBase::removeLayerEditDialog(Layer *layer) 03273 { 03274 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) { 03275 03276 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer]; 03277 03278 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin(); 03279 vi != m_viewDataDialogMap.end(); ++vi) { 03280 vi->second.erase(dialog); 03281 } 03282 03283 m_layerDataDialogMap.erase(layer); 03284 delete dialog; 03285 } 03286 } 03287 03288 void 03289 MainWindowBase::modelAdded(Model *model) 03290 { 03291 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl; 03292 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl; 03293 m_playSource->addModel(model); 03294 } 03295 03296 void 03297 MainWindowBase::mainModelChanged(WaveFileModel *model) 03298 { 03299 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; 03300 updateDescriptionLabel(); 03301 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); 03302 if (model && !m_playTarget && m_audioOutput) { 03303 createPlayTarget(); 03304 } 03305 } 03306 03307 void 03308 MainWindowBase::modelAboutToBeDeleted(Model *model) 03309 { 03310 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl; 03311 if (model == m_viewManager->getPlaybackModel()) { 03312 m_viewManager->setPlaybackModel(0); 03313 } 03314 m_playSource->removeModel(model); 03315 FFTDataServer::modelAboutToBeDeleted(model); 03316 } 03317 03318 void 03319 MainWindowBase::paneDeleteButtonClicked(Pane *pane) 03320 { 03321 bool found = false; 03322 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { 03323 if (m_paneStack->getPane(i) == pane) { 03324 found = true; 03325 break; 03326 } 03327 } 03328 if (!found) { 03329 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane " 03330 << pane << endl; 03331 return; 03332 } 03333 03334 CommandHistory::getInstance()->startCompoundOperation 03335 (tr("Delete Pane"), true); 03336 03337 while (pane->getLayerCount() > 0) { 03338 Layer *layer = pane->getLayer(0); 03339 if (layer) { 03340 m_document->removeLayerFromView(pane, layer); 03341 } else { 03342 break; 03343 } 03344 } 03345 03346 RemovePaneCommand *command = new RemovePaneCommand(this, pane); 03347 CommandHistory::getInstance()->addCommand(command); 03348 03349 CommandHistory::getInstance()->endCompoundOperation(); 03350 03351 updateMenuStates(); 03352 } 03353 03354 void 03355 MainWindowBase::pollOSC() 03356 { 03357 if (!m_oscQueue || m_oscQueue->isEmpty()) return; 03358 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl; 03359 03360 if (m_openingAudioFile) return; 03361 03362 OSCMessage message = m_oscQueue->readMessage(); 03363 03364 if (message.getTarget() != 0) { 03365 return; 03366 } 03367 03368 handleOSCMessage(message); 03369 } 03370 03371 void 03372 MainWindowBase::inProgressSelectionChanged() 03373 { 03374 Pane *currentPane = 0; 03375 if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); 03376 if (currentPane) { 03377 //cerr << "JTEST: mouse event on selection pane" << endl; 03378 updateVisibleRangeDisplay(currentPane); 03379 } 03380 } 03381 03382 void 03383 MainWindowBase::contextHelpChanged(const QString &s) 03384 { 03385 QLabel *lab = getStatusLabel(); 03386 03387 if (s == "" && m_myStatusMessage != "") { 03388 if (lab->text() != m_myStatusMessage) { 03389 lab->setText(m_myStatusMessage); 03390 } 03391 return; 03392 } 03393 03394 lab->setText(s); 03395 } 03396 03397 void 03398 MainWindowBase::openHelpUrl(QString url) 03399 { 03400 // This method mostly lifted from Qt Assistant source code 03401 03402 QProcess *process = new QProcess(this); 03403 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); 03404 03405 QStringList args; 03406 03407 #ifdef Q_OS_MAC 03408 args.append(url); 03409 process->start("open", args); 03410 #else 03411 #ifdef Q_OS_WIN32 03412 QString pf(getenv("ProgramFiles")); 03413 QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); 03414 03415 args.append(url); 03416 process->start(command, args); 03417 #else 03418 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { 03419 args.append("exec"); 03420 args.append(url); 03421 process->start("kfmclient", args); 03422 } else if (!qgetenv("BROWSER").isEmpty()) { 03423 args.append(url); 03424 process->start(qgetenv("BROWSER"), args); 03425 } else { 03426 args.append(url); 03427 process->start("firefox", args); 03428 } 03429 #endif 03430 #endif 03431 } 03432 03433