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