svapp  1.9
MainWindowBase.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-2007 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "MainWindowBase.h"
00017 #include "Document.h"
00018 
00019 
00020 #include "view/Pane.h"
00021 #include "view/PaneStack.h"
00022 #include "data/model/WaveFileModel.h"
00023 #include "data/model/SparseOneDimensionalModel.h"
00024 #include "data/model/NoteModel.h"
00025 #include "data/model/FlexiNoteModel.h"
00026 #include "data/model/Labeller.h"
00027 #include "data/model/TabularModel.h"
00028 #include "view/ViewManager.h"
00029 
00030 #include "layer/WaveformLayer.h"
00031 #include "layer/TimeRulerLayer.h"
00032 #include "layer/TimeInstantLayer.h"
00033 #include "layer/TimeValueLayer.h"
00034 #include "layer/Colour3DPlotLayer.h"
00035 #include "layer/SliceLayer.h"
00036 #include "layer/SliceableLayer.h"
00037 #include "layer/ImageLayer.h"
00038 #include "layer/NoteLayer.h"
00039 #include "layer/FlexiNoteLayer.h"
00040 #include "layer/RegionLayer.h"
00041 
00042 #include "widgets/ListInputDialog.h"
00043 #include "widgets/CommandHistory.h"
00044 #include "widgets/ProgressDialog.h"
00045 #include "widgets/MIDIFileImportDialog.h"
00046 #include "widgets/CSVFormatDialog.h"
00047 #include "widgets/ModelDataTableDialog.h"
00048 #include "widgets/InteractiveFileFinder.h"
00049 
00050 #include "audioio/AudioCallbackPlaySource.h"
00051 #include "audioio/AudioCallbackPlayTarget.h"
00052 #include "audioio/AudioTargetFactory.h"
00053 #include "audioio/PlaySpeedRangeMapper.h"
00054 #include "data/fileio/DataFileReaderFactory.h"
00055 #include "data/fileio/PlaylistFileReader.h"
00056 #include "data/fileio/WavFileWriter.h"
00057 #include "data/fileio/CSVFileWriter.h"
00058 #include "data/fileio/MIDIFileWriter.h"
00059 #include "data/fileio/BZipFileDevice.h"
00060 #include "data/fileio/FileSource.h"
00061 #include "data/fileio/AudioFileReaderFactory.h"
00062 #include "rdf/RDFImporter.h"
00063 
00064 #include "data/fft/FFTDataServer.h"
00065 
00066 #include "base/RecentFiles.h"
00067 
00068 #include "base/PlayParameterRepository.h"
00069 #include "base/XmlExportable.h"
00070 #include "base/Profiler.h"
00071 #include "base/Preferences.h"
00072 #include "base/TempWriteFile.h"
00073 #include "base/Exceptions.h"
00074 #include "base/ResourceFinder.h"
00075 
00076 #include "data/osc/OSCQueue.h"
00077 #include "data/midi/MIDIInput.h"
00078 
00079 #include <QApplication>
00080 #include <QMessageBox>
00081 #include <QGridLayout>
00082 #include <QLabel>
00083 #include <QAction>
00084 #include <QMenuBar>
00085 #include <QToolBar>
00086 #include <QInputDialog>
00087 #include <QStatusBar>
00088 #include <QTreeView>
00089 #include <QFile>
00090 #include <QFileInfo>
00091 #include <QDir>
00092 #include <QTextStream>
00093 #include <QProcess>
00094 #include <QShortcut>
00095 #include <QSettings>
00096 #include <QDateTime>
00097 #include <QProcess>
00098 #include <QCheckBox>
00099 #include <QRegExp>
00100 #include <QScrollArea>
00101 #include <QDesktopWidget>
00102 #include <QSignalMapper>
00103 
00104 #include <iostream>
00105 #include <cstdio>
00106 #include <errno.h>
00107 
00108 
00109 
00110 
00111 using std::vector;
00112 using std::map;
00113 using std::set;
00114 
00115 #ifdef Q_WS_X11
00116 #define Window X11Window
00117 #include <X11/Xlib.h>
00118 #include <X11/Xutil.h>
00119 #include <X11/Xatom.h>
00120 #include <X11/SM/SMlib.h>
00121 
00122 static int handle_x11_error(Display *dpy, XErrorEvent *err)
00123 {
00124     char errstr[256];
00125     XGetErrorText(dpy, err->error_code, errstr, 256);
00126     if (err->error_code != BadWindow) {
00127         cerr << "Sonic Visualiser: X Error: "
00128                   << errstr << " " << int(err->error_code)
00129                   << "\nin major opcode:  "
00130                   << int(err->request_code) << endl;
00131     }
00132     return 0;
00133 }
00134 #undef Window
00135 #endif
00136 
00137 MainWindowBase::MainWindowBase(bool withAudioOutput,
00138                                bool withMIDIInput) :
00139     m_document(0),
00140     m_paneStack(0),
00141     m_viewManager(0),
00142     m_timeRulerLayer(0),
00143     m_audioOutput(withAudioOutput),
00144     m_playSource(0),
00145     m_playTarget(0),
00146     m_oscQueue(0),
00147     m_oscQueueStarter(0),
00148     m_midiInput(0),
00149     m_recentFiles("RecentFiles", 20),
00150     m_recentTransforms("RecentTransforms", 20),
00151     m_documentModified(false),
00152     m_openingAudioFile(false),
00153     m_abandoning(false),
00154     m_labeller(0),
00155     m_lastPlayStatusSec(0),
00156     m_initialDarkBackground(false),
00157     m_defaultFfwdRwdStep(2, 0),
00158     m_statusLabel(0),
00159     m_menuShortcutMapper(0)
00160 {
00161     Profiler profiler("MainWindowBase::MainWindowBase");
00162 
00163 #ifdef Q_WS_X11
00164     XSetErrorHandler(handle_x11_error);
00165 #endif
00166 
00167     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
00168             this, SLOT(documentModified()));
00169     connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
00170             this, SLOT(documentRestored()));
00171     
00172     m_viewManager = new ViewManager();
00173     connect(m_viewManager, SIGNAL(selectionChanged()),
00174             this, SLOT(updateMenuStates()));
00175     connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
00176             this, SLOT(inProgressSelectionChanged()));
00177 
00178     // set a sensible default font size for views -- cannot do this
00179     // in Preferences, which is in base and not supposed to use QtGui
00180     int viewFontSize = QApplication::font().pointSize() * 0.9;
00181     QSettings settings;
00182     settings.beginGroup("Preferences");
00183     viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
00184     settings.setValue("view-font-size", viewFontSize);
00185     settings.endGroup();
00186 
00187     Preferences::BackgroundMode mode =
00188         Preferences::getInstance()->getBackgroundMode();
00189     m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
00190     if (mode != Preferences::BackgroundFromTheme) {
00191         m_viewManager->setGlobalDarkBackground
00192             (mode == Preferences::DarkBackground);
00193     }
00194 
00195     m_paneStack = new PaneStack(0, m_viewManager);
00196     connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
00197             this, SLOT(currentPaneChanged(Pane *)));
00198     connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
00199             this, SLOT(currentLayerChanged(Pane *, Layer *)));
00200     connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
00201             this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
00202     connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
00203             this, SLOT(contextHelpChanged(const QString &)));
00204     connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
00205             this, SLOT(paneAdded(Pane *)));
00206     connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
00207             this, SLOT(paneHidden(Pane *)));
00208     connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
00209             this, SLOT(paneAboutToBeDeleted(Pane *)));
00210     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
00211             this, SLOT(paneDropAccepted(Pane *, QStringList)));
00212     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
00213             this, SLOT(paneDropAccepted(Pane *, QString)));
00214     connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
00215             this, SLOT(paneDeleteButtonClicked(Pane *)));
00216 
00217     m_playSource = new AudioCallbackPlaySource(m_viewManager,
00218                                                QApplication::applicationName());
00219 
00220     connect(m_playSource, SIGNAL(sampleRateMismatch(int, int, bool)),
00221             this,           SLOT(sampleRateMismatch(int, int, bool)));
00222     connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
00223             this,           SLOT(audioOverloadPluginDisabled()));
00224     connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()),
00225             this,           SLOT(audioTimeStretchMultiChannelDisabled()));
00226 
00227     connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)),
00228             this, SLOT(outputLevelsChanged(float, float)));
00229 
00230     connect(m_viewManager, SIGNAL(playbackFrameChanged(int)),
00231             this, SLOT(playbackFrameChanged(int)));
00232 
00233     connect(m_viewManager, SIGNAL(globalCentreFrameChanged(int)),
00234             this, SLOT(globalCentreFrameChanged(int)));
00235 
00236     connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, int)),
00237             this, SLOT(viewCentreFrameChanged(View *, int)));
00238 
00239     connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, int, bool)),
00240             this, SLOT(viewZoomLevelChanged(View *, int, bool)));
00241 
00242     connect(Preferences::getInstance(),
00243             SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
00244             this,
00245             SLOT(preferenceChanged(PropertyContainer::PropertyName)));
00246 
00247     Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
00248     settings.beginGroup("MainWindow");
00249 
00250     labellerType = (Labeller::ValueType)
00251         settings.value("labellertype", (int)labellerType).toInt();
00252     int cycle = settings.value("labellercycle", 4).toInt();
00253 
00254     settings.endGroup();
00255 
00256     m_labeller = new Labeller(labellerType);
00257     m_labeller->setCounterCycleSize(cycle);
00258 
00259     if (withMIDIInput) {
00260         m_midiInput = new MIDIInput(QApplication::applicationName(), this);
00261     }
00262 }
00263 
00264 MainWindowBase::~MainWindowBase()
00265 {
00266     SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
00267     if (m_playTarget) m_playTarget->shutdown();
00268 //    delete m_playTarget;
00269     delete m_playSource;
00270     delete m_viewManager;
00271     delete m_oscQueue;
00272     delete m_oscQueueStarter;
00273     delete m_midiInput;
00274     Profiles::getInstance()->dump();
00275 }
00276 
00277 void
00278 MainWindowBase::finaliseMenus()
00279 {
00280     delete m_menuShortcutMapper;
00281     m_menuShortcutMapper = 0;
00282 
00283     foreach (QShortcut *sc, m_appShortcuts) {
00284         delete sc;
00285     }
00286     m_appShortcuts.clear();
00287 
00288     QMenuBar *mb = menuBar();
00289 
00290     // This used to find all children of QMenu type, and call
00291     // finaliseMenu on those. But it seems we are getting hold of some
00292     // menus that way that are not actually active in the menu bar and
00293     // are not returned in their parent menu's actions() list, and if
00294     // we finalise those, we end up with duplicate shortcuts in the
00295     // app shortcut mapper. So we should do this by descending the
00296     // menu tree through only those menus accessible via actions()
00297     // from their parents instead.
00298 
00299     QList<QMenu *> menus = mb->findChildren<QMenu *>
00300         (QString(), Qt::FindDirectChildrenOnly);
00301 
00302     foreach (QMenu *menu, menus) {
00303         if (menu) finaliseMenu(menu);
00304     }
00305 }
00306 
00307 void
00308 MainWindowBase::finaliseMenu(QMenu *
00309 #ifdef Q_OS_MAC
00310                              menu
00311 #endif
00312     )
00313 {
00314 #ifdef Q_OS_MAC
00315     // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
00316     // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
00317     // single-key shortcuts that are associated only with a menu
00318     // action (and not with a toolbar button) do not work with Qt 5.x
00319     // under OS/X.
00320     // 
00321     // Apparently Cocoa never handled them as a matter of course, but
00322     // earlier versions of Qt picked them up as widget shortcuts and
00323     // handled them anyway. That behaviour was removed to fix a crash
00324     // when invoking a menu while its window was overridden by a modal
00325     // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
00326     //
00327     // This workaround restores the single-key shortcut behaviour by
00328     // searching in menus for single-key shortcuts that are associated
00329     // only with the menu and not with a toolbar button, and
00330     // augmenting them with global application shortcuts that invoke
00331     // the relevant actions, testing whether the actions are enabled
00332     // on invocation.
00333     //
00334     // (Previously this acted on all single-key shortcuts in menus,
00335     // and it removed the shortcut from the action when it created
00336     // each new global one, in order to avoid an "ambiguous shortcut"
00337     // error in the case where the action was also associated with a
00338     // toolbar button. But that has the unwelcome side-effect of
00339     // removing the shortcut hint from the menu entry. So now we leave
00340     // the shortcut in the menu action as well as creating a global
00341     // one, and we only act on shortcuts that have no toolbar button,
00342     // i.e. that will not otherwise work. The downside is that if this
00343     // bug is fixed in a future Qt release, we will start getting
00344     // "ambiguous shortcut" errors from the menu entry actions and
00345     // will need to update the code.)
00346 
00347     if (!m_menuShortcutMapper) {
00348         m_menuShortcutMapper = new QSignalMapper(this);
00349         connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)),
00350                 this, SLOT(menuActionMapperInvoked(QObject *)));
00351     }
00352 
00353     foreach (QAction *a, menu->actions()) {
00354 
00355         if (a->isSeparator()) {
00356             continue;
00357         } else if (a->menu()) {
00358             finaliseMenu(a->menu());
00359         } else {
00360 
00361             QWidgetList ww = a->associatedWidgets();
00362             bool hasButton = false;
00363             foreach (QWidget *w, ww) {
00364                 if (qobject_cast<QAbstractButton *>(w)) {
00365                     hasButton = true;
00366                     break;
00367                 }
00368             }
00369             if (hasButton) continue;
00370             QKeySequence sc = a->shortcut();
00371 
00372             // Note that the set of "single-key shortcuts" that aren't
00373             // working and that we need to handle here includes those
00374             // with the Shift modifier mask as well as those with no
00375             // modifier at all
00376             if (sc.count() == 1 &&
00377                 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier ||
00378                  (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) {
00379                 QShortcut *newSc = new QShortcut(sc, a->parentWidget());
00380                 QObject::connect(newSc, SIGNAL(activated()),
00381                                  m_menuShortcutMapper, SLOT(map()));
00382                 m_menuShortcutMapper->setMapping(newSc, a);
00383                 m_appShortcuts.push_back(newSc);
00384             }
00385         }
00386     }
00387 #endif
00388 }
00389 
00390 void
00391 MainWindowBase::menuActionMapperInvoked(QObject *o)
00392 {
00393     QAction *a = qobject_cast<QAction *>(o);
00394     if (a && a->isEnabled()) {
00395         a->trigger();
00396     }
00397 }
00398 
00399 void
00400 MainWindowBase::resizeConstrained(QSize size)
00401 {
00402     QDesktopWidget *desktop = QApplication::desktop();
00403     QRect available = desktop->availableGeometry();
00404     QSize actual(std::min(size.width(), available.width()),
00405                  std::min(size.height(), available.height()));
00406     resize(actual);
00407 }
00408 
00409 void
00410 MainWindowBase::startOSCQueue()
00411 {
00412     m_oscQueueStarter = new OSCQueueStarter(this);
00413     connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
00414     m_oscQueueStarter->start();
00415 }
00416 
00417 void
00418 MainWindowBase::oscReady()
00419 {
00420     if (m_oscQueue && m_oscQueue->isOK()) {
00421         connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
00422         QTimer *oscTimer = new QTimer(this);
00423         connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
00424         oscTimer->start(1000);
00425         cerr << "Finished setting up OSC interface" << endl;
00426     }
00427 }
00428 
00429 QString
00430 MainWindowBase::getOpenFileName(FileFinder::FileType type)
00431 {
00432     FileFinder *ff = FileFinder::getInstance();
00433 
00434     if (type == FileFinder::AnyFile) {
00435         if (getMainModel() != 0 &&
00436             m_paneStack != 0 &&
00437             m_paneStack->getCurrentPane() != 0) { // can import a layer
00438             return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
00439         } else {
00440             return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
00441                                        m_sessionFile);
00442         }
00443     }        
00444 
00445     QString lastPath = m_sessionFile;
00446 
00447     if (type == FileFinder::AudioFile) {
00448         lastPath = m_audioFile;
00449     }
00450 
00451     return ff->getOpenFileName(type, lastPath);
00452 }
00453 
00454 QString
00455 MainWindowBase::getSaveFileName(FileFinder::FileType type)
00456 {
00457     QString lastPath = m_sessionFile;
00458 
00459     if (type == FileFinder::AudioFile) {
00460         lastPath = m_audioFile;
00461     }
00462 
00463     FileFinder *ff = FileFinder::getInstance();
00464     return ff->getSaveFileName(type, lastPath);
00465 }
00466 
00467 void
00468 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
00469 {
00470     FileFinder *ff = FileFinder::getInstance();
00471     ff->registerLastOpenedFilePath(type, path);
00472 }
00473 
00474 QString
00475 MainWindowBase::getDefaultSessionTemplate() const
00476 {
00477     QSettings settings;
00478     settings.beginGroup("MainWindow");
00479     QString templateName = settings.value("sessiontemplate", "").toString();
00480     if (templateName == "") templateName = "default";
00481     return templateName;
00482 }
00483 
00484 void
00485 MainWindowBase::setDefaultSessionTemplate(QString n) 
00486 {
00487     QSettings settings;
00488     settings.beginGroup("MainWindow");
00489     settings.setValue("sessiontemplate", n);
00490 }    
00491 
00492 void
00493 MainWindowBase::updateMenuStates()
00494 {
00495     Pane *currentPane = 0;
00496     Layer *currentLayer = 0;
00497 
00498     if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
00499     if (currentPane) currentLayer = currentPane->getSelectedLayer();
00500 
00501     bool havePrevPane = false, haveNextPane = false;
00502     bool havePrevLayer = false, haveNextLayer = false;
00503 
00504     if (currentPane) {
00505         for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
00506             if (m_paneStack->getPane(i) == currentPane) {
00507                 if (i > 0) havePrevPane = true;
00508                 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true;
00509                 break;
00510             }
00511         }
00512         // the prev/next layer commands actually include the pane
00513         // itself as one of the selectables -- so we always have a
00514         // prev and next layer, as long as we have a pane with at
00515         // least one layer in it
00516         if (currentPane->getLayerCount() > 0) {
00517             havePrevLayer = true;
00518             haveNextLayer = true;
00519         }
00520     }        
00521 
00522     bool haveCurrentPane =
00523         (currentPane != 0);
00524     bool haveCurrentLayer =
00525         (haveCurrentPane &&
00526          (currentLayer != 0));
00527     bool haveMainModel =
00528         (getMainModel() != 0);
00529     bool havePlayTarget =
00530         (m_playTarget != 0);
00531     bool haveSelection = 
00532         (m_viewManager &&
00533          !m_viewManager->getSelections().empty());
00534     bool haveCurrentEditableLayer =
00535         (haveCurrentLayer &&
00536          currentLayer->isLayerEditable());
00537     bool haveCurrentTimeInstantsLayer = 
00538         (haveCurrentLayer &&
00539          dynamic_cast<TimeInstantLayer *>(currentLayer));
00540     bool haveCurrentDurationLayer = 
00541         (haveCurrentLayer &&
00542          (dynamic_cast<NoteLayer *>(currentLayer) ||
00543           dynamic_cast<FlexiNoteLayer *>(currentLayer) ||
00544           dynamic_cast<RegionLayer *>(currentLayer)));
00545     bool haveCurrentColour3DPlot =
00546         (haveCurrentLayer &&
00547          dynamic_cast<Colour3DPlotLayer *>(currentLayer));
00548     bool haveClipboardContents =
00549         (m_viewManager &&
00550          !m_viewManager->getClipboard().empty());
00551     bool haveTabularLayer =
00552         (haveCurrentLayer &&
00553          dynamic_cast<TabularModel *>(currentLayer->getModel()));
00554 
00555     emit canAddPane(haveMainModel);
00556     emit canDeleteCurrentPane(haveCurrentPane);
00557     emit canZoom(haveMainModel && haveCurrentPane);
00558     emit canScroll(haveMainModel && haveCurrentPane);
00559     emit canAddLayer(haveMainModel && haveCurrentPane);
00560     emit canImportMoreAudio(haveMainModel);
00561     emit canReplaceMainAudio(haveMainModel);
00562     emit canImportLayer(haveMainModel && haveCurrentPane);
00563     emit canExportAudio(haveMainModel);
00564     emit canChangeSessionTemplate(haveMainModel);
00565     emit canExportLayer(haveMainModel &&
00566                         (haveCurrentEditableLayer || haveCurrentColour3DPlot));
00567     emit canExportImage(haveMainModel && haveCurrentPane);
00568     emit canDeleteCurrentLayer(haveCurrentLayer);
00569     emit canRenameLayer(haveCurrentLayer);
00570     emit canEditLayer(haveCurrentEditableLayer);
00571     emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer);
00572     emit canMeasureLayer(haveCurrentLayer);
00573     emit canSelect(haveMainModel && haveCurrentPane);
00574     emit canPlay(haveMainModel && havePlayTarget);
00575     emit canFfwd(true);
00576     emit canRewind(true);
00577     emit canPaste(haveClipboardContents);
00578     emit canInsertInstant(haveCurrentPane);
00579     emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
00580     emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer);
00581     emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
00582     emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
00583     emit canClearSelection(haveSelection);
00584     emit canEditSelection(haveSelection && haveCurrentEditableLayer);
00585     emit canSave(m_sessionFile != "" && m_documentModified);
00586     emit canSaveAs(haveMainModel);
00587     emit canSelectPreviousPane(havePrevPane);
00588     emit canSelectNextPane(haveNextPane);
00589     emit canSelectPreviousLayer(havePrevLayer);
00590     emit canSelectNextLayer(haveNextLayer);
00591 }
00592 
00593 void
00594 MainWindowBase::documentModified()
00595 {
00596 //    SVDEBUG << "MainWindowBase::documentModified" << endl;
00597 
00598     if (!m_documentModified) {
00600         setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
00601     }
00602 
00603     m_documentModified = true;
00604     updateMenuStates();
00605 }
00606 
00607 void
00608 MainWindowBase::documentRestored()
00609 {
00610 //    SVDEBUG << "MainWindowBase::documentRestored" << endl;
00611 
00612     if (m_documentModified) {
00614         QString wt(windowTitle());
00615         wt.replace(tr(" (modified)"), "");
00616         setWindowTitle(wt);
00617     }
00618 
00619     m_documentModified = false;
00620     updateMenuStates();
00621 }
00622 
00623 void
00624 MainWindowBase::playLoopToggled()
00625 {
00626     QAction *action = dynamic_cast<QAction *>(sender());
00627     
00628     if (action) {
00629         m_viewManager->setPlayLoopMode(action->isChecked());
00630     } else {
00631         m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
00632     }
00633 }
00634 
00635 void
00636 MainWindowBase::playSelectionToggled()
00637 {
00638     QAction *action = dynamic_cast<QAction *>(sender());
00639     
00640     if (action) {
00641         m_viewManager->setPlaySelectionMode(action->isChecked());
00642     } else {
00643         m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
00644     }
00645 }
00646 
00647 void
00648 MainWindowBase::playSoloToggled()
00649 {
00650     QAction *action = dynamic_cast<QAction *>(sender());
00651     
00652     if (action) {
00653         m_viewManager->setPlaySoloMode(action->isChecked());
00654     } else {
00655         m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
00656     }
00657 
00658     if (m_viewManager->getPlaySoloMode()) {
00659         currentPaneChanged(m_paneStack->getCurrentPane());
00660     } else {
00661         m_viewManager->setPlaybackModel(0);
00662         if (m_playSource) {
00663             m_playSource->clearSoloModelSet();
00664         }
00665     }
00666 }
00667 
00668 void
00669 MainWindowBase::currentPaneChanged(Pane *p)
00670 {
00671     updateMenuStates();
00672     updateVisibleRangeDisplay(p);
00673 
00674     if (!p) return;
00675 
00676     if (!(m_viewManager &&
00677           m_playSource &&
00678           m_viewManager->getPlaySoloMode())) {
00679         if (m_viewManager) m_viewManager->setPlaybackModel(0);
00680         return;
00681     }
00682 
00683     Model *prevPlaybackModel = m_viewManager->getPlaybackModel();
00684 
00685     // What we want here is not the currently playing frame (unless we
00686     // are about to clear out the audio playback buffers -- which may
00687     // or may not be possible, depending on the audio driver).  What
00688     // we want is the frame that was last committed to the soundcard
00689     // buffers, as the audio driver will continue playing up to that
00690     // frame before switching to whichever one we decide we want to
00691     // switch to, regardless of our efforts.
00692 
00693     int frame = m_playSource->getCurrentBufferedFrame();
00694 
00695     cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
00696 
00697     View::ModelSet soloModels = p->getModels();
00698     
00699     View::ModelSet sources;
00700     for (View::ModelSet::iterator mi = soloModels.begin();
00701          mi != soloModels.end(); ++mi) {
00702         // If a model in this pane is derived from something else,
00703         // then we want to play that model as well -- if the model
00704         // that's derived from it is not something that is itself
00705         // individually playable (e.g. a waveform)
00706         if (*mi &&
00707             !dynamic_cast<RangeSummarisableTimeValueModel *>(*mi) &&
00708             (*mi)->getSourceModel()) {
00709             sources.insert((*mi)->getSourceModel());
00710         }
00711     }
00712     for (View::ModelSet::iterator mi = sources.begin();
00713          mi != sources.end(); ++mi) {
00714         soloModels.insert(*mi);
00715     }
00716 
00718     //playback model has changed, and changing it on ViewManager --
00719     //the play source should be making the setPlaybackModel call to
00720     //ViewManager
00721 
00722     for (View::ModelSet::iterator mi = soloModels.begin();
00723          mi != soloModels.end(); ++mi) {
00724         if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) {
00725             m_viewManager->setPlaybackModel(*mi);
00726         }
00727     }
00728     
00729     RangeSummarisableTimeValueModel *a = 
00730         dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel);
00731     RangeSummarisableTimeValueModel *b = 
00732         dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager->
00733                                                         getPlaybackModel());
00734 
00735     m_playSource->setSoloModelSet(soloModels);
00736 
00737     if (a && b && (a != b)) {
00738         if (m_playSource->isPlaying()) m_playSource->play(frame);
00739     }
00740 }
00741 
00742 void
00743 MainWindowBase::currentLayerChanged(Pane *p, Layer *)
00744 {
00745     updateMenuStates();
00746     updateVisibleRangeDisplay(p);
00747 }
00748 
00749 void
00750 MainWindowBase::selectAll()
00751 {
00752     if (!getMainModel()) return;
00753     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
00754                                           getMainModel()->getEndFrame()));
00755 }
00756 
00757 void
00758 MainWindowBase::selectToStart()
00759 {
00760     if (!getMainModel()) return;
00761     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
00762                                           m_viewManager->getGlobalCentreFrame()));
00763 }
00764 
00765 void
00766 MainWindowBase::selectToEnd()
00767 {
00768     if (!getMainModel()) return;
00769     m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
00770                                           getMainModel()->getEndFrame()));
00771 }
00772 
00773 void
00774 MainWindowBase::selectVisible()
00775 {
00776     Model *model = getMainModel();
00777     if (!model) return;
00778 
00779     Pane *currentPane = m_paneStack->getCurrentPane();
00780     if (!currentPane) return;
00781 
00782     int startFrame, endFrame;
00783 
00784     if (currentPane->getStartFrame() < 0) startFrame = 0;
00785     else startFrame = currentPane->getStartFrame();
00786 
00787     if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
00788     else endFrame = currentPane->getEndFrame();
00789 
00790     m_viewManager->setSelection(Selection(startFrame, endFrame));
00791 }
00792 
00793 void
00794 MainWindowBase::clearSelection()
00795 {
00796     m_viewManager->clearSelections();
00797 }
00798 
00799 void
00800 MainWindowBase::cut()
00801 {
00802     Pane *currentPane = m_paneStack->getCurrentPane();
00803     if (!currentPane) return;
00804 
00805     Layer *layer = currentPane->getSelectedLayer();
00806     if (!layer) return;
00807 
00808     Clipboard &clipboard = m_viewManager->getClipboard();
00809     clipboard.clear();
00810 
00811     MultiSelection::SelectionList selections = m_viewManager->getSelections();
00812 
00813     CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
00814 
00815     for (MultiSelection::SelectionList::iterator i = selections.begin();
00816          i != selections.end(); ++i) {
00817         layer->copy(currentPane, *i, clipboard);
00818         layer->deleteSelection(*i);
00819     }
00820 
00821     CommandHistory::getInstance()->endCompoundOperation();
00822 }
00823 
00824 void
00825 MainWindowBase::copy()
00826 {
00827     Pane *currentPane = m_paneStack->getCurrentPane();
00828     if (!currentPane) return;
00829 
00830     Layer *layer = currentPane->getSelectedLayer();
00831     if (!layer) return;
00832 
00833     Clipboard &clipboard = m_viewManager->getClipboard();
00834     clipboard.clear();
00835 
00836     MultiSelection::SelectionList selections = m_viewManager->getSelections();
00837 
00838     for (MultiSelection::SelectionList::iterator i = selections.begin();
00839          i != selections.end(); ++i) {
00840         layer->copy(currentPane, *i, clipboard);
00841     }
00842 }
00843 
00844 void
00845 MainWindowBase::paste()
00846 {
00847     pasteRelative(0);
00848 }
00849 
00850 void
00851 MainWindowBase::pasteAtPlaybackPosition()
00852 {
00853     int pos = getFrame();
00854     Clipboard &clipboard = m_viewManager->getClipboard();
00855     if (!clipboard.empty()) {
00856         int firstEventFrame = clipboard.getPoints()[0].getFrame();
00857         int offset = 0;
00858         if (firstEventFrame < 0) {
00859             offset = pos - firstEventFrame;
00860         } else if (firstEventFrame < pos) {
00861             offset = pos - firstEventFrame;
00862         } else {
00863             offset = -(firstEventFrame - pos);
00864         }
00865         pasteRelative(offset);
00866     }
00867 }
00868 
00869 void
00870 MainWindowBase::pasteRelative(int offset)
00871 {
00872     Pane *currentPane = m_paneStack->getCurrentPane();
00873     if (!currentPane) return;
00874 
00875     Layer *layer = currentPane->getSelectedLayer();
00876 
00877     Clipboard &clipboard = m_viewManager->getClipboard();
00878 
00879     bool inCompound = false;
00880 
00881     if (!layer || !layer->isLayerEditable()) {
00882         
00883         CommandHistory::getInstance()->startCompoundOperation
00884             (tr("Paste"), true);
00885 
00886         // no suitable current layer: create one of the most
00887         // appropriate sort
00888         LayerFactory::LayerType type =
00889             LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard);
00890         layer = m_document->createEmptyLayer(type);
00891 
00892         if (!layer) {
00893             CommandHistory::getInstance()->endCompoundOperation();
00894             return;
00895         }
00896 
00897         m_document->addLayerToView(currentPane, layer);
00898         m_paneStack->setCurrentLayer(currentPane, layer);
00899 
00900         inCompound = true;
00901     }
00902 
00903     layer->paste(currentPane, clipboard, offset, true);
00904 
00905     if (inCompound) CommandHistory::getInstance()->endCompoundOperation();
00906 }
00907 
00908 void
00909 MainWindowBase::deleteSelected()
00910 {
00911     if (m_paneStack->getCurrentPane() &&
00912         m_paneStack->getCurrentPane()->getSelectedLayer()) {
00913         
00914         Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
00915 
00916         if (m_viewManager) {
00917 
00918             if (m_viewManager->getToolMode() == ViewManager::MeasureMode) {
00919 
00920                 layer->deleteCurrentMeasureRect();
00921             
00922             } else {
00923 
00924                 MultiSelection::SelectionList selections =
00925                     m_viewManager->getSelections();
00926             
00927                 for (MultiSelection::SelectionList::iterator i = selections.begin();
00928                      i != selections.end(); ++i) {
00929                     layer->deleteSelection(*i);
00930                 }
00931             }
00932         }
00933     }
00934 }
00935 
00936 // FrameTimer method
00937 
00938 int
00939 MainWindowBase::getFrame() const
00940 {
00941     if (m_playSource && m_playSource->isPlaying()) {
00942         return m_playSource->getCurrentPlayingFrame();
00943     } else {
00944         return m_viewManager->getPlaybackFrame();
00945     }
00946 }    
00947 
00948 void
00949 MainWindowBase::insertInstant()
00950 {
00951     insertInstantAt(getFrame());
00952 }
00953 
00954 void
00955 MainWindowBase::insertInstantsAtBoundaries()
00956 {
00957     MultiSelection::SelectionList selections = m_viewManager->getSelections();
00958     for (MultiSelection::SelectionList::iterator i = selections.begin();
00959          i != selections.end(); ++i) {
00960         int start = i->getStartFrame();
00961         int end = i->getEndFrame();
00962         if (start != end) {
00963             insertInstantAt(start);
00964             insertInstantAt(end);
00965         }
00966     }
00967 }
00968 
00969 void
00970 MainWindowBase::insertInstantAt(int frame)
00971 {
00972     Pane *pane = m_paneStack->getCurrentPane();
00973     if (!pane) {
00974         return;
00975     }
00976 
00977     frame = pane->alignFromReference(frame);
00978 
00979     Layer *layer = dynamic_cast<TimeInstantLayer *>
00980         (pane->getSelectedLayer());
00981 
00982     if (!layer) {
00983         for (int i = pane->getLayerCount(); i > 0; --i) {
00984             layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
00985             if (layer) break;
00986         }
00987 
00988         if (!layer) {
00989             CommandHistory::getInstance()->startCompoundOperation
00990                 (tr("Add Point"), true);
00991             layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
00992             if (layer) {
00993                 m_document->addLayerToView(pane, layer);
00994                 m_paneStack->setCurrentLayer(pane, layer);
00995             }
00996             CommandHistory::getInstance()->endCompoundOperation();
00997         }
00998     }
00999 
01000     if (layer) {
01001     
01002         Model *model = layer->getModel();
01003         SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
01004             (model);
01005 
01006         if (sodm) {
01007             SparseOneDimensionalModel::Point point(frame, "");
01008 
01009             SparseOneDimensionalModel::Point prevPoint(0);
01010             bool havePrevPoint = false;
01011 
01012             SparseOneDimensionalModel::EditCommand *command =
01013                 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point"));
01014 
01015             if (m_labeller) {
01016 
01017                 if (m_labeller->requiresPrevPoint()) {
01018 
01019                     SparseOneDimensionalModel::PointList prevPoints =
01020                         sodm->getPreviousPoints(frame);
01021 
01022                     if (!prevPoints.empty()) {
01023                         prevPoint = *prevPoints.begin();
01024                         havePrevPoint = true;
01025                     }
01026                 }
01027 
01028                 m_labeller->setSampleRate(sodm->getSampleRate());
01029 
01030                 if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
01031                     command->deletePoint(prevPoint);
01032                 }
01033 
01034                 m_labeller->label<SparseOneDimensionalModel::Point>
01035                     (point, havePrevPoint ? &prevPoint : 0);
01036 
01037                 if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
01038                     command->addPoint(prevPoint);
01039                 }
01040             }
01041             
01042             command->addPoint(point);
01043 
01044             command->setName(tr("Add Point at %1 s")
01045                              .arg(RealTime::frame2RealTime
01046                                   (frame,
01047                                    sodm->getSampleRate())
01048                                   .toText(false).c_str()));
01049 
01050             Command *c = command->finish();
01051             if (c) CommandHistory::getInstance()->addCommand(c, false);
01052         }
01053     }
01054 }
01055 
01056 void
01057 MainWindowBase::insertItemAtSelection()
01058 {
01059     MultiSelection::SelectionList selections = m_viewManager->getSelections();
01060     for (MultiSelection::SelectionList::iterator i = selections.begin();
01061          i != selections.end(); ++i) {
01062         int start = i->getStartFrame();
01063         int end = i->getEndFrame();
01064         if (start < end) {
01065             insertItemAt(start, end - start);
01066         }
01067     }
01068 }
01069 
01070 void
01071 MainWindowBase::insertItemAt(int frame, int duration)
01072 {
01073     Pane *pane = m_paneStack->getCurrentPane();
01074     if (!pane) {
01075         return;
01076     }
01077 
01078     // ugh!
01079 
01080     int alignedStart = pane->alignFromReference(frame);
01081     int alignedEnd = pane->alignFromReference(frame + duration);
01082     if (alignedStart >= alignedEnd) return;
01083     int alignedDuration = alignedEnd - alignedStart;
01084 
01085     Command *c = 0;
01086 
01087     QString name = tr("Add Item at %1 s")
01088         .arg(RealTime::frame2RealTime
01089              (alignedStart,
01090               getMainModel()->getSampleRate())
01091              .toText(false).c_str());
01092 
01093     Layer *layer = pane->getSelectedLayer();
01094     if (!layer) return;
01095 
01096     RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel());
01097     if (rm) {
01098         RegionModel::Point point(alignedStart,
01099                                  rm->getValueMaximum() + 1,
01100                                  alignedDuration,
01101                                  "");
01102         RegionModel::EditCommand *command =
01103             new RegionModel::EditCommand(rm, tr("Add Point"));
01104         command->addPoint(point);
01105         command->setName(name);
01106         c = command->finish();
01107     }
01108 
01109     if (c) {
01110         CommandHistory::getInstance()->addCommand(c, false);
01111         return;
01112     }
01113 
01114     NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel());
01115     if (nm) {
01116         NoteModel::Point point(alignedStart,
01117                                nm->getValueMinimum(),
01118                                alignedDuration,
01119                                1.f,
01120                                "");
01121         NoteModel::EditCommand *command =
01122             new NoteModel::EditCommand(nm, tr("Add Point"));
01123         command->addPoint(point);
01124         command->setName(name);
01125         c = command->finish();
01126     }
01127 
01128     if (c) {
01129         CommandHistory::getInstance()->addCommand(c, false);
01130         return;
01131     }
01132 
01133     FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel());
01134     if (fnm) {
01135         FlexiNoteModel::Point point(alignedStart,
01136                                     fnm->getValueMinimum(),
01137                                     alignedDuration,
01138                                     1.f,
01139                                     "");
01140         FlexiNoteModel::EditCommand *command =
01141             new FlexiNoteModel::EditCommand(fnm, tr("Add Point"));
01142         command->addPoint(point);
01143         command->setName(name);
01144         c = command->finish();
01145     }
01146     
01147     if (c) {
01148         CommandHistory::getInstance()->addCommand(c, false);
01149         return;
01150     }
01151 }
01152 
01153 void
01154 MainWindowBase::renumberInstants()
01155 {
01156     Pane *pane = m_paneStack->getCurrentPane();
01157     if (!pane) return;
01158 
01159     Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
01160     if (!layer) return;
01161 
01162     MultiSelection ms(m_viewManager->getSelection());
01163     
01164     Model *model = layer->getModel();
01165     SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
01166         (model);
01167     if (!sodm) return;
01168 
01169     if (!m_labeller) return;
01170 
01171     Labeller labeller(*m_labeller);
01172     labeller.setSampleRate(sodm->getSampleRate());
01173 
01174     // This uses a command
01175 
01176     labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
01177 }
01178 
01179 MainWindowBase::FileOpenStatus
01180 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
01181 {
01182     ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
01183     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
01184     return open(FileSource(fileOrUrl, &dialog), mode);
01185 }
01186 
01187 MainWindowBase::FileOpenStatus
01188 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
01189 {
01190     FileOpenStatus status;
01191 
01192     if (!source.isAvailable()) return FileOpenFailed;
01193     source.waitForData();
01194 
01195     bool canImportLayer = (getMainModel() != 0 &&
01196                            m_paneStack != 0 &&
01197                            m_paneStack->getCurrentPane() != 0);
01198 
01199     bool rdf = (source.getExtension().toLower() == "rdf" ||
01200                 source.getExtension().toLower() == "n3" ||
01201                 source.getExtension().toLower() == "ttl");
01202 
01203     bool audio = AudioFileReaderFactory::getKnownExtensions().contains
01204         (source.getExtension().toLower());
01205 
01206     bool rdfSession = false;
01207     if (rdf) {
01208         RDFImporter::RDFDocumentType rdfType = 
01209             RDFImporter::identifyDocumentType
01210             (QUrl::fromLocalFile(source.getLocalFilename()).toString());
01211         if (rdfType == RDFImporter::AudioRefAndAnnotations ||
01212             rdfType == RDFImporter::AudioRef) {
01213             rdfSession = true;
01214         } else if (rdfType == RDFImporter::NotRDF) {
01215             rdf = false;
01216         }
01217     }
01218 
01219     if (rdf) {
01220         if (rdfSession) {
01221             bool cancel = false;
01222             if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
01223                 return openSession(source);
01224             } else if (cancel) {
01225                 return FileOpenCancelled;
01226             } else {
01227                 return openLayer(source);
01228             }
01229         } else {
01230             if ((status = openSession(source)) != FileOpenFailed) {
01231                 return status;
01232             } else if (!canImportLayer) {
01233                 return FileOpenWrongMode;
01234             } else if ((status = openLayer(source)) != FileOpenFailed) {
01235                 return status;
01236             } else {
01237                 return FileOpenFailed;
01238             }
01239         }
01240     }
01241 
01242     if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
01243         return status;
01244     } else if ((status = openSession(source)) != FileOpenFailed) {
01245         return status;
01246     } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
01247         return status;
01248     } else if (!canImportLayer) {
01249         return FileOpenWrongMode;
01250     } else if ((status = openImage(source)) != FileOpenFailed) {
01251         return status;
01252     } else if ((status = openLayer(source)) != FileOpenFailed) {
01253         return status;
01254     } else {
01255         return FileOpenFailed;
01256     }
01257 }
01258 
01259 MainWindowBase::FileOpenStatus
01260 MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode,
01261                           QString templateName)
01262 {
01263     SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
01264 
01265     if (templateName == "") {
01266         templateName = getDefaultSessionTemplate();
01267     }
01268 
01269 //    cerr << "template is: \"" << templateName << "\"" << endl;
01270 
01271     if (!source.isAvailable()) {
01272         if (source.wasCancelled()) {
01273             return FileOpenCancelled;
01274         } else {
01275             return FileOpenFailed;
01276         }
01277     }
01278 
01279     source.waitForData();
01280 
01281     m_openingAudioFile = true;
01282 
01283     int rate = 0;
01284 
01285     if (Preferences::getInstance()->getFixedSampleRate() != 0) {
01286         rate = Preferences::getInstance()->getFixedSampleRate();
01287     } else if (Preferences::getInstance()->getResampleOnLoad()) {
01288         rate = m_playSource->getSourceSampleRate();
01289     }
01290 
01291     WaveFileModel *newModel = new WaveFileModel(source, rate);
01292 
01293     if (!newModel->isOK()) {
01294         delete newModel;
01295         m_openingAudioFile = false;
01296         if (source.wasCancelled()) {
01297             return FileOpenCancelled;
01298         } else { 
01299             return FileOpenFailed;
01300         }
01301     }
01302 
01303 //    cerr << "mode = " << mode << endl;
01304 
01305     if (mode == AskUser) {
01306         if (getMainModel()) {
01307 
01308             QSettings settings;
01309             settings.beginGroup("MainWindow");
01310             int lastMode = settings.value("lastaudioopenmode", 0).toBool();
01311             settings.endGroup();
01312             int imode = 0;
01313             
01314             QStringList items;
01315             items << tr("Close the current session and start a new one")
01316                   << tr("Replace the main audio file in this session")
01317                   << tr("Add the audio file to this session");
01318 
01319             bool ok = false;
01320             QString item = ListInputDialog::getItem
01321                 (this, tr("Select target for import"),
01322                  tr("<b>Select a target for import</b><p>You already have an audio file loaded.<br>What would you like to do with the new audio file?"),
01323                  items, lastMode, &ok);
01324             
01325             if (!ok || item.isEmpty()) {
01326                 delete newModel;
01327                 m_openingAudioFile = false;
01328                 return FileOpenCancelled;
01329             }
01330             
01331             for (int i = 0; i < items.size(); ++i) {
01332                 if (item == items[i]) imode = i;
01333             }
01334 
01335             settings.beginGroup("MainWindow");
01336             settings.setValue("lastaudioopenmode", imode);
01337             settings.endGroup();
01338 
01339             mode = (AudioFileOpenMode)imode;
01340 
01341         } else {
01342             // no main model: make a new session
01343             mode = ReplaceSession;
01344         }
01345     }
01346 
01347     if (mode == ReplaceCurrentPane) {
01348 
01349         Pane *pane = m_paneStack->getCurrentPane();
01350         if (pane) {
01351             if (getMainModel()) {
01352                 View::ModelSet models(pane->getModels());
01353                 if (models.find(getMainModel()) != models.end()) {
01354                     // Current pane contains main model: replace that
01355                     mode = ReplaceMainModel;
01356                 }
01357                 // Otherwise the current pane has a non-default model,
01358                 // which we will deal with later
01359             } else {
01360                 // We have no main model, so start a new session with
01361                 // optional template
01362                 mode = ReplaceSession;
01363             }
01364         } else {
01365             // We seem to have no current pane!  Oh well
01366             mode = CreateAdditionalModel;
01367         }
01368     }
01369 
01370     if (mode == CreateAdditionalModel && !getMainModel()) {
01371         SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
01372         mode = ReplaceSession;
01373     }
01374 
01375     bool loadedTemplate = false;
01376 
01377     if (mode == ReplaceSession) {
01378 
01379         if (!checkSaveModified()) return FileOpenCancelled;
01380 
01381         SVDEBUG << "SV looking for template " << templateName << endl;
01382         if (templateName != "") {
01383             FileOpenStatus tplStatus = openSessionTemplate(templateName);
01384             if (tplStatus == FileOpenCancelled) {
01385                 cerr << "Template load cancelled" << endl;
01386                 return FileOpenCancelled;
01387             }
01388             if (tplStatus != FileOpenFailed) {
01389                 cerr << "Template load succeeded" << endl;
01390                 loadedTemplate = true;
01391             }
01392         }
01393 
01394         if (!loadedTemplate) {
01395             SVDEBUG << "No template found: closing session, creating new empty document" << endl;
01396             closeSession();
01397             createDocument();
01398         }
01399 
01400         SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
01401         mode = ReplaceMainModel;
01402     }
01403 
01404     emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
01405 
01406     if (mode == ReplaceMainModel) {
01407 
01408         Model *prevMain = getMainModel();
01409         if (prevMain) {
01410             m_playSource->removeModel(prevMain);
01411             PlayParameterRepository::getInstance()->removePlayable(prevMain);
01412         }
01413         PlayParameterRepository::getInstance()->addPlayable(newModel);
01414 
01415         SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
01416 
01417         m_document->setMainModel(newModel);
01418 
01419         setupMenus();
01420 
01421         if (loadedTemplate || (m_sessionFile == "")) {
01423             setWindowTitle(tr("%1: %2")
01424                            .arg(QApplication::applicationName())
01425                            .arg(source.getLocation()));
01426             CommandHistory::getInstance()->clear();
01427             CommandHistory::getInstance()->documentSaved();
01428             m_documentModified = false;
01429         } else {
01430             setWindowTitle(tr("%1: %2 [%3]")
01431                            .arg(QApplication::applicationName())
01432                            .arg(QFileInfo(m_sessionFile).fileName())
01433                            .arg(source.getLocation()));
01434             if (m_documentModified) {
01435                 m_documentModified = false;
01436                 documentModified(); // so as to restore "(modified)" window title
01437             }
01438         }
01439 
01440         if (!source.isRemote()) m_audioFile = source.getLocalFilename();
01441 
01442     } else if (mode == CreateAdditionalModel) {
01443 
01444         CommandHistory::getInstance()->startCompoundOperation
01445             (tr("Import \"%1\"").arg(source.getBasename()), true);
01446 
01447         m_document->addImportedModel(newModel);
01448 
01449         AddPaneCommand *command = new AddPaneCommand(this);
01450         CommandHistory::getInstance()->addCommand(command);
01451 
01452         Pane *pane = command->getPane();
01453 
01454         if (m_timeRulerLayer) {
01455             m_document->addLayerToView(pane, m_timeRulerLayer);
01456         }
01457 
01458         Layer *newLayer = m_document->createImportedLayer(newModel);
01459 
01460         if (newLayer) {
01461             m_document->addLayerToView(pane, newLayer);
01462         }
01463         
01464         CommandHistory::getInstance()->endCompoundOperation();
01465 
01466     } else if (mode == ReplaceCurrentPane) {
01467 
01468         // We know there is a current pane, otherwise we would have
01469         // reset the mode to CreateAdditionalModel above; and we know
01470         // the current pane does not contain the main model, otherwise
01471         // we would have reset it to ReplaceMainModel.  But we don't
01472         // know whether the pane contains a waveform model at all.
01473         
01474         Pane *pane = m_paneStack->getCurrentPane();
01475         Layer *replace = 0;
01476 
01477         for (int i = 0; i < pane->getLayerCount(); ++i) {
01478             Layer *layer = pane->getLayer(i);
01479             if (dynamic_cast<WaveformLayer *>(layer)) {
01480                 replace = layer;
01481                 break;
01482             }
01483         }
01484 
01485         CommandHistory::getInstance()->startCompoundOperation
01486             (tr("Import \"%1\"").arg(source.getBasename()), true);
01487 
01488         m_document->addImportedModel(newModel);
01489 
01490         if (replace) {
01491             m_document->removeLayerFromView(pane, replace);
01492         }
01493 
01494         Layer *newLayer = m_document->createImportedLayer(newModel);
01495 
01496         if (newLayer) {
01497             m_document->addLayerToView(pane, newLayer);
01498         }
01499         
01500         CommandHistory::getInstance()->endCompoundOperation();
01501     }
01502 
01503     updateMenuStates();
01504     m_recentFiles.addFile(source.getLocation());
01505     if (!source.isRemote()) {
01506         // for file dialog
01507         registerLastOpenedFilePath(FileFinder::AudioFile,
01508                                    source.getLocalFilename());
01509     }
01510     m_openingAudioFile = false;
01511 
01512     currentPaneChanged(m_paneStack->getCurrentPane());
01513 
01514     emit audioFileLoaded();
01515 
01516     return FileOpenSucceeded;
01517 }
01518 
01519 MainWindowBase::FileOpenStatus
01520 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
01521 {
01522     SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
01523 
01524     std::set<QString> extensions;
01525     PlaylistFileReader::getSupportedExtensions(extensions);
01526     QString extension = source.getExtension().toLower();
01527     if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
01528 
01529     if (!source.isAvailable()) return FileOpenFailed;
01530     source.waitForData();
01531 
01532     PlaylistFileReader reader(source.getLocalFilename());
01533     if (!reader.isOK()) return FileOpenFailed;
01534 
01535     PlaylistFileReader::Playlist playlist = reader.load();
01536 
01537     bool someSuccess = false;
01538 
01539     for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
01540          i != playlist.end(); ++i) {
01541 
01542         ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
01543         connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
01544         FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
01545 
01546         if (status == FileOpenCancelled) {
01547             return FileOpenCancelled;
01548         }
01549 
01550         if (status == FileOpenSucceeded) {
01551             someSuccess = true;
01552             mode = CreateAdditionalModel;
01553         }
01554     }
01555 
01556     if (someSuccess) return FileOpenSucceeded;
01557     else return FileOpenFailed;
01558 }
01559 
01560 MainWindowBase::FileOpenStatus
01561 MainWindowBase::openLayer(FileSource source)
01562 {
01563     SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
01564 
01565     Pane *pane = m_paneStack->getCurrentPane();
01566     
01567     if (!pane) {
01568         // shouldn't happen, as the menu action should have been disabled
01569         cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
01570         return FileOpenWrongMode;
01571     }
01572 
01573     if (!getMainModel()) {
01574         // shouldn't happen, as the menu action should have been disabled
01575         cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
01576         return FileOpenWrongMode;
01577     }
01578 
01579     if (!source.isAvailable()) return FileOpenFailed;
01580     source.waitForData();
01581 
01582     QString path = source.getLocalFilename();
01583 
01584     RDFImporter::RDFDocumentType rdfType = 
01585         RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
01586 
01587 //    cerr << "RDF type:  (in layer) " << (int) rdfType << endl;
01588 
01589     if (rdfType != RDFImporter::NotRDF) {
01590 
01591         return openLayersFromRDF(source);
01592 
01593     } else if (source.getExtension().toLower() == "svl" ||
01594                (source.getExtension().toLower() == "xml" &&
01595                 (SVFileReader::identifyXmlFile(source.getLocalFilename())
01596                  == SVFileReader::SVLayerFile))) {
01597 
01598         PaneCallback callback(this);
01599         QFile file(path);
01600         
01601         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
01602             cerr << "ERROR: MainWindowBase::openLayer("
01603                       << source.getLocation()
01604                       << "): Failed to open file for reading" << endl;
01605             return FileOpenFailed;
01606         }
01607         
01608         SVFileReader reader(m_document, callback, source.getLocation());
01609         connect
01610             (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
01611              this, SLOT(modelRegenerationFailed(QString, QString, QString)));
01612         connect
01613             (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
01614              this, SLOT(modelRegenerationWarning(QString, QString, QString)));
01615         reader.setCurrentPane(pane);
01616         
01617         QXmlInputSource inputSource(&file);
01618         reader.parse(inputSource);
01619         
01620         if (!reader.isOK()) {
01621             cerr << "ERROR: MainWindowBase::openLayer("
01622                       << source.getLocation()
01623                       << "): Failed to read XML file: "
01624                       << reader.getErrorString() << endl;
01625             return FileOpenFailed;
01626         }
01627 
01628         emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
01629 
01630         m_recentFiles.addFile(source.getLocation());
01631 
01632         if (!source.isRemote()) {
01633             registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
01634         }
01635 
01636         return FileOpenSucceeded;
01637 
01638     } else {
01639         
01640         try {
01641 
01642             MIDIFileImportDialog midiDlg(this);
01643 
01644             Model *model = DataFileReaderFactory::loadNonCSV
01645                 (path, &midiDlg, getMainModel()->getSampleRate());
01646         
01647             if (!model) {
01648                 CSVFormat format(path);
01649                 format.setSampleRate(getMainModel()->getSampleRate());
01650                 CSVFormatDialog *dialog = new CSVFormatDialog(this, format);
01651                 if (dialog->exec() == QDialog::Accepted) {
01652                     model = DataFileReaderFactory::loadCSV
01653                         (path, dialog->getFormat(),
01654                          getMainModel()->getSampleRate());
01655                 }
01656             }
01657 
01658             if (model) {
01659 
01660                 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
01661 
01662                 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
01663 
01664                 Layer *newLayer = m_document->createImportedLayer(model);
01665 
01666                 if (newLayer) {
01667 
01668                     m_document->addLayerToView(pane, newLayer);
01669                     m_paneStack->setCurrentLayer(pane, newLayer);
01670 
01671                     m_recentFiles.addFile(source.getLocation());
01672                     
01673                     if (!source.isRemote()) {
01674                         registerLastOpenedFilePath
01675                             (FileFinder::LayerFile,
01676                              path); // for file dialog
01677                     }
01678 
01679                     return FileOpenSucceeded;
01680                 }
01681             }
01682         } catch (DataFileReaderFactory::Exception e) {
01683             if (e == DataFileReaderFactory::ImportCancelled) {
01684                 return FileOpenCancelled;
01685             }
01686         }
01687     }
01688     
01689     return FileOpenFailed;
01690 }
01691 
01692 MainWindowBase::FileOpenStatus
01693 MainWindowBase::openImage(FileSource source)
01694 {
01695     SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
01696 
01697     Pane *pane = m_paneStack->getCurrentPane();
01698     
01699     if (!pane) {
01700         // shouldn't happen, as the menu action should have been disabled
01701         cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
01702         return FileOpenWrongMode;
01703     }
01704 
01705     if (!m_document->getMainModel()) {
01706         return FileOpenWrongMode;
01707     }
01708 
01709     bool newLayer = false;
01710 
01711     ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
01712     if (!il) {
01713         for (int i = pane->getLayerCount()-1; i >= 0; --i) {
01714             il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
01715             if (il) break;
01716         }
01717     }
01718     if (!il) {
01719         il = dynamic_cast<ImageLayer *>
01720             (m_document->createEmptyLayer(LayerFactory::Image));
01721         if (!il) return FileOpenFailed;
01722         newLayer = true;
01723     }
01724 
01725     // We don't put the image file in Recent Files
01726 
01727     cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
01728 
01729     if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
01730         if (newLayer) {
01731             m_document->deleteLayer(il); // also releases its model
01732         }
01733         return FileOpenFailed;
01734     } else {
01735         if (newLayer) {
01736             m_document->addLayerToView(pane, il);
01737         }
01738         m_paneStack->setCurrentLayer(pane, il);
01739     }
01740 
01741     return FileOpenSucceeded;
01742 }
01743 
01744 MainWindowBase::FileOpenStatus
01745 MainWindowBase::openSessionPath(QString fileOrUrl)
01746 {
01747     ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
01748     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
01749     return openSession(FileSource(fileOrUrl, &dialog));
01750 }
01751 
01752 MainWindowBase::FileOpenStatus
01753 MainWindowBase::openSession(FileSource source)
01754 {
01755     SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
01756 
01757     if (!source.isAvailable()) return FileOpenFailed;
01758     source.waitForData();
01759 
01760     QString sessionExt = 
01761         InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
01762 
01763     if (source.getExtension().toLower() != sessionExt) {
01764 
01765         RDFImporter::RDFDocumentType rdfType = 
01766             RDFImporter::identifyDocumentType
01767             (QUrl::fromLocalFile(source.getLocalFilename()).toString());
01768 
01769 //        cerr << "RDF type: " << (int)rdfType << endl;
01770 
01771         if (rdfType == RDFImporter::AudioRefAndAnnotations ||
01772             rdfType == RDFImporter::AudioRef) {
01773             return openSessionFromRDF(source);
01774         } else if (rdfType != RDFImporter::NotRDF) {
01775             return FileOpenFailed;
01776         }
01777 
01778         if (source.getExtension().toLower() == "xml") {
01779             if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
01780                 SVFileReader::SVSessionFile) {
01781                 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
01782             } else {
01783                 return FileOpenFailed;
01784             }
01785         } else {
01786             return FileOpenFailed;
01787         }
01788     }
01789 
01790     QXmlInputSource *inputSource = 0;
01791     BZipFileDevice *bzFile = 0;
01792     QFile *rawFile = 0;
01793 
01794     if (source.getExtension().toLower() == sessionExt) {
01795         bzFile = new BZipFileDevice(source.getLocalFilename());
01796         if (!bzFile->open(QIODevice::ReadOnly)) {
01797             delete bzFile;
01798             return FileOpenFailed;
01799         }
01800         inputSource = new QXmlInputSource(bzFile);
01801     } else {
01802         rawFile = new QFile(source.getLocalFilename());
01803         inputSource = new QXmlInputSource(rawFile);
01804     }
01805 
01806     if (!checkSaveModified()) {
01807         if (bzFile) bzFile->close();
01808         delete inputSource;
01809         delete bzFile;
01810         delete rawFile;
01811         return FileOpenCancelled;
01812     }
01813 
01814     QString error;
01815     closeSession();
01816     createDocument();
01817 
01818     PaneCallback callback(this);
01819     m_viewManager->clearSelections();
01820 
01821     SVFileReader reader(m_document, callback, source.getLocation());
01822     connect
01823         (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
01824          this, SLOT(modelRegenerationFailed(QString, QString, QString)));
01825     connect
01826         (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
01827          this, SLOT(modelRegenerationWarning(QString, QString, QString)));
01828 
01829     reader.parse(*inputSource);
01830     
01831     if (!reader.isOK()) {
01832         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
01833     }
01834     
01835     if (bzFile) bzFile->close();
01836 
01837     delete inputSource;
01838     delete bzFile;
01839     delete rawFile;
01840 
01841     bool ok = (error == "");
01842 
01843     if (ok) {
01844 
01845         emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
01846 
01847         setWindowTitle(tr("%1: %2")
01848                        .arg(QApplication::applicationName())
01849                        .arg(source.getLocation()));
01850 
01851         if (!source.isRemote()) m_sessionFile = source.getLocalFilename();
01852 
01853         setupMenus();
01854 
01855         CommandHistory::getInstance()->clear();
01856         CommandHistory::getInstance()->documentSaved();
01857         m_documentModified = false;
01858         updateMenuStates();
01859 
01860         m_recentFiles.addFile(source.getLocation());
01861 
01862         if (!source.isRemote()) {
01863             // for file dialog
01864             registerLastOpenedFilePath(FileFinder::SessionFile,
01865                                        source.getLocalFilename());
01866         }
01867 
01868         emit sessionLoaded();
01869 
01870     } else {
01871         setWindowTitle(QApplication::applicationName());
01872     }
01873 
01874     return ok ? FileOpenSucceeded : FileOpenFailed;
01875 }
01876 
01877 MainWindowBase::FileOpenStatus
01878 MainWindowBase::openSessionTemplate(QString templateName)
01879 {
01880     // Template in the user's template directory takes
01881     // priority over a bundled one; we don't unbundle, but
01882     // open directly from the bundled file (where applicable)
01883     ResourceFinder rf;
01884     QString tfile = rf.getResourcePath("templates", templateName + ".svt");
01885     if (tfile != "") {
01886         cerr << "SV loading template file " << tfile << endl;
01887         return openSessionTemplate(FileSource("file:" + tfile));
01888     } else {
01889         return FileOpenFailed;
01890     }
01891 }
01892 
01893 MainWindowBase::FileOpenStatus
01894 MainWindowBase::openSessionTemplate(FileSource source)
01895 {
01896     cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
01897 
01898     if (!source.isAvailable()) return FileOpenFailed;
01899     source.waitForData();
01900 
01901     QXmlInputSource *inputSource = 0;
01902     QFile *file = 0;
01903 
01904     file = new QFile(source.getLocalFilename());
01905     inputSource = new QXmlInputSource(file);
01906 
01907     if (!checkSaveModified()) {
01908         delete inputSource;
01909         delete file;
01910         return FileOpenCancelled;
01911     }
01912 
01913     QString error;
01914     closeSession();
01915     createDocument();
01916 
01917     PaneCallback callback(this);
01918     m_viewManager->clearSelections();
01919 
01920     SVFileReader reader(m_document, callback, source.getLocation());
01921     connect
01922         (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
01923          this, SLOT(modelRegenerationFailed(QString, QString, QString)));
01924     connect
01925         (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
01926          this, SLOT(modelRegenerationWarning(QString, QString, QString)));
01927 
01928     reader.parse(*inputSource);
01929     
01930     if (!reader.isOK()) {
01931         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
01932     }
01933     
01934     delete inputSource;
01935     delete file;
01936 
01937     bool ok = (error == "");
01938 
01939     setWindowTitle(QApplication::applicationName());
01940 
01941     if (ok) {
01942 
01943         emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
01944 
01945         setupMenus();
01946 
01947         CommandHistory::getInstance()->clear();
01948         CommandHistory::getInstance()->documentSaved();
01949         m_documentModified = false;
01950         updateMenuStates();
01951 
01952         emit sessionLoaded();
01953     }
01954 
01955     return ok ? FileOpenSucceeded : FileOpenFailed;
01956 }
01957 
01958 MainWindowBase::FileOpenStatus
01959 MainWindowBase::openSessionFromRDF(FileSource source)
01960 {
01961     SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
01962 
01963     if (!source.isAvailable()) return FileOpenFailed;
01964     source.waitForData();
01965 
01966     if (!checkSaveModified()) {
01967         return FileOpenCancelled;
01968     }
01969     
01970     closeSession();
01971     createDocument();
01972 
01973     FileOpenStatus status = openLayersFromRDF(source);
01974 
01975     setupMenus();
01976     
01977     setWindowTitle(tr("%1: %2")
01978                    .arg(QApplication::applicationName())
01979                    .arg(source.getLocation()));
01980     CommandHistory::getInstance()->clear();
01981     CommandHistory::getInstance()->documentSaved();
01982     m_documentModified = false;
01983 
01984     emit sessionLoaded();
01985 
01986     return status;
01987 }
01988 
01989 MainWindowBase::FileOpenStatus
01990 MainWindowBase::openLayersFromRDF(FileSource source)
01991 {
01992     int rate = 0;
01993 
01994     SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
01995 
01996     ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
01997     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
01998 
01999     if (getMainModel()) {
02000         rate = getMainModel()->getSampleRate();
02001     } else if (Preferences::getInstance()->getResampleOnLoad()) {
02002         rate = m_playSource->getSourceSampleRate();
02003     }
02004 
02005     RDFImporter importer
02006         (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
02007 
02008     if (!importer.isOK()) {
02009         if (importer.getErrorString() != "") {
02010             QMessageBox::critical
02011                 (this, tr("Failed to import RDF"),
02012                  tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
02013                  .arg(source.getLocation()).arg(importer.getErrorString()));
02014         }
02015         return FileOpenFailed;
02016     }
02017 
02018     std::vector<Model *> models = importer.getDataModels(&dialog);
02019 
02020     dialog.setMessage(tr("Importing from RDF..."));
02021 
02022     if (models.empty()) {
02023         QMessageBox::critical
02024             (this, tr("Failed to import RDF"),
02025              tr("<b>Failed to import RDF</b><p>No suitable data models found for import from RDF document at \"%1\"</p>").arg(source.getLocation()));
02026         return FileOpenFailed;
02027     }
02028 
02029     emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
02030 
02031     std::set<Model *> added;
02032 
02033     for (int i = 0; i < (int)models.size(); ++i) {
02034 
02035         Model *m = models[i];
02036         WaveFileModel *w = dynamic_cast<WaveFileModel *>(m);
02037 
02038         if (w) {
02039 
02040             Pane *pane = addPaneToStack();
02041             Layer *layer = 0;
02042 
02043             if (m_timeRulerLayer) {
02044                 m_document->addLayerToView(pane, m_timeRulerLayer);
02045             }
02046 
02047             if (!getMainModel()) {
02048                 m_document->setMainModel(w);
02049                 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
02050             } else {
02051                 layer = m_document->createImportedLayer(w);
02052             }
02053 
02054             m_document->addLayerToView(pane, layer);
02055 
02056             added.insert(w);
02057             
02058             for (int j = 0; j < (int)models.size(); ++j) {
02059 
02060                 Model *dm = models[j];
02061 
02062                 if (dm == m) continue;
02063                 if (dm->getSourceModel() != m) continue;
02064 
02065                 layer = m_document->createImportedLayer(dm);
02066 
02067                 if (layer->isLayerOpaque() ||
02068                     dynamic_cast<Colour3DPlotLayer *>(layer)) {
02069 
02070                     // these always go in a new pane, with nothing
02071                     // else going in the same pane
02072 
02073                     Pane *singleLayerPane = addPaneToStack();
02074                     if (m_timeRulerLayer) {
02075                         m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
02076                     }
02077                     m_document->addLayerToView(singleLayerPane, layer);
02078 
02079                 } else if (layer->getLayerColourSignificance() ==
02080                            Layer::ColourHasMeaningfulValue) {
02081 
02082                     // these can go in a pane with something else, but
02083                     // only if none of the something elses also have
02084                     // this quality
02085 
02086                     bool needNewPane = false;
02087                     for (int i = 0; i < pane->getLayerCount(); ++i) {
02088                         Layer *otherLayer = pane->getLayer(i);
02089                         if (otherLayer &&
02090                             (otherLayer->getLayerColourSignificance() ==
02091                              Layer::ColourHasMeaningfulValue)) {
02092                             needNewPane = true;
02093                             break;
02094                         }
02095                     }
02096                     if (needNewPane) {
02097                         pane = addPaneToStack();
02098                     }
02099 
02100                     m_document->addLayerToView(pane, layer);
02101 
02102                 } else {
02103 
02104                     if (pane->getLayerCount() > 4) {
02105                         pane = addPaneToStack();
02106                     }
02107 
02108                     m_document->addLayerToView(pane, layer);
02109                 }
02110 
02111                 added.insert(dm);
02112             }
02113         }
02114     }
02115 
02116     for (int i = 0; i < (int)models.size(); ++i) {
02117 
02118         Model *m = models[i];
02119 
02120         if (added.find(m) == added.end()) {
02121             
02122             Layer *layer = m_document->createImportedLayer(m);
02123             if (!layer) return FileOpenFailed;
02124 
02125             Pane *singleLayerPane = addPaneToStack();
02126             if (m_timeRulerLayer) {
02127                 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
02128             }
02129             m_document->addLayerToView(singleLayerPane, layer);
02130         }
02131     }
02132             
02133     m_recentFiles.addFile(source.getLocation());
02134     return FileOpenSucceeded;
02135 }
02136 
02137 void
02138 MainWindowBase::createPlayTarget()
02139 {
02140     if (m_playTarget) return;
02141 
02142     QSettings settings;
02143     settings.beginGroup("Preferences");
02144     QString targetName = settings.value("audio-target", "").toString();
02145     settings.endGroup();
02146 
02147     AudioTargetFactory *factory = AudioTargetFactory::getInstance();
02148 
02149     factory->setDefaultCallbackTarget(targetName);
02150     m_playTarget = factory->createCallbackTarget(m_playSource);
02151 
02152     if (!m_playTarget) {
02153         emit hideSplash();
02154 
02155         if (factory->isAutoCallbackTarget(targetName)) {
02156             QMessageBox::warning
02157             (this, tr("Couldn't open audio device"),
02158              tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"),
02159              QMessageBox::Ok);
02160         } else {
02161             QMessageBox::warning
02162                 (this, tr("Couldn't open audio device"),
02163                  tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>")
02164                  .arg(factory->getCallbackTargetDescription(targetName)),
02165                  QMessageBox::Ok);
02166         }
02167     }
02168 }
02169 
02170 WaveFileModel *
02171 MainWindowBase::getMainModel()
02172 {
02173     if (!m_document) return 0;
02174     return m_document->getMainModel();
02175 }
02176 
02177 const WaveFileModel *
02178 MainWindowBase::getMainModel() const
02179 {
02180     if (!m_document) return 0;
02181     return m_document->getMainModel();
02182 }
02183 
02184 void
02185 MainWindowBase::createDocument()
02186 {
02187     m_document = new Document;
02188 
02189     connect(m_document, SIGNAL(layerAdded(Layer *)),
02190             this, SLOT(layerAdded(Layer *)));
02191     connect(m_document, SIGNAL(layerRemoved(Layer *)),
02192             this, SLOT(layerRemoved(Layer *)));
02193     connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
02194             this, SLOT(layerAboutToBeDeleted(Layer *)));
02195     connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
02196             this, SLOT(layerInAView(Layer *, bool)));
02197 
02198     connect(m_document, SIGNAL(modelAdded(Model *)),
02199             this, SLOT(modelAdded(Model *)));
02200     connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
02201             this, SLOT(mainModelChanged(WaveFileModel *)));
02202     connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
02203             this, SLOT(modelAboutToBeDeleted(Model *)));
02204 
02205     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
02206             this, SLOT(modelGenerationFailed(QString, QString)));
02207     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
02208             this, SLOT(modelRegenerationWarning(QString, QString, QString)));
02209     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
02210             this, SLOT(modelGenerationFailed(QString, QString)));
02211     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
02212             this, SLOT(modelRegenerationWarning(QString, QString, QString)));
02213     connect(m_document, SIGNAL(alignmentFailed(QString, QString)),
02214             this, SLOT(alignmentFailed(QString, QString)));
02215 
02216     emit replacedDocument();
02217 }
02218 
02219 bool
02220 MainWindowBase::saveSessionFile(QString path)
02221 {
02222     try {
02223 
02224         TempWriteFile temp(path);
02225 
02226         BZipFileDevice bzFile(temp.getTemporaryFilename());
02227         if (!bzFile.open(QIODevice::WriteOnly)) {
02228             cerr << "Failed to open session file \""
02229                       << temp.getTemporaryFilename()
02230                       << "\" for writing: "
02231                       << bzFile.errorString() << endl;
02232             return false;
02233         }
02234 
02235         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
02236 
02237         QTextStream out(&bzFile);
02238         toXml(out, false);
02239         out.flush();
02240 
02241         QApplication::restoreOverrideCursor();
02242 
02243         if (!bzFile.isOK()) {
02244             QMessageBox::critical(this, tr("Failed to write file"),
02245                                   tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
02246                                   .arg(path).arg(bzFile.errorString()));
02247             bzFile.close();
02248             return false;
02249         }
02250 
02251         bzFile.close();
02252         temp.moveToTarget();
02253         return true;
02254 
02255     } catch (FileOperationFailed &f) {
02256         
02257         QMessageBox::critical(this, tr("Failed to write file"),
02258                               tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
02259                               .arg(path).arg(f.what()));
02260         return false;
02261     }
02262 }
02263 
02264 bool
02265 MainWindowBase::saveSessionTemplate(QString path)
02266 {
02267     try {
02268 
02269         TempWriteFile temp(path);
02270 
02271         QFile file(temp.getTemporaryFilename());
02272         if (!file.open(QIODevice::WriteOnly)) {
02273             cerr << "Failed to open session template file \""
02274                       << temp.getTemporaryFilename()
02275                       << "\" for writing: "
02276                       << file.errorString() << endl;
02277             return false;
02278         }
02279         
02280         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
02281 
02282         QTextStream out(&file);
02283         toXml(out, true);
02284         out.flush();
02285 
02286         QApplication::restoreOverrideCursor();
02287 
02288         file.close();
02289         temp.moveToTarget();
02290         return true;
02291 
02292     } catch (FileOperationFailed &f) {
02293         
02294         QMessageBox::critical(this, tr("Failed to write file"),
02295                               tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
02296                               .arg(path).arg(f.what()));
02297         return false;
02298     }
02299 }
02300 
02301 void
02302 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
02303 {
02304     QString indent("  ");
02305 
02306     out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
02307     out << "<!DOCTYPE sonic-visualiser>\n";
02308     out << "<sv>\n";
02309 
02310     if (asTemplate) {
02311         m_document->toXmlAsTemplate(out, "", "");
02312     } else {
02313         m_document->toXml(out, "", "");
02314     }
02315 
02316     out << "<display>\n";
02317 
02318     out << QString("  <window width=\"%1\" height=\"%2\"/>\n")
02319         .arg(width()).arg(height());
02320 
02321     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
02322 
02323         Pane *pane = m_paneStack->getPane(i);
02324 
02325         if (pane) {
02326             pane->toXml(out, indent);
02327         }
02328     }
02329 
02330     out << "</display>\n";
02331 
02332     m_viewManager->getSelection().toXml(out);
02333 
02334     out << "</sv>\n";
02335 }
02336 
02337 Pane *
02338 MainWindowBase::addPaneToStack()
02339 {
02340     cerr << "MainWindowBase::addPaneToStack()" << endl;
02341     AddPaneCommand *command = new AddPaneCommand(this);
02342     CommandHistory::getInstance()->addCommand(command);
02343     Pane *pane = command->getPane();
02344     return pane;
02345 }
02346 
02347 void
02348 MainWindowBase::zoomIn()
02349 {
02350     Pane *currentPane = m_paneStack->getCurrentPane();
02351     if (currentPane) currentPane->zoom(true);
02352 }
02353 
02354 void
02355 MainWindowBase::zoomOut()
02356 {
02357     Pane *currentPane = m_paneStack->getCurrentPane();
02358     if (currentPane) currentPane->zoom(false);
02359 }
02360 
02361 void
02362 MainWindowBase::zoomToFit()
02363 {
02364     Pane *currentPane = m_paneStack->getCurrentPane();
02365     if (!currentPane) return;
02366 
02367     Model *model = getMainModel();
02368     if (!model) return;
02369     
02370     int start = model->getStartFrame();
02371     int end = model->getEndFrame();
02372     if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
02373     int pixels = currentPane->width();
02374 
02375     int sw = currentPane->getVerticalScaleWidth();
02376     if (pixels > sw * 2) pixels -= sw * 2;
02377     else pixels = 1;
02378     if (pixels > 4) pixels -= 4;
02379 
02380     int zoomLevel = (end - start) / pixels;
02381     if (zoomLevel < 1) zoomLevel = 1;
02382 
02383     currentPane->setZoomLevel(zoomLevel);
02384     currentPane->setCentreFrame((start + end) / 2);
02385 }
02386 
02387 void
02388 MainWindowBase::zoomDefault()
02389 {
02390     Pane *currentPane = m_paneStack->getCurrentPane();
02391     QSettings settings;
02392     settings.beginGroup("MainWindow");
02393     int zoom = settings.value("zoom-default", 1024).toInt();
02394     settings.endGroup();
02395     if (currentPane) currentPane->setZoomLevel(zoom);
02396 }
02397 
02398 void
02399 MainWindowBase::scrollLeft()
02400 {
02401     Pane *currentPane = m_paneStack->getCurrentPane();
02402     if (currentPane) currentPane->scroll(false, false);
02403 }
02404 
02405 void
02406 MainWindowBase::jumpLeft()
02407 {
02408     Pane *currentPane = m_paneStack->getCurrentPane();
02409     if (currentPane) currentPane->scroll(false, true);
02410 }
02411 
02412 void
02413 MainWindowBase::peekLeft()
02414 {
02415     Pane *currentPane = m_paneStack->getCurrentPane();
02416     if (currentPane) currentPane->scroll(false, false, false);
02417 }
02418 
02419 void
02420 MainWindowBase::scrollRight()
02421 {
02422     Pane *currentPane = m_paneStack->getCurrentPane();
02423     if (currentPane) currentPane->scroll(true, false);
02424 }
02425 
02426 void
02427 MainWindowBase::jumpRight()
02428 {
02429     Pane *currentPane = m_paneStack->getCurrentPane();
02430     if (currentPane) currentPane->scroll(true, true);
02431 }
02432 
02433 void
02434 MainWindowBase::peekRight()
02435 {
02436     Pane *currentPane = m_paneStack->getCurrentPane();
02437     if (currentPane) currentPane->scroll(true, false, false);
02438 }
02439 
02440 void
02441 MainWindowBase::showNoOverlays()
02442 {
02443     m_viewManager->setOverlayMode(ViewManager::NoOverlays);
02444 }
02445 
02446 void
02447 MainWindowBase::showMinimalOverlays()
02448 {
02449     m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
02450 }
02451 
02452 void
02453 MainWindowBase::showAllOverlays()
02454 {
02455     m_viewManager->setOverlayMode(ViewManager::AllOverlays);
02456 }
02457 
02458 void
02459 MainWindowBase::toggleTimeRulers()
02460 {
02461     bool haveRulers = false;
02462     bool someHidden = false;
02463 
02464     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
02465 
02466         Pane *pane = m_paneStack->getPane(i);
02467         if (!pane) continue;
02468 
02469         for (int j = 0; j < pane->getLayerCount(); ++j) {
02470 
02471             Layer *layer = pane->getLayer(j);
02472             if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
02473 
02474             haveRulers = true;
02475             if (layer->isLayerDormant(pane)) someHidden = true;
02476         }
02477     }
02478 
02479     if (haveRulers) {
02480 
02481         bool show = someHidden;
02482 
02483         for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
02484 
02485             Pane *pane = m_paneStack->getPane(i);
02486             if (!pane) continue;
02487 
02488             for (int j = 0; j < pane->getLayerCount(); ++j) {
02489 
02490                 Layer *layer = pane->getLayer(j);
02491                 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
02492 
02493                 layer->showLayer(pane, show);
02494             }
02495         }
02496     }
02497 }
02498 
02499 void
02500 MainWindowBase::toggleZoomWheels()
02501 {
02502     if (m_viewManager->getZoomWheelsEnabled()) {
02503         m_viewManager->setZoomWheelsEnabled(false);
02504     } else {
02505         m_viewManager->setZoomWheelsEnabled(true);
02506     }
02507 }
02508 
02509 void
02510 MainWindowBase::togglePropertyBoxes()
02511 {
02512     if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
02513         if (Preferences::getInstance()->getPropertyBoxLayout() ==
02514             Preferences::VerticallyStacked) {
02515             m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
02516         } else {
02517             m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
02518         }
02519     } else {
02520         m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
02521     }
02522 }
02523 
02524 QLabel *
02525 MainWindowBase::getStatusLabel() const
02526 {
02527     if (!m_statusLabel) {
02528         m_statusLabel = new QLabel();
02529         statusBar()->addWidget(m_statusLabel, 1);
02530     }
02531 
02532     QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
02533     foreach (QFrame *f, frames) {
02534         f->setFrameStyle(QFrame::NoFrame);
02535     }
02536 
02537     return m_statusLabel;
02538 }
02539 
02540 void
02541 MainWindowBase::toggleStatusBar()
02542 {
02543     QSettings settings;
02544     settings.beginGroup("MainWindow");
02545     bool sb = settings.value("showstatusbar", true).toBool();
02546 
02547     if (sb) {
02548         statusBar()->hide();
02549     } else {
02550         statusBar()->show();
02551     }
02552 
02553     settings.setValue("showstatusbar", !sb);
02554 
02555     settings.endGroup();
02556 }
02557 
02558 void
02559 MainWindowBase::toggleCentreLine()
02560 {
02561     if (m_viewManager->shouldShowCentreLine()) {
02562         m_viewManager->setShowCentreLine(false);
02563     } else {
02564         m_viewManager->setShowCentreLine(true);
02565     }
02566 }
02567 
02568 void
02569 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
02570 {
02571     if (name == "Property Box Layout") {
02572         if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
02573             if (Preferences::getInstance()->getPropertyBoxLayout() ==
02574                 Preferences::VerticallyStacked) {
02575                 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
02576             } else {
02577                 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
02578             }
02579         }
02580     } else if (name == "Background Mode" && m_viewManager) {
02581         Preferences::BackgroundMode mode =
02582             Preferences::getInstance()->getBackgroundMode();
02583         if (mode == Preferences::BackgroundFromTheme) {
02584             m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
02585         } else if (mode == Preferences::DarkBackground) {
02586             m_viewManager->setGlobalDarkBackground(true);
02587         } else {
02588             m_viewManager->setGlobalDarkBackground(false);
02589         }
02590     }            
02591 }
02592 
02593 void
02594 MainWindowBase::play()
02595 {
02596     if (m_playSource->isPlaying()) {
02597         stop();
02598     } else {
02599         playbackFrameChanged(m_viewManager->getPlaybackFrame());
02600         m_playSource->play(m_viewManager->getPlaybackFrame());
02601     }
02602 }
02603 
02604 void
02605 MainWindowBase::ffwd()
02606 {
02607     if (!getMainModel()) return;
02608 
02609     int frame = m_viewManager->getPlaybackFrame();
02610     ++frame;
02611 
02612     Pane *pane = m_paneStack->getCurrentPane();
02613     Layer *layer = getSnapLayer();
02614     int sr = getMainModel()->getSampleRate();
02615 
02616     if (!layer) {
02617 
02618         frame = RealTime::realTime2Frame
02619             (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
02620         if (frame > int(getMainModel()->getEndFrame())) {
02621             frame = getMainModel()->getEndFrame();
02622         }
02623 
02624     } else {
02625 
02626         int resolution = 0;
02627         if (pane) frame = pane->alignFromReference(frame);
02628         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
02629                                       frame, resolution, Layer::SnapRight)) {
02630             if (pane) frame = pane->alignToReference(frame);
02631         } else {
02632             frame = getMainModel()->getEndFrame();
02633         }
02634     }
02635         
02636     if (frame < 0) frame = 0;
02637 
02638     if (m_viewManager->getPlaySelectionMode()) {
02639         frame = m_viewManager->constrainFrameToSelection(int(frame));
02640     }
02641     
02642     m_viewManager->setPlaybackFrame(frame);
02643 
02644     if (frame == (int)getMainModel()->getEndFrame() &&
02645         m_playSource &&
02646         m_playSource->isPlaying() &&
02647         !m_viewManager->getPlayLoopMode()) {
02648         stop();
02649     }
02650 }
02651 
02652 void
02653 MainWindowBase::ffwdEnd()
02654 {
02655     if (!getMainModel()) return;
02656 
02657     if (m_playSource &&
02658         m_playSource->isPlaying() &&
02659         !m_viewManager->getPlayLoopMode()) {
02660         stop();
02661     }
02662 
02663     int frame = getMainModel()->getEndFrame();
02664 
02665     if (m_viewManager->getPlaySelectionMode()) {
02666         frame = m_viewManager->constrainFrameToSelection(frame);
02667     }
02668 
02669     m_viewManager->setPlaybackFrame(frame);
02670 }
02671 
02672 void
02673 MainWindowBase::ffwdSimilar()
02674 {
02675     if (!getMainModel()) return;
02676 
02677     Layer *layer = getSnapLayer();
02678     if (!layer) { ffwd(); return; }
02679 
02680     Pane *pane = m_paneStack->getCurrentPane();
02681     int frame = m_viewManager->getPlaybackFrame();
02682 
02683     int resolution = 0;
02684     if (pane) frame = pane->alignFromReference(frame);
02685     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
02686                                     frame, resolution, Layer::SnapRight)) {
02687         if (pane) frame = pane->alignToReference(frame);
02688     } else {
02689         frame = getMainModel()->getEndFrame();
02690     }
02691         
02692     if (frame < 0) frame = 0;
02693 
02694     if (m_viewManager->getPlaySelectionMode()) {
02695         frame = m_viewManager->constrainFrameToSelection(int(frame));
02696     }
02697     
02698     m_viewManager->setPlaybackFrame(frame);
02699 
02700     if (frame == (int)getMainModel()->getEndFrame() &&
02701         m_playSource &&
02702         m_playSource->isPlaying() &&
02703         !m_viewManager->getPlayLoopMode()) {
02704         stop();
02705     }
02706 }
02707 
02708 void
02709 MainWindowBase::rewind()
02710 {
02711     if (!getMainModel()) return;
02712 
02713     int frame = m_viewManager->getPlaybackFrame();
02714     if (frame > 0) --frame;
02715 
02716     Pane *pane = m_paneStack->getCurrentPane();
02717     Layer *layer = getSnapLayer();
02718     int sr = getMainModel()->getSampleRate();
02719     
02720     // when rewinding during playback, we want to allow a period
02721     // following a rewind target point at which the rewind will go to
02722     // the prior point instead of the immediately neighbouring one
02723     if (m_playSource && m_playSource->isPlaying()) {
02724         RealTime ct = RealTime::frame2RealTime(frame, sr);
02725         ct = ct - RealTime::fromSeconds(0.15);
02726         if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
02727         frame = RealTime::realTime2Frame(ct, sr);
02728     }
02729 
02730     if (!layer) {
02731         
02732         frame = RealTime::realTime2Frame
02733             (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
02734         if (frame < int(getMainModel()->getStartFrame())) {
02735             frame = getMainModel()->getStartFrame();
02736         }
02737 
02738     } else {
02739 
02740         int resolution = 0;
02741         if (pane) frame = pane->alignFromReference(frame);
02742         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
02743                                       frame, resolution, Layer::SnapLeft)) {
02744             if (pane) frame = pane->alignToReference(frame);
02745         } else {
02746             frame = getMainModel()->getStartFrame();
02747         }
02748     }
02749 
02750     if (frame < 0) frame = 0;
02751 
02752     if (m_viewManager->getPlaySelectionMode()) {
02753         frame = m_viewManager->constrainFrameToSelection(int(frame));
02754     }
02755 
02756     m_viewManager->setPlaybackFrame(frame);
02757 }
02758 
02759 void
02760 MainWindowBase::rewindStart()
02761 {
02762     if (!getMainModel()) return;
02763 
02764     int frame = getMainModel()->getStartFrame();
02765 
02766     if (m_viewManager->getPlaySelectionMode()) {
02767         frame = m_viewManager->constrainFrameToSelection(frame);
02768     }
02769 
02770     m_viewManager->setPlaybackFrame(frame);
02771 }
02772 
02773 void
02774 MainWindowBase::rewindSimilar()
02775 {
02776     if (!getMainModel()) return;
02777 
02778     Layer *layer = getSnapLayer();
02779     if (!layer) { rewind(); return; }
02780 
02781     Pane *pane = m_paneStack->getCurrentPane();
02782     int frame = m_viewManager->getPlaybackFrame();
02783 
02784     int resolution = 0;
02785     if (pane) frame = pane->alignFromReference(frame);
02786     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
02787                                     frame, resolution, Layer::SnapLeft)) {
02788         if (pane) frame = pane->alignToReference(frame);
02789     } else {
02790         frame = getMainModel()->getStartFrame();
02791     }
02792         
02793     if (frame < 0) frame = 0;
02794 
02795     if (m_viewManager->getPlaySelectionMode()) {
02796         frame = m_viewManager->constrainFrameToSelection(int(frame));
02797     }
02798     
02799     m_viewManager->setPlaybackFrame(frame);
02800 }
02801 
02802 Layer *
02803 MainWindowBase::getSnapLayer() const
02804 {
02805     Pane *pane = m_paneStack->getCurrentPane();
02806     if (!pane) return 0;
02807 
02808     Layer *layer = pane->getSelectedLayer();
02809 
02810     if (!dynamic_cast<TimeInstantLayer *>(layer) &&
02811         !dynamic_cast<TimeValueLayer *>(layer) &&
02812         !dynamic_cast<RegionLayer *>(layer) &&
02813         !dynamic_cast<TimeRulerLayer *>(layer)) {
02814 
02815         layer = 0;
02816 
02817         for (int i = pane->getLayerCount(); i > 0; --i) {
02818             Layer *l = pane->getLayer(i-1);
02819             if (dynamic_cast<TimeRulerLayer *>(l)) {
02820                 layer = l;
02821                 break;
02822             }
02823         }
02824     }
02825 
02826     return layer;
02827 }
02828 
02829 void
02830 MainWindowBase::stop()
02831 {
02832     m_playSource->stop();
02833 
02834     if (m_paneStack && m_paneStack->getCurrentPane()) {
02835         updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
02836     } else {
02837         m_myStatusMessage = "";
02838         getStatusLabel()->setText("");
02839     }
02840 }
02841 
02842 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
02843     m_mw(mw),
02844     m_pane(0),
02845     m_prevCurrentPane(0),
02846     m_added(false)
02847 {
02848 }
02849 
02850 MainWindowBase::AddPaneCommand::~AddPaneCommand()
02851 {
02852     if (m_pane && !m_added) {
02853         m_mw->m_paneStack->deletePane(m_pane);
02854     }
02855 }
02856 
02857 QString
02858 MainWindowBase::AddPaneCommand::getName() const
02859 {
02860     return tr("Add Pane");
02861 }
02862 
02863 void
02864 MainWindowBase::AddPaneCommand::execute()
02865 {
02866     if (!m_pane) {
02867         m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
02868         m_pane = m_mw->m_paneStack->addPane();
02869 
02870         connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
02871                 m_mw, SLOT(contextHelpChanged(const QString &)));
02872     } else {
02873         m_mw->m_paneStack->showPane(m_pane);
02874     }
02875 
02876     m_mw->m_paneStack->setCurrentPane(m_pane);
02877     m_added = true;
02878 }
02879 
02880 void
02881 MainWindowBase::AddPaneCommand::unexecute()
02882 {
02883     m_mw->m_paneStack->hidePane(m_pane);
02884     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
02885     m_added = false;
02886 }
02887 
02888 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
02889     m_mw(mw),
02890     m_pane(pane),
02891     m_prevCurrentPane(0),
02892     m_added(true)
02893 {
02894 }
02895 
02896 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
02897 {
02898     if (m_pane && !m_added) {
02899         m_mw->m_paneStack->deletePane(m_pane);
02900     }
02901 }
02902 
02903 QString
02904 MainWindowBase::RemovePaneCommand::getName() const
02905 {
02906     return tr("Remove Pane");
02907 }
02908 
02909 void
02910 MainWindowBase::RemovePaneCommand::execute()
02911 {
02912     m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
02913     m_mw->m_paneStack->hidePane(m_pane);
02914     m_added = false;
02915 }
02916 
02917 void
02918 MainWindowBase::RemovePaneCommand::unexecute()
02919 {
02920     m_mw->m_paneStack->showPane(m_pane);
02921     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
02922     m_added = true;
02923 }
02924 
02925 void
02926 MainWindowBase::deleteCurrentPane()
02927 {
02928     CommandHistory::getInstance()->startCompoundOperation
02929         (tr("Delete Pane"), true);
02930 
02931     Pane *pane = m_paneStack->getCurrentPane();
02932     if (pane) {
02933         while (pane->getLayerCount() > 0) {
02934             Layer *layer = pane->getLayer(0);
02935             if (layer) {
02936                 m_document->removeLayerFromView(pane, layer);
02937             } else {
02938                 break;
02939             }
02940         }
02941 
02942         RemovePaneCommand *command = new RemovePaneCommand(this, pane);
02943         CommandHistory::getInstance()->addCommand(command);
02944     }
02945 
02946     CommandHistory::getInstance()->endCompoundOperation();
02947 
02948     updateMenuStates();
02949 }
02950 
02951 void
02952 MainWindowBase::deleteCurrentLayer()
02953 {
02954     Pane *pane = m_paneStack->getCurrentPane();
02955     if (pane) {
02956         Layer *layer = pane->getSelectedLayer();
02957         if (layer) {
02958             m_document->removeLayerFromView(pane, layer);
02959         }
02960     }
02961     updateMenuStates();
02962 }
02963 
02964 void
02965 MainWindowBase::editCurrentLayer()
02966 {
02967     Layer *layer = 0;
02968     Pane *pane = m_paneStack->getCurrentPane();
02969     if (pane) layer = pane->getSelectedLayer();
02970     if (!layer) return;
02971 
02972     Model *model = layer->getModel();
02973     if (!model) return;
02974 
02975     TabularModel *tabular = dynamic_cast<TabularModel *>(model);
02976     if (!tabular) {
02978         //appropriate model type?  or will we ultimately support
02979         //tabular display for all editable models?
02980         SVDEBUG << "NOTE: Not a tabular model" << endl;
02981         return;
02982     }
02983 
02984     if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
02985         if (!m_layerDataDialogMap[layer].isNull()) {
02986             m_layerDataDialogMap[layer]->show();
02987             m_layerDataDialogMap[layer]->raise();
02988             return;
02989         }
02990     }
02991 
02992     QString title = layer->getLayerPresentationName();
02993 
02994     ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
02995     dialog->setAttribute(Qt::WA_DeleteOnClose);
02996     
02997     connectLayerEditDialog(dialog);
02998 
02999     m_layerDataDialogMap[layer] = dialog;
03000     m_viewDataDialogMap[pane].insert(dialog);
03001 
03002     dialog->show();
03003 }
03004 
03005 void
03006 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
03007 {
03008     connect(m_viewManager,
03009             SIGNAL(globalCentreFrameChanged(int)),
03010             dialog,
03011             SLOT(userScrolledToFrame(int)));
03012 
03013     connect(m_viewManager,
03014             SIGNAL(playbackFrameChanged(int)),
03015             dialog,
03016             SLOT(playbackScrolledToFrame(int)));
03017 
03018     connect(dialog,
03019             SIGNAL(scrollToFrame(int)),
03020             m_viewManager,
03021             SLOT(setGlobalCentreFrame(int)));
03022 
03023     connect(dialog,
03024             SIGNAL(scrollToFrame(int)),
03025             m_viewManager,
03026             SLOT(setPlaybackFrame(int)));
03027 }    
03028 
03029 void
03030 MainWindowBase::previousPane()
03031 {
03032     if (!m_paneStack) return;
03033 
03034     Pane *currentPane = m_paneStack->getCurrentPane();
03035     if (!currentPane) return;
03036 
03037     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
03038         if (m_paneStack->getPane(i) == currentPane) {
03039             if (i == 0) return;
03040             m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
03041             updateMenuStates();
03042             return;
03043         }
03044     }
03045 }
03046 
03047 void
03048 MainWindowBase::nextPane()
03049 {
03050     if (!m_paneStack) return;
03051 
03052     Pane *currentPane = m_paneStack->getCurrentPane();
03053     if (!currentPane) return;
03054 
03055     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
03056         if (m_paneStack->getPane(i) == currentPane) {
03057             if (i == m_paneStack->getPaneCount()-1) return;
03058             m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
03059             updateMenuStates();
03060             return;
03061         }
03062     }
03063 }
03064 
03065 void
03066 MainWindowBase::previousLayer()
03067 {
03068     if (!m_paneStack) return;
03069 
03070     Pane *currentPane = m_paneStack->getCurrentPane();
03071     if (!currentPane) return;
03072 
03073     int count = currentPane->getLayerCount();
03074     if (count == 0) return;
03075 
03076     Layer *currentLayer = currentPane->getSelectedLayer();
03077 
03078     if (!currentLayer) {
03079         // The pane itself is current
03080         m_paneStack->setCurrentLayer
03081             (currentPane, currentPane->getFixedOrderLayer(count-1));
03082     } else {
03083         for (int i = 0; i < count; ++i) {
03084             if (currentPane->getFixedOrderLayer(i) == currentLayer) {
03085                 if (i == 0) {
03086                     m_paneStack->setCurrentLayer
03087                         (currentPane, 0); // pane
03088                 } else {
03089                     m_paneStack->setCurrentLayer
03090                         (currentPane, currentPane->getFixedOrderLayer(i-1));
03091                 }
03092                 break;
03093             }
03094         }
03095     }
03096 
03097     updateMenuStates();
03098 }
03099 
03100 void
03101 MainWindowBase::nextLayer()
03102 {
03103     if (!m_paneStack) return;
03104 
03105     Pane *currentPane = m_paneStack->getCurrentPane();
03106     if (!currentPane) return;
03107 
03108     int count = currentPane->getLayerCount();
03109     if (count == 0) return;
03110 
03111     Layer *currentLayer = currentPane->getSelectedLayer();
03112 
03113     if (!currentLayer) {
03114         // The pane itself is current
03115         m_paneStack->setCurrentLayer
03116             (currentPane, currentPane->getFixedOrderLayer(0));
03117     } else {
03118         for (int i = 0; i < count; ++i) {
03119             if (currentPane->getFixedOrderLayer(i) == currentLayer) {
03120                 if (i == currentPane->getLayerCount()-1) {
03121                     m_paneStack->setCurrentLayer
03122                         (currentPane, 0); // pane
03123                 } else {
03124                     m_paneStack->setCurrentLayer
03125                         (currentPane, currentPane->getFixedOrderLayer(i+1));
03126                 }
03127                 break;
03128             }
03129         }
03130     }
03131 
03132     updateMenuStates();
03133 }
03134 
03135 void
03136 MainWindowBase::playbackFrameChanged(int frame)
03137 {
03138     if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
03139 
03140     updatePositionStatusDisplays();
03141 
03142     RealTime now = RealTime::frame2RealTime
03143         (frame, getMainModel()->getSampleRate());
03144 
03145     if (now.sec == m_lastPlayStatusSec) return;
03146 
03147     RealTime then = RealTime::frame2RealTime
03148         (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
03149 
03150     QString nowStr;
03151     QString thenStr;
03152     QString remainingStr;
03153 
03154     if (then.sec > 10) {
03155         nowStr = now.toSecText().c_str();
03156         thenStr = then.toSecText().c_str();
03157         remainingStr = (then - now).toSecText().c_str();
03158         m_lastPlayStatusSec = now.sec;
03159     } else {
03160         nowStr = now.toText(true).c_str();
03161         thenStr = then.toText(true).c_str();
03162         remainingStr = (then - now).toText(true).c_str();
03163     }        
03164 
03165     m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
03166         .arg(nowStr).arg(thenStr).arg(remainingStr);
03167 
03168     getStatusLabel()->setText(m_myStatusMessage);
03169 }
03170 
03171 void
03172 MainWindowBase::globalCentreFrameChanged(int )
03173 {
03174     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
03175     Pane *p = 0;
03176     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
03177     if (!p->getFollowGlobalPan()) return;
03178     updateVisibleRangeDisplay(p);
03179 }
03180 
03181 void
03182 MainWindowBase::viewCentreFrameChanged(View *v, int frame)
03183 {
03184 //    SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
03185 
03186     if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
03187         for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
03188              i != m_viewDataDialogMap[v].end(); ++i) {
03189             (*i)->userScrolledToFrame(frame);
03190         }
03191     }
03192     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
03193     Pane *p = 0;
03194     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
03195     if (v == p) updateVisibleRangeDisplay(p);
03196 }
03197 
03198 void
03199 MainWindowBase::viewZoomLevelChanged(View *v, int , bool )
03200 {
03201     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
03202     Pane *p = 0;
03203     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
03204     if (v == p) updateVisibleRangeDisplay(p);
03205 }
03206 
03207 void
03208 MainWindowBase::layerAdded(Layer *)
03209 {
03210 //    SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
03211     updateMenuStates();
03212 }
03213 
03214 void
03215 MainWindowBase::layerRemoved(Layer *)
03216 {
03217 //    SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
03218     updateMenuStates();
03219 }
03220 
03221 void
03222 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
03223 {
03224 //    SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
03225 
03226     removeLayerEditDialog(layer);
03227 
03228     if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
03229 //      cerr << "(this is the time ruler layer)" << endl;
03230         m_timeRulerLayer = 0;
03231     }
03232 }
03233 
03234 void
03235 MainWindowBase::layerInAView(Layer *layer, bool inAView)
03236 {
03237 //    SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
03238 
03239     if (!inAView) removeLayerEditDialog(layer);
03240 
03241     // Check whether we need to add or remove model from play source
03242     Model *model = layer->getModel();
03243     if (model) {
03244         if (inAView) {
03245             m_playSource->addModel(model);
03246         } else {
03247             bool found = false;
03248             for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
03249                 Pane *pane = m_paneStack->getPane(i);
03250                 if (!pane) continue;
03251                 for (int j = 0; j < pane->getLayerCount(); ++j) {
03252                     Layer *pl = pane->getLayer(j);
03253                     if (pl &&
03254                         !dynamic_cast<TimeRulerLayer *>(pl) &&
03255                         (pl->getModel() == model)) {
03256                         found = true;
03257                         break;
03258                     }
03259                 }
03260                 if (found) break;
03261             }
03262             if (!found) {
03263                 m_playSource->removeModel(model);
03264             }
03265         }
03266     }
03267 
03268     updateMenuStates();
03269 }
03270 
03271 void
03272 MainWindowBase::removeLayerEditDialog(Layer *layer)
03273 {
03274     if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
03275 
03276         ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
03277 
03278         for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
03279              vi != m_viewDataDialogMap.end(); ++vi) {
03280             vi->second.erase(dialog);
03281         }
03282 
03283         m_layerDataDialogMap.erase(layer);
03284         delete dialog;
03285     }
03286 }
03287 
03288 void
03289 MainWindowBase::modelAdded(Model *model)
03290 {
03291 //    SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
03292         std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
03293     m_playSource->addModel(model);
03294 }
03295 
03296 void
03297 MainWindowBase::mainModelChanged(WaveFileModel *model)
03298 {
03299 //    SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
03300     updateDescriptionLabel();
03301     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
03302     if (model && !m_playTarget && m_audioOutput) {
03303         createPlayTarget();
03304     }
03305 }
03306 
03307 void
03308 MainWindowBase::modelAboutToBeDeleted(Model *model)
03309 {
03310 //    SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
03311     if (model == m_viewManager->getPlaybackModel()) {
03312         m_viewManager->setPlaybackModel(0);
03313     }
03314     m_playSource->removeModel(model);
03315     FFTDataServer::modelAboutToBeDeleted(model);
03316 }
03317 
03318 void
03319 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
03320 {
03321     bool found = false;
03322     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
03323         if (m_paneStack->getPane(i) == pane) {
03324             found = true;
03325             break;
03326         }
03327     }
03328     if (!found) {
03329         SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
03330                   << pane << endl;
03331         return;
03332     }
03333 
03334     CommandHistory::getInstance()->startCompoundOperation
03335         (tr("Delete Pane"), true);
03336 
03337     while (pane->getLayerCount() > 0) {
03338         Layer *layer = pane->getLayer(0);
03339         if (layer) {
03340             m_document->removeLayerFromView(pane, layer);
03341         } else {
03342             break;
03343         }
03344     }
03345 
03346     RemovePaneCommand *command = new RemovePaneCommand(this, pane);
03347     CommandHistory::getInstance()->addCommand(command);
03348 
03349     CommandHistory::getInstance()->endCompoundOperation();
03350 
03351     updateMenuStates();
03352 }
03353 
03354 void
03355 MainWindowBase::pollOSC()
03356 {
03357     if (!m_oscQueue || m_oscQueue->isEmpty()) return;
03358     SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
03359 
03360     if (m_openingAudioFile) return;
03361 
03362     OSCMessage message = m_oscQueue->readMessage();
03363 
03364     if (message.getTarget() != 0) {
03365         return; 
03366     }
03367 
03368     handleOSCMessage(message);
03369 }
03370 
03371 void
03372 MainWindowBase::inProgressSelectionChanged()
03373 {
03374     Pane *currentPane = 0;
03375     if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
03376     if (currentPane) {
03377         //cerr << "JTEST: mouse event on selection pane" << endl;
03378         updateVisibleRangeDisplay(currentPane);
03379     }
03380 }
03381 
03382 void
03383 MainWindowBase::contextHelpChanged(const QString &s)
03384 {
03385     QLabel *lab = getStatusLabel();
03386 
03387     if (s == "" && m_myStatusMessage != "") {
03388         if (lab->text() != m_myStatusMessage) {
03389             lab->setText(m_myStatusMessage);
03390         }
03391         return;
03392     }
03393 
03394     lab->setText(s);
03395 }
03396 
03397 void
03398 MainWindowBase::openHelpUrl(QString url)
03399 {
03400     // This method mostly lifted from Qt Assistant source code
03401 
03402     QProcess *process = new QProcess(this);
03403     connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
03404 
03405     QStringList args;
03406 
03407 #ifdef Q_OS_MAC
03408     args.append(url);
03409     process->start("open", args);
03410 #else
03411 #ifdef Q_OS_WIN32
03412     QString pf(getenv("ProgramFiles"));
03413     QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE");
03414 
03415     args.append(url);
03416     process->start(command, args);
03417 #else
03418     if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
03419         args.append("exec");
03420         args.append(url);
03421         process->start("kfmclient", args);
03422     } else if (!qgetenv("BROWSER").isEmpty()) {
03423         args.append(url);
03424         process->start(qgetenv("BROWSER"), args);
03425     } else {
03426         args.append(url);
03427         process->start("firefox", args);
03428     }
03429 #endif
03430 #endif
03431 }
03432 
03433