diff main/MainWindow.cpp @ 200:1871581e4da9

* Split MainWindow out into MainWindowBase (pane stack management, basic file I/O etc) and MainWindow (widget structure, menus and actions etc)
author Chris Cannam
date Mon, 22 Oct 2007 14:24:31 +0000
parents 6e1d4d500092
children 0de89bce24e4
line wrap: on
line diff
--- a/main/MainWindow.cpp	Mon Oct 22 09:45:35 2007 +0000
+++ b/main/MainWindow.cpp	Mon Oct 22 14:24:31 2007 +0000
@@ -4,7 +4,7 @@
     Sonic Visualiser
     An audio file viewer and annotation editor.
     Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam and QMUL.
+    This file copyright 2006-2007 Chris Cannam and QMUL.
     
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License as
@@ -70,9 +70,6 @@
 #include "base/ColourDatabase.h"
 #include "osc/OSCQueue.h"
 
-//!!!
-#include "data/model/AggregateWaveModel.h"
-
 // For version information
 #include "vamp/vamp.h"
 #include "vamp-sdk/PluginBase.h"
@@ -115,17 +112,8 @@
 
 
 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
-    m_document(0),
-    m_paneStack(0),
-    m_viewManager(0),
+    MainWindowBase(withAudioOutput, withOSCSupport),
     m_overview(0),
-    m_timeRulerLayer(0),
-    m_audioOutput(withAudioOutput),
-    m_playSource(0),
-    m_playTarget(0),
-    m_oscQueue(withOSCSupport ? new OSCQueue() : 0),
-    m_recentFiles("RecentFiles", 20),
-    m_recentTransforms("RecentTransforms", 20),
     m_mainMenusCreated(false),
     m_paneMenu(0),
     m_layerMenu(0),
@@ -141,10 +129,6 @@
     m_rightButtonPlaybackMenu(0),
     m_ffwdAction(0),
     m_rwdAction(0),
-    m_documentModified(false),
-    m_openingAudioFile(false),
-    m_abandoning(false),
-    m_labeller(0),
     m_preferencesDialog(0),
     m_layerTreeView(0),
     m_keyReference(new KeyReference())
@@ -170,29 +154,10 @@
     cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
     cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
 
-    connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
-	    this, SLOT(documentModified()));
-    connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
-	    this, SLOT(documentRestored()));
-
     QFrame *frame = new QFrame;
     setCentralWidget(frame);
 
     QGridLayout *layout = new QGridLayout;
-    
-    m_viewManager = new ViewManager();
-    connect(m_viewManager, SIGNAL(selectionChanged()),
-	    this, SLOT(updateMenuStates()));
-    connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
-	    this, SLOT(inProgressSelectionChanged()));
-
-    Preferences::BackgroundMode mode =
-        Preferences::getInstance()->getBackgroundMode();
-    m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
-    if (mode != Preferences::BackgroundFromTheme) {
-        m_viewManager->setGlobalDarkBackground
-            (mode == Preferences::DarkBackground);
-    }
 
     m_descriptionLabel = new QLabel;
 
@@ -201,22 +166,6 @@
     scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     scroll->setFrameShape(QFrame::NoFrame);
 
-    m_paneStack = new PaneStack(scroll, m_viewManager);
-    connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
-	    this, SLOT(currentPaneChanged(Pane *)));
-    connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
-	    this, SLOT(currentLayerChanged(Pane *, Layer *)));
-    connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
-            this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
-    connect(m_paneStack, SIGNAL(propertyStacksResized()),
-            this, SLOT(propertyStacksResized()));
-    connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
-            this, SLOT(contextHelpChanged(const QString &)));
-    connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
-            this, SLOT(paneDropAccepted(Pane *, QStringList)));
-    connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
-            this, SLOT(paneDropAccepted(Pane *, QString)));
-
     scroll->setWidget(m_paneStack);
 
     m_overview = new Overview(frame);
@@ -242,14 +191,7 @@
     } else {
         m_panLayer->setBaseColour
             (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
-    }        
-
-    m_playSource = new AudioCallbackPlaySource(m_viewManager);
-
-    connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)),
-	    this,           SLOT(sampleRateMismatch(size_t, size_t, bool)));
-    connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
-            this,           SLOT(audioOverloadPluginDisabled()));
+    }
 
     m_fader = new Fader(frame, false);
     connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
@@ -318,45 +260,6 @@
 
     frame->setLayout(layout);
 
-    connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)),
-	    this, SLOT(outputLevelsChanged(float, float)));
-
-    connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)),
-            this, SLOT(playbackFrameChanged(unsigned long)));
-
-    connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)),
-            this, SLOT(globalCentreFrameChanged(unsigned long)));
-
-    connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)),
-            this, SLOT(viewCentreFrameChanged(View *, unsigned long)));
-
-    connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
-            this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
-
-    connect(Preferences::getInstance(),
-            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
-            this,
-            SLOT(preferenceChanged(PropertyContainer::PropertyName)));
-
-//    preferenceChanged("Property Box Layout");
-
-    if (m_oscQueue && m_oscQueue->isOK()) {
-        connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
-        QTimer *oscTimer = new QTimer(this);
-        connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
-        oscTimer->start(1000);
-    }
-
-    Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
-    settings.beginGroup("MainWindow");
-    labellerType = (Labeller::ValueType)
-        settings.value("labellertype", (int)labellerType).toInt();
-    int cycle = settings.value("labellercycle", 4).toInt();
-    settings.endGroup();
-
-    m_labeller = new Labeller(labellerType);
-    m_labeller->setCounterCycleSize(cycle);
-
     setupMenus();
     setupToolbars();
     setupHelpMenu();
@@ -368,81 +271,12 @@
 
 MainWindow::~MainWindow()
 {
-//    std::cerr << "MainWindow::~MainWindow()" << std::endl;
-
-    if (!m_abandoning) {
-        closeSession();
-    }
-    delete m_playTarget;
-    delete m_playSource;
-    delete m_viewManager;
-    delete m_oscQueue;
     delete m_keyReference;
     delete m_preferencesDialog;
     delete m_layerTreeView;
     Profiles::getInstance()->dump();
 }
 
-QString
-MainWindow::getOpenFileName(FileFinder::FileType type)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    switch (type) {
-    case FileFinder::SessionFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::AudioFile:
-        return ff->getOpenFileName(type, m_audioFile);
-    case FileFinder::LayerFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::LayerFileNoMidi:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::SessionOrAudioFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::ImageFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::AnyFile:
-        if (getMainModel() != 0 &&
-            m_paneStack != 0 &&
-            m_paneStack->getCurrentPane() != 0) { // can import a layer
-            return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
-        } else {
-            return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
-                                       m_sessionFile);
-        }
-    }
-    return "";
-}
-
-QString
-MainWindow::getSaveFileName(FileFinder::FileType type)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    switch (type) {
-    case FileFinder::SessionFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::AudioFile:
-        return ff->getSaveFileName(type, m_audioFile);
-    case FileFinder::LayerFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::LayerFileNoMidi:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::SessionOrAudioFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::ImageFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::AnyFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    }
-    return "";
-}
-
-void
-MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    ff->registerLastOpenedFilePath(type, path);
-}
-
 void
 MainWindow::setupMenus()
 {
@@ -777,7 +611,7 @@
             QActionGroup *cycleGroup = new QActionGroup(this);
 
             int cycles[] = { 2, 3, 4, 5, 6, 7, 8, 10, 12, 16 };
-            for (int i = 0; i < sizeof(cycles)/sizeof(cycles[0]); ++i) {
+            for (int i = 0; i < int(sizeof(cycles)/sizeof(cycles[0])); ++i) {
                 action = new QAction(QString("%1").arg(cycles[i]), this);
                 connect(action, SIGNAL(triggered()), this, SLOT(setInstantsCounterCycle()));
                 action->setCheckable(true);
@@ -1909,6 +1743,8 @@
 void
 MainWindow::updateMenuStates()
 {
+    MainWindowBase::updateMenuStates();
+
     Pane *currentPane = 0;
     Layer *currentLayer = 0;
 
@@ -1920,10 +1756,6 @@
     bool haveCurrentLayer =
         (haveCurrentPane &&
          (currentLayer != 0));
-    bool haveMainModel =
-	(getMainModel() != 0);
-    bool havePlayTarget =
-	(m_playTarget != 0);
     bool haveSelection = 
 	(m_viewManager &&
 	 !m_viewManager->getSelections().empty());
@@ -1936,40 +1768,11 @@
     bool haveCurrentTimeValueLayer = 
 	(haveCurrentLayer &&
 	 dynamic_cast<TimeValueLayer *>(currentLayer));
-    bool haveCurrentColour3DPlot =
-        (haveCurrentLayer &&
-         dynamic_cast<Colour3DPlotLayer *>(currentLayer));
-    bool haveClipboardContents =
-        (m_viewManager &&
-         !m_viewManager->getClipboard().empty());
-
-    emit canAddPane(haveMainModel);
-    emit canDeleteCurrentPane(haveCurrentPane);
-    emit canZoom(haveMainModel && haveCurrentPane);
-    emit canScroll(haveMainModel && haveCurrentPane);
-    emit canAddLayer(haveMainModel && haveCurrentPane);
-    emit canImportMoreAudio(haveMainModel);
-    emit canImportLayer(haveMainModel && haveCurrentPane);
-    emit canExportAudio(haveMainModel);
-    emit canExportLayer(haveMainModel &&
-                        (haveCurrentEditableLayer || haveCurrentColour3DPlot));
-    emit canExportImage(haveMainModel && haveCurrentPane);
-    emit canDeleteCurrentLayer(haveCurrentLayer);
-    emit canRenameLayer(haveCurrentLayer);
-    emit canEditLayer(haveCurrentEditableLayer);
-    emit canMeasureLayer(haveCurrentLayer);
-    emit canSelect(haveMainModel && haveCurrentPane);
-    emit canPlay(havePlayTarget);
-    emit canFfwd(true);
-    emit canRewind(true);
-    emit canPaste(haveCurrentEditableLayer && haveClipboardContents);
-    emit canInsertInstant(haveCurrentPane);
-    emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
-    emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
-    emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
-    emit canClearSelection(haveSelection);
-    emit canEditSelection(haveSelection && haveCurrentEditableLayer);
-    emit canSave(m_sessionFile != "" && m_documentModified);
+
+    emit canChangePlaybackSpeed(true);
+    int v = m_playSpeed->value();
+    emit canSpeedUpPlayback(v < m_playSpeed->maximum());
+    emit canSlowDownPlayback(v > m_playSpeed->minimum());
 
     if (m_viewManager && 
         (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
@@ -1982,11 +1785,6 @@
         m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer"));
     }
 
-    emit canChangePlaybackSpeed(true);
-    int v = m_playSpeed->value();
-    emit canSpeedUpPlayback(v < m_playSpeed->maximum());
-    emit canSlowDownPlayback(v > m_playSpeed->minimum());
-
     if (m_ffwdAction && m_rwdAction) {
         if (haveCurrentTimeInstantsLayer) {
             m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
@@ -2038,124 +1836,15 @@
 void
 MainWindow::documentModified()
 {
-//    std::cerr << "MainWindow::documentModified" << std::endl;
-
-    if (!m_documentModified) {
-	setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
-    }
-
-    m_documentModified = true;
-    updateMenuStates();
+    //!!!
+    MainWindowBase::documentModified();
 }
 
 void
 MainWindow::documentRestored()
 {
-//    std::cerr << "MainWindow::documentRestored" << std::endl;
-
-    if (m_documentModified) {
-	QString wt(windowTitle());
-	wt.replace(tr(" (modified)"), "");
-	setWindowTitle(wt);
-    }
-
-    m_documentModified = false;
-    updateMenuStates();
-}
-
-void
-MainWindow::playLoopToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlayLoopMode(action->isChecked());
-    } else {
-	m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
-    }
-}
-
-void
-MainWindow::playSelectionToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlaySelectionMode(action->isChecked());
-    } else {
-	m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
-    }
-}
-
-void
-MainWindow::playSoloToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlaySoloMode(action->isChecked());
-    } else {
-	m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
-    }
-
-    if (m_viewManager->getPlaySoloMode()) {
-        currentPaneChanged(m_paneStack->getCurrentPane());
-    } else {
-        m_viewManager->setPlaybackModel(0);
-        if (m_playSource) {
-            m_playSource->clearSoloModelSet();
-        }
-    }
-}
-
-void
-MainWindow::currentPaneChanged(Pane *p)
-{
-    updateMenuStates();
-    updateVisibleRangeDisplay(p);
-
-    if (!p) return;
-
-    if (!(m_viewManager &&
-          m_playSource &&
-          m_viewManager->getPlaySoloMode())) {
-        if (m_viewManager) m_viewManager->setPlaybackModel(0);
-        return;
-    }
-
-    Model *prevPlaybackModel = m_viewManager->getPlaybackModel();
-
-    View::ModelSet soloModels = p->getModels();
-    
-    for (View::ModelSet::iterator mi = soloModels.begin();
-         mi != soloModels.end(); ++mi) {
-        if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) {
-            m_viewManager->setPlaybackModel(*mi);
-        }
-    }
-    
-    RangeSummarisableTimeValueModel *a = 
-        dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel);
-    RangeSummarisableTimeValueModel *b = 
-        dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager->
-                                                        getPlaybackModel());
-
-    m_playSource->setSoloModelSet(soloModels);
-
-    if (a && b && (a != b)) {
-        int frame = m_playSource->getCurrentPlayingFrame();
-        //!!! I don't really believe that these functions are the right way around
-        int rframe = a->alignFromReference(frame);
-        int bframe = b->alignToReference(rframe);
-        if (m_playSource->isPlaying()) m_playSource->play(bframe);
-    }
-}
-
-void
-MainWindow::currentLayerChanged(Pane *p, Layer *)
-{
-    updateMenuStates();
-    updateVisibleRangeDisplay(p);
+    //!!!
+    MainWindowBase::documentRestored();
 }
 
 void
@@ -2195,331 +1884,6 @@
 //}
 
 void
-MainWindow::selectAll()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
-					  getMainModel()->getEndFrame()));
-}
-
-void
-MainWindow::selectToStart()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
-					  m_viewManager->getGlobalCentreFrame()));
-}
-
-void
-MainWindow::selectToEnd()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
-					  getMainModel()->getEndFrame()));
-}
-
-void
-MainWindow::selectVisible()
-{
-    Model *model = getMainModel();
-    if (!model) return;
-
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    size_t startFrame, endFrame;
-
-    if (currentPane->getStartFrame() < 0) startFrame = 0;
-    else startFrame = currentPane->getStartFrame();
-
-    if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
-    else endFrame = currentPane->getEndFrame();
-
-    m_viewManager->setSelection(Selection(startFrame, endFrame));
-}
-
-void
-MainWindow::clearSelection()
-{
-    m_viewManager->clearSelections();
-}
-
-void
-MainWindow::cut()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    clipboard.clear();
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-
-    CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
-
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        layer->copy(*i, clipboard);
-        layer->deleteSelection(*i);
-    }
-
-    CommandHistory::getInstance()->endCompoundOperation();
-}
-
-void
-MainWindow::copy()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    clipboard.clear();
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        layer->copy(*i, clipboard);
-    }
-}
-
-void
-MainWindow::paste()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    //!!! if we have no current layer, we should create one of the most
-    // appropriate type
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    Clipboard::PointList contents = clipboard.getPoints();
-/*
-    long minFrame = 0;
-    bool have = false;
-    for (int i = 0; i < contents.size(); ++i) {
-        if (!contents[i].haveFrame()) continue;
-        if (!have || contents[i].getFrame() < minFrame) {
-            minFrame = contents[i].getFrame();
-            have = true;
-        }
-    }
-
-    long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame;
-
-    layer->paste(clipboard, frameOffset);
-*/
-    layer->paste(clipboard, 0, true);
-}
-
-void
-MainWindow::deleteSelected()
-{
-    if (m_paneStack->getCurrentPane() &&
-	m_paneStack->getCurrentPane()->getSelectedLayer()) {
-        
-        Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
-
-        if (m_viewManager && 
-            (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
-
-            layer->deleteCurrentMeasureRect();
-
-        } else {
-
-            MultiSelection::SelectionList selections =
-                m_viewManager->getSelections();
-            
-            for (MultiSelection::SelectionList::iterator i = selections.begin();
-                 i != selections.end(); ++i) {
-                layer->deleteSelection(*i);
-            }
-	}
-    }
-}
-
-void
-MainWindow::insertInstant()
-{
-    int frame = m_viewManager->getPlaybackFrame();
-    insertInstantAt(frame);
-}
-
-void
-MainWindow::insertInstantsAtBoundaries()
-{
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        size_t start = i->getStartFrame();
-        size_t end = i->getEndFrame();
-        if (start != end) {
-            insertInstantAt(i->getStartFrame());
-            insertInstantAt(i->getEndFrame());
-        }
-    }
-}
-
-void
-MainWindow::insertInstantAt(size_t frame)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) {
-        return;
-    }
-
-    Layer *layer = dynamic_cast<TimeInstantLayer *>
-        (pane->getSelectedLayer());
-
-    if (!layer) {
-        for (int i = pane->getLayerCount(); i > 0; --i) {
-            layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
-            if (layer) break;
-        }
-
-        if (!layer) {
-            CommandHistory::getInstance()->startCompoundOperation
-                (tr("Add Point"), true);
-            layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
-            if (layer) {
-                m_document->addLayerToView(pane, layer);
-                m_paneStack->setCurrentLayer(pane, layer);
-            }
-            CommandHistory::getInstance()->endCompoundOperation();
-        }
-    }
-
-    if (layer) {
-    
-        Model *model = layer->getModel();
-        SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-            (model);
-
-        if (sodm) {
-            SparseOneDimensionalModel::Point point(frame, "");
-
-            SparseOneDimensionalModel::Point prevPoint(0);
-            bool havePrevPoint = false;
-
-            SparseOneDimensionalModel::EditCommand *command =
-                new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point"));
-
-            if (m_labeller->actingOnPrevPoint()) {
-
-                SparseOneDimensionalModel::PointList prevPoints =
-                    sodm->getPreviousPoints(frame);
-
-                if (!prevPoints.empty()) {
-                    prevPoint = *prevPoints.begin();
-                    havePrevPoint = true;
-                }
-            }
-
-            if (m_labeller) {
-
-                m_labeller->setSampleRate(sodm->getSampleRate());
-
-                if (havePrevPoint) {
-                    command->deletePoint(prevPoint);
-                }
-
-                m_labeller->label<SparseOneDimensionalModel::Point>
-                    (point, havePrevPoint ? &prevPoint : 0);
-
-                if (havePrevPoint) {
-                    command->addPoint(prevPoint);
-                }
-            }
-            
-            command->addPoint(point);
-
-            command->setName(tr("Add Point at %1 s")
-                             .arg(RealTime::frame2RealTime
-                                  (frame,
-                                   sodm->getSampleRate())
-                                  .toText(false).c_str()));
-
-            command->finish();
-        }
-    }
-}
-
-void
-MainWindow::setInstantsNumbering()
-{
-    QAction *a = dynamic_cast<QAction *>(sender());
-    if (!a) return;
-
-    int type = m_numberingActions[a];
-    
-    if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
-
-    QSettings settings;
-    settings.beginGroup("MainWindow");
-    settings.setValue("labellertype", type);
-    settings.endGroup();
-}
-
-void
-MainWindow::setInstantsCounterCycle()
-{
-    QAction *a = dynamic_cast<QAction *>(sender());
-    if (!a) return;
-    
-    int cycle = a->text().toInt();
-    if (cycle == 0) return;
-
-    if (m_labeller) m_labeller->setCounterCycleSize(cycle);
-    
-    QSettings settings;
-    settings.beginGroup("MainWindow");
-    settings.setValue("labellercycle", cycle);
-    settings.endGroup();
-}
-
-void
-MainWindow::resetInstantsCounters()
-{
-    LabelCounterInputDialog dialog(m_labeller, this);
-    dialog.exec();
-}
-
-void
-MainWindow::renumberInstants()
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) return;
-
-    Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
-    if (!layer) return;
-
-    MultiSelection ms(m_viewManager->getSelection());
-    
-    Model *model = layer->getModel();
-    SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-        (model);
-    if (!sodm) return;
-
-    if (!m_labeller) return;
-
-    Labeller labeller(*m_labeller);
-    labeller.setSampleRate(sodm->getSampleRate());
-
-    // This uses a command
-
-    labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
-}
-
-void
 MainWindow::importAudio()
 {
     QString path = getOpenFileName(FileFinder::AudioFile);
@@ -2857,498 +2221,6 @@
     delete image;
 }
 
-MainWindow::FileOpenStatus
-MainWindow::open(QString fileOrUrl, AudioFileOpenMode mode)
-{
-    return open(FileSource(fileOrUrl), mode);
-}
-
-MainWindow::FileOpenStatus
-MainWindow::open(FileSource source, AudioFileOpenMode mode)
-{
-    FileOpenStatus status;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    bool canImportLayer = (getMainModel() != 0 &&
-                           m_paneStack != 0 &&
-                           m_paneStack->getCurrentPane() != 0);
-
-    if ((status = openAudio(source, mode)) != FileOpenFailed) {
-        return status;
-    } else if ((status = openSession(source)) != FileOpenFailed) {
-	return status;
-    } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
-        return status;
-    } else if (!canImportLayer) {
-        return FileOpenWrongMode;
-    } else if ((status = openImage(source)) != FileOpenFailed) {
-        return status;
-    } else if ((status = openLayer(source)) != FileOpenFailed) {
-        return status;
-    } else {
-	return FileOpenFailed;
-    }
-}
-
-MainWindow::FileOpenStatus
-MainWindow::openAudio(FileSource source, AudioFileOpenMode mode)
-{
-    std::cerr << "MainWindow::openAudio(" << source.getLocation().toStdString() << ")" << std::endl;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    m_openingAudioFile = true;
-
-    size_t rate = 0;
-
-    if (Preferences::getInstance()->getResampleOnLoad()) {
-        rate = m_playSource->getSourceSampleRate();
-    }
-
-    WaveFileModel *newModel = new WaveFileModel(source, rate);
-
-    if (!newModel->isOK()) {
-	delete newModel;
-        m_openingAudioFile = false;
-	return FileOpenFailed;
-    }
-
-    std::cerr << "mode = " << mode << std::endl;
-
-    if (mode == AskUser) {
-        if (getMainModel()) {
-
-            static bool prevSetAsMain = true;
-            bool setAsMain = true;
-            
-            QStringList items;
-            items << tr("Replace the existing main waveform")
-                  << tr("Load this file into a new waveform pane");
-
-            bool ok = false;
-            QString item = ListInputDialog::getItem
-                (this, tr("Select target for import"),
-                 tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"),
-                 items, prevSetAsMain ? 0 : 1, &ok);
-            
-            if (!ok || item.isEmpty()) {
-                delete newModel;
-                m_openingAudioFile = false;
-                return FileOpenCancelled;
-            }
-            
-            setAsMain = (item == items[0]);
-            prevSetAsMain = setAsMain;
-
-            if (setAsMain) mode = ReplaceMainModel;
-            else mode = CreateAdditionalModel;
-
-        } else {
-            mode = ReplaceMainModel;
-        }
-    }
-
-    if (mode == ReplaceCurrentPane) {
-
-        Pane *pane = m_paneStack->getCurrentPane();
-        if (pane) {
-            if (getMainModel()) {
-                View::ModelSet models(pane->getModels());
-                if (models.find(getMainModel()) != models.end()) {
-                    mode = ReplaceMainModel;
-                }
-            } else {
-                mode = ReplaceMainModel;
-            }
-        } else {
-            mode = CreateAdditionalModel;
-        }
-    }
-
-    if (mode == CreateAdditionalModel && !getMainModel()) {
-        mode = ReplaceMainModel;
-    }
-
-    if (mode == ReplaceMainModel) {
-
-        Model *prevMain = getMainModel();
-        if (prevMain) {
-            m_playSource->removeModel(prevMain);
-            PlayParameterRepository::getInstance()->removeModel(prevMain);
-        }
-        PlayParameterRepository::getInstance()->addModel(newModel);
-
-	m_document->setMainModel(newModel);
-	setupMenus();
-
-	if (m_sessionFile == "") {
-	    setWindowTitle(tr("Sonic Visualiser: %1")
-                           .arg(source.getLocation()));
-	    CommandHistory::getInstance()->clear();
-	    CommandHistory::getInstance()->documentSaved();
-	    m_documentModified = false;
-	} else {
-	    setWindowTitle(tr("Sonic Visualiser: %1 [%2]")
-			   .arg(QFileInfo(m_sessionFile).fileName())
-			   .arg(source.getLocation()));
-	    if (m_documentModified) {
-		m_documentModified = false;
-		documentModified(); // so as to restore "(modified)" window title
-	    }
-	}
-
-        if (!source.isRemote()) m_audioFile = source.getLocalFilename();
-
-    } else if (mode == CreateAdditionalModel) {
-
-	CommandHistory::getInstance()->startCompoundOperation
-	    (tr("Import \"%1\"").arg(source.getLocation()), true);
-
-	m_document->addImportedModel(newModel);
-
-	AddPaneCommand *command = new AddPaneCommand(this);
-	CommandHistory::getInstance()->addCommand(command);
-
-	Pane *pane = command->getPane();
-
-	if (!m_timeRulerLayer) {
-	    m_timeRulerLayer = m_document->createMainModelLayer
-		(LayerFactory::TimeRuler);
-	}
-
-	m_document->addLayerToView(pane, m_timeRulerLayer);
-
-	Layer *newLayer = m_document->createImportedLayer(newModel);
-
-	if (newLayer) {
-	    m_document->addLayerToView(pane, newLayer);
-	}
-	
-	CommandHistory::getInstance()->endCompoundOperation();
-
-    } else if (mode == ReplaceCurrentPane) {
-
-        // We know there is a current pane, otherwise we would have
-        // reset the mode to CreateAdditionalModel above; and we know
-        // the current pane does not contain the main model, otherwise
-        // we would have reset it to ReplaceMainModel.  But we don't
-        // know whether the pane contains a waveform model at all.
-        
-        Pane *pane = m_paneStack->getCurrentPane();
-        Layer *replace = 0;
-
-        for (int i = 0; i < pane->getLayerCount(); ++i) {
-            Layer *layer = pane->getLayer(i);
-            if (dynamic_cast<WaveformLayer *>(layer)) {
-                replace = layer;
-                break;
-            }
-        }
-
-	CommandHistory::getInstance()->startCompoundOperation
-	    (tr("Import \"%1\"").arg(source.getLocation()), true);
-
-	m_document->addImportedModel(newModel);
-
-        if (replace) {
-            m_document->removeLayerFromView(pane, replace);
-        }
-
-	Layer *newLayer = m_document->createImportedLayer(newModel);
-
-	if (newLayer) {
-	    m_document->addLayerToView(pane, newLayer);
-	}
-	
-	CommandHistory::getInstance()->endCompoundOperation();
-    }
-
-    updateMenuStates();
-    m_recentFiles.addFile(source.getLocation());
-    if (!source.isRemote()) {
-        // for file dialog
-        registerLastOpenedFilePath(FileFinder::AudioFile,
-                                   source.getLocalFilename());
-    }
-    m_openingAudioFile = false;
-
-    currentPaneChanged(m_paneStack->getCurrentPane());
-
-    return FileOpenSucceeded;
-}
-
-MainWindow::FileOpenStatus
-MainWindow::openPlaylist(FileSource source, AudioFileOpenMode mode)
-{
-    std::set<QString> extensions;
-    PlaylistFileReader::getSupportedExtensions(extensions);
-    QString extension = source.getExtension();
-    if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    PlaylistFileReader reader(source.getLocalFilename());
-    if (!reader.isOK()) return FileOpenFailed;
-
-    PlaylistFileReader::Playlist playlist = reader.load();
-
-    bool someSuccess = false;
-
-    for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
-         i != playlist.end(); ++i) {
-
-        FileOpenStatus status = openAudio(*i, mode);
-
-        if (status == FileOpenCancelled) {
-            return FileOpenCancelled;
-        }
-
-        if (status == FileOpenSucceeded) {
-            someSuccess = true;
-            mode = CreateAdditionalModel;
-        }
-    }
-
-    if (someSuccess) return FileOpenSucceeded;
-    else return FileOpenFailed;
-}
-
-MainWindow::FileOpenStatus
-MainWindow::openLayer(FileSource source)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    
-    if (!pane) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindow::openLayer: no current pane" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!getMainModel()) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindow::openLayer: No main model -- hence no default sample rate available" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    QString path = source.getLocalFilename();
-
-    if (source.getExtension() == "svl" || source.getExtension() == "xml") {
-
-        PaneCallback callback(this);
-        QFile file(path);
-        
-        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            std::cerr << "ERROR: MainWindow::openLayer("
-                      << source.getLocation().toStdString()
-                      << "): Failed to open file for reading" << std::endl;
-            return FileOpenFailed;
-        }
-        
-        SVFileReader reader(m_document, callback, source.getLocation());
-        reader.setCurrentPane(pane);
-        
-        QXmlInputSource inputSource(&file);
-        reader.parse(inputSource);
-        
-        if (!reader.isOK()) {
-            std::cerr << "ERROR: MainWindow::openLayer("
-                      << source.getLocation().toStdString()
-                      << "): Failed to read XML file: "
-                      << reader.getErrorString().toStdString() << std::endl;
-            return FileOpenFailed;
-        }
-
-        m_recentFiles.addFile(source.getLocation());
-
-        if (!source.isRemote()) {
-            registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
-        }
-
-    } else {
-        
-        try {
-
-            Model *model = DataFileReaderFactory::load
-                (path, getMainModel()->getSampleRate());
-        
-            if (model) {
-
-                std::cerr << "MainWindow::openLayer: Have model" << std::endl;
-
-                Layer *newLayer = m_document->createImportedLayer(model);
-
-                if (newLayer) {
-
-                    m_document->addLayerToView(pane, newLayer);
-                    m_recentFiles.addFile(source.getLocation());
-                    
-                    if (!source.isRemote()) {
-                        registerLastOpenedFilePath
-                            (FileFinder::LayerFile,
-                             path); // for file dialog
-                    }
-                    
-                    return FileOpenSucceeded;
-                }
-            }
-        } catch (DataFileReaderFactory::Exception e) {
-            if (e == DataFileReaderFactory::ImportCancelled) {
-                return FileOpenCancelled;
-            }
-        }
-    }
-    
-    source.setLeaveLocalFile(true);
-    return FileOpenFailed;
-}
-
-MainWindow::FileOpenStatus
-MainWindow::openImage(FileSource source)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    
-    if (!pane) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindow::openImage: no current pane" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!m_document->getMainModel()) {
-        return FileOpenWrongMode;
-    }
-
-    bool newLayer = false;
-
-    ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
-    if (!il) {
-        for (int i = pane->getLayerCount()-1; i >= 0; --i) {
-            il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
-            if (il) break;
-        }
-    }
-    if (!il) {
-        il = dynamic_cast<ImageLayer *>
-            (m_document->createEmptyLayer(LayerFactory::Image));
-        if (!il) return FileOpenFailed;
-        newLayer = true;
-    }
-
-    // We don't put the image file in Recent Files
-
-    std::cerr << "openImage: trying location \"" << source.getLocation().toStdString() << "\" in image layer" << std::endl;
-
-    if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
-        if (newLayer) {
-            m_document->setModel(il, 0); // releasing its model
-            delete il;
-        }
-        return FileOpenFailed;
-    } else {
-        if (newLayer) {
-            m_document->addLayerToView(pane, il);
-        }
-        m_paneStack->setCurrentLayer(pane, il);
-    }
-
-    return FileOpenSucceeded;
-}
-
-MainWindow::FileOpenStatus
-MainWindow::openSession(FileSource source)
-{
-    if (!source.isAvailable()) return FileOpenFailed;
-    if (source.getExtension() != "sv") return FileOpenFailed;
-    source.waitForData();
-
-    BZipFileDevice bzFile(source.getLocalFilename());
-    if (!bzFile.open(QIODevice::ReadOnly)) return FileOpenFailed;
-
-    if (!checkSaveModified()) return FileOpenCancelled;
-
-    QString error;
-    closeSession();
-    createDocument();
-
-    PaneCallback callback(this);
-    m_viewManager->clearSelections();
-
-    SVFileReader reader(m_document, callback, source.getLocation());
-    QXmlInputSource inputSource(&bzFile);
-    reader.parse(inputSource);
-    
-    if (!reader.isOK()) {
-        error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
-    }
-    
-    bzFile.close();
-
-    bool ok = (error == "");
-
-    if (ok) {
-
-	setWindowTitle(tr("Sonic Visualiser: %1")
-		       .arg(source.getLocation()));
-
-	if (!source.isRemote()) m_sessionFile = source.getLocalFilename();
-
-	setupMenus();
-	CommandHistory::getInstance()->clear();
-	CommandHistory::getInstance()->documentSaved();
-	m_documentModified = false;
-	updateMenuStates();
-
-        m_recentFiles.addFile(source.getLocation());
-
-        if (!source.isRemote()) {
-            // for file dialog
-            registerLastOpenedFilePath(FileFinder::SessionFile,
-                                        source.getLocalFilename());
-        }
-
-    } else {
-	setWindowTitle(tr("Sonic Visualiser"));
-    }
-
-    return ok ? FileOpenSucceeded : FileOpenFailed;
-}
-
-void
-MainWindow::createPlayTarget()
-{
-    if (m_playTarget) return;
-
-    m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource);
-    if (!m_playTarget) {
-	QMessageBox::warning
-	    (this, tr("Couldn't open audio device"),
-	     tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Audio playback will not be available during this session."),
-	     QMessageBox::Ok);
-    }
-    connect(m_fader, SIGNAL(valueChanged(float)),
-	    m_playTarget, SLOT(setOutputGain(float)));
-}
-
-WaveFileModel *
-MainWindow::getMainModel()
-{
-    if (!m_document) return 0;
-    return m_document->getMainModel();
-}
-
-const WaveFileModel *
-MainWindow::getMainModel() const
-{
-    if (!m_document) return 0;
-    return m_document->getMainModel();
-}
-
 void
 MainWindow::newSession()
 {
@@ -3381,33 +2253,6 @@
 }
 
 void
-MainWindow::createDocument()
-{
-    m_document = new Document;
-
-    connect(m_document, SIGNAL(layerAdded(Layer *)),
-	    this, SLOT(layerAdded(Layer *)));
-    connect(m_document, SIGNAL(layerRemoved(Layer *)),
-	    this, SLOT(layerRemoved(Layer *)));
-    connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
-	    this, SLOT(layerAboutToBeDeleted(Layer *)));
-    connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
-	    this, SLOT(layerInAView(Layer *, bool)));
-
-    connect(m_document, SIGNAL(modelAdded(Model *)),
-	    this, SLOT(modelAdded(Model *)));
-    connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
-	    this, SLOT(mainModelChanged(WaveFileModel *)));
-    connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
-	    this, SLOT(modelAboutToBeDeleted(Model *)));
-
-    connect(m_document, SIGNAL(modelGenerationFailed(QString)),
-            this, SLOT(modelGenerationFailed(QString)));
-    connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)),
-            this, SLOT(modelRegenerationFailed(QString, QString)));
-}
-
-void
 MainWindow::closeSession()
 {
     if (!checkSaveModified()) return;
@@ -3465,7 +2310,7 @@
 
     if (path.isEmpty()) return;
 
-    if (openSession(path) == FileOpenFailed) {
+    if (openSessionFile(path) == FileOpenFailed) {
 	QMessageBox::critical(this, tr("Failed to open file"),
 			      tr("<b>File open failed</b><p>Session file \"%1\" could not be opened").arg(path));
     }
@@ -3550,6 +2395,24 @@
 }
 
 void
+MainWindow::paneAdded(Pane *pane)
+{
+    if (m_overview) m_overview->registerView(pane);
+}    
+
+void
+MainWindow::paneHidden(Pane *pane)
+{
+    if (m_overview) m_overview->unregisterView(pane); 
+}    
+
+void
+MainWindow::paneAboutToBeDeleted(Pane *pane)
+{
+    if (m_overview) m_overview->unregisterView(pane); 
+}    
+
+void
 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
 {
     if (pane) m_paneStack->setCurrentPane(pane);
@@ -3624,6 +2487,8 @@
         delete m_layerTreeView;
     }
 
+    closeSession();
+
     e->accept();
     return;
 }
@@ -3751,239 +2616,12 @@
     }
 }
 
-bool
-MainWindow::saveSessionFile(QString path)
-{
-    BZipFileDevice bzFile(path);
-    if (!bzFile.open(QIODevice::WriteOnly)) {
-        std::cerr << "Failed to open session file \"" << path.toStdString()
-                  << "\" for writing: "
-                  << bzFile.errorString().toStdString() << std::endl;
-        return false;
-    }
-
-    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-
-    QTextStream out(&bzFile);
-    toXml(out);
-    out.flush();
-
-    QApplication::restoreOverrideCursor();
-
-    if (!bzFile.isOK()) {
-	QMessageBox::critical(this, tr("Failed to write file"),
-			      tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
-			      .arg(path).arg(bzFile.errorString()));
-        bzFile.close();
-	return false;
-    }
-
-    bzFile.close();
-    return true;
-}
-
-void
-MainWindow::toXml(QTextStream &out)
-{
-    QString indent("  ");
-
-    out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-    out << "<!DOCTYPE sonic-visualiser>\n";
-    out << "<sv>\n";
-
-    m_document->toXml(out, "", "");
-
-    out << "<display>\n";
-
-    out << QString("  <window width=\"%1\" height=\"%2\"/>\n")
-	.arg(width()).arg(height());
-
-    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
-
-	Pane *pane = m_paneStack->getPane(i);
-
-	if (pane) {
-            pane->toXml(out, indent);
-	}
-    }
-
-    out << "</display>\n";
-
-    m_viewManager->getSelection().toXml(out);
-
-    out << "</sv>\n";
-}
-
-Pane *
-MainWindow::addPaneToStack()
-{
-    AddPaneCommand *command = new AddPaneCommand(this);
-    CommandHistory::getInstance()->addCommand(command);
-    return command->getPane();
-}
-
-void
-MainWindow::zoomIn()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->zoom(true);
-}
-
-void
-MainWindow::zoomOut()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->zoom(false);
-}
-
-void
-MainWindow::zoomToFit()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Model *model = getMainModel();
-    if (!model) return;
-    
-    size_t start = model->getStartFrame();
-    size_t end = model->getEndFrame();
-    size_t pixels = currentPane->width();
-
-    size_t sw = currentPane->getVerticalScaleWidth();
-    if (pixels > sw * 2) pixels -= sw * 2;
-    else pixels = 1;
-    if (pixels > 4) pixels -= 4;
-
-    size_t zoomLevel = (end - start) / pixels;
-
-    currentPane->setZoomLevel(zoomLevel);
-    currentPane->setCentreFrame((start + end) / 2);
-}
-
-void
-MainWindow::zoomDefault()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->setZoomLevel(1024);
-}
-
-void
-MainWindow::scrollLeft()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(false, false);
-}
-
-void
-MainWindow::jumpLeft()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(false, true);
-}
-
-void
-MainWindow::scrollRight()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(true, false);
-}
-
-void
-MainWindow::jumpRight()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(true, true);
-}
-
-void
-MainWindow::showNoOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::NoOverlays);
-}
-
-void
-MainWindow::showMinimalOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::MinimalOverlays);
-}
-
-void
-MainWindow::showStandardOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
-}
-
-void
-MainWindow::showAllOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::AllOverlays);
-}
-
-void
-MainWindow::toggleZoomWheels()
-{
-    if (m_viewManager->getZoomWheelsEnabled()) {
-        m_viewManager->setZoomWheelsEnabled(false);
-    } else {
-        m_viewManager->setZoomWheelsEnabled(true);
-    }
-}
-
-void
-MainWindow::togglePropertyBoxes()
-{
-    if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
-        if (Preferences::getInstance()->getPropertyBoxLayout() ==
-            Preferences::VerticallyStacked) {
-            m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
-        } else {
-            m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
-        }
-    } else {
-        m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
-    }
-}
-
-void
-MainWindow::toggleStatusBar()
-{
-    QSettings settings;
-    settings.beginGroup("MainWindow");
-    bool sb = settings.value("showstatusbar", true).toBool();
-
-    if (sb) {
-        statusBar()->hide();
-    } else {
-        statusBar()->show();
-    }
-
-    settings.setValue("showstatusbar", !sb);
-
-    settings.endGroup();
-}
-
 void
 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
 {
-    if (name == "Property Box Layout") {
-        if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
-            if (Preferences::getInstance()->getPropertyBoxLayout() ==
-                Preferences::VerticallyStacked) {
-                m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
-            } else {
-                m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
-            }
-        }
-    } else if (name == "Background Mode" && m_viewManager) {
-        Preferences::BackgroundMode mode =
-            Preferences::getInstance()->getBackgroundMode();
-        if (mode == Preferences::BackgroundFromTheme) {
-            m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
-        } else if (mode == Preferences::DarkBackground) {
-            m_viewManager->setGlobalDarkBackground(true);
-        } else {
-            m_viewManager->setGlobalDarkBackground(false);
-        }
+    MainWindowBase::preferenceChanged(name);
+
+    if (name == "Background Mode" && m_viewManager) {
         if (m_viewManager->getGlobalDarkBackground()) {
             m_panLayer->setBaseColour
                 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
@@ -3991,171 +2629,7 @@
             m_panLayer->setBaseColour
                 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
         }      
-    }            
-}
-
-void
-MainWindow::play()
-{
-    if (m_playSource->isPlaying()) {
-        stop();
-    } else {
-        playbackFrameChanged(m_viewManager->getPlaybackFrame());
-	m_playSource->play(m_viewManager->getPlaybackFrame());
-    }
-}
-
-void
-MainWindow::ffwd()
-{
-    if (!getMainModel()) return;
-
-    int frame = m_viewManager->getPlaybackFrame();
-    ++frame;
-
-    Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
-
-    if (!layer) {
-
-        frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr);
-        if (frame > int(getMainModel()->getEndFrame())) {
-            frame = getMainModel()->getEndFrame();
-        }
-
-    } else {
-
-        size_t resolution = 0;
-        if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
-                                       frame, resolution, Layer::SnapRight)) {
-            frame = getMainModel()->getEndFrame();
-        }
-    }
-        
-    if (frame < 0) frame = 0;
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
-    }
-    
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindow::ffwdEnd()
-{
-    if (!getMainModel()) return;
-
-    size_t frame = getMainModel()->getEndFrame();
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(frame);
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindow::rewind()
-{
-    if (!getMainModel()) return;
-
-    int frame = m_viewManager->getPlaybackFrame();
-    if (frame > 0) --frame;
-
-    Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
-    
-    // when rewinding during playback, we want to allow a period
-    // following a rewind target point at which the rewind will go to
-    // the prior point instead of the immediately neighbouring one
-    if (m_playSource && m_playSource->isPlaying()) {
-        RealTime ct = RealTime::frame2RealTime(frame, sr);
-        ct = ct - RealTime::fromSeconds(0.25);
-        if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
-//        std::cerr << "rewind: frame " << frame << " -> ";
-        frame = RealTime::realTime2Frame(ct, sr);
-//        std::cerr << frame << std::endl;
-    }
-
-    if (!layer) {
-        
-        frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr);
-        if (frame < int(getMainModel()->getStartFrame())) {
-            frame = getMainModel()->getStartFrame();
-        }
-
-    } else {
-
-        size_t resolution = 0;
-        if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
-                                       frame, resolution, Layer::SnapLeft)) {
-            frame = getMainModel()->getStartFrame();
-        }
-    }
-
-    if (frame < 0) frame = 0;
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindow::rewindStart()
-{
-    if (!getMainModel()) return;
-
-    size_t frame = getMainModel()->getStartFrame();
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(frame);
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-Layer *
-MainWindow::getSnapLayer() const
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) return 0;
-
-    Layer *layer = pane->getSelectedLayer();
-
-    if (!dynamic_cast<TimeInstantLayer *>(layer) &&
-        !dynamic_cast<TimeValueLayer *>(layer) &&
-        !dynamic_cast<TimeRulerLayer *>(layer)) {
-
-        layer = 0;
-
-        for (int i = pane->getLayerCount(); i > 0; --i) {
-            Layer *l = pane->getLayer(i-1);
-            if (dynamic_cast<TimeRulerLayer *>(l)) {
-                layer = l;
-                break;
-            }
-        }
-    }
-
-    return layer;
-}
-
-void
-MainWindow::stop()
-{
-    m_playSource->stop();
-
-    if (m_paneStack && m_paneStack->getCurrentPane()) {
-        updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
-    } else {
-        m_myStatusMessage = "";
-        statusBar()->showMessage("");
-    }
+    }     
 }
 
 void
@@ -4253,92 +2727,6 @@
     updateMenuStates();
 }
 
-MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) :
-    m_mw(mw),
-    m_pane(0),
-    m_prevCurrentPane(0),
-    m_added(false)
-{
-}
-
-MainWindow::AddPaneCommand::~AddPaneCommand()
-{
-    if (m_pane && !m_added) {
-	m_mw->m_paneStack->deletePane(m_pane);
-    }
-}
-
-QString
-MainWindow::AddPaneCommand::getName() const
-{
-    return tr("Add Pane");
-}
-
-void
-MainWindow::AddPaneCommand::execute()
-{
-    if (!m_pane) {
-	m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
-	m_pane = m_mw->m_paneStack->addPane();
-
-        connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
-                m_mw, SLOT(contextHelpChanged(const QString &)));
-    } else {
-	m_mw->m_paneStack->showPane(m_pane);
-    }
-
-    m_mw->m_paneStack->setCurrentPane(m_pane);
-    m_mw->m_overview->registerView(m_pane);
-    m_added = true;
-}
-
-void
-MainWindow::AddPaneCommand::unexecute()
-{
-    m_mw->m_paneStack->hidePane(m_pane);
-    m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
-    m_mw->m_overview->unregisterView(m_pane); 
-    m_added = false;
-}
-
-MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) :
-    m_mw(mw),
-    m_pane(pane),
-    m_added(true)
-{
-}
-
-MainWindow::RemovePaneCommand::~RemovePaneCommand()
-{
-    if (m_pane && !m_added) {
-	m_mw->m_paneStack->deletePane(m_pane);
-    }
-}
-
-QString
-MainWindow::RemovePaneCommand::getName() const
-{
-    return tr("Remove Pane");
-}
-
-void
-MainWindow::RemovePaneCommand::execute()
-{
-    m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
-    m_mw->m_paneStack->hidePane(m_pane);
-    m_mw->m_overview->unregisterView(m_pane);
-    m_added = false;
-}
-
-void
-MainWindow::RemovePaneCommand::unexecute()
-{
-    m_mw->m_paneStack->showPane(m_pane);
-    m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
-    m_mw->m_overview->registerView(m_pane);
-    m_added = true;
-}
-
 void
 MainWindow::addLayer()
 {
@@ -4478,45 +2866,6 @@
 }
 
 void
-MainWindow::deleteCurrentPane()
-{
-    CommandHistory::getInstance()->startCompoundOperation
-	(tr("Delete Pane"), true);
-
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (pane) {
-	while (pane->getLayerCount() > 0) {
-	    Layer *layer = pane->getLayer(0);
-	    if (layer) {
-		m_document->removeLayerFromView(pane, layer);
-	    } else {
-		break;
-	    }
-	}
-
-	RemovePaneCommand *command = new RemovePaneCommand(this, pane);
-	CommandHistory::getInstance()->addCommand(command);
-    }
-
-    CommandHistory::getInstance()->endCompoundOperation();
-
-    updateMenuStates();
-}
-
-void
-MainWindow::deleteCurrentLayer()
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (pane) {
-	Layer *layer = pane->getSelectedLayer();
-	if (layer) {
-	    m_document->removeLayerFromView(pane, layer);
-	}
-    }
-    updateMenuStates();
-}
-
-void
 MainWindow::renameCurrentLayer()
 {
     Pane *pane = m_paneStack->getCurrentPane();
@@ -4614,68 +2963,6 @@
 }
 
 void
-MainWindow::playbackFrameChanged(unsigned long frame)
-{
-    if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-
-    RealTime now = RealTime::frame2RealTime
-        (frame, getMainModel()->getSampleRate());
-
-    if (now.sec == m_lastPlayStatusSec) return;
-
-    RealTime then = RealTime::frame2RealTime
-        (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
-
-    QString nowStr;
-    QString thenStr;
-    QString remainingStr;
-
-    if (then.sec > 10) {
-        nowStr = now.toSecText().c_str();
-        thenStr = then.toSecText().c_str();
-        remainingStr = (then - now).toSecText().c_str();
-        m_lastPlayStatusSec = now.sec;
-    } else {
-        nowStr = now.toText(true).c_str();
-        thenStr = then.toText(true).c_str();
-        remainingStr = (then - now).toText(true).c_str();
-    }        
-
-    m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
-        .arg(nowStr).arg(thenStr).arg(remainingStr);
-
-    statusBar()->showMessage(m_myStatusMessage);
-}
-
-void
-MainWindow::globalCentreFrameChanged(unsigned long )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (!p->getFollowGlobalPan()) return;
-    updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindow::viewCentreFrameChanged(View *v, unsigned long )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (v == p) updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindow::viewZoomLevelChanged(View *v, unsigned long , bool )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (v == p) updateVisibleRangeDisplay(p);
-}
-
-void
 MainWindow::updateVisibleRangeDisplay(Pane *p) const
 {
     if (!getMainModel() || !p) {
@@ -4756,68 +3043,23 @@
 }
 
 void
-MainWindow::layerAdded(Layer *)
+MainWindow::layerRemoved(Layer *layer)
 {
-//    std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl;
-//    setupExistingLayersMenu();
-    updateMenuStates();
-}
-
-void
-MainWindow::layerRemoved(Layer *)
-{
-//    std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl;
     setupExistingLayersMenus();
-    updateMenuStates();
-}
-
-void
-MainWindow::layerAboutToBeDeleted(Layer *layer)
-{
-//    std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl;
-    if (layer == m_timeRulerLayer) {
-//	std::cerr << "(this is the time ruler layer)" << std::endl;
-	m_timeRulerLayer = 0;
-    }
+    MainWindowBase::layerRemoved(layer);
 }
 
 void
 MainWindow::layerInAView(Layer *layer, bool inAView)
 {
-//    std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl;
-
-    // Check whether we need to add or remove model from play source
-    Model *model = layer->getModel();
-    if (model) {
-        if (inAView) {
-            m_playSource->addModel(model);
-        } else {
-            bool found = false;
-            for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
-                Pane *pane = m_paneStack->getPane(i);
-                if (!pane) continue;
-                for (int j = 0; j < pane->getLayerCount(); ++j) {
-                    Layer *pl = pane->getLayer(j);
-                    if (pl && pl->getModel() == model) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (found) break;
-            }
-            if (!found) m_playSource->removeModel(model);
-        }
-    }
-
     setupExistingLayersMenus();
-    updateMenuStates();
+    MainWindowBase::layerInAView(layer, inAView);
 }
 
 void
 MainWindow::modelAdded(Model *model)
 {
-//    std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl;
-    m_playSource->addModel(model);
+    MainWindowBase::modelAdded(model);
     if (dynamic_cast<DenseTimeValueModel *>(model)) {
         setupPaneAndLayerMenus();
     }
@@ -4826,22 +3068,54 @@
 void
 MainWindow::mainModelChanged(WaveFileModel *model)
 {
-//    std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl;
-    updateDescriptionLabel();
     m_panLayer->setModel(model);
-    if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
-    if (model && !m_playTarget && m_audioOutput) createPlayTarget();
+
+    MainWindowBase::mainModelChanged(model);
+
+    if (m_playTarget) {
+        connect(m_fader, SIGNAL(valueChanged(float)),
+                m_playTarget, SLOT(setOutputGain(float)));
+    }
 }
 
 void
-MainWindow::modelAboutToBeDeleted(Model *model)
+MainWindow::setInstantsNumbering()
 {
-//    std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl;
-    if (model == m_viewManager->getPlaybackModel()) {
-        m_viewManager->setPlaybackModel(0);
-    }
-    m_playSource->removeModel(model);
-    FFTDataServer::modelAboutToBeDeleted(model);
+    QAction *a = dynamic_cast<QAction *>(sender());
+    if (!a) return;
+
+    int type = m_numberingActions[a];
+    
+    if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
+
+    QSettings settings;
+    settings.beginGroup("MainWindow");
+    settings.setValue("labellertype", type);
+    settings.endGroup();
+}
+
+void
+MainWindow::setInstantsCounterCycle()
+{
+    QAction *a = dynamic_cast<QAction *>(sender());
+    if (!a) return;
+    
+    int cycle = a->text().toInt();
+    if (cycle == 0) return;
+
+    if (m_labeller) m_labeller->setCounterCycleSize(cycle);
+    
+    QSettings settings;
+    settings.beginGroup("MainWindow");
+    settings.setValue("labellercycle", cycle);
+    settings.endGroup();
+}
+
+void
+MainWindow::resetInstantsCounters()
+{
+    LabelCounterInputDialog dialog(m_labeller, this);
+    dialog.exec();
 }
 
 void
@@ -4875,19 +3149,6 @@
 }
 
 void
-MainWindow::propertyStacksResized()
-{
-/*
-    std::cerr << "MainWindow::propertyStacksResized" << std::endl;
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (pane && m_overview) {
-        std::cerr << "Fixed width: "  << pane->width() << std::endl;
-        m_overview->setFixedWidth(pane->width());
-    }
-*/
-}
-
-void
 MainWindow::showLayerTree()
 {
     if (!m_layerTreeView.isNull()) {
@@ -4907,23 +3168,6 @@
 }
 
 void
-MainWindow::pollOSC()
-{
-    if (!m_oscQueue || m_oscQueue->isEmpty()) return;
-    std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl;
-
-    if (m_openingAudioFile) return;
-
-    OSCMessage message = m_oscQueue->readMessage();
-
-    if (message.getTarget() != 0) {
-        return; //!!! for now -- this class is target 0, others not handled yet
-    }
-
-    handleOSCMessage(message);
-}
-
-void
 MainWindow::handleOSCMessage(const OSCMessage &message)
 {
     std::cerr << "MainWindow::handleOSCMessage: thread id = " 
@@ -5457,24 +3701,6 @@
 }
 
 void
-MainWindow::inProgressSelectionChanged()
-{
-    Pane *currentPane = 0;
-    if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) updateVisibleRangeDisplay(currentPane);
-}
-
-void
-MainWindow::contextHelpChanged(const QString &s)
-{
-    if (s == "" && m_myStatusMessage != "") {
-        statusBar()->showMessage(m_myStatusMessage);
-        return;
-    }
-    statusBar()->showMessage(s);
-}
-
-void
 MainWindow::website()
 {
     openHelpUrl(tr("http://www.sonicvisualiser.org/"));
@@ -5487,46 +3713,6 @@
 }
 
 void
-MainWindow::openHelpUrl(QString url)
-{
-    // This method mostly lifted from Qt Assistant source code
-
-    QProcess *process = new QProcess(this);
-    connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
-
-    QStringList args;
-
-#ifdef Q_OS_MAC
-    args.append(url);
-    process->start("open", args);
-#else
-#ifdef Q_OS_WIN32
-
-	QString pf(getenv("ProgramFiles"));
-	QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE");
-
-	args.append(url);
-	process->start(command, args);
-
-#else
-#ifdef Q_WS_X11
-    if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
-        args.append("exec");
-        args.append(url);
-        process->start("kfmclient", args);
-    } else if (!qgetenv("BROWSER").isEmpty()) {
-        args.append(url);
-        process->start(qgetenv("BROWSER"), args);
-    } else {
-        args.append(url);
-        process->start("firefox", args);
-    }
-#endif
-#endif
-#endif
-}
-
-void
 MainWindow::about()
 {
     bool debug = false;