| Chris@0 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 2 | 
| Chris@0 | 3 /* | 
| Chris@0 | 4     Sonic Visualiser | 
| Chris@0 | 5     An audio file viewer and annotation editor. | 
| Chris@0 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@77 | 7     This file copyright 2006 Chris Cannam and QMUL. | 
| Chris@0 | 8 | 
| Chris@0 | 9     This program is free software; you can redistribute it and/or | 
| Chris@0 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@0 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@0 | 12     License, or (at your option) any later version.  See the file | 
| Chris@0 | 13     COPYING included with this distribution for more information. | 
| Chris@0 | 14 */ | 
| Chris@0 | 15 | 
| Chris@1 | 16 #include "version.h" | 
| Chris@0 | 17 | 
| Chris@0 | 18 #include "MainWindow.h" | 
| Chris@1 | 19 #include "document/Document.h" | 
| Chris@0 | 20 #include "PreferencesDialog.h" | 
| Chris@0 | 21 | 
| Chris@1 | 22 #include "view/Pane.h" | 
| Chris@1 | 23 #include "view/PaneStack.h" | 
| Chris@1 | 24 #include "data/model/WaveFileModel.h" | 
| Chris@1 | 25 #include "data/model/SparseOneDimensionalModel.h" | 
| Chris@1 | 26 #include "view/ViewManager.h" | 
| Chris@0 | 27 #include "base/Preferences.h" | 
| Chris@0 | 28 #include "layer/WaveformLayer.h" | 
| Chris@0 | 29 #include "layer/TimeRulerLayer.h" | 
| Chris@0 | 30 #include "layer/TimeInstantLayer.h" | 
| Chris@0 | 31 #include "layer/TimeValueLayer.h" | 
| Chris@0 | 32 #include "layer/Colour3DPlotLayer.h" | 
| Chris@95 | 33 #include "layer/SliceLayer.h" | 
| Chris@95 | 34 #include "layer/SliceableLayer.h" | 
| Chris@0 | 35 #include "widgets/Fader.h" | 
| Chris@65 | 36 #include "view/Overview.h" | 
| Chris@0 | 37 #include "widgets/PropertyBox.h" | 
| Chris@0 | 38 #include "widgets/PropertyStack.h" | 
| Chris@0 | 39 #include "widgets/AudioDial.h" | 
| Chris@0 | 40 #include "widgets/LayerTree.h" | 
| Chris@0 | 41 #include "widgets/ListInputDialog.h" | 
| Chris@36 | 42 #include "widgets/SubdividingMenu.h" | 
| Chris@90 | 43 #include "widgets/NotifyingPushButton.h" | 
| Chris@0 | 44 #include "audioio/AudioCallbackPlaySource.h" | 
| Chris@0 | 45 #include "audioio/AudioCallbackPlayTarget.h" | 
| Chris@0 | 46 #include "audioio/AudioTargetFactory.h" | 
| Chris@59 | 47 #include "audioio/PlaySpeedRangeMapper.h" | 
| Chris@1 | 48 #include "data/fileio/AudioFileReaderFactory.h" | 
| Chris@1 | 49 #include "data/fileio/DataFileReaderFactory.h" | 
| Chris@1 | 50 #include "data/fileio/WavFileWriter.h" | 
| Chris@1 | 51 #include "data/fileio/CSVFileWriter.h" | 
| Chris@1 | 52 #include "data/fileio/BZipFileDevice.h" | 
| Chris@85 | 53 #include "data/fileio/RemoteFile.h" | 
| Chris@91 | 54 #include "data/fft/FFTDataServer.h" | 
| Chris@1 | 55 #include "base/RecentFiles.h" | 
| Chris@0 | 56 #include "transform/TransformFactory.h" | 
| Chris@0 | 57 #include "base/PlayParameterRepository.h" | 
| Chris@0 | 58 #include "base/XmlExportable.h" | 
| Chris@0 | 59 #include "base/CommandHistory.h" | 
| Chris@0 | 60 #include "base/Profiler.h" | 
| Chris@0 | 61 #include "base/Clipboard.h" | 
| Chris@69 | 62 #include "osc/OSCQueue.h" | 
| Chris@0 | 63 | 
| Chris@0 | 64 // For version information | 
| Chris@0 | 65 #include "vamp/vamp.h" | 
| Chris@0 | 66 #include "vamp-sdk/PluginBase.h" | 
| Chris@0 | 67 #include "plugin/api/ladspa.h" | 
| Chris@0 | 68 #include "plugin/api/dssi.h" | 
| Chris@0 | 69 | 
| Chris@0 | 70 #include <QApplication> | 
| Chris@0 | 71 #include <QMessageBox> | 
| Chris@0 | 72 #include <QGridLayout> | 
| Chris@0 | 73 #include <QLabel> | 
| Chris@0 | 74 #include <QAction> | 
| Chris@0 | 75 #include <QMenuBar> | 
| Chris@0 | 76 #include <QToolBar> | 
| Chris@0 | 77 #include <QInputDialog> | 
| Chris@0 | 78 #include <QStatusBar> | 
| Chris@0 | 79 #include <QTreeView> | 
| Chris@0 | 80 #include <QFile> | 
| Chris@88 | 81 #include <QFileInfo> | 
| Chris@88 | 82 #include <QDir> | 
| Chris@0 | 83 #include <QTextStream> | 
| Chris@0 | 84 #include <QProcess> | 
| Chris@7 | 85 #include <QShortcut> | 
| Chris@5 | 86 #include <QSettings> | 
| Chris@11 | 87 #include <QDateTime> | 
| Chris@11 | 88 #include <QProcess> | 
| Chris@16 | 89 #include <QCheckBox> | 
| Chris@55 | 90 #include <QRegExp> | 
| Chris@0 | 91 | 
| Chris@0 | 92 #include <iostream> | 
| Chris@0 | 93 #include <cstdio> | 
| Chris@0 | 94 #include <errno.h> | 
| Chris@0 | 95 | 
| Chris@0 | 96 using std::cerr; | 
| Chris@0 | 97 using std::endl; | 
| Chris@0 | 98 | 
| Chris@33 | 99 using std::vector; | 
| Chris@33 | 100 using std::map; | 
| Chris@33 | 101 using std::set; | 
| Chris@33 | 102 | 
| Chris@0 | 103 | 
| Chris@70 | 104 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : | 
| Chris@0 | 105     m_document(0), | 
| Chris@0 | 106     m_paneStack(0), | 
| Chris@0 | 107     m_viewManager(0), | 
| Chris@65 | 108     m_overview(0), | 
| Chris@0 | 109     m_timeRulerLayer(0), | 
| Chris@46 | 110     m_audioOutput(withAudioOutput), | 
| Chris@0 | 111     m_playSource(0), | 
| Chris@0 | 112     m_playTarget(0), | 
| Chris@70 | 113     m_oscQueue(withOSCSupport ? new OSCQueue() : 0), | 
| Chris@81 | 114     m_recentFiles("RecentFiles", 20), | 
| Chris@49 | 115     m_recentTransforms("RecentTransforms", 20), | 
| Chris@0 | 116     m_mainMenusCreated(false), | 
| Chris@0 | 117     m_paneMenu(0), | 
| Chris@0 | 118     m_layerMenu(0), | 
| Chris@34 | 119     m_transformsMenu(0), | 
| Chris@0 | 120     m_existingLayersMenu(0), | 
| Chris@95 | 121     m_sliceMenu(0), | 
| Chris@34 | 122     m_recentFilesMenu(0), | 
| Chris@34 | 123     m_recentTransformsMenu(0), | 
| Chris@0 | 124     m_rightButtonMenu(0), | 
| Chris@0 | 125     m_rightButtonLayerMenu(0), | 
| Chris@34 | 126     m_rightButtonTransformsMenu(0), | 
| Chris@0 | 127     m_documentModified(false), | 
| Chris@70 | 128     m_openingAudioFile(false), | 
| Chris@70 | 129     m_abandoning(false), | 
| Chris@0 | 130     m_preferencesDialog(0) | 
| Chris@0 | 131 { | 
| Chris@0 | 132     setWindowTitle(tr("Sonic Visualiser")); | 
| Chris@0 | 133 | 
| Chris@0 | 134     UnitDatabase::getInstance()->registerUnit("Hz"); | 
| Chris@0 | 135     UnitDatabase::getInstance()->registerUnit("dB"); | 
| Chris@0 | 136 | 
| Chris@0 | 137     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), | 
| Chris@0 | 138 	    this, SLOT(documentModified())); | 
| Chris@0 | 139     connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), | 
| Chris@0 | 140 	    this, SLOT(documentRestored())); | 
| Chris@0 | 141 | 
| Chris@0 | 142     QFrame *frame = new QFrame; | 
| Chris@0 | 143     setCentralWidget(frame); | 
| Chris@0 | 144 | 
| Chris@0 | 145     QGridLayout *layout = new QGridLayout; | 
| Chris@0 | 146 | 
| Chris@0 | 147     m_viewManager = new ViewManager(); | 
| Chris@0 | 148     connect(m_viewManager, SIGNAL(selectionChanged()), | 
| Chris@0 | 149 	    this, SLOT(updateMenuStates())); | 
| Chris@117 | 150     connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), | 
| Chris@117 | 151 	    this, SLOT(inProgressSelectionChanged())); | 
| Chris@0 | 152 | 
| Chris@0 | 153     m_descriptionLabel = new QLabel; | 
| Chris@0 | 154 | 
| Chris@0 | 155     m_paneStack = new PaneStack(frame, m_viewManager); | 
| Chris@0 | 156     connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), | 
| Chris@0 | 157 	    this, SLOT(currentPaneChanged(Pane *))); | 
| Chris@0 | 158     connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), | 
| Chris@0 | 159 	    this, SLOT(currentLayerChanged(Pane *, Layer *))); | 
| Chris@0 | 160     connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), | 
| Chris@0 | 161             this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); | 
| Chris@73 | 162     connect(m_paneStack, SIGNAL(propertyStacksResized()), | 
| Chris@73 | 163             this, SLOT(propertyStacksResized())); | 
| Chris@90 | 164     connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@116 | 165             this, SLOT(contextHelpChanged(const QString &))); | 
| Chris@0 | 166 | 
| Chris@65 | 167     m_overview = new Overview(frame); | 
| Chris@65 | 168     m_overview->setViewManager(m_viewManager); | 
| Chris@65 | 169     m_overview->setFixedHeight(40); | 
| Chris@129 | 170     m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); | 
| Chris@90 | 171     connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@116 | 172             this, SLOT(contextHelpChanged(const QString &))); | 
| Chris@0 | 173 | 
| Chris@0 | 174     m_panLayer = new WaveformLayer; | 
| Chris@0 | 175     m_panLayer->setChannelMode(WaveformLayer::MergeChannels); | 
| Chris@0 | 176 //    m_panLayer->setScale(WaveformLayer::MeterScale); | 
| Chris@39 | 177 //    m_panLayer->setAutoNormalize(true); | 
| Chris@0 | 178     m_panLayer->setBaseColour(Qt::darkGreen); | 
| Chris@0 | 179     m_panLayer->setAggressiveCacheing(true); | 
| Chris@65 | 180     m_overview->addLayer(m_panLayer); | 
| Chris@0 | 181 | 
| Chris@0 | 182     m_playSource = new AudioCallbackPlaySource(m_viewManager); | 
| Chris@0 | 183 | 
| Chris@0 | 184     connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), | 
| Chris@0 | 185 	    this,           SLOT(sampleRateMismatch(size_t, size_t, bool))); | 
| Chris@42 | 186     connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), | 
| Chris@42 | 187             this,           SLOT(audioOverloadPluginDisabled())); | 
| Chris@0 | 188 | 
| Chris@0 | 189     m_fader = new Fader(frame, false); | 
| Chris@90 | 190     connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); | 
| Chris@90 | 191     connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); | 
| Chris@0 | 192 | 
| Chris@0 | 193     m_playSpeed = new AudioDial(frame); | 
| Chris@12 | 194     m_playSpeed->setMinimum(0); | 
| Chris@48 | 195     m_playSpeed->setMaximum(200); | 
| Chris@25 | 196     m_playSpeed->setValue(100); | 
| Chris@0 | 197     m_playSpeed->setFixedWidth(24); | 
| Chris@0 | 198     m_playSpeed->setFixedHeight(24); | 
| Chris@0 | 199     m_playSpeed->setNotchesVisible(true); | 
| Chris@25 | 200     m_playSpeed->setPageStep(10); | 
| Chris@60 | 201     m_playSpeed->setObjectName(tr("Playback Speedup")); | 
| Chris@25 | 202     m_playSpeed->setDefaultValue(100); | 
| Chris@59 | 203     m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200)); | 
| Chris@60 | 204     m_playSpeed->setShowToolTip(true); | 
| Chris@0 | 205     connect(m_playSpeed, SIGNAL(valueChanged(int)), | 
| Chris@0 | 206 	    this, SLOT(playSpeedChanged(int))); | 
| Chris@90 | 207     connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); | 
| Chris@90 | 208     connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); | 
| Chris@90 | 209 | 
| Chris@90 | 210     m_playSharpen = new NotifyingPushButton(frame); | 
| Chris@17 | 211     m_playSharpen->setToolTip(tr("Sharpen percussive transients")); | 
| Chris@26 | 212     m_playSharpen->setFixedSize(20, 20); | 
| Chris@26 | 213 //    m_playSharpen->setFlat(true); | 
| Chris@16 | 214     m_playSharpen->setEnabled(false); | 
| Chris@26 | 215     m_playSharpen->setCheckable(true); | 
| Chris@26 | 216     m_playSharpen->setChecked(false); | 
| Chris@26 | 217     m_playSharpen->setIcon(QIcon(":icons/sharpen.png")); | 
| Chris@26 | 218     connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled())); | 
| Chris@90 | 219     connect(m_playSharpen, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); | 
| Chris@90 | 220     connect(m_playSharpen, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); | 
| Chris@90 | 221 | 
| Chris@90 | 222     m_playMono = new NotifyingPushButton(frame); | 
| Chris@26 | 223     m_playMono->setToolTip(tr("Run time stretcher in mono only")); | 
| Chris@26 | 224     m_playMono->setFixedSize(20, 20); | 
| Chris@26 | 225 //    m_playMono->setFlat(true); | 
| Chris@26 | 226     m_playMono->setEnabled(false); | 
| Chris@26 | 227     m_playMono->setCheckable(true); | 
| Chris@26 | 228     m_playMono->setChecked(false); | 
| Chris@26 | 229     m_playMono->setIcon(QIcon(":icons/mono.png")); | 
| Chris@26 | 230     connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled())); | 
| Chris@90 | 231     connect(m_playMono, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); | 
| Chris@90 | 232     connect(m_playMono, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); | 
| Chris@21 | 233 | 
| Chris@21 | 234     QSettings settings; | 
| Chris@21 | 235     settings.beginGroup("MainWindow"); | 
| Chris@24 | 236     m_playSharpen->setChecked(settings.value("playsharpen", true).toBool()); | 
| Chris@26 | 237     m_playMono->setChecked(settings.value("playmono", false).toBool()); | 
| Chris@21 | 238     settings.endGroup(); | 
| Chris@21 | 239 | 
| Chris@129 | 240     layout->setSpacing(4); | 
| Chris@26 | 241     layout->addWidget(m_paneStack, 0, 0, 1, 5); | 
| Chris@65 | 242     layout->addWidget(m_overview, 1, 0); | 
| Chris@0 | 243     layout->addWidget(m_fader, 1, 1); | 
| Chris@0 | 244     layout->addWidget(m_playSpeed, 1, 2); | 
| Chris@16 | 245     layout->addWidget(m_playSharpen, 1, 3); | 
| Chris@26 | 246     layout->addWidget(m_playMono, 1, 4); | 
| Chris@16 | 247 | 
| Chris@129 | 248     m_paneStack->setPropertyStackMinWidth | 
| Chris@129 | 249         (m_fader->width() + m_playSpeed->width() + m_playSharpen->width() + | 
| Chris@129 | 250          m_playMono->width() + layout->spacing() * 4); | 
| Chris@129 | 251 | 
| Chris@16 | 252     layout->setColumnStretch(0, 10); | 
| Chris@16 | 253 | 
| Chris@0 | 254     frame->setLayout(layout); | 
| Chris@0 | 255 | 
| Chris@0 | 256     connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), | 
| Chris@0 | 257 	    this, SLOT(outputLevelsChanged(float, float))); | 
| Chris@0 | 258 | 
| Chris@116 | 259     connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), | 
| Chris@116 | 260             this, SLOT(playbackFrameChanged(unsigned long))); | 
| Chris@116 | 261 | 
| Chris@116 | 262     connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), | 
| Chris@116 | 263             this, SLOT(globalCentreFrameChanged(unsigned long))); | 
| Chris@116 | 264 | 
| Chris@116 | 265     connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), | 
| Chris@116 | 266             this, SLOT(viewCentreFrameChanged(View *, unsigned long))); | 
| Chris@116 | 267 | 
| Chris@116 | 268     connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), | 
| Chris@116 | 269             this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); | 
| Chris@116 | 270 | 
| Chris@0 | 271     connect(Preferences::getInstance(), | 
| Chris@0 | 272             SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@0 | 273             this, | 
| Chris@0 | 274             SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@0 | 275 | 
| Chris@98 | 276 //    preferenceChanged("Property Box Layout"); | 
| Chris@98 | 277 | 
| Chris@70 | 278     if (m_oscQueue && m_oscQueue->isOK()) { | 
| Chris@69 | 279         connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); | 
| Chris@69 | 280         QTimer *oscTimer = new QTimer(this); | 
| Chris@69 | 281         connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); | 
| Chris@69 | 282         oscTimer->start(1000); | 
| Chris@69 | 283     } | 
| Chris@69 | 284 | 
| Chris@0 | 285     setupMenus(); | 
| Chris@0 | 286     setupToolbars(); | 
| Chris@0 | 287 | 
| Chris@90 | 288     statusBar(); | 
| Chris@0 | 289 | 
| Chris@0 | 290     newSession(); | 
| Chris@0 | 291 } | 
| Chris@0 | 292 | 
| Chris@0 | 293 MainWindow::~MainWindow() | 
| Chris@0 | 294 { | 
| Chris@118 | 295     std::cerr << "MainWindow::~MainWindow()" << std::endl; | 
| Chris@118 | 296 | 
| Chris@70 | 297     if (!m_abandoning) { | 
| Chris@70 | 298         closeSession(); | 
| Chris@70 | 299     } | 
| Chris@0 | 300     delete m_playTarget; | 
| Chris@0 | 301     delete m_playSource; | 
| Chris@0 | 302     delete m_viewManager; | 
| Chris@69 | 303     delete m_oscQueue; | 
| Chris@0 | 304     Profiles::getInstance()->dump(); | 
| Chris@0 | 305 } | 
| Chris@0 | 306 | 
| Chris@81 | 307 QString | 
| Chris@88 | 308 MainWindow::getOpenFileName(FileFinder::FileType type) | 
| Chris@81 | 309 { | 
| Chris@88 | 310     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@81 | 311     switch (type) { | 
| Chris@88 | 312     case FileFinder::SessionFile: | 
| Chris@88 | 313         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@88 | 314     case FileFinder::AudioFile: | 
| Chris@88 | 315         return ff->getOpenFileName(type, m_audioFile); | 
| Chris@88 | 316     case FileFinder::LayerFile: | 
| Chris@88 | 317         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@88 | 318     case FileFinder::SessionOrAudioFile: | 
| Chris@88 | 319         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@121 | 320     case FileFinder::ImageFile: | 
| Chris@121 | 321         return ff->getOpenFileName(type, m_sessionFile); | 
| Chris@88 | 322     case FileFinder::AnyFile: | 
| Chris@88 | 323         if (getMainModel() != 0 && | 
| Chris@88 | 324             m_paneStack != 0 && | 
| Chris@88 | 325             m_paneStack->getCurrentPane() != 0) { // can import a layer | 
| Chris@88 | 326             return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); | 
| Chris@81 | 327         } else { | 
| Chris@88 | 328             return ff->getOpenFileName(FileFinder::SessionOrAudioFile, | 
| Chris@88 | 329                                        m_sessionFile); | 
| Chris@81 | 330         } | 
| Chris@81 | 331     } | 
| Chris@88 | 332     return ""; | 
| Chris@88 | 333 } | 
| Chris@88 | 334 | 
| Chris@88 | 335 QString | 
| Chris@88 | 336 MainWindow::getSaveFileName(FileFinder::FileType type) | 
| Chris@88 | 337 { | 
| Chris@88 | 338     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@88 | 339     switch (type) { | 
| Chris@88 | 340     case FileFinder::SessionFile: | 
| Chris@88 | 341         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@88 | 342     case FileFinder::AudioFile: | 
| Chris@88 | 343         return ff->getSaveFileName(type, m_audioFile); | 
| Chris@88 | 344     case FileFinder::LayerFile: | 
| Chris@88 | 345         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@88 | 346     case FileFinder::SessionOrAudioFile: | 
| Chris@88 | 347         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@121 | 348     case FileFinder::ImageFile: | 
| Chris@121 | 349         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@88 | 350     case FileFinder::AnyFile: | 
| Chris@88 | 351         return ff->getSaveFileName(type, m_sessionFile); | 
| Chris@81 | 352     } | 
| Chris@88 | 353     return ""; | 
| Chris@81 | 354 } | 
| Chris@81 | 355 | 
| Chris@88 | 356 void | 
| Chris@88 | 357 MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path) | 
| Chris@81 | 358 { | 
| Chris@88 | 359     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@88 | 360     ff->registerLastOpenedFilePath(type, path); | 
| Chris@81 | 361 } | 
| Chris@81 | 362 | 
| Chris@81 | 363 void | 
| Chris@0 | 364 MainWindow::setupMenus() | 
| Chris@0 | 365 { | 
| Chris@0 | 366     if (!m_mainMenusCreated) { | 
| Chris@0 | 367         m_rightButtonMenu = new QMenu(); | 
| Chris@104 | 368 | 
| Chris@104 | 369         // No -- we don't want tear-off enabled on the right-button | 
| Chris@104 | 370         // menu.  If it is enabled, then simply right-clicking and | 
| Chris@104 | 371         // releasing will pop up the menu, activate the tear-off, and | 
| Chris@104 | 372         // leave the torn-off menu window in front of the main window. | 
| Chris@104 | 373         // That isn't desirable.  I'm not sure it ever would be, in a | 
| Chris@104 | 374         // context menu -- perhaps technically a Qt bug? | 
| Chris@104 | 375 //        m_rightButtonMenu->setTearOffEnabled(true); | 
| Chris@0 | 376     } | 
| Chris@0 | 377 | 
| Chris@0 | 378     if (m_rightButtonLayerMenu) { | 
| Chris@0 | 379         m_rightButtonLayerMenu->clear(); | 
| Chris@0 | 380     } else { | 
| Chris@0 | 381         m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer")); | 
| Chris@97 | 382         m_rightButtonLayerMenu->setTearOffEnabled(true); | 
| Chris@0 | 383         m_rightButtonMenu->addSeparator(); | 
| Chris@0 | 384     } | 
| Chris@0 | 385 | 
| Chris@34 | 386     if (m_rightButtonTransformsMenu) { | 
| Chris@34 | 387         m_rightButtonTransformsMenu->clear(); | 
| Chris@34 | 388     } else { | 
| Chris@34 | 389         m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform")); | 
| Chris@97 | 390         m_rightButtonTransformsMenu->setTearOffEnabled(true); | 
| Chris@34 | 391         m_rightButtonMenu->addSeparator(); | 
| Chris@34 | 392     } | 
| Chris@34 | 393 | 
| Chris@0 | 394     if (!m_mainMenusCreated) { | 
| Chris@0 | 395         CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); | 
| Chris@0 | 396         m_rightButtonMenu->addSeparator(); | 
| Chris@66 | 397     } | 
| Chris@66 | 398 | 
| Chris@66 | 399     setupFileMenu(); | 
| Chris@66 | 400     setupEditMenu(); | 
| Chris@66 | 401     setupViewMenu(); | 
| Chris@66 | 402     setupPaneAndLayerMenus(); | 
| Chris@66 | 403     setupTransformsMenu(); | 
| Chris@66 | 404     setupHelpMenu(); | 
| Chris@66 | 405 | 
| Chris@66 | 406     m_mainMenusCreated = true; | 
| Chris@66 | 407 } | 
| Chris@66 | 408 | 
| Chris@66 | 409 void | 
| Chris@66 | 410 MainWindow::setupFileMenu() | 
| Chris@66 | 411 { | 
| Chris@66 | 412     if (m_mainMenusCreated) return; | 
| Chris@66 | 413 | 
| Chris@66 | 414     QMenu *menu = menuBar()->addMenu(tr("&File")); | 
| Chris@97 | 415     menu->setTearOffEnabled(true); | 
| Chris@66 | 416     QToolBar *toolbar = addToolBar(tr("File Toolbar")); | 
| Chris@66 | 417 | 
| Chris@66 | 418     QIcon icon(":icons/filenew.png"); | 
| Chris@66 | 419     icon.addFile(":icons/filenew-22.png"); | 
| Chris@66 | 420     QAction *action = new QAction(icon, tr("&New Session"), this); | 
| Chris@66 | 421     action->setShortcut(tr("Ctrl+N")); | 
| Chris@90 | 422     action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one")); | 
| Chris@66 | 423     connect(action, SIGNAL(triggered()), this, SLOT(newSession())); | 
| Chris@66 | 424     menu->addAction(action); | 
| Chris@66 | 425     toolbar->addAction(action); | 
| Chris@0 | 426 | 
| Chris@66 | 427     icon = QIcon(":icons/fileopen.png"); | 
| Chris@66 | 428     icon.addFile(":icons/fileopen-22.png"); | 
| Chris@66 | 429 | 
| Chris@66 | 430     action = new QAction(icon, tr("&Open Session..."), this); | 
| Chris@66 | 431     action->setShortcut(tr("Ctrl+O")); | 
| Chris@66 | 432     action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file")); | 
| Chris@66 | 433     connect(action, SIGNAL(triggered()), this, SLOT(openSession())); | 
| Chris@66 | 434     menu->addAction(action); | 
| Chris@66 | 435 | 
| Chris@66 | 436     action = new QAction(icon, tr("&Open..."), this); | 
| Chris@66 | 437     action->setStatusTip(tr("Open a session file, audio file, or layer")); | 
| Chris@66 | 438     connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); | 
| Chris@66 | 439     toolbar->addAction(action); | 
| Chris@66 | 440 | 
| Chris@66 | 441     icon = QIcon(":icons/filesave.png"); | 
| Chris@66 | 442     icon.addFile(":icons/filesave-22.png"); | 
| Chris@66 | 443     action = new QAction(icon, tr("&Save Session"), this); | 
| Chris@66 | 444     action->setShortcut(tr("Ctrl+S")); | 
| Chris@66 | 445     action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file")); | 
| Chris@66 | 446     connect(action, SIGNAL(triggered()), this, SLOT(saveSession())); | 
| Chris@66 | 447     connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 448     menu->addAction(action); | 
| Chris@66 | 449     toolbar->addAction(action); | 
| Chris@0 | 450 | 
| Chris@66 | 451     icon = QIcon(":icons/filesaveas.png"); | 
| Chris@66 | 452     icon.addFile(":icons/filesaveas-22.png"); | 
| Chris@66 | 453     action = new QAction(icon, tr("Save Session &As..."), this); | 
| Chris@66 | 454     action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file")); | 
| Chris@66 | 455     connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs())); | 
| Chris@66 | 456     menu->addAction(action); | 
| Chris@66 | 457     toolbar->addAction(action); | 
| Chris@66 | 458 | 
| Chris@66 | 459     menu->addSeparator(); | 
| Chris@66 | 460 | 
| Chris@66 | 461     action = new QAction(tr("&Import Audio File..."), this); | 
| Chris@66 | 462     action->setShortcut(tr("Ctrl+I")); | 
| Chris@66 | 463     action->setStatusTip(tr("Import an existing audio file")); | 
| Chris@66 | 464     connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); | 
| Chris@66 | 465     menu->addAction(action); | 
| Chris@66 | 466 | 
| Chris@66 | 467     action = new QAction(tr("Import Secondary Audio File..."), this); | 
| Chris@66 | 468     action->setShortcut(tr("Ctrl+Shift+I")); | 
| Chris@66 | 469     action->setStatusTip(tr("Import an extra audio file as a separate layer")); | 
| Chris@66 | 470     connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio())); | 
| Chris@66 | 471     connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 472     menu->addAction(action); | 
| Chris@66 | 473 | 
| Chris@66 | 474     action = new QAction(tr("&Export Audio File..."), this); | 
| Chris@66 | 475     action->setStatusTip(tr("Export selection as an audio file")); | 
| Chris@66 | 476     connect(action, SIGNAL(triggered()), this, SLOT(exportAudio())); | 
| Chris@66 | 477     connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 478     menu->addAction(action); | 
| Chris@66 | 479 | 
| Chris@66 | 480     menu->addSeparator(); | 
| Chris@66 | 481 | 
| Chris@66 | 482     action = new QAction(tr("Import Annotation &Layer..."), this); | 
| Chris@66 | 483     action->setShortcut(tr("Ctrl+L")); | 
| Chris@66 | 484     action->setStatusTip(tr("Import layer data from an existing file")); | 
| Chris@66 | 485     connect(action, SIGNAL(triggered()), this, SLOT(importLayer())); | 
| Chris@66 | 486     connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 487     menu->addAction(action); | 
| Chris@66 | 488 | 
| Chris@66 | 489     action = new QAction(tr("Export Annotation Layer..."), this); | 
| Chris@66 | 490     action->setStatusTip(tr("Export layer data to a file")); | 
| Chris@66 | 491     connect(action, SIGNAL(triggered()), this, SLOT(exportLayer())); | 
| Chris@66 | 492     connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 493     menu->addAction(action); | 
| Chris@66 | 494 | 
| Chris@66 | 495     menu->addSeparator(); | 
| Chris@86 | 496 | 
| Chris@121 | 497     action = new QAction(tr("Export Image File..."), this); | 
| Chris@121 | 498     action->setStatusTip(tr("Export a single pane to an image file")); | 
| Chris@121 | 499     connect(action, SIGNAL(triggered()), this, SLOT(exportImage())); | 
| Chris@121 | 500     connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@121 | 501     menu->addAction(action); | 
| Chris@121 | 502 | 
| Chris@121 | 503     menu->addSeparator(); | 
| Chris@121 | 504 | 
| Chris@86 | 505     action = new QAction(tr("Open Lo&cation..."), this); | 
| Chris@86 | 506     action->setShortcut(tr("Ctrl+Shift+O")); | 
| Chris@86 | 507     action->setStatusTip(tr("Open or import a file from a remote URL")); | 
| Chris@86 | 508     connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); | 
| Chris@86 | 509     menu->addAction(action); | 
| Chris@86 | 510 | 
| Chris@86 | 511     menu->addSeparator(); | 
| Chris@86 | 512 | 
| Chris@66 | 513     m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); | 
| Chris@97 | 514     m_recentFilesMenu->setTearOffEnabled(true); | 
| Chris@66 | 515     setupRecentFilesMenu(); | 
| Chris@66 | 516     connect(&m_recentFiles, SIGNAL(recentChanged()), | 
| Chris@66 | 517             this, SLOT(setupRecentFilesMenu())); | 
| Chris@66 | 518 | 
| Chris@66 | 519     menu->addSeparator(); | 
| Chris@66 | 520     action = new QAction(tr("&Preferences..."), this); | 
| Chris@66 | 521     action->setStatusTip(tr("Adjust the application preferences")); | 
| Chris@66 | 522     connect(action, SIGNAL(triggered()), this, SLOT(preferences())); | 
| Chris@66 | 523     menu->addAction(action); | 
| Chris@0 | 524 | 
| Chris@66 | 525     /*!!! | 
| Chris@66 | 526       menu->addSeparator(); | 
| Chris@0 | 527 | 
| Chris@66 | 528       action = new QAction(tr("Play / Pause"), this); | 
| Chris@66 | 529       action->setShortcut(tr("Space")); | 
| Chris@66 | 530       action->setStatusTip(tr("Start or stop playback from the current position")); | 
| Chris@66 | 531       connect(action, SIGNAL(triggered()), this, SLOT(play())); | 
| Chris@66 | 532       menu->addAction(action); | 
| Chris@66 | 533     */ | 
| Chris@66 | 534 | 
| Chris@66 | 535     menu->addSeparator(); | 
| Chris@66 | 536     action = new QAction(QIcon(":/icons/exit.png"), | 
| Chris@66 | 537                          tr("&Quit"), this); | 
| Chris@66 | 538     action->setShortcut(tr("Ctrl+Q")); | 
| Chris@90 | 539     action->setStatusTip(tr("Exit Sonic Visualiser")); | 
| Chris@66 | 540     connect(action, SIGNAL(triggered()), this, SLOT(close())); | 
| Chris@66 | 541     menu->addAction(action); | 
| Chris@66 | 542 } | 
| Chris@66 | 543 | 
| Chris@66 | 544 void | 
| Chris@66 | 545 MainWindow::setupEditMenu() | 
| Chris@66 | 546 { | 
| Chris@66 | 547     if (m_mainMenusCreated) return; | 
| Chris@66 | 548 | 
| Chris@66 | 549     QMenu *menu = menuBar()->addMenu(tr("&Edit")); | 
| Chris@97 | 550     menu->setTearOffEnabled(true); | 
| Chris@66 | 551     CommandHistory::getInstance()->registerMenu(menu); | 
| Chris@66 | 552 | 
| Chris@66 | 553     menu->addSeparator(); | 
| Chris@66 | 554 | 
| Chris@66 | 555     QAction *action = new QAction(QIcon(":/icons/editcut.png"), | 
| Chris@66 | 556                                   tr("Cu&t"), this); | 
| Chris@66 | 557     action->setShortcut(tr("Ctrl+X")); | 
| Chris@90 | 558     action->setStatusTip(tr("Cut the selection from the current layer to the clipboard")); | 
| Chris@66 | 559     connect(action, SIGNAL(triggered()), this, SLOT(cut())); | 
| Chris@66 | 560     connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 561     menu->addAction(action); | 
| Chris@66 | 562     m_rightButtonMenu->addAction(action); | 
| Chris@66 | 563 | 
| Chris@66 | 564     action = new QAction(QIcon(":/icons/editcopy.png"), | 
| Chris@66 | 565                          tr("&Copy"), this); | 
| Chris@66 | 566     action->setShortcut(tr("Ctrl+C")); | 
| Chris@90 | 567     action->setStatusTip(tr("Copy the selection from the current layer to the clipboard")); | 
| Chris@66 | 568     connect(action, SIGNAL(triggered()), this, SLOT(copy())); | 
| Chris@66 | 569     connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 570     menu->addAction(action); | 
| Chris@66 | 571     m_rightButtonMenu->addAction(action); | 
| Chris@66 | 572 | 
| Chris@66 | 573     action = new QAction(QIcon(":/icons/editpaste.png"), | 
| Chris@66 | 574                          tr("&Paste"), this); | 
| Chris@66 | 575     action->setShortcut(tr("Ctrl+V")); | 
| Chris@90 | 576     action->setStatusTip(tr("Paste from the clipboard to the current layer")); | 
| Chris@66 | 577     connect(action, SIGNAL(triggered()), this, SLOT(paste())); | 
| Chris@66 | 578     connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 579     menu->addAction(action); | 
| Chris@66 | 580     m_rightButtonMenu->addAction(action); | 
| Chris@66 | 581 | 
| Chris@66 | 582     action = new QAction(tr("&Delete Selected Items"), this); | 
| Chris@66 | 583     action->setShortcut(tr("Del")); | 
| Chris@90 | 584     action->setStatusTip(tr("Delete the selection from the current layer")); | 
| Chris@66 | 585     connect(action, SIGNAL(triggered()), this, SLOT(deleteSelected())); | 
| Chris@66 | 586     connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 587     menu->addAction(action); | 
| Chris@66 | 588     m_rightButtonMenu->addAction(action); | 
| Chris@66 | 589 | 
| Chris@66 | 590     menu->addSeparator(); | 
| Chris@66 | 591     m_rightButtonMenu->addSeparator(); | 
| Chris@0 | 592 | 
| Chris@66 | 593     action = new QAction(tr("Select &All"), this); | 
| Chris@66 | 594     action->setShortcut(tr("Ctrl+A")); | 
| Chris@90 | 595     action->setStatusTip(tr("Select the whole duration of the current session")); | 
| Chris@66 | 596     connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); | 
| Chris@66 | 597     connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 598     menu->addAction(action); | 
| Chris@66 | 599     m_rightButtonMenu->addAction(action); | 
| Chris@0 | 600 | 
| Chris@66 | 601     action = new QAction(tr("Select &Visible Range"), this); | 
| Chris@66 | 602     action->setShortcut(tr("Ctrl+Shift+A")); | 
| Chris@90 | 603     action->setStatusTip(tr("Select the time range corresponding to the current window width")); | 
| Chris@66 | 604     connect(action, SIGNAL(triggered()), this, SLOT(selectVisible())); | 
| Chris@66 | 605     connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 606     menu->addAction(action); | 
| Chris@0 | 607 | 
| Chris@66 | 608     action = new QAction(tr("Select to &Start"), this); | 
| Chris@66 | 609     action->setShortcut(tr("Shift+Left")); | 
| Chris@90 | 610     action->setStatusTip(tr("Select from the start of the session to the current playback position")); | 
| Chris@66 | 611     connect(action, SIGNAL(triggered()), this, SLOT(selectToStart())); | 
| Chris@66 | 612     connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 613     menu->addAction(action); | 
| Chris@0 | 614 | 
| Chris@66 | 615     action = new QAction(tr("Select to &End"), this); | 
| Chris@66 | 616     action->setShortcut(tr("Shift+Right")); | 
| Chris@90 | 617     action->setStatusTip(tr("Select from the current playback position to the end of the session")); | 
| Chris@66 | 618     connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd())); | 
| Chris@66 | 619     connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 620     menu->addAction(action); | 
| Chris@66 | 621 | 
| Chris@66 | 622     action = new QAction(tr("C&lear Selection"), this); | 
| Chris@66 | 623     action->setShortcut(tr("Esc")); | 
| Chris@90 | 624     action->setStatusTip(tr("Clear the selection")); | 
| Chris@66 | 625     connect(action, SIGNAL(triggered()), this, SLOT(clearSelection())); | 
| Chris@66 | 626     connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 627     menu->addAction(action); | 
| Chris@66 | 628     m_rightButtonMenu->addAction(action); | 
| Chris@66 | 629 | 
| Chris@66 | 630     menu->addSeparator(); | 
| Chris@66 | 631 | 
| Chris@66 | 632     action = new QAction(tr("&Insert Instant at Playback Position"), this); | 
| Chris@66 | 633     action->setShortcut(tr("Enter")); | 
| Chris@90 | 634     action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary")); | 
| Chris@66 | 635     connect(action, SIGNAL(triggered()), this, SLOT(insertInstant())); | 
| Chris@66 | 636     connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 637     menu->addAction(action); | 
| Chris@66 | 638 | 
| Chris@81 | 639     action = new QAction(tr("Insert Instants at Selection &Boundaries"), this); | 
| Chris@81 | 640     action->setShortcut(tr("Shift+Enter")); | 
| Chris@90 | 641     action->setStatusTip(tr("Insert new time instants at the start and end of the current selection, in a new layer if necessary")); | 
| Chris@81 | 642     connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries())); | 
| Chris@81 | 643     connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@81 | 644     menu->addAction(action); | 
| Chris@81 | 645 | 
| Chris@66 | 646     // Laptop shortcut (no keypad Enter key) | 
| Chris@66 | 647     connect(new QShortcut(tr(";"), this), SIGNAL(activated()), | 
| Chris@66 | 648             this, SLOT(insertInstant())); | 
| Chris@66 | 649 } | 
| Chris@66 | 650 | 
| Chris@66 | 651 void | 
| Chris@66 | 652 MainWindow::setupViewMenu() | 
| Chris@66 | 653 { | 
| Chris@66 | 654     if (m_mainMenusCreated) return; | 
| Chris@66 | 655 | 
| Chris@90 | 656     QAction *action = 0; | 
| Chris@90 | 657 | 
| Chris@66 | 658     QMenu *menu = menuBar()->addMenu(tr("&View")); | 
| Chris@97 | 659     menu->setTearOffEnabled(true); | 
| Chris@66 | 660     action = new QAction(tr("Scroll &Left"), this); | 
| Chris@66 | 661     action->setShortcut(tr("Left")); | 
| Chris@66 | 662     action->setStatusTip(tr("Scroll the current pane to the left")); | 
| Chris@66 | 663     connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft())); | 
| Chris@66 | 664     connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 665     menu->addAction(action); | 
| Chris@0 | 666 | 
| Chris@66 | 667     action = new QAction(tr("Scroll &Right"), this); | 
| Chris@66 | 668     action->setShortcut(tr("Right")); | 
| Chris@66 | 669     action->setStatusTip(tr("Scroll the current pane to the right")); | 
| Chris@66 | 670     connect(action, SIGNAL(triggered()), this, SLOT(scrollRight())); | 
| Chris@66 | 671     connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 672     menu->addAction(action); | 
| Chris@0 | 673 | 
| Chris@90 | 674     action = new QAction(tr("&Jump Left"), this); | 
| Chris@66 | 675     action->setShortcut(tr("Ctrl+Left")); | 
| Chris@66 | 676     action->setStatusTip(tr("Scroll the current pane a big step to the left")); | 
| Chris@66 | 677     connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft())); | 
| Chris@66 | 678     connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 679     menu->addAction(action); | 
| Chris@0 | 680 | 
| Chris@90 | 681     action = new QAction(tr("J&ump Right"), this); | 
| Chris@66 | 682     action->setShortcut(tr("Ctrl+Right")); | 
| Chris@66 | 683     action->setStatusTip(tr("Scroll the current pane a big step to the right")); | 
| Chris@66 | 684     connect(action, SIGNAL(triggered()), this, SLOT(jumpRight())); | 
| Chris@66 | 685     connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 686     menu->addAction(action); | 
| Chris@66 | 687 | 
| Chris@66 | 688     menu->addSeparator(); | 
| Chris@66 | 689 | 
| Chris@66 | 690     action = new QAction(QIcon(":/icons/zoom-in.png"), | 
| Chris@66 | 691                          tr("Zoom &In"), this); | 
| Chris@66 | 692     action->setShortcut(tr("Up")); | 
| Chris@66 | 693     action->setStatusTip(tr("Increase the zoom level")); | 
| Chris@66 | 694     connect(action, SIGNAL(triggered()), this, SLOT(zoomIn())); | 
| Chris@66 | 695     connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 696     menu->addAction(action); | 
| Chris@0 | 697 | 
| Chris@66 | 698     action = new QAction(QIcon(":/icons/zoom-out.png"), | 
| Chris@66 | 699                          tr("Zoom &Out"), this); | 
| Chris@66 | 700     action->setShortcut(tr("Down")); | 
| Chris@66 | 701     action->setStatusTip(tr("Decrease the zoom level")); | 
| Chris@66 | 702     connect(action, SIGNAL(triggered()), this, SLOT(zoomOut())); | 
| Chris@66 | 703     connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 704     menu->addAction(action); | 
| Chris@0 | 705 | 
| Chris@66 | 706     action = new QAction(tr("Restore &Default Zoom"), this); | 
| Chris@90 | 707     action->setStatusTip(tr("Restore the zoom level to the default")); | 
| Chris@66 | 708     connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault())); | 
| Chris@66 | 709     connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 710     menu->addAction(action); | 
| Chris@66 | 711 | 
| Chris@66 | 712     action = new QAction(tr("Zoom to &Fit"), this); | 
| Chris@66 | 713     action->setStatusTip(tr("Zoom to show the whole file")); | 
| Chris@66 | 714     connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit())); | 
| Chris@66 | 715     connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 716     menu->addAction(action); | 
| Chris@90 | 717 | 
| Chris@90 | 718     menu->addSeparator(); | 
| Chris@90 | 719 | 
| Chris@90 | 720     QActionGroup *overlayGroup = new QActionGroup(this); | 
| Chris@90 | 721 | 
| Chris@90 | 722     action = new QAction(tr("Show &No Overlays"), this); | 
| Chris@90 | 723     action->setShortcut(tr("0")); | 
| Chris@90 | 724     action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale")); | 
| Chris@90 | 725     connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays())); | 
| Chris@90 | 726     action->setCheckable(true); | 
| Chris@90 | 727     action->setChecked(false); | 
| Chris@90 | 728     overlayGroup->addAction(action); | 
| Chris@90 | 729     menu->addAction(action); | 
| Chris@90 | 730 | 
| Chris@90 | 731     action = new QAction(tr("Show &Minimal Overlays"), this); | 
| Chris@90 | 732     action->setShortcut(tr("9")); | 
| Chris@90 | 733     action->setStatusTip(tr("Show centre indicator only")); | 
| Chris@90 | 734     connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays())); | 
| Chris@90 | 735     action->setCheckable(true); | 
| Chris@90 | 736     action->setChecked(false); | 
| Chris@90 | 737     overlayGroup->addAction(action); | 
| Chris@90 | 738     menu->addAction(action); | 
| Chris@90 | 739 | 
| Chris@90 | 740     action = new QAction(tr("Show &Standard Overlays"), this); | 
| Chris@90 | 741     action->setShortcut(tr("8")); | 
| Chris@90 | 742     action->setStatusTip(tr("Show centre indicator, frame times and scale")); | 
| Chris@90 | 743     connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays())); | 
| Chris@90 | 744     action->setCheckable(true); | 
| Chris@90 | 745     action->setChecked(true); | 
| Chris@90 | 746     overlayGroup->addAction(action); | 
| Chris@90 | 747     menu->addAction(action); | 
| Chris@90 | 748 | 
| Chris@90 | 749     action = new QAction(tr("Show &All Overlays"), this); | 
| Chris@90 | 750     action->setShortcut(tr("7")); | 
| Chris@90 | 751     action->setStatusTip(tr("Show all texts and scale")); | 
| Chris@90 | 752     connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays())); | 
| Chris@90 | 753     action->setCheckable(true); | 
| Chris@90 | 754     action->setChecked(false); | 
| Chris@90 | 755     overlayGroup->addAction(action); | 
| Chris@90 | 756     menu->addAction(action); | 
| Chris@7 | 757 | 
| Chris@72 | 758     menu->addSeparator(); | 
| Chris@72 | 759 | 
| Chris@66 | 760     action = new QAction(tr("Show &Zoom Wheels"), this); | 
| Chris@66 | 761     action->setShortcut(tr("Z")); | 
| Chris@66 | 762     action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically")); | 
| Chris@66 | 763     connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels())); | 
| Chris@66 | 764     action->setCheckable(true); | 
| Chris@66 | 765     action->setChecked(m_viewManager->getZoomWheelsEnabled()); | 
| Chris@66 | 766     menu->addAction(action); | 
| Chris@72 | 767 | 
| Chris@72 | 768     action = new QAction(tr("Show Property Bo&xes"), this); | 
| Chris@72 | 769     action->setShortcut(tr("X")); | 
| Chris@72 | 770     action->setStatusTip(tr("Show the layer property boxes at the side of the main window")); | 
| Chris@72 | 771     connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes())); | 
| Chris@72 | 772     action->setCheckable(true); | 
| Chris@72 | 773     action->setChecked(true); | 
| Chris@72 | 774     menu->addAction(action); | 
| Chris@0 | 775 | 
| Chris@90 | 776     action = new QAction(tr("Show Status &Bar"), this); | 
| Chris@90 | 777     action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window")); | 
| Chris@90 | 778     connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar())); | 
| Chris@90 | 779     action->setCheckable(true); | 
| Chris@90 | 780     action->setChecked(true); | 
| Chris@90 | 781     menu->addAction(action); | 
| Chris@90 | 782 | 
| Chris@90 | 783     QSettings settings; | 
| Chris@90 | 784     settings.beginGroup("MainWindow"); | 
| Chris@90 | 785     bool sb = settings.value("showstatusbar", true).toBool(); | 
| Chris@90 | 786     if (!sb) { | 
| Chris@90 | 787         action->setChecked(false); | 
| Chris@90 | 788         statusBar()->hide(); | 
| Chris@90 | 789     } | 
| Chris@90 | 790     settings.endGroup(); | 
| Chris@90 | 791 | 
| Chris@0 | 792 /*!!! This one doesn't work properly yet | 
| Chris@90 | 793 | 
| Chris@66 | 794     menu->addSeparator(); | 
| Chris@66 | 795 | 
| Chris@90 | 796     action = new QAction(tr("Show La&yer Hierarchy"), this); | 
| Chris@66 | 797     action->setShortcut(tr("Alt+L")); | 
| Chris@90 | 798     action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session")); | 
| Chris@66 | 799     connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); | 
| Chris@66 | 800     menu->addAction(action); | 
| Chris@90 | 801  */ | 
| Chris@66 | 802 } | 
| Chris@66 | 803 | 
| Chris@66 | 804 void | 
| Chris@66 | 805 MainWindow::setupPaneAndLayerMenus() | 
| Chris@66 | 806 { | 
| Chris@0 | 807     if (m_paneMenu) { | 
| Chris@0 | 808 	m_paneActions.clear(); | 
| Chris@0 | 809 	m_paneMenu->clear(); | 
| Chris@0 | 810     } else { | 
| Chris@0 | 811 	m_paneMenu = menuBar()->addMenu(tr("&Pane")); | 
| Chris@97 | 812         m_paneMenu->setTearOffEnabled(true); | 
| Chris@0 | 813     } | 
| Chris@0 | 814 | 
| Chris@0 | 815     if (m_layerMenu) { | 
| Chris@0 | 816 	m_layerActions.clear(); | 
| Chris@0 | 817 	m_layerMenu->clear(); | 
| Chris@0 | 818     } else { | 
| Chris@0 | 819 	m_layerMenu = menuBar()->addMenu(tr("&Layer")); | 
| Chris@97 | 820         m_layerMenu->setTearOffEnabled(true); | 
| Chris@0 | 821     } | 
| Chris@0 | 822 | 
| Chris@66 | 823     QMenu *menu = m_paneMenu; | 
| Chris@66 | 824 | 
| Chris@66 | 825     QAction *action = new QAction(QIcon(":/icons/pane.png"), tr("Add &New Pane"), this); | 
| Chris@66 | 826     action->setShortcut(tr("Alt+N")); | 
| Chris@66 | 827     action->setStatusTip(tr("Add a new pane containing only a time ruler")); | 
| Chris@66 | 828     connect(action, SIGNAL(triggered()), this, SLOT(addPane())); | 
| Chris@66 | 829     connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 830     m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler); | 
| Chris@66 | 831     menu->addAction(action); | 
| Chris@66 | 832 | 
| Chris@66 | 833     menu->addSeparator(); | 
| Chris@66 | 834 | 
| Chris@66 | 835     menu = m_layerMenu; | 
| Chris@66 | 836 | 
| Chris@66 | 837 //    menu->addSeparator(); | 
| Chris@66 | 838 | 
| Chris@66 | 839     LayerFactory::LayerTypeSet emptyLayerTypes = | 
| Chris@66 | 840 	LayerFactory::getInstance()->getValidEmptyLayerTypes(); | 
| Chris@66 | 841 | 
| Chris@66 | 842     for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin(); | 
| Chris@66 | 843 	 i != emptyLayerTypes.end(); ++i) { | 
| Chris@66 | 844 | 
| Chris@66 | 845 	QIcon icon; | 
| Chris@66 | 846 	QString mainText, tipText, channelText; | 
| Chris@66 | 847 	LayerFactory::LayerType type = *i; | 
| Chris@66 | 848 	QString name = LayerFactory::getInstance()->getLayerPresentationName(type); | 
| Chris@66 | 849 | 
| Chris@66 | 850 	icon = QIcon(QString(":/icons/%1.png") | 
| Chris@66 | 851 		     .arg(LayerFactory::getInstance()->getLayerIconName(type))); | 
| Chris@66 | 852 | 
| Chris@66 | 853 	mainText = tr("Add New %1 Layer").arg(name); | 
| Chris@66 | 854 	tipText = tr("Add a new empty layer of type %1").arg(name); | 
| Chris@66 | 855 | 
| Chris@66 | 856 	action = new QAction(icon, mainText, this); | 
| Chris@66 | 857 	action->setStatusTip(tipText); | 
| Chris@66 | 858 | 
| Chris@66 | 859 	if (type == LayerFactory::Text) { | 
| Chris@66 | 860 	    action->setShortcut(tr("Alt+T")); | 
| Chris@66 | 861 	} | 
| Chris@66 | 862 | 
| Chris@66 | 863 	connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@66 | 864 	connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 865 	m_layerActions[action] = type; | 
| Chris@66 | 866 	menu->addAction(action); | 
| Chris@66 | 867         m_rightButtonLayerMenu->addAction(action); | 
| Chris@66 | 868     } | 
| Chris@66 | 869 | 
| Chris@66 | 870     m_rightButtonLayerMenu->addSeparator(); | 
| Chris@66 | 871     menu->addSeparator(); | 
| Chris@66 | 872 | 
| Chris@66 | 873     LayerFactory::LayerType backgroundTypes[] = { | 
| Chris@66 | 874 	LayerFactory::Waveform, | 
| Chris@66 | 875 	LayerFactory::Spectrogram, | 
| Chris@66 | 876 	LayerFactory::MelodicRangeSpectrogram, | 
| Chris@66 | 877 	LayerFactory::PeakFrequencySpectrogram, | 
| Chris@66 | 878         LayerFactory::Spectrum | 
| Chris@66 | 879     }; | 
| Chris@66 | 880 | 
| Chris@66 | 881     std::vector<Model *> models; | 
| Chris@66 | 882     if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this! | 
| Chris@66 | 883     bool plural = (models.size() > 1); | 
| Chris@66 | 884     if (models.empty()) { | 
| Chris@67 | 885         models.push_back(getMainModel()); // probably 0 | 
| Chris@66 | 886     } | 
| Chris@66 | 887 | 
| Chris@66 | 888     for (unsigned int i = 0; | 
| Chris@66 | 889 	 i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) { | 
| Chris@66 | 890 | 
| Chris@66 | 891 	for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer | 
| Chris@66 | 892 | 
| Chris@66 | 893 	    if (menuType == 0) menu = m_paneMenu; | 
| Chris@66 | 894 	    else menu = m_layerMenu; | 
| Chris@66 | 895 | 
| Chris@66 | 896 	    QMenu *submenu = 0; | 
| Chris@66 | 897 | 
| Chris@66 | 898             QIcon icon; | 
| Chris@66 | 899             QString mainText, shortcutText, tipText, channelText; | 
| Chris@66 | 900             LayerFactory::LayerType type = backgroundTypes[i]; | 
| Chris@66 | 901             bool mono = true; | 
| Chris@66 | 902 | 
| Chris@66 | 903             switch (type) { | 
| Chris@66 | 904 | 
| Chris@66 | 905             case LayerFactory::Waveform: | 
| Chris@66 | 906                 icon = QIcon(":/icons/waveform.png"); | 
| Chris@66 | 907                 mainText = tr("Add &Waveform"); | 
| Chris@66 | 908                 if (menuType == 0) { | 
| Chris@66 | 909                     shortcutText = tr("Alt+W"); | 
| Chris@66 | 910                     tipText = tr("Add a new pane showing a waveform view"); | 
| Chris@66 | 911                 } else { | 
| Chris@66 | 912                     tipText = tr("Add a new layer showing a waveform view"); | 
| Chris@66 | 913                 } | 
| Chris@66 | 914                 mono = false; | 
| Chris@66 | 915                 break; | 
| Chris@66 | 916 | 
| Chris@66 | 917             case LayerFactory::Spectrogram: | 
| Chris@67 | 918                 icon = QIcon(":/icons/spectrogram.png"); | 
| Chris@66 | 919                 mainText = tr("Add &Spectrogram"); | 
| Chris@66 | 920                 if (menuType == 0) { | 
| Chris@66 | 921                     shortcutText = tr("Alt+S"); | 
| Chris@90 | 922                     tipText = tr("Add a new pane showing a spectrogram"); | 
| Chris@66 | 923                 } else { | 
| Chris@90 | 924                     tipText = tr("Add a new layer showing a spectrogram"); | 
| Chris@66 | 925                 } | 
| Chris@66 | 926                 break; | 
| Chris@66 | 927 | 
| Chris@66 | 928             case LayerFactory::MelodicRangeSpectrogram: | 
| Chris@67 | 929                 icon = QIcon(":/icons/spectrogram.png"); | 
| Chris@66 | 930                 mainText = tr("Add &Melodic Range Spectrogram"); | 
| Chris@66 | 931                 if (menuType == 0) { | 
| Chris@66 | 932                     shortcutText = tr("Alt+M"); | 
| Chris@90 | 933                     tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches"); | 
| Chris@66 | 934                 } else { | 
| Chris@90 | 935                     tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches"); | 
| Chris@66 | 936                 } | 
| Chris@66 | 937                 break; | 
| Chris@66 | 938 | 
| Chris@66 | 939             case LayerFactory::PeakFrequencySpectrogram: | 
| Chris@67 | 940                 icon = QIcon(":/icons/spectrogram.png"); | 
| Chris@66 | 941                 mainText = tr("Add &Peak Frequency Spectrogram"); | 
| Chris@66 | 942                 if (menuType == 0) { | 
| Chris@66 | 943                     shortcutText = tr("Alt+P"); | 
| Chris@66 | 944                     tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies"); | 
| Chris@66 | 945                 } else { | 
| Chris@66 | 946                     tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies"); | 
| Chris@66 | 947                 } | 
| Chris@66 | 948                 break; | 
| Chris@66 | 949 | 
| Chris@66 | 950             case LayerFactory::Spectrum: | 
| Chris@67 | 951                 icon = QIcon(":/icons/spectrum.png"); | 
| Chris@66 | 952                 mainText = tr("Add Spectr&um"); | 
| Chris@66 | 953                 if (menuType == 0) { | 
| Chris@66 | 954                     shortcutText = tr("Alt+U"); | 
| Chris@66 | 955                     tipText = tr("Add a new pane showing a frequency spectrum"); | 
| Chris@66 | 956                 } else { | 
| Chris@66 | 957                     tipText = tr("Add a new layer showing a frequency spectrum"); | 
| Chris@66 | 958                 } | 
| Chris@66 | 959                 break; | 
| Chris@66 | 960 | 
| Chris@66 | 961             default: break; | 
| Chris@66 | 962             } | 
| Chris@66 | 963 | 
| Chris@66 | 964             std::vector<Model *> candidateModels; | 
| Chris@66 | 965             if (menuType == 0) { | 
| Chris@66 | 966                 candidateModels = models; | 
| Chris@66 | 967             } else { | 
| Chris@66 | 968                 candidateModels.push_back(0); | 
| Chris@66 | 969             } | 
| Chris@66 | 970 | 
| Chris@66 | 971             for (std::vector<Model *>::iterator mi = | 
| Chris@66 | 972                      candidateModels.begin(); | 
| Chris@66 | 973                  mi != candidateModels.end(); ++mi) { | 
| Chris@66 | 974 | 
| Chris@66 | 975                 Model *model = *mi; | 
| Chris@66 | 976 | 
| Chris@66 | 977                 int channels = 0; | 
| Chris@66 | 978                 if (model) { | 
| Chris@66 | 979                     DenseTimeValueModel *dtvm = | 
| Chris@66 | 980                         dynamic_cast<DenseTimeValueModel *>(model); | 
| Chris@66 | 981                     if (dtvm) channels = dtvm->getChannelCount(); | 
| Chris@66 | 982                 } | 
| Chris@66 | 983                 if (channels < 1 && getMainModel()) { | 
| Chris@66 | 984                     channels = getMainModel()->getChannelCount(); | 
| Chris@66 | 985                 } | 
| Chris@66 | 986                 if (channels < 1) channels = 1; | 
| Chris@66 | 987 | 
| Chris@66 | 988                 for (int c = 0; c <= channels; ++c) { | 
| Chris@66 | 989 | 
| Chris@66 | 990                     if (c == 1 && channels == 1) continue; | 
| Chris@66 | 991                     bool isDefault = (c == 0); | 
| Chris@66 | 992                     bool isOnly = (isDefault && (channels == 1)); | 
| Chris@66 | 993 | 
| Chris@66 | 994                     if (menuType == 1) { | 
| Chris@66 | 995                         if (isDefault) isOnly = true; | 
| Chris@66 | 996                         else continue; | 
| Chris@66 | 997                     } | 
| Chris@66 | 998 | 
| Chris@66 | 999                     if (isOnly && (!plural || menuType == 1)) { | 
| Chris@66 | 1000 | 
| Chris@67 | 1001                         if (type != LayerFactory::Waveform) { | 
| Chris@67 | 1002                             action = new QAction(mainText, this); | 
| Chris@67 | 1003                         } else { | 
| Chris@67 | 1004                             action = new QAction(icon, mainText, this); | 
| Chris@67 | 1005                         } | 
| Chris@67 | 1006 | 
| Chris@66 | 1007                         action->setShortcut(shortcutText); | 
| Chris@66 | 1008                         action->setStatusTip(tipText); | 
| Chris@66 | 1009                         if (menuType == 0) { | 
| Chris@66 | 1010                             connect(action, SIGNAL(triggered()), | 
| Chris@66 | 1011                                     this, SLOT(addPane())); | 
| Chris@66 | 1012                             connect(this, SIGNAL(canAddPane(bool)), | 
| Chris@66 | 1013                                     action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1014                             m_paneActions[action] = PaneConfiguration(type); | 
| Chris@66 | 1015                         } else { | 
| Chris@66 | 1016                             connect(action, SIGNAL(triggered()), | 
| Chris@66 | 1017                                     this, SLOT(addLayer())); | 
| Chris@66 | 1018                             connect(this, SIGNAL(canAddLayer(bool)), | 
| Chris@66 | 1019                                     action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1020                             m_layerActions[action] = type; | 
| Chris@66 | 1021                         } | 
| Chris@66 | 1022                         menu->addAction(action); | 
| Chris@66 | 1023 | 
| Chris@66 | 1024                     } else { | 
| Chris@66 | 1025 | 
| Chris@66 | 1026                         if (!submenu) { | 
| Chris@66 | 1027                             submenu = menu->addMenu(mainText); | 
| Chris@97 | 1028                             submenu->setTearOffEnabled(true); | 
| Chris@67 | 1029                         } else if (isDefault) { | 
| Chris@67 | 1030                             submenu->addSeparator(); | 
| Chris@66 | 1031                         } | 
| Chris@66 | 1032 | 
| Chris@66 | 1033                         QString actionText; | 
| Chris@66 | 1034                         if (c == 0) { | 
| Chris@66 | 1035                             if (mono) { | 
| Chris@66 | 1036                                 actionText = tr("&All Channels Mixed"); | 
| Chris@66 | 1037                             } else { | 
| Chris@66 | 1038                                 actionText = tr("&All Channels"); | 
| Chris@66 | 1039                             } | 
| Chris@66 | 1040                         } else { | 
| Chris@66 | 1041                             actionText = tr("Channel &%1").arg(c); | 
| Chris@66 | 1042                         } | 
| Chris@66 | 1043 | 
| Chris@66 | 1044                         if (model) { | 
| Chris@66 | 1045                             actionText = tr("%1: %2") | 
| Chris@66 | 1046                                 .arg(model->objectName()) | 
| Chris@66 | 1047                                 .arg(actionText); | 
| Chris@66 | 1048                         } | 
| Chris@67 | 1049 | 
| Chris@67 | 1050                         if (isDefault) { | 
| Chris@67 | 1051                             action = new QAction(icon, actionText, this); | 
| Chris@67 | 1052                             if (!model || model == getMainModel()) { | 
| Chris@67 | 1053                                 action->setShortcut(shortcutText); | 
| Chris@67 | 1054                             } | 
| Chris@67 | 1055                         } else { | 
| Chris@67 | 1056                             action = new QAction(actionText, this); | 
| Chris@67 | 1057                         } | 
| Chris@67 | 1058 | 
| Chris@66 | 1059                         action->setStatusTip(tipText); | 
| Chris@66 | 1060 | 
| Chris@66 | 1061                         if (menuType == 0) { | 
| Chris@66 | 1062                             connect(action, SIGNAL(triggered()), | 
| Chris@66 | 1063                                     this, SLOT(addPane())); | 
| Chris@66 | 1064                             connect(this, SIGNAL(canAddPane(bool)), | 
| Chris@66 | 1065                                     action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1066                             m_paneActions[action] = | 
| Chris@66 | 1067                                 PaneConfiguration(type, model, c - 1); | 
| Chris@66 | 1068                         } else { | 
| Chris@66 | 1069                             connect(action, SIGNAL(triggered()), | 
| Chris@66 | 1070                                     this, SLOT(addLayer())); | 
| Chris@66 | 1071                             connect(this, SIGNAL(canAddLayer(bool)), | 
| Chris@66 | 1072                                     action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1073                             m_layerActions[action] = type; | 
| Chris@66 | 1074                         } | 
| Chris@66 | 1075 | 
| Chris@66 | 1076                         submenu->addAction(action); | 
| Chris@66 | 1077                     } | 
| Chris@66 | 1078 		} | 
| Chris@66 | 1079 	    } | 
| Chris@66 | 1080 	} | 
| Chris@66 | 1081     } | 
| Chris@66 | 1082 | 
| Chris@66 | 1083     menu = m_paneMenu; | 
| Chris@66 | 1084 | 
| Chris@66 | 1085     menu->addSeparator(); | 
| Chris@66 | 1086 | 
| Chris@66 | 1087     action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Pane"), this); | 
| Chris@66 | 1088     action->setShortcut(tr("Alt+D")); | 
| Chris@90 | 1089     action->setStatusTip(tr("Delete the currently active pane")); | 
| Chris@66 | 1090     connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane())); | 
| Chris@66 | 1091     connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1092     menu->addAction(action); | 
| Chris@66 | 1093 | 
| Chris@66 | 1094     menu = m_layerMenu; | 
| Chris@66 | 1095 | 
| Chris@66 | 1096     action = new QAction(QIcon(":/icons/timeruler.png"), tr("Add &Time Ruler"), this); | 
| Chris@66 | 1097     action->setStatusTip(tr("Add a new layer showing a time ruler")); | 
| Chris@66 | 1098     connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@66 | 1099     connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1100     m_layerActions[action] = LayerFactory::TimeRuler; | 
| Chris@66 | 1101     menu->addAction(action); | 
| Chris@66 | 1102 | 
| Chris@66 | 1103     menu->addSeparator(); | 
| Chris@66 | 1104 | 
| Chris@66 | 1105     m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer")); | 
| Chris@97 | 1106     m_existingLayersMenu->setTearOffEnabled(true); | 
| Chris@66 | 1107     m_rightButtonLayerMenu->addMenu(m_existingLayersMenu); | 
| Chris@95 | 1108 | 
| Chris@95 | 1109     m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer")); | 
| Chris@97 | 1110     m_sliceMenu->setTearOffEnabled(true); | 
| Chris@95 | 1111     m_rightButtonLayerMenu->addMenu(m_sliceMenu); | 
| Chris@95 | 1112 | 
| Chris@95 | 1113     setupExistingLayersMenus(); | 
| Chris@66 | 1114 | 
| Chris@66 | 1115     m_rightButtonLayerMenu->addSeparator(); | 
| Chris@66 | 1116     menu->addSeparator(); | 
| Chris@66 | 1117 | 
| Chris@66 | 1118     action = new QAction(tr("&Rename Layer..."), this); | 
| Chris@66 | 1119     action->setShortcut(tr("Alt+R")); | 
| Chris@66 | 1120     action->setStatusTip(tr("Rename the currently active layer")); | 
| Chris@66 | 1121     connect(action, SIGNAL(triggered()), this, SLOT(renameCurrentLayer())); | 
| Chris@66 | 1122     connect(this, SIGNAL(canRenameLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1123     menu->addAction(action); | 
| Chris@66 | 1124     m_rightButtonLayerMenu->addAction(action); | 
| Chris@66 | 1125 | 
| Chris@66 | 1126     action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Layer"), this); | 
| Chris@66 | 1127     action->setShortcut(tr("Alt+Shift+D")); | 
| Chris@66 | 1128     action->setStatusTip(tr("Delete the currently active layer")); | 
| Chris@66 | 1129     connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer())); | 
| Chris@66 | 1130     connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@66 | 1131     menu->addAction(action); | 
| Chris@66 | 1132     m_rightButtonLayerMenu->addAction(action); | 
| Chris@66 | 1133 } | 
| Chris@66 | 1134 | 
| Chris@66 | 1135 void | 
| Chris@66 | 1136 MainWindow::setupTransformsMenu() | 
| Chris@66 | 1137 { | 
| Chris@34 | 1138     if (m_transformsMenu) { | 
| Chris@34 | 1139         m_transformActions.clear(); | 
| Chris@34 | 1140         m_transformActionsReverse.clear(); | 
| Chris@34 | 1141         m_transformsMenu->clear(); | 
| Chris@34 | 1142     } else { | 
| Chris@97 | 1143 	m_transformsMenu = menuBar()->addMenu(tr("&Transform")); | 
| Chris@97 | 1144         m_transformsMenu->setTearOffEnabled(true); | 
| Chris@97 | 1145    } | 
| Chris@34 | 1146 | 
| Chris@0 | 1147     TransformFactory::TransformList transforms = | 
| Chris@0 | 1148 	TransformFactory::getInstance()->getAllTransforms(); | 
| Chris@0 | 1149 | 
| Chris@33 | 1150     vector<QString> types = | 
| Chris@0 | 1151         TransformFactory::getInstance()->getAllTransformTypes(); | 
| Chris@0 | 1152 | 
| Chris@37 | 1153     map<QString, map<QString, SubdividingMenu *> > categoryMenus; | 
| Chris@37 | 1154     map<QString, map<QString, SubdividingMenu *> > makerMenus; | 
| Chris@33 | 1155 | 
| Chris@36 | 1156     map<QString, SubdividingMenu *> byPluginNameMenus; | 
| Chris@33 | 1157     map<QString, map<QString, QMenu *> > pluginNameMenus; | 
| Chris@33 | 1158 | 
| Chris@37 | 1159     set<SubdividingMenu *> pendingMenus; | 
| Chris@37 | 1160 | 
| Chris@34 | 1161     m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms")); | 
| Chris@97 | 1162     m_recentTransformsMenu->setTearOffEnabled(true); | 
| Chris@34 | 1163     m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu); | 
| Chris@34 | 1164     connect(&m_recentTransforms, SIGNAL(recentChanged()), | 
| Chris@34 | 1165             this, SLOT(setupRecentTransformsMenu())); | 
| Chris@34 | 1166 | 
| Chris@34 | 1167     m_transformsMenu->addSeparator(); | 
| Chris@34 | 1168     m_rightButtonTransformsMenu->addSeparator(); | 
| Chris@34 | 1169 | 
| Chris@33 | 1170     for (vector<QString>::iterator i = types.begin(); i != types.end(); ++i) { | 
| Chris@33 | 1171 | 
| Chris@33 | 1172         if (i != types.begin()) { | 
| Chris@34 | 1173             m_transformsMenu->addSeparator(); | 
| Chris@34 | 1174             m_rightButtonTransformsMenu->addSeparator(); | 
| Chris@33 | 1175         } | 
| Chris@33 | 1176 | 
| Chris@33 | 1177         QString byCategoryLabel = tr("%1 by Category").arg(*i); | 
| Chris@37 | 1178         SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel, | 
| Chris@37 | 1179                                                               20, 40); | 
| Chris@97 | 1180         byCategoryMenu->setTearOffEnabled(true); | 
| Chris@37 | 1181         m_transformsMenu->addMenu(byCategoryMenu); | 
| Chris@34 | 1182         m_rightButtonTransformsMenu->addMenu(byCategoryMenu); | 
| Chris@37 | 1183         pendingMenus.insert(byCategoryMenu); | 
| Chris@33 | 1184 | 
| Chris@33 | 1185         vector<QString> categories = | 
| Chris@33 | 1186             TransformFactory::getInstance()->getTransformCategories(*i); | 
| Chris@33 | 1187 | 
| Chris@33 | 1188         for (vector<QString>::iterator j = categories.begin(); | 
| Chris@33 | 1189              j != categories.end(); ++j) { | 
| Chris@33 | 1190 | 
| Chris@33 | 1191             QString category = *j; | 
| Chris@33 | 1192             if (category == "") category = tr("Unclassified"); | 
| Chris@33 | 1193 | 
| Chris@33 | 1194             if (categories.size() < 2) { | 
| Chris@33 | 1195                 categoryMenus[*i][category] = byCategoryMenu; | 
| Chris@33 | 1196                 continue; | 
| Chris@33 | 1197             } | 
| Chris@33 | 1198 | 
| Chris@33 | 1199             QStringList components = category.split(" > "); | 
| Chris@33 | 1200             QString key; | 
| Chris@33 | 1201 | 
| Chris@33 | 1202             for (QStringList::iterator k = components.begin(); | 
| Chris@33 | 1203                  k != components.end(); ++k) { | 
| Chris@33 | 1204 | 
| Chris@33 | 1205                 QString parentKey = key; | 
| Chris@33 | 1206                 if (key != "") key += " > "; | 
| Chris@33 | 1207                 key += *k; | 
| Chris@33 | 1208 | 
| Chris@33 | 1209                 if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) { | 
| Chris@37 | 1210                     SubdividingMenu *m = new SubdividingMenu(*k, 20, 40); | 
| Chris@97 | 1211                     m->setTearOffEnabled(true); | 
| Chris@37 | 1212                     pendingMenus.insert(m); | 
| Chris@37 | 1213                     categoryMenus[*i][key] = m; | 
| Chris@33 | 1214                     if (parentKey == "") { | 
| Chris@37 | 1215                         byCategoryMenu->addMenu(m); | 
| Chris@33 | 1216                     } else { | 
| Chris@37 | 1217                         categoryMenus[*i][parentKey]->addMenu(m); | 
| Chris@33 | 1218                     } | 
| Chris@33 | 1219                 } | 
| Chris@33 | 1220             } | 
| Chris@33 | 1221         } | 
| Chris@33 | 1222 | 
| Chris@34 | 1223         QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); | 
| Chris@36 | 1224         byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel); | 
| Chris@97 | 1225         byPluginNameMenus[*i]->setTearOffEnabled(true); | 
| Chris@36 | 1226         m_transformsMenu->addMenu(byPluginNameMenus[*i]); | 
| Chris@34 | 1227         m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]); | 
| Chris@37 | 1228         pendingMenus.insert(byPluginNameMenus[*i]); | 
| Chris@34 | 1229 | 
| Chris@33 | 1230         QString byMakerLabel = tr("%1 by Maker").arg(*i); | 
| Chris@37 | 1231         SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40); | 
| Chris@97 | 1232         byMakerMenu->setTearOffEnabled(true); | 
| Chris@37 | 1233         m_transformsMenu->addMenu(byMakerMenu); | 
| Chris@34 | 1234         m_rightButtonTransformsMenu->addMenu(byMakerMenu); | 
| Chris@37 | 1235         pendingMenus.insert(byMakerMenu); | 
| Chris@33 | 1236 | 
| Chris@33 | 1237         vector<QString> makers = | 
| Chris@33 | 1238             TransformFactory::getInstance()->getTransformMakers(*i); | 
| Chris@37 | 1239 | 
| Chris@33 | 1240         for (vector<QString>::iterator j = makers.begin(); | 
| Chris@33 | 1241              j != makers.end(); ++j) { | 
| Chris@33 | 1242 | 
| Chris@33 | 1243             QString maker = *j; | 
| Chris@33 | 1244             if (maker == "") maker = tr("Unknown"); | 
| Chris@55 | 1245             maker.replace(QRegExp(tr(" [\\(<].*$")), ""); | 
| Chris@55 | 1246 | 
| Chris@37 | 1247             makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40); | 
| Chris@97 | 1248             makerMenus[*i][maker]->setTearOffEnabled(true); | 
| Chris@37 | 1249             byMakerMenu->addMenu(makerMenus[*i][maker]); | 
| Chris@37 | 1250             pendingMenus.insert(makerMenus[*i][maker]); | 
| Chris@33 | 1251         } | 
| Chris@0 | 1252     } | 
| Chris@0 | 1253 | 
| Chris@0 | 1254     for (unsigned int i = 0; i < transforms.size(); ++i) { | 
| Chris@0 | 1255 | 
| Chris@107 | 1256 	QString name = transforms[i].name; | 
| Chris@107 | 1257 	if (name == "") name = transforms[i].identifier; | 
| Chris@107 | 1258 | 
| Chris@107 | 1259 //        std::cerr << "Plugin Name: " << name.toStdString() << std::endl; | 
| Chris@80 | 1260 | 
| Chris@33 | 1261         QString type = transforms[i].type; | 
| Chris@33 | 1262 | 
| Chris@33 | 1263         QString category = transforms[i].category; | 
| Chris@33 | 1264         if (category == "") category = tr("Unclassified"); | 
| Chris@33 | 1265 | 
| Chris@33 | 1266         QString maker = transforms[i].maker; | 
| Chris@33 | 1267         if (maker == "") maker = tr("Unknown"); | 
| Chris@55 | 1268         maker.replace(QRegExp(tr(" [\\(<].*$")), ""); | 
| Chris@33 | 1269 | 
| Chris@107 | 1270         QString pluginName = name.section(": ", 0, 0); | 
| Chris@107 | 1271         QString output = name.section(": ", 1); | 
| Chris@107 | 1272 | 
| Chris@107 | 1273 	QAction *action = new QAction(tr("%1...").arg(name), this); | 
| Chris@0 | 1274 	connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@107 | 1275 	m_transformActions[action] = transforms[i].identifier; | 
| Chris@107 | 1276         m_transformActionsReverse[transforms[i].identifier] = action; | 
| Chris@0 | 1277 	connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@33 | 1278 | 
| Chris@108 | 1279         action->setStatusTip(transforms[i].description); | 
| Chris@90 | 1280 | 
| Chris@33 | 1281         if (categoryMenus[type].find(category) == categoryMenus[type].end()) { | 
| Chris@33 | 1282             std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " | 
| Chris@33 | 1283                       << "No category menu for transform \"" | 
| Chris@107 | 1284                       << name.toStdString() << "\" (category = \"" | 
| Chris@33 | 1285                       << category.toStdString() << "\")" << std::endl; | 
| Chris@33 | 1286         } else { | 
| Chris@33 | 1287             categoryMenus[type][category]->addAction(action); | 
| Chris@33 | 1288         } | 
| Chris@33 | 1289 | 
| Chris@33 | 1290         if (makerMenus[type].find(maker) == makerMenus[type].end()) { | 
| Chris@33 | 1291             std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " | 
| Chris@33 | 1292                       << "No maker menu for transform \"" | 
| Chris@107 | 1293                       << name.toStdString() << "\" (maker = \"" | 
| Chris@33 | 1294                       << maker.toStdString() << "\")" << std::endl; | 
| Chris@33 | 1295         } else { | 
| Chris@80 | 1296             makerMenus[type][maker]->addAction(action); | 
| Chris@33 | 1297         } | 
| Chris@33 | 1298 | 
| Chris@33 | 1299         action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this); | 
| Chris@33 | 1300         connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@107 | 1301         m_transformActions[action] = transforms[i].identifier; | 
| Chris@33 | 1302         connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@108 | 1303         action->setStatusTip(transforms[i].description); | 
| Chris@33 | 1304 | 
| Chris@34 | 1305 //        cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl; | 
| Chris@34 | 1306 | 
| Chris@33 | 1307         if (pluginNameMenus[type].find(pluginName) == | 
| Chris@33 | 1308             pluginNameMenus[type].end()) { | 
| Chris@33 | 1309 | 
| Chris@36 | 1310             SubdividingMenu *parentMenu = byPluginNameMenus[type]; | 
| Chris@97 | 1311             parentMenu->setTearOffEnabled(true); | 
| Chris@34 | 1312 | 
| Chris@33 | 1313             if (output == "") { | 
| Chris@36 | 1314                 parentMenu->addAction(pluginName, action); | 
| Chris@33 | 1315             } else { | 
| Chris@34 | 1316                 pluginNameMenus[type][pluginName] = | 
| Chris@34 | 1317                     parentMenu->addMenu(pluginName); | 
| Chris@33 | 1318                 connect(this, SIGNAL(canAddLayer(bool)), | 
| Chris@33 | 1319                         pluginNameMenus[type][pluginName], | 
| Chris@33 | 1320                         SLOT(setEnabled(bool))); | 
| Chris@33 | 1321             } | 
| Chris@33 | 1322         } | 
| Chris@33 | 1323 | 
| Chris@33 | 1324         if (pluginNameMenus[type].find(pluginName) != | 
| Chris@33 | 1325             pluginNameMenus[type].end()) { | 
| Chris@33 | 1326             pluginNameMenus[type][pluginName]->addAction(action); | 
| Chris@33 | 1327         } | 
| Chris@0 | 1328     } | 
| Chris@0 | 1329 | 
| Chris@37 | 1330     for (set<SubdividingMenu *>::iterator i = pendingMenus.begin(); | 
| Chris@37 | 1331          i != pendingMenus.end(); ++i) { | 
| Chris@37 | 1332         (*i)->entriesAdded(); | 
| Chris@37 | 1333     } | 
| Chris@37 | 1334 | 
| Chris@34 | 1335     setupRecentTransformsMenu(); | 
| Chris@66 | 1336 } | 
| Chris@66 | 1337 | 
| Chris@66 | 1338 void | 
| Chris@66 | 1339 MainWindow::setupHelpMenu() | 
| Chris@66 | 1340 { | 
| Chris@66 | 1341     if (m_mainMenusCreated) return; | 
| Chris@66 | 1342 | 
| Chris@66 | 1343     QMenu *menu = menuBar()->addMenu(tr("&Help")); | 
| Chris@97 | 1344     menu->setTearOffEnabled(true); | 
| Chris@66 | 1345 | 
| Chris@66 | 1346     QAction *action = new QAction(tr("&Help Reference"), this); | 
| Chris@66 | 1347     action->setStatusTip(tr("Open the Sonic Visualiser reference manual")); | 
| Chris@66 | 1348     connect(action, SIGNAL(triggered()), this, SLOT(help())); | 
| Chris@0 | 1349     menu->addAction(action); | 
| Chris@0 | 1350 | 
| Chris@66 | 1351     action = new QAction(tr("Sonic Visualiser on the &Web"), this); | 
| Chris@66 | 1352     action->setStatusTip(tr("Open the Sonic Visualiser website")); | 
| Chris@66 | 1353     connect(action, SIGNAL(triggered()), this, SLOT(website())); | 
| Chris@0 | 1354     menu->addAction(action); | 
| Chris@66 | 1355 | 
| Chris@66 | 1356     action = new QAction(tr("&About Sonic Visualiser"), this); | 
| Chris@66 | 1357     action->setStatusTip(tr("Show information about Sonic Visualiser")); | 
| Chris@66 | 1358     connect(action, SIGNAL(triggered()), this, SLOT(about())); | 
| Chris@0 | 1359     menu->addAction(action); | 
| Chris@0 | 1360 } | 
| Chris@0 | 1361 | 
| Chris@0 | 1362 void | 
| Chris@0 | 1363 MainWindow::setupRecentFilesMenu() | 
| Chris@0 | 1364 { | 
| Chris@0 | 1365     m_recentFilesMenu->clear(); | 
| Chris@34 | 1366     vector<QString> files = m_recentFiles.getRecent(); | 
| Chris@0 | 1367     for (size_t i = 0; i < files.size(); ++i) { | 
| Chris@0 | 1368 	QAction *action = new QAction(files[i], this); | 
| Chris@0 | 1369 	connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); | 
| Chris@0 | 1370 	m_recentFilesMenu->addAction(action); | 
| Chris@0 | 1371     } | 
| Chris@0 | 1372 } | 
| Chris@0 | 1373 | 
| Chris@0 | 1374 void | 
| Chris@34 | 1375 MainWindow::setupRecentTransformsMenu() | 
| Chris@34 | 1376 { | 
| Chris@34 | 1377     m_recentTransformsMenu->clear(); | 
| Chris@34 | 1378     vector<QString> transforms = m_recentTransforms.getRecent(); | 
| Chris@34 | 1379     for (size_t i = 0; i < transforms.size(); ++i) { | 
| Chris@34 | 1380         TransformActionReverseMap::iterator ti = | 
| Chris@34 | 1381             m_transformActionsReverse.find(transforms[i]); | 
| Chris@34 | 1382         if (ti == m_transformActionsReverse.end()) { | 
| Chris@34 | 1383             std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: " | 
| Chris@34 | 1384                       << "Unknown transform \"" << transforms[i].toStdString() | 
| Chris@34 | 1385                       << "\" in recent transforms list" << std::endl; | 
| Chris@34 | 1386             continue; | 
| Chris@34 | 1387         } | 
| Chris@34 | 1388 	m_recentTransformsMenu->addAction(ti->second); | 
| Chris@34 | 1389     } | 
| Chris@34 | 1390 } | 
| Chris@34 | 1391 | 
| Chris@34 | 1392 void | 
| Chris@95 | 1393 MainWindow::setupExistingLayersMenus() | 
| Chris@0 | 1394 { | 
| Chris@0 | 1395     if (!m_existingLayersMenu) return; // should have been created by setupMenus | 
| Chris@0 | 1396 | 
| Chris@0 | 1397 //    std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl; | 
| Chris@0 | 1398 | 
| Chris@0 | 1399     m_existingLayersMenu->clear(); | 
| Chris@0 | 1400     m_existingLayerActions.clear(); | 
| Chris@0 | 1401 | 
| Chris@95 | 1402     m_sliceMenu->clear(); | 
| Chris@95 | 1403     m_sliceActions.clear(); | 
| Chris@95 | 1404 | 
| Chris@33 | 1405     vector<Layer *> orderedLayers; | 
| Chris@33 | 1406     set<Layer *> observedLayers; | 
| Chris@95 | 1407     set<Layer *> sliceableLayers; | 
| Chris@95 | 1408 | 
| Chris@95 | 1409     LayerFactory *factory = LayerFactory::getInstance(); | 
| Chris@0 | 1410 | 
| Chris@0 | 1411     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@0 | 1412 | 
| Chris@0 | 1413 	Pane *pane = m_paneStack->getPane(i); | 
| Chris@0 | 1414 	if (!pane) continue; | 
| Chris@0 | 1415 | 
| Chris@0 | 1416 	for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@0 | 1417 | 
| Chris@0 | 1418 	    Layer *layer = pane->getLayer(j); | 
| Chris@0 | 1419 	    if (!layer) continue; | 
| Chris@0 | 1420 	    if (observedLayers.find(layer) != observedLayers.end()) { | 
| Chris@0 | 1421 		std::cerr << "found duplicate layer " << layer << std::endl; | 
| Chris@0 | 1422 		continue; | 
| Chris@0 | 1423 	    } | 
| Chris@0 | 1424 | 
| Chris@0 | 1425 //	    std::cerr << "found new layer " << layer << " (name = " | 
| Chris@0 | 1426 //		      << layer->getLayerPresentationName().toStdString() << ")" << std::endl; | 
| Chris@0 | 1427 | 
| Chris@0 | 1428 	    orderedLayers.push_back(layer); | 
| Chris@0 | 1429 	    observedLayers.insert(layer); | 
| Chris@95 | 1430 | 
| Chris@95 | 1431             if (factory->isLayerSliceable(layer)) { | 
| Chris@95 | 1432                 sliceableLayers.insert(layer); | 
| Chris@95 | 1433             } | 
| Chris@0 | 1434 	} | 
| Chris@0 | 1435     } | 
| Chris@0 | 1436 | 
| Chris@33 | 1437     map<QString, int> observedNames; | 
| Chris@0 | 1438 | 
| Chris@0 | 1439     for (int i = 0; i < orderedLayers.size(); ++i) { | 
| Chris@0 | 1440 | 
| Chris@95 | 1441         Layer *layer = orderedLayers[i]; | 
| Chris@95 | 1442 | 
| Chris@95 | 1443 	QString name = layer->getLayerPresentationName(); | 
| Chris@0 | 1444 	int n = ++observedNames[name]; | 
| Chris@0 | 1445 	if (n > 1) name = QString("%1 <%2>").arg(name).arg(n); | 
| Chris@0 | 1446 | 
| Chris@95 | 1447 	QIcon icon = QIcon(QString(":/icons/%1.png") | 
| Chris@95 | 1448                            .arg(factory->getLayerIconName | 
| Chris@95 | 1449                                 (factory->getLayerType(layer)))); | 
| Chris@95 | 1450 | 
| Chris@95 | 1451 	QAction *action = new QAction(icon, name, this); | 
| Chris@0 | 1452 	connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@0 | 1453 	connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@95 | 1454 	m_existingLayerActions[action] = layer; | 
| Chris@0 | 1455 | 
| Chris@0 | 1456 	m_existingLayersMenu->addAction(action); | 
| Chris@95 | 1457 | 
| Chris@95 | 1458         if (sliceableLayers.find(layer) != sliceableLayers.end()) { | 
| Chris@95 | 1459             action = new QAction(icon, name, this); | 
| Chris@95 | 1460             connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | 
| Chris@95 | 1461             connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@95 | 1462             m_sliceActions[action] = layer; | 
| Chris@95 | 1463             m_sliceMenu->addAction(action); | 
| Chris@95 | 1464         } | 
| Chris@0 | 1465     } | 
| Chris@95 | 1466 | 
| Chris@95 | 1467     m_sliceMenu->setEnabled(!m_sliceActions.empty()); | 
| Chris@0 | 1468 } | 
| Chris@0 | 1469 | 
| Chris@0 | 1470 void | 
| Chris@0 | 1471 MainWindow::setupToolbars() | 
| Chris@0 | 1472 { | 
| Chris@0 | 1473     QToolBar *toolbar = addToolBar(tr("Transport Toolbar")); | 
| Chris@0 | 1474 | 
| Chris@0 | 1475     QAction *action = toolbar->addAction(QIcon(":/icons/rewind-start.png"), | 
| Chris@0 | 1476 					 tr("Rewind to Start")); | 
| Chris@0 | 1477     action->setShortcut(tr("Home")); | 
| Chris@0 | 1478     action->setStatusTip(tr("Rewind to the start")); | 
| Chris@0 | 1479     connect(action, SIGNAL(triggered()), this, SLOT(rewindStart())); | 
| Chris@0 | 1480     connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1481 | 
| Chris@0 | 1482     action = toolbar->addAction(QIcon(":/icons/rewind.png"), | 
| Chris@0 | 1483 				tr("Rewind")); | 
| Chris@0 | 1484     action->setShortcut(tr("PageUp")); | 
| Chris@0 | 1485     action->setStatusTip(tr("Rewind to the previous time instant in the current layer")); | 
| Chris@0 | 1486     connect(action, SIGNAL(triggered()), this, SLOT(rewind())); | 
| Chris@0 | 1487     connect(this, SIGNAL(canRewind(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1488 | 
| Chris@0 | 1489     action = toolbar->addAction(QIcon(":/icons/playpause.png"), | 
| Chris@0 | 1490 				tr("Play / Pause")); | 
| Chris@0 | 1491     action->setCheckable(true); | 
| Chris@0 | 1492     action->setShortcut(tr("Space")); | 
| Chris@0 | 1493     action->setStatusTip(tr("Start or stop playback from the current position")); | 
| Chris@0 | 1494     connect(action, SIGNAL(triggered()), this, SLOT(play())); | 
| Chris@0 | 1495     connect(m_playSource, SIGNAL(playStatusChanged(bool)), | 
| Chris@0 | 1496 	    action, SLOT(setChecked(bool))); | 
| Chris@0 | 1497     connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1498 | 
| Chris@0 | 1499     action = toolbar->addAction(QIcon(":/icons/ffwd.png"), | 
| Chris@0 | 1500 				tr("Fast Forward")); | 
| Chris@0 | 1501     action->setShortcut(tr("PageDown")); | 
| Chris@0 | 1502     action->setStatusTip(tr("Fast forward to the next time instant in the current layer")); | 
| Chris@0 | 1503     connect(action, SIGNAL(triggered()), this, SLOT(ffwd())); | 
| Chris@0 | 1504     connect(this, SIGNAL(canFfwd(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1505 | 
| Chris@0 | 1506     action = toolbar->addAction(QIcon(":/icons/ffwd-end.png"), | 
| Chris@0 | 1507 				tr("Fast Forward to End")); | 
| Chris@0 | 1508     action->setShortcut(tr("End")); | 
| Chris@0 | 1509     action->setStatusTip(tr("Fast-forward to the end")); | 
| Chris@0 | 1510     connect(action, SIGNAL(triggered()), this, SLOT(ffwdEnd())); | 
| Chris@0 | 1511     connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1512 | 
| Chris@0 | 1513     toolbar = addToolBar(tr("Play Mode Toolbar")); | 
| Chris@0 | 1514 | 
| Chris@0 | 1515     action = toolbar->addAction(QIcon(":/icons/playselection.png"), | 
| Chris@0 | 1516 				tr("Constrain Playback to Selection")); | 
| Chris@0 | 1517     action->setCheckable(true); | 
| Chris@0 | 1518     action->setChecked(m_viewManager->getPlaySelectionMode()); | 
| Chris@0 | 1519     action->setShortcut(tr("s")); | 
| Chris@0 | 1520     action->setStatusTip(tr("Constrain playback to the selected area")); | 
| Chris@69 | 1521     connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), | 
| Chris@69 | 1522             action, SLOT(setChecked(bool))); | 
| Chris@0 | 1523     connect(action, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); | 
| Chris@0 | 1524     connect(this, SIGNAL(canPlaySelection(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1525 | 
| Chris@0 | 1526     action = toolbar->addAction(QIcon(":/icons/playloop.png"), | 
| Chris@0 | 1527 				tr("Loop Playback")); | 
| Chris@0 | 1528     action->setCheckable(true); | 
| Chris@0 | 1529     action->setChecked(m_viewManager->getPlayLoopMode()); | 
| Chris@0 | 1530     action->setShortcut(tr("l")); | 
| Chris@0 | 1531     action->setStatusTip(tr("Loop playback")); | 
| Chris@69 | 1532     connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), | 
| Chris@69 | 1533             action, SLOT(setChecked(bool))); | 
| Chris@0 | 1534     connect(action, SIGNAL(triggered()), this, SLOT(playLoopToggled())); | 
| Chris@0 | 1535     connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1536 | 
| Chris@0 | 1537     toolbar = addToolBar(tr("Edit Toolbar")); | 
| Chris@0 | 1538     CommandHistory::getInstance()->registerToolbar(toolbar); | 
| Chris@0 | 1539 | 
| Chris@0 | 1540     toolbar = addToolBar(tr("Tools Toolbar")); | 
| Chris@0 | 1541     QActionGroup *group = new QActionGroup(this); | 
| Chris@0 | 1542 | 
| Chris@0 | 1543     action = toolbar->addAction(QIcon(":/icons/navigate.png"), | 
| Chris@0 | 1544 				tr("Navigate")); | 
| Chris@0 | 1545     action->setCheckable(true); | 
| Chris@0 | 1546     action->setChecked(true); | 
| Chris@0 | 1547     action->setShortcut(tr("1")); | 
| Chris@90 | 1548     action->setStatusTip(tr("Navigate")); | 
| Chris@0 | 1549     connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); | 
| Chris@0 | 1550     group->addAction(action); | 
| Chris@0 | 1551     m_toolActions[ViewManager::NavigateMode] = action; | 
| Chris@0 | 1552 | 
| Chris@0 | 1553     action = toolbar->addAction(QIcon(":/icons/select.png"), | 
| Chris@0 | 1554 				tr("Select")); | 
| Chris@0 | 1555     action->setCheckable(true); | 
| Chris@0 | 1556     action->setShortcut(tr("2")); | 
| Chris@90 | 1557     action->setStatusTip(tr("Select ranges")); | 
| Chris@0 | 1558     connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected())); | 
| Chris@0 | 1559     group->addAction(action); | 
| Chris@0 | 1560     m_toolActions[ViewManager::SelectMode] = action; | 
| Chris@0 | 1561 | 
| Chris@0 | 1562     action = toolbar->addAction(QIcon(":/icons/move.png"), | 
| Chris@0 | 1563 				tr("Edit")); | 
| Chris@0 | 1564     action->setCheckable(true); | 
| Chris@0 | 1565     action->setShortcut(tr("3")); | 
| Chris@90 | 1566     action->setStatusTip(tr("Edit items in layer")); | 
| Chris@0 | 1567     connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); | 
| Chris@0 | 1568     connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1569     group->addAction(action); | 
| Chris@0 | 1570     m_toolActions[ViewManager::EditMode] = action; | 
| Chris@0 | 1571 | 
| Chris@0 | 1572     action = toolbar->addAction(QIcon(":/icons/draw.png"), | 
| Chris@0 | 1573 				tr("Draw")); | 
| Chris@0 | 1574     action->setCheckable(true); | 
| Chris@0 | 1575     action->setShortcut(tr("4")); | 
| Chris@90 | 1576     action->setStatusTip(tr("Draw new items in layer")); | 
| Chris@0 | 1577     connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected())); | 
| Chris@0 | 1578     connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); | 
| Chris@0 | 1579     group->addAction(action); | 
| Chris@0 | 1580     m_toolActions[ViewManager::DrawMode] = action; | 
| Chris@0 | 1581 | 
| Chris@0 | 1582 //    action = toolbar->addAction(QIcon(":/icons/text.png"), | 
| Chris@0 | 1583 //				tr("Text")); | 
| Chris@0 | 1584 //    action->setCheckable(true); | 
| Chris@0 | 1585 //    action->setShortcut(tr("5")); | 
| Chris@0 | 1586 //    connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected())); | 
| Chris@0 | 1587 //    group->addAction(action); | 
| Chris@0 | 1588 //    m_toolActions[ViewManager::TextMode] = action; | 
| Chris@0 | 1589 | 
| Chris@0 | 1590     toolNavigateSelected(); | 
| Chris@0 | 1591 } | 
| Chris@0 | 1592 | 
| Chris@0 | 1593 void | 
| Chris@0 | 1594 MainWindow::updateMenuStates() | 
| Chris@0 | 1595 { | 
| Chris@117 | 1596     Pane *currentPane = 0; | 
| Chris@117 | 1597     Layer *currentLayer = 0; | 
| Chris@117 | 1598 | 
| Chris@117 | 1599     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| Chris@117 | 1600     if (currentPane) currentLayer = currentPane->getSelectedLayer(); | 
| Chris@117 | 1601 | 
| Chris@0 | 1602     bool haveCurrentPane = | 
| Chris@117 | 1603         (currentPane != 0); | 
| Chris@0 | 1604     bool haveCurrentLayer = | 
| Chris@117 | 1605         (haveCurrentPane && | 
| Chris@117 | 1606          (currentLayer != 0)); | 
| Chris@0 | 1607     bool haveMainModel = | 
| Chris@0 | 1608 	(getMainModel() != 0); | 
| Chris@0 | 1609     bool havePlayTarget = | 
| Chris@0 | 1610 	(m_playTarget != 0); | 
| Chris@0 | 1611     bool haveSelection = | 
| Chris@0 | 1612 	(m_viewManager && | 
| Chris@0 | 1613 	 !m_viewManager->getSelections().empty()); | 
| Chris@0 | 1614     bool haveCurrentEditableLayer = | 
| Chris@0 | 1615 	(haveCurrentLayer && | 
| Chris@117 | 1616 	 currentLayer->isLayerEditable()); | 
| Chris@0 | 1617     bool haveCurrentTimeInstantsLayer = | 
| Chris@0 | 1618 	(haveCurrentLayer && | 
| Chris@117 | 1619 	 dynamic_cast<TimeInstantLayer *>(currentLayer)); | 
| Chris@0 | 1620     bool haveCurrentTimeValueLayer = | 
| Chris@0 | 1621 	(haveCurrentLayer && | 
| Chris@117 | 1622 	 dynamic_cast<TimeValueLayer *>(currentLayer)); | 
| Chris@0 | 1623     bool haveCurrentColour3DPlot = | 
| Chris@0 | 1624         (haveCurrentLayer && | 
| Chris@117 | 1625          dynamic_cast<Colour3DPlotLayer *>(currentLayer)); | 
| Chris@0 | 1626     bool haveClipboardContents = | 
| Chris@0 | 1627         (m_viewManager && | 
| Chris@0 | 1628          !m_viewManager->getClipboard().empty()); | 
| Chris@0 | 1629 | 
| Chris@0 | 1630     emit canAddPane(haveMainModel); | 
| Chris@0 | 1631     emit canDeleteCurrentPane(haveCurrentPane); | 
| Chris@0 | 1632     emit canZoom(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1633     emit canScroll(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1634     emit canAddLayer(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1635     emit canImportMoreAudio(haveMainModel); | 
| Chris@0 | 1636     emit canImportLayer(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1637     emit canExportAudio(haveMainModel); | 
| Chris@0 | 1638     emit canExportLayer(haveMainModel && | 
| Chris@0 | 1639                         (haveCurrentEditableLayer || haveCurrentColour3DPlot)); | 
| Chris@121 | 1640     emit canExportImage(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1641     emit canDeleteCurrentLayer(haveCurrentLayer); | 
| Chris@0 | 1642     emit canRenameLayer(haveCurrentLayer); | 
| Chris@0 | 1643     emit canEditLayer(haveCurrentEditableLayer); | 
| Chris@0 | 1644     emit canSelect(haveMainModel && haveCurrentPane); | 
| Chris@0 | 1645     emit canPlay(/*!!! haveMainModel && */ havePlayTarget); | 
| Chris@0 | 1646     emit canFfwd(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); | 
| Chris@0 | 1647     emit canRewind(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); | 
| Chris@0 | 1648     emit canPaste(haveCurrentEditableLayer && haveClipboardContents); | 
| Chris@0 | 1649     emit canInsertInstant(haveCurrentPane); | 
| Chris@81 | 1650     emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); | 
| Chris@0 | 1651     emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); | 
| Chris@0 | 1652     emit canClearSelection(haveSelection); | 
| Chris@0 | 1653     emit canEditSelection(haveSelection && haveCurrentEditableLayer); | 
| Chris@0 | 1654     emit canSave(m_sessionFile != "" && m_documentModified); | 
| Chris@0 | 1655 } | 
| Chris@0 | 1656 | 
| Chris@0 | 1657 void | 
| Chris@0 | 1658 MainWindow::updateDescriptionLabel() | 
| Chris@0 | 1659 { | 
| Chris@0 | 1660     if (!getMainModel()) { | 
| Chris@0 | 1661 	m_descriptionLabel->setText(tr("No audio file loaded.")); | 
| Chris@0 | 1662 	return; | 
| Chris@0 | 1663     } | 
| Chris@0 | 1664 | 
| Chris@0 | 1665     QString description; | 
| Chris@0 | 1666 | 
| Chris@0 | 1667     size_t ssr = getMainModel()->getSampleRate(); | 
| Chris@0 | 1668     size_t tsr = ssr; | 
| Chris@0 | 1669     if (m_playSource) tsr = m_playSource->getTargetSampleRate(); | 
| Chris@0 | 1670 | 
| Chris@0 | 1671     if (ssr != tsr) { | 
| Chris@0 | 1672 	description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr); | 
| Chris@0 | 1673     } else { | 
| Chris@0 | 1674 	description = QString("%1Hz").arg(ssr); | 
| Chris@0 | 1675     } | 
| Chris@0 | 1676 | 
| Chris@0 | 1677     description = QString("%1 - %2") | 
| Chris@0 | 1678 	.arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr) | 
| Chris@0 | 1679 	     .toText(false).c_str()) | 
| Chris@0 | 1680 	.arg(description); | 
| Chris@0 | 1681 | 
| Chris@0 | 1682     m_descriptionLabel->setText(description); | 
| Chris@0 | 1683 } | 
| Chris@0 | 1684 | 
| Chris@0 | 1685 void | 
| Chris@0 | 1686 MainWindow::documentModified() | 
| Chris@0 | 1687 { | 
| Chris@0 | 1688 //    std::cerr << "MainWindow::documentModified" << std::endl; | 
| Chris@0 | 1689 | 
| Chris@0 | 1690     if (!m_documentModified) { | 
| Chris@0 | 1691 	setWindowTitle(tr("%1 (modified)").arg(windowTitle())); | 
| Chris@0 | 1692     } | 
| Chris@0 | 1693 | 
| Chris@0 | 1694     m_documentModified = true; | 
| Chris@0 | 1695     updateMenuStates(); | 
| Chris@0 | 1696 } | 
| Chris@0 | 1697 | 
| Chris@0 | 1698 void | 
| Chris@0 | 1699 MainWindow::documentRestored() | 
| Chris@0 | 1700 { | 
| Chris@0 | 1701 //    std::cerr << "MainWindow::documentRestored" << std::endl; | 
| Chris@0 | 1702 | 
| Chris@0 | 1703     if (m_documentModified) { | 
| Chris@0 | 1704 	QString wt(windowTitle()); | 
| Chris@0 | 1705 	wt.replace(tr(" (modified)"), ""); | 
| Chris@0 | 1706 	setWindowTitle(wt); | 
| Chris@0 | 1707     } | 
| Chris@0 | 1708 | 
| Chris@0 | 1709     m_documentModified = false; | 
| Chris@0 | 1710     updateMenuStates(); | 
| Chris@0 | 1711 } | 
| Chris@0 | 1712 | 
| Chris@0 | 1713 void | 
| Chris@0 | 1714 MainWindow::playLoopToggled() | 
| Chris@0 | 1715 { | 
| Chris@0 | 1716     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@0 | 1717 | 
| Chris@0 | 1718     if (action) { | 
| Chris@0 | 1719 	m_viewManager->setPlayLoopMode(action->isChecked()); | 
| Chris@0 | 1720     } else { | 
| Chris@0 | 1721 	m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); | 
| Chris@0 | 1722     } | 
| Chris@0 | 1723 } | 
| Chris@0 | 1724 | 
| Chris@0 | 1725 void | 
| Chris@0 | 1726 MainWindow::playSelectionToggled() | 
| Chris@0 | 1727 { | 
| Chris@0 | 1728     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@0 | 1729 | 
| Chris@0 | 1730     if (action) { | 
| Chris@0 | 1731 	m_viewManager->setPlaySelectionMode(action->isChecked()); | 
| Chris@0 | 1732     } else { | 
| Chris@0 | 1733 	m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); | 
| Chris@0 | 1734     } | 
| Chris@0 | 1735 } | 
| Chris@0 | 1736 | 
| Chris@0 | 1737 void | 
| Chris@116 | 1738 MainWindow::currentPaneChanged(Pane *p) | 
| Chris@0 | 1739 { | 
| Chris@0 | 1740     updateMenuStates(); | 
| Chris@116 | 1741     updateVisibleRangeDisplay(p); | 
| Chris@0 | 1742 } | 
| Chris@0 | 1743 | 
| Chris@0 | 1744 void | 
| Chris@116 | 1745 MainWindow::currentLayerChanged(Pane *p, Layer *) | 
| Chris@0 | 1746 { | 
| Chris@0 | 1747     updateMenuStates(); | 
| Chris@116 | 1748     updateVisibleRangeDisplay(p); | 
| Chris@0 | 1749 } | 
| Chris@0 | 1750 | 
| Chris@0 | 1751 void | 
| Chris@0 | 1752 MainWindow::toolNavigateSelected() | 
| Chris@0 | 1753 { | 
| Chris@0 | 1754     m_viewManager->setToolMode(ViewManager::NavigateMode); | 
| Chris@0 | 1755 } | 
| Chris@0 | 1756 | 
| Chris@0 | 1757 void | 
| Chris@0 | 1758 MainWindow::toolSelectSelected() | 
| Chris@0 | 1759 { | 
| Chris@0 | 1760     m_viewManager->setToolMode(ViewManager::SelectMode); | 
| Chris@0 | 1761 } | 
| Chris@0 | 1762 | 
| Chris@0 | 1763 void | 
| Chris@0 | 1764 MainWindow::toolEditSelected() | 
| Chris@0 | 1765 { | 
| Chris@0 | 1766     m_viewManager->setToolMode(ViewManager::EditMode); | 
| Chris@0 | 1767 } | 
| Chris@0 | 1768 | 
| Chris@0 | 1769 void | 
| Chris@0 | 1770 MainWindow::toolDrawSelected() | 
| Chris@0 | 1771 { | 
| Chris@0 | 1772     m_viewManager->setToolMode(ViewManager::DrawMode); | 
| Chris@0 | 1773 } | 
| Chris@0 | 1774 | 
| Chris@0 | 1775 //void | 
| Chris@0 | 1776 //MainWindow::toolTextSelected() | 
| Chris@0 | 1777 //{ | 
| Chris@0 | 1778 //    m_viewManager->setToolMode(ViewManager::TextMode); | 
| Chris@0 | 1779 //} | 
| Chris@0 | 1780 | 
| Chris@0 | 1781 void | 
| Chris@0 | 1782 MainWindow::selectAll() | 
| Chris@0 | 1783 { | 
| Chris@0 | 1784     if (!getMainModel()) return; | 
| Chris@0 | 1785     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@0 | 1786 					  getMainModel()->getEndFrame())); | 
| Chris@0 | 1787 } | 
| Chris@0 | 1788 | 
| Chris@0 | 1789 void | 
| Chris@0 | 1790 MainWindow::selectToStart() | 
| Chris@0 | 1791 { | 
| Chris@0 | 1792     if (!getMainModel()) return; | 
| Chris@0 | 1793     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@0 | 1794 					  m_viewManager->getGlobalCentreFrame())); | 
| Chris@0 | 1795 } | 
| Chris@0 | 1796 | 
| Chris@0 | 1797 void | 
| Chris@0 | 1798 MainWindow::selectToEnd() | 
| Chris@0 | 1799 { | 
| Chris@0 | 1800     if (!getMainModel()) return; | 
| Chris@0 | 1801     m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), | 
| Chris@0 | 1802 					  getMainModel()->getEndFrame())); | 
| Chris@0 | 1803 } | 
| Chris@0 | 1804 | 
| Chris@0 | 1805 void | 
| Chris@0 | 1806 MainWindow::selectVisible() | 
| Chris@0 | 1807 { | 
| Chris@0 | 1808     Model *model = getMainModel(); | 
| Chris@0 | 1809     if (!model) return; | 
| Chris@0 | 1810 | 
| Chris@0 | 1811     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 1812     if (!currentPane) return; | 
| Chris@0 | 1813 | 
| Chris@0 | 1814     size_t startFrame, endFrame; | 
| Chris@0 | 1815 | 
| Chris@0 | 1816     if (currentPane->getStartFrame() < 0) startFrame = 0; | 
| Chris@0 | 1817     else startFrame = currentPane->getStartFrame(); | 
| Chris@0 | 1818 | 
| Chris@0 | 1819     if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); | 
| Chris@0 | 1820     else endFrame = currentPane->getEndFrame(); | 
| Chris@0 | 1821 | 
| Chris@0 | 1822     m_viewManager->setSelection(Selection(startFrame, endFrame)); | 
| Chris@0 | 1823 } | 
| Chris@0 | 1824 | 
| Chris@0 | 1825 void | 
| Chris@0 | 1826 MainWindow::clearSelection() | 
| Chris@0 | 1827 { | 
| Chris@0 | 1828     m_viewManager->clearSelections(); | 
| Chris@0 | 1829 } | 
| Chris@0 | 1830 | 
| Chris@0 | 1831 void | 
| Chris@0 | 1832 MainWindow::cut() | 
| Chris@0 | 1833 { | 
| Chris@0 | 1834     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 1835     if (!currentPane) return; | 
| Chris@0 | 1836 | 
| Chris@0 | 1837     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@0 | 1838     if (!layer) return; | 
| Chris@0 | 1839 | 
| Chris@0 | 1840     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@0 | 1841     clipboard.clear(); | 
| Chris@0 | 1842 | 
| Chris@0 | 1843     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@0 | 1844 | 
| Chris@0 | 1845     CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); | 
| Chris@0 | 1846 | 
| Chris@0 | 1847     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@0 | 1848          i != selections.end(); ++i) { | 
| Chris@0 | 1849         layer->copy(*i, clipboard); | 
| Chris@0 | 1850         layer->deleteSelection(*i); | 
| Chris@0 | 1851     } | 
| Chris@0 | 1852 | 
| Chris@0 | 1853     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@0 | 1854 } | 
| Chris@0 | 1855 | 
| Chris@0 | 1856 void | 
| Chris@0 | 1857 MainWindow::copy() | 
| Chris@0 | 1858 { | 
| Chris@0 | 1859     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 1860     if (!currentPane) return; | 
| Chris@0 | 1861 | 
| Chris@0 | 1862     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@0 | 1863     if (!layer) return; | 
| Chris@0 | 1864 | 
| Chris@0 | 1865     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@0 | 1866     clipboard.clear(); | 
| Chris@0 | 1867 | 
| Chris@0 | 1868     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@0 | 1869 | 
| Chris@0 | 1870     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@0 | 1871          i != selections.end(); ++i) { | 
| Chris@0 | 1872         layer->copy(*i, clipboard); | 
| Chris@0 | 1873     } | 
| Chris@0 | 1874 } | 
| Chris@0 | 1875 | 
| Chris@0 | 1876 void | 
| Chris@0 | 1877 MainWindow::paste() | 
| Chris@0 | 1878 { | 
| Chris@0 | 1879     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 1880     if (!currentPane) return; | 
| Chris@0 | 1881 | 
| Chris@0 | 1882     //!!! if we have no current layer, we should create one of the most | 
| Chris@0 | 1883     // appropriate type | 
| Chris@0 | 1884 | 
| Chris@0 | 1885     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@0 | 1886     if (!layer) return; | 
| Chris@0 | 1887 | 
| Chris@0 | 1888     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@0 | 1889     Clipboard::PointList contents = clipboard.getPoints(); | 
| Chris@0 | 1890 /* | 
| Chris@0 | 1891     long minFrame = 0; | 
| Chris@0 | 1892     bool have = false; | 
| Chris@0 | 1893     for (int i = 0; i < contents.size(); ++i) { | 
| Chris@0 | 1894         if (!contents[i].haveFrame()) continue; | 
| Chris@0 | 1895         if (!have || contents[i].getFrame() < minFrame) { | 
| Chris@0 | 1896             minFrame = contents[i].getFrame(); | 
| Chris@0 | 1897             have = true; | 
| Chris@0 | 1898         } | 
| Chris@0 | 1899     } | 
| Chris@0 | 1900 | 
| Chris@0 | 1901     long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; | 
| Chris@0 | 1902 | 
| Chris@0 | 1903     layer->paste(clipboard, frameOffset); | 
| Chris@0 | 1904 */ | 
| Chris@0 | 1905     layer->paste(clipboard, 0, true); | 
| Chris@0 | 1906 } | 
| Chris@0 | 1907 | 
| Chris@0 | 1908 void | 
| Chris@0 | 1909 MainWindow::deleteSelected() | 
| Chris@0 | 1910 { | 
| Chris@0 | 1911     if (m_paneStack->getCurrentPane() && | 
| Chris@0 | 1912 	m_paneStack->getCurrentPane()->getSelectedLayer()) { | 
| Chris@0 | 1913 | 
| Chris@0 | 1914 	MultiSelection::SelectionList selections = | 
| Chris@0 | 1915 	    m_viewManager->getSelections(); | 
| Chris@0 | 1916 | 
| Chris@0 | 1917 	for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@0 | 1918 	     i != selections.end(); ++i) { | 
| Chris@0 | 1919 | 
| Chris@0 | 1920 	    m_paneStack->getCurrentPane()->getSelectedLayer()->deleteSelection(*i); | 
| Chris@0 | 1921 	} | 
| Chris@0 | 1922     } | 
| Chris@0 | 1923 } | 
| Chris@0 | 1924 | 
| Chris@0 | 1925 void | 
| Chris@0 | 1926 MainWindow::insertInstant() | 
| Chris@0 | 1927 { | 
| Chris@0 | 1928     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@81 | 1929     insertInstantAt(frame); | 
| Chris@81 | 1930 } | 
| Chris@81 | 1931 | 
| Chris@81 | 1932 void | 
| Chris@81 | 1933 MainWindow::insertInstantsAtBoundaries() | 
| Chris@81 | 1934 { | 
| Chris@81 | 1935     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@81 | 1936     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@81 | 1937          i != selections.end(); ++i) { | 
| Chris@81 | 1938         size_t start = i->getStartFrame(); | 
| Chris@81 | 1939         size_t end = i->getEndFrame(); | 
| Chris@81 | 1940         if (start != end) { | 
| Chris@81 | 1941             insertInstantAt(i->getStartFrame()); | 
| Chris@81 | 1942             insertInstantAt(i->getEndFrame()); | 
| Chris@81 | 1943         } | 
| Chris@81 | 1944     } | 
| Chris@81 | 1945 } | 
| Chris@81 | 1946 | 
| Chris@81 | 1947 void | 
| Chris@81 | 1948 MainWindow::insertInstantAt(size_t frame) | 
| Chris@81 | 1949 { | 
| Chris@0 | 1950     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 1951     if (!pane) { | 
| Chris@0 | 1952         return; | 
| Chris@0 | 1953     } | 
| Chris@0 | 1954 | 
| Chris@0 | 1955     Layer *layer = dynamic_cast<TimeInstantLayer *> | 
| Chris@0 | 1956         (pane->getSelectedLayer()); | 
| Chris@0 | 1957 | 
| Chris@0 | 1958     if (!layer) { | 
| Chris@0 | 1959         for (int i = pane->getLayerCount(); i > 0; --i) { | 
| Chris@0 | 1960             layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); | 
| Chris@0 | 1961             if (layer) break; | 
| Chris@0 | 1962         } | 
| Chris@0 | 1963 | 
| Chris@0 | 1964         if (!layer) { | 
| Chris@0 | 1965             CommandHistory::getInstance()->startCompoundOperation | 
| Chris@0 | 1966                 (tr("Add Point"), true); | 
| Chris@0 | 1967             layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); | 
| Chris@0 | 1968             if (layer) { | 
| Chris@0 | 1969                 m_document->addLayerToView(pane, layer); | 
| Chris@0 | 1970                 m_paneStack->setCurrentLayer(pane, layer); | 
| Chris@0 | 1971             } | 
| Chris@0 | 1972             CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@0 | 1973         } | 
| Chris@0 | 1974     } | 
| Chris@0 | 1975 | 
| Chris@0 | 1976     if (layer) { | 
| Chris@0 | 1977 | 
| Chris@0 | 1978         Model *model = layer->getModel(); | 
| Chris@0 | 1979         SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 
| Chris@0 | 1980             (model); | 
| Chris@0 | 1981 | 
| Chris@0 | 1982         if (sodm) { | 
| Chris@0 | 1983             SparseOneDimensionalModel::Point point | 
| Chris@0 | 1984                 (frame, QString("%1").arg(sodm->getPointCount() + 1)); | 
| Chris@0 | 1985             CommandHistory::getInstance()->addCommand | 
| Chris@0 | 1986                 (new SparseOneDimensionalModel::AddPointCommand(sodm, point, | 
| Chris@0 | 1987                                                                 tr("Add Points")), | 
| Chris@0 | 1988                  true, true); // bundled | 
| Chris@0 | 1989         } | 
| Chris@0 | 1990     } | 
| Chris@0 | 1991 } | 
| Chris@0 | 1992 | 
| Chris@0 | 1993 void | 
| Chris@0 | 1994 MainWindow::importAudio() | 
| Chris@0 | 1995 { | 
| Chris@88 | 1996     QString path = getOpenFileName(FileFinder::AudioFile); | 
| Chris@0 | 1997 | 
| Chris@0 | 1998     if (path != "") { | 
| Chris@82 | 1999 	if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) { | 
| Chris@0 | 2000 	    QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2001 				  tr("Audio file \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2002 	} | 
| Chris@0 | 2003     } | 
| Chris@0 | 2004 } | 
| Chris@0 | 2005 | 
| Chris@0 | 2006 void | 
| Chris@0 | 2007 MainWindow::importMoreAudio() | 
| Chris@0 | 2008 { | 
| Chris@88 | 2009     QString path = getOpenFileName(FileFinder::AudioFile); | 
| Chris@0 | 2010 | 
| Chris@0 | 2011     if (path != "") { | 
| Chris@82 | 2012 	if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) { | 
| Chris@0 | 2013 	    QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2014 				  tr("Audio file \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2015 	} | 
| Chris@0 | 2016     } | 
| Chris@0 | 2017 } | 
| Chris@0 | 2018 | 
| Chris@0 | 2019 void | 
| Chris@0 | 2020 MainWindow::exportAudio() | 
| Chris@0 | 2021 { | 
| Chris@0 | 2022     if (!getMainModel()) return; | 
| Chris@0 | 2023 | 
| Chris@88 | 2024     QString path = getSaveFileName(FileFinder::AudioFile); | 
| Chris@0 | 2025 | 
| Chris@0 | 2026     if (path == "") return; | 
| Chris@0 | 2027 | 
| Chris@0 | 2028     bool ok = false; | 
| Chris@0 | 2029     QString error; | 
| Chris@0 | 2030 | 
| Chris@0 | 2031     MultiSelection ms = m_viewManager->getSelection(); | 
| Chris@0 | 2032     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@0 | 2033 | 
| Chris@0 | 2034     bool multiple = false; | 
| Chris@0 | 2035 | 
| Chris@38 | 2036     MultiSelection *selectionToWrite = 0; | 
| Chris@38 | 2037 | 
| Chris@38 | 2038     if (selections.size() == 1) { | 
| Chris@0 | 2039 | 
| Chris@0 | 2040 	QStringList items; | 
| Chris@0 | 2041 	items << tr("Export the selected region only") | 
| Chris@0 | 2042 	      << tr("Export the whole audio file"); | 
| Chris@0 | 2043 | 
| Chris@0 | 2044 	bool ok = false; | 
| Chris@0 | 2045 	QString item = ListInputDialog::getItem | 
| Chris@0 | 2046 	    (this, tr("Select region to export"), | 
| Chris@0 | 2047 	     tr("Which region from the original audio file do you want to export?"), | 
| Chris@0 | 2048 	     items, 0, &ok); | 
| Chris@0 | 2049 | 
| Chris@0 | 2050 	if (!ok || item.isEmpty()) return; | 
| Chris@0 | 2051 | 
| Chris@38 | 2052 	if (item == items[0]) selectionToWrite = &ms; | 
| Chris@38 | 2053 | 
| Chris@38 | 2054     } else if (selections.size() > 1) { | 
| Chris@0 | 2055 | 
| Chris@0 | 2056 	QStringList items; | 
| Chris@0 | 2057 	items << tr("Export the selected regions into a single audio file") | 
| Chris@0 | 2058 	      << tr("Export the selected regions into separate files") | 
| Chris@0 | 2059 	      << tr("Export the whole audio file"); | 
| Chris@0 | 2060 | 
| Chris@0 | 2061 	QString item = ListInputDialog::getItem | 
| Chris@0 | 2062 	    (this, tr("Select region to export"), | 
| Chris@0 | 2063 	     tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"), | 
| Chris@0 | 2064 	     items, 0, &ok); | 
| Chris@0 | 2065 | 
| Chris@0 | 2066 	if (!ok || item.isEmpty()) return; | 
| Chris@0 | 2067 | 
| Chris@0 | 2068 	if (item == items[0]) { | 
| Chris@0 | 2069 | 
| Chris@38 | 2070             selectionToWrite = &ms; | 
| Chris@38 | 2071 | 
| Chris@38 | 2072         } else if (item == items[1]) { | 
| Chris@0 | 2073 | 
| Chris@0 | 2074             multiple = true; | 
| Chris@0 | 2075 | 
| Chris@0 | 2076 	    int n = 1; | 
| Chris@0 | 2077 	    QString base = path; | 
| Chris@0 | 2078 	    base.replace(".wav", ""); | 
| Chris@0 | 2079 | 
| Chris@0 | 2080 	    for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@0 | 2081 		 i != selections.end(); ++i) { | 
| Chris@0 | 2082 | 
| Chris@0 | 2083 		MultiSelection subms; | 
| Chris@0 | 2084 		subms.setSelection(*i); | 
| Chris@0 | 2085 | 
| Chris@0 | 2086 		QString subpath = QString("%1.%2.wav").arg(base).arg(n); | 
| Chris@0 | 2087 		++n; | 
| Chris@0 | 2088 | 
| Chris@0 | 2089 		if (QFileInfo(subpath).exists()) { | 
| Chris@0 | 2090 		    error = tr("Fragment file %1 already exists, aborting").arg(subpath); | 
| Chris@0 | 2091 		    break; | 
| Chris@0 | 2092 		} | 
| Chris@0 | 2093 | 
| Chris@38 | 2094 		WavFileWriter subwriter(subpath, | 
| Chris@38 | 2095                                         getMainModel()->getSampleRate(), | 
| Chris@38 | 2096                                         getMainModel()->getChannelCount()); | 
| Chris@38 | 2097                 subwriter.writeModel(getMainModel(), &subms); | 
| Chris@0 | 2098 		ok = subwriter.isOK(); | 
| Chris@0 | 2099 | 
| Chris@0 | 2100 		if (!ok) { | 
| Chris@0 | 2101 		    error = subwriter.getError(); | 
| Chris@0 | 2102 		    break; | 
| Chris@0 | 2103 		} | 
| Chris@0 | 2104 	    } | 
| Chris@0 | 2105 	} | 
| Chris@0 | 2106     } | 
| Chris@0 | 2107 | 
| Chris@38 | 2108     if (!multiple) { | 
| Chris@38 | 2109         WavFileWriter writer(path, | 
| Chris@38 | 2110                              getMainModel()->getSampleRate(), | 
| Chris@38 | 2111                              getMainModel()->getChannelCount()); | 
| Chris@38 | 2112         writer.writeModel(getMainModel(), selectionToWrite); | 
| Chris@38 | 2113 	ok = writer.isOK(); | 
| Chris@38 | 2114 	error = writer.getError(); | 
| Chris@0 | 2115     } | 
| Chris@0 | 2116 | 
| Chris@0 | 2117     if (ok) { | 
| Chris@0 | 2118         if (!multiple) { | 
| Chris@34 | 2119             m_recentFiles.addFile(path); | 
| Chris@0 | 2120         } | 
| Chris@0 | 2121     } else { | 
| Chris@0 | 2122 	QMessageBox::critical(this, tr("Failed to write file"), error); | 
| Chris@0 | 2123     } | 
| Chris@0 | 2124 } | 
| Chris@0 | 2125 | 
| Chris@0 | 2126 void | 
| Chris@0 | 2127 MainWindow::importLayer() | 
| Chris@0 | 2128 { | 
| Chris@0 | 2129     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 2130 | 
| Chris@0 | 2131     if (!pane) { | 
| Chris@0 | 2132 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@0 | 2133 	std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl; | 
| Chris@0 | 2134 	return; | 
| Chris@0 | 2135     } | 
| Chris@0 | 2136 | 
| Chris@0 | 2137     if (!getMainModel()) { | 
| Chris@0 | 2138 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@0 | 2139 	std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl; | 
| Chris@0 | 2140 	return; | 
| Chris@0 | 2141     } | 
| Chris@0 | 2142 | 
| Chris@88 | 2143     QString path = getOpenFileName(FileFinder::LayerFile); | 
| Chris@0 | 2144 | 
| Chris@0 | 2145     if (path != "") { | 
| Chris@0 | 2146 | 
| Chris@82 | 2147         if (openLayerFile(path) == FileOpenFailed) { | 
| Chris@0 | 2148             QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2149                                   tr("File %1 could not be opened.").arg(path)); | 
| Chris@0 | 2150             return; | 
| Chris@0 | 2151         } | 
| Chris@0 | 2152     } | 
| Chris@0 | 2153 } | 
| Chris@0 | 2154 | 
| Chris@82 | 2155 MainWindow::FileOpenStatus | 
| Chris@0 | 2156 MainWindow::openLayerFile(QString path) | 
| Chris@0 | 2157 { | 
| Chris@86 | 2158     return openLayerFile(path, path); | 
| Chris@86 | 2159 } | 
| Chris@86 | 2160 | 
| Chris@86 | 2161 MainWindow::FileOpenStatus | 
| Chris@86 | 2162 MainWindow::openLayerFile(QString path, QString location) | 
| Chris@86 | 2163 { | 
| Chris@0 | 2164     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 2165 | 
| Chris@0 | 2166     if (!pane) { | 
| Chris@0 | 2167 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@0 | 2168 	std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl; | 
| Chris@82 | 2169 	return FileOpenFailed; | 
| Chris@0 | 2170     } | 
| Chris@0 | 2171 | 
| Chris@0 | 2172     if (!getMainModel()) { | 
| Chris@0 | 2173 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@0 | 2174 	std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl; | 
| Chris@82 | 2175 	return FileOpenFailed; | 
| Chris@0 | 2176     } | 
| Chris@0 | 2177 | 
| Chris@86 | 2178     bool realFile = (location == path); | 
| Chris@86 | 2179 | 
| Chris@0 | 2180     if (path.endsWith(".svl") || path.endsWith(".xml")) { | 
| Chris@0 | 2181 | 
| Chris@0 | 2182         PaneCallback callback(this); | 
| Chris@0 | 2183         QFile file(path); | 
| Chris@0 | 2184 | 
| Chris@0 | 2185         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | 
| Chris@0 | 2186             std::cerr << "ERROR: MainWindow::openLayerFile(" | 
| Chris@86 | 2187                       << location.toStdString() | 
| Chris@0 | 2188                       << "): Failed to open file for reading" << std::endl; | 
| Chris@82 | 2189             return FileOpenFailed; | 
| Chris@0 | 2190         } | 
| Chris@0 | 2191 | 
| Chris@87 | 2192         SVFileReader reader(m_document, callback, location); | 
| Chris@0 | 2193         reader.setCurrentPane(pane); | 
| Chris@0 | 2194 | 
| Chris@0 | 2195         QXmlInputSource inputSource(&file); | 
| Chris@0 | 2196         reader.parse(inputSource); | 
| Chris@0 | 2197 | 
| Chris@0 | 2198         if (!reader.isOK()) { | 
| Chris@0 | 2199             std::cerr << "ERROR: MainWindow::openLayerFile(" | 
| Chris@86 | 2200                       << location.toStdString() | 
| Chris@0 | 2201                       << "): Failed to read XML file: " | 
| Chris@0 | 2202                       << reader.getErrorString().toStdString() << std::endl; | 
| Chris@82 | 2203             return FileOpenFailed; | 
| Chris@0 | 2204         } | 
| Chris@0 | 2205 | 
| Chris@86 | 2206         m_recentFiles.addFile(location); | 
| Chris@86 | 2207 | 
| Chris@86 | 2208         if (realFile) { | 
| Chris@88 | 2209             registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog | 
| Chris@86 | 2210         } | 
| Chris@86 | 2211 | 
| Chris@82 | 2212         return FileOpenSucceeded; | 
| Chris@0 | 2213 | 
| Chris@0 | 2214     } else { | 
| Chris@0 | 2215 | 
| Chris@0 | 2216         Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate()); | 
| Chris@0 | 2217 | 
| Chris@0 | 2218         if (model) { | 
| Chris@86 | 2219 | 
| Chris@0 | 2220             Layer *newLayer = m_document->createImportedLayer(model); | 
| Chris@86 | 2221 | 
| Chris@0 | 2222             if (newLayer) { | 
| Chris@86 | 2223 | 
| Chris@0 | 2224                 m_document->addLayerToView(pane, newLayer); | 
| Chris@86 | 2225                 m_recentFiles.addFile(location); | 
| Chris@86 | 2226 | 
| Chris@86 | 2227                 if (realFile) { | 
| Chris@88 | 2228                     registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog | 
| Chris@86 | 2229                 } | 
| Chris@86 | 2230 | 
| Chris@82 | 2231                 return FileOpenSucceeded; | 
| Chris@0 | 2232             } | 
| Chris@0 | 2233         } | 
| Chris@0 | 2234     } | 
| Chris@0 | 2235 | 
| Chris@82 | 2236     return FileOpenFailed; | 
| Chris@0 | 2237 } | 
| Chris@0 | 2238 | 
| Chris@0 | 2239 void | 
| Chris@0 | 2240 MainWindow::exportLayer() | 
| Chris@0 | 2241 { | 
| Chris@0 | 2242     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 2243     if (!pane) return; | 
| Chris@0 | 2244 | 
| Chris@0 | 2245     Layer *layer = pane->getSelectedLayer(); | 
| Chris@0 | 2246     if (!layer) return; | 
| Chris@0 | 2247 | 
| Chris@0 | 2248     Model *model = layer->getModel(); | 
| Chris@0 | 2249     if (!model) return; | 
| Chris@0 | 2250 | 
| Chris@88 | 2251     QString path = getSaveFileName(FileFinder::LayerFile); | 
| Chris@0 | 2252 | 
| Chris@0 | 2253     if (path == "") return; | 
| Chris@0 | 2254 | 
| Chris@0 | 2255     if (QFileInfo(path).suffix() == "") path += ".svl"; | 
| Chris@0 | 2256 | 
| Chris@0 | 2257     QString error; | 
| Chris@0 | 2258 | 
| Chris@0 | 2259     if (path.endsWith(".xml") || path.endsWith(".svl")) { | 
| Chris@0 | 2260 | 
| Chris@0 | 2261         QFile file(path); | 
| Chris@0 | 2262         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { | 
| Chris@0 | 2263             error = tr("Failed to open file %1 for writing").arg(path); | 
| Chris@0 | 2264         } else { | 
| Chris@0 | 2265             QTextStream out(&file); | 
| Chris@0 | 2266             out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | 
| Chris@0 | 2267                 << "<!DOCTYPE sonic-visualiser>\n" | 
| Chris@0 | 2268                 << "<sv>\n" | 
| Chris@0 | 2269                 << "  <data>\n"; | 
| Chris@0 | 2270 | 
| Chris@0 | 2271             model->toXml(out, "    "); | 
| Chris@0 | 2272 | 
| Chris@0 | 2273             out << "  </data>\n" | 
| Chris@0 | 2274                 << "  <display>\n"; | 
| Chris@0 | 2275 | 
| Chris@0 | 2276             layer->toXml(out, "    "); | 
| Chris@0 | 2277 | 
| Chris@0 | 2278             out << "  </display>\n" | 
| Chris@0 | 2279                 << "</sv>\n"; | 
| Chris@0 | 2280         } | 
| Chris@0 | 2281 | 
| Chris@0 | 2282     } else { | 
| Chris@0 | 2283 | 
| Chris@0 | 2284         CSVFileWriter writer(path, model, | 
| Chris@0 | 2285                              (path.endsWith(".csv") ? "," : "\t")); | 
| Chris@0 | 2286         writer.write(); | 
| Chris@0 | 2287 | 
| Chris@0 | 2288         if (!writer.isOK()) { | 
| Chris@0 | 2289             error = writer.getError(); | 
| Chris@0 | 2290         } | 
| Chris@0 | 2291     } | 
| Chris@0 | 2292 | 
| Chris@0 | 2293     if (error != "") { | 
| Chris@0 | 2294         QMessageBox::critical(this, tr("Failed to write file"), error); | 
| Chris@0 | 2295     } else { | 
| Chris@34 | 2296         m_recentFiles.addFile(path); | 
| Chris@0 | 2297     } | 
| Chris@0 | 2298 } | 
| Chris@0 | 2299 | 
| Chris@121 | 2300 void | 
| Chris@121 | 2301 MainWindow::exportImage() | 
| Chris@121 | 2302 { | 
| Chris@121 | 2303     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@121 | 2304     if (!pane) return; | 
| Chris@121 | 2305 | 
| Chris@121 | 2306     QString path = getSaveFileName(FileFinder::ImageFile); | 
| Chris@121 | 2307 | 
| Chris@121 | 2308     if (path == "") return; | 
| Chris@121 | 2309 | 
| Chris@121 | 2310     if (QFileInfo(path).suffix() == "") path += ".png"; | 
| Chris@121 | 2311 | 
| Chris@123 | 2312     bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty(); | 
| Chris@123 | 2313 | 
| Chris@123 | 2314     QSize total, visible, selected; | 
| Chris@123 | 2315     total = pane->getImageSize(); | 
| Chris@123 | 2316     visible = pane->getImageSize(pane->getFirstVisibleFrame(), | 
| Chris@123 | 2317                                  pane->getLastVisibleFrame()); | 
| Chris@123 | 2318 | 
| Chris@123 | 2319     size_t sf0 = 0, sf1 = 0; | 
| Chris@123 | 2320 | 
| Chris@123 | 2321     if (haveSelection) { | 
| Chris@123 | 2322         MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@123 | 2323         sf0 = selections.begin()->getStartFrame(); | 
| Chris@123 | 2324         MultiSelection::SelectionList::iterator e = selections.end(); | 
| Chris@123 | 2325         --e; | 
| Chris@123 | 2326         sf1 = e->getEndFrame(); | 
| Chris@123 | 2327         selected = pane->getImageSize(sf0, sf1); | 
| Chris@123 | 2328     } | 
| Chris@123 | 2329 | 
| Chris@123 | 2330     QStringList items; | 
| Chris@125 | 2331     items << tr("Export the whole pane (%1x%2 pixels)") | 
| Chris@123 | 2332         .arg(total.width()).arg(total.height()); | 
| Chris@123 | 2333     items << tr("Export the visible area only (%1x%2 pixels)") | 
| Chris@123 | 2334         .arg(visible.width()).arg(visible.height()); | 
| Chris@123 | 2335     if (haveSelection) { | 
| Chris@123 | 2336         items << tr("Export the selection extent (%1x%2 pixels)") | 
| Chris@123 | 2337             .arg(selected.width()).arg(selected.height()); | 
| Chris@124 | 2338     } else { | 
| Chris@124 | 2339         items << tr("Export the selection extent"); | 
| Chris@123 | 2340     } | 
| Chris@123 | 2341 | 
| Chris@123 | 2342     QSettings settings; | 
| Chris@123 | 2343     settings.beginGroup("MainWindow"); | 
| Chris@123 | 2344     int deflt = settings.value("lastimageexportregion", 0).toInt(); | 
| Chris@123 | 2345     if (deflt == 2 && !haveSelection) deflt = 1; | 
| Chris@124 | 2346     if (deflt == 0 && total.width() > 32767) deflt = 1; | 
| Chris@124 | 2347 | 
| Chris@124 | 2348     ListInputDialog *lid = new ListInputDialog | 
| Chris@123 | 2349         (this, tr("Select region to export"), | 
| Chris@123 | 2350          tr("Which region of the current pane do you want to export as an image?"), | 
| Chris@124 | 2351          items, deflt); | 
| Chris@124 | 2352 | 
| Chris@124 | 2353     if (!haveSelection) { | 
| Chris@124 | 2354         lid->setItemAvailability(2, false); | 
| Chris@124 | 2355     } | 
| Chris@124 | 2356     if (total.width() > 32767) { // appears to be the limit of a QImage | 
| Chris@124 | 2357         lid->setItemAvailability(0, false); | 
| Chris@124 | 2358         lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image.")); | 
| Chris@124 | 2359     } | 
| Chris@124 | 2360 | 
| Chris@124 | 2361     bool ok = lid->exec(); | 
| Chris@124 | 2362     QString item = lid->getCurrentString(); | 
| Chris@124 | 2363     delete lid; | 
| Chris@123 | 2364 | 
| Chris@123 | 2365     if (!ok || item.isEmpty()) return; | 
| Chris@123 | 2366 | 
| Chris@123 | 2367     settings.setValue("lastimageexportregion", deflt); | 
| Chris@123 | 2368 | 
| Chris@123 | 2369     QImage *image = 0; | 
| Chris@123 | 2370 | 
| Chris@123 | 2371     if (item == items[0]) { | 
| Chris@123 | 2372         image = pane->toNewImage(); | 
| Chris@123 | 2373     } else if (item == items[1]) { | 
| Chris@123 | 2374         image = pane->toNewImage(pane->getFirstVisibleFrame(), | 
| Chris@123 | 2375                                  pane->getLastVisibleFrame()); | 
| Chris@123 | 2376     } else if (haveSelection) { | 
| Chris@123 | 2377         image = pane->toNewImage(sf0, sf1); | 
| Chris@123 | 2378     } | 
| Chris@123 | 2379 | 
| Chris@121 | 2380     if (!image) return; | 
| Chris@121 | 2381 | 
| Chris@121 | 2382     if (!image->save(path, "PNG")) { | 
| Chris@121 | 2383         QMessageBox::critical(this, tr("Failed to save image file"), | 
| Chris@121 | 2384                               tr("Failed to save image file %1").arg(path)); | 
| Chris@121 | 2385     } | 
| Chris@121 | 2386 | 
| Chris@121 | 2387     delete image; | 
| Chris@121 | 2388 } | 
| Chris@121 | 2389 | 
| Chris@82 | 2390 MainWindow::FileOpenStatus | 
| Chris@0 | 2391 MainWindow::openAudioFile(QString path, AudioFileOpenMode mode) | 
| Chris@0 | 2392 { | 
| Chris@86 | 2393     return openAudioFile(path, path, mode); | 
| Chris@86 | 2394 } | 
| Chris@86 | 2395 | 
| Chris@86 | 2396 MainWindow::FileOpenStatus | 
| Chris@86 | 2397 MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode) | 
| Chris@86 | 2398 { | 
| Chris@0 | 2399     if (!(QFileInfo(path).exists() && | 
| Chris@0 | 2400 	  QFileInfo(path).isFile() && | 
| Chris@0 | 2401 	  QFileInfo(path).isReadable())) { | 
| Chris@82 | 2402 	return FileOpenFailed; | 
| Chris@0 | 2403     } | 
| Chris@0 | 2404 | 
| Chris@70 | 2405     m_openingAudioFile = true; | 
| Chris@70 | 2406 | 
| Chris@88 | 2407     WaveFileModel *newModel = new WaveFileModel(path, location); | 
| Chris@0 | 2408 | 
| Chris@0 | 2409     if (!newModel->isOK()) { | 
| Chris@0 | 2410 	delete newModel; | 
| Chris@70 | 2411         m_openingAudioFile = false; | 
| Chris@82 | 2412 	return FileOpenFailed; | 
| Chris@0 | 2413     } | 
| Chris@0 | 2414 | 
| Chris@0 | 2415     bool setAsMain = true; | 
| Chris@0 | 2416     static bool prevSetAsMain = true; | 
| Chris@0 | 2417 | 
| Chris@86 | 2418     bool realFile = (location == path); | 
| Chris@86 | 2419 | 
| Chris@0 | 2420     if (mode == CreateAdditionalModel) setAsMain = false; | 
| Chris@0 | 2421     else if (mode == AskUser) { | 
| Chris@0 | 2422         if (m_document->getMainModel()) { | 
| Chris@0 | 2423 | 
| Chris@0 | 2424             QStringList items; | 
| Chris@0 | 2425             items << tr("Replace the existing main waveform") | 
| Chris@0 | 2426                   << tr("Load this file into a new waveform pane"); | 
| Chris@0 | 2427 | 
| Chris@0 | 2428             bool ok = false; | 
| Chris@0 | 2429             QString item = ListInputDialog::getItem | 
| Chris@0 | 2430                 (this, tr("Select target for import"), | 
| Chris@0 | 2431                  tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), | 
| Chris@0 | 2432                  items, prevSetAsMain ? 0 : 1, &ok); | 
| Chris@0 | 2433 | 
| Chris@0 | 2434             if (!ok || item.isEmpty()) { | 
| Chris@0 | 2435                 delete newModel; | 
| Chris@70 | 2436                 m_openingAudioFile = false; | 
| Chris@82 | 2437                 return FileOpenCancelled; | 
| Chris@0 | 2438             } | 
| Chris@0 | 2439 | 
| Chris@0 | 2440             setAsMain = (item == items[0]); | 
| Chris@0 | 2441             prevSetAsMain = setAsMain; | 
| Chris@0 | 2442         } | 
| Chris@0 | 2443     } | 
| Chris@0 | 2444 | 
| Chris@0 | 2445     if (setAsMain) { | 
| Chris@0 | 2446 | 
| Chris@0 | 2447         Model *prevMain = getMainModel(); | 
| Chris@118 | 2448         if (prevMain) { | 
| Chris@118 | 2449             m_playSource->removeModel(prevMain); | 
| Chris@118 | 2450             PlayParameterRepository::getInstance()->removeModel(prevMain); | 
| Chris@118 | 2451         } | 
| Chris@118 | 2452 | 
| Chris@118 | 2453         PlayParameterRepository::getInstance()->addModel(newModel); | 
| Chris@0 | 2454 | 
| Chris@0 | 2455 	m_document->setMainModel(newModel); | 
| Chris@0 | 2456 	setupMenus(); | 
| Chris@0 | 2457 | 
| Chris@0 | 2458 	if (m_sessionFile == "") { | 
| Chris@0 | 2459 	    setWindowTitle(tr("Sonic Visualiser: %1") | 
| Chris@86 | 2460 			   .arg(QFileInfo(location).fileName())); | 
| Chris@0 | 2461 	    CommandHistory::getInstance()->clear(); | 
| Chris@0 | 2462 	    CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 2463 	    m_documentModified = false; | 
| Chris@0 | 2464 	} else { | 
| Chris@0 | 2465 	    setWindowTitle(tr("Sonic Visualiser: %1 [%2]") | 
| Chris@0 | 2466 			   .arg(QFileInfo(m_sessionFile).fileName()) | 
| Chris@86 | 2467 			   .arg(QFileInfo(location).fileName())); | 
| Chris@0 | 2468 	    if (m_documentModified) { | 
| Chris@0 | 2469 		m_documentModified = false; | 
| Chris@0 | 2470 		documentModified(); // so as to restore "(modified)" window title | 
| Chris@0 | 2471 	    } | 
| Chris@0 | 2472 	} | 
| Chris@0 | 2473 | 
| Chris@86 | 2474         if (realFile) m_audioFile = path; | 
| Chris@0 | 2475 | 
| Chris@0 | 2476     } else { // !setAsMain | 
| Chris@0 | 2477 | 
| Chris@0 | 2478 	CommandHistory::getInstance()->startCompoundOperation | 
| Chris@86 | 2479 	    (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true); | 
| Chris@0 | 2480 | 
| Chris@0 | 2481 	m_document->addImportedModel(newModel); | 
| Chris@0 | 2482 | 
| Chris@0 | 2483 	AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@0 | 2484 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@0 | 2485 | 
| Chris@0 | 2486 	Pane *pane = command->getPane(); | 
| Chris@0 | 2487 | 
| Chris@0 | 2488 	if (!m_timeRulerLayer) { | 
| Chris@0 | 2489 	    m_timeRulerLayer = m_document->createMainModelLayer | 
| Chris@0 | 2490 		(LayerFactory::TimeRuler); | 
| Chris@0 | 2491 	} | 
| Chris@0 | 2492 | 
| Chris@0 | 2493 	m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@0 | 2494 | 
| Chris@0 | 2495 	Layer *newLayer = m_document->createImportedLayer(newModel); | 
| Chris@0 | 2496 | 
| Chris@0 | 2497 	if (newLayer) { | 
| Chris@0 | 2498 	    m_document->addLayerToView(pane, newLayer); | 
| Chris@0 | 2499 	} | 
| Chris@0 | 2500 | 
| Chris@0 | 2501 	CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@0 | 2502     } | 
| Chris@0 | 2503 | 
| Chris@0 | 2504     updateMenuStates(); | 
| Chris@86 | 2505     m_recentFiles.addFile(location); | 
| Chris@86 | 2506     if (realFile) { | 
| Chris@88 | 2507         registerLastOpenedFilePath(FileFinder::AudioFile, path); // for file dialog | 
| Chris@86 | 2508     } | 
| Chris@70 | 2509     m_openingAudioFile = false; | 
| Chris@0 | 2510 | 
| Chris@82 | 2511     return FileOpenSucceeded; | 
| Chris@0 | 2512 } | 
| Chris@0 | 2513 | 
| Chris@0 | 2514 void | 
| Chris@0 | 2515 MainWindow::createPlayTarget() | 
| Chris@0 | 2516 { | 
| Chris@0 | 2517     if (m_playTarget) return; | 
| Chris@0 | 2518 | 
| Chris@0 | 2519     m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); | 
| Chris@0 | 2520     if (!m_playTarget) { | 
| Chris@0 | 2521 	QMessageBox::warning | 
| Chris@0 | 2522 	    (this, tr("Couldn't open audio device"), | 
| Chris@0 | 2523 	     tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"), | 
| Chris@0 | 2524 	     QMessageBox::Ok, 0); | 
| Chris@0 | 2525     } | 
| Chris@0 | 2526     connect(m_fader, SIGNAL(valueChanged(float)), | 
| Chris@0 | 2527 	    m_playTarget, SLOT(setOutputGain(float))); | 
| Chris@0 | 2528 } | 
| Chris@0 | 2529 | 
| Chris@0 | 2530 WaveFileModel * | 
| Chris@0 | 2531 MainWindow::getMainModel() | 
| Chris@0 | 2532 { | 
| Chris@0 | 2533     if (!m_document) return 0; | 
| Chris@0 | 2534     return m_document->getMainModel(); | 
| Chris@0 | 2535 } | 
| Chris@0 | 2536 | 
| Chris@116 | 2537 const WaveFileModel * | 
| Chris@116 | 2538 MainWindow::getMainModel() const | 
| Chris@116 | 2539 { | 
| Chris@116 | 2540     if (!m_document) return 0; | 
| Chris@116 | 2541     return m_document->getMainModel(); | 
| Chris@116 | 2542 } | 
| Chris@116 | 2543 | 
| Chris@0 | 2544 void | 
| Chris@0 | 2545 MainWindow::newSession() | 
| Chris@0 | 2546 { | 
| Chris@0 | 2547     if (!checkSaveModified()) return; | 
| Chris@0 | 2548 | 
| Chris@0 | 2549     closeSession(); | 
| Chris@0 | 2550     createDocument(); | 
| Chris@0 | 2551 | 
| Chris@0 | 2552     Pane *pane = m_paneStack->addPane(); | 
| Chris@0 | 2553 | 
| Chris@90 | 2554     connect(pane, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@116 | 2555             this, SLOT(contextHelpChanged(const QString &))); | 
| Chris@90 | 2556 | 
| Chris@0 | 2557     if (!m_timeRulerLayer) { | 
| Chris@0 | 2558 	m_timeRulerLayer = m_document->createMainModelLayer | 
| Chris@0 | 2559 	    (LayerFactory::TimeRuler); | 
| Chris@0 | 2560     } | 
| Chris@0 | 2561 | 
| Chris@0 | 2562     m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@0 | 2563 | 
| Chris@0 | 2564     Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); | 
| Chris@0 | 2565     m_document->addLayerToView(pane, waveform); | 
| Chris@0 | 2566 | 
| Chris@65 | 2567     m_overview->registerView(pane); | 
| Chris@0 | 2568 | 
| Chris@0 | 2569     CommandHistory::getInstance()->clear(); | 
| Chris@0 | 2570     CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 2571     documentRestored(); | 
| Chris@0 | 2572     updateMenuStates(); | 
| Chris@0 | 2573 } | 
| Chris@0 | 2574 | 
| Chris@0 | 2575 void | 
| Chris@0 | 2576 MainWindow::createDocument() | 
| Chris@0 | 2577 { | 
| Chris@0 | 2578     m_document = new Document; | 
| Chris@0 | 2579 | 
| Chris@0 | 2580     connect(m_document, SIGNAL(layerAdded(Layer *)), | 
| Chris@0 | 2581 	    this, SLOT(layerAdded(Layer *))); | 
| Chris@0 | 2582     connect(m_document, SIGNAL(layerRemoved(Layer *)), | 
| Chris@0 | 2583 	    this, SLOT(layerRemoved(Layer *))); | 
| Chris@0 | 2584     connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), | 
| Chris@0 | 2585 	    this, SLOT(layerAboutToBeDeleted(Layer *))); | 
| Chris@0 | 2586     connect(m_document, SIGNAL(layerInAView(Layer *, bool)), | 
| Chris@0 | 2587 	    this, SLOT(layerInAView(Layer *, bool))); | 
| Chris@0 | 2588 | 
| Chris@0 | 2589     connect(m_document, SIGNAL(modelAdded(Model *)), | 
| Chris@0 | 2590 	    this, SLOT(modelAdded(Model *))); | 
| Chris@0 | 2591     connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), | 
| Chris@0 | 2592 	    this, SLOT(mainModelChanged(WaveFileModel *))); | 
| Chris@0 | 2593     connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), | 
| Chris@0 | 2594 	    this, SLOT(modelAboutToBeDeleted(Model *))); | 
| Chris@0 | 2595 | 
| Chris@0 | 2596     connect(m_document, SIGNAL(modelGenerationFailed(QString)), | 
| Chris@0 | 2597             this, SLOT(modelGenerationFailed(QString))); | 
| Chris@63 | 2598     connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), | 
| Chris@63 | 2599             this, SLOT(modelRegenerationFailed(QString, QString))); | 
| Chris@0 | 2600 } | 
| Chris@0 | 2601 | 
| Chris@0 | 2602 void | 
| Chris@0 | 2603 MainWindow::closeSession() | 
| Chris@0 | 2604 { | 
| Chris@0 | 2605     if (!checkSaveModified()) return; | 
| Chris@0 | 2606 | 
| Chris@0 | 2607     while (m_paneStack->getPaneCount() > 0) { | 
| Chris@0 | 2608 | 
| Chris@0 | 2609 	Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1); | 
| Chris@0 | 2610 | 
| Chris@0 | 2611 	while (pane->getLayerCount() > 0) { | 
| Chris@0 | 2612 	    m_document->removeLayerFromView | 
| Chris@0 | 2613 		(pane, pane->getLayer(pane->getLayerCount() - 1)); | 
| Chris@0 | 2614 	} | 
| Chris@0 | 2615 | 
| Chris@65 | 2616 	m_overview->unregisterView(pane); | 
| Chris@0 | 2617 	m_paneStack->deletePane(pane); | 
| Chris@0 | 2618     } | 
| Chris@0 | 2619 | 
| Chris@0 | 2620     while (m_paneStack->getHiddenPaneCount() > 0) { | 
| Chris@0 | 2621 | 
| Chris@0 | 2622 	Pane *pane = m_paneStack->getHiddenPane | 
| Chris@0 | 2623 	    (m_paneStack->getHiddenPaneCount() - 1); | 
| Chris@0 | 2624 | 
| Chris@0 | 2625 	while (pane->getLayerCount() > 0) { | 
| Chris@0 | 2626 	    m_document->removeLayerFromView | 
| Chris@0 | 2627 		(pane, pane->getLayer(pane->getLayerCount() - 1)); | 
| Chris@0 | 2628 	} | 
| Chris@0 | 2629 | 
| Chris@65 | 2630 	m_overview->unregisterView(pane); | 
| Chris@0 | 2631 	m_paneStack->deletePane(pane); | 
| Chris@0 | 2632     } | 
| Chris@0 | 2633 | 
| Chris@0 | 2634     delete m_document; | 
| Chris@0 | 2635     m_document = 0; | 
| Chris@0 | 2636     m_viewManager->clearSelections(); | 
| Chris@0 | 2637     m_timeRulerLayer = 0; // document owned this | 
| Chris@0 | 2638 | 
| Chris@0 | 2639     m_sessionFile = ""; | 
| Chris@0 | 2640     setWindowTitle(tr("Sonic Visualiser")); | 
| Chris@0 | 2641 | 
| Chris@0 | 2642     CommandHistory::getInstance()->clear(); | 
| Chris@0 | 2643     CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 2644     documentRestored(); | 
| Chris@0 | 2645 } | 
| Chris@0 | 2646 | 
| Chris@0 | 2647 void | 
| Chris@0 | 2648 MainWindow::openSession() | 
| Chris@0 | 2649 { | 
| Chris@0 | 2650     if (!checkSaveModified()) return; | 
| Chris@0 | 2651 | 
| Chris@0 | 2652     QString orig = m_audioFile; | 
| Chris@0 | 2653     if (orig == "") orig = "."; | 
| Chris@0 | 2654     else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | 
| Chris@0 | 2655 | 
| Chris@88 | 2656     QString path = getOpenFileName(FileFinder::SessionFile); | 
| Chris@0 | 2657 | 
| Chris@0 | 2658     if (path.isEmpty()) return; | 
| Chris@0 | 2659 | 
| Chris@82 | 2660     if (openSessionFile(path) == FileOpenFailed) { | 
| Chris@0 | 2661 	QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2662 			      tr("Session file \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2663     } | 
| Chris@0 | 2664 } | 
| Chris@0 | 2665 | 
| Chris@0 | 2666 void | 
| Chris@0 | 2667 MainWindow::openSomething() | 
| Chris@0 | 2668 { | 
| Chris@0 | 2669     QString orig = m_audioFile; | 
| Chris@0 | 2670     if (orig == "") orig = "."; | 
| Chris@0 | 2671     else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | 
| Chris@0 | 2672 | 
| Chris@0 | 2673     bool canImportLayer = (getMainModel() != 0 && | 
| Chris@0 | 2674                            m_paneStack != 0 && | 
| Chris@0 | 2675                            m_paneStack->getCurrentPane() != 0); | 
| Chris@0 | 2676 | 
| Chris@88 | 2677     QString path = getOpenFileName(FileFinder::AnyFile); | 
| Chris@0 | 2678 | 
| Chris@0 | 2679     if (path.isEmpty()) return; | 
| Chris@0 | 2680 | 
| Chris@0 | 2681     if (path.endsWith(".sv")) { | 
| Chris@0 | 2682 | 
| Chris@0 | 2683         if (!checkSaveModified()) return; | 
| Chris@0 | 2684 | 
| Chris@82 | 2685         if (openSessionFile(path) == FileOpenFailed) { | 
| Chris@0 | 2686             QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2687                                   tr("Session file \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2688         } | 
| Chris@0 | 2689 | 
| Chris@0 | 2690     } else { | 
| Chris@0 | 2691 | 
| Chris@82 | 2692         if (openAudioFile(path, AskUser) == FileOpenFailed) { | 
| Chris@82 | 2693 | 
| Chris@82 | 2694             if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { | 
| Chris@0 | 2695 | 
| Chris@0 | 2696                 QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2697                                       tr("File \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2698             } | 
| Chris@0 | 2699         } | 
| Chris@0 | 2700     } | 
| Chris@0 | 2701 } | 
| Chris@0 | 2702 | 
| Chris@0 | 2703 void | 
| Chris@86 | 2704 MainWindow::openLocation() | 
| Chris@86 | 2705 { | 
| Chris@103 | 2706     QSettings settings; | 
| Chris@103 | 2707     settings.beginGroup("MainWindow"); | 
| Chris@103 | 2708     QString lastLocation = settings.value("lastremote", "").toString(); | 
| Chris@103 | 2709 | 
| Chris@86 | 2710     bool ok = false; | 
| Chris@86 | 2711     QString text = QInputDialog::getText | 
| Chris@86 | 2712         (this, tr("Open Location"), | 
| Chris@86 | 2713          tr("Please enter the URL of the location to open:"), | 
| Chris@103 | 2714          QLineEdit::Normal, lastLocation, &ok); | 
| Chris@103 | 2715 | 
| Chris@103 | 2716     if (!ok) return; | 
| Chris@103 | 2717 | 
| Chris@103 | 2718     settings.setValue("lastremote", text); | 
| Chris@103 | 2719 | 
| Chris@103 | 2720     if (text.isEmpty()) return; | 
| Chris@86 | 2721 | 
| Chris@86 | 2722     if (openURL(QUrl(text)) == FileOpenFailed) { | 
| Chris@86 | 2723         QMessageBox::critical(this, tr("Failed to open location"), | 
| Chris@86 | 2724                               tr("URL \"%1\" could not be opened").arg(text)); | 
| Chris@86 | 2725     } | 
| Chris@86 | 2726 } | 
| Chris@86 | 2727 | 
| Chris@86 | 2728 void | 
| Chris@0 | 2729 MainWindow::openRecentFile() | 
| Chris@0 | 2730 { | 
| Chris@0 | 2731     QObject *obj = sender(); | 
| Chris@0 | 2732     QAction *action = dynamic_cast<QAction *>(obj); | 
| Chris@0 | 2733 | 
| Chris@0 | 2734     if (!action) { | 
| Chris@0 | 2735 	std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action" | 
| Chris@0 | 2736 		  << std::endl; | 
| Chris@0 | 2737 	return; | 
| Chris@0 | 2738     } | 
| Chris@0 | 2739 | 
| Chris@0 | 2740     QString path = action->text(); | 
| Chris@0 | 2741     if (path == "") return; | 
| Chris@0 | 2742 | 
| Chris@86 | 2743     QUrl url(path); | 
| Chris@86 | 2744     if (RemoteFile::canHandleScheme(url)) { | 
| Chris@86 | 2745         openURL(url); | 
| Chris@86 | 2746         return; | 
| Chris@86 | 2747     } | 
| Chris@86 | 2748 | 
| Chris@0 | 2749     if (path.endsWith("sv")) { | 
| Chris@0 | 2750 | 
| Chris@103 | 2751         if (!checkSaveModified()) return; | 
| Chris@0 | 2752 | 
| Chris@82 | 2753         if (openSessionFile(path) == FileOpenFailed) { | 
| Chris@0 | 2754             QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2755                                   tr("Session file \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2756         } | 
| Chris@0 | 2757 | 
| Chris@0 | 2758     } else { | 
| Chris@0 | 2759 | 
| Chris@82 | 2760         if (openAudioFile(path, AskUser) == FileOpenFailed) { | 
| Chris@0 | 2761 | 
| Chris@0 | 2762             bool canImportLayer = (getMainModel() != 0 && | 
| Chris@0 | 2763                                    m_paneStack != 0 && | 
| Chris@0 | 2764                                    m_paneStack->getCurrentPane() != 0); | 
| Chris@0 | 2765 | 
| Chris@82 | 2766             if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { | 
| Chris@0 | 2767 | 
| Chris@0 | 2768                 QMessageBox::critical(this, tr("Failed to open file"), | 
| Chris@0 | 2769                                       tr("File \"%1\" could not be opened").arg(path)); | 
| Chris@0 | 2770             } | 
| Chris@0 | 2771         } | 
| Chris@0 | 2772     } | 
| Chris@0 | 2773 } | 
| Chris@0 | 2774 | 
| Chris@82 | 2775 MainWindow::FileOpenStatus | 
| Chris@85 | 2776 MainWindow::openURL(QUrl url) | 
| Chris@85 | 2777 { | 
| Chris@85 | 2778     if (url.scheme().toLower() == "file") { | 
| Chris@85 | 2779         return openSomeFile(url.toLocalFile()); | 
| Chris@86 | 2780     } else if (!RemoteFile::canHandleScheme(url)) { | 
| Chris@85 | 2781         QMessageBox::critical(this, tr("Unsupported scheme in URL"), | 
| Chris@85 | 2782                               tr("The URL scheme \"%1\" is not supported") | 
| Chris@85 | 2783                               .arg(url.scheme())); | 
| Chris@85 | 2784         return FileOpenFailed; | 
| Chris@85 | 2785     } else { | 
| Chris@85 | 2786         RemoteFile rf(url); | 
| Chris@85 | 2787         rf.wait(); | 
| Chris@85 | 2788         if (!rf.isOK()) { | 
| Chris@85 | 2789             QMessageBox::critical(this, tr("File download failed"), | 
| Chris@85 | 2790                                   tr("Failed to download URL \"%1\": %2") | 
| Chris@85 | 2791                                   .arg(url.toString()).arg(rf.getErrorString())); | 
| Chris@85 | 2792             return FileOpenFailed; | 
| Chris@85 | 2793         } | 
| Chris@88 | 2794         FileOpenStatus status; | 
| Chris@88 | 2795         if ((status = openSomeFile(rf.getLocalFilename(), url.toString())) != | 
| Chris@88 | 2796             FileOpenSucceeded) { | 
| Chris@88 | 2797             rf.deleteLocalFile(); | 
| Chris@88 | 2798         } | 
| Chris@88 | 2799         return status; | 
| Chris@85 | 2800     } | 
| Chris@85 | 2801 } | 
| Chris@85 | 2802 | 
| Chris@85 | 2803 MainWindow::FileOpenStatus | 
| Chris@54 | 2804 MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) | 
| Chris@0 | 2805 { | 
| Chris@86 | 2806     return openSomeFile(path, path, mode); | 
| Chris@86 | 2807 } | 
| Chris@86 | 2808 | 
| Chris@86 | 2809 MainWindow::FileOpenStatus | 
| Chris@86 | 2810 MainWindow::openSomeFile(QString path, QString location, | 
| Chris@86 | 2811                          AudioFileOpenMode mode) | 
| Chris@86 | 2812 { | 
| Chris@82 | 2813     FileOpenStatus status; | 
| Chris@82 | 2814 | 
| Chris@85 | 2815     bool canImportLayer = (getMainModel() != 0 && | 
| Chris@85 | 2816                            m_paneStack != 0 && | 
| Chris@85 | 2817                            m_paneStack->getCurrentPane() != 0); | 
| Chris@85 | 2818 | 
| Chris@86 | 2819     if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { | 
| Chris@82 | 2820         return status; | 
| Chris@86 | 2821     } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { | 
| Chris@82 | 2822 	return status; | 
| Chris@85 | 2823     } else if (!canImportLayer) { | 
| Chris@85 | 2824         return FileOpenFailed; | 
| Chris@86 | 2825     } else if ((status = openLayerFile(path, location)) != FileOpenFailed) { | 
| Chris@85 | 2826         return status; | 
| Chris@0 | 2827     } else { | 
| Chris@82 | 2828 	return FileOpenFailed; | 
| Chris@0 | 2829     } | 
| Chris@0 | 2830 } | 
| Chris@0 | 2831 | 
| Chris@82 | 2832 MainWindow::FileOpenStatus | 
| Chris@0 | 2833 MainWindow::openSessionFile(QString path) | 
| Chris@0 | 2834 { | 
| Chris@86 | 2835     return openSessionFile(path, path); | 
| Chris@86 | 2836 } | 
| Chris@86 | 2837 | 
| Chris@86 | 2838 MainWindow::FileOpenStatus | 
| Chris@86 | 2839 MainWindow::openSessionFile(QString path, QString location) | 
| Chris@86 | 2840 { | 
| Chris@0 | 2841     BZipFileDevice bzFile(path); | 
| Chris@0 | 2842     if (!bzFile.open(QIODevice::ReadOnly)) { | 
| Chris@86 | 2843         std::cerr << "Failed to open session file \"" << location.toStdString() | 
| Chris@0 | 2844                   << "\": " << bzFile.errorString().toStdString() << std::endl; | 
| Chris@82 | 2845         return FileOpenFailed; | 
| Chris@0 | 2846     } | 
| Chris@0 | 2847 | 
| Chris@103 | 2848     if (!checkSaveModified()) return FileOpenCancelled; | 
| Chris@103 | 2849 | 
| Chris@0 | 2850     QString error; | 
| Chris@0 | 2851     closeSession(); | 
| Chris@0 | 2852     createDocument(); | 
| Chris@0 | 2853 | 
| Chris@0 | 2854     PaneCallback callback(this); | 
| Chris@0 | 2855     m_viewManager->clearSelections(); | 
| Chris@0 | 2856 | 
| Chris@87 | 2857     SVFileReader reader(m_document, callback, location); | 
| Chris@0 | 2858     QXmlInputSource inputSource(&bzFile); | 
| Chris@0 | 2859     reader.parse(inputSource); | 
| Chris@0 | 2860 | 
| Chris@0 | 2861     if (!reader.isOK()) { | 
| Chris@0 | 2862         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); | 
| Chris@0 | 2863     } | 
| Chris@0 | 2864 | 
| Chris@0 | 2865     bzFile.close(); | 
| Chris@0 | 2866 | 
| Chris@0 | 2867     bool ok = (error == ""); | 
| Chris@86 | 2868 | 
| Chris@86 | 2869     bool realFile = (location == path); | 
| Chris@0 | 2870 | 
| Chris@0 | 2871     if (ok) { | 
| Chris@86 | 2872 | 
| Chris@0 | 2873 	setWindowTitle(tr("Sonic Visualiser: %1") | 
| Chris@86 | 2874 		       .arg(QFileInfo(location).fileName())); | 
| Chris@86 | 2875 | 
| Chris@86 | 2876 	if (realFile) m_sessionFile = path; | 
| Chris@86 | 2877 | 
| Chris@0 | 2878 	setupMenus(); | 
| Chris@0 | 2879 	CommandHistory::getInstance()->clear(); | 
| Chris@0 | 2880 	CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 2881 	m_documentModified = false; | 
| Chris@0 | 2882 	updateMenuStates(); | 
| Chris@86 | 2883 | 
| Chris@86 | 2884         m_recentFiles.addFile(location); | 
| Chris@86 | 2885 | 
| Chris@86 | 2886         if (realFile) { | 
| Chris@88 | 2887             registerLastOpenedFilePath(FileFinder::SessionFile, path); // for file dialog | 
| Chris@86 | 2888         } | 
| Chris@86 | 2889 | 
| Chris@0 | 2890     } else { | 
| Chris@0 | 2891 	setWindowTitle(tr("Sonic Visualiser")); | 
| Chris@0 | 2892     } | 
| Chris@0 | 2893 | 
| Chris@82 | 2894     return ok ? FileOpenSucceeded : FileOpenFailed; | 
| Chris@0 | 2895 } | 
| Chris@0 | 2896 | 
| Chris@0 | 2897 void | 
| Chris@0 | 2898 MainWindow::closeEvent(QCloseEvent *e) | 
| Chris@0 | 2899 { | 
| Chris@118 | 2900     std::cerr << "MainWindow::closeEvent" << std::endl; | 
| Chris@118 | 2901 | 
| Chris@70 | 2902     if (!m_abandoning && !checkSaveModified()) { | 
| Chris@118 | 2903         std::cerr << "Ignoring close event" << std::endl; | 
| Chris@0 | 2904 	e->ignore(); | 
| Chris@0 | 2905 	return; | 
| Chris@0 | 2906     } | 
| Chris@0 | 2907 | 
| Chris@5 | 2908     QSettings settings; | 
| Chris@5 | 2909     settings.beginGroup("MainWindow"); | 
| Chris@5 | 2910     settings.setValue("size", size()); | 
| Chris@5 | 2911     settings.setValue("position", pos()); | 
| Chris@5 | 2912     settings.endGroup(); | 
| Chris@5 | 2913 | 
| Chris@0 | 2914     e->accept(); | 
| Chris@0 | 2915     return; | 
| Chris@0 | 2916 } | 
| Chris@0 | 2917 | 
| Chris@0 | 2918 bool | 
| Chris@11 | 2919 MainWindow::commitData(bool mayAskUser) | 
| Chris@11 | 2920 { | 
| Chris@11 | 2921     if (mayAskUser) { | 
| Chris@11 | 2922         return checkSaveModified(); | 
| Chris@11 | 2923     } else { | 
| Chris@11 | 2924         if (!m_documentModified) return true; | 
| Chris@11 | 2925 | 
| Chris@11 | 2926         // If we can't check with the user first, then we can't save | 
| Chris@11 | 2927         // to the original session file (even if we have it) -- have | 
| Chris@11 | 2928         // to use a temporary file | 
| Chris@11 | 2929 | 
| Chris@11 | 2930         QString svDirBase = ".sv1"; | 
| Chris@11 | 2931         QString svDir = QDir::home().filePath(svDirBase); | 
| Chris@11 | 2932 | 
| Chris@11 | 2933         if (!QFileInfo(svDir).exists()) { | 
| Chris@11 | 2934             if (!QDir::home().mkdir(svDirBase)) return false; | 
| Chris@11 | 2935         } else { | 
| Chris@11 | 2936             if (!QFileInfo(svDir).isDir()) return false; | 
| Chris@11 | 2937         } | 
| Chris@11 | 2938 | 
| Chris@11 | 2939         // This name doesn't have to be unguessable | 
| Chris@93 | 2940 #ifndef _WIN32 | 
| Chris@11 | 2941         QString fname = QString("tmp-%1-%2.sv") | 
| Chris@11 | 2942             .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")) | 
| Chris@11 | 2943             .arg(QProcess().pid()); | 
| Chris@93 | 2944 #else | 
| Chris@93 | 2945         QString fname = QString("tmp-%1.sv") | 
| Chris@93 | 2946             .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")); | 
| Chris@93 | 2947 #endif | 
| Chris@11 | 2948         QString fpath = QDir(svDir).filePath(fname); | 
| Chris@11 | 2949         if (saveSessionFile(fpath)) { | 
| Chris@34 | 2950             m_recentFiles.addFile(fpath); | 
| Chris@11 | 2951             return true; | 
| Chris@11 | 2952         } else { | 
| Chris@11 | 2953             return false; | 
| Chris@11 | 2954         } | 
| Chris@11 | 2955     } | 
| Chris@11 | 2956 } | 
| Chris@11 | 2957 | 
| Chris@11 | 2958 bool | 
| Chris@0 | 2959 MainWindow::checkSaveModified() | 
| Chris@0 | 2960 { | 
| Chris@0 | 2961     // Called before some destructive operation (e.g. new session, | 
| Chris@0 | 2962     // exit program).  Return true if we can safely proceed, false to | 
| Chris@0 | 2963     // cancel. | 
| Chris@0 | 2964 | 
| Chris@0 | 2965     if (!m_documentModified) return true; | 
| Chris@0 | 2966 | 
| Chris@0 | 2967     int button = | 
| Chris@0 | 2968 	QMessageBox::warning(this, | 
| Chris@0 | 2969 			     tr("Session modified"), | 
| Chris@0 | 2970 			     tr("The current session has been modified.\nDo you want to save it?"), | 
| Chris@0 | 2971 			     QMessageBox::Yes, | 
| Chris@0 | 2972 			     QMessageBox::No, | 
| Chris@0 | 2973 			     QMessageBox::Cancel); | 
| Chris@0 | 2974 | 
| Chris@0 | 2975     if (button == QMessageBox::Yes) { | 
| Chris@0 | 2976 	saveSession(); | 
| Chris@0 | 2977 	if (m_documentModified) { // save failed -- don't proceed! | 
| Chris@0 | 2978 	    return false; | 
| Chris@0 | 2979 	} else { | 
| Chris@0 | 2980             return true; // saved, so it's safe to continue now | 
| Chris@0 | 2981         } | 
| Chris@0 | 2982     } else if (button == QMessageBox::No) { | 
| Chris@0 | 2983 	m_documentModified = false; // so we know to abandon it | 
| Chris@0 | 2984 	return true; | 
| Chris@0 | 2985     } | 
| Chris@0 | 2986 | 
| Chris@0 | 2987     // else cancel | 
| Chris@0 | 2988     return false; | 
| Chris@0 | 2989 } | 
| Chris@0 | 2990 | 
| Chris@0 | 2991 void | 
| Chris@0 | 2992 MainWindow::saveSession() | 
| Chris@0 | 2993 { | 
| Chris@0 | 2994     if (m_sessionFile != "") { | 
| Chris@0 | 2995 	if (!saveSessionFile(m_sessionFile)) { | 
| Chris@0 | 2996 	    QMessageBox::critical(this, tr("Failed to save file"), | 
| Chris@0 | 2997 				  tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); | 
| Chris@0 | 2998 	} else { | 
| Chris@0 | 2999 	    CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 3000 	    documentRestored(); | 
| Chris@0 | 3001 	} | 
| Chris@0 | 3002     } else { | 
| Chris@0 | 3003 	saveSessionAs(); | 
| Chris@0 | 3004     } | 
| Chris@0 | 3005 } | 
| Chris@0 | 3006 | 
| Chris@0 | 3007 void | 
| Chris@0 | 3008 MainWindow::saveSessionAs() | 
| Chris@0 | 3009 { | 
| Chris@0 | 3010     QString orig = m_audioFile; | 
| Chris@0 | 3011     if (orig == "") orig = "."; | 
| Chris@0 | 3012     else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | 
| Chris@0 | 3013 | 
| Chris@88 | 3014     QString path = getSaveFileName(FileFinder::SessionFile); | 
| Chris@81 | 3015 | 
| Chris@81 | 3016     if (path == "") return; | 
| Chris@0 | 3017 | 
| Chris@0 | 3018     if (!saveSessionFile(path)) { | 
| Chris@0 | 3019 	QMessageBox::critical(this, tr("Failed to save file"), | 
| Chris@79 | 3020 			      tr("Session file \"%1\" could not be saved.").arg(path)); | 
| Chris@0 | 3021     } else { | 
| Chris@0 | 3022 	setWindowTitle(tr("Sonic Visualiser: %1") | 
| Chris@0 | 3023 		       .arg(QFileInfo(path).fileName())); | 
| Chris@0 | 3024 	m_sessionFile = path; | 
| Chris@0 | 3025 	CommandHistory::getInstance()->documentSaved(); | 
| Chris@0 | 3026 	documentRestored(); | 
| Chris@34 | 3027         m_recentFiles.addFile(path); | 
| Chris@0 | 3028     } | 
| Chris@0 | 3029 } | 
| Chris@0 | 3030 | 
| Chris@0 | 3031 bool | 
| Chris@0 | 3032 MainWindow::saveSessionFile(QString path) | 
| Chris@0 | 3033 { | 
| Chris@0 | 3034     BZipFileDevice bzFile(path); | 
| Chris@0 | 3035     if (!bzFile.open(QIODevice::WriteOnly)) { | 
| Chris@0 | 3036         std::cerr << "Failed to open session file \"" << path.toStdString() | 
| Chris@0 | 3037                   << "\" for writing: " | 
| Chris@0 | 3038                   << bzFile.errorString().toStdString() << std::endl; | 
| Chris@0 | 3039         return false; | 
| Chris@0 | 3040     } | 
| Chris@0 | 3041 | 
| Chris@0 | 3042     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); | 
| Chris@0 | 3043 | 
| Chris@0 | 3044     QTextStream out(&bzFile); | 
| Chris@0 | 3045     toXml(out); | 
| Chris@0 | 3046     out.flush(); | 
| Chris@0 | 3047 | 
| Chris@0 | 3048     QApplication::restoreOverrideCursor(); | 
| Chris@0 | 3049 | 
| Chris@84 | 3050     if (!bzFile.isOK()) { | 
| Chris@0 | 3051 	QMessageBox::critical(this, tr("Failed to write file"), | 
| Chris@0 | 3052 			      tr("Failed to write to file \"%1\": %2") | 
| Chris@0 | 3053 			      .arg(path).arg(bzFile.errorString())); | 
| Chris@0 | 3054         bzFile.close(); | 
| Chris@0 | 3055 	return false; | 
| Chris@0 | 3056     } | 
| Chris@0 | 3057 | 
| Chris@0 | 3058     bzFile.close(); | 
| Chris@0 | 3059     return true; | 
| Chris@0 | 3060 } | 
| Chris@0 | 3061 | 
| Chris@0 | 3062 void | 
| Chris@0 | 3063 MainWindow::toXml(QTextStream &out) | 
| Chris@0 | 3064 { | 
| Chris@0 | 3065     QString indent("  "); | 
| Chris@0 | 3066 | 
| Chris@0 | 3067     out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | 
| Chris@0 | 3068     out << "<!DOCTYPE sonic-visualiser>\n"; | 
| Chris@0 | 3069     out << "<sv>\n"; | 
| Chris@0 | 3070 | 
| Chris@0 | 3071     m_document->toXml(out, "", ""); | 
| Chris@0 | 3072 | 
| Chris@0 | 3073     out << "<display>\n"; | 
| Chris@0 | 3074 | 
| Chris@0 | 3075     out << QString("  <window width=\"%1\" height=\"%2\"/>\n") | 
| Chris@0 | 3076 	.arg(width()).arg(height()); | 
| Chris@0 | 3077 | 
| Chris@0 | 3078     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@0 | 3079 | 
| Chris@0 | 3080 	Pane *pane = m_paneStack->getPane(i); | 
| Chris@0 | 3081 | 
| Chris@0 | 3082 	if (pane) { | 
| Chris@0 | 3083             pane->toXml(out, indent); | 
| Chris@0 | 3084 	} | 
| Chris@0 | 3085     } | 
| Chris@0 | 3086 | 
| Chris@0 | 3087     out << "</display>\n"; | 
| Chris@0 | 3088 | 
| Chris@0 | 3089     m_viewManager->getSelection().toXml(out); | 
| Chris@0 | 3090 | 
| Chris@0 | 3091     out << "</sv>\n"; | 
| Chris@0 | 3092 } | 
| Chris@0 | 3093 | 
| Chris@0 | 3094 Pane * | 
| Chris@0 | 3095 MainWindow::addPaneToStack() | 
| Chris@0 | 3096 { | 
| Chris@0 | 3097     AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@0 | 3098     CommandHistory::getInstance()->addCommand(command); | 
| Chris@0 | 3099     return command->getPane(); | 
| Chris@0 | 3100 } | 
| Chris@0 | 3101 | 
| Chris@0 | 3102 void | 
| Chris@0 | 3103 MainWindow::zoomIn() | 
| Chris@0 | 3104 { | 
| Chris@0 | 3105     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3106     if (currentPane) currentPane->zoom(true); | 
| Chris@0 | 3107 } | 
| Chris@0 | 3108 | 
| Chris@0 | 3109 void | 
| Chris@0 | 3110 MainWindow::zoomOut() | 
| Chris@0 | 3111 { | 
| Chris@0 | 3112     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3113     if (currentPane) currentPane->zoom(false); | 
| Chris@0 | 3114 } | 
| Chris@0 | 3115 | 
| Chris@0 | 3116 void | 
| Chris@0 | 3117 MainWindow::zoomToFit() | 
| Chris@0 | 3118 { | 
| Chris@0 | 3119     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3120     if (!currentPane) return; | 
| Chris@0 | 3121 | 
| Chris@0 | 3122     Model *model = getMainModel(); | 
| Chris@0 | 3123     if (!model) return; | 
| Chris@0 | 3124 | 
| Chris@0 | 3125     size_t start = model->getStartFrame(); | 
| Chris@0 | 3126     size_t end = model->getEndFrame(); | 
| Chris@0 | 3127     size_t pixels = currentPane->width(); | 
| Chris@0 | 3128     size_t zoomLevel = (end - start) / pixels; | 
| Chris@0 | 3129 | 
| Chris@0 | 3130     currentPane->setZoomLevel(zoomLevel); | 
| Chris@0 | 3131     currentPane->setStartFrame(start); | 
| Chris@0 | 3132 } | 
| Chris@0 | 3133 | 
| Chris@0 | 3134 void | 
| Chris@0 | 3135 MainWindow::zoomDefault() | 
| Chris@0 | 3136 { | 
| Chris@0 | 3137     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3138     if (currentPane) currentPane->setZoomLevel(1024); | 
| Chris@0 | 3139 } | 
| Chris@0 | 3140 | 
| Chris@0 | 3141 void | 
| Chris@0 | 3142 MainWindow::scrollLeft() | 
| Chris@0 | 3143 { | 
| Chris@0 | 3144     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3145     if (currentPane) currentPane->scroll(false, false); | 
| Chris@0 | 3146 } | 
| Chris@0 | 3147 | 
| Chris@0 | 3148 void | 
| Chris@0 | 3149 MainWindow::jumpLeft() | 
| Chris@0 | 3150 { | 
| Chris@0 | 3151     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3152     if (currentPane) currentPane->scroll(false, true); | 
| Chris@0 | 3153 } | 
| Chris@0 | 3154 | 
| Chris@0 | 3155 void | 
| Chris@0 | 3156 MainWindow::scrollRight() | 
| Chris@0 | 3157 { | 
| Chris@0 | 3158     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3159     if (currentPane) currentPane->scroll(true, false); | 
| Chris@0 | 3160 } | 
| Chris@0 | 3161 | 
| Chris@0 | 3162 void | 
| Chris@0 | 3163 MainWindow::jumpRight() | 
| Chris@0 | 3164 { | 
| Chris@0 | 3165     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3166     if (currentPane) currentPane->scroll(true, true); | 
| Chris@0 | 3167 } | 
| Chris@0 | 3168 | 
| Chris@0 | 3169 void | 
| Chris@0 | 3170 MainWindow::showNoOverlays() | 
| Chris@0 | 3171 { | 
| Chris@0 | 3172     m_viewManager->setOverlayMode(ViewManager::NoOverlays); | 
| Chris@0 | 3173 } | 
| Chris@0 | 3174 | 
| Chris@0 | 3175 void | 
| Chris@90 | 3176 MainWindow::showMinimalOverlays() | 
| Chris@0 | 3177 { | 
| Chris@90 | 3178     m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); | 
| Chris@0 | 3179 } | 
| Chris@0 | 3180 | 
| Chris@0 | 3181 void | 
| Chris@90 | 3182 MainWindow::showStandardOverlays() | 
| Chris@90 | 3183 { | 
| Chris@90 | 3184     m_viewManager->setOverlayMode(ViewManager::StandardOverlays); | 
| Chris@90 | 3185 } | 
| Chris@90 | 3186 | 
| Chris@90 | 3187 void | 
| Chris@90 | 3188 MainWindow::showAllOverlays() | 
| Chris@0 | 3189 { | 
| Chris@0 | 3190     m_viewManager->setOverlayMode(ViewManager::AllOverlays); | 
| Chris@0 | 3191 } | 
| Chris@0 | 3192 | 
| Chris@0 | 3193 void | 
| Chris@7 | 3194 MainWindow::toggleZoomWheels() | 
| Chris@7 | 3195 { | 
| Chris@7 | 3196     if (m_viewManager->getZoomWheelsEnabled()) { | 
| Chris@7 | 3197         m_viewManager->setZoomWheelsEnabled(false); | 
| Chris@7 | 3198     } else { | 
| Chris@7 | 3199         m_viewManager->setZoomWheelsEnabled(true); | 
| Chris@7 | 3200     } | 
| Chris@7 | 3201 } | 
| Chris@7 | 3202 | 
| Chris@7 | 3203 void | 
| Chris@72 | 3204 MainWindow::togglePropertyBoxes() | 
| Chris@72 | 3205 { | 
| Chris@72 | 3206     if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { | 
| Chris@72 | 3207         if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@72 | 3208             Preferences::VerticallyStacked) { | 
| Chris@72 | 3209             m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@72 | 3210         } else { | 
| Chris@72 | 3211             m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@72 | 3212         } | 
| Chris@72 | 3213     } else { | 
| Chris@72 | 3214         m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); | 
| Chris@72 | 3215     } | 
| Chris@72 | 3216 } | 
| Chris@72 | 3217 | 
| Chris@72 | 3218 void | 
| Chris@90 | 3219 MainWindow::toggleStatusBar() | 
| Chris@90 | 3220 { | 
| Chris@90 | 3221     QSettings settings; | 
| Chris@90 | 3222     settings.beginGroup("MainWindow"); | 
| Chris@90 | 3223     bool sb = settings.value("showstatusbar", true).toBool(); | 
| Chris@90 | 3224 | 
| Chris@90 | 3225     if (sb) { | 
| Chris@90 | 3226         statusBar()->hide(); | 
| Chris@90 | 3227     } else { | 
| Chris@90 | 3228         statusBar()->show(); | 
| Chris@90 | 3229     } | 
| Chris@90 | 3230 | 
| Chris@90 | 3231     settings.setValue("showstatusbar", !sb); | 
| Chris@90 | 3232 | 
| Chris@90 | 3233     settings.endGroup(); | 
| Chris@90 | 3234 } | 
| Chris@90 | 3235 | 
| Chris@90 | 3236 void | 
| Chris@72 | 3237 MainWindow::preferenceChanged(PropertyContainer::PropertyName name) | 
| Chris@72 | 3238 { | 
| Chris@72 | 3239     if (name == "Property Box Layout") { | 
| Chris@72 | 3240         if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { | 
| Chris@72 | 3241             if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@72 | 3242                 Preferences::VerticallyStacked) { | 
| Chris@72 | 3243                 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@72 | 3244             } else { | 
| Chris@72 | 3245                 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@72 | 3246             } | 
| Chris@72 | 3247         } | 
| Chris@72 | 3248     } | 
| Chris@72 | 3249 } | 
| Chris@72 | 3250 | 
| Chris@72 | 3251 void | 
| Chris@0 | 3252 MainWindow::play() | 
| Chris@0 | 3253 { | 
| Chris@0 | 3254     if (m_playSource->isPlaying()) { | 
| Chris@116 | 3255         stop(); | 
| Chris@0 | 3256     } else { | 
| Chris@116 | 3257         playbackFrameChanged(m_viewManager->getPlaybackFrame()); | 
| Chris@0 | 3258 	m_playSource->play(m_viewManager->getPlaybackFrame()); | 
| Chris@0 | 3259     } | 
| Chris@0 | 3260 } | 
| Chris@0 | 3261 | 
| Chris@0 | 3262 void | 
| Chris@0 | 3263 MainWindow::ffwd() | 
| Chris@0 | 3264 { | 
| Chris@0 | 3265     if (!getMainModel()) return; | 
| Chris@0 | 3266 | 
| Chris@0 | 3267     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@0 | 3268     ++frame; | 
| Chris@0 | 3269 | 
| Chris@0 | 3270     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3271     if (!pane) return; | 
| Chris@0 | 3272 | 
| Chris@0 | 3273     Layer *layer = pane->getSelectedLayer(); | 
| Chris@0 | 3274 | 
| Chris@0 | 3275     if (!dynamic_cast<TimeInstantLayer *>(layer) && | 
| Chris@0 | 3276         !dynamic_cast<TimeValueLayer *>(layer)) return; | 
| Chris@0 | 3277 | 
| Chris@0 | 3278     size_t resolution = 0; | 
| Chris@0 | 3279     if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapRight)) { | 
| Chris@0 | 3280         frame = getMainModel()->getEndFrame(); | 
| Chris@0 | 3281     } | 
| Chris@0 | 3282 | 
| Chris@0 | 3283     m_viewManager->setPlaybackFrame(frame); | 
| Chris@0 | 3284 } | 
| Chris@0 | 3285 | 
| Chris@0 | 3286 void | 
| Chris@0 | 3287 MainWindow::ffwdEnd() | 
| Chris@0 | 3288 { | 
| Chris@0 | 3289     if (!getMainModel()) return; | 
| Chris@0 | 3290     m_viewManager->setPlaybackFrame(getMainModel()->getEndFrame()); | 
| Chris@0 | 3291 } | 
| Chris@0 | 3292 | 
| Chris@0 | 3293 void | 
| Chris@0 | 3294 MainWindow::rewind() | 
| Chris@0 | 3295 { | 
| Chris@0 | 3296     if (!getMainModel()) return; | 
| Chris@0 | 3297 | 
| Chris@0 | 3298     int frame = m_viewManager->getPlaybackFrame(); | 
| Chris@0 | 3299     if (frame > 0) --frame; | 
| Chris@0 | 3300 | 
| Chris@0 | 3301     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3302     if (!pane) return; | 
| Chris@0 | 3303 | 
| Chris@0 | 3304     Layer *layer = pane->getSelectedLayer(); | 
| Chris@0 | 3305 | 
| Chris@0 | 3306     if (!dynamic_cast<TimeInstantLayer *>(layer) && | 
| Chris@0 | 3307         !dynamic_cast<TimeValueLayer *>(layer)) return; | 
| Chris@0 | 3308 | 
| Chris@0 | 3309     size_t resolution = 0; | 
| Chris@0 | 3310     if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapLeft)) { | 
| Chris@0 | 3311         frame = getMainModel()->getEndFrame(); | 
| Chris@0 | 3312     } | 
| Chris@0 | 3313 | 
| Chris@0 | 3314     m_viewManager->setPlaybackFrame(frame); | 
| Chris@0 | 3315 } | 
| Chris@0 | 3316 | 
| Chris@0 | 3317 void | 
| Chris@0 | 3318 MainWindow::rewindStart() | 
| Chris@0 | 3319 { | 
| Chris@0 | 3320     if (!getMainModel()) return; | 
| Chris@0 | 3321     m_viewManager->setPlaybackFrame(getMainModel()->getStartFrame()); | 
| Chris@0 | 3322 } | 
| Chris@0 | 3323 | 
| Chris@0 | 3324 void | 
| Chris@0 | 3325 MainWindow::stop() | 
| Chris@0 | 3326 { | 
| Chris@0 | 3327     m_playSource->stop(); | 
| Chris@116 | 3328 | 
| Chris@116 | 3329     if (m_paneStack && m_paneStack->getCurrentPane()) { | 
| Chris@116 | 3330         updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); | 
| Chris@116 | 3331     } else { | 
| Chris@116 | 3332         m_myStatusMessage = ""; | 
| Chris@116 | 3333         statusBar()->showMessage(""); | 
| Chris@116 | 3334     } | 
| Chris@0 | 3335 } | 
| Chris@0 | 3336 | 
| Chris@0 | 3337 void | 
| Chris@0 | 3338 MainWindow::addPane() | 
| Chris@0 | 3339 { | 
| Chris@0 | 3340     QObject *s = sender(); | 
| Chris@0 | 3341     QAction *action = dynamic_cast<QAction *>(s); | 
| Chris@0 | 3342 | 
| Chris@0 | 3343     if (!action) { | 
| Chris@0 | 3344 	std::cerr << "WARNING: MainWindow::addPane: sender is not an action" | 
| Chris@0 | 3345 		  << std::endl; | 
| Chris@0 | 3346 	return; | 
| Chris@0 | 3347     } | 
| Chris@0 | 3348 | 
| Chris@0 | 3349     PaneActionMap::iterator i = m_paneActions.find(action); | 
| Chris@0 | 3350 | 
| Chris@0 | 3351     if (i == m_paneActions.end()) { | 
| Chris@0 | 3352 	std::cerr << "WARNING: MainWindow::addPane: unknown action " | 
| Chris@0 | 3353 		  << action->objectName().toStdString() << std::endl; | 
| Chris@0 | 3354 	return; | 
| Chris@0 | 3355     } | 
| Chris@0 | 3356 | 
| Chris@69 | 3357     addPane(i->second, action->text()); | 
| Chris@69 | 3358 } | 
| Chris@69 | 3359 | 
| Chris@69 | 3360 void | 
| Chris@69 | 3361 MainWindow::addPane(const PaneConfiguration &configuration, QString text) | 
| Chris@69 | 3362 { | 
| Chris@69 | 3363     CommandHistory::getInstance()->startCompoundOperation(text, true); | 
| Chris@0 | 3364 | 
| Chris@0 | 3365     AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@0 | 3366     CommandHistory::getInstance()->addCommand(command); | 
| Chris@0 | 3367 | 
| Chris@0 | 3368     Pane *pane = command->getPane(); | 
| Chris@0 | 3369 | 
| Chris@69 | 3370     if (configuration.layer == LayerFactory::Spectrum) { | 
| Chris@109 | 3371         pane->setPlaybackFollow(PlaybackScrollContinuous); | 
| Chris@110 | 3372         pane->setFollowGlobalZoom(false); | 
| Chris@112 | 3373         pane->setZoomLevel(512); | 
| Chris@7 | 3374     } | 
| Chris@7 | 3375 | 
| Chris@69 | 3376     if (configuration.layer != LayerFactory::TimeRuler && | 
| Chris@69 | 3377         configuration.layer != LayerFactory::Spectrum) { | 
| Chris@8 | 3378 | 
| Chris@0 | 3379 	if (!m_timeRulerLayer) { | 
| Chris@0 | 3380 //	    std::cerr << "no time ruler layer, creating one" << std::endl; | 
| Chris@0 | 3381 	    m_timeRulerLayer = m_document->createMainModelLayer | 
| Chris@0 | 3382 		(LayerFactory::TimeRuler); | 
| Chris@0 | 3383 	} | 
| Chris@0 | 3384 | 
| Chris@0 | 3385 //	std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl; | 
| Chris@0 | 3386 | 
| Chris@0 | 3387 	m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@0 | 3388     } | 
| Chris@0 | 3389 | 
| Chris@69 | 3390     Layer *newLayer = m_document->createLayer(configuration.layer); | 
| Chris@69 | 3391 | 
| Chris@69 | 3392     Model *suggestedModel = configuration.sourceModel; | 
| Chris@66 | 3393     Model *model = 0; | 
| Chris@66 | 3394 | 
| Chris@66 | 3395     if (suggestedModel) { | 
| Chris@66 | 3396 | 
| Chris@66 | 3397         // check its validity | 
| Chris@66 | 3398         std::vector<Model *> inputModels = m_document->getTransformInputModels(); | 
| Chris@66 | 3399         for (size_t j = 0; j < inputModels.size(); ++j) { | 
| Chris@66 | 3400             if (inputModels[j] == suggestedModel) { | 
| Chris@66 | 3401                 model = suggestedModel; | 
| Chris@66 | 3402                 break; | 
| Chris@66 | 3403             } | 
| Chris@66 | 3404         } | 
| Chris@66 | 3405 | 
| Chris@66 | 3406         if (!model) { | 
| Chris@66 | 3407             std::cerr << "WARNING: Model " << (void *)suggestedModel | 
| Chris@66 | 3408                       << " appears in pane action map, but is not reported " | 
| Chris@66 | 3409                       << "by document as a valid transform source" << std::endl; | 
| Chris@66 | 3410         } | 
| Chris@66 | 3411     } | 
| Chris@66 | 3412 | 
| Chris@66 | 3413     if (!model) model = m_document->getMainModel(); | 
| Chris@66 | 3414 | 
| Chris@66 | 3415     m_document->setModel(newLayer, model); | 
| Chris@66 | 3416 | 
| Chris@69 | 3417     m_document->setChannel(newLayer, configuration.channel); | 
| Chris@0 | 3418     m_document->addLayerToView(pane, newLayer); | 
| Chris@0 | 3419 | 
| Chris@0 | 3420     m_paneStack->setCurrentPane(pane); | 
| Chris@70 | 3421     m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@0 | 3422 | 
| Chris@130 | 3423 //    std::cerr << "MainWindow::addPane: global centre frame is " | 
| Chris@130 | 3424 //              << m_viewManager->getGlobalCentreFrame() << std::endl; | 
| Chris@130 | 3425 //    pane->setCentreFrame(m_viewManager->getGlobalCentreFrame()); | 
| Chris@73 | 3426 | 
| Chris@0 | 3427     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@0 | 3428 | 
| Chris@0 | 3429     updateMenuStates(); | 
| Chris@0 | 3430 } | 
| Chris@0 | 3431 | 
| Chris@0 | 3432 MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) : | 
| Chris@0 | 3433     m_mw(mw), | 
| Chris@0 | 3434     m_pane(0), | 
| Chris@0 | 3435     m_prevCurrentPane(0), | 
| Chris@0 | 3436     m_added(false) | 
| Chris@0 | 3437 { | 
| Chris@0 | 3438 } | 
| Chris@0 | 3439 | 
| Chris@0 | 3440 MainWindow::AddPaneCommand::~AddPaneCommand() | 
| Chris@0 | 3441 { | 
| Chris@0 | 3442     if (m_pane && !m_added) { | 
| Chris@0 | 3443 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@0 | 3444     } | 
| Chris@0 | 3445 } | 
| Chris@0 | 3446 | 
| Chris@0 | 3447 QString | 
| Chris@0 | 3448 MainWindow::AddPaneCommand::getName() const | 
| Chris@0 | 3449 { | 
| Chris@0 | 3450     return tr("Add Pane"); | 
| Chris@0 | 3451 } | 
| Chris@0 | 3452 | 
| Chris@0 | 3453 void | 
| Chris@0 | 3454 MainWindow::AddPaneCommand::execute() | 
| Chris@0 | 3455 { | 
| Chris@0 | 3456     if (!m_pane) { | 
| Chris@0 | 3457 	m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@0 | 3458 	m_pane = m_mw->m_paneStack->addPane(); | 
| Chris@90 | 3459 | 
| Chris@90 | 3460         connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@116 | 3461                 m_mw, SLOT(contextHelpChanged(const QString &))); | 
| Chris@0 | 3462     } else { | 
| Chris@0 | 3463 	m_mw->m_paneStack->showPane(m_pane); | 
| Chris@0 | 3464     } | 
| Chris@0 | 3465 | 
| Chris@0 | 3466     m_mw->m_paneStack->setCurrentPane(m_pane); | 
| Chris@65 | 3467     m_mw->m_overview->registerView(m_pane); | 
| Chris@0 | 3468     m_added = true; | 
| Chris@0 | 3469 } | 
| Chris@0 | 3470 | 
| Chris@0 | 3471 void | 
| Chris@0 | 3472 MainWindow::AddPaneCommand::unexecute() | 
| Chris@0 | 3473 { | 
| Chris@0 | 3474     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@0 | 3475     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@65 | 3476     m_mw->m_overview->unregisterView(m_pane); | 
| Chris@0 | 3477     m_added = false; | 
| Chris@0 | 3478 } | 
| Chris@0 | 3479 | 
| Chris@0 | 3480 MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) : | 
| Chris@0 | 3481     m_mw(mw), | 
| Chris@0 | 3482     m_pane(pane), | 
| Chris@0 | 3483     m_added(true) | 
| Chris@0 | 3484 { | 
| Chris@0 | 3485 } | 
| Chris@0 | 3486 | 
| Chris@0 | 3487 MainWindow::RemovePaneCommand::~RemovePaneCommand() | 
| Chris@0 | 3488 { | 
| Chris@0 | 3489     if (m_pane && !m_added) { | 
| Chris@0 | 3490 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@0 | 3491     } | 
| Chris@0 | 3492 } | 
| Chris@0 | 3493 | 
| Chris@0 | 3494 QString | 
| Chris@0 | 3495 MainWindow::RemovePaneCommand::getName() const | 
| Chris@0 | 3496 { | 
| Chris@0 | 3497     return tr("Remove Pane"); | 
| Chris@0 | 3498 } | 
| Chris@0 | 3499 | 
| Chris@0 | 3500 void | 
| Chris@0 | 3501 MainWindow::RemovePaneCommand::execute() | 
| Chris@0 | 3502 { | 
| Chris@0 | 3503     m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@0 | 3504     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@65 | 3505     m_mw->m_overview->unregisterView(m_pane); | 
| Chris@0 | 3506     m_added = false; | 
| Chris@0 | 3507 } | 
| Chris@0 | 3508 | 
| Chris@0 | 3509 void | 
| Chris@0 | 3510 MainWindow::RemovePaneCommand::unexecute() | 
| Chris@0 | 3511 { | 
| Chris@0 | 3512     m_mw->m_paneStack->showPane(m_pane); | 
| Chris@0 | 3513     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@65 | 3514     m_mw->m_overview->registerView(m_pane); | 
| Chris@0 | 3515     m_added = true; | 
| Chris@0 | 3516 } | 
| Chris@0 | 3517 | 
| Chris@0 | 3518 void | 
| Chris@0 | 3519 MainWindow::addLayer() | 
| Chris@0 | 3520 { | 
| Chris@0 | 3521     QObject *s = sender(); | 
| Chris@0 | 3522     QAction *action = dynamic_cast<QAction *>(s); | 
| Chris@0 | 3523 | 
| Chris@0 | 3524     if (!action) { | 
| Chris@0 | 3525 	std::cerr << "WARNING: MainWindow::addLayer: sender is not an action" | 
| Chris@0 | 3526 		  << std::endl; | 
| Chris@0 | 3527 	return; | 
| Chris@0 | 3528     } | 
| Chris@0 | 3529 | 
| Chris@0 | 3530     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3531 | 
| Chris@0 | 3532     if (!pane) { | 
| Chris@0 | 3533 	std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl; | 
| Chris@0 | 3534 	return; | 
| Chris@0 | 3535     } | 
| Chris@0 | 3536 | 
| Chris@0 | 3537     ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action); | 
| Chris@0 | 3538 | 
| Chris@0 | 3539     if (ei != m_existingLayerActions.end()) { | 
| Chris@0 | 3540 	Layer *newLayer = ei->second; | 
| Chris@0 | 3541 	m_document->addLayerToView(pane, newLayer); | 
| Chris@0 | 3542 	m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@0 | 3543 	return; | 
| Chris@0 | 3544     } | 
| Chris@0 | 3545 | 
| Chris@95 | 3546     ei = m_sliceActions.find(action); | 
| Chris@95 | 3547 | 
| Chris@95 | 3548     if (ei != m_sliceActions.end()) { | 
| Chris@95 | 3549         Layer *newLayer = m_document->createLayer(LayerFactory::Slice); | 
| Chris@95 | 3550 //        document->setModel(newLayer, ei->second->getModel()); | 
| Chris@95 | 3551         SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second); | 
| Chris@95 | 3552         SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer); | 
| Chris@95 | 3553         if (source && dest) { | 
| Chris@95 | 3554             dest->setSliceableModel(source->getSliceableModel()); | 
| Chris@95 | 3555             connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)), | 
| Chris@95 | 3556                     dest, SLOT(sliceableModelReplaced(const Model *, const Model *))); | 
| Chris@95 | 3557             connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), | 
| Chris@95 | 3558                     dest, SLOT(modelAboutToBeDeleted(Model *))); | 
| Chris@95 | 3559         } | 
| Chris@95 | 3560 	m_document->addLayerToView(pane, newLayer); | 
| Chris@95 | 3561 	m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@95 | 3562 	return; | 
| Chris@95 | 3563     } | 
| Chris@95 | 3564 | 
| Chris@34 | 3565     TransformActionMap::iterator i = m_transformActions.find(action); | 
| Chris@34 | 3566 | 
| Chris@34 | 3567     if (i == m_transformActions.end()) { | 
| Chris@0 | 3568 | 
| Chris@0 | 3569 	LayerActionMap::iterator i = m_layerActions.find(action); | 
| Chris@0 | 3570 | 
| Chris@0 | 3571 	if (i == m_layerActions.end()) { | 
| Chris@0 | 3572 	    std::cerr << "WARNING: MainWindow::addLayer: unknown action " | 
| Chris@0 | 3573 		      << action->objectName().toStdString() << std::endl; | 
| Chris@0 | 3574 	    return; | 
| Chris@0 | 3575 	} | 
| Chris@0 | 3576 | 
| Chris@0 | 3577 	LayerFactory::LayerType type = i->second; | 
| Chris@0 | 3578 | 
| Chris@0 | 3579 	LayerFactory::LayerTypeSet emptyTypes = | 
| Chris@0 | 3580 	    LayerFactory::getInstance()->getValidEmptyLayerTypes(); | 
| Chris@0 | 3581 | 
| Chris@0 | 3582 	Layer *newLayer; | 
| Chris@0 | 3583 | 
| Chris@0 | 3584 	if (emptyTypes.find(type) != emptyTypes.end()) { | 
| Chris@0 | 3585 | 
| Chris@0 | 3586 	    newLayer = m_document->createEmptyLayer(type); | 
| Chris@0 | 3587 	    m_toolActions[ViewManager::DrawMode]->trigger(); | 
| Chris@0 | 3588 | 
| Chris@0 | 3589 	} else { | 
| Chris@0 | 3590 | 
| Chris@0 | 3591 	    newLayer = m_document->createMainModelLayer(type); | 
| Chris@0 | 3592 	} | 
| Chris@0 | 3593 | 
| Chris@0 | 3594 	m_document->addLayerToView(pane, newLayer); | 
| Chris@0 | 3595 	m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@0 | 3596 | 
| Chris@0 | 3597 	return; | 
| Chris@0 | 3598     } | 
| Chris@0 | 3599 | 
| Chris@107 | 3600     TransformId transform = i->second; | 
| Chris@0 | 3601     TransformFactory *factory = TransformFactory::getInstance(); | 
| Chris@0 | 3602 | 
| Chris@0 | 3603     QString configurationXml; | 
| Chris@0 | 3604 | 
| Chris@0 | 3605     int channel = -1; | 
| Chris@0 | 3606     // pick up the default channel from any existing layers on the same pane | 
| Chris@0 | 3607     for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@0 | 3608 	int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j)); | 
| Chris@0 | 3609 	if (c != -1) { | 
| Chris@0 | 3610 	    channel = c; | 
| Chris@0 | 3611 	    break; | 
| Chris@0 | 3612 	} | 
| Chris@0 | 3613     } | 
| Chris@0 | 3614 | 
| Chris@33 | 3615     // We always ask for configuration, even if the plugin isn't | 
| Chris@33 | 3616     // supposed to be configurable, because we need to let the user | 
| Chris@33 | 3617     // change the execution context (block size etc). | 
| Chris@0 | 3618 | 
| Chris@27 | 3619     PluginTransform::ExecutionContext context(channel); | 
| Chris@27 | 3620 | 
| Chris@66 | 3621     std::vector<Model *> candidateInputModels = | 
| Chris@66 | 3622         m_document->getTransformInputModels(); | 
| Chris@53 | 3623 | 
| Chris@54 | 3624     Model *inputModel = factory->getConfigurationForTransform(transform, | 
| Chris@54 | 3625                                                               candidateInputModels, | 
| Chris@54 | 3626                                                               context, | 
| Chris@54 | 3627                                                               configurationXml, | 
| Chris@54 | 3628                                                               m_playSource); | 
| Chris@54 | 3629     if (!inputModel) return; | 
| Chris@54 | 3630 | 
| Chris@54 | 3631     std::cerr << "Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << std::endl; | 
| Chris@0 | 3632 | 
| Chris@0 | 3633     Layer *newLayer = m_document->createDerivedLayer(transform, | 
| Chris@54 | 3634                                                      inputModel, | 
| Chris@27 | 3635                                                      context, | 
| Chris@0 | 3636                                                      configurationXml); | 
| Chris@0 | 3637 | 
| Chris@0 | 3638     if (newLayer) { | 
| Chris@0 | 3639         m_document->addLayerToView(pane, newLayer); | 
| Chris@27 | 3640         m_document->setChannel(newLayer, context.channel); | 
| Chris@34 | 3641         m_recentTransforms.add(transform); | 
| Chris@70 | 3642         m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@0 | 3643     } | 
| Chris@0 | 3644 | 
| Chris@0 | 3645     updateMenuStates(); | 
| Chris@0 | 3646 } | 
| Chris@0 | 3647 | 
| Chris@0 | 3648 void | 
| Chris@0 | 3649 MainWindow::deleteCurrentPane() | 
| Chris@0 | 3650 { | 
| Chris@0 | 3651     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@0 | 3652 	(tr("Delete Pane"), true); | 
| Chris@0 | 3653 | 
| Chris@0 | 3654     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3655     if (pane) { | 
| Chris@0 | 3656 	while (pane->getLayerCount() > 0) { | 
| Chris@0 | 3657 	    Layer *layer = pane->getLayer(0); | 
| Chris@0 | 3658 	    if (layer) { | 
| Chris@0 | 3659 		m_document->removeLayerFromView(pane, layer); | 
| Chris@0 | 3660 	    } else { | 
| Chris@0 | 3661 		break; | 
| Chris@0 | 3662 	    } | 
| Chris@0 | 3663 	} | 
| Chris@0 | 3664 | 
| Chris@0 | 3665 	RemovePaneCommand *command = new RemovePaneCommand(this, pane); | 
| Chris@0 | 3666 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@0 | 3667     } | 
| Chris@0 | 3668 | 
| Chris@0 | 3669     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@0 | 3670 | 
| Chris@0 | 3671     updateMenuStates(); | 
| Chris@0 | 3672 } | 
| Chris@0 | 3673 | 
| Chris@0 | 3674 void | 
| Chris@0 | 3675 MainWindow::deleteCurrentLayer() | 
| Chris@0 | 3676 { | 
| Chris@0 | 3677     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3678     if (pane) { | 
| Chris@0 | 3679 	Layer *layer = pane->getSelectedLayer(); | 
| Chris@0 | 3680 	if (layer) { | 
| Chris@0 | 3681 	    m_document->removeLayerFromView(pane, layer); | 
| Chris@0 | 3682 	} | 
| Chris@0 | 3683     } | 
| Chris@0 | 3684     updateMenuStates(); | 
| Chris@0 | 3685 } | 
| Chris@0 | 3686 | 
| Chris@0 | 3687 void | 
| Chris@0 | 3688 MainWindow::renameCurrentLayer() | 
| Chris@0 | 3689 { | 
| Chris@0 | 3690     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@0 | 3691     if (pane) { | 
| Chris@0 | 3692 	Layer *layer = pane->getSelectedLayer(); | 
| Chris@0 | 3693 	if (layer) { | 
| Chris@0 | 3694 	    bool ok = false; | 
| Chris@0 | 3695 	    QString newName = QInputDialog::getText | 
| Chris@0 | 3696 		(this, tr("Rename Layer"), | 
| Chris@0 | 3697 		 tr("New name for this layer:"), | 
| Chris@0 | 3698 		 QLineEdit::Normal, layer->objectName(), &ok); | 
| Chris@0 | 3699 	    if (ok) { | 
| Chris@0 | 3700 		layer->setObjectName(newName); | 
| Chris@95 | 3701 		setupExistingLayersMenus(); | 
| Chris@0 | 3702 	    } | 
| Chris@0 | 3703 	} | 
| Chris@0 | 3704     } | 
| Chris@0 | 3705 } | 
| Chris@0 | 3706 | 
| Chris@0 | 3707 void | 
| Chris@59 | 3708 MainWindow::playSpeedChanged(int position) | 
| Chris@0 | 3709 { | 
| Chris@59 | 3710     PlaySpeedRangeMapper mapper(0, 200); | 
| Chris@60 | 3711 | 
| Chris@60 | 3712     float percent = m_playSpeed->mappedValue(); | 
| Chris@60 | 3713 | 
| Chris@60 | 3714     float factor = mapper.getFactorForValue(percent); | 
| Chris@60 | 3715 | 
| Chris@60 | 3716 //    float factor = mapper.getFactorForPosition(position); | 
| Chris@60 | 3717 //    float percent = mapper.getValueForPosition(position); | 
| Chris@60 | 3718 | 
| Chris@60 | 3719     std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl; | 
| Chris@60 | 3720 | 
| Chris@60 | 3721 //!!!    bool slow = (position < 100); | 
| Chris@59 | 3722     bool something = (position != 100); | 
| Chris@60 | 3723 /*!!! | 
| Chris@59 | 3724     int pc = lrintf(percent); | 
| Chris@21 | 3725 | 
| Chris@21 | 3726     m_playSpeed->setToolTip(tr("Playback speed: %1%2%") | 
| Chris@60 | 3727                             .arg(!slow ? "+" : "") | 
| Chris@21 | 3728 			    .arg(pc)); | 
| Chris@60 | 3729 */ | 
| Chris@25 | 3730     m_playSharpen->setEnabled(something); | 
| Chris@26 | 3731     m_playMono->setEnabled(something); | 
| Chris@25 | 3732     bool sharpen = (something && m_playSharpen->isChecked()); | 
| Chris@26 | 3733     bool mono = (something && m_playMono->isChecked()); | 
| Chris@26 | 3734     m_playSource->setTimeStretch(factor, sharpen, mono); | 
| Chris@16 | 3735 } | 
| Chris@16 | 3736 | 
| Chris@16 | 3737 void | 
| Chris@16 | 3738 MainWindow::playSharpenToggled() | 
| Chris@16 | 3739 { | 
| Chris@21 | 3740     QSettings settings; | 
| Chris@21 | 3741     settings.beginGroup("MainWindow"); | 
| Chris@21 | 3742     settings.setValue("playsharpen", m_playSharpen->isChecked()); | 
| Chris@21 | 3743     settings.endGroup(); | 
| Chris@21 | 3744 | 
| Chris@16 | 3745     playSpeedChanged(m_playSpeed->value()); | 
| Chris@0 | 3746 } | 
| Chris@0 | 3747 | 
| Chris@0 | 3748 void | 
| Chris@26 | 3749 MainWindow::playMonoToggled() | 
| Chris@26 | 3750 { | 
| Chris@26 | 3751     QSettings settings; | 
| Chris@26 | 3752     settings.beginGroup("MainWindow"); | 
| Chris@26 | 3753     settings.setValue("playmono", m_playMono->isChecked()); | 
| Chris@26 | 3754     settings.endGroup(); | 
| Chris@26 | 3755 | 
| Chris@26 | 3756     playSpeedChanged(m_playSpeed->value()); | 
| Chris@26 | 3757 } | 
| Chris@26 | 3758 | 
| Chris@26 | 3759 void | 
| Chris@116 | 3760 MainWindow::playbackFrameChanged(unsigned long frame) | 
| Chris@116 | 3761 { | 
| Chris@116 | 3762     if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@116 | 3763 | 
| Chris@116 | 3764     RealTime now = RealTime::frame2RealTime | 
| Chris@116 | 3765         (frame, getMainModel()->getSampleRate()); | 
| Chris@116 | 3766 | 
| Chris@116 | 3767     if (now.sec == m_lastPlayStatusSec) return; | 
| Chris@116 | 3768 | 
| Chris@116 | 3769     RealTime then = RealTime::frame2RealTime | 
| Chris@116 | 3770         (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); | 
| Chris@116 | 3771 | 
| Chris@116 | 3772     QString nowStr; | 
| Chris@116 | 3773     QString thenStr; | 
| Chris@116 | 3774     QString remainingStr; | 
| Chris@116 | 3775 | 
| Chris@116 | 3776     if (then.sec > 10) { | 
| Chris@116 | 3777         nowStr = now.toSecText().c_str(); | 
| Chris@116 | 3778         thenStr = then.toSecText().c_str(); | 
| Chris@116 | 3779         remainingStr = (then - now).toSecText().c_str(); | 
| Chris@116 | 3780         m_lastPlayStatusSec = now.sec; | 
| Chris@116 | 3781     } else { | 
| Chris@116 | 3782         nowStr = now.toText(true).c_str(); | 
| Chris@116 | 3783         thenStr = then.toText(true).c_str(); | 
| Chris@116 | 3784         remainingStr = (then - now).toText(true).c_str(); | 
| Chris@116 | 3785     } | 
| Chris@116 | 3786 | 
| Chris@116 | 3787     m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") | 
| Chris@116 | 3788         .arg(nowStr).arg(thenStr).arg(remainingStr); | 
| Chris@116 | 3789 | 
| Chris@116 | 3790     statusBar()->showMessage(m_myStatusMessage); | 
| Chris@116 | 3791 } | 
| Chris@116 | 3792 | 
| Chris@116 | 3793 void | 
| Chris@116 | 3794 MainWindow::globalCentreFrameChanged(unsigned long frame) | 
| Chris@116 | 3795 { | 
| Chris@116 | 3796     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@116 | 3797     Pane *p = 0; | 
| Chris@116 | 3798     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@116 | 3799     if (!p->getFollowGlobalPan()) return; | 
| Chris@116 | 3800     updateVisibleRangeDisplay(p); | 
| Chris@116 | 3801 } | 
| Chris@116 | 3802 | 
| Chris@116 | 3803 void | 
| Chris@116 | 3804 MainWindow::viewCentreFrameChanged(View *v, unsigned long frame) | 
| Chris@116 | 3805 { | 
| Chris@116 | 3806     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@116 | 3807     Pane *p = 0; | 
| Chris@116 | 3808     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@116 | 3809     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@116 | 3810 } | 
| Chris@116 | 3811 | 
| Chris@116 | 3812 void | 
| Chris@116 | 3813 MainWindow::viewZoomLevelChanged(View *v, unsigned long zoom, bool locked) | 
| Chris@116 | 3814 { | 
| Chris@116 | 3815     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@116 | 3816     Pane *p = 0; | 
| Chris@116 | 3817     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@116 | 3818     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@116 | 3819 } | 
| Chris@116 | 3820 | 
| Chris@116 | 3821 void | 
| Chris@116 | 3822 MainWindow::updateVisibleRangeDisplay(Pane *p) const | 
| Chris@116 | 3823 { | 
| Chris@116 | 3824     if (!getMainModel() || !p) { | 
| Chris@116 | 3825         return; | 
| Chris@116 | 3826     } | 
| Chris@116 | 3827 | 
| Chris@117 | 3828     bool haveSelection = false; | 
| Chris@117 | 3829     size_t startFrame = 0, endFrame = 0; | 
| Chris@117 | 3830 | 
| Chris@117 | 3831     if (m_viewManager && m_viewManager->haveInProgressSelection()) { | 
| Chris@117 | 3832 | 
| Chris@117 | 3833         bool exclusive = false; | 
| Chris@117 | 3834         Selection s = m_viewManager->getInProgressSelection(exclusive); | 
| Chris@117 | 3835 | 
| Chris@117 | 3836         if (!s.isEmpty()) { | 
| Chris@117 | 3837             haveSelection = true; | 
| Chris@117 | 3838             startFrame = s.getStartFrame(); | 
| Chris@117 | 3839             endFrame = s.getEndFrame(); | 
| Chris@117 | 3840         } | 
| Chris@117 | 3841     } | 
| Chris@117 | 3842 | 
| Chris@117 | 3843     if (!haveSelection) { | 
| Chris@117 | 3844         startFrame = p->getFirstVisibleFrame(); | 
| Chris@117 | 3845         endFrame = p->getLastVisibleFrame(); | 
| Chris@117 | 3846     } | 
| Chris@117 | 3847 | 
| Chris@116 | 3848     RealTime start = RealTime::frame2RealTime | 
| Chris@117 | 3849         (startFrame, getMainModel()->getSampleRate()); | 
| Chris@116 | 3850 | 
| Chris@116 | 3851     RealTime end = RealTime::frame2RealTime | 
| Chris@117 | 3852         (endFrame, getMainModel()->getSampleRate()); | 
| Chris@116 | 3853 | 
| Chris@116 | 3854     RealTime duration = end - start; | 
| Chris@116 | 3855 | 
| Chris@116 | 3856     QString startStr, endStr, durationStr; | 
| Chris@116 | 3857     startStr = start.toText(true).c_str(); | 
| Chris@116 | 3858     endStr = end.toText(true).c_str(); | 
| Chris@116 | 3859     durationStr = duration.toText(true).c_str(); | 
| Chris@116 | 3860 | 
| Chris@117 | 3861     if (haveSelection) { | 
| Chris@117 | 3862         m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)") | 
| Chris@117 | 3863             .arg(startStr).arg(endStr).arg(durationStr); | 
| Chris@117 | 3864     } else { | 
| Chris@117 | 3865         m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)") | 
| Chris@117 | 3866             .arg(startStr).arg(endStr).arg(durationStr); | 
| Chris@117 | 3867     } | 
| Chris@116 | 3868 | 
| Chris@116 | 3869     statusBar()->showMessage(m_myStatusMessage); | 
| Chris@116 | 3870 } | 
| Chris@116 | 3871 | 
| Chris@116 | 3872 void | 
| Chris@0 | 3873 MainWindow::outputLevelsChanged(float left, float right) | 
| Chris@0 | 3874 { | 
| Chris@0 | 3875     m_fader->setPeakLeft(left); | 
| Chris@0 | 3876     m_fader->setPeakRight(right); | 
| Chris@0 | 3877 } | 
| Chris@0 | 3878 | 
| Chris@0 | 3879 void | 
| Chris@0 | 3880 MainWindow::sampleRateMismatch(size_t requested, size_t actual, | 
| Chris@0 | 3881                                bool willResample) | 
| Chris@0 | 3882 { | 
| Chris@0 | 3883     if (!willResample) { | 
| Chris@0 | 3884         //!!! more helpful message needed | 
| Chris@0 | 3885         QMessageBox::information | 
| Chris@0 | 3886             (this, tr("Sample rate mismatch"), | 
| Chris@118 | 3887              tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed and pitch.") | 
| Chris@0 | 3888              .arg(requested).arg(actual)); | 
| Chris@0 | 3889     } | 
| Chris@0 | 3890 | 
| Chris@0 | 3891     updateDescriptionLabel(); | 
| Chris@0 | 3892 } | 
| Chris@0 | 3893 | 
| Chris@0 | 3894 void | 
| Chris@42 | 3895 MainWindow::audioOverloadPluginDisabled() | 
| Chris@42 | 3896 { | 
| Chris@42 | 3897     QMessageBox::information | 
| Chris@42 | 3898         (this, tr("Audio processing overload"), | 
| Chris@42 | 3899          tr("Audio effects plugin auditioning has been disabled\ndue to a processing overload.")); | 
| Chris@42 | 3900 } | 
| Chris@42 | 3901 | 
| Chris@42 | 3902 void | 
| Chris@0 | 3903 MainWindow::layerAdded(Layer *layer) | 
| Chris@0 | 3904 { | 
| Chris@0 | 3905 //    std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl; | 
| Chris@0 | 3906 //    setupExistingLayersMenu(); | 
| Chris@0 | 3907     updateMenuStates(); | 
| Chris@0 | 3908 } | 
| Chris@0 | 3909 | 
| Chris@0 | 3910 void | 
| Chris@0 | 3911 MainWindow::layerRemoved(Layer *layer) | 
| Chris@0 | 3912 { | 
| Chris@0 | 3913 //    std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl; | 
| Chris@95 | 3914     setupExistingLayersMenus(); | 
| Chris@0 | 3915     updateMenuStates(); | 
| Chris@0 | 3916 } | 
| Chris@0 | 3917 | 
| Chris@0 | 3918 void | 
| Chris@0 | 3919 MainWindow::layerAboutToBeDeleted(Layer *layer) | 
| Chris@0 | 3920 { | 
| Chris@0 | 3921 //    std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl; | 
| Chris@0 | 3922     if (layer == m_timeRulerLayer) { | 
| Chris@0 | 3923 //	std::cerr << "(this is the time ruler layer)" << std::endl; | 
| Chris@0 | 3924 	m_timeRulerLayer = 0; | 
| Chris@0 | 3925     } | 
| Chris@0 | 3926 } | 
| Chris@0 | 3927 | 
| Chris@0 | 3928 void | 
| Chris@0 | 3929 MainWindow::layerInAView(Layer *layer, bool inAView) | 
| Chris@0 | 3930 { | 
| Chris@0 | 3931 //    std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl; | 
| Chris@0 | 3932 | 
| Chris@0 | 3933     // Check whether we need to add or remove model from play source | 
| Chris@0 | 3934     Model *model = layer->getModel(); | 
| Chris@0 | 3935     if (model) { | 
| Chris@0 | 3936         if (inAView) { | 
| Chris@0 | 3937             m_playSource->addModel(model); | 
| Chris@0 | 3938         } else { | 
| Chris@0 | 3939             bool found = false; | 
| Chris@0 | 3940             for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@0 | 3941                 Pane *pane = m_paneStack->getPane(i); | 
| Chris@0 | 3942                 if (!pane) continue; | 
| Chris@0 | 3943                 for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@0 | 3944                     Layer *pl = pane->getLayer(j); | 
| Chris@0 | 3945                     if (pl && pl->getModel() == model) { | 
| Chris@0 | 3946                         found = true; | 
| Chris@0 | 3947                         break; | 
| Chris@0 | 3948                     } | 
| Chris@0 | 3949                 } | 
| Chris@0 | 3950                 if (found) break; | 
| Chris@0 | 3951             } | 
| Chris@0 | 3952             if (!found) m_playSource->removeModel(model); | 
| Chris@0 | 3953         } | 
| Chris@0 | 3954     } | 
| Chris@0 | 3955 | 
| Chris@95 | 3956     setupExistingLayersMenus(); | 
| Chris@0 | 3957     updateMenuStates(); | 
| Chris@0 | 3958 } | 
| Chris@0 | 3959 | 
| Chris@0 | 3960 void | 
| Chris@0 | 3961 MainWindow::modelAdded(Model *model) | 
| Chris@0 | 3962 { | 
| Chris@0 | 3963 //    std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl; | 
| Chris@0 | 3964     m_playSource->addModel(model); | 
| Chris@66 | 3965     if (dynamic_cast<DenseTimeValueModel *>(model)) { | 
| Chris@66 | 3966         setupPaneAndLayerMenus(); | 
| Chris@66 | 3967     } | 
| Chris@0 | 3968 } | 
| Chris@0 | 3969 | 
| Chris@0 | 3970 void | 
| Chris@0 | 3971 MainWindow::mainModelChanged(WaveFileModel *model) | 
| Chris@0 | 3972 { | 
| Chris@0 | 3973 //    std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl; | 
| Chris@0 | 3974     updateDescriptionLabel(); | 
| Chris@0 | 3975     m_panLayer->setModel(model); | 
| Chris@0 | 3976     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); | 
| Chris@46 | 3977     if (model && !m_playTarget && m_audioOutput) createPlayTarget(); | 
| Chris@0 | 3978 } | 
| Chris@0 | 3979 | 
| Chris@0 | 3980 void | 
| Chris@0 | 3981 MainWindow::modelAboutToBeDeleted(Model *model) | 
| Chris@0 | 3982 { | 
| Chris@0 | 3983 //    std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; | 
| Chris@0 | 3984     m_playSource->removeModel(model); | 
| Chris@91 | 3985     FFTDataServer::modelAboutToBeDeleted(model); | 
| Chris@0 | 3986 } | 
| Chris@0 | 3987 | 
| Chris@0 | 3988 void | 
| Chris@0 | 3989 MainWindow::modelGenerationFailed(QString transformName) | 
| Chris@0 | 3990 { | 
| Chris@0 | 3991     QMessageBox::warning | 
| Chris@0 | 3992         (this, | 
| Chris@0 | 3993          tr("Failed to generate layer"), | 
| Chris@34 | 3994          tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.") | 
| Chris@0 | 3995          .arg(transformName), | 
| Chris@0 | 3996          QMessageBox::Ok, 0); | 
| Chris@0 | 3997 } | 
| Chris@0 | 3998 | 
| Chris@0 | 3999 void | 
| Chris@0 | 4000 MainWindow::modelRegenerationFailed(QString layerName, QString transformName) | 
| Chris@0 | 4001 { | 
| Chris@0 | 4002     QMessageBox::warning | 
| Chris@0 | 4003         (this, | 
| Chris@0 | 4004          tr("Failed to regenerate layer"), | 
| Chris@34 | 4005          tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.") | 
| Chris@0 | 4006          .arg(layerName).arg(transformName), | 
| Chris@0 | 4007          QMessageBox::Ok, 0); | 
| Chris@0 | 4008 } | 
| Chris@0 | 4009 | 
| Chris@0 | 4010 void | 
| Chris@0 | 4011 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position) | 
| Chris@0 | 4012 { | 
| Chris@0 | 4013 //    std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl; | 
| Chris@0 | 4014     m_paneStack->setCurrentPane(pane); | 
| Chris@0 | 4015     m_rightButtonMenu->popup(position); | 
| Chris@0 | 4016 } | 
| Chris@0 | 4017 | 
| Chris@0 | 4018 void | 
| Chris@73 | 4019 MainWindow::propertyStacksResized() | 
| Chris@73 | 4020 { | 
| Chris@73 | 4021 /* | 
| Chris@73 | 4022     std::cerr << "MainWindow::propertyStacksResized" << std::endl; | 
| Chris@73 | 4023     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 4024     if (pane && m_overview) { | 
| Chris@73 | 4025         std::cerr << "Fixed width: "  << pane->width() << std::endl; | 
| Chris@73 | 4026         m_overview->setFixedWidth(pane->width()); | 
| Chris@73 | 4027     } | 
| Chris@73 | 4028 */ | 
| Chris@73 | 4029 } | 
| Chris@73 | 4030 | 
| Chris@73 | 4031 void | 
| Chris@0 | 4032 MainWindow::showLayerTree() | 
| Chris@0 | 4033 { | 
| Chris@0 | 4034     QTreeView *view = new QTreeView(); | 
| Chris@0 | 4035     LayerTreeModel *tree = new LayerTreeModel(m_paneStack); | 
| Chris@0 | 4036     view->expand(tree->index(0, 0, QModelIndex())); | 
| Chris@0 | 4037     view->setModel(tree); | 
| Chris@0 | 4038     view->show(); | 
| Chris@0 | 4039 } | 
| Chris@0 | 4040 | 
| Chris@0 | 4041 void | 
| Chris@69 | 4042 MainWindow::pollOSC() | 
| Chris@69 | 4043 { | 
| Chris@70 | 4044     if (!m_oscQueue || m_oscQueue->isEmpty()) return; | 
| Chris@69 | 4045     std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; | 
| Chris@69 | 4046 | 
| Chris@70 | 4047     if (m_openingAudioFile) return; | 
| Chris@70 | 4048 | 
| Chris@69 | 4049     OSCMessage message = m_oscQueue->readMessage(); | 
| Chris@69 | 4050 | 
| Chris@69 | 4051     if (message.getTarget() != 0) { | 
| Chris@69 | 4052         return; //!!! for now -- this class is target 0, others not handled yet | 
| Chris@69 | 4053     } | 
| Chris@69 | 4054 | 
| Chris@69 | 4055     handleOSCMessage(message); | 
| Chris@69 | 4056 } | 
| Chris@69 | 4057 | 
| Chris@69 | 4058 void | 
| Chris@69 | 4059 MainWindow::handleOSCMessage(const OSCMessage &message) | 
| Chris@69 | 4060 { | 
| Chris@70 | 4061     std::cerr << "MainWindow::handleOSCMessage: thread id = " | 
| Chris@70 | 4062               << QThread::currentThreadId() << std::endl; | 
| Chris@70 | 4063 | 
| Chris@69 | 4064     // This large function should really be abstracted out. | 
| Chris@69 | 4065 | 
| Chris@69 | 4066     if (message.getMethod() == "open") { | 
| Chris@69 | 4067 | 
| Chris@69 | 4068         if (message.getArgCount() == 1 && | 
| Chris@69 | 4069             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4070             QString path = message.getArg(0).toString(); | 
| Chris@82 | 4071             if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) { | 
| Chris@69 | 4072                 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" | 
| Chris@69 | 4073                           << path.toStdString() << "\"" << std::endl; | 
| Chris@69 | 4074             } | 
| Chris@69 | 4075             //!!! we really need to spin here and not return until the | 
| Chris@69 | 4076             // file has been completely decoded... | 
| Chris@69 | 4077         } | 
| Chris@69 | 4078 | 
| Chris@69 | 4079     } else if (message.getMethod() == "openadditional") { | 
| Chris@69 | 4080 | 
| Chris@69 | 4081         if (message.getArgCount() == 1 && | 
| Chris@69 | 4082             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4083             QString path = message.getArg(0).toString(); | 
| Chris@82 | 4084             if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) { | 
| Chris@69 | 4085                 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" | 
| Chris@69 | 4086                           << path.toStdString() << "\"" << std::endl; | 
| Chris@69 | 4087             } | 
| Chris@69 | 4088         } | 
| Chris@69 | 4089 | 
| Chris@69 | 4090     } else if (message.getMethod() == "recent" || | 
| Chris@69 | 4091                message.getMethod() == "last") { | 
| Chris@69 | 4092 | 
| Chris@69 | 4093         int n = 0; | 
| Chris@69 | 4094         if (message.getMethod() == "recent" && | 
| Chris@69 | 4095             message.getArgCount() == 1 && | 
| Chris@69 | 4096             message.getArg(0).canConvert(QVariant::Int)) { | 
| Chris@69 | 4097             n = message.getArg(0).toInt() - 1; | 
| Chris@69 | 4098         } | 
| Chris@69 | 4099         std::vector<QString> recent = m_recentFiles.getRecent(); | 
| Chris@69 | 4100         if (n >= 0 && n < recent.size()) { | 
| Chris@82 | 4101             if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) { | 
| Chris@69 | 4102                 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" | 
| Chris@69 | 4103                           << recent[n].toStdString() << "\"" << std::endl; | 
| Chris@69 | 4104             } | 
| Chris@69 | 4105         } | 
| Chris@69 | 4106 | 
| Chris@69 | 4107     } else if (message.getMethod() == "save") { | 
| Chris@69 | 4108 | 
| Chris@69 | 4109         QString path; | 
| Chris@69 | 4110         if (message.getArgCount() == 1 && | 
| Chris@69 | 4111             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4112             path = message.getArg(0).toString(); | 
| Chris@69 | 4113             if (QFileInfo(path).exists()) { | 
| Chris@69 | 4114                 std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl; | 
| Chris@69 | 4115             } else { | 
| Chris@69 | 4116                 saveSessionFile(path); | 
| Chris@69 | 4117             } | 
| Chris@69 | 4118         } | 
| Chris@69 | 4119 | 
| Chris@69 | 4120     } else if (message.getMethod() == "export") { | 
| Chris@69 | 4121 | 
| Chris@69 | 4122         QString path; | 
| Chris@69 | 4123         if (getMainModel()) { | 
| Chris@69 | 4124             if (message.getArgCount() == 1 && | 
| Chris@69 | 4125                 message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4126                 path = message.getArg(0).toString(); | 
| Chris@69 | 4127                 if (QFileInfo(path).exists()) { | 
| Chris@69 | 4128                     std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl; | 
| Chris@69 | 4129                 } else { | 
| Chris@69 | 4130                     WavFileWriter writer(path, | 
| Chris@69 | 4131                                          getMainModel()->getSampleRate(), | 
| Chris@69 | 4132                                          getMainModel()->getChannelCount()); | 
| Chris@69 | 4133                     MultiSelection ms = m_viewManager->getSelection(); | 
| Chris@69 | 4134                     if (!ms.getSelections().empty()) { | 
| Chris@69 | 4135                         writer.writeModel(getMainModel(), &ms); | 
| Chris@69 | 4136                     } else { | 
| Chris@69 | 4137                         writer.writeModel(getMainModel()); | 
| Chris@69 | 4138                     } | 
| Chris@69 | 4139                 } | 
| Chris@69 | 4140             } | 
| Chris@69 | 4141         } | 
| Chris@69 | 4142 | 
| Chris@69 | 4143     } else if (message.getMethod() == "jump" || | 
| Chris@69 | 4144                message.getMethod() == "play") { | 
| Chris@69 | 4145 | 
| Chris@69 | 4146         if (getMainModel()) { | 
| Chris@69 | 4147 | 
| Chris@69 | 4148             unsigned long frame = m_viewManager->getPlaybackFrame(); | 
| Chris@69 | 4149             bool selection = false; | 
| Chris@69 | 4150             bool play = (message.getMethod() == "play"); | 
| Chris@69 | 4151 | 
| Chris@69 | 4152             if (message.getArgCount() == 1) { | 
| Chris@69 | 4153 | 
| Chris@69 | 4154                 if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4155                     message.getArg(0).toString() == "selection") { | 
| Chris@69 | 4156 | 
| Chris@69 | 4157                     selection = true; | 
| Chris@69 | 4158 | 
| Chris@69 | 4159                 } else if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4160                            message.getArg(0).toString() == "end") { | 
| Chris@69 | 4161 | 
| Chris@69 | 4162                     frame = getMainModel()->getEndFrame(); | 
| Chris@69 | 4163 | 
| Chris@69 | 4164                 } else if (message.getArg(0).canConvert(QVariant::Double)) { | 
| Chris@69 | 4165 | 
| Chris@69 | 4166                     double time = message.getArg(0).toDouble(); | 
| Chris@69 | 4167                     if (time < 0.0) time = 0.0; | 
| Chris@69 | 4168 | 
| Chris@69 | 4169                     frame = lrint(time * getMainModel()->getSampleRate()); | 
| Chris@69 | 4170                 } | 
| Chris@69 | 4171             } | 
| Chris@69 | 4172 | 
| Chris@69 | 4173             if (frame > getMainModel()->getEndFrame()) { | 
| Chris@69 | 4174                 frame = getMainModel()->getEndFrame(); | 
| Chris@69 | 4175             } | 
| Chris@69 | 4176 | 
| Chris@69 | 4177             if (play) { | 
| Chris@69 | 4178                 m_viewManager->setPlaySelectionMode(selection); | 
| Chris@69 | 4179             } | 
| Chris@69 | 4180 | 
| Chris@69 | 4181             if (selection) { | 
| Chris@69 | 4182                 MultiSelection::SelectionList sl = m_viewManager->getSelections(); | 
| Chris@69 | 4183                 if (!sl.empty()) { | 
| Chris@69 | 4184                     frame = sl.begin()->getStartFrame(); | 
| Chris@69 | 4185                 } | 
| Chris@69 | 4186             } | 
| Chris@69 | 4187 | 
| Chris@69 | 4188             m_viewManager->setPlaybackFrame(frame); | 
| Chris@69 | 4189 | 
| Chris@69 | 4190             if (play && !m_playSource->isPlaying()) { | 
| Chris@69 | 4191                 m_playSource->play(frame); | 
| Chris@69 | 4192             } | 
| Chris@69 | 4193         } | 
| Chris@69 | 4194 | 
| Chris@69 | 4195     } else if (message.getMethod() == "stop") { | 
| Chris@69 | 4196 | 
| Chris@69 | 4197         if (m_playSource->isPlaying()) m_playSource->stop(); | 
| Chris@69 | 4198 | 
| Chris@69 | 4199     } else if (message.getMethod() == "loop") { | 
| Chris@69 | 4200 | 
| Chris@69 | 4201         if (message.getArgCount() == 1 && | 
| Chris@69 | 4202             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4203 | 
| Chris@69 | 4204             QString str = message.getArg(0).toString(); | 
| Chris@69 | 4205             if (str == "on") { | 
| Chris@69 | 4206                 m_viewManager->setPlayLoopMode(true); | 
| Chris@69 | 4207             } else if (str == "off") { | 
| Chris@69 | 4208                 m_viewManager->setPlayLoopMode(false); | 
| Chris@69 | 4209             } | 
| Chris@69 | 4210         } | 
| Chris@69 | 4211 | 
| Chris@69 | 4212     } else if (message.getMethod() == "select" || | 
| Chris@69 | 4213                message.getMethod() == "addselect") { | 
| Chris@69 | 4214 | 
| Chris@69 | 4215         if (getMainModel()) { | 
| Chris@69 | 4216 | 
| Chris@73 | 4217             int f0 = getMainModel()->getStartFrame(); | 
| Chris@73 | 4218             int f1 = getMainModel()->getEndFrame(); | 
| Chris@69 | 4219 | 
| Chris@69 | 4220             bool done = false; | 
| Chris@69 | 4221 | 
| Chris@69 | 4222             if (message.getArgCount() == 2 && | 
| Chris@69 | 4223                 message.getArg(0).canConvert(QVariant::Double) && | 
| Chris@69 | 4224                 message.getArg(1).canConvert(QVariant::Double)) { | 
| Chris@69 | 4225 | 
| Chris@69 | 4226                 double t0 = message.getArg(0).toDouble(); | 
| Chris@69 | 4227                 double t1 = message.getArg(1).toDouble(); | 
| Chris@69 | 4228                 if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } | 
| Chris@69 | 4229                 if (t0 < 0.0) t0 = 0.0; | 
| Chris@69 | 4230                 if (t1 < 0.0) t1 = 0.0; | 
| Chris@69 | 4231 | 
| Chris@69 | 4232                 f0 = lrint(t0 * getMainModel()->getSampleRate()); | 
| Chris@69 | 4233                 f1 = lrint(t1 * getMainModel()->getSampleRate()); | 
| Chris@73 | 4234 | 
| Chris@73 | 4235                 Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 4236                 Layer *layer = 0; | 
| Chris@73 | 4237                 if (pane) layer = pane->getSelectedLayer(); | 
| Chris@73 | 4238                 if (layer) { | 
| Chris@73 | 4239                     size_t resolution; | 
| Chris@73 | 4240                     layer->snapToFeatureFrame(pane, f0, resolution, | 
| Chris@73 | 4241                                               Layer::SnapLeft); | 
| Chris@73 | 4242                     layer->snapToFeatureFrame(pane, f1, resolution, | 
| Chris@73 | 4243                                               Layer::SnapRight); | 
| Chris@73 | 4244                 } | 
| Chris@69 | 4245 | 
| Chris@69 | 4246             } else if (message.getArgCount() == 1 && | 
| Chris@69 | 4247                        message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4248 | 
| Chris@69 | 4249                 QString str = message.getArg(0).toString(); | 
| Chris@69 | 4250                 if (str == "none") { | 
| Chris@69 | 4251                     m_viewManager->clearSelections(); | 
| Chris@69 | 4252                     done = true; | 
| Chris@69 | 4253                 } | 
| Chris@69 | 4254             } | 
| Chris@69 | 4255 | 
| Chris@69 | 4256             if (!done) { | 
| Chris@69 | 4257                 if (message.getMethod() == "select") { | 
| Chris@69 | 4258                     m_viewManager->setSelection(Selection(f0, f1)); | 
| Chris@69 | 4259                 } else { | 
| Chris@69 | 4260                     m_viewManager->addSelection(Selection(f0, f1)); | 
| Chris@69 | 4261                 } | 
| Chris@69 | 4262             } | 
| Chris@69 | 4263         } | 
| Chris@69 | 4264 | 
| Chris@69 | 4265     } else if (message.getMethod() == "add") { | 
| Chris@69 | 4266 | 
| Chris@69 | 4267         if (getMainModel()) { | 
| Chris@69 | 4268 | 
| Chris@69 | 4269             if (message.getArgCount() >= 1 && | 
| Chris@69 | 4270                 message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4271 | 
| Chris@69 | 4272                 int channel = -1; | 
| Chris@69 | 4273                 if (message.getArgCount() == 2 && | 
| Chris@69 | 4274                     message.getArg(0).canConvert(QVariant::Int)) { | 
| Chris@69 | 4275                     channel = message.getArg(0).toInt(); | 
| Chris@69 | 4276                     if (channel < -1 || | 
| Chris@69 | 4277                         channel > getMainModel()->getChannelCount()) { | 
| Chris@69 | 4278                         std::cerr << "WARNING: MainWindow::handleOSCMessage: channel " | 
| Chris@69 | 4279                                   << channel << " out of range" << std::endl; | 
| Chris@69 | 4280                         channel = -1; | 
| Chris@69 | 4281                     } | 
| Chris@69 | 4282                 } | 
| Chris@69 | 4283 | 
| Chris@69 | 4284                 QString str = message.getArg(0).toString(); | 
| Chris@69 | 4285 | 
| Chris@69 | 4286                 LayerFactory::LayerType type = | 
| Chris@69 | 4287                     LayerFactory::getInstance()->getLayerTypeForName(str); | 
| Chris@69 | 4288 | 
| Chris@69 | 4289                 if (type == LayerFactory::UnknownLayer) { | 
| Chris@69 | 4290                     std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer " | 
| Chris@69 | 4291                               << "type " << str.toStdString() << std::endl; | 
| Chris@69 | 4292                 } else { | 
| Chris@69 | 4293 | 
| Chris@69 | 4294                     PaneConfiguration configuration(type, | 
| Chris@69 | 4295                                                     getMainModel(), | 
| Chris@69 | 4296                                                     channel); | 
| Chris@69 | 4297 | 
| Chris@69 | 4298                     addPane(configuration, | 
| Chris@69 | 4299                             tr("Add %1 Pane") | 
| Chris@69 | 4300                             .arg(LayerFactory::getInstance()-> | 
| Chris@69 | 4301                                  getLayerPresentationName(type))); | 
| Chris@69 | 4302                 } | 
| Chris@69 | 4303             } | 
| Chris@69 | 4304         } | 
| Chris@69 | 4305 | 
| Chris@69 | 4306     } else if (message.getMethod() == "undo") { | 
| Chris@69 | 4307 | 
| Chris@69 | 4308         CommandHistory::getInstance()->undo(); | 
| Chris@69 | 4309 | 
| Chris@69 | 4310     } else if (message.getMethod() == "redo") { | 
| Chris@69 | 4311 | 
| Chris@69 | 4312         CommandHistory::getInstance()->redo(); | 
| Chris@69 | 4313 | 
| Chris@69 | 4314     } else if (message.getMethod() == "set") { | 
| Chris@69 | 4315 | 
| Chris@70 | 4316         if (message.getArgCount() == 2 && | 
| Chris@69 | 4317             message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4318             message.getArg(1).canConvert(QVariant::Double)) { | 
| Chris@69 | 4319 | 
| Chris@69 | 4320             QString property = message.getArg(0).toString(); | 
| Chris@69 | 4321             float value = (float)message.getArg(1).toDouble(); | 
| Chris@69 | 4322 | 
| Chris@69 | 4323             if (property == "gain") { | 
| Chris@69 | 4324                 if (value < 0.0) value = 0.0; | 
| Chris@69 | 4325                 m_fader->setValue(value); | 
| Chris@69 | 4326                 if (m_playTarget) m_playTarget->setOutputGain(value); | 
| Chris@69 | 4327             } else if (property == "speedup") { | 
| Chris@69 | 4328                 m_playSpeed->setMappedValue(value); | 
| Chris@69 | 4329             } else if (property == "overlays") { | 
| Chris@69 | 4330                 if (value < 0.5) { | 
| Chris@69 | 4331                     m_viewManager->setOverlayMode(ViewManager::NoOverlays); | 
| Chris@69 | 4332                 } else if (value < 1.5) { | 
| Chris@90 | 4333                     m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); | 
| Chris@90 | 4334                 } else if (value < 2.5) { | 
| Chris@90 | 4335                     m_viewManager->setOverlayMode(ViewManager::StandardOverlays); | 
| Chris@69 | 4336                 } else { | 
| Chris@69 | 4337                     m_viewManager->setOverlayMode(ViewManager::AllOverlays); | 
| Chris@69 | 4338                 } | 
| Chris@70 | 4339             } else if (property == "zoomwheels") { | 
| Chris@70 | 4340                 m_viewManager->setZoomWheelsEnabled(value > 0.5); | 
| Chris@72 | 4341             } else if (property == "propertyboxes") { | 
| Chris@72 | 4342                 bool toggle = ((value < 0.5) != | 
| Chris@72 | 4343                                (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks)); | 
| Chris@72 | 4344                 if (toggle) togglePropertyBoxes(); | 
| Chris@70 | 4345             } | 
| Chris@70 | 4346 | 
| Chris@70 | 4347         } else { | 
| Chris@70 | 4348             PropertyContainer *container = 0; | 
| Chris@70 | 4349             Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@70 | 4350             if (pane && | 
| Chris@70 | 4351                 message.getArgCount() == 3 && | 
| Chris@70 | 4352                 message.getArg(0).canConvert(QVariant::String) && | 
| Chris@70 | 4353                 message.getArg(1).canConvert(QVariant::String) && | 
| Chris@70 | 4354                 message.getArg(2).canConvert(QVariant::String)) { | 
| Chris@70 | 4355                 if (message.getArg(0).toString() == "pane") { | 
| Chris@70 | 4356                     container = pane->getPropertyContainer(0); | 
| Chris@70 | 4357                 } else if (message.getArg(0).toString() == "layer") { | 
| Chris@70 | 4358                     container = pane->getSelectedLayer(); | 
| Chris@70 | 4359                 } | 
| Chris@70 | 4360             } | 
| Chris@70 | 4361             if (container) { | 
| Chris@70 | 4362                 QString nameString = message.getArg(1).toString(); | 
| Chris@70 | 4363                 QString valueString = message.getArg(2).toString(); | 
| Chris@70 | 4364                 container->setPropertyWithCommand(nameString, valueString); | 
| Chris@69 | 4365             } | 
| Chris@69 | 4366         } | 
| Chris@69 | 4367 | 
| Chris@69 | 4368     } else if (message.getMethod() == "setcurrent") { | 
| Chris@69 | 4369 | 
| Chris@69 | 4370         int paneIndex = -1, layerIndex = -1; | 
| Chris@69 | 4371         bool wantLayer = false; | 
| Chris@69 | 4372 | 
| Chris@69 | 4373         if (message.getArgCount() >= 1 && | 
| Chris@69 | 4374             message.getArg(0).canConvert(QVariant::Int)) { | 
| Chris@69 | 4375 | 
| Chris@69 | 4376             paneIndex = message.getArg(0).toInt() - 1; | 
| Chris@69 | 4377 | 
| Chris@69 | 4378             if (message.getArgCount() >= 2 && | 
| Chris@69 | 4379                 message.getArg(1).canConvert(QVariant::Int)) { | 
| Chris@69 | 4380                 wantLayer = true; | 
| Chris@69 | 4381                 layerIndex = message.getArg(1).toInt() - 1; | 
| Chris@69 | 4382             } | 
| Chris@69 | 4383         } | 
| Chris@69 | 4384 | 
| Chris@69 | 4385         if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { | 
| Chris@69 | 4386             Pane *pane = m_paneStack->getPane(paneIndex); | 
| Chris@70 | 4387             m_paneStack->setCurrentPane(pane); | 
| Chris@69 | 4388             if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { | 
| Chris@69 | 4389                 Layer *layer = pane->getLayer(layerIndex); | 
| Chris@69 | 4390                 m_paneStack->setCurrentLayer(pane, layer); | 
| Chris@69 | 4391             } else if (wantLayer && layerIndex == -1) { | 
| Chris@69 | 4392                 m_paneStack->setCurrentLayer(pane, 0); | 
| Chris@69 | 4393             } | 
| Chris@69 | 4394         } | 
| Chris@69 | 4395 | 
| Chris@69 | 4396     } else if (message.getMethod() == "delete") { | 
| Chris@69 | 4397 | 
| Chris@69 | 4398         if (message.getArgCount() == 1 && | 
| Chris@69 | 4399             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@69 | 4400 | 
| Chris@69 | 4401             QString target = message.getArg(0).toString(); | 
| Chris@69 | 4402 | 
| Chris@69 | 4403             if (target == "pane") { | 
| Chris@69 | 4404 | 
| Chris@69 | 4405                 deleteCurrentPane(); | 
| Chris@69 | 4406 | 
| Chris@69 | 4407             } else if (target == "layer") { | 
| Chris@69 | 4408 | 
| Chris@69 | 4409                 deleteCurrentLayer(); | 
| Chris@69 | 4410 | 
| Chris@69 | 4411             } else { | 
| Chris@69 | 4412 | 
| Chris@69 | 4413                 std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl; | 
| Chris@69 | 4414             } | 
| Chris@69 | 4415         } | 
| Chris@69 | 4416 | 
| Chris@69 | 4417     } else if (message.getMethod() == "zoom") { | 
| Chris@69 | 4418 | 
| Chris@69 | 4419         if (message.getArgCount() == 1) { | 
| Chris@69 | 4420             if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4421                 message.getArg(0).toString() == "in") { | 
| Chris@69 | 4422                 zoomIn(); | 
| Chris@69 | 4423             } else if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4424                        message.getArg(0).toString() == "out") { | 
| Chris@69 | 4425                 zoomOut(); | 
| Chris@69 | 4426             } else if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@69 | 4427                        message.getArg(0).toString() == "default") { | 
| Chris@69 | 4428                 zoomDefault(); | 
| Chris@69 | 4429             } else if (message.getArg(0).canConvert(QVariant::Double)) { | 
| Chris@69 | 4430                 double level = message.getArg(0).toDouble(); | 
| Chris@69 | 4431                 Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@69 | 4432                 if (level < 1.0) level = 1.0; | 
| Chris@69 | 4433                 if (currentPane) currentPane->setZoomLevel(lrint(level)); | 
| Chris@69 | 4434             } | 
| Chris@69 | 4435         } | 
| Chris@69 | 4436 | 
| Chris@73 | 4437     } else if (message.getMethod() == "zoomvertical") { | 
| Chris@73 | 4438 | 
| Chris@73 | 4439         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 4440         Layer *layer = 0; | 
| Chris@73 | 4441         if (pane && pane->getLayerCount() > 0) { | 
| Chris@73 | 4442             layer = pane->getLayer(pane->getLayerCount() - 1); | 
| Chris@73 | 4443         } | 
| Chris@73 | 4444         int defaultStep = 0; | 
| Chris@73 | 4445         int steps = 0; | 
| Chris@73 | 4446         if (layer) { | 
| Chris@73 | 4447             steps = layer->getVerticalZoomSteps(defaultStep); | 
| Chris@73 | 4448             if (message.getArgCount() == 1 && steps > 0) { | 
| Chris@73 | 4449                 if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@73 | 4450                     message.getArg(0).toString() == "in") { | 
| Chris@73 | 4451                     int step = layer->getCurrentVerticalZoomStep() + 1; | 
| Chris@73 | 4452                     if (step < steps) layer->setVerticalZoomStep(step); | 
| Chris@73 | 4453                 } else if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@73 | 4454                            message.getArg(0).toString() == "out") { | 
| Chris@73 | 4455                     int step = layer->getCurrentVerticalZoomStep() - 1; | 
| Chris@73 | 4456                     if (step >= 0) layer->setVerticalZoomStep(step); | 
| Chris@73 | 4457                 } else if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@73 | 4458                            message.getArg(0).toString() == "default") { | 
| Chris@73 | 4459                     layer->setVerticalZoomStep(defaultStep); | 
| Chris@73 | 4460                 } | 
| Chris@73 | 4461             } else if (message.getArgCount() == 2) { | 
| Chris@73 | 4462                 if (message.getArg(0).canConvert(QVariant::Double) && | 
| Chris@73 | 4463                     message.getArg(1).canConvert(QVariant::Double)) { | 
| Chris@73 | 4464                     double min = message.getArg(0).toDouble(); | 
| Chris@73 | 4465                     double max = message.getArg(1).toDouble(); | 
| Chris@73 | 4466                     layer->setDisplayExtents(min, max); | 
| Chris@73 | 4467                 } | 
| Chris@73 | 4468             } | 
| Chris@73 | 4469         } | 
| Chris@73 | 4470 | 
| Chris@70 | 4471     } else if (message.getMethod() == "quit") { | 
| Chris@70 | 4472 | 
| Chris@70 | 4473         m_abandoning = true; | 
| Chris@70 | 4474         close(); | 
| Chris@70 | 4475 | 
| Chris@70 | 4476     } else if (message.getMethod() == "resize") { | 
| Chris@70 | 4477 | 
| Chris@70 | 4478         if (message.getArgCount() == 2) { | 
| Chris@70 | 4479 | 
| Chris@70 | 4480             int width = 0, height = 0; | 
| Chris@70 | 4481 | 
| Chris@70 | 4482             if (message.getArg(1).canConvert(QVariant::Int)) { | 
| Chris@70 | 4483 | 
| Chris@70 | 4484                 height = message.getArg(1).toInt(); | 
| Chris@70 | 4485 | 
| Chris@70 | 4486                 if (message.getArg(0).canConvert(QVariant::String) && | 
| Chris@70 | 4487                     message.getArg(0).toString() == "pane") { | 
| Chris@70 | 4488 | 
| Chris@70 | 4489                     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@70 | 4490                     if (pane) pane->resize(pane->width(), height); | 
| Chris@70 | 4491 | 
| Chris@70 | 4492                 } else if (message.getArg(0).canConvert(QVariant::Int)) { | 
| Chris@70 | 4493 | 
| Chris@70 | 4494                     width = message.getArg(0).toInt(); | 
| Chris@70 | 4495                     resize(width, height); | 
| Chris@70 | 4496                 } | 
| Chris@70 | 4497             } | 
| Chris@70 | 4498         } | 
| Chris@70 | 4499 | 
| Chris@70 | 4500     } else if (message.getMethod() == "transform") { | 
| Chris@70 | 4501 | 
| Chris@70 | 4502         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@70 | 4503 | 
| Chris@70 | 4504         if (getMainModel() && | 
| Chris@70 | 4505             pane && | 
| Chris@70 | 4506             message.getArgCount() == 1 && | 
| Chris@70 | 4507             message.getArg(0).canConvert(QVariant::String)) { | 
| Chris@70 | 4508 | 
| Chris@107 | 4509             TransformId transform = message.getArg(0).toString(); | 
| Chris@70 | 4510 | 
| Chris@70 | 4511             Layer *newLayer = m_document->createDerivedLayer | 
| Chris@70 | 4512                 (transform, | 
| Chris@70 | 4513                  getMainModel(), | 
| Chris@73 | 4514                  TransformFactory::getInstance()->getDefaultContextForTransform | 
| Chris@73 | 4515                  (transform, getMainModel()), | 
| Chris@70 | 4516                  ""); | 
| Chris@70 | 4517 | 
| Chris@70 | 4518             if (newLayer) { | 
| Chris@70 | 4519                 m_document->addLayerToView(pane, newLayer); | 
| Chris@70 | 4520                 m_recentTransforms.add(transform); | 
| Chris@70 | 4521                 m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@70 | 4522             } | 
| Chris@70 | 4523         } | 
| Chris@70 | 4524 | 
| Chris@69 | 4525     } else { | 
| Chris@69 | 4526         std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported " | 
| Chris@69 | 4527                   << "method \"" << message.getMethod().toStdString() | 
| Chris@69 | 4528                   << "\"" << std::endl; | 
| Chris@69 | 4529     } | 
| Chris@69 | 4530 | 
| Chris@69 | 4531 } | 
| Chris@69 | 4532 | 
| Chris@69 | 4533 void | 
| Chris@0 | 4534 MainWindow::preferences() | 
| Chris@0 | 4535 { | 
| Chris@0 | 4536     if (!m_preferencesDialog.isNull()) { | 
| Chris@0 | 4537         m_preferencesDialog->show(); | 
| Chris@0 | 4538         m_preferencesDialog->raise(); | 
| Chris@0 | 4539         return; | 
| Chris@0 | 4540     } | 
| Chris@0 | 4541 | 
| Chris@0 | 4542     m_preferencesDialog = new PreferencesDialog(this); | 
| Chris@0 | 4543 | 
| Chris@0 | 4544     // DeleteOnClose is safe here, because m_preferencesDialog is a | 
| Chris@0 | 4545     // QPointer that will be zeroed when the dialog is deleted.  We | 
| Chris@0 | 4546     // use it in preference to leaving the dialog lying around because | 
| Chris@0 | 4547     // if you Cancel the dialog, it resets the preferences state | 
| Chris@0 | 4548     // without resetting its own widgets, so its state will be | 
| Chris@0 | 4549     // incorrect when next shown unless we construct it afresh | 
| Chris@0 | 4550     m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); | 
| Chris@0 | 4551 | 
| Chris@0 | 4552     m_preferencesDialog->show(); | 
| Chris@0 | 4553 } | 
| Chris@0 | 4554 | 
| Chris@0 | 4555 void | 
| Chris@90 | 4556 MainWindow::mouseEnteredWidget() | 
| Chris@90 | 4557 { | 
| Chris@90 | 4558     QWidget *w = dynamic_cast<QWidget *>(sender()); | 
| Chris@90 | 4559     if (!w) return; | 
| Chris@90 | 4560 | 
| Chris@90 | 4561     if (w == m_fader) { | 
| Chris@116 | 4562         contextHelpChanged(tr("Adjust the master playback level")); | 
| Chris@90 | 4563     } else if (w == m_playSpeed) { | 
| Chris@116 | 4564         contextHelpChanged(tr("Adjust the master playback speed")); | 
| Chris@90 | 4565     } else if (w == m_playSharpen && w->isEnabled()) { | 
| Chris@116 | 4566         contextHelpChanged(tr("Toggle transient sharpening for playback time scaling")); | 
| Chris@90 | 4567     } else if (w == m_playMono && w->isEnabled()) { | 
| Chris@116 | 4568         contextHelpChanged(tr("Toggle mono mode for playback time scaling")); | 
| Chris@90 | 4569     } | 
| Chris@90 | 4570 } | 
| Chris@90 | 4571 | 
| Chris@90 | 4572 void | 
| Chris@90 | 4573 MainWindow::mouseLeftWidget() | 
| Chris@90 | 4574 { | 
| Chris@116 | 4575     contextHelpChanged(""); | 
| Chris@116 | 4576 } | 
| Chris@116 | 4577 | 
| Chris@116 | 4578 void | 
| Chris@117 | 4579 MainWindow::inProgressSelectionChanged() | 
| Chris@117 | 4580 { | 
| Chris@117 | 4581     Pane *currentPane = 0; | 
| Chris@117 | 4582     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| Chris@117 | 4583     if (currentPane) updateVisibleRangeDisplay(currentPane); | 
| Chris@117 | 4584 } | 
| Chris@117 | 4585 | 
| Chris@117 | 4586 void | 
| Chris@116 | 4587 MainWindow::contextHelpChanged(const QString &s) | 
| Chris@116 | 4588 { | 
| Chris@116 | 4589     if (s == "" && m_myStatusMessage != "") { | 
| Chris@116 | 4590         statusBar()->showMessage(m_myStatusMessage); | 
| Chris@116 | 4591         return; | 
| Chris@116 | 4592     } | 
| Chris@116 | 4593     statusBar()->showMessage(s); | 
| Chris@90 | 4594 } | 
| Chris@90 | 4595 | 
| Chris@90 | 4596 void | 
| Chris@0 | 4597 MainWindow::website() | 
| Chris@0 | 4598 { | 
| Chris@0 | 4599     openHelpUrl(tr("http://www.sonicvisualiser.org/")); | 
| Chris@0 | 4600 } | 
| Chris@0 | 4601 | 
| Chris@0 | 4602 void | 
| Chris@0 | 4603 MainWindow::help() | 
| Chris@0 | 4604 { | 
| Chris@126 | 4605     openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/")); | 
| Chris@0 | 4606 } | 
| Chris@0 | 4607 | 
| Chris@0 | 4608 void | 
| Chris@0 | 4609 MainWindow::openHelpUrl(QString url) | 
| Chris@0 | 4610 { | 
| Chris@0 | 4611     // This method mostly lifted from Qt Assistant source code | 
| Chris@0 | 4612 | 
| Chris@0 | 4613     QProcess *process = new QProcess(this); | 
| Chris@0 | 4614     connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); | 
| Chris@0 | 4615 | 
| Chris@0 | 4616     QStringList args; | 
| Chris@0 | 4617 | 
| Chris@0 | 4618 #ifdef Q_OS_MAC | 
| Chris@0 | 4619     args.append(url); | 
| Chris@0 | 4620     process->start("open", args); | 
| Chris@0 | 4621 #else | 
| Chris@0 | 4622 #ifdef Q_OS_WIN32 | 
| Chris@0 | 4623 | 
| Chris@0 | 4624 	QString pf(getenv("ProgramFiles")); | 
| Chris@0 | 4625 	QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); | 
| Chris@0 | 4626 | 
| Chris@0 | 4627 	args.append(url); | 
| Chris@0 | 4628 	process->start(command, args); | 
| Chris@0 | 4629 | 
| Chris@0 | 4630 #else | 
| Chris@0 | 4631 #ifdef Q_WS_X11 | 
| Chris@0 | 4632     if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { | 
| Chris@0 | 4633         args.append("exec"); | 
| Chris@0 | 4634         args.append(url); | 
| Chris@0 | 4635         process->start("kfmclient", args); | 
| Chris@0 | 4636     } else if (!qgetenv("BROWSER").isEmpty()) { | 
| Chris@0 | 4637         args.append(url); | 
| Chris@0 | 4638         process->start(qgetenv("BROWSER"), args); | 
| Chris@0 | 4639     } else { | 
| Chris@0 | 4640         args.append(url); | 
| Chris@0 | 4641         process->start("firefox", args); | 
| Chris@0 | 4642     } | 
| Chris@0 | 4643 #endif | 
| Chris@0 | 4644 #endif | 
| Chris@0 | 4645 #endif | 
| Chris@0 | 4646 } | 
| Chris@0 | 4647 | 
| Chris@0 | 4648 void | 
| Chris@0 | 4649 MainWindow::about() | 
| Chris@0 | 4650 { | 
| Chris@0 | 4651     bool debug = false; | 
| Chris@0 | 4652     QString version = "(unknown version)"; | 
| Chris@0 | 4653 | 
| Chris@0 | 4654 #ifdef BUILD_DEBUG | 
| Chris@0 | 4655     debug = true; | 
| Chris@0 | 4656 #endif | 
| Chris@0 | 4657 #ifdef SV_VERSION | 
| Chris@0 | 4658 #ifdef SVNREV | 
| Chris@0 | 4659     version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV); | 
| Chris@0 | 4660 #else | 
| Chris@0 | 4661     version = tr("Release %1").arg(SV_VERSION); | 
| Chris@0 | 4662 #endif | 
| Chris@0 | 4663 #else | 
| Chris@0 | 4664 #ifdef SVNREV | 
| Chris@0 | 4665     version = tr("Unreleased : Revision %1").arg(SVNREV); | 
| Chris@0 | 4666 #endif | 
| Chris@0 | 4667 #endif | 
| Chris@0 | 4668 | 
| Chris@0 | 4669     QString aboutText; | 
| Chris@0 | 4670 | 
| Chris@0 | 4671     aboutText += tr("<h3>About Sonic Visualiser</h3>"); | 
| Chris@90 | 4672     aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for<br>semantic music analysis and annotation.</p>"); | 
| Chris@111 | 4673     aboutText += tr("<p>%1 : %2 configuration</p>") | 
| Chris@0 | 4674         .arg(version) | 
| Chris@0 | 4675         .arg(debug ? tr("Debug") : tr("Release")); | 
| Chris@0 | 4676 | 
| Chris@132 | 4677 #ifndef BUILD_STATIC | 
| Chris@132 | 4678     aboutText += tr("<br>Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); | 
| Chris@132 | 4679 #else | 
| Chris@132 | 4680 #ifdef QT_SHARED | 
| Chris@132 | 4681     aboutText += tr("<br>Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); | 
| Chris@132 | 4682 #endif | 
| Chris@132 | 4683 #endif | 
| Chris@132 | 4684 | 
| Chris@0 | 4685 #ifdef BUILD_STATIC | 
| Chris@0 | 4686     aboutText += tr("<p>Statically linked"); | 
| Chris@0 | 4687 #ifndef QT_SHARED | 
| Chris@0 | 4688     aboutText += tr("<br>With Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); | 
| Chris@0 | 4689 #endif | 
| Chris@0 | 4690 #ifdef HAVE_JACK | 
| Chris@93 | 4691 #ifdef JACK_VERSION | 
| Chris@0 | 4692     aboutText += tr("<br>With JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); | 
| Chris@93 | 4693 #else | 
| Chris@93 | 4694     aboutText += tr("<br>With JACK audio output © Paul Davis and Jack O'Quin"); | 
| Chris@93 | 4695 #endif | 
| Chris@0 | 4696 #endif | 
| Chris@0 | 4697 #ifdef HAVE_PORTAUDIO | 
| Chris@0 | 4698     aboutText += tr("<br>With PortAudio audio output © Ross Bencina and Phil Burk"); | 
| Chris@0 | 4699 #endif | 
| Chris@0 | 4700 #ifdef HAVE_OGGZ | 
| Chris@93 | 4701 #ifdef OGGZ_VERSION | 
| Chris@0 | 4702     aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); | 
| Chris@93 | 4703 #else | 
| Chris@93 | 4704     aboutText += tr("<br>With Ogg file decoder © CSIRO Australia"); | 
| Chris@93 | 4705 #endif | 
| Chris@0 | 4706 #endif | 
| Chris@0 | 4707 #ifdef HAVE_MAD | 
| Chris@93 | 4708 #ifdef MAD_VERSION | 
| Chris@0 | 4709     aboutText += tr("<br>With MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); | 
| Chris@93 | 4710 #else | 
| Chris@93 | 4711     aboutText += tr("<br>With MAD mp3 decoder © Underbit Technologies Inc"); | 
| Chris@93 | 4712 #endif | 
| Chris@0 | 4713 #endif | 
| Chris@0 | 4714 #ifdef HAVE_SAMPLERATE | 
| Chris@93 | 4715 #ifdef SAMPLERATE_VERSION | 
| Chris@0 | 4716     aboutText += tr("<br>With libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); | 
| Chris@93 | 4717 #else | 
| Chris@93 | 4718     aboutText += tr("<br>With libsamplerate © Erik de Castro Lopo"); | 
| Chris@93 | 4719 #endif | 
| Chris@0 | 4720 #endif | 
| Chris@0 | 4721 #ifdef HAVE_SNDFILE | 
| Chris@93 | 4722 #ifdef SNDFILE_VERSION | 
| Chris@0 | 4723     aboutText += tr("<br>With libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); | 
| Chris@93 | 4724 #else | 
| Chris@93 | 4725     aboutText += tr("<br>With libsndfile © Erik de Castro Lopo"); | 
| Chris@93 | 4726 #endif | 
| Chris@0 | 4727 #endif | 
| Chris@127 | 4728 #ifdef HAVE_FFTW3F | 
| Chris@93 | 4729 #ifdef FFTW3_VERSION | 
| Chris@0 | 4730     aboutText += tr("<br>With FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); | 
| Chris@93 | 4731 #else | 
| Chris@93 | 4732     aboutText += tr("<br>With FFTW3 © Matteo Frigo and MIT"); | 
| Chris@93 | 4733 #endif | 
| Chris@0 | 4734 #endif | 
| Chris@0 | 4735 #ifdef HAVE_VAMP | 
| Chris@114 | 4736     aboutText += tr("<br>With Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); | 
| Chris@0 | 4737 #endif | 
| Chris@0 | 4738     aboutText += tr("<br>With LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); | 
| Chris@0 | 4739     aboutText += tr("<br>With DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); | 
| Chris@69 | 4740 #ifdef HAVE_LIBLO | 
| Chris@93 | 4741 #ifdef LIBLO_VERSION | 
| Chris@69 | 4742     aboutText += tr("<br>With liblo Lite OSC library (v%1) © Steve Harris").arg(LIBLO_VERSION); | 
| Chris@93 | 4743 #else | 
| Chris@93 | 4744     aboutText += tr("<br>With liblo Lite OSC library © Steve Harris").arg(LIBLO_VERSION); | 
| Chris@93 | 4745 #endif | 
| Chris@70 | 4746     if (m_oscQueue && m_oscQueue->isOK()) { | 
| Chris@69 | 4747         aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL()); | 
| Chris@69 | 4748     } | 
| Chris@69 | 4749 #endif | 
| Chris@0 | 4750     aboutText += "</p>"; | 
| Chris@0 | 4751 #endif | 
| Chris@0 | 4752 | 
| Chris@0 | 4753     aboutText += | 
| Chris@90 | 4754         "<p>Sonic Visualiser Copyright © 2005 - 2007 Chris Cannam and<br>" | 
| Chris@90 | 4755         "Queen Mary, University of London.</p>" | 
| Chris@0 | 4756         "<p>This program is free software; you can redistribute it and/or<br>" | 
| Chris@0 | 4757         "modify it under the terms of the GNU General Public License as<br>" | 
| Chris@0 | 4758         "published by the Free Software Foundation; either version 2 of the<br>" | 
| Chris@0 | 4759         "License, or (at your option) any later version.<br>See the file " | 
| Chris@0 | 4760         "COPYING included with this distribution for more information.</p>"; | 
| Chris@0 | 4761 | 
| Chris@0 | 4762     QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText); | 
| Chris@0 | 4763 } | 
| Chris@0 | 4764 |