Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: Tony Chris@0: An intonation analysis and annotation tool Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@0: This file copyright 2006-2012 Chris Cannam and QMUL. Chris@0: Chris@0: This program is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU General Public License as Chris@0: published by the Free Software Foundation; either version 2 of the Chris@0: License, or (at your option) any later version. See the file Chris@0: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #include "../version.h" Chris@0: Chris@0: #include "MainWindow.h" Chris@95: #include "NetworkPermissionTester.h" Chris@6: #include "Analyser.h" Chris@6: Chris@0: #include "framework/Document.h" Chris@95: #include "framework/VersionTester.h" Chris@0: Chris@0: #include "view/Pane.h" Chris@0: #include "view/PaneStack.h" Chris@0: #include "data/model/WaveFileModel.h" matthiasm@26: #include "data/model/NoteModel.h" matthiasm@26: #include "data/model/FlexiNoteModel.h" matthiasm@42: #include "layer/FlexiNoteLayer.h" matthiasm@26: #include "data/model/NoteModel.h" Chris@0: #include "view/ViewManager.h" Chris@0: #include "base/Preferences.h" Chris@0: #include "layer/WaveformLayer.h" Chris@0: #include "layer/TimeInstantLayer.h" Chris@0: #include "layer/TimeValueLayer.h" Chris@0: #include "widgets/Fader.h" Chris@0: #include "view/Overview.h" Chris@0: #include "widgets/AudioDial.h" Chris@0: #include "widgets/IconLoader.h" Chris@0: #include "widgets/KeyReference.h" Chris@0: #include "audioio/AudioCallbackPlaySource.h" Chris@0: #include "audioio/AudioCallbackPlayTarget.h" Chris@0: #include "audioio/PlaySpeedRangeMapper.h" Chris@0: #include "base/Profiler.h" Chris@0: #include "base/UnitDatabase.h" Chris@0: #include "layer/ColourDatabase.h" Chris@0: matthiasm@26: #include "data/fileio/CSVFileWriter.h" matthiasm@26: #include "data/fileio/MIDIFileWriter.h" matthiasm@26: #include "rdf/RDFExporter.h" matthiasm@26: Chris@0: // For version information Chris@0: #include "vamp/vamp.h" Chris@0: #include "vamp-sdk/PluginBase.h" Chris@0: #include "plugin/api/ladspa.h" Chris@0: #include "plugin/api/dssi.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@2: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: using std::vector; Chris@0: Chris@0: Chris@0: MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : Chris@0: MainWindowBase(withAudioOutput, withOSCSupport, false), Chris@0: m_overview(0), Chris@0: m_mainMenusCreated(false), gyorgyf@45: m_intelligentActionOn(true), //GF: !!! temporary Chris@0: m_playbackMenu(0), gyorgyf@45: m_recentFilesMenu(0), Chris@0: m_rightButtonMenu(0), Chris@0: m_rightButtonPlaybackMenu(0), Chris@0: m_deleteSelectedAction(0), Chris@0: m_ffwdAction(0), Chris@0: m_rwdAction(0), Chris@0: m_keyReference(new KeyReference()) Chris@0: { Chris@0: setWindowTitle(QApplication::applicationName()); Chris@0: Chris@0: UnitDatabase *udb = UnitDatabase::getInstance(); Chris@0: udb->registerUnit("Hz"); Chris@0: udb->registerUnit("dB"); Chris@0: udb->registerUnit("s"); Chris@0: Chris@0: ColourDatabase *cdb = ColourDatabase::getInstance(); Chris@0: cdb->addColour(Qt::black, tr("Black")); Chris@0: cdb->addColour(Qt::darkRed, tr("Red")); Chris@0: cdb->addColour(Qt::darkBlue, tr("Blue")); Chris@0: cdb->addColour(Qt::darkGreen, tr("Green")); Chris@0: cdb->addColour(QColor(200, 50, 255), tr("Purple")); Chris@0: cdb->addColour(QColor(255, 150, 50), tr("Orange")); Chris@0: cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true); Chris@0: cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true); Chris@0: cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true); Chris@0: cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true); Chris@0: cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true); Chris@0: cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true); Chris@0: Chris@0: Preferences::getInstance()->setResampleOnLoad(true); Chris@0: Preferences::getInstance()->setSpectrogramSmoothing Chris@0: (Preferences::SpectrogramInterpolated); Chris@0: Chris@0: QSettings settings; Chris@0: Chris@0: settings.beginGroup("MainWindow"); Chris@0: settings.setValue("showstatusbar", false); Chris@0: settings.endGroup(); Chris@0: Chris@96: settings.beginGroup("Transformer"); Chris@96: settings.setValue("use-flexi-note-model", true); Chris@96: settings.endGroup(); Chris@96: Chris@73: settings.beginGroup("LayerDefaults"); Chris@73: settings.setValue("waveform", Chris@73: QString("") Chris@73: .arg(int(WaveformLayer::LinearScale)) Chris@73: .arg(int(WaveformLayer::MixChannels))); Chris@73: settings.endGroup(); Chris@73: Chris@6: m_viewManager->setAlignMode(false); Chris@6: m_viewManager->setPlaySoloMode(false); Chris@0: m_viewManager->setToolMode(ViewManager::NavigateMode); Chris@0: m_viewManager->setZoomWheelsEnabled(false); Chris@6: m_viewManager->setIlluminateLocalFeatures(true); Chris@0: m_viewManager->setShowWorkTitle(true); Chris@6: m_viewManager->setShowCentreLine(false); Chris@91: m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); Chris@0: Chris@0: QFrame *frame = new QFrame; Chris@0: setCentralWidget(frame); Chris@0: Chris@0: QGridLayout *layout = new QGridLayout; Chris@0: Chris@0: QScrollArea *scroll = new QScrollArea(frame); Chris@0: scroll->setWidgetResizable(true); Chris@0: scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); Chris@0: scroll->setFrameShape(QFrame::NoFrame); Chris@0: Chris@6: // We have a pane stack: it comes with the territory. However, we Chris@6: // have a fixed and known number of panes in it (e.g. 1) -- it Chris@6: // isn't variable Chris@0: m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); Chris@0: scroll->setWidget(m_paneStack); Chris@6: Chris@0: m_overview = new Overview(frame); Chris@0: m_overview->setViewManager(m_viewManager); Chris@0: m_overview->setFixedHeight(40); Chris@0: #ifndef _WIN32 Chris@0: // For some reason, the contents of the overview never appear if we Chris@0: // make this setting on Windows. I have no inclination at the moment Chris@0: // to track down the reason why. Chris@0: m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); Chris@0: #endif Chris@0: connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), Chris@0: this, SLOT(contextHelpChanged(const QString &))); Chris@0: Chris@0: m_panLayer = new WaveformLayer; Chris@0: m_panLayer->setChannelMode(WaveformLayer::MergeChannels); Chris@0: m_panLayer->setAggressiveCacheing(true); Chris@0: m_overview->addLayer(m_panLayer); Chris@0: Chris@0: if (m_viewManager->getGlobalDarkBackground()) { Chris@0: m_panLayer->setBaseColour Chris@0: (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); Chris@0: } else { Chris@0: m_panLayer->setBaseColour matthiasm@13: (ColourDatabase::getInstance()->getColourIndex(tr("Blue"))); Chris@0: } Chris@0: Chris@0: m_fader = new Fader(frame, false); Chris@0: connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@0: connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@0: Chris@0: m_playSpeed = new AudioDial(frame); Chris@0: m_playSpeed->setMinimum(0); Chris@0: m_playSpeed->setMaximum(200); Chris@0: m_playSpeed->setValue(100); Chris@0: m_playSpeed->setFixedWidth(24); Chris@0: m_playSpeed->setFixedHeight(24); Chris@0: m_playSpeed->setNotchesVisible(true); Chris@0: m_playSpeed->setPageStep(10); Chris@0: m_playSpeed->setObjectName(tr("Playback Speedup")); Chris@0: m_playSpeed->setDefaultValue(100); Chris@0: m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200)); Chris@0: m_playSpeed->setShowToolTip(true); Chris@0: connect(m_playSpeed, SIGNAL(valueChanged(int)), gyorgyf@27: this, SLOT(playSpeedChanged(int))); Chris@0: connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); Chris@0: connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); Chris@0: Chris@0: layout->setSpacing(4); Chris@6: layout->addWidget(m_overview, 0, 1); Chris@6: layout->addWidget(scroll, 1, 1); Chris@0: Chris@0: layout->setColumnStretch(1, 10); Chris@0: Chris@0: frame->setLayout(layout); Chris@0: gyorgyf@21: m_analyser = new Analyser(); gyorgyf@21: Chris@0: setupMenus(); Chris@0: setupToolbars(); Chris@0: setupHelpMenu(); Chris@0: Chris@0: statusBar(); Chris@0: Chris@95: newSession(); Chris@6: Chris@95: settings.beginGroup("MainWindow"); Chris@95: settings.setValue("zoom-default", 512); Chris@95: settings.endGroup(); Chris@95: zoomDefault(); Chris@95: Chris@95: NetworkPermissionTester tester; Chris@95: bool networkPermission = tester.havePermission(); Chris@95: if (networkPermission) { Chris@95: m_versionTester = new VersionTester Chris@95: ("sonicvisualiser.org", "latest-tony-version.txt", TONY_VERSION); Chris@95: connect(m_versionTester, SIGNAL(newerVersionAvailable(QString)), Chris@95: this, SLOT(newerVersionAvailable(QString))); Chris@95: } else { Chris@95: m_versionTester = 0; Chris@95: } Chris@0: } Chris@0: Chris@0: MainWindow::~MainWindow() Chris@0: { Chris@6: delete m_analyser; Chris@0: delete m_keyReference; Chris@0: Profiles::getInstance()->dump(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupMenus() Chris@0: { Chris@0: if (!m_mainMenusCreated) { Chris@0: m_rightButtonMenu = new QMenu(); Chris@0: } Chris@0: Chris@0: if (!m_mainMenusCreated) { Chris@0: CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); Chris@0: m_rightButtonMenu->addSeparator(); Chris@0: } Chris@0: Chris@0: setupFileMenu(); Chris@0: setupEditMenu(); Chris@0: setupViewMenu(); Chris@0: Chris@0: m_mainMenusCreated = true; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupFileMenu() Chris@0: { Chris@0: if (m_mainMenusCreated) return; Chris@0: Chris@0: QMenu *menu = menuBar()->addMenu(tr("&File")); Chris@0: menu->setTearOffEnabled(true); Chris@0: QToolBar *toolbar = addToolBar(tr("File Toolbar")); Chris@0: Chris@0: m_keyReference->setCategory(tr("File and Session Management")); Chris@0: Chris@0: IconLoader il; Chris@1: QIcon icon; Chris@1: QAction *action; Chris@0: Chris@0: icon = il.load("fileopen"); Chris@0: icon.addPixmap(il.loadPixmap("fileopen-22")); Chris@1: action = new QAction(icon, tr("&Open..."), this); Chris@0: action->setShortcut(tr("Ctrl+O")); Chris@1: action->setStatusTip(tr("Open a file")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(openFile())); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: toolbar->addAction(action); Chris@0: Chris@1: action = new QAction(tr("Open Lo&cation..."), this); Chris@0: action->setShortcut(tr("Ctrl+Shift+O")); Chris@1: action->setStatusTip(tr("Open a file from a remote URL")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: Chris@1: m_recentFilesMenu = menu->addMenu(tr("Open &Recent")); Chris@0: m_recentFilesMenu->setTearOffEnabled(true); Chris@0: setupRecentFilesMenu(); Chris@0: connect(&m_recentFiles, SIGNAL(recentChanged()), Chris@0: this, SLOT(setupRecentFilesMenu())); Chris@0: Chris@0: menu->addSeparator(); Chris@85: action = new QAction(tr("Export Pitch Track Data..."), this); Chris@85: action->setStatusTip(tr("Export pitch-track data to a file")); Chris@85: connect(action, SIGNAL(triggered()), this, SLOT(exportPitchLayer())); Chris@85: connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); Chris@85: menu->addAction(action); Chris@85: Chris@85: action = new QAction(tr("Export Note Data..."), this); Chris@85: action->setStatusTip(tr("Export note data to a file")); Chris@85: connect(action, SIGNAL(triggered()), this, SLOT(exportNoteLayer())); matthiasm@26: connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); matthiasm@26: menu->addAction(action); matthiasm@26: matthiasm@26: menu->addSeparator(); Chris@0: action = new QAction(il.load("exit"), tr("&Quit"), this); Chris@0: action->setShortcut(tr("Ctrl+Q")); Chris@0: action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName())); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(close())); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupEditMenu() Chris@0: { Chris@0: if (m_mainMenusCreated) return; Chris@0: Chris@0: QMenu *menu = menuBar()->addMenu(tr("&Edit")); Chris@0: menu->setTearOffEnabled(true); Chris@0: CommandHistory::getInstance()->registerMenu(menu); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupViewMenu() Chris@0: { Chris@0: if (m_mainMenusCreated) return; Chris@0: Chris@0: IconLoader il; Chris@0: Chris@0: QAction *action = 0; Chris@0: Chris@0: m_keyReference->setCategory(tr("Panning and Navigation")); Chris@0: Chris@0: QMenu *menu = menuBar()->addMenu(tr("&View")); Chris@0: menu->setTearOffEnabled(true); Chris@0: action = new QAction(tr("Scroll &Left"), this); Chris@0: action->setShortcut(tr("Left")); Chris@0: action->setStatusTip(tr("Scroll the current pane to the left")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft())); Chris@0: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); gyorgyf@27: Chris@0: action = new QAction(tr("Scroll &Right"), this); Chris@0: action->setShortcut(tr("Right")); Chris@0: action->setStatusTip(tr("Scroll the current pane to the right")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(scrollRight())); Chris@0: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); gyorgyf@27: Chris@0: action = new QAction(tr("&Jump Left"), this); Chris@0: action->setShortcut(tr("Ctrl+Left")); Chris@0: action->setStatusTip(tr("Scroll the current pane a big step to the left")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft())); Chris@0: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); gyorgyf@27: Chris@0: action = new QAction(tr("J&ump Right"), this); Chris@0: action->setShortcut(tr("Ctrl+Right")); Chris@0: action->setStatusTip(tr("Scroll the current pane a big step to the right")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(jumpRight())); Chris@0: connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: Chris@0: menu->addSeparator(); Chris@0: Chris@0: m_keyReference->setCategory(tr("Zoom")); Chris@0: Chris@0: action = new QAction(il.load("zoom-in"), Chris@0: tr("Zoom &In"), this); Chris@0: action->setShortcut(tr("Up")); Chris@0: action->setStatusTip(tr("Increase the zoom level")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(zoomIn())); Chris@0: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); gyorgyf@27: Chris@0: action = new QAction(il.load("zoom-out"), Chris@0: tr("Zoom &Out"), this); Chris@0: action->setShortcut(tr("Down")); Chris@0: action->setStatusTip(tr("Decrease the zoom level")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(zoomOut())); Chris@0: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); gyorgyf@27: Chris@0: action = new QAction(tr("Restore &Default Zoom"), this); Chris@0: action->setStatusTip(tr("Restore the zoom level to the default")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault())); Chris@0: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@0: menu->addAction(action); Chris@0: Chris@0: action = new QAction(il.load("zoom-fit"), Chris@0: tr("Zoom to &Fit"), this); Chris@0: action->setShortcut(tr("F")); Chris@0: action->setStatusTip(tr("Zoom to show the whole file")); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit())); Chris@0: connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupHelpMenu() Chris@0: { Chris@0: QMenu *menu = menuBar()->addMenu(tr("&Help")); Chris@0: menu->setTearOffEnabled(true); Chris@0: Chris@0: m_keyReference->setCategory(tr("Help")); Chris@0: Chris@0: IconLoader il; Chris@0: Chris@1: QString name = QApplication::applicationName(); Chris@1: Chris@0: QAction *action = new QAction(il.load("help"), Chris@0: tr("&Help Reference"), this); Chris@0: action->setShortcut(tr("F1")); Chris@1: action->setStatusTip(tr("Open the %1 reference manual").arg(name)); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(help())); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: Chris@0: action = new QAction(tr("&Key and Mouse Reference"), this); Chris@0: action->setShortcut(tr("F2")); Chris@1: action->setStatusTip(tr("Open a window showing the keystrokes you can use in %1").arg(name)); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(keyReference())); Chris@0: m_keyReference->registerShortcut(action); Chris@0: menu->addAction(action); Chris@0: Chris@1: action = new QAction(tr("%1 on the &Web").arg(name), this); Chris@1: action->setStatusTip(tr("Open the %1 website").arg(name)); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(website())); Chris@0: menu->addAction(action); Chris@0: Chris@1: action = new QAction(tr("&About %1").arg(name), this); Chris@1: action->setStatusTip(tr("Show information about %1").arg(name)); Chris@0: connect(action, SIGNAL(triggered()), this, SLOT(about())); Chris@0: menu->addAction(action); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupRecentFilesMenu() Chris@0: { Chris@0: m_recentFilesMenu->clear(); Chris@0: vector files = m_recentFiles.getRecent(); Chris@0: for (size_t i = 0; i < files.size(); ++i) { Chris@50: QAction *action = new QAction(files[i], this); Chris@50: connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); Chris@0: if (i == 0) { Chris@0: action->setShortcut(tr("Ctrl+R")); Chris@0: m_keyReference->registerShortcut Chris@0: (tr("Re-open"), Chris@50: action->shortcut().toString(), Chris@0: tr("Re-open the current or most recently opened file")); Chris@0: } Chris@50: m_recentFilesMenu->addAction(action); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::setupToolbars() Chris@0: { Chris@0: m_keyReference->setCategory(tr("Playback and Transport Controls")); Chris@0: Chris@0: IconLoader il; Chris@0: Chris@0: QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back")); Chris@0: menu->setTearOffEnabled(true); Chris@0: m_rightButtonMenu->addSeparator(); Chris@0: m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback")); Chris@0: Chris@0: QToolBar *toolbar = addToolBar(tr("Playback Toolbar")); Chris@0: Chris@0: QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"), Chris@0: tr("Rewind to Start")); Chris@0: rwdStartAction->setShortcut(tr("Home")); Chris@0: rwdStartAction->setStatusTip(tr("Rewind to the start")); Chris@0: connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart())); Chris@0: connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *m_rwdAction = toolbar->addAction(il.load("rewind"), Chris@0: tr("Rewind")); Chris@0: m_rwdAction->setShortcut(tr("PgUp")); Chris@0: m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch")); Chris@0: connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind())); Chris@0: connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *playAction = toolbar->addAction(il.load("playpause"), Chris@0: tr("Play / Pause")); Chris@0: playAction->setCheckable(true); Chris@0: playAction->setShortcut(tr("Space")); Chris@0: playAction->setStatusTip(tr("Start or stop playback from the current position")); Chris@0: connect(playAction, SIGNAL(triggered()), this, SLOT(play())); Chris@0: connect(m_playSource, SIGNAL(playStatusChanged(bool)), gyorgyf@27: playAction, SLOT(setChecked(bool))); Chris@0: connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: m_ffwdAction = toolbar->addAction(il.load("ffwd"), Chris@0: tr("Fast Forward")); Chris@0: m_ffwdAction->setShortcut(tr("PgDown")); Chris@0: m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch")); Chris@0: connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd())); Chris@0: connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"), Chris@0: tr("Fast Forward to End")); Chris@0: ffwdEndAction->setShortcut(tr("End")); Chris@0: ffwdEndAction->setStatusTip(tr("Fast-forward to the end")); Chris@0: connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd())); Chris@0: connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool))); Chris@6: Chris@0: toolbar = addToolBar(tr("Play Mode Toolbar")); Chris@0: Chris@0: QAction *psAction = toolbar->addAction(il.load("playselection"), Chris@0: tr("Constrain Playback to Selection")); Chris@0: psAction->setCheckable(true); Chris@0: psAction->setChecked(m_viewManager->getPlaySelectionMode()); Chris@0: psAction->setShortcut(tr("s")); Chris@0: psAction->setStatusTip(tr("Constrain playback to the selected regions")); Chris@0: connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), Chris@0: psAction, SLOT(setChecked(bool))); Chris@0: connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); Chris@0: connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *plAction = toolbar->addAction(il.load("playloop"), Chris@0: tr("Loop Playback")); Chris@0: plAction->setCheckable(true); Chris@0: plAction->setChecked(m_viewManager->getPlayLoopMode()); Chris@0: plAction->setShortcut(tr("l")); Chris@0: plAction->setStatusTip(tr("Loop playback")); Chris@0: connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), Chris@0: plAction, SLOT(setChecked(bool))); Chris@0: connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled())); Chris@0: connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: m_keyReference->registerShortcut(psAction); Chris@0: m_keyReference->registerShortcut(plAction); Chris@0: m_keyReference->registerShortcut(playAction); Chris@0: m_keyReference->registerShortcut(m_rwdAction); Chris@0: m_keyReference->registerShortcut(m_ffwdAction); Chris@0: m_keyReference->registerShortcut(rwdStartAction); Chris@0: m_keyReference->registerShortcut(ffwdEndAction); Chris@0: Chris@6: menu->addAction(playAction); Chris@0: menu->addAction(psAction); Chris@0: menu->addAction(plAction); Chris@0: menu->addSeparator(); Chris@0: menu->addAction(m_rwdAction); Chris@0: menu->addAction(m_ffwdAction); Chris@0: menu->addSeparator(); Chris@0: menu->addAction(rwdStartAction); Chris@0: menu->addAction(ffwdEndAction); Chris@0: menu->addSeparator(); Chris@0: Chris@0: m_rightButtonPlaybackMenu->addAction(playAction); Chris@0: m_rightButtonPlaybackMenu->addAction(psAction); Chris@0: m_rightButtonPlaybackMenu->addAction(plAction); Chris@0: m_rightButtonPlaybackMenu->addSeparator(); Chris@0: m_rightButtonPlaybackMenu->addAction(m_rwdAction); Chris@0: m_rightButtonPlaybackMenu->addAction(m_ffwdAction); Chris@0: m_rightButtonPlaybackMenu->addSeparator(); Chris@0: m_rightButtonPlaybackMenu->addAction(rwdStartAction); Chris@0: m_rightButtonPlaybackMenu->addAction(ffwdEndAction); Chris@0: m_rightButtonPlaybackMenu->addSeparator(); Chris@0: Chris@0: QAction *fastAction = menu->addAction(tr("Speed Up")); Chris@0: fastAction->setShortcut(tr("Ctrl+PgUp")); Chris@0: fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch")); Chris@0: connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback())); Chris@0: connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *slowAction = menu->addAction(tr("Slow Down")); Chris@0: slowAction->setShortcut(tr("Ctrl+PgDown")); Chris@0: slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch")); Chris@0: connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback())); Chris@0: connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: QAction *normalAction = menu->addAction(tr("Restore Normal Speed")); Chris@0: normalAction->setShortcut(tr("Ctrl+Home")); Chris@0: normalAction->setStatusTip(tr("Restore non-time-stretched playback")); Chris@0: connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback())); Chris@0: connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool))); Chris@0: Chris@0: m_keyReference->registerShortcut(fastAction); Chris@0: m_keyReference->registerShortcut(slowAction); Chris@0: m_keyReference->registerShortcut(normalAction); Chris@0: Chris@0: m_rightButtonPlaybackMenu->addAction(fastAction); Chris@0: m_rightButtonPlaybackMenu->addAction(slowAction); Chris@0: m_rightButtonPlaybackMenu->addAction(normalAction); Chris@6: Chris@6: toolbar = addToolBar(tr("Playback Controls")); Chris@6: toolbar->addWidget(m_playSpeed); Chris@6: toolbar->addWidget(m_fader); Chris@0: Chris@70: toolbar = addToolBar(tr("Tools Toolbar")); Chris@70: Chris@70: CommandHistory::getInstance()->registerToolbar(toolbar); Chris@70: Chris@70: m_keyReference->setCategory(tr("Tool Selection")); Chris@70: Chris@70: toolbar = addToolBar(tr("Tools Toolbar")); Chris@70: QActionGroup *group = new QActionGroup(this); Chris@70: Chris@70: QAction *action = toolbar->addAction(il.load("navigate"), Chris@70: tr("Navigate")); Chris@70: action->setCheckable(true); Chris@70: action->setChecked(true); Chris@70: action->setShortcut(tr("1")); Chris@70: action->setStatusTip(tr("Navigate")); Chris@70: connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); Chris@70: connect(this, SIGNAL(replacedDocument()), action, SLOT(trigger())); Chris@70: group->addAction(action); Chris@70: m_keyReference->registerShortcut(action); Chris@70: Chris@70: action = toolbar->addAction(il.load("move"), Chris@70: tr("Edit")); Chris@70: action->setCheckable(true); Chris@70: action->setShortcut(tr("2")); Chris@70: action->setStatusTip(tr("Edit with Note Intelligence")); Chris@70: connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); Chris@70: connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); Chris@70: group->addAction(action); Chris@70: m_keyReference->registerShortcut(action); Chris@70: Chris@96: /* Remove for now... Chris@96: Chris@70: action = toolbar->addAction(il.load("notes"), Chris@70: tr("Free Edit")); Chris@70: action->setCheckable(true); Chris@70: action->setShortcut(tr("3")); Chris@70: action->setStatusTip(tr("Free Edit")); Chris@70: connect(action, SIGNAL(triggered()), this, SLOT(toolFreeEditSelected())); Chris@73: connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); Chris@70: group->addAction(action); Chris@70: m_keyReference->registerShortcut(action); Chris@96: */ Chris@0: Pane::registerShortcuts(*m_keyReference); Chris@0: } Chris@0: Chris@0: void Chris@70: MainWindow::toolNavigateSelected() Chris@70: { Chris@70: m_viewManager->setToolMode(ViewManager::NavigateMode); Chris@70: m_intelligentActionOn = true; Chris@70: } Chris@70: Chris@70: void Chris@70: MainWindow::toolEditSelected() Chris@70: { Chris@77: m_viewManager->setToolMode(ViewManager::NoteEditMode); Chris@70: m_intelligentActionOn = true; Chris@70: m_analyser->setIntelligentActions(m_intelligentActionOn); Chris@70: } Chris@70: Chris@70: void Chris@70: MainWindow::toolFreeEditSelected() Chris@70: { Chris@77: m_viewManager->setToolMode(ViewManager::NoteEditMode); Chris@70: m_intelligentActionOn = false; Chris@70: m_analyser->setIntelligentActions(m_intelligentActionOn); Chris@70: } Chris@70: gyorgyf@21: void Chris@0: MainWindow::updateMenuStates() Chris@0: { Chris@0: MainWindowBase::updateMenuStates(); Chris@0: Chris@0: Pane *currentPane = 0; Chris@0: Layer *currentLayer = 0; Chris@0: Chris@0: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@0: if (currentPane) currentLayer = currentPane->getSelectedLayer(); Chris@0: Chris@0: bool haveCurrentPane = Chris@0: (currentPane != 0); Chris@0: bool haveCurrentLayer = Chris@0: (haveCurrentPane && Chris@0: (currentLayer != 0)); Chris@0: bool haveSelection = gyorgyf@27: (m_viewManager && gyorgyf@27: !m_viewManager->getSelections().empty()); Chris@0: bool haveCurrentEditableLayer = gyorgyf@27: (haveCurrentLayer && gyorgyf@27: currentLayer->isLayerEditable()); Chris@0: bool haveCurrentTimeInstantsLayer = gyorgyf@27: (haveCurrentLayer && gyorgyf@27: qobject_cast(currentLayer)); Chris@0: bool haveCurrentTimeValueLayer = gyorgyf@27: (haveCurrentLayer && gyorgyf@27: qobject_cast(currentLayer)); Chris@0: Chris@0: emit canChangePlaybackSpeed(true); Chris@0: int v = m_playSpeed->value(); Chris@0: emit canSpeedUpPlayback(v < m_playSpeed->maximum()); Chris@0: emit canSlowDownPlayback(v > m_playSpeed->minimum()); Chris@0: Chris@0: if (m_ffwdAction && m_rwdAction) { Chris@0: if (haveCurrentTimeInstantsLayer) { Chris@0: m_ffwdAction->setText(tr("Fast Forward to Next Instant")); Chris@0: m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer")); Chris@0: m_rwdAction->setText(tr("Rewind to Previous Instant")); Chris@0: m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer")); Chris@0: } else if (haveCurrentTimeValueLayer) { Chris@0: m_ffwdAction->setText(tr("Fast Forward to Next Point")); Chris@0: m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer")); Chris@0: m_rwdAction->setText(tr("Rewind to Previous Point")); Chris@0: m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer")); Chris@0: } else { Chris@0: m_ffwdAction->setText(tr("Fast Forward")); Chris@0: m_ffwdAction->setStatusTip(tr("Fast forward")); Chris@0: m_rwdAction->setText(tr("Rewind")); Chris@0: m_rwdAction->setStatusTip(tr("Rewind")); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::updateDescriptionLabel() Chris@0: { Chris@6: // Nothing, we don't have one Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::documentModified() Chris@0: { Chris@0: MainWindowBase::documentModified(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::documentRestored() Chris@0: { Chris@0: MainWindowBase::documentRestored(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::newSession() Chris@0: { Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@0: closeSession(); Chris@0: createDocument(); Chris@0: m_document->setAutoAlignment(true); Chris@0: Chris@0: Pane *pane = m_paneStack->addPane(); Chris@0: Chris@0: connect(pane, SIGNAL(contextHelpChanged(const QString &)), Chris@0: this, SLOT(contextHelpChanged(const QString &))); Chris@0: Chris@6: // Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); Chris@6: // m_document->addLayerToView(pane, waveform); Chris@0: Chris@0: m_overview->registerView(pane); Chris@0: Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::closeSession() Chris@0: { Chris@0: if (!checkSaveModified()) return; Chris@0: Chris@0: while (m_paneStack->getPaneCount() > 0) { Chris@0: gyorgyf@27: Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1); Chris@0: gyorgyf@27: while (pane->getLayerCount() > 0) { gyorgyf@27: m_document->removeLayerFromView gyorgyf@27: (pane, pane->getLayer(pane->getLayerCount() - 1)); gyorgyf@27: } Chris@0: gyorgyf@27: m_overview->unregisterView(pane); gyorgyf@27: m_paneStack->deletePane(pane); Chris@0: } Chris@0: Chris@0: while (m_paneStack->getHiddenPaneCount() > 0) { Chris@0: gyorgyf@27: Pane *pane = m_paneStack->getHiddenPane gyorgyf@27: (m_paneStack->getHiddenPaneCount() - 1); Chris@0: gyorgyf@27: while (pane->getLayerCount() > 0) { gyorgyf@27: m_document->removeLayerFromView gyorgyf@27: (pane, pane->getLayer(pane->getLayerCount() - 1)); gyorgyf@27: } Chris@0: gyorgyf@27: m_overview->unregisterView(pane); gyorgyf@27: m_paneStack->deletePane(pane); Chris@0: } Chris@0: Chris@0: delete m_document; Chris@0: m_document = 0; Chris@0: m_viewManager->clearSelections(); Chris@0: m_timeRulerLayer = 0; // document owned this Chris@0: Chris@0: m_sessionFile = ""; Chris@0: Chris@0: CommandHistory::getInstance()->clear(); Chris@0: CommandHistory::getInstance()->documentSaved(); Chris@0: documentRestored(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openFile() Chris@0: { Chris@0: QString orig = m_audioFile; Chris@0: if (orig == "") orig = "."; Chris@0: else orig = QFileInfo(orig).absoluteDir().canonicalPath(); Chris@0: Chris@0: QString path = getOpenFileName(FileFinder::AnyFile); Chris@0: Chris@0: if (path.isEmpty()) return; Chris@0: Chris@1: FileOpenStatus status = open(path, ReplaceSession); Chris@0: Chris@0: if (status == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("File open failed

File \"%1\" could not be opened").arg(path)); Chris@0: } else if (status == FileOpenWrongMode) { Chris@0: QMessageBox::critical(this, tr("Failed to open file"), Chris@0: tr("Audio required

Please load at least one audio file before importing annotation data")); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openLocation() Chris@0: { Chris@0: QSettings settings; Chris@0: settings.beginGroup("MainWindow"); Chris@0: QString lastLocation = settings.value("lastremote", "").toString(); Chris@0: Chris@0: bool ok = false; Chris@0: QString text = QInputDialog::getText Chris@0: (this, tr("Open Location"), Chris@0: tr("Please enter the URL of the location to open:"), Chris@0: QLineEdit::Normal, lastLocation, &ok); Chris@0: Chris@0: if (!ok) return; Chris@0: Chris@0: settings.setValue("lastremote", text); Chris@0: Chris@0: if (text.isEmpty()) return; Chris@0: Chris@1: FileOpenStatus status = open(text, ReplaceSession); Chris@0: Chris@0: if (status == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open location"), Chris@0: tr("Open failed

URL \"%1\" could not be opened").arg(text)); Chris@0: } else if (status == FileOpenWrongMode) { Chris@0: QMessageBox::critical(this, tr("Failed to open location"), Chris@0: tr("Audio required

Please load at least one audio file before importing annotation data")); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::openRecentFile() Chris@0: { Chris@0: QObject *obj = sender(); Chris@3: QAction *action = qobject_cast(obj); Chris@0: Chris@0: if (!action) { Chris@70: cerr << "WARNING: MainWindow::openRecentFile: sender is not an action" Chris@70: << endl; gyorgyf@27: return; Chris@0: } Chris@0: Chris@0: QString path = action->text(); Chris@0: if (path == "") return; Chris@0: Chris@1: FileOpenStatus status = open(path, ReplaceSession); Chris@0: Chris@0: if (status == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open location"), Chris@0: tr("Open failed

File or URL \"%1\" could not be opened").arg(path)); Chris@0: } else if (status == FileOpenWrongMode) { Chris@0: QMessageBox::critical(this, tr("Failed to open location"), Chris@0: tr("Audio required

Please load at least one audio file before importing annotation data")); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paneAdded(Pane *pane) Chris@0: { Chris@6: pane->setPlaybackFollow(PlaybackScrollPage); Chris@0: m_paneStack->sizePanesEqually(); Chris@0: if (m_overview) m_overview->registerView(pane); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paneHidden(Pane *pane) Chris@0: { Chris@0: if (m_overview) m_overview->unregisterView(pane); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paneAboutToBeDeleted(Pane *pane) Chris@0: { Chris@0: if (m_overview) m_overview->unregisterView(pane); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paneDropAccepted(Pane *pane, QStringList uriList) Chris@0: { Chris@4: if (pane) m_paneStack->setCurrentPane(pane); Chris@0: Chris@0: for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) { Chris@0: Chris@1: FileOpenStatus status = open(*i, ReplaceSession); Chris@0: Chris@0: if (status == FileOpenFailed) { Chris@0: QMessageBox::critical(this, tr("Failed to open dropped URL"), Chris@0: tr("Open failed

Dropped URL \"%1\" could not be opened").arg(*i)); Chris@0: } else if (status == FileOpenWrongMode) { Chris@0: QMessageBox::critical(this, tr("Failed to open dropped URL"), Chris@0: tr("Audio required

Please load at least one audio file before importing annotation data")); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::paneDropAccepted(Pane *pane, QString text) Chris@0: { Chris@0: if (pane) m_paneStack->setCurrentPane(pane); Chris@0: Chris@0: QUrl testUrl(text); Chris@0: if (testUrl.scheme() == "file" || Chris@0: testUrl.scheme() == "http" || Chris@0: testUrl.scheme() == "ftp") { Chris@0: QStringList list; Chris@0: list.push_back(text); Chris@0: paneDropAccepted(pane, list); Chris@0: return; Chris@0: } Chris@0: Chris@0: //!!! open as text -- but by importing as if a CSV, or just adding Chris@0: //to a text layer? Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::closeEvent(QCloseEvent *e) Chris@0: { Chris@70: // cerr << "MainWindow::closeEvent" << endl; Chris@0: Chris@0: if (m_openingAudioFile) { Chris@70: // cerr << "Busy - ignoring close event" << endl; gyorgyf@27: e->ignore(); gyorgyf@27: return; Chris@0: } Chris@0: Chris@0: if (!m_abandoning && !checkSaveModified()) { Chris@70: // cerr << "Ignoring close event" << endl; gyorgyf@27: e->ignore(); gyorgyf@27: return; Chris@0: } Chris@0: Chris@0: QSettings settings; Chris@0: settings.beginGroup("MainWindow"); Chris@0: settings.setValue("size", size()); Chris@0: settings.setValue("position", pos()); Chris@0: settings.endGroup(); Chris@0: Chris@0: delete m_keyReference; Chris@0: m_keyReference = 0; Chris@0: Chris@0: closeSession(); Chris@0: Chris@0: e->accept(); Chris@0: return; Chris@0: } Chris@0: Chris@0: bool Chris@0: MainWindow::commitData(bool mayAskUser) Chris@0: { Chris@0: if (mayAskUser) { Chris@0: bool rv = checkSaveModified(); Chris@0: return rv; Chris@0: } else { Chris@0: if (!m_documentModified) return true; Chris@0: Chris@0: // If we can't check with the user first, then we can't save Chris@0: // to the original session file (even if we have it) -- have Chris@0: // to use a temporary file Chris@0: Chris@0: QString svDirBase = ".sv1"; Chris@0: QString svDir = QDir::home().filePath(svDirBase); Chris@0: Chris@0: if (!QFileInfo(svDir).exists()) { Chris@0: if (!QDir::home().mkdir(svDirBase)) return false; Chris@0: } else { Chris@0: if (!QFileInfo(svDir).isDir()) return false; Chris@0: } Chris@0: Chris@0: // This name doesn't have to be unguessable Chris@0: #ifndef _WIN32 Chris@0: QString fname = QString("tmp-%1-%2.sv") Chris@0: .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")) Chris@0: .arg(QProcess().pid()); Chris@0: #else Chris@0: QString fname = QString("tmp-%1.sv") Chris@0: .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")); Chris@0: #endif Chris@0: QString fpath = QDir(svDir).filePath(fname); Chris@0: if (saveSessionFile(fpath)) { Chris@0: m_recentFiles.addFile(fpath); Chris@0: return true; Chris@0: } else { Chris@0: return false; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: bool Chris@0: MainWindow::checkSaveModified() Chris@0: { Chris@0: // Called before some destructive operation (e.g. new session, Chris@0: // exit program). Return true if we can safely proceed, false to Chris@0: // cancel. Chris@0: Chris@0: if (!m_documentModified) return true; Chris@0: Chris@0: int button = gyorgyf@27: QMessageBox::warning(this, gyorgyf@27: tr("Session modified"), gyorgyf@27: tr("The current session has been modified.\nDo you want to save it?"), gyorgyf@27: QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, Chris@0: QMessageBox::Yes); Chris@0: Chris@0: if (button == QMessageBox::Yes) { gyorgyf@27: saveSession(); gyorgyf@27: if (m_documentModified) { // save failed -- don't proceed! gyorgyf@27: return false; gyorgyf@27: } else { Chris@0: return true; // saved, so it's safe to continue now Chris@0: } Chris@0: } else if (button == QMessageBox::No) { gyorgyf@27: m_documentModified = false; // so we know to abandon it gyorgyf@27: return true; Chris@0: } Chris@0: Chris@0: // else cancel Chris@0: return false; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::saveSession() Chris@0: { Chris@0: if (m_sessionFile != "") { gyorgyf@27: if (!saveSessionFile(m_sessionFile)) { gyorgyf@27: QMessageBox::critical(this, tr("Failed to save file"), gyorgyf@27: tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); Chris@0: } else { gyorgyf@27: CommandHistory::getInstance()->documentSaved(); gyorgyf@27: documentRestored(); gyorgyf@27: } gyorgyf@27: } else { gyorgyf@27: saveSessionAs(); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::saveSessionAs() Chris@0: { Chris@0: QString orig = m_audioFile; Chris@0: if (orig == "") orig = "."; Chris@0: else orig = QFileInfo(orig).absoluteDir().canonicalPath(); Chris@0: Chris@0: QString path = getSaveFileName(FileFinder::SessionFile); Chris@0: Chris@0: if (path == "") return; Chris@0: Chris@0: if (!saveSessionFile(path)) { Chris@85: QMessageBox::critical(this, tr("Failed to save file"), Chris@85: tr("Session file \"%1\" could not be saved.").arg(path)); Chris@0: } else { Chris@85: setWindowTitle(tr("%1: %2") Chris@0: .arg(QApplication::applicationName()) Chris@85: .arg(QFileInfo(path).fileName())); Chris@85: m_sessionFile = path; Chris@85: CommandHistory::getInstance()->documentSaved(); Chris@85: documentRestored(); Chris@0: m_recentFiles.addFile(path); Chris@0: } Chris@0: } Chris@0: Chris@85: QString Chris@85: MainWindow::exportToSVL(QString path, Layer *layer) Chris@85: { Chris@85: Model *model = layer->getModel(); Chris@85: Chris@85: QFile file(path); Chris@85: if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { Chris@85: return tr("Failed to open file %1 for writing").arg(path); Chris@85: } else { Chris@85: QTextStream out(&file); Chris@85: out << "\n" Chris@85: << "\n" Chris@85: << "\n" Chris@85: << " \n"; Chris@85: Chris@85: model->toXml(out, " "); Chris@85: Chris@85: out << " \n" Chris@85: << " \n"; Chris@85: Chris@85: layer->toXml(out, " "); Chris@85: Chris@85: out << " \n" Chris@85: << "\n"; Chris@85: Chris@85: return ""; Chris@85: } Chris@85: } Chris@85: Chris@0: void Chris@85: MainWindow::exportPitchLayer() matthiasm@26: { Chris@85: Layer *layer = 0; Chris@85: for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Chris@85: Pane *pane = m_paneStack->getPane(i); Chris@85: for (int j = 0; j < pane->getLayerCount(); ++j) { Chris@85: layer = qobject_cast(pane->getLayer(j)); Chris@85: if (layer) break; Chris@85: } Chris@85: if (layer) break; Chris@85: } matthiasm@26: matthiasm@26: if (!layer) return; matthiasm@26: Chris@85: SparseTimeValueModel *model = Chris@85: qobject_cast(layer->getModel()); matthiasm@26: if (!model) return; matthiasm@26: Chris@96: FileFinder::FileType type = FileFinder::LayerFileNoMidiNonSV; matthiasm@26: matthiasm@26: QString path = getSaveFileName(type); matthiasm@26: matthiasm@26: if (path == "") return; matthiasm@26: matthiasm@26: if (QFileInfo(path).suffix() == "") path += ".svl"; matthiasm@26: matthiasm@26: QString suffix = QFileInfo(path).suffix().toLower(); matthiasm@26: matthiasm@26: QString error; matthiasm@26: matthiasm@26: if (suffix == "xml" || suffix == "svl") { matthiasm@26: Chris@85: error = exportToSVL(path, layer); matthiasm@26: matthiasm@26: } else if (suffix == "ttl" || suffix == "n3") { matthiasm@26: Chris@85: RDFExporter exporter(path, model); Chris@85: exporter.write(); Chris@85: if (!exporter.isOK()) { Chris@85: error = exporter.getError(); Chris@85: } Chris@85: Chris@85: } else { Chris@85: Chris@85: CSVFileWriter writer(path, model, Chris@85: ((suffix == "csv") ? "," : "\t")); Chris@85: writer.write(); Chris@85: Chris@85: if (!writer.isOK()) { Chris@85: error = writer.getError(); Chris@85: } Chris@85: } Chris@85: Chris@85: if (error != "") { Chris@85: QMessageBox::critical(this, tr("Failed to write file"), error); Chris@85: } else { Chris@85: m_recentFiles.addFile(path); Chris@85: emit activity(tr("Export layer to \"%1\"").arg(path)); Chris@85: } Chris@85: } Chris@85: Chris@85: void Chris@85: MainWindow::exportNoteLayer() Chris@85: { Chris@85: Layer *layer = 0; Chris@85: for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Chris@85: Pane *pane = m_paneStack->getPane(i); Chris@85: for (int j = 0; j < pane->getLayerCount(); ++j) { Chris@85: layer = qobject_cast(pane->getLayer(j)); Chris@85: if (layer) break; Chris@85: } Chris@85: if (layer) break; Chris@85: } Chris@85: Chris@85: if (!layer) return; Chris@85: Chris@85: FlexiNoteModel *model = qobject_cast(layer->getModel()); Chris@85: if (!model) return; Chris@85: Chris@96: FileFinder::FileType type = FileFinder::LayerFileNonSV; Chris@85: Chris@85: QString path = getSaveFileName(type); Chris@85: Chris@85: if (path == "") return; Chris@85: Chris@85: if (QFileInfo(path).suffix() == "") path += ".svl"; Chris@85: Chris@85: QString suffix = QFileInfo(path).suffix().toLower(); Chris@85: Chris@85: QString error; Chris@85: Chris@85: if (suffix == "xml" || suffix == "svl") { Chris@85: Chris@85: error = exportToSVL(path, layer); Chris@85: Chris@85: } else if (suffix == "mid" || suffix == "midi") { Chris@85: Chris@85: MIDIFileWriter writer(path, model, model->getSampleRate()); Chris@85: writer.write(); Chris@85: if (!writer.isOK()) { Chris@85: error = writer.getError(); Chris@85: } Chris@85: Chris@85: } else if (suffix == "ttl" || suffix == "n3") { Chris@85: Chris@85: RDFExporter exporter(path, model); Chris@85: exporter.write(); Chris@85: if (!exporter.isOK()) { Chris@85: error = exporter.getError(); matthiasm@26: } matthiasm@26: matthiasm@26: } else { matthiasm@26: matthiasm@26: CSVFileWriter writer(path, model, matthiasm@26: ((suffix == "csv") ? "," : "\t")); matthiasm@26: writer.write(); matthiasm@26: matthiasm@26: if (!writer.isOK()) { matthiasm@26: error = writer.getError(); matthiasm@26: } matthiasm@26: } matthiasm@26: matthiasm@26: if (error != "") { matthiasm@26: QMessageBox::critical(this, tr("Failed to write file"), error); matthiasm@26: } else { matthiasm@26: m_recentFiles.addFile(path); matthiasm@26: emit activity(tr("Export layer to \"%1\"").arg(path)); matthiasm@26: } matthiasm@26: } matthiasm@26: matthiasm@26: matthiasm@26: void Chris@0: MainWindow::renameCurrentLayer() Chris@0: { Chris@0: Pane *pane = m_paneStack->getCurrentPane(); Chris@0: if (pane) { gyorgyf@27: Layer *layer = pane->getSelectedLayer(); gyorgyf@27: if (layer) { gyorgyf@27: bool ok = false; gyorgyf@27: QString newName = QInputDialog::getText gyorgyf@27: (this, tr("Rename Layer"), gyorgyf@27: tr("New name for this layer:"), gyorgyf@27: QLineEdit::Normal, layer->objectName(), &ok); gyorgyf@27: if (ok) { gyorgyf@27: layer->setObjectName(newName); gyorgyf@27: } gyorgyf@27: } Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::playSpeedChanged(int position) Chris@0: { Chris@0: PlaySpeedRangeMapper mapper(0, 200); Chris@0: Chris@0: float percent = m_playSpeed->mappedValue(); Chris@0: float factor = mapper.getFactorForValue(percent); Chris@0: Chris@70: cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << endl; Chris@0: Chris@0: bool something = (position != 100); Chris@0: Chris@0: int pc = lrintf(percent); Chris@0: Chris@0: if (!something) { Chris@0: contextHelpChanged(tr("Playback speed: Normal")); Chris@0: } else { Chris@0: contextHelpChanged(tr("Playback speed: %1%2%") Chris@0: .arg(position > 100 ? "+" : "") Chris@0: .arg(pc)); Chris@0: } Chris@0: Chris@0: m_playSource->setTimeStretch(factor); Chris@0: Chris@0: updateMenuStates(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::playSharpenToggled() Chris@0: { Chris@0: QSettings settings; Chris@0: settings.beginGroup("MainWindow"); Chris@0: settings.setValue("playsharpen", m_playSharpen->isChecked()); Chris@0: settings.endGroup(); Chris@0: Chris@0: playSpeedChanged(m_playSpeed->value()); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::playMonoToggled() Chris@0: { Chris@0: QSettings settings; Chris@0: settings.beginGroup("MainWindow"); Chris@0: settings.setValue("playmono", m_playMono->isChecked()); Chris@0: settings.endGroup(); Chris@0: Chris@0: playSpeedChanged(m_playSpeed->value()); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::speedUpPlayback() Chris@0: { Chris@0: int value = m_playSpeed->value(); Chris@0: value = value + m_playSpeed->pageStep(); Chris@0: if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum(); Chris@0: m_playSpeed->setValue(value); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::slowDownPlayback() Chris@0: { Chris@0: int value = m_playSpeed->value(); Chris@0: value = value - m_playSpeed->pageStep(); Chris@0: if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum(); Chris@0: m_playSpeed->setValue(value); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::restoreNormalPlayback() Chris@0: { Chris@0: m_playSpeed->setValue(m_playSpeed->defaultValue()); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::updateVisibleRangeDisplay(Pane *p) const Chris@0: { Chris@0: if (!getMainModel() || !p) { Chris@0: return; Chris@0: } Chris@0: Chris@0: bool haveSelection = false; Chris@0: size_t startFrame = 0, endFrame = 0; Chris@0: Chris@0: if (m_viewManager && m_viewManager->haveInProgressSelection()) { Chris@0: Chris@0: bool exclusive = false; Chris@0: Selection s = m_viewManager->getInProgressSelection(exclusive); Chris@0: Chris@0: if (!s.isEmpty()) { Chris@0: haveSelection = true; Chris@0: startFrame = s.getStartFrame(); Chris@0: endFrame = s.getEndFrame(); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!haveSelection) { Chris@0: startFrame = p->getFirstVisibleFrame(); Chris@0: endFrame = p->getLastVisibleFrame(); Chris@0: } Chris@0: Chris@0: RealTime start = RealTime::frame2RealTime Chris@0: (startFrame, getMainModel()->getSampleRate()); Chris@0: Chris@0: RealTime end = RealTime::frame2RealTime Chris@0: (endFrame, getMainModel()->getSampleRate()); Chris@0: Chris@0: RealTime duration = end - start; Chris@0: Chris@0: QString startStr, endStr, durationStr; Chris@0: startStr = start.toText(true).c_str(); Chris@0: endStr = end.toText(true).c_str(); Chris@0: durationStr = duration.toText(true).c_str(); Chris@0: Chris@0: if (haveSelection) { Chris@0: m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)") Chris@0: .arg(startStr).arg(endStr).arg(durationStr); Chris@0: } else { Chris@0: m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)") Chris@0: .arg(startStr).arg(endStr).arg(durationStr); Chris@0: } matthiasm@42: matthiasm@42: // scale Y axis matthiasm@42: FlexiNoteLayer *fnl = dynamic_cast(p->getLayer(2)); matthiasm@42: if (fnl) { matthiasm@42: fnl->setVerticalRangeToNoteRange(p); matthiasm@42: } matthiasm@42: Chris@0: statusBar()->showMessage(m_myStatusMessage); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::updatePositionStatusDisplays() const Chris@0: { Chris@0: if (!statusBar()->isVisible()) return; Chris@0: Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::outputLevelsChanged(float left, float right) Chris@0: { Chris@0: m_fader->setPeakLeft(left); Chris@0: m_fader->setPeakRight(right); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::sampleRateMismatch(size_t requested, size_t actual, Chris@0: bool willResample) Chris@0: { Chris@0: updateDescriptionLabel(); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::audioOverloadPluginDisabled() Chris@0: { Chris@0: QMessageBox::information Chris@0: (this, tr("Audio processing overload"), Chris@0: tr("Overloaded

Audio effects plugin auditioning has been disabled due to a processing overload.")); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::audioTimeStretchMultiChannelDisabled() Chris@0: { Chris@0: static bool shownOnce = false; Chris@0: if (shownOnce) return; Chris@0: QMessageBox::information Chris@0: (this, tr("Audio processing overload"), Chris@0: tr("Overloaded

Audio playback speed processing has been reduced to a single channel, due to a processing overload.")); Chris@0: shownOnce = true; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::layerRemoved(Layer *layer) Chris@0: { Chris@0: MainWindowBase::layerRemoved(layer); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::layerInAView(Layer *layer, bool inAView) Chris@0: { Chris@0: MainWindowBase::layerInAView(layer, inAView); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelAdded(Model *model) Chris@0: { Chris@0: MainWindowBase::modelAdded(model); Chris@3: DenseTimeValueModel *dtvm = qobject_cast(model); Chris@0: if (dtvm) { Chris@70: cerr << "A dense time-value model (such as an audio file) has been loaded" << endl; Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelAboutToBeDeleted(Model *model) Chris@0: { Chris@0: MainWindowBase::modelAboutToBeDeleted(model); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::mainModelChanged(WaveFileModel *model) Chris@0: { Chris@0: m_panLayer->setModel(model); Chris@0: Chris@0: MainWindowBase::mainModelChanged(model); Chris@0: Chris@0: if (m_playTarget) { Chris@0: connect(m_fader, SIGNAL(valueChanged(float)), Chris@0: m_playTarget, SLOT(setOutputGain(float))); Chris@0: } Chris@72: Chris@72: if (model) { Chris@72: if (m_paneStack) { Chris@72: Pane *pane = m_paneStack->getCurrentPane(); Chris@72: if (!pane) { Chris@72: pane = m_paneStack->addPane(); Chris@77: Chris@77: //!!! ugly. a waveform "shadow layer" might be nicer Chris@73: Pane *p2 = m_paneStack->addPane(); Chris@77: p2->setFixedHeight(60); Chris@73: m_document->addLayerToView Chris@73: (p2, Chris@73: m_document->createMainModelLayer(LayerFactory::TimeRuler)); Chris@73: m_document->addLayerToView Chris@73: (p2, Chris@73: m_document->createMainModelLayer(LayerFactory::Waveform)); Chris@77: m_paneStack->sizePanesEqually(); Chris@72: } Chris@72: if (pane) { Chris@72: m_analyser->newFileLoaded Chris@72: (m_document, getMainModel(), m_paneStack, pane); Chris@72: } Chris@72: } Chris@72: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelGenerationFailed(QString transformName, QString message) Chris@0: { Chris@0: if (message != "") { Chris@0: Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to generate layer"), Chris@0: tr("Layer generation failed

Failed to generate derived layer.

The layer transform \"%1\" failed:

%2") Chris@0: .arg(transformName).arg(message), Chris@0: QMessageBox::Ok); Chris@0: } else { Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to generate layer"), Chris@0: tr("Layer generation failed

Failed to generate a derived layer.

The layer transform \"%1\" failed.

No error information is available.") Chris@0: .arg(transformName), Chris@0: QMessageBox::Ok); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelGenerationWarning(QString transformName, QString message) Chris@0: { Chris@0: QMessageBox::warning Chris@0: (this, tr("Warning"), message, QMessageBox::Ok); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelRegenerationFailed(QString layerName, Chris@0: QString transformName, QString message) Chris@0: { Chris@0: if (message != "") { Chris@0: Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to regenerate layer"), Chris@0: tr("Layer generation failed

Failed to regenerate derived layer \"%1\" using new data model as input.

The layer transform \"%2\" failed:

%3") Chris@0: .arg(layerName).arg(transformName).arg(message), Chris@0: QMessageBox::Ok); Chris@0: } else { Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to regenerate layer"), Chris@0: tr("Layer generation failed

Failed to regenerate derived layer \"%1\" using new data model as input.

The layer transform \"%2\" failed.

No error information is available.") Chris@0: .arg(layerName).arg(transformName), Chris@0: QMessageBox::Ok); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::modelRegenerationWarning(QString layerName, Chris@0: QString transformName, QString message) Chris@0: { Chris@0: QMessageBox::warning Chris@0: (this, tr("Warning"), tr("Warning when regenerating layer

When regenerating the derived layer \"%1\" using new data model as input:

%2").arg(layerName).arg(message), QMessageBox::Ok); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::alignmentFailed(QString transformName, QString message) Chris@0: { Chris@0: QMessageBox::warning Chris@0: (this, Chris@0: tr("Failed to calculate alignment"), Chris@0: tr("Alignment calculation failed

Failed to calculate an audio alignment using transform \"%1\":

%2") Chris@0: .arg(transformName).arg(message), Chris@0: QMessageBox::Ok); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position) Chris@0: { Chris@70: // cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl; Chris@0: m_paneStack->setCurrentPane(pane); Chris@0: m_rightButtonMenu->popup(position); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::handleOSCMessage(const OSCMessage &message) Chris@0: { Chris@70: cerr << "MainWindow::handleOSCMessage: Not implemented" << endl; Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::mouseEnteredWidget() Chris@0: { Chris@3: QWidget *w = qobject_cast(sender()); Chris@0: if (!w) return; Chris@0: Chris@0: if (w == m_fader) { Chris@0: contextHelpChanged(tr("Adjust the master playback level")); Chris@0: } else if (w == m_playSpeed) { Chris@0: contextHelpChanged(tr("Adjust the master playback speed")); Chris@0: } else if (w == m_playSharpen && w->isEnabled()) { Chris@0: contextHelpChanged(tr("Toggle transient sharpening for playback time scaling")); Chris@0: } else if (w == m_playMono && w->isEnabled()) { Chris@0: contextHelpChanged(tr("Toggle mono mode for playback time scaling")); Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::mouseLeftWidget() Chris@0: { Chris@0: contextHelpChanged(""); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::website() Chris@0: { Chris@4: //!!! todo: URL! Chris@4: openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/")); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::help() Chris@0: { Chris@4: //!!! todo: help URL! Chris@4: openHelpUrl(tr("http://code.soundsoftware.ac.uk/projects/tony/")); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::about() Chris@0: { Chris@0: bool debug = false; Chris@0: QString version = "(unknown version)"; Chris@0: Chris@0: #ifdef BUILD_DEBUG Chris@0: debug = true; Chris@0: #endif Chris@0: version = tr("Release %1").arg(TONY_VERSION); Chris@0: Chris@0: QString aboutText; Chris@0: Chris@0: aboutText += tr("

About Tony

"); Chris@0: aboutText += tr("

Tony is a program for interactive note and pitch analysis and annotation.

"); Chris@0: aboutText += tr("

%1 : %2 configuration

") Chris@0: .arg(version) Chris@0: .arg(debug ? tr("Debug") : tr("Release")); Chris@0: Chris@0: aboutText += Chris@90: "

Copyright © 2005–2013 Chris Cannam, Matthias Mauch, George Fazekas, and Queen Mary University of London.

" Chris@90: "

This program is free software; you can redistribute it and/or " Chris@90: "modify it under the terms of the GNU General Public License as " Chris@90: "published by the Free Software Foundation; either version 2 of the " Chris@0: "License, or (at your option) any later version.
See the file " Chris@0: "COPYING included with this distribution for more information.

"; Chris@0: Chris@0: QMessageBox::about(this, tr("About %1").arg(QApplication::applicationName()), aboutText); Chris@0: } Chris@0: Chris@0: void Chris@0: MainWindow::keyReference() Chris@0: { Chris@0: m_keyReference->show(); Chris@0: } Chris@0: Chris@0: