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