| Chris@45 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@45 | 2 | 
| Chris@45 | 3 /* | 
| Chris@45 | 4     Sonic Visualiser | 
| Chris@45 | 5     An audio file viewer and annotation editor. | 
| Chris@45 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@45 | 7     This file copyright 2006-2007 Chris Cannam and QMUL. | 
| Chris@45 | 8 | 
| Chris@45 | 9     This program is free software; you can redistribute it and/or | 
| Chris@45 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@45 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@45 | 12     License, or (at your option) any later version.  See the file | 
| Chris@45 | 13     COPYING included with this distribution for more information. | 
| Chris@45 | 14 */ | 
| Chris@45 | 15 | 
| Chris@45 | 16 #include "MainWindowBase.h" | 
| Chris@46 | 17 #include "Document.h" | 
| Chris@45 | 18 | 
| Chris@45 | 19 #include "view/Pane.h" | 
| Chris@45 | 20 #include "view/PaneStack.h" | 
| Chris@45 | 21 #include "data/model/WaveFileModel.h" | 
| Chris@477 | 22 #include "data/model/WritableWaveFileModel.h" | 
| Chris@45 | 23 #include "data/model/SparseOneDimensionalModel.h" | 
| Chris@45 | 24 #include "data/model/NoteModel.h" | 
| matthiasm@267 | 25 #include "data/model/FlexiNoteModel.h" | 
| Chris@45 | 26 #include "data/model/Labeller.h" | 
| Chris@124 | 27 #include "data/model/TabularModel.h" | 
| Chris@45 | 28 #include "view/ViewManager.h" | 
| Chris@45 | 29 | 
| Chris@45 | 30 #include "layer/WaveformLayer.h" | 
| Chris@45 | 31 #include "layer/TimeRulerLayer.h" | 
| Chris@45 | 32 #include "layer/TimeInstantLayer.h" | 
| Chris@45 | 33 #include "layer/TimeValueLayer.h" | 
| Chris@45 | 34 #include "layer/Colour3DPlotLayer.h" | 
| Chris@45 | 35 #include "layer/SliceLayer.h" | 
| Chris@45 | 36 #include "layer/SliceableLayer.h" | 
| Chris@45 | 37 #include "layer/ImageLayer.h" | 
| Chris@184 | 38 #include "layer/NoteLayer.h" | 
| matthiasm@267 | 39 #include "layer/FlexiNoteLayer.h" | 
| Chris@184 | 40 #include "layer/RegionLayer.h" | 
| Chris@45 | 41 | 
| Chris@45 | 42 #include "widgets/ListInputDialog.h" | 
| Chris@105 | 43 #include "widgets/CommandHistory.h" | 
| Chris@109 | 44 #include "widgets/ProgressDialog.h" | 
| Chris@109 | 45 #include "widgets/MIDIFileImportDialog.h" | 
| Chris@109 | 46 #include "widgets/CSVFormatDialog.h" | 
| Chris@123 | 47 #include "widgets/ModelDataTableDialog.h" | 
| Chris@341 | 48 #include "widgets/InteractiveFileFinder.h" | 
| Chris@45 | 49 | 
| Chris@468 | 50 #include "audio/AudioCallbackPlaySource.h" | 
| Chris@475 | 51 #include "audio/AudioRecordTarget.h" | 
| Chris@468 | 52 #include "audio/PlaySpeedRangeMapper.h" | 
| Chris@475 | 53 | 
| Chris@45 | 54 #include "data/fileio/DataFileReaderFactory.h" | 
| Chris@45 | 55 #include "data/fileio/PlaylistFileReader.h" | 
| Chris@45 | 56 #include "data/fileio/WavFileWriter.h" | 
| Chris@45 | 57 #include "data/fileio/MIDIFileWriter.h" | 
| Chris@45 | 58 #include "data/fileio/BZipFileDevice.h" | 
| Chris@45 | 59 #include "data/fileio/FileSource.h" | 
| Chris@152 | 60 #include "data/fileio/AudioFileReaderFactory.h" | 
| Chris@134 | 61 #include "rdf/RDFImporter.h" | 
| Chris@45 | 62 | 
| Chris@45 | 63 #include "data/fft/FFTDataServer.h" | 
| Chris@45 | 64 | 
| Chris@45 | 65 #include "base/RecentFiles.h" | 
| Chris@45 | 66 | 
| Chris@45 | 67 #include "base/PlayParameterRepository.h" | 
| Chris@45 | 68 #include "base/XmlExportable.h" | 
| Chris@45 | 69 #include "base/Profiler.h" | 
| Chris@45 | 70 #include "base/Preferences.h" | 
| Chris@217 | 71 #include "base/TempWriteFile.h" | 
| Chris@217 | 72 #include "base/Exceptions.h" | 
| Chris@223 | 73 #include "base/ResourceFinder.h" | 
| Chris@45 | 74 | 
| Chris@45 | 75 #include "data/osc/OSCQueue.h" | 
| Chris@157 | 76 #include "data/midi/MIDIInput.h" | 
| Chris@45 | 77 | 
| Chris@468 | 78 #include <bqaudioio/SystemPlaybackTarget.h> | 
| Chris@475 | 79 #include <bqaudioio/SystemAudioIO.h> | 
| Chris@468 | 80 #include <bqaudioio/AudioFactory.h> | 
| Chris@468 | 81 | 
| Chris@45 | 82 #include <QApplication> | 
| Chris@45 | 83 #include <QMessageBox> | 
| Chris@45 | 84 #include <QGridLayout> | 
| Chris@45 | 85 #include <QLabel> | 
| Chris@45 | 86 #include <QAction> | 
| Chris@45 | 87 #include <QMenuBar> | 
| Chris@45 | 88 #include <QToolBar> | 
| Chris@45 | 89 #include <QInputDialog> | 
| Chris@45 | 90 #include <QStatusBar> | 
| Chris@45 | 91 #include <QTreeView> | 
| Chris@45 | 92 #include <QFile> | 
| Chris@45 | 93 #include <QFileInfo> | 
| Chris@45 | 94 #include <QDir> | 
| Chris@45 | 95 #include <QTextStream> | 
| Chris@432 | 96 #include <QTextCodec> | 
| Chris@45 | 97 #include <QProcess> | 
| Chris@45 | 98 #include <QShortcut> | 
| Chris@45 | 99 #include <QSettings> | 
| Chris@45 | 100 #include <QDateTime> | 
| Chris@45 | 101 #include <QProcess> | 
| Chris@45 | 102 #include <QCheckBox> | 
| Chris@45 | 103 #include <QRegExp> | 
| Chris@45 | 104 #include <QScrollArea> | 
| Chris@168 | 105 #include <QDesktopWidget> | 
| Chris@354 | 106 #include <QSignalMapper> | 
| Chris@45 | 107 | 
| Chris@45 | 108 #include <iostream> | 
| Chris@45 | 109 #include <cstdio> | 
| Chris@45 | 110 #include <errno.h> | 
| Chris@45 | 111 | 
| Chris@45 | 112 using std::vector; | 
| Chris@45 | 113 using std::map; | 
| Chris@45 | 114 using std::set; | 
| Chris@45 | 115 | 
| Chris@255 | 116 #ifdef Q_WS_X11 | 
| Chris@255 | 117 #define Window X11Window | 
| Chris@255 | 118 #include <X11/Xlib.h> | 
| Chris@255 | 119 #include <X11/Xutil.h> | 
| Chris@255 | 120 #include <X11/Xatom.h> | 
| Chris@255 | 121 #include <X11/SM/SMlib.h> | 
| Chris@255 | 122 | 
| Chris@255 | 123 static int handle_x11_error(Display *dpy, XErrorEvent *err) | 
| Chris@255 | 124 { | 
| Chris@255 | 125     char errstr[256]; | 
| Chris@255 | 126     XGetErrorText(dpy, err->error_code, errstr, 256); | 
| Chris@255 | 127     if (err->error_code != BadWindow) { | 
| Chris@293 | 128 	cerr << "Sonic Visualiser: X Error: " | 
| Chris@255 | 129 		  << errstr << " " << int(err->error_code) | 
| Chris@255 | 130 		  << "\nin major opcode:  " | 
| Chris@293 | 131 		  << int(err->request_code) << endl; | 
| Chris@255 | 132     } | 
| Chris@255 | 133     return 0; | 
| Chris@255 | 134 } | 
| Chris@255 | 135 #undef Window | 
| Chris@255 | 136 #endif | 
| Chris@45 | 137 | 
| Chris@475 | 138 MainWindowBase::MainWindowBase(SoundOptions options) : | 
| Chris@45 | 139     m_document(0), | 
| Chris@45 | 140     m_paneStack(0), | 
| Chris@45 | 141     m_viewManager(0), | 
| Chris@45 | 142     m_timeRulerLayer(0), | 
| Chris@475 | 143     m_soundOptions(options), | 
| Chris@45 | 144     m_playSource(0), | 
| Chris@475 | 145     m_recordTarget(0), | 
| Chris@45 | 146     m_playTarget(0), | 
| Chris@475 | 147     m_audioIO(0), | 
| Chris@113 | 148     m_oscQueue(0), | 
| Chris@113 | 149     m_oscQueueStarter(0), | 
| Chris@157 | 150     m_midiInput(0), | 
| Chris@45 | 151     m_recentFiles("RecentFiles", 20), | 
| Chris@54 | 152     m_recentTransforms("RecentTransforms", 20), | 
| Chris@45 | 153     m_documentModified(false), | 
| Chris@45 | 154     m_openingAudioFile(false), | 
| Chris@45 | 155     m_abandoning(false), | 
| Chris@121 | 156     m_labeller(0), | 
| Chris@357 | 157     m_lastPlayStatusSec(0), | 
| Chris@357 | 158     m_initialDarkBackground(false), | 
| Chris@378 | 159     m_defaultFfwdRwdStep(2, 0), | 
| Chris@390 | 160     m_statusLabel(0), | 
| Chris@390 | 161     m_menuShortcutMapper(0) | 
| Chris@45 | 162 { | 
| Chris@113 | 163     Profiler profiler("MainWindowBase::MainWindowBase"); | 
| Chris@113 | 164 | 
| Chris@475 | 165     if (options & WithAudioInput) { | 
| Chris@475 | 166         if (!(options & WithAudioOutput)) { | 
| Chris@475 | 167             cerr << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl; | 
| Chris@475 | 168         } | 
| Chris@475 | 169     } | 
| Chris@475 | 170 | 
| Chris@438 | 171     qRegisterMetaType<sv_frame_t>("sv_frame_t"); | 
| Chris@438 | 172     qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t"); | 
| Chris@438 | 173 | 
| Chris@255 | 174 #ifdef Q_WS_X11 | 
| Chris@255 | 175     XSetErrorHandler(handle_x11_error); | 
| Chris@255 | 176 #endif | 
| Chris@255 | 177 | 
| Chris@452 | 178     connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash())); | 
| Chris@452 | 179 | 
| Chris@45 | 180     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), | 
| Chris@45 | 181 	    this, SLOT(documentModified())); | 
| Chris@45 | 182     connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), | 
| Chris@45 | 183 	    this, SLOT(documentRestored())); | 
| Chris@45 | 184 | 
| Chris@45 | 185     m_viewManager = new ViewManager(); | 
| Chris@45 | 186     connect(m_viewManager, SIGNAL(selectionChanged()), | 
| Chris@45 | 187 	    this, SLOT(updateMenuStates())); | 
| Chris@45 | 188     connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), | 
| Chris@45 | 189 	    this, SLOT(inProgressSelectionChanged())); | 
| Chris@45 | 190 | 
| Chris@105 | 191     // set a sensible default font size for views -- cannot do this | 
| Chris@105 | 192     // in Preferences, which is in base and not supposed to use QtGui | 
| Chris@436 | 193     int viewFontSize = int(QApplication::font().pointSize() * 0.9); | 
| Chris@105 | 194     QSettings settings; | 
| Chris@105 | 195     settings.beginGroup("Preferences"); | 
| Chris@105 | 196     viewFontSize = settings.value("view-font-size", viewFontSize).toInt(); | 
| Chris@105 | 197     settings.setValue("view-font-size", viewFontSize); | 
| Chris@105 | 198     settings.endGroup(); | 
| Chris@105 | 199 | 
| Chris@45 | 200     Preferences::BackgroundMode mode = | 
| Chris@45 | 201         Preferences::getInstance()->getBackgroundMode(); | 
| Chris@45 | 202     m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); | 
| Chris@45 | 203     if (mode != Preferences::BackgroundFromTheme) { | 
| Chris@45 | 204         m_viewManager->setGlobalDarkBackground | 
| Chris@45 | 205             (mode == Preferences::DarkBackground); | 
| Chris@45 | 206     } | 
| Chris@45 | 207 | 
| Chris@45 | 208     m_paneStack = new PaneStack(0, m_viewManager); | 
| Chris@45 | 209     connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), | 
| Chris@45 | 210 	    this, SLOT(currentPaneChanged(Pane *))); | 
| Chris@45 | 211     connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), | 
| Chris@45 | 212 	    this, SLOT(currentLayerChanged(Pane *, Layer *))); | 
| Chris@45 | 213     connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), | 
| Chris@45 | 214             this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); | 
| Chris@45 | 215     connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@45 | 216             this, SLOT(contextHelpChanged(const QString &))); | 
| Chris@45 | 217     connect(m_paneStack, SIGNAL(paneAdded(Pane *)), | 
| Chris@45 | 218             this, SLOT(paneAdded(Pane *))); | 
| Chris@45 | 219     connect(m_paneStack, SIGNAL(paneHidden(Pane *)), | 
| Chris@45 | 220             this, SLOT(paneHidden(Pane *))); | 
| Chris@45 | 221     connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)), | 
| Chris@45 | 222             this, SLOT(paneAboutToBeDeleted(Pane *))); | 
| Chris@45 | 223     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)), | 
| Chris@45 | 224             this, SLOT(paneDropAccepted(Pane *, QStringList))); | 
| Chris@45 | 225     connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)), | 
| Chris@45 | 226             this, SLOT(paneDropAccepted(Pane *, QString))); | 
| Chris@55 | 227     connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), | 
| Chris@55 | 228             this, SLOT(paneDeleteButtonClicked(Pane *))); | 
| Chris@45 | 229 | 
| Chris@57 | 230     m_playSource = new AudioCallbackPlaySource(m_viewManager, | 
| Chris@57 | 231                                                QApplication::applicationName()); | 
| Chris@475 | 232     if (m_soundOptions & WithAudioInput) { | 
| Chris@475 | 233         m_recordTarget = new AudioRecordTarget(m_viewManager, | 
| Chris@475 | 234                                                QApplication::applicationName()); | 
| Chris@475 | 235     } | 
| Chris@45 | 236 | 
| Chris@436 | 237     connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)), | 
| Chris@436 | 238 	    this,           SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool))); | 
| Chris@45 | 239     connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), | 
| Chris@45 | 240             this,           SLOT(audioOverloadPluginDisabled())); | 
| Chris@130 | 241     connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()), | 
| Chris@130 | 242             this,           SLOT(audioTimeStretchMultiChannelDisabled())); | 
| Chris@45 | 243 | 
| Chris@45 | 244     connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), | 
| Chris@45 | 245 	    this, SLOT(outputLevelsChanged(float, float))); | 
| Chris@45 | 246 | 
| Chris@435 | 247     connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)), | 
| Chris@435 | 248             this, SLOT(playbackFrameChanged(sv_frame_t))); | 
| Chris@435 | 249 | 
| Chris@435 | 250     connect(m_viewManager, SIGNAL(globalCentreFrameChanged(sv_frame_t)), | 
| Chris@435 | 251             this, SLOT(globalCentreFrameChanged(sv_frame_t))); | 
| Chris@435 | 252 | 
| Chris@435 | 253     connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)), | 
| Chris@435 | 254             this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); | 
| Chris@366 | 255 | 
| Chris@366 | 256     connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, int, bool)), | 
| Chris@366 | 257             this, SLOT(viewZoomLevelChanged(View *, int, bool))); | 
| Chris@45 | 258 | 
| Chris@45 | 259     connect(Preferences::getInstance(), | 
| Chris@45 | 260             SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@45 | 261             this, | 
| Chris@45 | 262             SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@45 | 263 | 
| Chris@45 | 264     Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; | 
| Chris@45 | 265     settings.beginGroup("MainWindow"); | 
| Chris@230 | 266 | 
| Chris@45 | 267     labellerType = (Labeller::ValueType) | 
| Chris@45 | 268         settings.value("labellertype", (int)labellerType).toInt(); | 
| Chris@45 | 269     int cycle = settings.value("labellercycle", 4).toInt(); | 
| Chris@230 | 270 | 
| Chris@45 | 271     settings.endGroup(); | 
| Chris@45 | 272 | 
| Chris@45 | 273     m_labeller = new Labeller(labellerType); | 
| Chris@45 | 274     m_labeller->setCounterCycleSize(cycle); | 
| Chris@113 | 275 | 
| Chris@475 | 276     if (m_soundOptions & WithMIDIInput) { | 
| Chris@161 | 277         m_midiInput = new MIDIInput(QApplication::applicationName(), this); | 
| Chris@161 | 278     } | 
| Chris@452 | 279 | 
| Chris@452 | 280     QTimer::singleShot(1500, this, SIGNAL(hideSplash())); | 
| Chris@45 | 281 } | 
| Chris@45 | 282 | 
| Chris@45 | 283 MainWindowBase::~MainWindowBase() | 
| Chris@45 | 284 { | 
| Chris@233 | 285     SVDEBUG << "MainWindowBase::~MainWindowBase" << endl; | 
| Chris@468 | 286     delete m_playTarget; | 
| Chris@45 | 287     delete m_playSource; | 
| Chris@475 | 288     delete m_audioIO; | 
| Chris@475 | 289     delete m_recordTarget; | 
| Chris@45 | 290     delete m_viewManager; | 
| Chris@45 | 291     delete m_oscQueue; | 
| Chris@304 | 292     delete m_oscQueueStarter; | 
| Chris@157 | 293     delete m_midiInput; | 
| Chris@45 | 294     Profiles::getInstance()->dump(); | 
| Chris@45 | 295 } | 
| Chris@45 | 296 | 
| Chris@113 | 297 void | 
| Chris@452 | 298 MainWindowBase::emitHideSplash() | 
| Chris@452 | 299 { | 
| Chris@452 | 300     emit hideSplash(this); | 
| Chris@452 | 301 } | 
| Chris@452 | 302 | 
| Chris@452 | 303 void | 
| Chris@354 | 304 MainWindowBase::finaliseMenus() | 
| Chris@354 | 305 { | 
| Chris@390 | 306     delete m_menuShortcutMapper; | 
| Chris@390 | 307     m_menuShortcutMapper = 0; | 
| Chris@390 | 308 | 
| Chris@391 | 309     foreach (QShortcut *sc, m_appShortcuts) { | 
| Chris@391 | 310         delete sc; | 
| Chris@391 | 311     } | 
| Chris@391 | 312     m_appShortcuts.clear(); | 
| Chris@391 | 313 | 
| Chris@354 | 314     QMenuBar *mb = menuBar(); | 
| Chris@394 | 315 | 
| Chris@396 | 316     // This used to find all children of QMenu type, and call | 
| Chris@396 | 317     // finaliseMenu on those. But it seems we are getting hold of some | 
| Chris@396 | 318     // menus that way that are not actually active in the menu bar and | 
| Chris@396 | 319     // are not returned in their parent menu's actions() list, and if | 
| Chris@396 | 320     // we finalise those, we end up with duplicate shortcuts in the | 
| Chris@396 | 321     // app shortcut mapper. So we should do this by descending the | 
| Chris@396 | 322     // menu tree through only those menus accessible via actions() | 
| Chris@396 | 323     // from their parents instead. | 
| Chris@396 | 324 | 
| Chris@394 | 325     QList<QMenu *> menus = mb->findChildren<QMenu *> | 
| Chris@394 | 326         (QString(), Qt::FindDirectChildrenOnly); | 
| Chris@394 | 327 | 
| Chris@354 | 328     foreach (QMenu *menu, menus) { | 
| Chris@354 | 329         if (menu) finaliseMenu(menu); | 
| Chris@354 | 330     } | 
| Chris@354 | 331 } | 
| Chris@354 | 332 | 
| Chris@354 | 333 void | 
| Chris@358 | 334 MainWindowBase::finaliseMenu(QMenu * | 
| Chris@358 | 335 #ifdef Q_OS_MAC | 
| Chris@358 | 336                              menu | 
| Chris@358 | 337 #endif | 
| Chris@358 | 338     ) | 
| Chris@354 | 339 { | 
| Chris@354 | 340 #ifdef Q_OS_MAC | 
| Chris@354 | 341     // See https://bugreports.qt-project.org/browse/QTBUG-38256 and | 
| Chris@354 | 342     // our issue #890 http://code.soundsoftware.ac.uk/issues/890 -- | 
| Chris@354 | 343     // single-key shortcuts that are associated only with a menu | 
| Chris@384 | 344     // action (and not with a toolbar button) do not work with Qt 5.x | 
| Chris@384 | 345     // under OS/X. | 
| Chris@354 | 346     // | 
| Chris@354 | 347     // Apparently Cocoa never handled them as a matter of course, but | 
| Chris@354 | 348     // earlier versions of Qt picked them up as widget shortcuts and | 
| Chris@354 | 349     // handled them anyway. That behaviour was removed to fix a crash | 
| Chris@354 | 350     // when invoking a menu while its window was overridden by a modal | 
| Chris@354 | 351     // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657). | 
| Chris@354 | 352     // | 
| Chris@354 | 353     // This workaround restores the single-key shortcut behaviour by | 
| Chris@384 | 354     // searching in menus for single-key shortcuts that are associated | 
| Chris@384 | 355     // only with the menu and not with a toolbar button, and | 
| Chris@384 | 356     // augmenting them with global application shortcuts that invoke | 
| Chris@384 | 357     // the relevant actions, testing whether the actions are enabled | 
| Chris@384 | 358     // on invocation. | 
| Chris@354 | 359     // | 
| Chris@384 | 360     // (Previously this acted on all single-key shortcuts in menus, | 
| Chris@384 | 361     // and it removed the shortcut from the action when it created | 
| Chris@384 | 362     // each new global one, in order to avoid an "ambiguous shortcut" | 
| Chris@384 | 363     // error in the case where the action was also associated with a | 
| Chris@384 | 364     // toolbar button. But that has the unwelcome side-effect of | 
| Chris@384 | 365     // removing the shortcut hint from the menu entry. So now we leave | 
| Chris@384 | 366     // the shortcut in the menu action as well as creating a global | 
| Chris@384 | 367     // one, and we only act on shortcuts that have no toolbar button, | 
| Chris@384 | 368     // i.e. that will not otherwise work. The downside is that if this | 
| Chris@384 | 369     // bug is fixed in a future Qt release, we will start getting | 
| Chris@384 | 370     // "ambiguous shortcut" errors from the menu entry actions and | 
| Chris@384 | 371     // will need to update the code.) | 
| Chris@354 | 372 | 
| Chris@443 | 373     // Update: The bug was fixed in Qt 5.4 for shortcuts with no | 
| Chris@443 | 374     // modifier, and I believe it is fixed in Qt 5.5 for shortcuts | 
| Chris@443 | 375     // with Shift modifiers. The below reflects that | 
| Chris@443 | 376 | 
| Chris@443 | 377 #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0)) | 
| Chris@443 | 378 | 
| Chris@390 | 379     if (!m_menuShortcutMapper) { | 
| Chris@390 | 380         m_menuShortcutMapper = new QSignalMapper(this); | 
| Chris@392 | 381         connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)), | 
| Chris@392 | 382                 this, SLOT(menuActionMapperInvoked(QObject *))); | 
| Chris@390 | 383     } | 
| Chris@390 | 384 | 
| Chris@354 | 385     foreach (QAction *a, menu->actions()) { | 
| Chris@394 | 386 | 
| Chris@394 | 387         if (a->isSeparator()) { | 
| Chris@394 | 388             continue; | 
| Chris@394 | 389         } else if (a->menu()) { | 
| Chris@394 | 390             finaliseMenu(a->menu()); | 
| Chris@394 | 391         } else { | 
| Chris@394 | 392 | 
| Chris@394 | 393             QWidgetList ww = a->associatedWidgets(); | 
| Chris@394 | 394             bool hasButton = false; | 
| Chris@394 | 395             foreach (QWidget *w, ww) { | 
| Chris@394 | 396                 if (qobject_cast<QAbstractButton *>(w)) { | 
| Chris@394 | 397                     hasButton = true; | 
| Chris@394 | 398                     break; | 
| Chris@394 | 399                 } | 
| Chris@394 | 400             } | 
| Chris@394 | 401             if (hasButton) continue; | 
| Chris@394 | 402             QKeySequence sc = a->shortcut(); | 
| Chris@399 | 403 | 
| Chris@399 | 404             // Note that the set of "single-key shortcuts" that aren't | 
| Chris@399 | 405             // working and that we need to handle here includes those | 
| Chris@399 | 406             // with the Shift modifier mask as well as those with no | 
| Chris@399 | 407             // modifier at all | 
| Chris@443 | 408 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) | 
| Chris@443 | 409             // Nothing needed | 
| Chris@443 | 410             if (false) { | 
| Chris@443 | 411 #elif (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) | 
| Chris@443 | 412             if (sc.count() == 1 && | 
| Chris@443 | 413                 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier) { | 
| Chris@443 | 414 #else | 
| Chris@399 | 415             if (sc.count() == 1 && | 
| Chris@399 | 416                 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier || | 
| Chris@399 | 417                  (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) { | 
| Chris@443 | 418 #endif | 
| Chris@394 | 419                 QShortcut *newSc = new QShortcut(sc, a->parentWidget()); | 
| Chris@394 | 420                 QObject::connect(newSc, SIGNAL(activated()), | 
| Chris@394 | 421                                  m_menuShortcutMapper, SLOT(map())); | 
| Chris@394 | 422                 m_menuShortcutMapper->setMapping(newSc, a); | 
| Chris@394 | 423                 m_appShortcuts.push_back(newSc); | 
| Chris@384 | 424             } | 
| Chris@384 | 425         } | 
| Chris@354 | 426     } | 
| Chris@354 | 427 #endif | 
| Chris@443 | 428 #endif | 
| Chris@354 | 429 } | 
| Chris@354 | 430 | 
| Chris@354 | 431 void | 
| Chris@354 | 432 MainWindowBase::menuActionMapperInvoked(QObject *o) | 
| Chris@354 | 433 { | 
| Chris@354 | 434     QAction *a = qobject_cast<QAction *>(o); | 
| Chris@354 | 435     if (a && a->isEnabled()) { | 
| Chris@354 | 436         a->trigger(); | 
| Chris@354 | 437     } | 
| Chris@354 | 438 } | 
| Chris@354 | 439 | 
| Chris@354 | 440 void | 
| Chris@168 | 441 MainWindowBase::resizeConstrained(QSize size) | 
| Chris@168 | 442 { | 
| Chris@168 | 443     QDesktopWidget *desktop = QApplication::desktop(); | 
| Chris@168 | 444     QRect available = desktop->availableGeometry(); | 
| Chris@168 | 445     QSize actual(std::min(size.width(), available.width()), | 
| Chris@168 | 446                  std::min(size.height(), available.height())); | 
| Chris@168 | 447     resize(actual); | 
| Chris@168 | 448 } | 
| Chris@168 | 449 | 
| Chris@168 | 450 void | 
| Chris@304 | 451 MainWindowBase::startOSCQueue() | 
| Chris@304 | 452 { | 
| Chris@304 | 453     m_oscQueueStarter = new OSCQueueStarter(this); | 
| Chris@304 | 454     connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady())); | 
| Chris@304 | 455     m_oscQueueStarter->start(); | 
| Chris@304 | 456 } | 
| Chris@304 | 457 | 
| Chris@304 | 458 void | 
| Chris@113 | 459 MainWindowBase::oscReady() | 
| Chris@113 | 460 { | 
| Chris@113 | 461     if (m_oscQueue && m_oscQueue->isOK()) { | 
| Chris@113 | 462         connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); | 
| Chris@113 | 463         QTimer *oscTimer = new QTimer(this); | 
| Chris@113 | 464         connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); | 
| Chris@113 | 465         oscTimer->start(1000); | 
| Chris@293 | 466         cerr << "Finished setting up OSC interface" << endl; | 
| Chris@113 | 467     } | 
| Chris@113 | 468 } | 
| Chris@113 | 469 | 
| Chris@45 | 470 QString | 
| Chris@45 | 471 MainWindowBase::getOpenFileName(FileFinder::FileType type) | 
| Chris@45 | 472 { | 
| Chris@45 | 473     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@358 | 474 | 
| Chris@358 | 475     if (type == FileFinder::AnyFile) { | 
| Chris@45 | 476         if (getMainModel() != 0 && | 
| Chris@45 | 477             m_paneStack != 0 && | 
| Chris@45 | 478             m_paneStack->getCurrentPane() != 0) { // can import a layer | 
| Chris@45 | 479             return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); | 
| Chris@45 | 480         } else { | 
| Chris@45 | 481             return ff->getOpenFileName(FileFinder::SessionOrAudioFile, | 
| Chris@45 | 482                                        m_sessionFile); | 
| Chris@45 | 483         } | 
| Chris@358 | 484     } | 
| Chris@358 | 485 | 
| Chris@358 | 486     QString lastPath = m_sessionFile; | 
| Chris@358 | 487 | 
| Chris@358 | 488     if (type == FileFinder::AudioFile) { | 
| Chris@358 | 489         lastPath = m_audioFile; | 
| Chris@45 | 490     } | 
| Chris@358 | 491 | 
| Chris@358 | 492     return ff->getOpenFileName(type, lastPath); | 
| Chris@45 | 493 } | 
| Chris@45 | 494 | 
| Chris@45 | 495 QString | 
| Chris@45 | 496 MainWindowBase::getSaveFileName(FileFinder::FileType type) | 
| Chris@45 | 497 { | 
| Chris@358 | 498     QString lastPath = m_sessionFile; | 
| Chris@358 | 499 | 
| Chris@358 | 500     if (type == FileFinder::AudioFile) { | 
| Chris@358 | 501         lastPath = m_audioFile; | 
| Chris@358 | 502     } | 
| Chris@358 | 503 | 
| Chris@45 | 504     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@358 | 505     return ff->getSaveFileName(type, lastPath); | 
| Chris@45 | 506 } | 
| Chris@45 | 507 | 
| Chris@45 | 508 void | 
| Chris@45 | 509 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path) | 
| Chris@45 | 510 { | 
| Chris@45 | 511     FileFinder *ff = FileFinder::getInstance(); | 
| Chris@45 | 512     ff->registerLastOpenedFilePath(type, path); | 
| Chris@45 | 513 } | 
| Chris@45 | 514 | 
| Chris@222 | 515 QString | 
| Chris@222 | 516 MainWindowBase::getDefaultSessionTemplate() const | 
| Chris@222 | 517 { | 
| Chris@231 | 518     QSettings settings; | 
| Chris@231 | 519     settings.beginGroup("MainWindow"); | 
| Chris@231 | 520     QString templateName = settings.value("sessiontemplate", "").toString(); | 
| Chris@231 | 521     if (templateName == "") templateName = "default"; | 
| Chris@231 | 522     return templateName; | 
| Chris@222 | 523 } | 
| Chris@222 | 524 | 
| Chris@222 | 525 void | 
| Chris@251 | 526 MainWindowBase::setDefaultSessionTemplate(QString n) | 
| Chris@251 | 527 { | 
| Chris@251 | 528     QSettings settings; | 
| Chris@251 | 529     settings.beginGroup("MainWindow"); | 
| Chris@251 | 530     settings.setValue("sessiontemplate", n); | 
| Chris@251 | 531 } | 
| Chris@251 | 532 | 
| Chris@251 | 533 void | 
| Chris@45 | 534 MainWindowBase::updateMenuStates() | 
| Chris@45 | 535 { | 
| Chris@45 | 536     Pane *currentPane = 0; | 
| Chris@45 | 537     Layer *currentLayer = 0; | 
| Chris@45 | 538 | 
| Chris@45 | 539     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 540     if (currentPane) currentLayer = currentPane->getSelectedLayer(); | 
| Chris@45 | 541 | 
| Chris@73 | 542     bool havePrevPane = false, haveNextPane = false; | 
| Chris@73 | 543     bool havePrevLayer = false, haveNextLayer = false; | 
| Chris@73 | 544 | 
| Chris@73 | 545     if (currentPane) { | 
| Chris@73 | 546         for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 547             if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 548                 if (i > 0) havePrevPane = true; | 
| Chris@73 | 549                 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true; | 
| Chris@73 | 550                 break; | 
| Chris@73 | 551             } | 
| Chris@73 | 552         } | 
| Chris@403 | 553         // the prev/next layer commands actually include the pane | 
| Chris@403 | 554         // itself as one of the selectables -- so we always have a | 
| Chris@403 | 555         // prev and next layer, as long as we have a pane with at | 
| Chris@403 | 556         // least one layer in it | 
| Chris@403 | 557         if (currentPane->getLayerCount() > 0) { | 
| Chris@403 | 558             havePrevLayer = true; | 
| Chris@403 | 559             haveNextLayer = true; | 
| Chris@73 | 560         } | 
| Chris@73 | 561     } | 
| Chris@73 | 562 | 
| Chris@45 | 563     bool haveCurrentPane = | 
| Chris@45 | 564         (currentPane != 0); | 
| Chris@45 | 565     bool haveCurrentLayer = | 
| Chris@45 | 566         (haveCurrentPane && | 
| Chris@45 | 567          (currentLayer != 0)); | 
| Chris@45 | 568     bool haveMainModel = | 
| Chris@45 | 569 	(getMainModel() != 0); | 
| Chris@45 | 570     bool havePlayTarget = | 
| Chris@475 | 571 	(m_playTarget != 0 || m_audioIO != 0); | 
| Chris@475 | 572     bool haveRecordSource = | 
| Chris@475 | 573 	(m_audioIO != 0); | 
| Chris@45 | 574     bool haveSelection = | 
| Chris@45 | 575 	(m_viewManager && | 
| Chris@45 | 576 	 !m_viewManager->getSelections().empty()); | 
| Chris@45 | 577     bool haveCurrentEditableLayer = | 
| Chris@45 | 578 	(haveCurrentLayer && | 
| Chris@45 | 579 	 currentLayer->isLayerEditable()); | 
| Chris@45 | 580     bool haveCurrentTimeInstantsLayer = | 
| Chris@45 | 581 	(haveCurrentLayer && | 
| Chris@45 | 582 	 dynamic_cast<TimeInstantLayer *>(currentLayer)); | 
| Chris@184 | 583     bool haveCurrentDurationLayer = | 
| Chris@184 | 584 	(haveCurrentLayer && | 
| Chris@184 | 585 	 (dynamic_cast<NoteLayer *>(currentLayer) || | 
| matthiasm@267 | 586 	  dynamic_cast<FlexiNoteLayer *>(currentLayer) || | 
| Chris@184 | 587           dynamic_cast<RegionLayer *>(currentLayer))); | 
| Chris@45 | 588     bool haveCurrentColour3DPlot = | 
| Chris@45 | 589         (haveCurrentLayer && | 
| Chris@45 | 590          dynamic_cast<Colour3DPlotLayer *>(currentLayer)); | 
| Chris@45 | 591     bool haveClipboardContents = | 
| Chris@45 | 592         (m_viewManager && | 
| Chris@45 | 593          !m_viewManager->getClipboard().empty()); | 
| Chris@146 | 594     bool haveTabularLayer = | 
| Chris@146 | 595         (haveCurrentLayer && | 
| Chris@146 | 596          dynamic_cast<TabularModel *>(currentLayer->getModel())); | 
| Chris@45 | 597 | 
| Chris@45 | 598     emit canAddPane(haveMainModel); | 
| Chris@45 | 599     emit canDeleteCurrentPane(haveCurrentPane); | 
| Chris@45 | 600     emit canZoom(haveMainModel && haveCurrentPane); | 
| Chris@45 | 601     emit canScroll(haveMainModel && haveCurrentPane); | 
| Chris@45 | 602     emit canAddLayer(haveMainModel && haveCurrentPane); | 
| Chris@45 | 603     emit canImportMoreAudio(haveMainModel); | 
| Chris@259 | 604     emit canReplaceMainAudio(haveMainModel); | 
| Chris@45 | 605     emit canImportLayer(haveMainModel && haveCurrentPane); | 
| Chris@45 | 606     emit canExportAudio(haveMainModel); | 
| Chris@289 | 607     emit canChangeSessionTemplate(haveMainModel); | 
| Chris@45 | 608     emit canExportLayer(haveMainModel && | 
| Chris@45 | 609                         (haveCurrentEditableLayer || haveCurrentColour3DPlot)); | 
| Chris@45 | 610     emit canExportImage(haveMainModel && haveCurrentPane); | 
| Chris@45 | 611     emit canDeleteCurrentLayer(haveCurrentLayer); | 
| Chris@45 | 612     emit canRenameLayer(haveCurrentLayer); | 
| Chris@45 | 613     emit canEditLayer(haveCurrentEditableLayer); | 
| Chris@146 | 614     emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer); | 
| Chris@45 | 615     emit canMeasureLayer(haveCurrentLayer); | 
| Chris@45 | 616     emit canSelect(haveMainModel && haveCurrentPane); | 
| Chris@188 | 617     emit canPlay(haveMainModel && havePlayTarget); | 
| Chris@475 | 618     emit canRecord(haveRecordSource); | 
| Chris@453 | 619     emit canFfwd(haveMainModel); | 
| Chris@453 | 620     emit canRewind(haveMainModel); | 
| Chris@87 | 621     emit canPaste(haveClipboardContents); | 
| Chris@45 | 622     emit canInsertInstant(haveCurrentPane); | 
| Chris@45 | 623     emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); | 
| Chris@184 | 624     emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer); | 
| Chris@45 | 625     emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); | 
| Chris@45 | 626     emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); | 
| Chris@45 | 627     emit canClearSelection(haveSelection); | 
| Chris@45 | 628     emit canEditSelection(haveSelection && haveCurrentEditableLayer); | 
| Chris@45 | 629     emit canSave(m_sessionFile != "" && m_documentModified); | 
| Chris@359 | 630     emit canSaveAs(haveMainModel); | 
| Chris@73 | 631     emit canSelectPreviousPane(havePrevPane); | 
| Chris@73 | 632     emit canSelectNextPane(haveNextPane); | 
| Chris@73 | 633     emit canSelectPreviousLayer(havePrevLayer); | 
| Chris@73 | 634     emit canSelectNextLayer(haveNextLayer); | 
| Chris@45 | 635 } | 
| Chris@45 | 636 | 
| Chris@45 | 637 void | 
| Chris@45 | 638 MainWindowBase::documentModified() | 
| Chris@45 | 639 { | 
| Chris@233 | 640 //    SVDEBUG << "MainWindowBase::documentModified" << endl; | 
| Chris@45 | 641 | 
| Chris@45 | 642     if (!m_documentModified) { | 
| Chris@45 | 643         //!!! this in subclass implementation? | 
| Chris@45 | 644 	setWindowTitle(tr("%1 (modified)").arg(windowTitle())); | 
| Chris@45 | 645     } | 
| Chris@45 | 646 | 
| Chris@45 | 647     m_documentModified = true; | 
| Chris@45 | 648     updateMenuStates(); | 
| Chris@45 | 649 } | 
| Chris@45 | 650 | 
| Chris@45 | 651 void | 
| Chris@45 | 652 MainWindowBase::documentRestored() | 
| Chris@45 | 653 { | 
| Chris@233 | 654 //    SVDEBUG << "MainWindowBase::documentRestored" << endl; | 
| Chris@45 | 655 | 
| Chris@45 | 656     if (m_documentModified) { | 
| Chris@45 | 657         //!!! this in subclass implementation? | 
| Chris@45 | 658 	QString wt(windowTitle()); | 
| Chris@45 | 659 	wt.replace(tr(" (modified)"), ""); | 
| Chris@45 | 660 	setWindowTitle(wt); | 
| Chris@45 | 661     } | 
| Chris@45 | 662 | 
| Chris@45 | 663     m_documentModified = false; | 
| Chris@45 | 664     updateMenuStates(); | 
| Chris@45 | 665 } | 
| Chris@45 | 666 | 
| Chris@45 | 667 void | 
| Chris@45 | 668 MainWindowBase::playLoopToggled() | 
| Chris@45 | 669 { | 
| Chris@45 | 670     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 671 | 
| Chris@45 | 672     if (action) { | 
| Chris@45 | 673 	m_viewManager->setPlayLoopMode(action->isChecked()); | 
| Chris@45 | 674     } else { | 
| Chris@45 | 675 	m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); | 
| Chris@45 | 676     } | 
| Chris@45 | 677 } | 
| Chris@45 | 678 | 
| Chris@45 | 679 void | 
| Chris@45 | 680 MainWindowBase::playSelectionToggled() | 
| Chris@45 | 681 { | 
| Chris@45 | 682     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 683 | 
| Chris@45 | 684     if (action) { | 
| Chris@45 | 685 	m_viewManager->setPlaySelectionMode(action->isChecked()); | 
| Chris@45 | 686     } else { | 
| Chris@45 | 687 	m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); | 
| Chris@45 | 688     } | 
| Chris@45 | 689 } | 
| Chris@45 | 690 | 
| Chris@45 | 691 void | 
| Chris@45 | 692 MainWindowBase::playSoloToggled() | 
| Chris@45 | 693 { | 
| Chris@45 | 694     QAction *action = dynamic_cast<QAction *>(sender()); | 
| Chris@45 | 695 | 
| Chris@45 | 696     if (action) { | 
| Chris@45 | 697 	m_viewManager->setPlaySoloMode(action->isChecked()); | 
| Chris@45 | 698     } else { | 
| Chris@45 | 699 	m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); | 
| Chris@45 | 700     } | 
| Chris@45 | 701 | 
| Chris@45 | 702     if (m_viewManager->getPlaySoloMode()) { | 
| Chris@45 | 703         currentPaneChanged(m_paneStack->getCurrentPane()); | 
| Chris@45 | 704     } else { | 
| Chris@45 | 705         m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 706         if (m_playSource) { | 
| Chris@45 | 707             m_playSource->clearSoloModelSet(); | 
| Chris@45 | 708         } | 
| Chris@45 | 709     } | 
| Chris@45 | 710 } | 
| Chris@45 | 711 | 
| Chris@45 | 712 void | 
| Chris@45 | 713 MainWindowBase::currentPaneChanged(Pane *p) | 
| Chris@45 | 714 { | 
| Chris@45 | 715     updateMenuStates(); | 
| Chris@45 | 716     updateVisibleRangeDisplay(p); | 
| Chris@45 | 717 | 
| Chris@45 | 718     if (!p) return; | 
| Chris@45 | 719 | 
| Chris@45 | 720     if (!(m_viewManager && | 
| Chris@45 | 721           m_playSource && | 
| Chris@45 | 722           m_viewManager->getPlaySoloMode())) { | 
| Chris@45 | 723         if (m_viewManager) m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 724         return; | 
| Chris@45 | 725     } | 
| Chris@45 | 726 | 
| Chris@45 | 727     Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); | 
| Chris@60 | 728 | 
| Chris@93 | 729     // What we want here is not the currently playing frame (unless we | 
| Chris@93 | 730     // are about to clear out the audio playback buffers -- which may | 
| Chris@93 | 731     // or may not be possible, depending on the audio driver).  What | 
| Chris@93 | 732     // we want is the frame that was last committed to the soundcard | 
| Chris@93 | 733     // buffers, as the audio driver will continue playing up to that | 
| Chris@93 | 734     // frame before switching to whichever one we decide we want to | 
| Chris@93 | 735     // switch to, regardless of our efforts. | 
| Chris@93 | 736 | 
| Chris@435 | 737     sv_frame_t frame = m_playSource->getCurrentBufferedFrame(); | 
| Chris@93 | 738 | 
| Chris@388 | 739     cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl; | 
| Chris@45 | 740 | 
| Chris@45 | 741     View::ModelSet soloModels = p->getModels(); | 
| Chris@45 | 742 | 
| Chris@57 | 743     View::ModelSet sources; | 
| Chris@57 | 744     for (View::ModelSet::iterator mi = soloModels.begin(); | 
| Chris@57 | 745          mi != soloModels.end(); ++mi) { | 
| Chris@190 | 746         // If a model in this pane is derived from something else, | 
| Chris@190 | 747         // then we want to play that model as well -- if the model | 
| Chris@190 | 748         // that's derived from it is not something that is itself | 
| Chris@190 | 749         // individually playable (e.g. a waveform) | 
| Chris@190 | 750         if (*mi && | 
| Chris@190 | 751             !dynamic_cast<RangeSummarisableTimeValueModel *>(*mi) && | 
| Chris@190 | 752             (*mi)->getSourceModel()) { | 
| Chris@57 | 753             sources.insert((*mi)->getSourceModel()); | 
| Chris@57 | 754         } | 
| Chris@57 | 755     } | 
| Chris@57 | 756     for (View::ModelSet::iterator mi = sources.begin(); | 
| Chris@57 | 757          mi != sources.end(); ++mi) { | 
| Chris@57 | 758         soloModels.insert(*mi); | 
| Chris@57 | 759     } | 
| Chris@57 | 760 | 
| Chris@60 | 761     //!!! Need an "atomic" way of telling the play source that the | 
| Chris@60 | 762     //playback model has changed, and changing it on ViewManager -- | 
| Chris@60 | 763     //the play source should be making the setPlaybackModel call to | 
| Chris@60 | 764     //ViewManager | 
| Chris@60 | 765 | 
| Chris@45 | 766     for (View::ModelSet::iterator mi = soloModels.begin(); | 
| Chris@45 | 767          mi != soloModels.end(); ++mi) { | 
| Chris@45 | 768         if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) { | 
| Chris@45 | 769             m_viewManager->setPlaybackModel(*mi); | 
| Chris@45 | 770         } | 
| Chris@45 | 771     } | 
| Chris@45 | 772 | 
| Chris@45 | 773     RangeSummarisableTimeValueModel *a = | 
| Chris@45 | 774         dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); | 
| Chris@45 | 775     RangeSummarisableTimeValueModel *b = | 
| Chris@45 | 776         dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> | 
| Chris@45 | 777                                                         getPlaybackModel()); | 
| Chris@45 | 778 | 
| Chris@45 | 779     m_playSource->setSoloModelSet(soloModels); | 
| Chris@45 | 780 | 
| Chris@45 | 781     if (a && b && (a != b)) { | 
| Chris@60 | 782         if (m_playSource->isPlaying()) m_playSource->play(frame); | 
| Chris@45 | 783     } | 
| Chris@45 | 784 } | 
| Chris@45 | 785 | 
| Chris@45 | 786 void | 
| Chris@45 | 787 MainWindowBase::currentLayerChanged(Pane *p, Layer *) | 
| Chris@45 | 788 { | 
| Chris@45 | 789     updateMenuStates(); | 
| Chris@45 | 790     updateVisibleRangeDisplay(p); | 
| Chris@45 | 791 } | 
| Chris@45 | 792 | 
| Chris@45 | 793 void | 
| Chris@45 | 794 MainWindowBase::selectAll() | 
| Chris@45 | 795 { | 
| Chris@45 | 796     if (!getMainModel()) return; | 
| Chris@45 | 797     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@45 | 798 					  getMainModel()->getEndFrame())); | 
| Chris@45 | 799 } | 
| Chris@45 | 800 | 
| Chris@45 | 801 void | 
| Chris@45 | 802 MainWindowBase::selectToStart() | 
| Chris@45 | 803 { | 
| Chris@45 | 804     if (!getMainModel()) return; | 
| Chris@45 | 805     m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | 
| Chris@45 | 806 					  m_viewManager->getGlobalCentreFrame())); | 
| Chris@45 | 807 } | 
| Chris@45 | 808 | 
| Chris@45 | 809 void | 
| Chris@45 | 810 MainWindowBase::selectToEnd() | 
| Chris@45 | 811 { | 
| Chris@45 | 812     if (!getMainModel()) return; | 
| Chris@45 | 813     m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), | 
| Chris@45 | 814 					  getMainModel()->getEndFrame())); | 
| Chris@45 | 815 } | 
| Chris@45 | 816 | 
| Chris@45 | 817 void | 
| Chris@45 | 818 MainWindowBase::selectVisible() | 
| Chris@45 | 819 { | 
| Chris@45 | 820     Model *model = getMainModel(); | 
| Chris@45 | 821     if (!model) return; | 
| Chris@45 | 822 | 
| Chris@45 | 823     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 824     if (!currentPane) return; | 
| Chris@45 | 825 | 
| Chris@435 | 826     sv_frame_t startFrame, endFrame; | 
| Chris@45 | 827 | 
| Chris@45 | 828     if (currentPane->getStartFrame() < 0) startFrame = 0; | 
| Chris@45 | 829     else startFrame = currentPane->getStartFrame(); | 
| Chris@45 | 830 | 
| Chris@45 | 831     if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); | 
| Chris@45 | 832     else endFrame = currentPane->getEndFrame(); | 
| Chris@45 | 833 | 
| Chris@45 | 834     m_viewManager->setSelection(Selection(startFrame, endFrame)); | 
| Chris@45 | 835 } | 
| Chris@45 | 836 | 
| Chris@45 | 837 void | 
| Chris@45 | 838 MainWindowBase::clearSelection() | 
| Chris@45 | 839 { | 
| Chris@45 | 840     m_viewManager->clearSelections(); | 
| Chris@45 | 841 } | 
| Chris@45 | 842 | 
| Chris@45 | 843 void | 
| Chris@45 | 844 MainWindowBase::cut() | 
| Chris@45 | 845 { | 
| Chris@45 | 846     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 847     if (!currentPane) return; | 
| Chris@45 | 848 | 
| Chris@45 | 849     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 850     if (!layer) return; | 
| Chris@45 | 851 | 
| Chris@45 | 852     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@45 | 853     clipboard.clear(); | 
| Chris@45 | 854 | 
| Chris@45 | 855     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 856 | 
| Chris@45 | 857     CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); | 
| Chris@45 | 858 | 
| Chris@45 | 859     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 860          i != selections.end(); ++i) { | 
| Chris@86 | 861         layer->copy(currentPane, *i, clipboard); | 
| Chris@45 | 862         layer->deleteSelection(*i); | 
| Chris@45 | 863     } | 
| Chris@45 | 864 | 
| Chris@45 | 865     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 866 } | 
| Chris@45 | 867 | 
| Chris@45 | 868 void | 
| Chris@45 | 869 MainWindowBase::copy() | 
| Chris@45 | 870 { | 
| Chris@45 | 871     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 872     if (!currentPane) return; | 
| Chris@45 | 873 | 
| Chris@45 | 874     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 875     if (!layer) return; | 
| Chris@45 | 876 | 
| Chris@45 | 877     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@45 | 878     clipboard.clear(); | 
| Chris@45 | 879 | 
| Chris@45 | 880     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 881 | 
| Chris@45 | 882     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 883          i != selections.end(); ++i) { | 
| Chris@86 | 884         layer->copy(currentPane, *i, clipboard); | 
| Chris@45 | 885     } | 
| Chris@45 | 886 } | 
| Chris@45 | 887 | 
| Chris@45 | 888 void | 
| Chris@45 | 889 MainWindowBase::paste() | 
| Chris@45 | 890 { | 
| Chris@215 | 891     pasteRelative(0); | 
| Chris@215 | 892 } | 
| Chris@215 | 893 | 
| Chris@215 | 894 void | 
| Chris@215 | 895 MainWindowBase::pasteAtPlaybackPosition() | 
| Chris@215 | 896 { | 
| Chris@435 | 897     sv_frame_t pos = getFrame(); | 
| Chris@215 | 898     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@215 | 899     if (!clipboard.empty()) { | 
| Chris@435 | 900         sv_frame_t firstEventFrame = clipboard.getPoints()[0].getFrame(); | 
| Chris@435 | 901         sv_frame_t offset = 0; | 
| Chris@215 | 902         if (firstEventFrame < 0) { | 
| Chris@366 | 903             offset = pos - firstEventFrame; | 
| Chris@354 | 904         } else if (firstEventFrame < pos) { | 
| Chris@366 | 905             offset = pos - firstEventFrame; | 
| Chris@215 | 906         } else { | 
| Chris@366 | 907             offset = -(firstEventFrame - pos); | 
| Chris@215 | 908         } | 
| Chris@215 | 909         pasteRelative(offset); | 
| Chris@215 | 910     } | 
| Chris@215 | 911 } | 
| Chris@215 | 912 | 
| Chris@215 | 913 void | 
| Chris@435 | 914 MainWindowBase::pasteRelative(sv_frame_t offset) | 
| Chris@215 | 915 { | 
| Chris@45 | 916     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 917     if (!currentPane) return; | 
| Chris@45 | 918 | 
| Chris@45 | 919     Layer *layer = currentPane->getSelectedLayer(); | 
| Chris@45 | 920 | 
| Chris@45 | 921     Clipboard &clipboard = m_viewManager->getClipboard(); | 
| Chris@87 | 922 | 
| Chris@98 | 923     bool inCompound = false; | 
| Chris@87 | 924 | 
| Chris@87 | 925     if (!layer || !layer->isLayerEditable()) { | 
| Chris@87 | 926 | 
| Chris@87 | 927         CommandHistory::getInstance()->startCompoundOperation | 
| Chris@87 | 928             (tr("Paste"), true); | 
| Chris@87 | 929 | 
| Chris@87 | 930         // no suitable current layer: create one of the most | 
| Chris@87 | 931         // appropriate sort | 
| Chris@87 | 932         LayerFactory::LayerType type = | 
| Chris@87 | 933             LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard); | 
| Chris@87 | 934         layer = m_document->createEmptyLayer(type); | 
| Chris@87 | 935 | 
| Chris@87 | 936         if (!layer) { | 
| Chris@87 | 937             CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@87 | 938             return; | 
| Chris@45 | 939         } | 
| Chris@87 | 940 | 
| Chris@87 | 941         m_document->addLayerToView(currentPane, layer); | 
| Chris@87 | 942         m_paneStack->setCurrentLayer(currentPane, layer); | 
| Chris@87 | 943 | 
| Chris@87 | 944         inCompound = true; | 
| Chris@45 | 945     } | 
| Chris@45 | 946 | 
| Chris@215 | 947     layer->paste(currentPane, clipboard, offset, true); | 
| Chris@45 | 948 | 
| Chris@87 | 949     if (inCompound) CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 950 } | 
| Chris@45 | 951 | 
| Chris@45 | 952 void | 
| Chris@45 | 953 MainWindowBase::deleteSelected() | 
| Chris@45 | 954 { | 
| Chris@45 | 955     if (m_paneStack->getCurrentPane() && | 
| Chris@45 | 956 	m_paneStack->getCurrentPane()->getSelectedLayer()) { | 
| Chris@45 | 957 | 
| Chris@45 | 958         Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); | 
| Chris@45 | 959 | 
| Chris@409 | 960         if (m_viewManager) { | 
| Chris@409 | 961 | 
| Chris@409 | 962             if (m_viewManager->getToolMode() == ViewManager::MeasureMode) { | 
| Chris@409 | 963 | 
| Chris@409 | 964                 layer->deleteCurrentMeasureRect(); | 
| Chris@45 | 965 | 
| Chris@409 | 966             } else { | 
| Chris@409 | 967 | 
| Chris@409 | 968                 MultiSelection::SelectionList selections = | 
| Chris@409 | 969                     m_viewManager->getSelections(); | 
| Chris@409 | 970 | 
| Chris@409 | 971                 for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@409 | 972                      i != selections.end(); ++i) { | 
| Chris@409 | 973                     layer->deleteSelection(*i); | 
| Chris@409 | 974                 } | 
| Chris@45 | 975             } | 
| Chris@45 | 976 	} | 
| Chris@45 | 977     } | 
| Chris@45 | 978 } | 
| Chris@45 | 979 | 
| Chris@161 | 980 // FrameTimer method | 
| Chris@161 | 981 | 
| Chris@435 | 982 sv_frame_t | 
| Chris@161 | 983 MainWindowBase::getFrame() const | 
| Chris@161 | 984 { | 
| Chris@161 | 985     if (m_playSource && m_playSource->isPlaying()) { | 
| Chris@161 | 986         return m_playSource->getCurrentPlayingFrame(); | 
| Chris@161 | 987     } else { | 
| Chris@161 | 988         return m_viewManager->getPlaybackFrame(); | 
| Chris@161 | 989     } | 
| Chris@161 | 990 } | 
| Chris@161 | 991 | 
| Chris@45 | 992 void | 
| Chris@45 | 993 MainWindowBase::insertInstant() | 
| Chris@45 | 994 { | 
| Chris@161 | 995     insertInstantAt(getFrame()); | 
| Chris@45 | 996 } | 
| Chris@45 | 997 | 
| Chris@45 | 998 void | 
| Chris@45 | 999 MainWindowBase::insertInstantsAtBoundaries() | 
| Chris@45 | 1000 { | 
| Chris@45 | 1001     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@45 | 1002     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@45 | 1003          i != selections.end(); ++i) { | 
| Chris@435 | 1004         sv_frame_t start = i->getStartFrame(); | 
| Chris@435 | 1005         sv_frame_t end = i->getEndFrame(); | 
| Chris@45 | 1006         if (start != end) { | 
| Chris@184 | 1007             insertInstantAt(start); | 
| Chris@184 | 1008             insertInstantAt(end); | 
| Chris@45 | 1009         } | 
| Chris@45 | 1010     } | 
| Chris@45 | 1011 } | 
| Chris@45 | 1012 | 
| Chris@45 | 1013 void | 
| Chris@435 | 1014 MainWindowBase::insertInstantAt(sv_frame_t frame) | 
| Chris@45 | 1015 { | 
| Chris@45 | 1016     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1017     if (!pane) { | 
| Chris@45 | 1018         return; | 
| Chris@45 | 1019     } | 
| Chris@45 | 1020 | 
| Chris@74 | 1021     frame = pane->alignFromReference(frame); | 
| Chris@74 | 1022 | 
| Chris@45 | 1023     Layer *layer = dynamic_cast<TimeInstantLayer *> | 
| Chris@45 | 1024         (pane->getSelectedLayer()); | 
| Chris@45 | 1025 | 
| Chris@45 | 1026     if (!layer) { | 
| Chris@45 | 1027         for (int i = pane->getLayerCount(); i > 0; --i) { | 
| Chris@45 | 1028             layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); | 
| Chris@45 | 1029             if (layer) break; | 
| Chris@45 | 1030         } | 
| Chris@45 | 1031 | 
| Chris@45 | 1032         if (!layer) { | 
| Chris@45 | 1033             CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 1034                 (tr("Add Point"), true); | 
| Chris@45 | 1035             layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); | 
| Chris@45 | 1036             if (layer) { | 
| Chris@45 | 1037                 m_document->addLayerToView(pane, layer); | 
| Chris@45 | 1038                 m_paneStack->setCurrentLayer(pane, layer); | 
| Chris@45 | 1039             } | 
| Chris@45 | 1040             CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 1041         } | 
| Chris@45 | 1042     } | 
| Chris@45 | 1043 | 
| Chris@45 | 1044     if (layer) { | 
| Chris@45 | 1045 | 
| Chris@45 | 1046         Model *model = layer->getModel(); | 
| Chris@45 | 1047         SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 
| Chris@45 | 1048             (model); | 
| Chris@45 | 1049 | 
| Chris@45 | 1050         if (sodm) { | 
| Chris@45 | 1051             SparseOneDimensionalModel::Point point(frame, ""); | 
| Chris@45 | 1052 | 
| Chris@45 | 1053             SparseOneDimensionalModel::Point prevPoint(0); | 
| Chris@45 | 1054             bool havePrevPoint = false; | 
| Chris@45 | 1055 | 
| Chris@45 | 1056             SparseOneDimensionalModel::EditCommand *command = | 
| Chris@45 | 1057                 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); | 
| Chris@45 | 1058 | 
| Chris@409 | 1059             if (m_labeller) { | 
| Chris@409 | 1060 | 
| Chris@409 | 1061                 if (m_labeller->requiresPrevPoint()) { | 
| Chris@409 | 1062 | 
| Chris@409 | 1063                     SparseOneDimensionalModel::PointList prevPoints = | 
| Chris@409 | 1064                         sodm->getPreviousPoints(frame); | 
| Chris@409 | 1065 | 
| Chris@409 | 1066                     if (!prevPoints.empty()) { | 
| Chris@409 | 1067                         prevPoint = *prevPoints.begin(); | 
| Chris@409 | 1068                         havePrevPoint = true; | 
| Chris@409 | 1069                     } | 
| Chris@45 | 1070                 } | 
| Chris@45 | 1071 | 
| Chris@45 | 1072                 m_labeller->setSampleRate(sodm->getSampleRate()); | 
| Chris@45 | 1073 | 
| Chris@352 | 1074                 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { | 
| Chris@45 | 1075                     command->deletePoint(prevPoint); | 
| Chris@45 | 1076                 } | 
| Chris@45 | 1077 | 
| Chris@45 | 1078                 m_labeller->label<SparseOneDimensionalModel::Point> | 
| Chris@45 | 1079                     (point, havePrevPoint ? &prevPoint : 0); | 
| Chris@45 | 1080 | 
| Chris@352 | 1081                 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { | 
| Chris@45 | 1082                     command->addPoint(prevPoint); | 
| Chris@45 | 1083                 } | 
| Chris@45 | 1084             } | 
| Chris@45 | 1085 | 
| Chris@45 | 1086             command->addPoint(point); | 
| Chris@45 | 1087 | 
| Chris@45 | 1088             command->setName(tr("Add Point at %1 s") | 
| Chris@45 | 1089                              .arg(RealTime::frame2RealTime | 
| Chris@45 | 1090                                   (frame, | 
| Chris@45 | 1091                                    sodm->getSampleRate()) | 
| Chris@45 | 1092                                   .toText(false).c_str())); | 
| Chris@45 | 1093 | 
| Chris@108 | 1094             Command *c = command->finish(); | 
| Chris@108 | 1095             if (c) CommandHistory::getInstance()->addCommand(c, false); | 
| Chris@45 | 1096         } | 
| Chris@45 | 1097     } | 
| Chris@45 | 1098 } | 
| Chris@45 | 1099 | 
| Chris@45 | 1100 void | 
| Chris@184 | 1101 MainWindowBase::insertItemAtSelection() | 
| Chris@184 | 1102 { | 
| Chris@184 | 1103     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@184 | 1104     for (MultiSelection::SelectionList::iterator i = selections.begin(); | 
| Chris@184 | 1105          i != selections.end(); ++i) { | 
| Chris@435 | 1106         sv_frame_t start = i->getStartFrame(); | 
| Chris@435 | 1107         sv_frame_t end = i->getEndFrame(); | 
| Chris@184 | 1108         if (start < end) { | 
| Chris@184 | 1109             insertItemAt(start, end - start); | 
| Chris@184 | 1110         } | 
| Chris@184 | 1111     } | 
| Chris@184 | 1112 } | 
| Chris@184 | 1113 | 
| Chris@184 | 1114 void | 
| Chris@435 | 1115 MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration) | 
| Chris@184 | 1116 { | 
| Chris@184 | 1117     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@184 | 1118     if (!pane) { | 
| Chris@184 | 1119         return; | 
| Chris@184 | 1120     } | 
| Chris@184 | 1121 | 
| Chris@184 | 1122     // ugh! | 
| Chris@184 | 1123 | 
| Chris@435 | 1124     sv_frame_t alignedStart = pane->alignFromReference(frame); | 
| Chris@435 | 1125     sv_frame_t alignedEnd = pane->alignFromReference(frame + duration); | 
| Chris@184 | 1126     if (alignedStart >= alignedEnd) return; | 
| Chris@435 | 1127     sv_frame_t alignedDuration = alignedEnd - alignedStart; | 
| Chris@184 | 1128 | 
| Chris@184 | 1129     Command *c = 0; | 
| Chris@184 | 1130 | 
| Chris@184 | 1131     QString name = tr("Add Item at %1 s") | 
| Chris@184 | 1132         .arg(RealTime::frame2RealTime | 
| Chris@184 | 1133              (alignedStart, | 
| Chris@184 | 1134               getMainModel()->getSampleRate()) | 
| Chris@184 | 1135              .toText(false).c_str()); | 
| Chris@184 | 1136 | 
| Chris@184 | 1137     Layer *layer = pane->getSelectedLayer(); | 
| Chris@184 | 1138     if (!layer) return; | 
| Chris@184 | 1139 | 
| Chris@184 | 1140     RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel()); | 
| Chris@184 | 1141     if (rm) { | 
| Chris@184 | 1142         RegionModel::Point point(alignedStart, | 
| Chris@185 | 1143                                  rm->getValueMaximum() + 1, | 
| Chris@184 | 1144                                  alignedDuration, | 
| Chris@184 | 1145                                  ""); | 
| Chris@184 | 1146         RegionModel::EditCommand *command = | 
| Chris@184 | 1147             new RegionModel::EditCommand(rm, tr("Add Point")); | 
| Chris@184 | 1148         command->addPoint(point); | 
| Chris@184 | 1149         command->setName(name); | 
| Chris@184 | 1150         c = command->finish(); | 
| Chris@184 | 1151     } | 
| Chris@184 | 1152 | 
| Chris@184 | 1153     if (c) { | 
| Chris@184 | 1154         CommandHistory::getInstance()->addCommand(c, false); | 
| Chris@184 | 1155         return; | 
| Chris@184 | 1156     } | 
| Chris@184 | 1157 | 
| Chris@184 | 1158     NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel()); | 
| Chris@184 | 1159     if (nm) { | 
| Chris@184 | 1160         NoteModel::Point point(alignedStart, | 
| Chris@409 | 1161                                nm->getValueMinimum(), | 
| Chris@184 | 1162                                alignedDuration, | 
| Chris@184 | 1163                                1.f, | 
| Chris@184 | 1164                                ""); | 
| Chris@184 | 1165         NoteModel::EditCommand *command = | 
| Chris@184 | 1166             new NoteModel::EditCommand(nm, tr("Add Point")); | 
| Chris@184 | 1167         command->addPoint(point); | 
| Chris@184 | 1168         command->setName(name); | 
| Chris@184 | 1169         c = command->finish(); | 
| Chris@184 | 1170     } | 
| Chris@184 | 1171 | 
| Chris@184 | 1172     if (c) { | 
| Chris@184 | 1173         CommandHistory::getInstance()->addCommand(c, false); | 
| Chris@184 | 1174         return; | 
| Chris@184 | 1175     } | 
| matthiasm@267 | 1176 | 
| matthiasm@268 | 1177     FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel()); | 
| matthiasm@268 | 1178     if (fnm) { | 
| matthiasm@267 | 1179         FlexiNoteModel::Point point(alignedStart, | 
| Chris@409 | 1180                                     fnm->getValueMinimum(), | 
| Chris@409 | 1181                                     alignedDuration, | 
| Chris@409 | 1182                                     1.f, | 
| Chris@409 | 1183                                     ""); | 
| matthiasm@267 | 1184         FlexiNoteModel::EditCommand *command = | 
| matthiasm@268 | 1185             new FlexiNoteModel::EditCommand(fnm, tr("Add Point")); | 
| matthiasm@267 | 1186         command->addPoint(point); | 
| matthiasm@267 | 1187         command->setName(name); | 
| matthiasm@267 | 1188         c = command->finish(); | 
| matthiasm@267 | 1189     } | 
| Chris@409 | 1190 | 
| matthiasm@267 | 1191     if (c) { | 
| matthiasm@267 | 1192         CommandHistory::getInstance()->addCommand(c, false); | 
| matthiasm@267 | 1193         return; | 
| matthiasm@267 | 1194     } | 
| Chris@184 | 1195 } | 
| Chris@184 | 1196 | 
| Chris@184 | 1197 void | 
| Chris@45 | 1198 MainWindowBase::renumberInstants() | 
| Chris@45 | 1199 { | 
| Chris@45 | 1200     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1201     if (!pane) return; | 
| Chris@45 | 1202 | 
| Chris@45 | 1203     Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer()); | 
| Chris@45 | 1204     if (!layer) return; | 
| Chris@45 | 1205 | 
| Chris@45 | 1206     MultiSelection ms(m_viewManager->getSelection()); | 
| Chris@45 | 1207 | 
| Chris@45 | 1208     Model *model = layer->getModel(); | 
| Chris@45 | 1209     SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | 
| Chris@45 | 1210         (model); | 
| Chris@45 | 1211     if (!sodm) return; | 
| Chris@45 | 1212 | 
| Chris@45 | 1213     if (!m_labeller) return; | 
| Chris@45 | 1214 | 
| Chris@45 | 1215     Labeller labeller(*m_labeller); | 
| Chris@45 | 1216     labeller.setSampleRate(sodm->getSampleRate()); | 
| Chris@45 | 1217 | 
| Chris@45 | 1218     // This uses a command | 
| Chris@45 | 1219 | 
| Chris@45 | 1220     labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); | 
| Chris@45 | 1221 } | 
| Chris@45 | 1222 | 
| Chris@45 | 1223 MainWindowBase::FileOpenStatus | 
| Chris@373 | 1224 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode) | 
| Chris@45 | 1225 { | 
| Chris@134 | 1226     ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this); | 
| Chris@134 | 1227     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); | 
| Chris@109 | 1228     return open(FileSource(fileOrUrl, &dialog), mode); | 
| Chris@45 | 1229 } | 
| Chris@45 | 1230 | 
| Chris@45 | 1231 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1232 MainWindowBase::open(FileSource source, AudioFileOpenMode mode) | 
| Chris@45 | 1233 { | 
| Chris@45 | 1234     FileOpenStatus status; | 
| Chris@45 | 1235 | 
| Chris@45 | 1236     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1237     source.waitForData(); | 
| Chris@45 | 1238 | 
| Chris@45 | 1239     bool canImportLayer = (getMainModel() != 0 && | 
| Chris@45 | 1240                            m_paneStack != 0 && | 
| Chris@45 | 1241                            m_paneStack->getCurrentPane() != 0); | 
| Chris@45 | 1242 | 
| Chris@152 | 1243     bool rdf = (source.getExtension().toLower() == "rdf" || | 
| Chris@152 | 1244                 source.getExtension().toLower() == "n3" || | 
| Chris@152 | 1245                 source.getExtension().toLower() == "ttl"); | 
| Chris@152 | 1246 | 
| Chris@152 | 1247     bool audio = AudioFileReaderFactory::getKnownExtensions().contains | 
| Chris@152 | 1248         (source.getExtension().toLower()); | 
| Chris@145 | 1249 | 
| Chris@145 | 1250     bool rdfSession = false; | 
| Chris@145 | 1251     if (rdf) { | 
| Chris@145 | 1252         RDFImporter::RDFDocumentType rdfType = | 
| Chris@145 | 1253             RDFImporter::identifyDocumentType | 
| Chris@145 | 1254             (QUrl::fromLocalFile(source.getLocalFilename()).toString()); | 
| Chris@145 | 1255         if (rdfType == RDFImporter::AudioRefAndAnnotations || | 
| Chris@145 | 1256             rdfType == RDFImporter::AudioRef) { | 
| Chris@145 | 1257             rdfSession = true; | 
| Chris@145 | 1258         } else if (rdfType == RDFImporter::NotRDF) { | 
| Chris@145 | 1259             rdf = false; | 
| Chris@145 | 1260         } | 
| Chris@145 | 1261     } | 
| Chris@145 | 1262 | 
| Chris@145 | 1263     if (rdf) { | 
| Chris@145 | 1264         if (rdfSession) { | 
| Chris@147 | 1265             bool cancel = false; | 
| Chris@147 | 1266             if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) { | 
| Chris@145 | 1267                 return openSession(source); | 
| Chris@147 | 1268             } else if (cancel) { | 
| Chris@147 | 1269                 return FileOpenCancelled; | 
| Chris@145 | 1270             } else { | 
| Chris@145 | 1271                 return openLayer(source); | 
| Chris@145 | 1272             } | 
| Chris@145 | 1273         } else { | 
| Chris@145 | 1274             if ((status = openSession(source)) != FileOpenFailed) { | 
| Chris@145 | 1275                 return status; | 
| Chris@145 | 1276             } else if (!canImportLayer) { | 
| Chris@145 | 1277                 return FileOpenWrongMode; | 
| Chris@145 | 1278             } else if ((status = openLayer(source)) != FileOpenFailed) { | 
| Chris@145 | 1279                 return status; | 
| Chris@145 | 1280             } else { | 
| Chris@145 | 1281                 return FileOpenFailed; | 
| Chris@145 | 1282             } | 
| Chris@145 | 1283         } | 
| Chris@145 | 1284     } | 
| Chris@145 | 1285 | 
| Chris@152 | 1286     if (audio && (status = openAudio(source, mode)) != FileOpenFailed) { | 
| Chris@45 | 1287         return status; | 
| Chris@45 | 1288     } else if ((status = openSession(source)) != FileOpenFailed) { | 
| Chris@45 | 1289 	return status; | 
| Chris@45 | 1290     } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) { | 
| Chris@45 | 1291         return status; | 
| Chris@45 | 1292     } else if (!canImportLayer) { | 
| Chris@45 | 1293         return FileOpenWrongMode; | 
| Chris@45 | 1294     } else if ((status = openImage(source)) != FileOpenFailed) { | 
| Chris@45 | 1295         return status; | 
| Chris@45 | 1296     } else if ((status = openLayer(source)) != FileOpenFailed) { | 
| Chris@45 | 1297         return status; | 
| Chris@45 | 1298     } else { | 
| Chris@45 | 1299 	return FileOpenFailed; | 
| Chris@45 | 1300     } | 
| Chris@45 | 1301 } | 
| Chris@45 | 1302 | 
| Chris@45 | 1303 MainWindowBase::FileOpenStatus | 
| Chris@227 | 1304 MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode, | 
| Chris@227 | 1305                           QString templateName) | 
| Chris@45 | 1306 { | 
| Chris@386 | 1307     SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl; | 
| Chris@45 | 1308 | 
| Chris@222 | 1309     if (templateName == "") { | 
| Chris@231 | 1310         templateName = getDefaultSessionTemplate(); | 
| Chris@222 | 1311     } | 
| Chris@220 | 1312 | 
| Chris@374 | 1313 //    cerr << "template is: \"" << templateName << "\"" << endl; | 
| Chris@223 | 1314 | 
| Chris@413 | 1315     if (!source.isAvailable()) { | 
| Chris@413 | 1316         if (source.wasCancelled()) { | 
| Chris@413 | 1317             return FileOpenCancelled; | 
| Chris@413 | 1318         } else { | 
| Chris@413 | 1319             return FileOpenFailed; | 
| Chris@413 | 1320         } | 
| Chris@413 | 1321     } | 
| Chris@413 | 1322 | 
| Chris@45 | 1323     source.waitForData(); | 
| Chris@45 | 1324 | 
| Chris@45 | 1325     m_openingAudioFile = true; | 
| Chris@45 | 1326 | 
| Chris@435 | 1327     sv_samplerate_t rate = 0; | 
| Chris@45 | 1328 | 
| Chris@360 | 1329     if (Preferences::getInstance()->getFixedSampleRate() != 0) { | 
| Chris@360 | 1330         rate = Preferences::getInstance()->getFixedSampleRate(); | 
| Chris@360 | 1331     } else if (Preferences::getInstance()->getResampleOnLoad()) { | 
| Chris@45 | 1332         rate = m_playSource->getSourceSampleRate(); | 
| Chris@45 | 1333     } | 
| Chris@45 | 1334 | 
| Chris@45 | 1335     WaveFileModel *newModel = new WaveFileModel(source, rate); | 
| Chris@45 | 1336 | 
| Chris@45 | 1337     if (!newModel->isOK()) { | 
| Chris@45 | 1338 	delete newModel; | 
| Chris@45 | 1339         m_openingAudioFile = false; | 
| Chris@413 | 1340         if (source.wasCancelled()) { | 
| Chris@413 | 1341             return FileOpenCancelled; | 
| Chris@413 | 1342         } else { | 
| Chris@413 | 1343             return FileOpenFailed; | 
| Chris@413 | 1344         } | 
| Chris@45 | 1345     } | 
| Chris@45 | 1346 | 
| Chris@293 | 1347 //    cerr << "mode = " << mode << endl; | 
| Chris@45 | 1348 | 
| Chris@45 | 1349     if (mode == AskUser) { | 
| Chris@45 | 1350         if (getMainModel()) { | 
| Chris@45 | 1351 | 
| Chris@147 | 1352             QSettings settings; | 
| Chris@147 | 1353             settings.beginGroup("MainWindow"); | 
| Chris@221 | 1354             int lastMode = settings.value("lastaudioopenmode", 0).toBool(); | 
| Chris@147 | 1355             settings.endGroup(); | 
| Chris@221 | 1356             int imode = 0; | 
| Chris@45 | 1357 | 
| Chris@45 | 1358             QStringList items; | 
| Chris@221 | 1359             items << tr("Close the current session and start a new one") | 
| Chris@221 | 1360                   << tr("Replace the main audio file in this session") | 
| Chris@221 | 1361                   << tr("Add the audio file to this session"); | 
| Chris@45 | 1362 | 
| Chris@45 | 1363             bool ok = false; | 
| Chris@45 | 1364             QString item = ListInputDialog::getItem | 
| Chris@45 | 1365                 (this, tr("Select target for import"), | 
| Chris@221 | 1366                  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?"), | 
| Chris@221 | 1367                  items, lastMode, &ok); | 
| Chris@45 | 1368 | 
| Chris@45 | 1369             if (!ok || item.isEmpty()) { | 
| Chris@45 | 1370                 delete newModel; | 
| Chris@45 | 1371                 m_openingAudioFile = false; | 
| Chris@45 | 1372                 return FileOpenCancelled; | 
| Chris@45 | 1373             } | 
| Chris@45 | 1374 | 
| Chris@221 | 1375             for (int i = 0; i < items.size(); ++i) { | 
| Chris@221 | 1376                 if (item == items[i]) imode = i; | 
| Chris@221 | 1377             } | 
| Chris@221 | 1378 | 
| Chris@147 | 1379             settings.beginGroup("MainWindow"); | 
| Chris@221 | 1380             settings.setValue("lastaudioopenmode", imode); | 
| Chris@147 | 1381             settings.endGroup(); | 
| Chris@45 | 1382 | 
| Chris@221 | 1383             mode = (AudioFileOpenMode)imode; | 
| Chris@45 | 1384 | 
| Chris@45 | 1385         } else { | 
| Chris@221 | 1386             // no main model: make a new session | 
| Chris@221 | 1387             mode = ReplaceSession; | 
| Chris@45 | 1388         } | 
| Chris@45 | 1389     } | 
| Chris@45 | 1390 | 
| Chris@45 | 1391     if (mode == ReplaceCurrentPane) { | 
| Chris@45 | 1392 | 
| Chris@45 | 1393         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1394         if (pane) { | 
| Chris@45 | 1395             if (getMainModel()) { | 
| Chris@45 | 1396                 View::ModelSet models(pane->getModels()); | 
| Chris@45 | 1397                 if (models.find(getMainModel()) != models.end()) { | 
| Chris@221 | 1398                     // Current pane contains main model: replace that | 
| Chris@45 | 1399                     mode = ReplaceMainModel; | 
| Chris@45 | 1400                 } | 
| Chris@221 | 1401                 // Otherwise the current pane has a non-default model, | 
| Chris@221 | 1402                 // which we will deal with later | 
| Chris@45 | 1403             } else { | 
| Chris@221 | 1404                 // We have no main model, so start a new session with | 
| Chris@221 | 1405                 // optional template | 
| Chris@221 | 1406                 mode = ReplaceSession; | 
| Chris@45 | 1407             } | 
| Chris@45 | 1408         } else { | 
| Chris@221 | 1409             // We seem to have no current pane!  Oh well | 
| Chris@45 | 1410             mode = CreateAdditionalModel; | 
| Chris@45 | 1411         } | 
| Chris@45 | 1412     } | 
| Chris@45 | 1413 | 
| Chris@45 | 1414     if (mode == CreateAdditionalModel && !getMainModel()) { | 
| Chris@386 | 1415         SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl; | 
| Chris@221 | 1416         mode = ReplaceSession; | 
| Chris@221 | 1417     } | 
| Chris@221 | 1418 | 
| Chris@221 | 1419     bool loadedTemplate = false; | 
| Chris@221 | 1420 | 
| Chris@221 | 1421     if (mode == ReplaceSession) { | 
| Chris@258 | 1422 | 
| Chris@258 | 1423         if (!checkSaveModified()) return FileOpenCancelled; | 
| Chris@258 | 1424 | 
| Chris@386 | 1425         SVDEBUG << "SV looking for template " << templateName << endl; | 
| Chris@230 | 1426         if (templateName != "") { | 
| Chris@230 | 1427             FileOpenStatus tplStatus = openSessionTemplate(templateName); | 
| Chris@258 | 1428             if (tplStatus == FileOpenCancelled) { | 
| Chris@293 | 1429                 cerr << "Template load cancelled" << endl; | 
| Chris@258 | 1430                 return FileOpenCancelled; | 
| Chris@258 | 1431             } | 
| Chris@230 | 1432             if (tplStatus != FileOpenFailed) { | 
| Chris@293 | 1433                 cerr << "Template load succeeded" << endl; | 
| Chris@230 | 1434                 loadedTemplate = true; | 
| Chris@221 | 1435             } | 
| Chris@221 | 1436         } | 
| Chris@221 | 1437 | 
| Chris@221 | 1438         if (!loadedTemplate) { | 
| Chris@386 | 1439             SVDEBUG << "No template found: closing session, creating new empty document" << endl; | 
| Chris@221 | 1440             closeSession(); | 
| Chris@221 | 1441             createDocument(); | 
| Chris@221 | 1442         } | 
| Chris@221 | 1443 | 
| Chris@386 | 1444         SVDEBUG << "Now switching to ReplaceMainModel mode" << endl; | 
| Chris@45 | 1445         mode = ReplaceMainModel; | 
| Chris@45 | 1446     } | 
| Chris@45 | 1447 | 
| Chris@164 | 1448     emit activity(tr("Import audio file \"%1\"").arg(source.getLocation())); | 
| Chris@164 | 1449 | 
| Chris@45 | 1450     if (mode == ReplaceMainModel) { | 
| Chris@45 | 1451 | 
| Chris@45 | 1452         Model *prevMain = getMainModel(); | 
| Chris@45 | 1453         if (prevMain) { | 
| Chris@45 | 1454             m_playSource->removeModel(prevMain); | 
| Chris@108 | 1455             PlayParameterRepository::getInstance()->removePlayable(prevMain); | 
| Chris@45 | 1456         } | 
| Chris@108 | 1457         PlayParameterRepository::getInstance()->addPlayable(newModel); | 
| Chris@45 | 1458 | 
| Chris@248 | 1459         SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl; | 
| Chris@248 | 1460 | 
| Chris@45 | 1461 	m_document->setMainModel(newModel); | 
| Chris@45 | 1462 | 
| Chris@45 | 1463 	setupMenus(); | 
| Chris@45 | 1464 | 
| dan@210 | 1465 	if (loadedTemplate || (m_sessionFile == "")) { | 
| Chris@45 | 1466             //!!! shouldn't be dealing directly with title from here -- call a method | 
| Chris@57 | 1467 	    setWindowTitle(tr("%1: %2") | 
| Chris@57 | 1468                            .arg(QApplication::applicationName()) | 
| Chris@45 | 1469                            .arg(source.getLocation())); | 
| Chris@45 | 1470 	    CommandHistory::getInstance()->clear(); | 
| Chris@45 | 1471 	    CommandHistory::getInstance()->documentSaved(); | 
| Chris@45 | 1472 	    m_documentModified = false; | 
| Chris@45 | 1473 	} else { | 
| Chris@57 | 1474 	    setWindowTitle(tr("%1: %2 [%3]") | 
| Chris@57 | 1475                            .arg(QApplication::applicationName()) | 
| Chris@45 | 1476 			   .arg(QFileInfo(m_sessionFile).fileName()) | 
| Chris@45 | 1477 			   .arg(source.getLocation())); | 
| Chris@45 | 1478 	    if (m_documentModified) { | 
| Chris@45 | 1479 		m_documentModified = false; | 
| Chris@45 | 1480 		documentModified(); // so as to restore "(modified)" window title | 
| Chris@45 | 1481 	    } | 
| Chris@45 | 1482 	} | 
| Chris@45 | 1483 | 
| Chris@45 | 1484         if (!source.isRemote()) m_audioFile = source.getLocalFilename(); | 
| Chris@45 | 1485 | 
| Chris@45 | 1486     } else if (mode == CreateAdditionalModel) { | 
| Chris@45 | 1487 | 
| Chris@45 | 1488 	CommandHistory::getInstance()->startCompoundOperation | 
| Chris@219 | 1489 	    (tr("Import \"%1\"").arg(source.getBasename()), true); | 
| Chris@45 | 1490 | 
| Chris@45 | 1491 	m_document->addImportedModel(newModel); | 
| Chris@45 | 1492 | 
| Chris@45 | 1493 	AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@45 | 1494 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@45 | 1495 | 
| Chris@45 | 1496 	Pane *pane = command->getPane(); | 
| Chris@45 | 1497 | 
| Chris@47 | 1498         if (m_timeRulerLayer) { | 
| Chris@47 | 1499             m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@47 | 1500         } | 
| Chris@45 | 1501 | 
| Chris@45 | 1502 	Layer *newLayer = m_document->createImportedLayer(newModel); | 
| Chris@45 | 1503 | 
| Chris@45 | 1504 	if (newLayer) { | 
| Chris@45 | 1505 	    m_document->addLayerToView(pane, newLayer); | 
| Chris@45 | 1506 	} | 
| Chris@45 | 1507 | 
| Chris@45 | 1508 	CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 1509 | 
| Chris@45 | 1510     } else if (mode == ReplaceCurrentPane) { | 
| Chris@45 | 1511 | 
| Chris@45 | 1512         // We know there is a current pane, otherwise we would have | 
| Chris@45 | 1513         // reset the mode to CreateAdditionalModel above; and we know | 
| Chris@45 | 1514         // the current pane does not contain the main model, otherwise | 
| Chris@45 | 1515         // we would have reset it to ReplaceMainModel.  But we don't | 
| Chris@45 | 1516         // know whether the pane contains a waveform model at all. | 
| Chris@45 | 1517 | 
| Chris@45 | 1518         Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1519         Layer *replace = 0; | 
| Chris@45 | 1520 | 
| Chris@45 | 1521         for (int i = 0; i < pane->getLayerCount(); ++i) { | 
| Chris@45 | 1522             Layer *layer = pane->getLayer(i); | 
| Chris@45 | 1523             if (dynamic_cast<WaveformLayer *>(layer)) { | 
| Chris@45 | 1524                 replace = layer; | 
| Chris@45 | 1525                 break; | 
| Chris@45 | 1526             } | 
| Chris@45 | 1527         } | 
| Chris@45 | 1528 | 
| Chris@45 | 1529 	CommandHistory::getInstance()->startCompoundOperation | 
| Chris@219 | 1530 	    (tr("Import \"%1\"").arg(source.getBasename()), true); | 
| Chris@45 | 1531 | 
| Chris@45 | 1532 	m_document->addImportedModel(newModel); | 
| Chris@45 | 1533 | 
| Chris@45 | 1534         if (replace) { | 
| Chris@45 | 1535             m_document->removeLayerFromView(pane, replace); | 
| Chris@45 | 1536         } | 
| Chris@45 | 1537 | 
| Chris@45 | 1538 	Layer *newLayer = m_document->createImportedLayer(newModel); | 
| Chris@45 | 1539 | 
| Chris@45 | 1540 	if (newLayer) { | 
| Chris@45 | 1541 	    m_document->addLayerToView(pane, newLayer); | 
| Chris@45 | 1542 	} | 
| Chris@45 | 1543 | 
| Chris@45 | 1544 	CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 1545     } | 
| Chris@45 | 1546 | 
| Chris@45 | 1547     updateMenuStates(); | 
| Chris@45 | 1548     m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1549     if (!source.isRemote()) { | 
| Chris@45 | 1550         // for file dialog | 
| Chris@45 | 1551         registerLastOpenedFilePath(FileFinder::AudioFile, | 
| Chris@45 | 1552                                    source.getLocalFilename()); | 
| Chris@45 | 1553     } | 
| Chris@45 | 1554     m_openingAudioFile = false; | 
| Chris@45 | 1555 | 
| Chris@45 | 1556     currentPaneChanged(m_paneStack->getCurrentPane()); | 
| Chris@45 | 1557 | 
| Chris@342 | 1558     emit audioFileLoaded(); | 
| Chris@342 | 1559 | 
| Chris@45 | 1560     return FileOpenSucceeded; | 
| Chris@45 | 1561 } | 
| Chris@45 | 1562 | 
| Chris@45 | 1563 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1564 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode) | 
| Chris@45 | 1565 { | 
| Chris@233 | 1566     SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl; | 
| Chris@135 | 1567 | 
| Chris@45 | 1568     std::set<QString> extensions; | 
| Chris@45 | 1569     PlaylistFileReader::getSupportedExtensions(extensions); | 
| Chris@152 | 1570     QString extension = source.getExtension().toLower(); | 
| Chris@45 | 1571     if (extensions.find(extension) == extensions.end()) return FileOpenFailed; | 
| Chris@45 | 1572 | 
| Chris@45 | 1573     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1574     source.waitForData(); | 
| Chris@45 | 1575 | 
| Chris@45 | 1576     PlaylistFileReader reader(source.getLocalFilename()); | 
| Chris@45 | 1577     if (!reader.isOK()) return FileOpenFailed; | 
| Chris@45 | 1578 | 
| Chris@45 | 1579     PlaylistFileReader::Playlist playlist = reader.load(); | 
| Chris@45 | 1580 | 
| Chris@45 | 1581     bool someSuccess = false; | 
| Chris@45 | 1582 | 
| Chris@45 | 1583     for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); | 
| Chris@45 | 1584          i != playlist.end(); ++i) { | 
| Chris@45 | 1585 | 
| Chris@134 | 1586         ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this); | 
| Chris@134 | 1587         connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); | 
| Chris@109 | 1588         FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode); | 
| Chris@45 | 1589 | 
| Chris@45 | 1590         if (status == FileOpenCancelled) { | 
| Chris@45 | 1591             return FileOpenCancelled; | 
| Chris@45 | 1592         } | 
| Chris@45 | 1593 | 
| Chris@45 | 1594         if (status == FileOpenSucceeded) { | 
| Chris@45 | 1595             someSuccess = true; | 
| Chris@45 | 1596             mode = CreateAdditionalModel; | 
| Chris@45 | 1597         } | 
| Chris@45 | 1598     } | 
| Chris@45 | 1599 | 
| Chris@45 | 1600     if (someSuccess) return FileOpenSucceeded; | 
| Chris@45 | 1601     else return FileOpenFailed; | 
| Chris@45 | 1602 } | 
| Chris@45 | 1603 | 
| Chris@45 | 1604 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1605 MainWindowBase::openLayer(FileSource source) | 
| Chris@45 | 1606 { | 
| Chris@233 | 1607     SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl; | 
| Chris@135 | 1608 | 
| Chris@45 | 1609     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1610 | 
| Chris@45 | 1611     if (!pane) { | 
| Chris@45 | 1612 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@293 | 1613 	cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl; | 
| Chris@45 | 1614 	return FileOpenWrongMode; | 
| Chris@45 | 1615     } | 
| Chris@45 | 1616 | 
| Chris@45 | 1617     if (!getMainModel()) { | 
| Chris@45 | 1618 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@293 | 1619 	cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl; | 
| Chris@45 | 1620 	return FileOpenWrongMode; | 
| Chris@45 | 1621     } | 
| Chris@45 | 1622 | 
| Chris@45 | 1623     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@45 | 1624     source.waitForData(); | 
| Chris@45 | 1625 | 
| Chris@45 | 1626     QString path = source.getLocalFilename(); | 
| Chris@45 | 1627 | 
| Chris@145 | 1628     RDFImporter::RDFDocumentType rdfType = | 
| Chris@145 | 1629         RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString()); | 
| Chris@145 | 1630 | 
| Chris@293 | 1631 //    cerr << "RDF type:  (in layer) " << (int) rdfType << endl; | 
| Chris@148 | 1632 | 
| Chris@145 | 1633     if (rdfType != RDFImporter::NotRDF) { | 
| Chris@145 | 1634 | 
| Chris@145 | 1635         return openLayersFromRDF(source); | 
| Chris@134 | 1636 | 
| Chris@152 | 1637     } else if (source.getExtension().toLower() == "svl" || | 
| Chris@152 | 1638                (source.getExtension().toLower() == "xml" && | 
| Chris@140 | 1639                 (SVFileReader::identifyXmlFile(source.getLocalFilename()) | 
| Chris@140 | 1640                  == SVFileReader::SVLayerFile))) { | 
| Chris@45 | 1641 | 
| Chris@45 | 1642         PaneCallback callback(this); | 
| Chris@45 | 1643         QFile file(path); | 
| Chris@45 | 1644 | 
| Chris@45 | 1645         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | 
| Chris@293 | 1646             cerr << "ERROR: MainWindowBase::openLayer(" | 
| Chris@294 | 1647                       << source.getLocation() | 
| Chris@293 | 1648                       << "): Failed to open file for reading" << endl; | 
| Chris@45 | 1649             return FileOpenFailed; | 
| Chris@45 | 1650         } | 
| Chris@45 | 1651 | 
| Chris@45 | 1652         SVFileReader reader(m_document, callback, source.getLocation()); | 
| Chris@79 | 1653         connect | 
| Chris@79 | 1654             (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), | 
| Chris@79 | 1655              this, SLOT(modelRegenerationFailed(QString, QString, QString))); | 
| Chris@79 | 1656         connect | 
| Chris@79 | 1657             (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@79 | 1658              this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@45 | 1659         reader.setCurrentPane(pane); | 
| Chris@45 | 1660 | 
| Chris@45 | 1661         QXmlInputSource inputSource(&file); | 
| Chris@45 | 1662         reader.parse(inputSource); | 
| Chris@45 | 1663 | 
| Chris@45 | 1664         if (!reader.isOK()) { | 
| Chris@293 | 1665             cerr << "ERROR: MainWindowBase::openLayer(" | 
| Chris@294 | 1666                       << source.getLocation() | 
| Chris@45 | 1667                       << "): Failed to read XML file: " | 
| Chris@293 | 1668                       << reader.getErrorString() << endl; | 
| Chris@45 | 1669             return FileOpenFailed; | 
| Chris@45 | 1670         } | 
| Chris@45 | 1671 | 
| Chris@164 | 1672         emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation())); | 
| Chris@164 | 1673 | 
| Chris@45 | 1674         m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1675 | 
| Chris@45 | 1676         if (!source.isRemote()) { | 
| Chris@45 | 1677             registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog | 
| Chris@45 | 1678         } | 
| Chris@45 | 1679 | 
| Chris@75 | 1680         return FileOpenSucceeded; | 
| Chris@75 | 1681 | 
| Chris@45 | 1682     } else { | 
| Chris@45 | 1683 | 
| Chris@45 | 1684         try { | 
| Chris@45 | 1685 | 
| Chris@109 | 1686             MIDIFileImportDialog midiDlg(this); | 
| Chris@109 | 1687 | 
| Chris@109 | 1688             Model *model = DataFileReaderFactory::loadNonCSV | 
| Chris@109 | 1689                 (path, &midiDlg, getMainModel()->getSampleRate()); | 
| Chris@45 | 1690 | 
| Chris@109 | 1691             if (!model) { | 
| Chris@196 | 1692                 CSVFormat format(path); | 
| Chris@196 | 1693                 format.setSampleRate(getMainModel()->getSampleRate()); | 
| Chris@196 | 1694                 CSVFormatDialog *dialog = new CSVFormatDialog(this, format); | 
| Chris@109 | 1695                 if (dialog->exec() == QDialog::Accepted) { | 
| Chris@109 | 1696                     model = DataFileReaderFactory::loadCSV | 
| Chris@109 | 1697                         (path, dialog->getFormat(), | 
| Chris@109 | 1698                          getMainModel()->getSampleRate()); | 
| Chris@109 | 1699                 } | 
| Chris@109 | 1700             } | 
| Chris@109 | 1701 | 
| Chris@45 | 1702             if (model) { | 
| Chris@45 | 1703 | 
| Chris@233 | 1704                 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl; | 
| Chris@45 | 1705 | 
| Chris@164 | 1706                 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation())); | 
| Chris@164 | 1707 | 
| Chris@45 | 1708                 Layer *newLayer = m_document->createImportedLayer(model); | 
| Chris@45 | 1709 | 
| Chris@45 | 1710                 if (newLayer) { | 
| Chris@45 | 1711 | 
| Chris@45 | 1712                     m_document->addLayerToView(pane, newLayer); | 
| Chris@88 | 1713                     m_paneStack->setCurrentLayer(pane, newLayer); | 
| Chris@88 | 1714 | 
| Chris@45 | 1715                     m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1716 | 
| Chris@45 | 1717                     if (!source.isRemote()) { | 
| Chris@45 | 1718                         registerLastOpenedFilePath | 
| Chris@45 | 1719                             (FileFinder::LayerFile, | 
| Chris@45 | 1720                              path); // for file dialog | 
| Chris@45 | 1721                     } | 
| Chris@88 | 1722 | 
| Chris@45 | 1723                     return FileOpenSucceeded; | 
| Chris@45 | 1724                 } | 
| Chris@45 | 1725             } | 
| Chris@45 | 1726         } catch (DataFileReaderFactory::Exception e) { | 
| Chris@45 | 1727             if (e == DataFileReaderFactory::ImportCancelled) { | 
| Chris@45 | 1728                 return FileOpenCancelled; | 
| Chris@45 | 1729             } | 
| Chris@45 | 1730         } | 
| Chris@45 | 1731     } | 
| Chris@45 | 1732 | 
| Chris@45 | 1733     return FileOpenFailed; | 
| Chris@45 | 1734 } | 
| Chris@45 | 1735 | 
| Chris@45 | 1736 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1737 MainWindowBase::openImage(FileSource source) | 
| Chris@45 | 1738 { | 
| Chris@233 | 1739     SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl; | 
| Chris@135 | 1740 | 
| Chris@45 | 1741     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 1742 | 
| Chris@45 | 1743     if (!pane) { | 
| Chris@45 | 1744 	// shouldn't happen, as the menu action should have been disabled | 
| Chris@293 | 1745 	cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl; | 
| Chris@45 | 1746 	return FileOpenWrongMode; | 
| Chris@45 | 1747     } | 
| Chris@45 | 1748 | 
| Chris@45 | 1749     if (!m_document->getMainModel()) { | 
| Chris@45 | 1750         return FileOpenWrongMode; | 
| Chris@45 | 1751     } | 
| Chris@45 | 1752 | 
| Chris@45 | 1753     bool newLayer = false; | 
| Chris@45 | 1754 | 
| Chris@45 | 1755     ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer()); | 
| Chris@45 | 1756     if (!il) { | 
| Chris@45 | 1757         for (int i = pane->getLayerCount()-1; i >= 0; --i) { | 
| Chris@45 | 1758             il = dynamic_cast<ImageLayer *>(pane->getLayer(i)); | 
| Chris@45 | 1759             if (il) break; | 
| Chris@45 | 1760         } | 
| Chris@45 | 1761     } | 
| Chris@45 | 1762     if (!il) { | 
| Chris@45 | 1763         il = dynamic_cast<ImageLayer *> | 
| Chris@45 | 1764             (m_document->createEmptyLayer(LayerFactory::Image)); | 
| Chris@45 | 1765         if (!il) return FileOpenFailed; | 
| Chris@45 | 1766         newLayer = true; | 
| Chris@45 | 1767     } | 
| Chris@45 | 1768 | 
| Chris@45 | 1769     // We don't put the image file in Recent Files | 
| Chris@45 | 1770 | 
| Chris@293 | 1771     cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl; | 
| Chris@45 | 1772 | 
| Chris@45 | 1773     if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) { | 
| Chris@45 | 1774         if (newLayer) { | 
| Chris@52 | 1775             m_document->deleteLayer(il); // also releases its model | 
| Chris@45 | 1776         } | 
| Chris@45 | 1777         return FileOpenFailed; | 
| Chris@45 | 1778     } else { | 
| Chris@45 | 1779         if (newLayer) { | 
| Chris@45 | 1780             m_document->addLayerToView(pane, il); | 
| Chris@45 | 1781         } | 
| Chris@45 | 1782         m_paneStack->setCurrentLayer(pane, il); | 
| Chris@45 | 1783     } | 
| Chris@45 | 1784 | 
| Chris@45 | 1785     return FileOpenSucceeded; | 
| Chris@45 | 1786 } | 
| Chris@45 | 1787 | 
| Chris@45 | 1788 MainWindowBase::FileOpenStatus | 
| Chris@373 | 1789 MainWindowBase::openSessionPath(QString fileOrUrl) | 
| Chris@45 | 1790 { | 
| Chris@134 | 1791     ProgressDialog dialog(tr("Opening session..."), true, 2000, this); | 
| Chris@134 | 1792     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); | 
| Chris@109 | 1793     return openSession(FileSource(fileOrUrl, &dialog)); | 
| Chris@45 | 1794 } | 
| Chris@45 | 1795 | 
| Chris@45 | 1796 MainWindowBase::FileOpenStatus | 
| Chris@45 | 1797 MainWindowBase::openSession(FileSource source) | 
| Chris@45 | 1798 { | 
| Chris@233 | 1799     SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl; | 
| Chris@135 | 1800 | 
| Chris@45 | 1801     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@145 | 1802     source.waitForData(); | 
| Chris@141 | 1803 | 
| Chris@341 | 1804     QString sessionExt = | 
| Chris@341 | 1805         InteractiveFileFinder::getInstance()->getApplicationSessionExtension(); | 
| Chris@341 | 1806 | 
| Chris@341 | 1807     if (source.getExtension().toLower() != sessionExt) { | 
| Chris@145 | 1808 | 
| Chris@145 | 1809         RDFImporter::RDFDocumentType rdfType = | 
| Chris@145 | 1810             RDFImporter::identifyDocumentType | 
| Chris@145 | 1811             (QUrl::fromLocalFile(source.getLocalFilename()).toString()); | 
| Chris@145 | 1812 | 
| Chris@293 | 1813 //        cerr << "RDF type: " << (int)rdfType << endl; | 
| Chris@148 | 1814 | 
| Chris@145 | 1815         if (rdfType == RDFImporter::AudioRefAndAnnotations || | 
| Chris@145 | 1816             rdfType == RDFImporter::AudioRef) { | 
| Chris@145 | 1817             return openSessionFromRDF(source); | 
| Chris@145 | 1818         } else if (rdfType != RDFImporter::NotRDF) { | 
| Chris@145 | 1819             return FileOpenFailed; | 
| Chris@145 | 1820         } | 
| Chris@145 | 1821 | 
| Chris@152 | 1822         if (source.getExtension().toLower() == "xml") { | 
| Chris@140 | 1823             if (SVFileReader::identifyXmlFile(source.getLocalFilename()) == | 
| Chris@140 | 1824                 SVFileReader::SVSessionFile) { | 
| Chris@293 | 1825                 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl; | 
| Chris@140 | 1826             } else { | 
| Chris@140 | 1827                 return FileOpenFailed; | 
| Chris@140 | 1828             } | 
| Chris@140 | 1829         } else { | 
| Chris@140 | 1830             return FileOpenFailed; | 
| Chris@140 | 1831         } | 
| Chris@140 | 1832     } | 
| Chris@45 | 1833 | 
| Chris@140 | 1834     QXmlInputSource *inputSource = 0; | 
| Chris@140 | 1835     BZipFileDevice *bzFile = 0; | 
| Chris@140 | 1836     QFile *rawFile = 0; | 
| Chris@140 | 1837 | 
| Chris@341 | 1838     if (source.getExtension().toLower() == sessionExt) { | 
| Chris@140 | 1839         bzFile = new BZipFileDevice(source.getLocalFilename()); | 
| Chris@140 | 1840         if (!bzFile->open(QIODevice::ReadOnly)) { | 
| Chris@140 | 1841             delete bzFile; | 
| Chris@140 | 1842             return FileOpenFailed; | 
| Chris@140 | 1843         } | 
| Chris@140 | 1844         inputSource = new QXmlInputSource(bzFile); | 
| Chris@140 | 1845     } else { | 
| Chris@140 | 1846         rawFile = new QFile(source.getLocalFilename()); | 
| Chris@140 | 1847         inputSource = new QXmlInputSource(rawFile); | 
| Chris@140 | 1848     } | 
| Chris@140 | 1849 | 
| Chris@140 | 1850     if (!checkSaveModified()) { | 
| Chris@140 | 1851         if (bzFile) bzFile->close(); | 
| Chris@140 | 1852         delete inputSource; | 
| Chris@140 | 1853         delete bzFile; | 
| Chris@140 | 1854         delete rawFile; | 
| Chris@140 | 1855         return FileOpenCancelled; | 
| Chris@140 | 1856     } | 
| Chris@45 | 1857 | 
| Chris@45 | 1858     QString error; | 
| Chris@45 | 1859     closeSession(); | 
| Chris@45 | 1860     createDocument(); | 
| Chris@45 | 1861 | 
| Chris@45 | 1862     PaneCallback callback(this); | 
| Chris@45 | 1863     m_viewManager->clearSelections(); | 
| Chris@45 | 1864 | 
| Chris@45 | 1865     SVFileReader reader(m_document, callback, source.getLocation()); | 
| Chris@79 | 1866     connect | 
| Chris@79 | 1867         (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), | 
| Chris@79 | 1868          this, SLOT(modelRegenerationFailed(QString, QString, QString))); | 
| Chris@79 | 1869     connect | 
| Chris@79 | 1870         (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@79 | 1871          this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@140 | 1872 | 
| Chris@140 | 1873     reader.parse(*inputSource); | 
| Chris@45 | 1874 | 
| Chris@45 | 1875     if (!reader.isOK()) { | 
| Chris@45 | 1876         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); | 
| Chris@45 | 1877     } | 
| Chris@45 | 1878 | 
| Chris@140 | 1879     if (bzFile) bzFile->close(); | 
| Chris@140 | 1880 | 
| Chris@140 | 1881     delete inputSource; | 
| Chris@140 | 1882     delete bzFile; | 
| Chris@140 | 1883     delete rawFile; | 
| Chris@45 | 1884 | 
| Chris@45 | 1885     bool ok = (error == ""); | 
| Chris@45 | 1886 | 
| Chris@45 | 1887     if (ok) { | 
| Chris@45 | 1888 | 
| Chris@164 | 1889         emit activity(tr("Import session file \"%1\"").arg(source.getLocation())); | 
| Chris@164 | 1890 | 
| Chris@57 | 1891 	setWindowTitle(tr("%1: %2") | 
| Chris@57 | 1892                        .arg(QApplication::applicationName()) | 
| Chris@45 | 1893 		       .arg(source.getLocation())); | 
| Chris@45 | 1894 | 
| Chris@45 | 1895 	if (!source.isRemote()) m_sessionFile = source.getLocalFilename(); | 
| Chris@45 | 1896 | 
| Chris@45 | 1897 	setupMenus(); | 
| Chris@45 | 1898 | 
| Chris@45 | 1899 	CommandHistory::getInstance()->clear(); | 
| Chris@45 | 1900 	CommandHistory::getInstance()->documentSaved(); | 
| Chris@45 | 1901 	m_documentModified = false; | 
| Chris@45 | 1902 	updateMenuStates(); | 
| Chris@45 | 1903 | 
| Chris@227 | 1904         m_recentFiles.addFile(source.getLocation()); | 
| Chris@45 | 1905 | 
| Chris@45 | 1906         if (!source.isRemote()) { | 
| Chris@45 | 1907             // for file dialog | 
| Chris@45 | 1908             registerLastOpenedFilePath(FileFinder::SessionFile, | 
| Chris@227 | 1909                                        source.getLocalFilename()); | 
| Chris@45 | 1910         } | 
| Chris@45 | 1911 | 
| Chris@342 | 1912         emit sessionLoaded(); | 
| Chris@342 | 1913 | 
| Chris@45 | 1914     } else { | 
| Chris@57 | 1915 	setWindowTitle(QApplication::applicationName()); | 
| Chris@45 | 1916     } | 
| Chris@45 | 1917 | 
| Chris@45 | 1918     return ok ? FileOpenSucceeded : FileOpenFailed; | 
| Chris@45 | 1919 } | 
| Chris@45 | 1920 | 
| Chris@141 | 1921 MainWindowBase::FileOpenStatus | 
| Chris@230 | 1922 MainWindowBase::openSessionTemplate(QString templateName) | 
| Chris@230 | 1923 { | 
| Chris@230 | 1924     // Template in the user's template directory takes | 
| Chris@230 | 1925     // priority over a bundled one; we don't unbundle, but | 
| Chris@230 | 1926     // open directly from the bundled file (where applicable) | 
| Chris@230 | 1927     ResourceFinder rf; | 
| Chris@230 | 1928     QString tfile = rf.getResourcePath("templates", templateName + ".svt"); | 
| Chris@230 | 1929     if (tfile != "") { | 
| Chris@294 | 1930         cerr << "SV loading template file " << tfile << endl; | 
| Chris@230 | 1931         return openSessionTemplate(FileSource("file:" + tfile)); | 
| Chris@230 | 1932     } else { | 
| Chris@230 | 1933         return FileOpenFailed; | 
| Chris@230 | 1934     } | 
| Chris@230 | 1935 } | 
| Chris@230 | 1936 | 
| Chris@230 | 1937 MainWindowBase::FileOpenStatus | 
| Chris@227 | 1938 MainWindowBase::openSessionTemplate(FileSource source) | 
| Chris@227 | 1939 { | 
| Chris@294 | 1940     cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl; | 
| Chris@227 | 1941 | 
| Chris@227 | 1942     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@227 | 1943     source.waitForData(); | 
| Chris@227 | 1944 | 
| Chris@227 | 1945     QXmlInputSource *inputSource = 0; | 
| Chris@227 | 1946     QFile *file = 0; | 
| Chris@227 | 1947 | 
| Chris@227 | 1948     file = new QFile(source.getLocalFilename()); | 
| Chris@227 | 1949     inputSource = new QXmlInputSource(file); | 
| Chris@227 | 1950 | 
| Chris@227 | 1951     if (!checkSaveModified()) { | 
| Chris@227 | 1952         delete inputSource; | 
| Chris@227 | 1953         delete file; | 
| Chris@227 | 1954         return FileOpenCancelled; | 
| Chris@227 | 1955     } | 
| Chris@227 | 1956 | 
| Chris@227 | 1957     QString error; | 
| Chris@227 | 1958     closeSession(); | 
| Chris@227 | 1959     createDocument(); | 
| Chris@227 | 1960 | 
| Chris@227 | 1961     PaneCallback callback(this); | 
| Chris@227 | 1962     m_viewManager->clearSelections(); | 
| Chris@227 | 1963 | 
| Chris@227 | 1964     SVFileReader reader(m_document, callback, source.getLocation()); | 
| Chris@227 | 1965     connect | 
| Chris@227 | 1966         (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)), | 
| Chris@227 | 1967          this, SLOT(modelRegenerationFailed(QString, QString, QString))); | 
| Chris@227 | 1968     connect | 
| Chris@227 | 1969         (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@227 | 1970          this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@227 | 1971 | 
| Chris@227 | 1972     reader.parse(*inputSource); | 
| Chris@227 | 1973 | 
| Chris@227 | 1974     if (!reader.isOK()) { | 
| Chris@227 | 1975         error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); | 
| Chris@227 | 1976     } | 
| Chris@227 | 1977 | 
| Chris@227 | 1978     delete inputSource; | 
| Chris@227 | 1979     delete file; | 
| Chris@227 | 1980 | 
| Chris@227 | 1981     bool ok = (error == ""); | 
| Chris@227 | 1982 | 
| Chris@227 | 1983     setWindowTitle(QApplication::applicationName()); | 
| Chris@227 | 1984 | 
| Chris@227 | 1985     if (ok) { | 
| Chris@227 | 1986 | 
| Chris@227 | 1987         emit activity(tr("Open session template \"%1\"").arg(source.getLocation())); | 
| Chris@227 | 1988 | 
| Chris@227 | 1989 	setupMenus(); | 
| Chris@227 | 1990 | 
| Chris@227 | 1991 	CommandHistory::getInstance()->clear(); | 
| Chris@227 | 1992 	CommandHistory::getInstance()->documentSaved(); | 
| Chris@227 | 1993 	m_documentModified = false; | 
| Chris@227 | 1994 	updateMenuStates(); | 
| Chris@342 | 1995 | 
| Chris@342 | 1996         emit sessionLoaded(); | 
| Chris@227 | 1997     } | 
| Chris@227 | 1998 | 
| Chris@227 | 1999     return ok ? FileOpenSucceeded : FileOpenFailed; | 
| Chris@227 | 2000 } | 
| Chris@227 | 2001 | 
| Chris@227 | 2002 MainWindowBase::FileOpenStatus | 
| Chris@141 | 2003 MainWindowBase::openSessionFromRDF(FileSource source) | 
| Chris@141 | 2004 { | 
| Chris@233 | 2005     SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl; | 
| Chris@141 | 2006 | 
| Chris@141 | 2007     if (!source.isAvailable()) return FileOpenFailed; | 
| Chris@141 | 2008     source.waitForData(); | 
| Chris@141 | 2009 | 
| Chris@145 | 2010     if (!checkSaveModified()) { | 
| Chris@145 | 2011         return FileOpenCancelled; | 
| Chris@141 | 2012     } | 
| Chris@143 | 2013 | 
| Chris@145 | 2014     closeSession(); | 
| Chris@145 | 2015     createDocument(); | 
| Chris@145 | 2016 | 
| Chris@145 | 2017     FileOpenStatus status = openLayersFromRDF(source); | 
| Chris@141 | 2018 | 
| Chris@141 | 2019     setupMenus(); | 
| Chris@141 | 2020 | 
| Chris@141 | 2021     setWindowTitle(tr("%1: %2") | 
| Chris@141 | 2022                    .arg(QApplication::applicationName()) | 
| Chris@141 | 2023                    .arg(source.getLocation())); | 
| Chris@141 | 2024     CommandHistory::getInstance()->clear(); | 
| Chris@141 | 2025     CommandHistory::getInstance()->documentSaved(); | 
| Chris@141 | 2026     m_documentModified = false; | 
| Chris@145 | 2027 | 
| Chris@342 | 2028     emit sessionLoaded(); | 
| Chris@342 | 2029 | 
| Chris@145 | 2030     return status; | 
| Chris@145 | 2031 } | 
| Chris@145 | 2032 | 
| Chris@145 | 2033 MainWindowBase::FileOpenStatus | 
| Chris@145 | 2034 MainWindowBase::openLayersFromRDF(FileSource source) | 
| Chris@145 | 2035 { | 
| Chris@435 | 2036     sv_samplerate_t rate = 0; | 
| Chris@145 | 2037 | 
| Chris@233 | 2038     SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl; | 
| Chris@186 | 2039 | 
| Chris@145 | 2040     ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this); | 
| Chris@145 | 2041     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); | 
| Chris@145 | 2042 | 
| Chris@145 | 2043     if (getMainModel()) { | 
| Chris@145 | 2044         rate = getMainModel()->getSampleRate(); | 
| Chris@145 | 2045     } else if (Preferences::getInstance()->getResampleOnLoad()) { | 
| Chris@145 | 2046         rate = m_playSource->getSourceSampleRate(); | 
| Chris@145 | 2047     } | 
| Chris@145 | 2048 | 
| Chris@145 | 2049     RDFImporter importer | 
| Chris@145 | 2050         (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate); | 
| Chris@145 | 2051 | 
| Chris@145 | 2052     if (!importer.isOK()) { | 
| Chris@147 | 2053         if (importer.getErrorString() != "") { | 
| Chris@147 | 2054             QMessageBox::critical | 
| Chris@147 | 2055                 (this, tr("Failed to import RDF"), | 
| Chris@147 | 2056                  tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>") | 
| Chris@147 | 2057                  .arg(source.getLocation()).arg(importer.getErrorString())); | 
| Chris@147 | 2058         } | 
| Chris@145 | 2059         return FileOpenFailed; | 
| Chris@145 | 2060     } | 
| Chris@145 | 2061 | 
| Chris@145 | 2062     std::vector<Model *> models = importer.getDataModels(&dialog); | 
| Chris@145 | 2063 | 
| Chris@145 | 2064     dialog.setMessage(tr("Importing from RDF...")); | 
| Chris@145 | 2065 | 
| Chris@145 | 2066     if (models.empty()) { | 
| Chris@186 | 2067         QMessageBox::critical | 
| Chris@186 | 2068             (this, tr("Failed to import RDF"), | 
| Chris@186 | 2069              tr("<b>Failed to import RDF</b><p>No suitable data models found for import from RDF document at \"%1\"</p>").arg(source.getLocation())); | 
| Chris@145 | 2070         return FileOpenFailed; | 
| Chris@145 | 2071     } | 
| Chris@145 | 2072 | 
| Chris@164 | 2073     emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation())); | 
| Chris@164 | 2074 | 
| Chris@145 | 2075     std::set<Model *> added; | 
| Chris@145 | 2076 | 
| Chris@221 | 2077     for (int i = 0; i < (int)models.size(); ++i) { | 
| Chris@145 | 2078 | 
| Chris@145 | 2079         Model *m = models[i]; | 
| Chris@145 | 2080         WaveFileModel *w = dynamic_cast<WaveFileModel *>(m); | 
| Chris@145 | 2081 | 
| Chris@145 | 2082         if (w) { | 
| Chris@145 | 2083 | 
| Chris@145 | 2084             Pane *pane = addPaneToStack(); | 
| Chris@145 | 2085             Layer *layer = 0; | 
| Chris@145 | 2086 | 
| Chris@145 | 2087             if (m_timeRulerLayer) { | 
| Chris@145 | 2088                 m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@145 | 2089             } | 
| Chris@145 | 2090 | 
| Chris@145 | 2091             if (!getMainModel()) { | 
| Chris@145 | 2092                 m_document->setMainModel(w); | 
| Chris@145 | 2093                 layer = m_document->createMainModelLayer(LayerFactory::Waveform); | 
| Chris@145 | 2094             } else { | 
| Chris@145 | 2095                 layer = m_document->createImportedLayer(w); | 
| Chris@145 | 2096             } | 
| Chris@145 | 2097 | 
| Chris@145 | 2098             m_document->addLayerToView(pane, layer); | 
| Chris@145 | 2099 | 
| Chris@145 | 2100             added.insert(w); | 
| Chris@145 | 2101 | 
| Chris@221 | 2102             for (int j = 0; j < (int)models.size(); ++j) { | 
| Chris@145 | 2103 | 
| Chris@145 | 2104                 Model *dm = models[j]; | 
| Chris@145 | 2105 | 
| Chris@145 | 2106                 if (dm == m) continue; | 
| Chris@145 | 2107                 if (dm->getSourceModel() != m) continue; | 
| Chris@145 | 2108 | 
| Chris@145 | 2109                 layer = m_document->createImportedLayer(dm); | 
| Chris@145 | 2110 | 
| Chris@145 | 2111                 if (layer->isLayerOpaque() || | 
| Chris@145 | 2112                     dynamic_cast<Colour3DPlotLayer *>(layer)) { | 
| Chris@145 | 2113 | 
| Chris@156 | 2114                     // these always go in a new pane, with nothing | 
| Chris@156 | 2115                     // else going in the same pane | 
| Chris@156 | 2116 | 
| Chris@145 | 2117                     Pane *singleLayerPane = addPaneToStack(); | 
| Chris@145 | 2118                     if (m_timeRulerLayer) { | 
| Chris@145 | 2119                         m_document->addLayerToView(singleLayerPane, m_timeRulerLayer); | 
| Chris@145 | 2120                     } | 
| Chris@145 | 2121                     m_document->addLayerToView(singleLayerPane, layer); | 
| Chris@145 | 2122 | 
| Chris@156 | 2123                 } else if (layer->getLayerColourSignificance() == | 
| Chris@156 | 2124                            Layer::ColourHasMeaningfulValue) { | 
| Chris@156 | 2125 | 
| Chris@156 | 2126                     // these can go in a pane with something else, but | 
| Chris@156 | 2127                     // only if none of the something elses also have | 
| Chris@156 | 2128                     // this quality | 
| Chris@156 | 2129 | 
| Chris@156 | 2130                     bool needNewPane = false; | 
| Chris@156 | 2131                     for (int i = 0; i < pane->getLayerCount(); ++i) { | 
| Chris@156 | 2132                         Layer *otherLayer = pane->getLayer(i); | 
| Chris@156 | 2133                         if (otherLayer && | 
| Chris@156 | 2134                             (otherLayer->getLayerColourSignificance() == | 
| Chris@156 | 2135                              Layer::ColourHasMeaningfulValue)) { | 
| Chris@156 | 2136                             needNewPane = true; | 
| Chris@156 | 2137                             break; | 
| Chris@156 | 2138                         } | 
| Chris@156 | 2139                     } | 
| Chris@156 | 2140                     if (needNewPane) { | 
| Chris@156 | 2141                         pane = addPaneToStack(); | 
| Chris@156 | 2142                     } | 
| Chris@156 | 2143 | 
| Chris@156 | 2144                     m_document->addLayerToView(pane, layer); | 
| Chris@156 | 2145 | 
| Chris@145 | 2146                 } else { | 
| Chris@145 | 2147 | 
| Chris@145 | 2148                     if (pane->getLayerCount() > 4) { | 
| Chris@145 | 2149                         pane = addPaneToStack(); | 
| Chris@145 | 2150                     } | 
| Chris@145 | 2151 | 
| Chris@145 | 2152                     m_document->addLayerToView(pane, layer); | 
| Chris@145 | 2153                 } | 
| Chris@145 | 2154 | 
| Chris@145 | 2155                 added.insert(dm); | 
| Chris@145 | 2156             } | 
| Chris@145 | 2157         } | 
| Chris@145 | 2158     } | 
| Chris@145 | 2159 | 
| Chris@221 | 2160     for (int i = 0; i < (int)models.size(); ++i) { | 
| Chris@145 | 2161 | 
| Chris@145 | 2162         Model *m = models[i]; | 
| Chris@145 | 2163 | 
| Chris@145 | 2164         if (added.find(m) == added.end()) { | 
| Chris@145 | 2165 | 
| Chris@145 | 2166             Layer *layer = m_document->createImportedLayer(m); | 
| Chris@145 | 2167             if (!layer) return FileOpenFailed; | 
| Chris@145 | 2168 | 
| Chris@145 | 2169             Pane *singleLayerPane = addPaneToStack(); | 
| Chris@145 | 2170             if (m_timeRulerLayer) { | 
| Chris@145 | 2171                 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer); | 
| Chris@145 | 2172             } | 
| Chris@145 | 2173             m_document->addLayerToView(singleLayerPane, layer); | 
| Chris@145 | 2174         } | 
| Chris@145 | 2175     } | 
| Chris@145 | 2176 | 
| Chris@145 | 2177     m_recentFiles.addFile(source.getLocation()); | 
| Chris@145 | 2178     return FileOpenSucceeded; | 
| Chris@141 | 2179 } | 
| Chris@141 | 2180 | 
| Chris@45 | 2181 void | 
| Chris@475 | 2182 MainWindowBase::createAudioIO() | 
| Chris@45 | 2183 { | 
| Chris@475 | 2184     if (m_playTarget || m_audioIO) return; | 
| Chris@475 | 2185 | 
| Chris@475 | 2186     if (!(m_soundOptions & WithAudioOutput)) return; | 
| Chris@45 | 2187 | 
| Chris@468 | 2188     //!!! how to handle preferences | 
| Chris@468 | 2189 /* | 
| Chris@126 | 2190     QSettings settings; | 
| Chris@126 | 2191     settings.beginGroup("Preferences"); | 
| Chris@126 | 2192     QString targetName = settings.value("audio-target", "").toString(); | 
| Chris@126 | 2193     settings.endGroup(); | 
| Chris@126 | 2194     AudioTargetFactory *factory = AudioTargetFactory::getInstance(); | 
| Chris@126 | 2195 | 
| Chris@126 | 2196     factory->setDefaultCallbackTarget(targetName); | 
| Chris@468 | 2197 */ | 
| Chris@475 | 2198 | 
| Chris@475 | 2199     if (m_soundOptions & WithAudioInput) { | 
| Chris@475 | 2200         m_audioIO = breakfastquay::AudioFactory:: | 
| Chris@475 | 2201             createCallbackIO(m_recordTarget, m_playSource); | 
| Chris@475 | 2202         m_playSource->setSystemPlaybackTarget(m_audioIO); | 
| Chris@475 | 2203     } else { | 
| Chris@475 | 2204         m_playTarget = breakfastquay::AudioFactory:: | 
| Chris@475 | 2205             createCallbackPlayTarget(m_playSource); | 
| Chris@475 | 2206         m_playSource->setSystemPlaybackTarget(m_playTarget); | 
| Chris@475 | 2207     } | 
| Chris@475 | 2208 | 
| Chris@475 | 2209     if (!m_playTarget && !m_audioIO) { | 
| Chris@104 | 2210         emit hideSplash(); | 
| Chris@126 | 2211 | 
| Chris@468 | 2212 //        if (factory->isAutoCallbackTarget(targetName)) { | 
| Chris@128 | 2213             QMessageBox::warning | 
| Chris@45 | 2214 	    (this, tr("Couldn't open audio device"), | 
| Chris@126 | 2215 	     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>"), | 
| Chris@45 | 2216 	     QMessageBox::Ok); | 
| Chris@468 | 2217 /* | 
| Chris@126 | 2218         } else { | 
| Chris@126 | 2219             QMessageBox::warning | 
| Chris@126 | 2220                 (this, tr("Couldn't open audio device"), | 
| Chris@126 | 2221                  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>") | 
| Chris@126 | 2222                  .arg(factory->getCallbackTargetDescription(targetName)), | 
| Chris@126 | 2223                  QMessageBox::Ok); | 
| Chris@126 | 2224         } | 
| Chris@468 | 2225 */ | 
| Chris@475 | 2226             return; | 
| Chris@45 | 2227     } | 
| Chris@45 | 2228 } | 
| Chris@45 | 2229 | 
| Chris@45 | 2230 WaveFileModel * | 
| Chris@45 | 2231 MainWindowBase::getMainModel() | 
| Chris@45 | 2232 { | 
| Chris@45 | 2233     if (!m_document) return 0; | 
| Chris@45 | 2234     return m_document->getMainModel(); | 
| Chris@45 | 2235 } | 
| Chris@45 | 2236 | 
| Chris@45 | 2237 const WaveFileModel * | 
| Chris@45 | 2238 MainWindowBase::getMainModel() const | 
| Chris@45 | 2239 { | 
| Chris@45 | 2240     if (!m_document) return 0; | 
| Chris@45 | 2241     return m_document->getMainModel(); | 
| Chris@45 | 2242 } | 
| Chris@45 | 2243 | 
| Chris@45 | 2244 void | 
| Chris@45 | 2245 MainWindowBase::createDocument() | 
| Chris@45 | 2246 { | 
| Chris@45 | 2247     m_document = new Document; | 
| Chris@45 | 2248 | 
| Chris@45 | 2249     connect(m_document, SIGNAL(layerAdded(Layer *)), | 
| Chris@45 | 2250 	    this, SLOT(layerAdded(Layer *))); | 
| Chris@45 | 2251     connect(m_document, SIGNAL(layerRemoved(Layer *)), | 
| Chris@45 | 2252 	    this, SLOT(layerRemoved(Layer *))); | 
| Chris@45 | 2253     connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), | 
| Chris@45 | 2254 	    this, SLOT(layerAboutToBeDeleted(Layer *))); | 
| Chris@45 | 2255     connect(m_document, SIGNAL(layerInAView(Layer *, bool)), | 
| Chris@45 | 2256 	    this, SLOT(layerInAView(Layer *, bool))); | 
| Chris@45 | 2257 | 
| Chris@45 | 2258     connect(m_document, SIGNAL(modelAdded(Model *)), | 
| Chris@45 | 2259 	    this, SLOT(modelAdded(Model *))); | 
| Chris@45 | 2260     connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), | 
| Chris@45 | 2261 	    this, SLOT(mainModelChanged(WaveFileModel *))); | 
| Chris@45 | 2262     connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), | 
| Chris@45 | 2263 	    this, SLOT(modelAboutToBeDeleted(Model *))); | 
| Chris@45 | 2264 | 
| Chris@78 | 2265     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), | 
| Chris@78 | 2266             this, SLOT(modelGenerationFailed(QString, QString))); | 
| Chris@78 | 2267     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@78 | 2268             this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@78 | 2269     connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), | 
| Chris@78 | 2270             this, SLOT(modelGenerationFailed(QString, QString))); | 
| Chris@78 | 2271     connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), | 
| Chris@78 | 2272             this, SLOT(modelRegenerationWarning(QString, QString, QString))); | 
| Chris@78 | 2273     connect(m_document, SIGNAL(alignmentFailed(QString, QString)), | 
| Chris@78 | 2274             this, SLOT(alignmentFailed(QString, QString))); | 
| Chris@160 | 2275 | 
| Chris@160 | 2276     emit replacedDocument(); | 
| Chris@45 | 2277 } | 
| Chris@45 | 2278 | 
| Chris@45 | 2279 bool | 
| Chris@45 | 2280 MainWindowBase::saveSessionFile(QString path) | 
| Chris@45 | 2281 { | 
| Chris@217 | 2282     try { | 
| Chris@217 | 2283 | 
| Chris@217 | 2284         TempWriteFile temp(path); | 
| Chris@217 | 2285 | 
| Chris@217 | 2286         BZipFileDevice bzFile(temp.getTemporaryFilename()); | 
| Chris@217 | 2287         if (!bzFile.open(QIODevice::WriteOnly)) { | 
| Chris@293 | 2288             cerr << "Failed to open session file \"" | 
| Chris@294 | 2289                       << temp.getTemporaryFilename() | 
| Chris@217 | 2290                       << "\" for writing: " | 
| Chris@293 | 2291                       << bzFile.errorString() << endl; | 
| Chris@217 | 2292             return false; | 
| Chris@217 | 2293         } | 
| Chris@217 | 2294 | 
| Chris@217 | 2295         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); | 
| Chris@217 | 2296 | 
| Chris@217 | 2297         QTextStream out(&bzFile); | 
| Chris@432 | 2298         out.setCodec(QTextCodec::codecForName("UTF-8")); | 
| Chris@226 | 2299         toXml(out, false); | 
| Chris@217 | 2300         out.flush(); | 
| Chris@217 | 2301 | 
| Chris@217 | 2302         QApplication::restoreOverrideCursor(); | 
| Chris@217 | 2303 | 
| Chris@217 | 2304         if (!bzFile.isOK()) { | 
| Chris@217 | 2305             QMessageBox::critical(this, tr("Failed to write file"), | 
| Chris@217 | 2306                                   tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") | 
| Chris@217 | 2307                                   .arg(path).arg(bzFile.errorString())); | 
| Chris@217 | 2308             bzFile.close(); | 
| Chris@217 | 2309             return false; | 
| Chris@217 | 2310         } | 
| Chris@217 | 2311 | 
| Chris@217 | 2312         bzFile.close(); | 
| Chris@217 | 2313         temp.moveToTarget(); | 
| Chris@217 | 2314         return true; | 
| Chris@217 | 2315 | 
| Chris@217 | 2316     } catch (FileOperationFailed &f) { | 
| Chris@217 | 2317 | 
| Chris@217 | 2318         QMessageBox::critical(this, tr("Failed to write file"), | 
| Chris@217 | 2319                               tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") | 
| Chris@217 | 2320                               .arg(path).arg(f.what())); | 
| Chris@45 | 2321         return false; | 
| Chris@45 | 2322     } | 
| Chris@45 | 2323 } | 
| Chris@45 | 2324 | 
| Chris@224 | 2325 bool | 
| Chris@224 | 2326 MainWindowBase::saveSessionTemplate(QString path) | 
| Chris@224 | 2327 { | 
| Chris@224 | 2328     try { | 
| Chris@224 | 2329 | 
| Chris@224 | 2330         TempWriteFile temp(path); | 
| Chris@224 | 2331 | 
| Chris@224 | 2332         QFile file(temp.getTemporaryFilename()); | 
| Chris@224 | 2333         if (!file.open(QIODevice::WriteOnly)) { | 
| Chris@293 | 2334             cerr << "Failed to open session template file \"" | 
| Chris@294 | 2335                       << temp.getTemporaryFilename() | 
| Chris@224 | 2336                       << "\" for writing: " | 
| Chris@294 | 2337                       << file.errorString() << endl; | 
| Chris@224 | 2338             return false; | 
| Chris@224 | 2339         } | 
| Chris@224 | 2340 | 
| Chris@224 | 2341         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); | 
| Chris@224 | 2342 | 
| Chris@224 | 2343         QTextStream out(&file); | 
| Chris@432 | 2344         out.setCodec(QTextCodec::codecForName("UTF-8")); | 
| Chris@226 | 2345         toXml(out, true); | 
| Chris@224 | 2346         out.flush(); | 
| Chris@224 | 2347 | 
| Chris@224 | 2348         QApplication::restoreOverrideCursor(); | 
| Chris@224 | 2349 | 
| Chris@224 | 2350         file.close(); | 
| Chris@224 | 2351         temp.moveToTarget(); | 
| Chris@224 | 2352         return true; | 
| Chris@224 | 2353 | 
| Chris@224 | 2354     } catch (FileOperationFailed &f) { | 
| Chris@224 | 2355 | 
| Chris@224 | 2356         QMessageBox::critical(this, tr("Failed to write file"), | 
| Chris@224 | 2357                               tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") | 
| Chris@224 | 2358                               .arg(path).arg(f.what())); | 
| Chris@224 | 2359         return false; | 
| Chris@224 | 2360     } | 
| Chris@224 | 2361 } | 
| Chris@224 | 2362 | 
| Chris@45 | 2363 void | 
| Chris@226 | 2364 MainWindowBase::toXml(QTextStream &out, bool asTemplate) | 
| Chris@45 | 2365 { | 
| Chris@45 | 2366     QString indent("  "); | 
| Chris@45 | 2367 | 
| Chris@45 | 2368     out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | 
| Chris@45 | 2369     out << "<!DOCTYPE sonic-visualiser>\n"; | 
| Chris@45 | 2370     out << "<sv>\n"; | 
| Chris@45 | 2371 | 
| Chris@226 | 2372     if (asTemplate) { | 
| Chris@226 | 2373         m_document->toXmlAsTemplate(out, "", ""); | 
| Chris@226 | 2374     } else { | 
| Chris@226 | 2375         m_document->toXml(out, "", ""); | 
| Chris@226 | 2376     } | 
| Chris@45 | 2377 | 
| Chris@45 | 2378     out << "<display>\n"; | 
| Chris@45 | 2379 | 
| Chris@45 | 2380     out << QString("  <window width=\"%1\" height=\"%2\"/>\n") | 
| Chris@45 | 2381 	.arg(width()).arg(height()); | 
| Chris@45 | 2382 | 
| Chris@45 | 2383     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@45 | 2384 | 
| Chris@45 | 2385 	Pane *pane = m_paneStack->getPane(i); | 
| Chris@45 | 2386 | 
| Chris@45 | 2387 	if (pane) { | 
| Chris@45 | 2388             pane->toXml(out, indent); | 
| Chris@45 | 2389 	} | 
| Chris@45 | 2390     } | 
| Chris@45 | 2391 | 
| Chris@45 | 2392     out << "</display>\n"; | 
| Chris@45 | 2393 | 
| Chris@45 | 2394     m_viewManager->getSelection().toXml(out); | 
| Chris@45 | 2395 | 
| Chris@45 | 2396     out << "</sv>\n"; | 
| Chris@45 | 2397 } | 
| Chris@45 | 2398 | 
| Chris@45 | 2399 Pane * | 
| Chris@45 | 2400 MainWindowBase::addPaneToStack() | 
| Chris@45 | 2401 { | 
| Chris@342 | 2402     cerr << "MainWindowBase::addPaneToStack()" << endl; | 
| Chris@45 | 2403     AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@45 | 2404     CommandHistory::getInstance()->addCommand(command); | 
| Chris@57 | 2405     Pane *pane = command->getPane(); | 
| Chris@57 | 2406     return pane; | 
| Chris@45 | 2407 } | 
| Chris@45 | 2408 | 
| Chris@45 | 2409 void | 
| Chris@45 | 2410 MainWindowBase::zoomIn() | 
| Chris@45 | 2411 { | 
| Chris@45 | 2412     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2413     if (currentPane) currentPane->zoom(true); | 
| Chris@45 | 2414 } | 
| Chris@45 | 2415 | 
| Chris@45 | 2416 void | 
| Chris@45 | 2417 MainWindowBase::zoomOut() | 
| Chris@45 | 2418 { | 
| Chris@45 | 2419     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2420     if (currentPane) currentPane->zoom(false); | 
| Chris@45 | 2421 } | 
| Chris@45 | 2422 | 
| Chris@45 | 2423 void | 
| Chris@45 | 2424 MainWindowBase::zoomToFit() | 
| Chris@45 | 2425 { | 
| Chris@45 | 2426     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2427     if (!currentPane) return; | 
| Chris@45 | 2428 | 
| Chris@45 | 2429     Model *model = getMainModel(); | 
| Chris@45 | 2430     if (!model) return; | 
| Chris@45 | 2431 | 
| Chris@434 | 2432     sv_frame_t start = model->getStartFrame(); | 
| Chris@434 | 2433     sv_frame_t end = model->getEndFrame(); | 
| Chris@60 | 2434     if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame()); | 
| Chris@366 | 2435     int pixels = currentPane->width(); | 
| Chris@366 | 2436 | 
| Chris@366 | 2437     int sw = currentPane->getVerticalScaleWidth(); | 
| Chris@45 | 2438     if (pixels > sw * 2) pixels -= sw * 2; | 
| Chris@45 | 2439     else pixels = 1; | 
| Chris@45 | 2440     if (pixels > 4) pixels -= 4; | 
| Chris@45 | 2441 | 
| Chris@436 | 2442     int zoomLevel = int((end - start) / pixels); | 
| Chris@150 | 2443     if (zoomLevel < 1) zoomLevel = 1; | 
| Chris@45 | 2444 | 
| Chris@45 | 2445     currentPane->setZoomLevel(zoomLevel); | 
| Chris@45 | 2446     currentPane->setCentreFrame((start + end) / 2); | 
| Chris@45 | 2447 } | 
| Chris@45 | 2448 | 
| Chris@45 | 2449 void | 
| Chris@45 | 2450 MainWindowBase::zoomDefault() | 
| Chris@45 | 2451 { | 
| Chris@45 | 2452     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@302 | 2453     QSettings settings; | 
| Chris@302 | 2454     settings.beginGroup("MainWindow"); | 
| Chris@302 | 2455     int zoom = settings.value("zoom-default", 1024).toInt(); | 
| Chris@302 | 2456     settings.endGroup(); | 
| Chris@302 | 2457     if (currentPane) currentPane->setZoomLevel(zoom); | 
| Chris@45 | 2458 } | 
| Chris@45 | 2459 | 
| Chris@45 | 2460 void | 
| Chris@45 | 2461 MainWindowBase::scrollLeft() | 
| Chris@45 | 2462 { | 
| Chris@45 | 2463     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2464     if (currentPane) currentPane->scroll(false, false); | 
| Chris@45 | 2465 } | 
| Chris@45 | 2466 | 
| Chris@45 | 2467 void | 
| Chris@45 | 2468 MainWindowBase::jumpLeft() | 
| Chris@45 | 2469 { | 
| Chris@45 | 2470     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2471     if (currentPane) currentPane->scroll(false, true); | 
| Chris@45 | 2472 } | 
| Chris@45 | 2473 | 
| Chris@45 | 2474 void | 
| Chris@162 | 2475 MainWindowBase::peekLeft() | 
| Chris@162 | 2476 { | 
| Chris@162 | 2477     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@162 | 2478     if (currentPane) currentPane->scroll(false, false, false); | 
| Chris@162 | 2479 } | 
| Chris@162 | 2480 | 
| Chris@162 | 2481 void | 
| Chris@45 | 2482 MainWindowBase::scrollRight() | 
| Chris@45 | 2483 { | 
| Chris@45 | 2484     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2485     if (currentPane) currentPane->scroll(true, false); | 
| Chris@45 | 2486 } | 
| Chris@45 | 2487 | 
| Chris@45 | 2488 void | 
| Chris@45 | 2489 MainWindowBase::jumpRight() | 
| Chris@45 | 2490 { | 
| Chris@45 | 2491     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2492     if (currentPane) currentPane->scroll(true, true); | 
| Chris@45 | 2493 } | 
| Chris@45 | 2494 | 
| Chris@45 | 2495 void | 
| Chris@162 | 2496 MainWindowBase::peekRight() | 
| Chris@162 | 2497 { | 
| Chris@162 | 2498     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@162 | 2499     if (currentPane) currentPane->scroll(true, false, false); | 
| Chris@162 | 2500 } | 
| Chris@162 | 2501 | 
| Chris@162 | 2502 void | 
| Chris@45 | 2503 MainWindowBase::showNoOverlays() | 
| Chris@45 | 2504 { | 
| Chris@45 | 2505     m_viewManager->setOverlayMode(ViewManager::NoOverlays); | 
| Chris@45 | 2506 } | 
| Chris@45 | 2507 | 
| Chris@45 | 2508 void | 
| Chris@45 | 2509 MainWindowBase::showMinimalOverlays() | 
| Chris@45 | 2510 { | 
| Chris@335 | 2511     m_viewManager->setOverlayMode(ViewManager::StandardOverlays); | 
| Chris@45 | 2512 } | 
| Chris@45 | 2513 | 
| Chris@45 | 2514 void | 
| Chris@45 | 2515 MainWindowBase::showAllOverlays() | 
| Chris@45 | 2516 { | 
| Chris@45 | 2517     m_viewManager->setOverlayMode(ViewManager::AllOverlays); | 
| Chris@45 | 2518 } | 
| Chris@45 | 2519 | 
| Chris@45 | 2520 void | 
| Chris@211 | 2521 MainWindowBase::toggleTimeRulers() | 
| Chris@211 | 2522 { | 
| Chris@211 | 2523     bool haveRulers = false; | 
| Chris@211 | 2524     bool someHidden = false; | 
| Chris@211 | 2525 | 
| Chris@211 | 2526     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@211 | 2527 | 
| Chris@211 | 2528         Pane *pane = m_paneStack->getPane(i); | 
| Chris@211 | 2529         if (!pane) continue; | 
| Chris@211 | 2530 | 
| Chris@211 | 2531         for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@211 | 2532 | 
| Chris@211 | 2533             Layer *layer = pane->getLayer(j); | 
| Chris@211 | 2534             if (!dynamic_cast<TimeRulerLayer *>(layer)) continue; | 
| Chris@211 | 2535 | 
| Chris@211 | 2536             haveRulers = true; | 
| Chris@211 | 2537             if (layer->isLayerDormant(pane)) someHidden = true; | 
| Chris@211 | 2538         } | 
| Chris@211 | 2539     } | 
| Chris@211 | 2540 | 
| Chris@211 | 2541     if (haveRulers) { | 
| Chris@211 | 2542 | 
| Chris@211 | 2543         bool show = someHidden; | 
| Chris@211 | 2544 | 
| Chris@211 | 2545         for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@211 | 2546 | 
| Chris@211 | 2547             Pane *pane = m_paneStack->getPane(i); | 
| Chris@211 | 2548             if (!pane) continue; | 
| Chris@211 | 2549 | 
| Chris@211 | 2550             for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@211 | 2551 | 
| Chris@211 | 2552                 Layer *layer = pane->getLayer(j); | 
| Chris@211 | 2553                 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue; | 
| Chris@211 | 2554 | 
| Chris@211 | 2555                 layer->showLayer(pane, show); | 
| Chris@211 | 2556             } | 
| Chris@211 | 2557         } | 
| Chris@211 | 2558     } | 
| Chris@211 | 2559 } | 
| Chris@211 | 2560 | 
| Chris@211 | 2561 void | 
| Chris@45 | 2562 MainWindowBase::toggleZoomWheels() | 
| Chris@45 | 2563 { | 
| Chris@45 | 2564     if (m_viewManager->getZoomWheelsEnabled()) { | 
| Chris@45 | 2565         m_viewManager->setZoomWheelsEnabled(false); | 
| Chris@45 | 2566     } else { | 
| Chris@45 | 2567         m_viewManager->setZoomWheelsEnabled(true); | 
| Chris@45 | 2568     } | 
| Chris@45 | 2569 } | 
| Chris@45 | 2570 | 
| Chris@45 | 2571 void | 
| Chris@45 | 2572 MainWindowBase::togglePropertyBoxes() | 
| Chris@45 | 2573 { | 
| Chris@45 | 2574     if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { | 
| Chris@45 | 2575         if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@45 | 2576             Preferences::VerticallyStacked) { | 
| Chris@45 | 2577             m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@45 | 2578         } else { | 
| Chris@45 | 2579             m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@45 | 2580         } | 
| Chris@45 | 2581     } else { | 
| Chris@45 | 2582         m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); | 
| Chris@45 | 2583     } | 
| Chris@45 | 2584 } | 
| Chris@45 | 2585 | 
| Chris@378 | 2586 QLabel * | 
| Chris@378 | 2587 MainWindowBase::getStatusLabel() const | 
| Chris@378 | 2588 { | 
| Chris@378 | 2589     if (!m_statusLabel) { | 
| Chris@378 | 2590         m_statusLabel = new QLabel(); | 
| Chris@378 | 2591         statusBar()->addWidget(m_statusLabel, 1); | 
| Chris@378 | 2592     } | 
| Chris@379 | 2593 | 
| Chris@379 | 2594     QList<QFrame *> frames = statusBar()->findChildren<QFrame *>(); | 
| Chris@379 | 2595     foreach (QFrame *f, frames) { | 
| Chris@379 | 2596         f->setFrameStyle(QFrame::NoFrame); | 
| Chris@379 | 2597     } | 
| Chris@379 | 2598 | 
| Chris@378 | 2599     return m_statusLabel; | 
| Chris@378 | 2600 } | 
| Chris@378 | 2601 | 
| Chris@45 | 2602 void | 
| Chris@45 | 2603 MainWindowBase::toggleStatusBar() | 
| Chris@45 | 2604 { | 
| Chris@45 | 2605     QSettings settings; | 
| Chris@45 | 2606     settings.beginGroup("MainWindow"); | 
| Chris@45 | 2607     bool sb = settings.value("showstatusbar", true).toBool(); | 
| Chris@45 | 2608 | 
| Chris@45 | 2609     if (sb) { | 
| Chris@45 | 2610         statusBar()->hide(); | 
| Chris@45 | 2611     } else { | 
| Chris@45 | 2612         statusBar()->show(); | 
| Chris@45 | 2613     } | 
| Chris@45 | 2614 | 
| Chris@45 | 2615     settings.setValue("showstatusbar", !sb); | 
| Chris@45 | 2616 | 
| Chris@45 | 2617     settings.endGroup(); | 
| Chris@45 | 2618 } | 
| Chris@45 | 2619 | 
| Chris@45 | 2620 void | 
| Chris@256 | 2621 MainWindowBase::toggleCentreLine() | 
| Chris@256 | 2622 { | 
| Chris@256 | 2623     if (m_viewManager->shouldShowCentreLine()) { | 
| Chris@256 | 2624         m_viewManager->setShowCentreLine(false); | 
| Chris@256 | 2625     } else { | 
| Chris@256 | 2626         m_viewManager->setShowCentreLine(true); | 
| Chris@256 | 2627     } | 
| Chris@256 | 2628 } | 
| Chris@256 | 2629 | 
| Chris@256 | 2630 void | 
| Chris@45 | 2631 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name) | 
| Chris@45 | 2632 { | 
| Chris@45 | 2633     if (name == "Property Box Layout") { | 
| Chris@45 | 2634         if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { | 
| Chris@45 | 2635             if (Preferences::getInstance()->getPropertyBoxLayout() == | 
| Chris@45 | 2636                 Preferences::VerticallyStacked) { | 
| Chris@45 | 2637                 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | 
| Chris@45 | 2638             } else { | 
| Chris@45 | 2639                 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | 
| Chris@45 | 2640             } | 
| Chris@45 | 2641         } | 
| Chris@45 | 2642     } else if (name == "Background Mode" && m_viewManager) { | 
| Chris@45 | 2643         Preferences::BackgroundMode mode = | 
| Chris@45 | 2644             Preferences::getInstance()->getBackgroundMode(); | 
| Chris@45 | 2645         if (mode == Preferences::BackgroundFromTheme) { | 
| Chris@45 | 2646             m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); | 
| Chris@45 | 2647         } else if (mode == Preferences::DarkBackground) { | 
| Chris@45 | 2648             m_viewManager->setGlobalDarkBackground(true); | 
| Chris@45 | 2649         } else { | 
| Chris@45 | 2650             m_viewManager->setGlobalDarkBackground(false); | 
| Chris@45 | 2651         } | 
| Chris@45 | 2652     } | 
| Chris@45 | 2653 } | 
| Chris@45 | 2654 | 
| Chris@45 | 2655 void | 
| Chris@45 | 2656 MainWindowBase::play() | 
| Chris@45 | 2657 { | 
| Chris@477 | 2658     if (m_recordTarget->isRecording() || m_playSource->isPlaying()) { | 
| Chris@45 | 2659         stop(); | 
| Chris@45 | 2660     } else { | 
| Chris@45 | 2661         playbackFrameChanged(m_viewManager->getPlaybackFrame()); | 
| Chris@45 | 2662 	m_playSource->play(m_viewManager->getPlaybackFrame()); | 
| Chris@45 | 2663     } | 
| Chris@45 | 2664 } | 
| Chris@45 | 2665 | 
| Chris@45 | 2666 void | 
| Chris@477 | 2667 MainWindowBase::record() | 
| Chris@477 | 2668 { | 
| Chris@477 | 2669     if (!m_recordTarget) { | 
| Chris@477 | 2670         //!!! report | 
| Chris@477 | 2671         return; | 
| Chris@477 | 2672     } | 
| Chris@477 | 2673 | 
| Chris@477 | 2674     if (m_recordTarget->isRecording()) { | 
| Chris@477 | 2675         m_recordTarget->stopRecording(); | 
| Chris@477 | 2676         return; | 
| Chris@477 | 2677     } | 
| Chris@477 | 2678 | 
| Chris@477 | 2679     WritableWaveFileModel *model = m_recordTarget->startRecording(); | 
| Chris@477 | 2680     if (!model) { | 
| Chris@477 | 2681         cerr << "ERROR: MainWindowBase::record: Recording failed" << endl; | 
| Chris@477 | 2682         //!!! report | 
| Chris@477 | 2683         return; | 
| Chris@477 | 2684     } | 
| Chris@477 | 2685 | 
| Chris@477 | 2686     if (!model->isOK()) { | 
| Chris@477 | 2687         m_recordTarget->stopRecording(); | 
| Chris@477 | 2688         delete model; | 
| Chris@477 | 2689         //!!! ??? | 
| Chris@477 | 2690         return; | 
| Chris@477 | 2691     } | 
| Chris@477 | 2692 | 
| Chris@477 | 2693     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@477 | 2694         (tr("Import Recorded Audio"), true); | 
| Chris@477 | 2695 | 
| Chris@477 | 2696     m_document->addImportedModel(model); | 
| Chris@477 | 2697 | 
| Chris@477 | 2698     AddPaneCommand *command = new AddPaneCommand(this); | 
| Chris@477 | 2699     CommandHistory::getInstance()->addCommand(command); | 
| Chris@477 | 2700 | 
| Chris@477 | 2701     Pane *pane = command->getPane(); | 
| Chris@477 | 2702 | 
| Chris@477 | 2703     if (m_timeRulerLayer) { | 
| Chris@477 | 2704         m_document->addLayerToView(pane, m_timeRulerLayer); | 
| Chris@477 | 2705     } | 
| Chris@477 | 2706 | 
| Chris@477 | 2707     Layer *newLayer = m_document->createImportedLayer(model); | 
| Chris@477 | 2708 | 
| Chris@477 | 2709     if (newLayer) { | 
| Chris@477 | 2710         m_document->addLayerToView(pane, newLayer); | 
| Chris@477 | 2711     } | 
| Chris@477 | 2712 | 
| Chris@477 | 2713     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@477 | 2714 } | 
| Chris@477 | 2715 | 
| Chris@477 | 2716 void | 
| Chris@45 | 2717 MainWindowBase::ffwd() | 
| Chris@45 | 2718 { | 
| Chris@45 | 2719     if (!getMainModel()) return; | 
| Chris@45 | 2720 | 
| Chris@435 | 2721     sv_frame_t frame = m_viewManager->getPlaybackFrame(); | 
| Chris@45 | 2722     ++frame; | 
| Chris@45 | 2723 | 
| Chris@85 | 2724     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2725     Layer *layer = getSnapLayer(); | 
| Chris@435 | 2726     sv_samplerate_t sr = getMainModel()->getSampleRate(); | 
| Chris@45 | 2727 | 
| Chris@45 | 2728     if (!layer) { | 
| Chris@45 | 2729 | 
| Chris@45 | 2730         frame = RealTime::realTime2Frame | 
| Chris@357 | 2731             (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr); | 
| Chris@435 | 2732         if (frame > getMainModel()->getEndFrame()) { | 
| Chris@45 | 2733             frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 2734         } | 
| Chris@45 | 2735 | 
| Chris@45 | 2736     } else { | 
| Chris@45 | 2737 | 
| Chris@366 | 2738         int resolution = 0; | 
| Chris@166 | 2739         if (pane) frame = pane->alignFromReference(frame); | 
| Chris@85 | 2740         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), | 
| Chris@85 | 2741                                       frame, resolution, Layer::SnapRight)) { | 
| Chris@85 | 2742             if (pane) frame = pane->alignToReference(frame); | 
| Chris@85 | 2743         } else { | 
| Chris@45 | 2744             frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 2745         } | 
| Chris@45 | 2746     } | 
| Chris@45 | 2747 | 
| Chris@45 | 2748     if (frame < 0) frame = 0; | 
| Chris@45 | 2749 | 
| Chris@45 | 2750     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@435 | 2751         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 2752     } | 
| Chris@45 | 2753 | 
| Chris@45 | 2754     m_viewManager->setPlaybackFrame(frame); | 
| Chris@166 | 2755 | 
| Chris@435 | 2756     if (frame == getMainModel()->getEndFrame() && | 
| Chris@166 | 2757         m_playSource && | 
| Chris@166 | 2758         m_playSource->isPlaying() && | 
| Chris@166 | 2759         !m_viewManager->getPlayLoopMode()) { | 
| Chris@166 | 2760         stop(); | 
| Chris@166 | 2761     } | 
| Chris@45 | 2762 } | 
| Chris@45 | 2763 | 
| Chris@45 | 2764 void | 
| Chris@45 | 2765 MainWindowBase::ffwdEnd() | 
| Chris@45 | 2766 { | 
| Chris@45 | 2767     if (!getMainModel()) return; | 
| Chris@45 | 2768 | 
| Chris@139 | 2769     if (m_playSource && | 
| Chris@139 | 2770         m_playSource->isPlaying() && | 
| Chris@139 | 2771         !m_viewManager->getPlayLoopMode()) { | 
| Chris@139 | 2772         stop(); | 
| Chris@139 | 2773     } | 
| Chris@139 | 2774 | 
| Chris@435 | 2775     sv_frame_t frame = getMainModel()->getEndFrame(); | 
| Chris@45 | 2776 | 
| Chris@45 | 2777     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 2778         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 2779     } | 
| Chris@45 | 2780 | 
| Chris@45 | 2781     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 2782 } | 
| Chris@45 | 2783 | 
| Chris@45 | 2784 void | 
| Chris@166 | 2785 MainWindowBase::ffwdSimilar() | 
| Chris@166 | 2786 { | 
| Chris@166 | 2787     if (!getMainModel()) return; | 
| Chris@166 | 2788 | 
| Chris@166 | 2789     Layer *layer = getSnapLayer(); | 
| Chris@166 | 2790     if (!layer) { ffwd(); return; } | 
| Chris@166 | 2791 | 
| Chris@166 | 2792     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@435 | 2793     sv_frame_t frame = m_viewManager->getPlaybackFrame(); | 
| Chris@166 | 2794 | 
| Chris@366 | 2795     int resolution = 0; | 
| Chris@166 | 2796     if (pane) frame = pane->alignFromReference(frame); | 
| Chris@166 | 2797     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(), | 
| Chris@166 | 2798                                     frame, resolution, Layer::SnapRight)) { | 
| Chris@166 | 2799         if (pane) frame = pane->alignToReference(frame); | 
| Chris@166 | 2800     } else { | 
| Chris@166 | 2801         frame = getMainModel()->getEndFrame(); | 
| Chris@166 | 2802     } | 
| Chris@166 | 2803 | 
| Chris@166 | 2804     if (frame < 0) frame = 0; | 
| Chris@166 | 2805 | 
| Chris@166 | 2806     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@435 | 2807         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@166 | 2808     } | 
| Chris@166 | 2809 | 
| Chris@166 | 2810     m_viewManager->setPlaybackFrame(frame); | 
| Chris@166 | 2811 | 
| Chris@435 | 2812     if (frame == getMainModel()->getEndFrame() && | 
| Chris@166 | 2813         m_playSource && | 
| Chris@166 | 2814         m_playSource->isPlaying() && | 
| Chris@166 | 2815         !m_viewManager->getPlayLoopMode()) { | 
| Chris@166 | 2816         stop(); | 
| Chris@166 | 2817     } | 
| Chris@166 | 2818 } | 
| Chris@166 | 2819 | 
| Chris@166 | 2820 void | 
| Chris@45 | 2821 MainWindowBase::rewind() | 
| Chris@45 | 2822 { | 
| Chris@45 | 2823     if (!getMainModel()) return; | 
| Chris@45 | 2824 | 
| Chris@435 | 2825     sv_frame_t frame = m_viewManager->getPlaybackFrame(); | 
| Chris@45 | 2826     if (frame > 0) --frame; | 
| Chris@45 | 2827 | 
| Chris@85 | 2828     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2829     Layer *layer = getSnapLayer(); | 
| Chris@435 | 2830     sv_samplerate_t sr = getMainModel()->getSampleRate(); | 
| Chris@45 | 2831 | 
| Chris@45 | 2832     // when rewinding during playback, we want to allow a period | 
| Chris@45 | 2833     // following a rewind target point at which the rewind will go to | 
| Chris@45 | 2834     // the prior point instead of the immediately neighbouring one | 
| Chris@45 | 2835     if (m_playSource && m_playSource->isPlaying()) { | 
| Chris@45 | 2836         RealTime ct = RealTime::frame2RealTime(frame, sr); | 
| Chris@357 | 2837         ct = ct - RealTime::fromSeconds(0.15); | 
| Chris@45 | 2838         if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; | 
| Chris@45 | 2839         frame = RealTime::realTime2Frame(ct, sr); | 
| Chris@45 | 2840     } | 
| Chris@45 | 2841 | 
| Chris@45 | 2842     if (!layer) { | 
| Chris@45 | 2843 | 
| Chris@45 | 2844         frame = RealTime::realTime2Frame | 
| Chris@357 | 2845             (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr); | 
| Chris@435 | 2846         if (frame < getMainModel()->getStartFrame()) { | 
| Chris@45 | 2847             frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 2848         } | 
| Chris@45 | 2849 | 
| Chris@45 | 2850     } else { | 
| Chris@45 | 2851 | 
| Chris@366 | 2852         int resolution = 0; | 
| Chris@166 | 2853         if (pane) frame = pane->alignFromReference(frame); | 
| Chris@85 | 2854         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), | 
| Chris@85 | 2855                                       frame, resolution, Layer::SnapLeft)) { | 
| Chris@85 | 2856             if (pane) frame = pane->alignToReference(frame); | 
| Chris@85 | 2857         } else { | 
| Chris@45 | 2858             frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 2859         } | 
| Chris@45 | 2860     } | 
| Chris@45 | 2861 | 
| Chris@45 | 2862     if (frame < 0) frame = 0; | 
| Chris@45 | 2863 | 
| Chris@45 | 2864     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@435 | 2865         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 2866     } | 
| Chris@45 | 2867 | 
| Chris@45 | 2868     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 2869 } | 
| Chris@45 | 2870 | 
| Chris@45 | 2871 void | 
| Chris@45 | 2872 MainWindowBase::rewindStart() | 
| Chris@45 | 2873 { | 
| Chris@45 | 2874     if (!getMainModel()) return; | 
| Chris@45 | 2875 | 
| Chris@435 | 2876     sv_frame_t frame = getMainModel()->getStartFrame(); | 
| Chris@45 | 2877 | 
| Chris@45 | 2878     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@45 | 2879         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@45 | 2880     } | 
| Chris@45 | 2881 | 
| Chris@45 | 2882     m_viewManager->setPlaybackFrame(frame); | 
| Chris@45 | 2883 } | 
| Chris@45 | 2884 | 
| Chris@166 | 2885 void | 
| Chris@166 | 2886 MainWindowBase::rewindSimilar() | 
| Chris@166 | 2887 { | 
| Chris@166 | 2888     if (!getMainModel()) return; | 
| Chris@166 | 2889 | 
| Chris@166 | 2890     Layer *layer = getSnapLayer(); | 
| Chris@166 | 2891     if (!layer) { rewind(); return; } | 
| Chris@166 | 2892 | 
| Chris@166 | 2893     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@435 | 2894     sv_frame_t frame = m_viewManager->getPlaybackFrame(); | 
| Chris@166 | 2895 | 
| Chris@366 | 2896     int resolution = 0; | 
| Chris@166 | 2897     if (pane) frame = pane->alignFromReference(frame); | 
| Chris@166 | 2898     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(), | 
| Chris@166 | 2899                                     frame, resolution, Layer::SnapLeft)) { | 
| Chris@166 | 2900         if (pane) frame = pane->alignToReference(frame); | 
| Chris@166 | 2901     } else { | 
| Chris@166 | 2902         frame = getMainModel()->getStartFrame(); | 
| Chris@166 | 2903     } | 
| Chris@166 | 2904 | 
| Chris@166 | 2905     if (frame < 0) frame = 0; | 
| Chris@166 | 2906 | 
| Chris@166 | 2907     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@435 | 2908         frame = m_viewManager->constrainFrameToSelection(frame); | 
| Chris@166 | 2909     } | 
| Chris@166 | 2910 | 
| Chris@166 | 2911     m_viewManager->setPlaybackFrame(frame); | 
| Chris@166 | 2912 } | 
| Chris@166 | 2913 | 
| Chris@45 | 2914 Layer * | 
| Chris@45 | 2915 MainWindowBase::getSnapLayer() const | 
| Chris@45 | 2916 { | 
| Chris@45 | 2917     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 2918     if (!pane) return 0; | 
| Chris@45 | 2919 | 
| Chris@45 | 2920     Layer *layer = pane->getSelectedLayer(); | 
| Chris@45 | 2921 | 
| Chris@45 | 2922     if (!dynamic_cast<TimeInstantLayer *>(layer) && | 
| Chris@45 | 2923         !dynamic_cast<TimeValueLayer *>(layer) && | 
| Chris@194 | 2924         !dynamic_cast<RegionLayer *>(layer) && | 
| Chris@45 | 2925         !dynamic_cast<TimeRulerLayer *>(layer)) { | 
| Chris@45 | 2926 | 
| Chris@45 | 2927         layer = 0; | 
| Chris@45 | 2928 | 
| Chris@45 | 2929         for (int i = pane->getLayerCount(); i > 0; --i) { | 
| Chris@45 | 2930             Layer *l = pane->getLayer(i-1); | 
| Chris@45 | 2931             if (dynamic_cast<TimeRulerLayer *>(l)) { | 
| Chris@45 | 2932                 layer = l; | 
| Chris@45 | 2933                 break; | 
| Chris@45 | 2934             } | 
| Chris@45 | 2935         } | 
| Chris@45 | 2936     } | 
| Chris@45 | 2937 | 
| Chris@45 | 2938     return layer; | 
| Chris@45 | 2939 } | 
| Chris@45 | 2940 | 
| Chris@45 | 2941 void | 
| Chris@45 | 2942 MainWindowBase::stop() | 
| Chris@45 | 2943 { | 
| Chris@477 | 2944     if (m_recordTarget->isRecording()) { | 
| Chris@477 | 2945         m_recordTarget->stopRecording(); | 
| Chris@477 | 2946     } | 
| Chris@477 | 2947 | 
| Chris@45 | 2948     m_playSource->stop(); | 
| Chris@45 | 2949 | 
| Chris@45 | 2950     if (m_paneStack && m_paneStack->getCurrentPane()) { | 
| Chris@45 | 2951         updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); | 
| Chris@45 | 2952     } else { | 
| Chris@45 | 2953         m_myStatusMessage = ""; | 
| Chris@378 | 2954         getStatusLabel()->setText(""); | 
| Chris@45 | 2955     } | 
| Chris@45 | 2956 } | 
| Chris@45 | 2957 | 
| Chris@45 | 2958 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) : | 
| Chris@45 | 2959     m_mw(mw), | 
| Chris@45 | 2960     m_pane(0), | 
| Chris@45 | 2961     m_prevCurrentPane(0), | 
| Chris@45 | 2962     m_added(false) | 
| Chris@45 | 2963 { | 
| Chris@45 | 2964 } | 
| Chris@45 | 2965 | 
| Chris@45 | 2966 MainWindowBase::AddPaneCommand::~AddPaneCommand() | 
| Chris@45 | 2967 { | 
| Chris@45 | 2968     if (m_pane && !m_added) { | 
| Chris@45 | 2969 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@45 | 2970     } | 
| Chris@45 | 2971 } | 
| Chris@45 | 2972 | 
| Chris@45 | 2973 QString | 
| Chris@45 | 2974 MainWindowBase::AddPaneCommand::getName() const | 
| Chris@45 | 2975 { | 
| Chris@45 | 2976     return tr("Add Pane"); | 
| Chris@45 | 2977 } | 
| Chris@45 | 2978 | 
| Chris@45 | 2979 void | 
| Chris@45 | 2980 MainWindowBase::AddPaneCommand::execute() | 
| Chris@45 | 2981 { | 
| Chris@45 | 2982     if (!m_pane) { | 
| Chris@45 | 2983 	m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@45 | 2984 	m_pane = m_mw->m_paneStack->addPane(); | 
| Chris@45 | 2985 | 
| Chris@45 | 2986         connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), | 
| Chris@45 | 2987                 m_mw, SLOT(contextHelpChanged(const QString &))); | 
| Chris@45 | 2988     } else { | 
| Chris@45 | 2989 	m_mw->m_paneStack->showPane(m_pane); | 
| Chris@45 | 2990     } | 
| Chris@45 | 2991 | 
| Chris@45 | 2992     m_mw->m_paneStack->setCurrentPane(m_pane); | 
| Chris@45 | 2993     m_added = true; | 
| Chris@45 | 2994 } | 
| Chris@45 | 2995 | 
| Chris@45 | 2996 void | 
| Chris@45 | 2997 MainWindowBase::AddPaneCommand::unexecute() | 
| Chris@45 | 2998 { | 
| Chris@45 | 2999     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@45 | 3000     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@45 | 3001     m_added = false; | 
| Chris@45 | 3002 } | 
| Chris@45 | 3003 | 
| Chris@45 | 3004 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) : | 
| Chris@45 | 3005     m_mw(mw), | 
| Chris@45 | 3006     m_pane(pane), | 
| Chris@409 | 3007     m_prevCurrentPane(0), | 
| Chris@45 | 3008     m_added(true) | 
| Chris@45 | 3009 { | 
| Chris@45 | 3010 } | 
| Chris@45 | 3011 | 
| Chris@45 | 3012 MainWindowBase::RemovePaneCommand::~RemovePaneCommand() | 
| Chris@45 | 3013 { | 
| Chris@45 | 3014     if (m_pane && !m_added) { | 
| Chris@45 | 3015 	m_mw->m_paneStack->deletePane(m_pane); | 
| Chris@45 | 3016     } | 
| Chris@45 | 3017 } | 
| Chris@45 | 3018 | 
| Chris@45 | 3019 QString | 
| Chris@45 | 3020 MainWindowBase::RemovePaneCommand::getName() const | 
| Chris@45 | 3021 { | 
| Chris@45 | 3022     return tr("Remove Pane"); | 
| Chris@45 | 3023 } | 
| Chris@45 | 3024 | 
| Chris@45 | 3025 void | 
| Chris@45 | 3026 MainWindowBase::RemovePaneCommand::execute() | 
| Chris@45 | 3027 { | 
| Chris@45 | 3028     m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | 
| Chris@45 | 3029     m_mw->m_paneStack->hidePane(m_pane); | 
| Chris@45 | 3030     m_added = false; | 
| Chris@45 | 3031 } | 
| Chris@45 | 3032 | 
| Chris@45 | 3033 void | 
| Chris@45 | 3034 MainWindowBase::RemovePaneCommand::unexecute() | 
| Chris@45 | 3035 { | 
| Chris@45 | 3036     m_mw->m_paneStack->showPane(m_pane); | 
| Chris@45 | 3037     m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | 
| Chris@45 | 3038     m_added = true; | 
| Chris@45 | 3039 } | 
| Chris@45 | 3040 | 
| Chris@45 | 3041 void | 
| Chris@45 | 3042 MainWindowBase::deleteCurrentPane() | 
| Chris@45 | 3043 { | 
| Chris@45 | 3044     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@45 | 3045 	(tr("Delete Pane"), true); | 
| Chris@45 | 3046 | 
| Chris@45 | 3047     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 3048     if (pane) { | 
| Chris@45 | 3049 	while (pane->getLayerCount() > 0) { | 
| Chris@45 | 3050 	    Layer *layer = pane->getLayer(0); | 
| Chris@45 | 3051 	    if (layer) { | 
| Chris@45 | 3052 		m_document->removeLayerFromView(pane, layer); | 
| Chris@45 | 3053 	    } else { | 
| Chris@45 | 3054 		break; | 
| Chris@45 | 3055 	    } | 
| Chris@45 | 3056 	} | 
| Chris@45 | 3057 | 
| Chris@45 | 3058 	RemovePaneCommand *command = new RemovePaneCommand(this, pane); | 
| Chris@45 | 3059 	CommandHistory::getInstance()->addCommand(command); | 
| Chris@45 | 3060     } | 
| Chris@45 | 3061 | 
| Chris@45 | 3062     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@45 | 3063 | 
| Chris@45 | 3064     updateMenuStates(); | 
| Chris@45 | 3065 } | 
| Chris@45 | 3066 | 
| Chris@45 | 3067 void | 
| Chris@45 | 3068 MainWindowBase::deleteCurrentLayer() | 
| Chris@45 | 3069 { | 
| Chris@45 | 3070     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@45 | 3071     if (pane) { | 
| Chris@45 | 3072 	Layer *layer = pane->getSelectedLayer(); | 
| Chris@45 | 3073 	if (layer) { | 
| Chris@45 | 3074 	    m_document->removeLayerFromView(pane, layer); | 
| Chris@45 | 3075 	} | 
| Chris@45 | 3076     } | 
| Chris@45 | 3077     updateMenuStates(); | 
| Chris@45 | 3078 } | 
| Chris@45 | 3079 | 
| Chris@45 | 3080 void | 
| Chris@123 | 3081 MainWindowBase::editCurrentLayer() | 
| Chris@123 | 3082 { | 
| Chris@123 | 3083     Layer *layer = 0; | 
| Chris@123 | 3084     Pane *pane = m_paneStack->getCurrentPane(); | 
| Chris@123 | 3085     if (pane) layer = pane->getSelectedLayer(); | 
| Chris@123 | 3086     if (!layer) return; | 
| Chris@123 | 3087 | 
| Chris@123 | 3088     Model *model = layer->getModel(); | 
| Chris@123 | 3089     if (!model) return; | 
| Chris@123 | 3090 | 
| Chris@124 | 3091     TabularModel *tabular = dynamic_cast<TabularModel *>(model); | 
| Chris@124 | 3092     if (!tabular) { | 
| Chris@124 | 3093         //!!! how to prevent this function from being active if not | 
| Chris@124 | 3094         //appropriate model type?  or will we ultimately support | 
| Chris@124 | 3095         //tabular display for all editable models? | 
| Chris@233 | 3096         SVDEBUG << "NOTE: Not a tabular model" << endl; | 
| Chris@124 | 3097         return; | 
| Chris@124 | 3098     } | 
| Chris@124 | 3099 | 
| Chris@123 | 3100     if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) { | 
| Chris@126 | 3101         if (!m_layerDataDialogMap[layer].isNull()) { | 
| Chris@126 | 3102             m_layerDataDialogMap[layer]->show(); | 
| Chris@126 | 3103             m_layerDataDialogMap[layer]->raise(); | 
| Chris@126 | 3104             return; | 
| Chris@126 | 3105         } | 
| Chris@123 | 3106     } | 
| Chris@123 | 3107 | 
| Chris@125 | 3108     QString title = layer->getLayerPresentationName(); | 
| Chris@125 | 3109 | 
| Chris@125 | 3110     ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this); | 
| Chris@128 | 3111     dialog->setAttribute(Qt::WA_DeleteOnClose); | 
| Chris@128 | 3112 | 
| Chris@128 | 3113     connectLayerEditDialog(dialog); | 
| Chris@123 | 3114 | 
| Chris@128 | 3115     m_layerDataDialogMap[layer] = dialog; | 
| Chris@128 | 3116     m_viewDataDialogMap[pane].insert(dialog); | 
| Chris@128 | 3117 | 
| Chris@128 | 3118     dialog->show(); | 
| Chris@128 | 3119 } | 
| Chris@128 | 3120 | 
| Chris@128 | 3121 void | 
| Chris@128 | 3122 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog) | 
| Chris@128 | 3123 { | 
| Chris@123 | 3124     connect(m_viewManager, | 
| Chris@435 | 3125             SIGNAL(globalCentreFrameChanged(sv_frame_t)), | 
| Chris@123 | 3126             dialog, | 
| Chris@435 | 3127             SLOT(userScrolledToFrame(sv_frame_t))); | 
| Chris@127 | 3128 | 
| Chris@127 | 3129     connect(m_viewManager, | 
| Chris@435 | 3130             SIGNAL(playbackFrameChanged(sv_frame_t)), | 
| Chris@127 | 3131             dialog, | 
| Chris@435 | 3132             SLOT(playbackScrolledToFrame(sv_frame_t))); | 
| Chris@127 | 3133 | 
| Chris@123 | 3134     connect(dialog, | 
| Chris@435 | 3135             SIGNAL(scrollToFrame(sv_frame_t)), | 
| Chris@123 | 3136             m_viewManager, | 
| Chris@435 | 3137             SLOT(setGlobalCentreFrame(sv_frame_t))); | 
| Chris@129 | 3138 | 
| Chris@129 | 3139     connect(dialog, | 
| Chris@435 | 3140             SIGNAL(scrollToFrame(sv_frame_t)), | 
| Chris@129 | 3141             m_viewManager, | 
| Chris@435 | 3142             SLOT(setPlaybackFrame(sv_frame_t))); | 
| Chris@128 | 3143 } | 
| Chris@123 | 3144 | 
| Chris@123 | 3145 void | 
| Chris@73 | 3146 MainWindowBase::previousPane() | 
| Chris@73 | 3147 { | 
| Chris@73 | 3148     if (!m_paneStack) return; | 
| Chris@73 | 3149 | 
| Chris@73 | 3150     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 3151     if (!currentPane) return; | 
| Chris@73 | 3152 | 
| Chris@73 | 3153     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 3154         if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 3155             if (i == 0) return; | 
| Chris@73 | 3156             m_paneStack->setCurrentPane(m_paneStack->getPane(i-1)); | 
| Chris@73 | 3157             updateMenuStates(); | 
| Chris@73 | 3158             return; | 
| Chris@73 | 3159         } | 
| Chris@73 | 3160     } | 
| Chris@73 | 3161 } | 
| Chris@73 | 3162 | 
| Chris@73 | 3163 void | 
| Chris@73 | 3164 MainWindowBase::nextPane() | 
| Chris@73 | 3165 { | 
| Chris@73 | 3166     if (!m_paneStack) return; | 
| Chris@73 | 3167 | 
| Chris@73 | 3168     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 3169     if (!currentPane) return; | 
| Chris@73 | 3170 | 
| Chris@73 | 3171     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@73 | 3172         if (m_paneStack->getPane(i) == currentPane) { | 
| Chris@73 | 3173             if (i == m_paneStack->getPaneCount()-1) return; | 
| Chris@73 | 3174             m_paneStack->setCurrentPane(m_paneStack->getPane(i+1)); | 
| Chris@73 | 3175             updateMenuStates(); | 
| Chris@73 | 3176             return; | 
| Chris@73 | 3177         } | 
| Chris@73 | 3178     } | 
| Chris@73 | 3179 } | 
| Chris@73 | 3180 | 
| Chris@73 | 3181 void | 
| Chris@73 | 3182 MainWindowBase::previousLayer() | 
| Chris@73 | 3183 { | 
| Chris@73 | 3184     if (!m_paneStack) return; | 
| Chris@73 | 3185 | 
| Chris@73 | 3186     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 3187     if (!currentPane) return; | 
| Chris@73 | 3188 | 
| Chris@403 | 3189     int count = currentPane->getLayerCount(); | 
| Chris@403 | 3190     if (count == 0) return; | 
| Chris@403 | 3191 | 
| Chris@73 | 3192     Layer *currentLayer = currentPane->getSelectedLayer(); | 
| Chris@403 | 3193 | 
| Chris@403 | 3194     if (!currentLayer) { | 
| Chris@403 | 3195         // The pane itself is current | 
| Chris@403 | 3196         m_paneStack->setCurrentLayer | 
| Chris@403 | 3197             (currentPane, currentPane->getFixedOrderLayer(count-1)); | 
| Chris@403 | 3198     } else { | 
| Chris@403 | 3199         for (int i = 0; i < count; ++i) { | 
| Chris@403 | 3200             if (currentPane->getFixedOrderLayer(i) == currentLayer) { | 
| Chris@403 | 3201                 if (i == 0) { | 
| Chris@403 | 3202                     m_paneStack->setCurrentLayer | 
| Chris@403 | 3203                         (currentPane, 0); // pane | 
| Chris@403 | 3204                 } else { | 
| Chris@403 | 3205                     m_paneStack->setCurrentLayer | 
| Chris@403 | 3206                         (currentPane, currentPane->getFixedOrderLayer(i-1)); | 
| Chris@403 | 3207                 } | 
| Chris@403 | 3208                 break; | 
| Chris@403 | 3209             } | 
| Chris@73 | 3210         } | 
| Chris@73 | 3211     } | 
| Chris@403 | 3212 | 
| Chris@403 | 3213     updateMenuStates(); | 
| Chris@73 | 3214 } | 
| Chris@73 | 3215 | 
| Chris@73 | 3216 void | 
| Chris@73 | 3217 MainWindowBase::nextLayer() | 
| Chris@73 | 3218 { | 
| Chris@73 | 3219     if (!m_paneStack) return; | 
| Chris@73 | 3220 | 
| Chris@73 | 3221     Pane *currentPane = m_paneStack->getCurrentPane(); | 
| Chris@73 | 3222     if (!currentPane) return; | 
| Chris@73 | 3223 | 
| Chris@403 | 3224     int count = currentPane->getLayerCount(); | 
| Chris@403 | 3225     if (count == 0) return; | 
| Chris@403 | 3226 | 
| Chris@73 | 3227     Layer *currentLayer = currentPane->getSelectedLayer(); | 
| Chris@403 | 3228 | 
| Chris@403 | 3229     if (!currentLayer) { | 
| Chris@403 | 3230         // The pane itself is current | 
| Chris@403 | 3231         m_paneStack->setCurrentLayer | 
| Chris@403 | 3232             (currentPane, currentPane->getFixedOrderLayer(0)); | 
| Chris@403 | 3233     } else { | 
| Chris@403 | 3234         for (int i = 0; i < count; ++i) { | 
| Chris@403 | 3235             if (currentPane->getFixedOrderLayer(i) == currentLayer) { | 
| Chris@403 | 3236                 if (i == currentPane->getLayerCount()-1) { | 
| Chris@403 | 3237                     m_paneStack->setCurrentLayer | 
| Chris@403 | 3238                         (currentPane, 0); // pane | 
| Chris@403 | 3239                 } else { | 
| Chris@403 | 3240                     m_paneStack->setCurrentLayer | 
| Chris@403 | 3241                         (currentPane, currentPane->getFixedOrderLayer(i+1)); | 
| Chris@403 | 3242                 } | 
| Chris@403 | 3243                 break; | 
| Chris@403 | 3244             } | 
| Chris@73 | 3245         } | 
| Chris@73 | 3246     } | 
| Chris@403 | 3247 | 
| Chris@403 | 3248     updateMenuStates(); | 
| Chris@73 | 3249 } | 
| Chris@73 | 3250 | 
| Chris@73 | 3251 void | 
| Chris@435 | 3252 MainWindowBase::playbackFrameChanged(sv_frame_t frame) | 
| Chris@45 | 3253 { | 
| Chris@45 | 3254     if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 3255 | 
| Chris@187 | 3256     updatePositionStatusDisplays(); | 
| Chris@187 | 3257 | 
| Chris@45 | 3258     RealTime now = RealTime::frame2RealTime | 
| Chris@45 | 3259         (frame, getMainModel()->getSampleRate()); | 
| Chris@45 | 3260 | 
| Chris@45 | 3261     if (now.sec == m_lastPlayStatusSec) return; | 
| Chris@45 | 3262 | 
| Chris@45 | 3263     RealTime then = RealTime::frame2RealTime | 
| Chris@45 | 3264         (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); | 
| Chris@45 | 3265 | 
| Chris@45 | 3266     QString nowStr; | 
| Chris@45 | 3267     QString thenStr; | 
| Chris@45 | 3268     QString remainingStr; | 
| Chris@45 | 3269 | 
| Chris@45 | 3270     if (then.sec > 10) { | 
| Chris@45 | 3271         nowStr = now.toSecText().c_str(); | 
| Chris@45 | 3272         thenStr = then.toSecText().c_str(); | 
| Chris@45 | 3273         remainingStr = (then - now).toSecText().c_str(); | 
| Chris@45 | 3274         m_lastPlayStatusSec = now.sec; | 
| Chris@45 | 3275     } else { | 
| Chris@45 | 3276         nowStr = now.toText(true).c_str(); | 
| Chris@45 | 3277         thenStr = then.toText(true).c_str(); | 
| Chris@45 | 3278         remainingStr = (then - now).toText(true).c_str(); | 
| Chris@45 | 3279     } | 
| Chris@45 | 3280 | 
| Chris@45 | 3281     m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") | 
| Chris@45 | 3282         .arg(nowStr).arg(thenStr).arg(remainingStr); | 
| Chris@45 | 3283 | 
| Chris@378 | 3284     getStatusLabel()->setText(m_myStatusMessage); | 
| Chris@45 | 3285 } | 
| Chris@45 | 3286 | 
| Chris@45 | 3287 void | 
| Chris@435 | 3288 MainWindowBase::globalCentreFrameChanged(sv_frame_t ) | 
| Chris@45 | 3289 { | 
| Chris@45 | 3290     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 3291     Pane *p = 0; | 
| Chris@45 | 3292     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 3293     if (!p->getFollowGlobalPan()) return; | 
| Chris@45 | 3294     updateVisibleRangeDisplay(p); | 
| Chris@45 | 3295 } | 
| Chris@45 | 3296 | 
| Chris@45 | 3297 void | 
| Chris@435 | 3298 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame) | 
| Chris@45 | 3299 { | 
| Chris@233 | 3300 //    SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl; | 
| Chris@123 | 3301 | 
| Chris@123 | 3302     if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) { | 
| Chris@123 | 3303         for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin(); | 
| Chris@123 | 3304              i != m_viewDataDialogMap[v].end(); ++i) { | 
| Chris@127 | 3305             (*i)->userScrolledToFrame(frame); | 
| Chris@123 | 3306         } | 
| Chris@123 | 3307     } | 
| Chris@45 | 3308     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 3309     Pane *p = 0; | 
| Chris@45 | 3310     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 3311     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@45 | 3312 } | 
| Chris@45 | 3313 | 
| Chris@45 | 3314 void | 
| Chris@366 | 3315 MainWindowBase::viewZoomLevelChanged(View *v, int , bool ) | 
| Chris@45 | 3316 { | 
| Chris@45 | 3317     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; | 
| Chris@45 | 3318     Pane *p = 0; | 
| Chris@45 | 3319     if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; | 
| Chris@45 | 3320     if (v == p) updateVisibleRangeDisplay(p); | 
| Chris@45 | 3321 } | 
| Chris@45 | 3322 | 
| Chris@45 | 3323 void | 
| Chris@45 | 3324 MainWindowBase::layerAdded(Layer *) | 
| Chris@45 | 3325 { | 
| Chris@233 | 3326 //    SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl; | 
| Chris@45 | 3327     updateMenuStates(); | 
| Chris@45 | 3328 } | 
| Chris@45 | 3329 | 
| Chris@45 | 3330 void | 
| Chris@45 | 3331 MainWindowBase::layerRemoved(Layer *) | 
| Chris@45 | 3332 { | 
| Chris@233 | 3333 //    SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl; | 
| Chris@45 | 3334     updateMenuStates(); | 
| Chris@45 | 3335 } | 
| Chris@45 | 3336 | 
| Chris@45 | 3337 void | 
| Chris@45 | 3338 MainWindowBase::layerAboutToBeDeleted(Layer *layer) | 
| Chris@45 | 3339 { | 
| Chris@233 | 3340 //    SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl; | 
| Chris@123 | 3341 | 
| Chris@128 | 3342     removeLayerEditDialog(layer); | 
| Chris@123 | 3343 | 
| Chris@47 | 3344     if (m_timeRulerLayer && (layer == m_timeRulerLayer)) { | 
| Chris@293 | 3345 //	cerr << "(this is the time ruler layer)" << endl; | 
| Chris@45 | 3346 	m_timeRulerLayer = 0; | 
| Chris@45 | 3347     } | 
| Chris@45 | 3348 } | 
| Chris@45 | 3349 | 
| Chris@45 | 3350 void | 
| Chris@45 | 3351 MainWindowBase::layerInAView(Layer *layer, bool inAView) | 
| Chris@45 | 3352 { | 
| Chris@233 | 3353 //    SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl; | 
| Chris@128 | 3354 | 
| Chris@128 | 3355     if (!inAView) removeLayerEditDialog(layer); | 
| Chris@45 | 3356 | 
| Chris@45 | 3357     // Check whether we need to add or remove model from play source | 
| Chris@45 | 3358     Model *model = layer->getModel(); | 
| Chris@45 | 3359     if (model) { | 
| Chris@45 | 3360         if (inAView) { | 
| Chris@45 | 3361             m_playSource->addModel(model); | 
| Chris@45 | 3362         } else { | 
| Chris@45 | 3363             bool found = false; | 
| Chris@45 | 3364             for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@45 | 3365                 Pane *pane = m_paneStack->getPane(i); | 
| Chris@45 | 3366                 if (!pane) continue; | 
| Chris@45 | 3367                 for (int j = 0; j < pane->getLayerCount(); ++j) { | 
| Chris@45 | 3368                     Layer *pl = pane->getLayer(j); | 
| Chris@183 | 3369                     if (pl && | 
| Chris@183 | 3370                         !dynamic_cast<TimeRulerLayer *>(pl) && | 
| Chris@183 | 3371                         (pl->getModel() == model)) { | 
| Chris@45 | 3372                         found = true; | 
| Chris@45 | 3373                         break; | 
| Chris@45 | 3374                     } | 
| Chris@45 | 3375                 } | 
| Chris@45 | 3376                 if (found) break; | 
| Chris@45 | 3377             } | 
| Chris@173 | 3378             if (!found) { | 
| Chris@173 | 3379                 m_playSource->removeModel(model); | 
| Chris@173 | 3380             } | 
| Chris@45 | 3381         } | 
| Chris@45 | 3382     } | 
| Chris@45 | 3383 | 
| Chris@45 | 3384     updateMenuStates(); | 
| Chris@45 | 3385 } | 
| Chris@45 | 3386 | 
| Chris@45 | 3387 void | 
| Chris@128 | 3388 MainWindowBase::removeLayerEditDialog(Layer *layer) | 
| Chris@128 | 3389 { | 
| Chris@128 | 3390     if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) { | 
| Chris@128 | 3391 | 
| Chris@128 | 3392         ModelDataTableDialog *dialog = m_layerDataDialogMap[layer]; | 
| Chris@128 | 3393 | 
| Chris@128 | 3394         for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin(); | 
| Chris@128 | 3395              vi != m_viewDataDialogMap.end(); ++vi) { | 
| Chris@128 | 3396             vi->second.erase(dialog); | 
| Chris@128 | 3397         } | 
| Chris@128 | 3398 | 
| Chris@128 | 3399         m_layerDataDialogMap.erase(layer); | 
| Chris@128 | 3400         delete dialog; | 
| Chris@128 | 3401     } | 
| Chris@128 | 3402 } | 
| Chris@128 | 3403 | 
| Chris@128 | 3404 void | 
| Chris@45 | 3405 MainWindowBase::modelAdded(Model *model) | 
| Chris@45 | 3406 { | 
| Chris@233 | 3407 //    SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl; | 
| gyorgyf@273 | 3408 	std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl; | 
| Chris@45 | 3409     m_playSource->addModel(model); | 
| Chris@45 | 3410 } | 
| Chris@45 | 3411 | 
| Chris@45 | 3412 void | 
| Chris@45 | 3413 MainWindowBase::mainModelChanged(WaveFileModel *model) | 
| Chris@45 | 3414 { | 
| Chris@233 | 3415 //    SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; | 
| Chris@45 | 3416     updateDescriptionLabel(); | 
| Chris@45 | 3417     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); | 
| Chris@475 | 3418     if (model && !(m_playTarget || m_audioIO) && | 
| Chris@475 | 3419         (m_soundOptions & WithAudioOutput)) { | 
| Chris@475 | 3420         createAudioIO(); | 
| Chris@360 | 3421     } | 
| Chris@45 | 3422 } | 
| Chris@45 | 3423 | 
| Chris@45 | 3424 void | 
| Chris@45 | 3425 MainWindowBase::modelAboutToBeDeleted(Model *model) | 
| Chris@45 | 3426 { | 
| Chris@233 | 3427 //    SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl; | 
| Chris@45 | 3428     if (model == m_viewManager->getPlaybackModel()) { | 
| Chris@45 | 3429         m_viewManager->setPlaybackModel(0); | 
| Chris@45 | 3430     } | 
| Chris@45 | 3431     m_playSource->removeModel(model); | 
| Chris@45 | 3432     FFTDataServer::modelAboutToBeDeleted(model); | 
| Chris@45 | 3433 } | 
| Chris@45 | 3434 | 
| Chris@45 | 3435 void | 
| Chris@55 | 3436 MainWindowBase::paneDeleteButtonClicked(Pane *pane) | 
| Chris@55 | 3437 { | 
| Chris@55 | 3438     bool found = false; | 
| Chris@55 | 3439     for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | 
| Chris@55 | 3440         if (m_paneStack->getPane(i) == pane) { | 
| Chris@55 | 3441             found = true; | 
| Chris@55 | 3442             break; | 
| Chris@55 | 3443         } | 
| Chris@55 | 3444     } | 
| Chris@55 | 3445     if (!found) { | 
| Chris@233 | 3446         SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane " | 
| Chris@229 | 3447                   << pane << endl; | 
| Chris@55 | 3448         return; | 
| Chris@55 | 3449     } | 
| Chris@55 | 3450 | 
| Chris@55 | 3451     CommandHistory::getInstance()->startCompoundOperation | 
| Chris@55 | 3452 	(tr("Delete Pane"), true); | 
| Chris@55 | 3453 | 
| Chris@55 | 3454     while (pane->getLayerCount() > 0) { | 
| Chris@55 | 3455         Layer *layer = pane->getLayer(0); | 
| Chris@55 | 3456         if (layer) { | 
| Chris@55 | 3457             m_document->removeLayerFromView(pane, layer); | 
| Chris@55 | 3458         } else { | 
| Chris@55 | 3459             break; | 
| Chris@55 | 3460         } | 
| Chris@55 | 3461     } | 
| Chris@55 | 3462 | 
| Chris@55 | 3463     RemovePaneCommand *command = new RemovePaneCommand(this, pane); | 
| Chris@55 | 3464     CommandHistory::getInstance()->addCommand(command); | 
| Chris@55 | 3465 | 
| Chris@55 | 3466     CommandHistory::getInstance()->endCompoundOperation(); | 
| Chris@55 | 3467 | 
| Chris@55 | 3468     updateMenuStates(); | 
| Chris@55 | 3469 } | 
| Chris@55 | 3470 | 
| Chris@55 | 3471 void | 
| Chris@45 | 3472 MainWindowBase::pollOSC() | 
| Chris@45 | 3473 { | 
| Chris@45 | 3474     if (!m_oscQueue || m_oscQueue->isEmpty()) return; | 
| Chris@233 | 3475     SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl; | 
| Chris@45 | 3476 | 
| Chris@45 | 3477     if (m_openingAudioFile) return; | 
| Chris@45 | 3478 | 
| Chris@45 | 3479     OSCMessage message = m_oscQueue->readMessage(); | 
| Chris@45 | 3480 | 
| Chris@45 | 3481     if (message.getTarget() != 0) { | 
| Chris@45 | 3482         return; //!!! for now -- this class is target 0, others not handled yet | 
| Chris@45 | 3483     } | 
| Chris@45 | 3484 | 
| Chris@45 | 3485     handleOSCMessage(message); | 
| Chris@45 | 3486 } | 
| Chris@45 | 3487 | 
| Chris@45 | 3488 void | 
| Chris@45 | 3489 MainWindowBase::inProgressSelectionChanged() | 
| Chris@45 | 3490 { | 
| Chris@45 | 3491     Pane *currentPane = 0; | 
| Chris@45 | 3492     if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); | 
| justin@331 | 3493     if (currentPane) { | 
| justin@331 | 3494         //cerr << "JTEST: mouse event on selection pane" << endl; | 
| justin@331 | 3495         updateVisibleRangeDisplay(currentPane); | 
| justin@331 | 3496     } | 
| Chris@45 | 3497 } | 
| Chris@45 | 3498 | 
| Chris@45 | 3499 void | 
| Chris@45 | 3500 MainWindowBase::contextHelpChanged(const QString &s) | 
| Chris@45 | 3501 { | 
| Chris@378 | 3502     QLabel *lab = getStatusLabel(); | 
| Chris@375 | 3503 | 
| Chris@45 | 3504     if (s == "" && m_myStatusMessage != "") { | 
| Chris@378 | 3505         if (lab->text() != m_myStatusMessage) { | 
| Chris@378 | 3506             lab->setText(m_myStatusMessage); | 
| Chris@375 | 3507         } | 
| Chris@45 | 3508         return; | 
| Chris@45 | 3509     } | 
| Chris@375 | 3510 | 
| Chris@378 | 3511     lab->setText(s); | 
| Chris@45 | 3512 } | 
| Chris@45 | 3513 | 
| Chris@45 | 3514 void | 
| Chris@45 | 3515 MainWindowBase::openHelpUrl(QString url) | 
| Chris@45 | 3516 { | 
| Chris@45 | 3517     // This method mostly lifted from Qt Assistant source code | 
| Chris@45 | 3518 | 
| Chris@45 | 3519     QProcess *process = new QProcess(this); | 
| Chris@45 | 3520     connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); | 
| Chris@45 | 3521 | 
| Chris@45 | 3522     QStringList args; | 
| Chris@45 | 3523 | 
| Chris@45 | 3524 #ifdef Q_OS_MAC | 
| Chris@45 | 3525     args.append(url); | 
| Chris@45 | 3526     process->start("open", args); | 
| Chris@45 | 3527 #else | 
| Chris@45 | 3528 #ifdef Q_OS_WIN32 | 
| Chris@358 | 3529     QString pf(getenv("ProgramFiles")); | 
| Chris@358 | 3530     QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); | 
| Chris@358 | 3531 | 
| Chris@358 | 3532     args.append(url); | 
| Chris@358 | 3533     process->start(command, args); | 
| Chris@45 | 3534 #else | 
| Chris@45 | 3535     if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { | 
| Chris@45 | 3536         args.append("exec"); | 
| Chris@45 | 3537         args.append(url); | 
| Chris@45 | 3538         process->start("kfmclient", args); | 
| Chris@45 | 3539     } else if (!qgetenv("BROWSER").isEmpty()) { | 
| Chris@45 | 3540         args.append(url); | 
| Chris@45 | 3541         process->start(qgetenv("BROWSER"), args); | 
| Chris@45 | 3542     } else { | 
| Chris@45 | 3543         args.append(url); | 
| Chris@45 | 3544         process->start("firefox", args); | 
| Chris@45 | 3545     } | 
| Chris@45 | 3546 #endif | 
| Chris@45 | 3547 #endif | 
| Chris@45 | 3548 } | 
| Chris@45 | 3549 | 
| Chris@180 | 3550 |