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