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