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