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