annotate framework/MainWindowBase.cpp @ 766:cf4e0f3c2406

Profiling points
author Chris Cannam
date Thu, 14 May 2020 16:38:48 +0100
parents 4c91c95e146a
children 7bded7599874
rev   line source
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@479 21 #include "data/model/ReadOnlyWaveFileModel.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"
Chris@45 25 #include "data/model/Labeller.h"
Chris@124 26 #include "data/model/TabularModel.h"
Chris@45 27 #include "view/ViewManager.h"
Chris@45 28
Chris@45 29 #include "layer/WaveformLayer.h"
Chris@45 30 #include "layer/TimeRulerLayer.h"
Chris@45 31 #include "layer/TimeInstantLayer.h"
Chris@45 32 #include "layer/TimeValueLayer.h"
Chris@45 33 #include "layer/Colour3DPlotLayer.h"
Chris@45 34 #include "layer/SliceLayer.h"
Chris@45 35 #include "layer/SliceableLayer.h"
Chris@45 36 #include "layer/ImageLayer.h"
Chris@184 37 #include "layer/NoteLayer.h"
matthiasm@267 38 #include "layer/FlexiNoteLayer.h"
Chris@184 39 #include "layer/RegionLayer.h"
Chris@722 40 #include "layer/SpectrogramLayer.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@574 51 #include "audio/AudioCallbackRecordTarget.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@659 58 #include "data/fileio/CSVFileWriter.h"
Chris@45 59 #include "data/fileio/BZipFileDevice.h"
Chris@45 60 #include "data/fileio/FileSource.h"
Chris@152 61 #include "data/fileio/AudioFileReaderFactory.h"
Chris@762 62 #include "data/fileio/TextTest.h"
Chris@134 63 #include "rdf/RDFImporter.h"
Chris@659 64 #include "rdf/RDFExporter.h"
Chris@45 65
Chris@724 66 #include "transform/ModelTransformerFactory.h"
Chris@724 67
Chris@45 68 #include "base/RecentFiles.h"
Chris@45 69
Chris@45 70 #include "base/XmlExportable.h"
Chris@45 71 #include "base/Profiler.h"
Chris@45 72 #include "base/Preferences.h"
Chris@217 73 #include "base/TempWriteFile.h"
Chris@217 74 #include "base/Exceptions.h"
Chris@223 75 #include "base/ResourceFinder.h"
Chris@45 76
Chris@45 77 #include "data/osc/OSCQueue.h"
Chris@157 78 #include "data/midi/MIDIInput.h"
Chris@654 79 #include "OSCScript.h"
Chris@45 80
Chris@599 81 #include "system/System.h"
Chris@599 82
Chris@468 83 #include <bqaudioio/SystemPlaybackTarget.h>
Chris@475 84 #include <bqaudioio/SystemAudioIO.h>
Chris@468 85 #include <bqaudioio/AudioFactory.h>
Chris@468 86
Chris@45 87 #include <QApplication>
Chris@45 88 #include <QMessageBox>
Chris@45 89 #include <QGridLayout>
Chris@45 90 #include <QLabel>
Chris@45 91 #include <QAction>
Chris@45 92 #include <QMenuBar>
Chris@45 93 #include <QToolBar>
Chris@45 94 #include <QInputDialog>
Chris@45 95 #include <QStatusBar>
Chris@45 96 #include <QTreeView>
Chris@45 97 #include <QFile>
Chris@45 98 #include <QFileInfo>
Chris@45 99 #include <QDir>
Chris@45 100 #include <QTextStream>
Chris@432 101 #include <QTextCodec>
Chris@45 102 #include <QProcess>
Chris@45 103 #include <QShortcut>
Chris@45 104 #include <QSettings>
Chris@45 105 #include <QDateTime>
Chris@45 106 #include <QProcess>
Chris@45 107 #include <QCheckBox>
Chris@45 108 #include <QRegExp>
Chris@45 109 #include <QScrollArea>
Chris@684 110 #include <QScreen>
Chris@354 111 #include <QSignalMapper>
Chris@45 112
Chris@45 113 #include <iostream>
Chris@45 114 #include <cstdio>
Chris@45 115 #include <errno.h>
Chris@45 116
Chris@45 117 using std::vector;
Chris@45 118 using std::map;
Chris@45 119 using std::set;
Chris@45 120
Chris@255 121 #ifdef Q_WS_X11
Chris@255 122 #define Window X11Window
Chris@255 123 #include <X11/Xlib.h>
Chris@255 124 #include <X11/Xutil.h>
Chris@255 125 #include <X11/Xatom.h>
Chris@255 126 #include <X11/SM/SMlib.h>
Chris@255 127
Chris@255 128 static int handle_x11_error(Display *dpy, XErrorEvent *err)
Chris@255 129 {
Chris@255 130 char errstr[256];
Chris@255 131 XGetErrorText(dpy, err->error_code, errstr, 256);
Chris@255 132 if (err->error_code != BadWindow) {
Chris@595 133 cerr << "Sonic Visualiser: X Error: "
Chris@595 134 << errstr << " " << int(err->error_code)
Chris@595 135 << "\nin major opcode: "
Chris@595 136 << int(err->request_code) << endl;
Chris@255 137 }
Chris@255 138 return 0;
Chris@255 139 }
Chris@255 140 #undef Window
Chris@255 141 #endif
Chris@45 142
Chris@714 143 MainWindowBase::MainWindowBase(AudioMode audioMode,
Chris@714 144 MIDIMode midiMode,
Chris@712 145 PaneStack::Options paneStackOptions) :
Chris@636 146 m_document(nullptr),
Chris@636 147 m_paneStack(nullptr),
Chris@636 148 m_viewManager(nullptr),
Chris@636 149 m_timeRulerLayer(nullptr),
Chris@714 150 m_audioMode(audioMode),
Chris@714 151 m_midiMode(midiMode),
Chris@636 152 m_playSource(nullptr),
Chris@636 153 m_recordTarget(nullptr),
Chris@636 154 m_playTarget(nullptr),
Chris@636 155 m_audioIO(nullptr),
Chris@636 156 m_oscQueue(nullptr),
Chris@636 157 m_oscQueueStarter(nullptr),
Chris@654 158 m_oscScript(nullptr),
Chris@636 159 m_midiInput(nullptr),
Chris@45 160 m_recentFiles("RecentFiles", 20),
Chris@54 161 m_recentTransforms("RecentTransforms", 20),
Chris@45 162 m_documentModified(false),
Chris@45 163 m_openingAudioFile(false),
Chris@758 164 m_handlingOSC(false),
Chris@636 165 m_labeller(nullptr),
Chris@357 166 m_lastPlayStatusSec(0),
Chris@357 167 m_initialDarkBackground(false),
Chris@378 168 m_defaultFfwdRwdStep(2, 0),
Chris@483 169 m_audioRecordMode(RecordCreateAdditionalModel),
Chris@636 170 m_statusLabel(nullptr),
Chris@426 171 m_iconsVisibleInMenus(true),
Chris@636 172 m_menuShortcutMapper(nullptr)
Chris@45 173 {
Chris@113 174 Profiler profiler("MainWindowBase::MainWindowBase");
Chris@113 175
Chris@591 176 SVDEBUG << "MainWindowBase::MainWindowBase" << endl;
Chris@591 177
Chris@438 178 qRegisterMetaType<sv_frame_t>("sv_frame_t");
Chris@438 179 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
Chris@687 180 qRegisterMetaType<ModelId>("ModelId");
Chris@438 181
Chris@255 182 #ifdef Q_WS_X11
Chris@255 183 XSetErrorHandler(handle_x11_error);
Chris@255 184 #endif
Chris@255 185
Chris@452 186 connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash()));
Chris@452 187
Chris@45 188 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
Chris@595 189 this, SLOT(documentModified()));
Chris@45 190 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
Chris@595 191 this, SLOT(documentRestored()));
Chris@45 192
Chris@591 193 SVDEBUG << "MainWindowBase: Creating view manager" << endl;
Chris@591 194
Chris@45 195 m_viewManager = new ViewManager();
Chris@45 196 connect(m_viewManager, SIGNAL(selectionChanged()),
Chris@595 197 this, SLOT(updateMenuStates()));
Chris@45 198 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
Chris@595 199 this, SLOT(inProgressSelectionChanged()));
Chris@45 200
Chris@591 201 SVDEBUG << "MainWindowBase: Calculating view font size" << endl;
Chris@591 202
Chris@105 203 // set a sensible default font size for views -- cannot do this
Chris@105 204 // in Preferences, which is in base and not supposed to use QtGui
Chris@436 205 int viewFontSize = int(QApplication::font().pointSize() * 0.9);
Chris@105 206 QSettings settings;
Chris@105 207 settings.beginGroup("Preferences");
Chris@105 208 viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
Chris@105 209 settings.setValue("view-font-size", viewFontSize);
Chris@105 210 settings.endGroup();
Chris@105 211
Chris@591 212 SVDEBUG << "MainWindowBase: View font size is " << viewFontSize << endl;
Chris@591 213
Chris@732 214 #ifndef Q_OS_MAC
Chris@734 215
Chris@45 216 Preferences::BackgroundMode mode =
Chris@45 217 Preferences::getInstance()->getBackgroundMode();
Chris@734 218
Chris@734 219 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
Chris@734 220
Chris@734 221 if (OSReportsDarkThemeActive()) {
Chris@734 222 // NB !(OSReportsDarkThemeActive()) doesn't necessarily mean
Chris@734 223 // the theme is light - the function also cunningly returns
Chris@734 224 // false if it has no way to tell
Chris@734 225 m_initialDarkBackground = true;
Chris@733 226 }
Chris@734 227
Chris@734 228 if (mode == Preferences::BackgroundFromTheme) {
Chris@734 229 m_viewManager->setGlobalDarkBackground
Chris@734 230 (m_initialDarkBackground);
Chris@734 231 } else {
Chris@45 232 m_viewManager->setGlobalDarkBackground
Chris@45 233 (mode == Preferences::DarkBackground);
Chris@45 234 }
Chris@734 235
Chris@511 236 #endif
Chris@45 237
Chris@712 238 m_paneStack = new PaneStack(nullptr, m_viewManager, paneStackOptions);
Chris@45 239 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
Chris@595 240 this, SLOT(currentPaneChanged(Pane *)));
Chris@45 241 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
Chris@595 242 this, SLOT(currentLayerChanged(Pane *, Layer *)));
Chris@743 243 connect(m_paneStack, SIGNAL(paneRightButtonMenuRequested(Pane *, QPoint)),
Chris@743 244 this, SLOT(paneRightButtonMenuRequested(Pane *, QPoint)));
Chris@743 245 connect(m_paneStack, SIGNAL(panePropertiesRightButtonMenuRequested(Pane *, QPoint)),
Chris@743 246 this, SLOT(panePropertiesRightButtonMenuRequested(Pane *, QPoint)));
Chris@743 247 connect(m_paneStack, SIGNAL(layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint)),
Chris@743 248 this, SLOT(layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint)));
Chris@45 249 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 250 this, SLOT(contextHelpChanged(const QString &)));
Chris@45 251 connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
Chris@45 252 this, SLOT(paneAdded(Pane *)));
Chris@45 253 connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
Chris@45 254 this, SLOT(paneHidden(Pane *)));
Chris@45 255 connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
Chris@45 256 this, SLOT(paneAboutToBeDeleted(Pane *)));
Chris@45 257 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
Chris@45 258 this, SLOT(paneDropAccepted(Pane *, QStringList)));
Chris@45 259 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
Chris@45 260 this, SLOT(paneDropAccepted(Pane *, QString)));
Chris@55 261 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
Chris@55 262 this, SLOT(paneDeleteButtonClicked(Pane *)));
Chris@591 263
Chris@591 264 SVDEBUG << "MainWindowBase: Creating play source" << endl;
Chris@571 265
Chris@574 266 m_playSource = new AudioCallbackPlaySource
Chris@574 267 (m_viewManager, QApplication::applicationName());
Chris@574 268
Chris@714 269 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 270 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@591 271 SVDEBUG << "MainWindowBase: Creating record target" << endl;
Chris@574 272 m_recordTarget = new AudioCallbackRecordTarget
Chris@574 273 (m_viewManager, QApplication::applicationName());
Chris@572 274 connect(m_recordTarget,
Chris@572 275 SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
Chris@572 276 this,
Chris@572 277 SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
Chris@475 278 }
Chris@45 279
Chris@436 280 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
Chris@591 281 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
Chris@570 282 connect(m_playSource, SIGNAL(channelCountIncreased(int)),
Chris@570 283 this, SLOT(audioChannelCountIncreased(int)));
Chris@45 284 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
Chris@45 285 this, SLOT(audioOverloadPluginDisabled()));
Chris@45 286
Chris@574 287 connect(m_viewManager, SIGNAL(monitoringLevelsChanged(float, float)),
Chris@595 288 this, SLOT(monitoringLevelsChanged(float, float)));
Chris@45 289
Chris@435 290 connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@435 291 this, SLOT(playbackFrameChanged(sv_frame_t)));
Chris@435 292
Chris@435 293 connect(m_viewManager, SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@435 294 this, SLOT(globalCentreFrameChanged(sv_frame_t)));
Chris@435 295
Chris@435 296 connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)),
Chris@435 297 this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
Chris@366 298
Chris@640 299 connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, ZoomLevel, bool)),
Chris@640 300 this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
Chris@45 301
Chris@45 302 connect(Preferences::getInstance(),
Chris@45 303 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@45 304 this,
Chris@45 305 SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@45 306
Chris@591 307 SVDEBUG << "MainWindowBase: Creating labeller" << endl;
Chris@591 308
Chris@45 309 Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
Chris@45 310 settings.beginGroup("MainWindow");
Chris@230 311
Chris@45 312 labellerType = (Labeller::ValueType)
Chris@45 313 settings.value("labellertype", (int)labellerType).toInt();
Chris@45 314 int cycle = settings.value("labellercycle", 4).toInt();
Chris@230 315
Chris@45 316 settings.endGroup();
Chris@45 317
Chris@45 318 m_labeller = new Labeller(labellerType);
Chris@45 319 m_labeller->setCounterCycleSize(cycle);
Chris@113 320
Chris@714 321 if (m_midiMode == MIDI_LISTEN) {
Chris@591 322 SVDEBUG << "MainWindowBase: Creating MIDI input" << endl;
Chris@161 323 m_midiInput = new MIDIInput(QApplication::applicationName(), this);
Chris@161 324 }
Chris@452 325
Chris@452 326 QTimer::singleShot(1500, this, SIGNAL(hideSplash()));
Chris@591 327
Chris@591 328 SVDEBUG << "MainWindowBase: Constructor done" << endl;
Chris@45 329 }
Chris@45 330
Chris@45 331 MainWindowBase::~MainWindowBase()
Chris@45 332 {
Chris@233 333 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
Chris@540 334
Chris@540 335 // We have to delete the breakfastquay::SystemPlaybackTarget or
Chris@540 336 // breakfastquay::SystemAudioIO object (whichever we have -- it
Chris@540 337 // depends on whether we handle recording or not) before we delete
Chris@540 338 // the ApplicationPlaybackSource and ApplicationRecordTarget that
Chris@540 339 // they refer to.
Chris@556 340
Chris@556 341 deleteAudioIO();
Chris@540 342
Chris@540 343 // Then delete the Application objects.
Chris@45 344 delete m_playSource;
Chris@475 345 delete m_recordTarget;
Chris@540 346
Chris@45 347 delete m_viewManager;
cannam@632 348 delete m_midiInput;
cannam@632 349
Chris@654 350 if (m_oscScript) {
Chris@654 351 disconnect(m_oscScript, nullptr, nullptr, nullptr);
Chris@654 352 m_oscScript->abandon();
Chris@654 353 m_oscScript->wait(1000);
Chris@654 354 if (m_oscScript->isRunning()) {
Chris@654 355 m_oscScript->terminate();
Chris@654 356 m_oscScript->wait(1000);
Chris@654 357 }
Chris@654 358 delete m_oscScript;
Chris@654 359 }
Chris@654 360
Chris@639 361 if (m_oscQueueStarter) {
Chris@644 362 disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr);
cannam@632 363 m_oscQueueStarter->wait(1000);
Chris@639 364 if (m_oscQueueStarter->isRunning()) {
Chris@639 365 m_oscQueueStarter->terminate();
Chris@639 366 m_oscQueueStarter->wait(1000);
Chris@639 367 }
Chris@639 368 delete m_oscQueueStarter;
Chris@639 369 delete m_oscQueue;
cannam@632 370 }
cannam@632 371
Chris@45 372 Profiles::getInstance()->dump();
Chris@45 373 }
Chris@45 374
Chris@113 375 void
Chris@452 376 MainWindowBase::emitHideSplash()
Chris@452 377 {
Chris@591 378 SVDEBUG << "MainWindowBase: Hiding splash screen" << endl;
Chris@452 379 emit hideSplash(this);
Chris@452 380 }
Chris@452 381
Chris@452 382 void
Chris@354 383 MainWindowBase::finaliseMenus()
Chris@354 384 {
Chris@591 385 SVDEBUG << "MainWindowBase::finaliseMenus called" << endl;
Chris@591 386
Chris@390 387 delete m_menuShortcutMapper;
Chris@636 388 m_menuShortcutMapper = nullptr;
Chris@390 389
Chris@391 390 foreach (QShortcut *sc, m_appShortcuts) {
Chris@391 391 delete sc;
Chris@391 392 }
Chris@391 393 m_appShortcuts.clear();
Chris@391 394
Chris@354 395 QMenuBar *mb = menuBar();
Chris@394 396
Chris@396 397 // This used to find all children of QMenu type, and call
Chris@396 398 // finaliseMenu on those. But it seems we are getting hold of some
Chris@396 399 // menus that way that are not actually active in the menu bar and
Chris@396 400 // are not returned in their parent menu's actions() list, and if
Chris@396 401 // we finalise those, we end up with duplicate shortcuts in the
Chris@396 402 // app shortcut mapper. So we should do this by descending the
Chris@396 403 // menu tree through only those menus accessible via actions()
Chris@396 404 // from their parents instead.
Chris@396 405
Chris@394 406 QList<QMenu *> menus = mb->findChildren<QMenu *>
Chris@394 407 (QString(), Qt::FindDirectChildrenOnly);
Chris@394 408
Chris@354 409 foreach (QMenu *menu, menus) {
Chris@354 410 if (menu) finaliseMenu(menu);
Chris@354 411 }
Chris@591 412
Chris@591 413 SVDEBUG << "MainWindowBase::finaliseMenus done" << endl;
Chris@354 414 }
Chris@354 415
Chris@354 416 void
Chris@426 417 MainWindowBase::finaliseMenu(QMenu *menu)
Chris@354 418 {
Chris@426 419 foreach (QAction *a, menu->actions()) {
Chris@426 420 a->setIconVisibleInMenu(m_iconsVisibleInMenus);
Chris@426 421 }
Chris@426 422
Chris@354 423 #ifdef Q_OS_MAC
Chris@354 424 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
Chris@354 425 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
Chris@354 426 // single-key shortcuts that are associated only with a menu
Chris@384 427 // action (and not with a toolbar button) do not work with Qt 5.x
Chris@384 428 // under OS/X.
Chris@354 429 //
Chris@354 430 // Apparently Cocoa never handled them as a matter of course, but
Chris@354 431 // earlier versions of Qt picked them up as widget shortcuts and
Chris@354 432 // handled them anyway. That behaviour was removed to fix a crash
Chris@354 433 // when invoking a menu while its window was overridden by a modal
Chris@354 434 // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
Chris@354 435 //
Chris@354 436 // This workaround restores the single-key shortcut behaviour by
Chris@384 437 // searching in menus for single-key shortcuts that are associated
Chris@384 438 // only with the menu and not with a toolbar button, and
Chris@384 439 // augmenting them with global application shortcuts that invoke
Chris@384 440 // the relevant actions, testing whether the actions are enabled
Chris@384 441 // on invocation.
Chris@354 442 //
Chris@384 443 // (Previously this acted on all single-key shortcuts in menus,
Chris@384 444 // and it removed the shortcut from the action when it created
Chris@384 445 // each new global one, in order to avoid an "ambiguous shortcut"
Chris@384 446 // error in the case where the action was also associated with a
Chris@384 447 // toolbar button. But that has the unwelcome side-effect of
Chris@384 448 // removing the shortcut hint from the menu entry. So now we leave
Chris@384 449 // the shortcut in the menu action as well as creating a global
Chris@384 450 // one, and we only act on shortcuts that have no toolbar button,
Chris@384 451 // i.e. that will not otherwise work. The downside is that if this
Chris@384 452 // bug is fixed in a future Qt release, we will start getting
Chris@384 453 // "ambiguous shortcut" errors from the menu entry actions and
Chris@384 454 // will need to update the code.)
Chris@354 455
Chris@443 456 // Update: The bug was fixed in Qt 5.4 for shortcuts with no
Chris@443 457 // modifier, and I believe it is fixed in Qt 5.5 for shortcuts
Chris@443 458 // with Shift modifiers. The below reflects that
Chris@443 459
Chris@443 460 #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
Chris@443 461
Chris@390 462 if (!m_menuShortcutMapper) {
Chris@390 463 m_menuShortcutMapper = new QSignalMapper(this);
Chris@392 464 connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)),
Chris@392 465 this, SLOT(menuActionMapperInvoked(QObject *)));
Chris@390 466 }
Chris@390 467
Chris@354 468 foreach (QAction *a, menu->actions()) {
Chris@394 469
Chris@394 470 if (a->isSeparator()) {
Chris@394 471 continue;
Chris@394 472 } else if (a->menu()) {
Chris@394 473 finaliseMenu(a->menu());
Chris@394 474 } else {
Chris@394 475
Chris@394 476 QWidgetList ww = a->associatedWidgets();
Chris@394 477 bool hasButton = false;
Chris@394 478 foreach (QWidget *w, ww) {
Chris@394 479 if (qobject_cast<QAbstractButton *>(w)) {
Chris@394 480 hasButton = true;
Chris@394 481 break;
Chris@394 482 }
Chris@394 483 }
Chris@394 484 if (hasButton) continue;
Chris@394 485 QKeySequence sc = a->shortcut();
Chris@399 486
Chris@399 487 // Note that the set of "single-key shortcuts" that aren't
Chris@399 488 // working and that we need to handle here includes those
Chris@399 489 // with the Shift modifier mask as well as those with no
Chris@399 490 // modifier at all
Chris@443 491 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
Chris@443 492 // Nothing needed
Chris@443 493 if (false) {
Chris@443 494 #elif (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
Chris@443 495 if (sc.count() == 1 &&
Chris@443 496 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier) {
Chris@443 497 #else
Chris@399 498 if (sc.count() == 1 &&
Chris@399 499 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier ||
Chris@399 500 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) {
Chris@443 501 #endif
Chris@394 502 QShortcut *newSc = new QShortcut(sc, a->parentWidget());
Chris@394 503 QObject::connect(newSc, SIGNAL(activated()),
Chris@394 504 m_menuShortcutMapper, SLOT(map()));
Chris@394 505 m_menuShortcutMapper->setMapping(newSc, a);
Chris@394 506 m_appShortcuts.push_back(newSc);
Chris@384 507 }
Chris@384 508 }
Chris@354 509 }
Chris@354 510 #endif
Chris@443 511 #endif
Chris@354 512 }
Chris@354 513
Chris@354 514 void
Chris@354 515 MainWindowBase::menuActionMapperInvoked(QObject *o)
Chris@354 516 {
Chris@354 517 QAction *a = qobject_cast<QAction *>(o);
Chris@354 518 if (a && a->isEnabled()) {
Chris@354 519 a->trigger();
Chris@354 520 }
Chris@354 521 }
Chris@354 522
Chris@354 523 void
Chris@168 524 MainWindowBase::resizeConstrained(QSize size)
Chris@168 525 {
Chris@684 526 QScreen *screen = QApplication::primaryScreen();
Chris@684 527 QRect available = screen->availableGeometry();
Chris@168 528 QSize actual(std::min(size.width(), available.width()),
Chris@168 529 std::min(size.height(), available.height()));
Chris@168 530 resize(actual);
Chris@168 531 }
Chris@168 532
Chris@168 533 void
Chris@658 534 MainWindowBase::startOSCQueue(bool withNetworkPort)
Chris@304 535 {
Chris@658 536 m_oscQueueStarter = new OSCQueueStarter(this, withNetworkPort);
Chris@304 537 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
Chris@304 538 m_oscQueueStarter->start();
Chris@304 539 }
Chris@304 540
Chris@304 541 void
Chris@113 542 MainWindowBase::oscReady()
Chris@113 543 {
Chris@113 544 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@113 545 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
Chris@113 546 QTimer *oscTimer = new QTimer(this);
Chris@113 547 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
Chris@725 548 oscTimer->start(2000);
Chris@658 549
Chris@658 550 if (m_oscQueue->hasPort()) {
Chris@658 551 SVDEBUG << "Finished setting up OSC interface" << endl;
Chris@658 552 } else {
Chris@658 553 SVDEBUG << "Finished setting up internal-only OSC queue" << endl;
Chris@658 554 }
Chris@654 555
Chris@654 556 if (m_oscScriptFile != QString()) {
Chris@654 557 startOSCScript();
Chris@654 558 }
Chris@113 559 }
Chris@113 560 }
Chris@113 561
Chris@654 562 void
Chris@654 563 MainWindowBase::startOSCScript()
Chris@654 564 {
Chris@654 565 m_oscScript = new OSCScript(m_oscScriptFile, m_oscQueue);
Chris@654 566 connect(m_oscScript, SIGNAL(finished()),
Chris@654 567 this, SLOT(oscScriptFinished()));
Chris@654 568 m_oscScriptFile = QString();
Chris@654 569 m_oscScript->start();
Chris@654 570 }
Chris@654 571
Chris@654 572 void
Chris@654 573 MainWindowBase::cueOSCScript(QString fileName)
Chris@654 574 {
Chris@654 575 m_oscScriptFile = fileName;
Chris@654 576 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@654 577 startOSCScript();
Chris@654 578 }
Chris@654 579 }
Chris@654 580
Chris@654 581 void
Chris@654 582 MainWindowBase::oscScriptFinished()
Chris@654 583 {
Chris@654 584 delete m_oscScript;
Chris@654 585 m_oscScript = 0;
Chris@654 586 }
Chris@654 587
Chris@45 588 QString
Chris@45 589 MainWindowBase::getOpenFileName(FileFinder::FileType type)
Chris@45 590 {
Chris@45 591 FileFinder *ff = FileFinder::getInstance();
Chris@358 592
Chris@358 593 if (type == FileFinder::AnyFile) {
Chris@684 594 if (!getMainModelId().isNone() &&
Chris@636 595 m_paneStack != nullptr &&
Chris@636 596 m_paneStack->getCurrentPane() != nullptr) { // can import a layer
Chris@45 597 return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
Chris@45 598 } else {
Chris@45 599 return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
Chris@45 600 m_sessionFile);
Chris@45 601 }
Chris@358 602 }
Chris@358 603
Chris@358 604 QString lastPath = m_sessionFile;
Chris@358 605
Chris@358 606 if (type == FileFinder::AudioFile) {
Chris@358 607 lastPath = m_audioFile;
Chris@45 608 }
Chris@358 609
Chris@358 610 return ff->getOpenFileName(type, lastPath);
Chris@45 611 }
Chris@45 612
Chris@45 613 QString
Chris@45 614 MainWindowBase::getSaveFileName(FileFinder::FileType type)
Chris@45 615 {
Chris@358 616 QString lastPath = m_sessionFile;
Chris@358 617
Chris@358 618 if (type == FileFinder::AudioFile) {
Chris@358 619 lastPath = m_audioFile;
Chris@358 620 }
Chris@358 621
Chris@45 622 FileFinder *ff = FileFinder::getInstance();
Chris@358 623 return ff->getSaveFileName(type, lastPath);
Chris@45 624 }
Chris@45 625
Chris@45 626 void
Chris@45 627 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
Chris@45 628 {
Chris@45 629 FileFinder *ff = FileFinder::getInstance();
Chris@45 630 ff->registerLastOpenedFilePath(type, path);
Chris@45 631 }
Chris@45 632
Chris@222 633 QString
Chris@222 634 MainWindowBase::getDefaultSessionTemplate() const
Chris@222 635 {
Chris@231 636 QSettings settings;
Chris@231 637 settings.beginGroup("MainWindow");
Chris@231 638 QString templateName = settings.value("sessiontemplate", "").toString();
Chris@231 639 if (templateName == "") templateName = "default";
Chris@231 640 return templateName;
Chris@222 641 }
Chris@222 642
Chris@222 643 void
Chris@251 644 MainWindowBase::setDefaultSessionTemplate(QString n)
Chris@251 645 {
Chris@251 646 QSettings settings;
Chris@251 647 settings.beginGroup("MainWindow");
Chris@251 648 settings.setValue("sessiontemplate", n);
Chris@251 649 }
Chris@251 650
Chris@251 651 void
Chris@45 652 MainWindowBase::updateMenuStates()
Chris@45 653 {
Chris@636 654 Pane *currentPane = nullptr;
Chris@636 655 Layer *currentLayer = nullptr;
Chris@45 656
Chris@45 657 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@45 658 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@45 659
Chris@73 660 bool havePrevPane = false, haveNextPane = false;
Chris@73 661 bool havePrevLayer = false, haveNextLayer = false;
Chris@73 662
Chris@73 663 if (currentPane) {
Chris@73 664 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 665 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 666 if (i > 0) havePrevPane = true;
Chris@73 667 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true;
Chris@73 668 break;
Chris@73 669 }
Chris@73 670 }
Chris@403 671 // the prev/next layer commands actually include the pane
Chris@403 672 // itself as one of the selectables -- so we always have a
Chris@403 673 // prev and next layer, as long as we have a pane with at
Chris@403 674 // least one layer in it
Chris@403 675 if (currentPane->getLayerCount() > 0) {
Chris@403 676 havePrevLayer = true;
Chris@403 677 haveNextLayer = true;
Chris@73 678 }
Chris@73 679 }
Chris@73 680
Chris@45 681 bool haveCurrentPane =
Chris@636 682 (currentPane != nullptr);
Chris@45 683 bool haveCurrentLayer =
Chris@45 684 (haveCurrentPane &&
Chris@636 685 (currentLayer != nullptr));
Chris@45 686 bool haveMainModel =
Chris@684 687 (!getMainModelId().isNone());
Chris@45 688 bool havePlayTarget =
Chris@636 689 (m_playTarget != nullptr || m_audioIO != nullptr);
Chris@45 690 bool haveSelection =
Chris@595 691 (m_viewManager &&
Chris@595 692 !m_viewManager->getSelections().empty());
Chris@45 693 bool haveCurrentEditableLayer =
Chris@595 694 (haveCurrentLayer &&
Chris@595 695 currentLayer->isLayerEditable());
Chris@45 696 bool haveCurrentTimeInstantsLayer =
Chris@595 697 (haveCurrentLayer &&
Chris@595 698 dynamic_cast<TimeInstantLayer *>(currentLayer));
Chris@184 699 bool haveCurrentDurationLayer =
Chris@595 700 (haveCurrentLayer &&
Chris@595 701 (dynamic_cast<NoteLayer *>(currentLayer) ||
Chris@595 702 dynamic_cast<FlexiNoteLayer *>(currentLayer) ||
Chris@184 703 dynamic_cast<RegionLayer *>(currentLayer)));
Chris@45 704 bool haveCurrentColour3DPlot =
Chris@45 705 (haveCurrentLayer &&
Chris@45 706 dynamic_cast<Colour3DPlotLayer *>(currentLayer));
Chris@722 707 bool haveCurrentSpectrogram =
Chris@722 708 (haveCurrentLayer &&
Chris@722 709 dynamic_cast<SpectrogramLayer *>(currentLayer));
Chris@45 710 bool haveClipboardContents =
Chris@45 711 (m_viewManager &&
Chris@45 712 !m_viewManager->getClipboard().empty());
Chris@146 713 bool haveTabularLayer =
Chris@146 714 (haveCurrentLayer &&
Chris@684 715 ModelById::isa<TabularModel>(currentLayer->getModel()));
Chris@45 716
Chris@45 717 emit canAddPane(haveMainModel);
Chris@45 718 emit canDeleteCurrentPane(haveCurrentPane);
Chris@45 719 emit canZoom(haveMainModel && haveCurrentPane);
Chris@45 720 emit canScroll(haveMainModel && haveCurrentPane);
Chris@45 721 emit canAddLayer(haveMainModel && haveCurrentPane);
Chris@45 722 emit canImportMoreAudio(haveMainModel);
Chris@259 723 emit canReplaceMainAudio(haveMainModel);
Chris@45 724 emit canImportLayer(haveMainModel && haveCurrentPane);
Chris@45 725 emit canExportAudio(haveMainModel);
Chris@289 726 emit canChangeSessionTemplate(haveMainModel);
Chris@45 727 emit canExportLayer(haveMainModel &&
Chris@722 728 (haveCurrentEditableLayer ||
Chris@722 729 haveCurrentColour3DPlot ||
Chris@722 730 haveCurrentSpectrogram));
Chris@45 731 emit canExportImage(haveMainModel && haveCurrentPane);
Chris@45 732 emit canDeleteCurrentLayer(haveCurrentLayer);
Chris@45 733 emit canRenameLayer(haveCurrentLayer);
Chris@45 734 emit canEditLayer(haveCurrentEditableLayer);
Chris@146 735 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer);
Chris@45 736 emit canMeasureLayer(haveCurrentLayer);
Chris@45 737 emit canSelect(haveMainModel && haveCurrentPane);
Chris@188 738 emit canPlay(haveMainModel && havePlayTarget);
Chris@453 739 emit canFfwd(haveMainModel);
Chris@453 740 emit canRewind(haveMainModel);
Chris@87 741 emit canPaste(haveClipboardContents);
Chris@45 742 emit canInsertInstant(haveCurrentPane);
Chris@45 743 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
Chris@184 744 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer);
Chris@45 745 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@537 746 emit canSubdivideInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@538 747 emit canWinnowInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@45 748 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
Chris@45 749 emit canClearSelection(haveSelection);
Chris@45 750 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
Chris@45 751 emit canSave(m_sessionFile != "" && m_documentModified);
Chris@601 752 emit canSaveAs(haveMainModel); // possibly used only in Tony, not SV
Chris@73 753 emit canSelectPreviousPane(havePrevPane);
Chris@73 754 emit canSelectNextPane(haveNextPane);
Chris@73 755 emit canSelectPreviousLayer(havePrevLayer);
Chris@73 756 emit canSelectNextLayer(haveNextLayer);
Chris@586 757
Chris@586 758 // This is quite subtle -- whereas we can play back only if a
Chris@586 759 // system play target or I/O exists, we can record even if no
Chris@586 760 // record source (i.e. audioIO) exists because we can record into
Chris@586 761 // an empty session before the audio device has been
Chris@714 762 // opened.
Chris@714 763 //
Chris@714 764 // However, if there is no record *target* then recording was
Chris@714 765 // actively disabled via the audio mode setting.
Chris@714 766 //
Chris@714 767 // If we have a play target instead of an audioIO, then if the
Chris@714 768 // audio mode is AUDIO_PLAYBACK_NOW_RECORD_LATER, we are still
Chris@714 769 // expecting to open the IO on demand, but if it is
Chris@714 770 // AUDIO_PLAYBACK_AND_RECORD then we must have tried to open the
Chris@714 771 // device and failed to find any capture source.
Chris@714 772 //
Chris@586 773 bool recordDisabled = (m_recordTarget == nullptr);
Chris@714 774 bool recordDeviceFailed =
Chris@714 775 (m_audioMode == AUDIO_PLAYBACK_AND_RECORD &&
Chris@714 776 (m_playTarget != nullptr && m_audioIO == nullptr));
Chris@586 777 emit canRecord(!recordDisabled && !recordDeviceFailed);
Chris@45 778 }
Chris@45 779
Chris@45 780 void
Chris@669 781 MainWindowBase::updateWindowTitle()
Chris@669 782 {
Chris@669 783 QString title;
Chris@669 784
Chris@669 785 if (m_sessionFile != "") {
Chris@669 786 if (m_originalLocation != "" &&
Chris@669 787 m_originalLocation != m_sessionFile) { // session + location
Chris@669 788 title = tr("%1: %2 [%3]")
Chris@669 789 .arg(QApplication::applicationName())
Chris@669 790 .arg(QFileInfo(m_sessionFile).fileName())
Chris@669 791 .arg(m_originalLocation);
Chris@669 792 } else { // session only
Chris@669 793 title = tr("%1: %2")
Chris@669 794 .arg(QApplication::applicationName())
Chris@669 795 .arg(QFileInfo(m_sessionFile).fileName());
Chris@669 796 }
Chris@669 797 } else {
Chris@669 798 if (m_originalLocation != "") { // location only
Chris@669 799 title = tr("%1: %2")
Chris@669 800 .arg(QApplication::applicationName())
Chris@669 801 .arg(m_originalLocation);
Chris@669 802 } else { // neither
Chris@669 803 title = QApplication::applicationName();
Chris@669 804 }
Chris@669 805 }
Chris@669 806
Chris@669 807 if (m_documentModified) {
Chris@669 808 title = tr("%1 (modified)").arg(title);
Chris@669 809 }
Chris@669 810
Chris@669 811 setWindowTitle(title);
Chris@669 812 }
Chris@669 813
Chris@669 814 void
Chris@45 815 MainWindowBase::documentModified()
Chris@45 816 {
Chris@233 817 // SVDEBUG << "MainWindowBase::documentModified" << endl;
Chris@45 818
Chris@45 819 m_documentModified = true;
Chris@669 820 updateWindowTitle();
Chris@45 821 updateMenuStates();
Chris@45 822 }
Chris@45 823
Chris@45 824 void
Chris@45 825 MainWindowBase::documentRestored()
Chris@45 826 {
Chris@233 827 // SVDEBUG << "MainWindowBase::documentRestored" << endl;
Chris@45 828
Chris@45 829 m_documentModified = false;
Chris@669 830 updateWindowTitle();
Chris@45 831 updateMenuStates();
Chris@45 832 }
Chris@45 833
Chris@45 834 void
Chris@45 835 MainWindowBase::playLoopToggled()
Chris@45 836 {
Chris@45 837 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 838
Chris@45 839 if (action) {
Chris@595 840 m_viewManager->setPlayLoopMode(action->isChecked());
Chris@45 841 } else {
Chris@595 842 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
Chris@45 843 }
Chris@45 844 }
Chris@45 845
Chris@45 846 void
Chris@45 847 MainWindowBase::playSelectionToggled()
Chris@45 848 {
Chris@45 849 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 850
Chris@45 851 if (action) {
Chris@595 852 m_viewManager->setPlaySelectionMode(action->isChecked());
Chris@45 853 } else {
Chris@595 854 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
Chris@45 855 }
Chris@45 856 }
Chris@45 857
Chris@45 858 void
Chris@45 859 MainWindowBase::playSoloToggled()
Chris@45 860 {
Chris@45 861 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 862
Chris@45 863 if (action) {
Chris@595 864 m_viewManager->setPlaySoloMode(action->isChecked());
Chris@45 865 } else {
Chris@595 866 m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
Chris@45 867 }
Chris@45 868
Chris@45 869 if (m_viewManager->getPlaySoloMode()) {
Chris@45 870 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@45 871 } else {
Chris@684 872 m_viewManager->setPlaybackModel({});
Chris@45 873 if (m_playSource) {
Chris@45 874 m_playSource->clearSoloModelSet();
Chris@45 875 }
Chris@45 876 }
Chris@45 877 }
Chris@45 878
Chris@45 879 void
Chris@45 880 MainWindowBase::currentPaneChanged(Pane *p)
Chris@45 881 {
Chris@45 882 updateMenuStates();
Chris@45 883 updateVisibleRangeDisplay(p);
Chris@45 884
Chris@45 885 if (!p) return;
Chris@45 886
Chris@45 887 if (!(m_viewManager &&
Chris@45 888 m_playSource &&
Chris@45 889 m_viewManager->getPlaySoloMode())) {
Chris@684 890 if (m_viewManager) {
Chris@684 891 m_viewManager->setPlaybackModel(ModelId());
Chris@684 892 }
Chris@45 893 return;
Chris@45 894 }
Chris@45 895
Chris@684 896 ModelId prevPlaybackModel = m_viewManager->getPlaybackModel();
Chris@60 897
Chris@93 898 // What we want here is not the currently playing frame (unless we
Chris@93 899 // are about to clear out the audio playback buffers -- which may
Chris@93 900 // or may not be possible, depending on the audio driver). What
Chris@93 901 // we want is the frame that was last committed to the soundcard
Chris@93 902 // buffers, as the audio driver will continue playing up to that
Chris@93 903 // frame before switching to whichever one we decide we want to
Chris@93 904 // switch to, regardless of our efforts.
Chris@93 905
Chris@435 906 sv_frame_t frame = m_playSource->getCurrentBufferedFrame();
Chris@93 907
Chris@388 908 cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
Chris@45 909
Chris@45 910 View::ModelSet soloModels = p->getModels();
Chris@45 911
Chris@57 912 View::ModelSet sources;
Chris@684 913 for (ModelId modelId: sources) {
Chris@190 914 // If a model in this pane is derived from something else,
Chris@190 915 // then we want to play that model as well -- if the model
Chris@190 916 // that's derived from it is not something that is itself
Chris@190 917 // individually playable (e.g. a waveform)
Chris@684 918 if (auto model = ModelById::get(modelId)) {
Chris@684 919 if (!ModelById::isa<RangeSummarisableTimeValueModel>(modelId) &&
Chris@684 920 !model->getSourceModel().isNone()) {
Chris@684 921 sources.insert(model->getSourceModel());
Chris@684 922 }
Chris@57 923 }
Chris@57 924 }
Chris@684 925 for (ModelId modelId: sources) {
Chris@684 926 soloModels.insert(modelId);
Chris@57 927 }
Chris@57 928
Chris@60 929 //!!! Need an "atomic" way of telling the play source that the
Chris@60 930 //playback model has changed, and changing it on ViewManager --
Chris@60 931 //the play source should be making the setPlaybackModel call to
Chris@60 932 //ViewManager
Chris@60 933
Chris@684 934 ModelId newPlaybackModel;
Chris@684 935
Chris@684 936 for (ModelId modelId: soloModels) {
Chris@684 937 if (ModelById::isa<RangeSummarisableTimeValueModel>(modelId)) {
Chris@684 938 m_viewManager->setPlaybackModel(modelId);
Chris@684 939 newPlaybackModel = modelId;
Chris@45 940 }
Chris@45 941 }
Chris@45 942
Chris@45 943 m_playSource->setSoloModelSet(soloModels);
Chris@45 944
Chris@684 945 if (!prevPlaybackModel.isNone() && !newPlaybackModel.isNone() &&
Chris@684 946 prevPlaybackModel != newPlaybackModel) {
Chris@684 947 if (m_playSource->isPlaying()) {
Chris@684 948 m_playSource->play(frame);
Chris@684 949 }
Chris@45 950 }
Chris@45 951 }
Chris@45 952
Chris@45 953 void
Chris@45 954 MainWindowBase::currentLayerChanged(Pane *p, Layer *)
Chris@45 955 {
Chris@45 956 updateMenuStates();
Chris@45 957 updateVisibleRangeDisplay(p);
Chris@45 958 }
Chris@45 959
Chris@597 960 sv_frame_t
Chris@597 961 MainWindowBase::getModelsStartFrame() const
Chris@597 962 {
Chris@597 963 sv_frame_t startFrame = 0;
Chris@597 964 if (!m_paneStack) return startFrame;
Chris@597 965 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@597 966 sv_frame_t thisStart = m_paneStack->getPane(i)->getModelsStartFrame();
Chris@597 967 if (i == 0 || thisStart < startFrame) {
Chris@597 968 startFrame = thisStart;
Chris@597 969 }
Chris@597 970 }
Chris@597 971 return startFrame;
Chris@597 972 }
Chris@597 973
Chris@597 974 sv_frame_t
Chris@597 975 MainWindowBase::getModelsEndFrame() const
Chris@597 976 {
Chris@597 977 sv_frame_t endFrame = 0;
Chris@597 978 if (!m_paneStack) return endFrame;
Chris@597 979 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@597 980 sv_frame_t thisEnd = m_paneStack->getPane(i)->getModelsEndFrame();
Chris@597 981 if (i == 0 || thisEnd > endFrame) {
Chris@597 982 endFrame = thisEnd;
Chris@597 983 }
Chris@597 984 }
Chris@597 985 return endFrame;
Chris@597 986 }
Chris@597 987
Chris@45 988 void
Chris@45 989 MainWindowBase::selectAll()
Chris@45 990 {
Chris@597 991 m_viewManager->setSelection(Selection(getModelsStartFrame(),
Chris@597 992 getModelsEndFrame()));
Chris@45 993 }
Chris@45 994
Chris@45 995 void
Chris@45 996 MainWindowBase::selectToStart()
Chris@45 997 {
Chris@684 998 m_viewManager->setSelection(Selection(getModelsStartFrame(),
Chris@595 999 m_viewManager->getGlobalCentreFrame()));
Chris@45 1000 }
Chris@45 1001
Chris@45 1002 void
Chris@45 1003 MainWindowBase::selectToEnd()
Chris@45 1004 {
Chris@45 1005 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
Chris@684 1006 getModelsEndFrame()));
Chris@45 1007 }
Chris@45 1008
Chris@45 1009 void
Chris@45 1010 MainWindowBase::selectVisible()
Chris@45 1011 {
Chris@684 1012 auto model = getMainModel();
Chris@45 1013 if (!model) return;
Chris@45 1014
Chris@45 1015 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1016 if (!currentPane) return;
Chris@45 1017
Chris@435 1018 sv_frame_t startFrame, endFrame;
Chris@45 1019
Chris@684 1020 if (currentPane->getStartFrame() < 0) {
Chris@684 1021 startFrame = 0;
Chris@684 1022 } else {
Chris@684 1023 startFrame = currentPane->getStartFrame();
Chris@684 1024 }
Chris@684 1025
Chris@684 1026 if (currentPane->getEndFrame() > model->getEndFrame()) {
Chris@684 1027 endFrame = model->getEndFrame();
Chris@684 1028 } else {
Chris@684 1029 endFrame = currentPane->getEndFrame();
Chris@684 1030 }
Chris@45 1031
Chris@45 1032 m_viewManager->setSelection(Selection(startFrame, endFrame));
Chris@45 1033 }
Chris@45 1034
Chris@45 1035 void
Chris@45 1036 MainWindowBase::clearSelection()
Chris@45 1037 {
Chris@45 1038 m_viewManager->clearSelections();
Chris@45 1039 }
Chris@45 1040
Chris@45 1041 void
Chris@45 1042 MainWindowBase::cut()
Chris@45 1043 {
Chris@45 1044 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1045 if (!currentPane) return;
Chris@45 1046
Chris@45 1047 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1048 if (!layer) return;
Chris@45 1049
Chris@45 1050 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@45 1051 clipboard.clear();
Chris@45 1052
Chris@45 1053 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1054
Chris@45 1055 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
Chris@45 1056
Chris@45 1057 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1058 i != selections.end(); ++i) {
Chris@86 1059 layer->copy(currentPane, *i, clipboard);
Chris@45 1060 layer->deleteSelection(*i);
Chris@45 1061 }
Chris@45 1062
Chris@45 1063 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1064 }
Chris@45 1065
Chris@45 1066 void
Chris@45 1067 MainWindowBase::copy()
Chris@45 1068 {
Chris@45 1069 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1070 if (!currentPane) return;
Chris@45 1071
Chris@45 1072 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1073 if (!layer) return;
Chris@45 1074
Chris@45 1075 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@45 1076 clipboard.clear();
Chris@45 1077
Chris@45 1078 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1079
Chris@45 1080 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1081 i != selections.end(); ++i) {
Chris@86 1082 layer->copy(currentPane, *i, clipboard);
Chris@45 1083 }
Chris@45 1084 }
Chris@45 1085
Chris@45 1086 void
Chris@45 1087 MainWindowBase::paste()
Chris@45 1088 {
Chris@215 1089 pasteRelative(0);
Chris@215 1090 }
Chris@215 1091
Chris@215 1092 void
Chris@215 1093 MainWindowBase::pasteAtPlaybackPosition()
Chris@215 1094 {
Chris@435 1095 sv_frame_t pos = getFrame();
Chris@215 1096 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@215 1097 if (!clipboard.empty()) {
Chris@435 1098 sv_frame_t firstEventFrame = clipboard.getPoints()[0].getFrame();
Chris@435 1099 sv_frame_t offset = 0;
Chris@215 1100 if (firstEventFrame < 0) {
Chris@366 1101 offset = pos - firstEventFrame;
Chris@354 1102 } else if (firstEventFrame < pos) {
Chris@366 1103 offset = pos - firstEventFrame;
Chris@215 1104 } else {
Chris@366 1105 offset = -(firstEventFrame - pos);
Chris@215 1106 }
Chris@215 1107 pasteRelative(offset);
Chris@215 1108 }
Chris@215 1109 }
Chris@215 1110
Chris@215 1111 void
Chris@435 1112 MainWindowBase::pasteRelative(sv_frame_t offset)
Chris@215 1113 {
Chris@45 1114 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1115 if (!currentPane) return;
Chris@45 1116
Chris@45 1117 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1118
Chris@45 1119 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@87 1120
Chris@98 1121 bool inCompound = false;
Chris@87 1122
Chris@87 1123 if (!layer || !layer->isLayerEditable()) {
Chris@87 1124
Chris@87 1125 CommandHistory::getInstance()->startCompoundOperation
Chris@87 1126 (tr("Paste"), true);
Chris@87 1127
Chris@87 1128 // no suitable current layer: create one of the most
Chris@87 1129 // appropriate sort
Chris@87 1130 LayerFactory::LayerType type =
Chris@87 1131 LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard);
Chris@87 1132 layer = m_document->createEmptyLayer(type);
Chris@87 1133
Chris@87 1134 if (!layer) {
Chris@87 1135 CommandHistory::getInstance()->endCompoundOperation();
Chris@87 1136 return;
Chris@45 1137 }
Chris@87 1138
Chris@87 1139 m_document->addLayerToView(currentPane, layer);
Chris@87 1140 m_paneStack->setCurrentLayer(currentPane, layer);
Chris@87 1141
Chris@87 1142 inCompound = true;
Chris@45 1143 }
Chris@45 1144
Chris@215 1145 layer->paste(currentPane, clipboard, offset, true);
Chris@45 1146
Chris@87 1147 if (inCompound) CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1148 }
Chris@45 1149
Chris@45 1150 void
Chris@45 1151 MainWindowBase::deleteSelected()
Chris@45 1152 {
Chris@45 1153 if (m_paneStack->getCurrentPane() &&
Chris@595 1154 m_paneStack->getCurrentPane()->getSelectedLayer()) {
Chris@45 1155
Chris@45 1156 Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
Chris@45 1157
Chris@409 1158 if (m_viewManager) {
Chris@409 1159
Chris@409 1160 if (m_viewManager->getToolMode() == ViewManager::MeasureMode) {
Chris@409 1161
Chris@409 1162 layer->deleteCurrentMeasureRect();
Chris@45 1163
Chris@409 1164 } else {
Chris@409 1165
Chris@409 1166 MultiSelection::SelectionList selections =
Chris@409 1167 m_viewManager->getSelections();
Chris@409 1168
Chris@409 1169 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@409 1170 i != selections.end(); ++i) {
Chris@409 1171 layer->deleteSelection(*i);
Chris@409 1172 }
Chris@45 1173 }
Chris@595 1174 }
Chris@45 1175 }
Chris@45 1176 }
Chris@45 1177
Chris@161 1178 // FrameTimer method
Chris@161 1179
Chris@435 1180 sv_frame_t
Chris@161 1181 MainWindowBase::getFrame() const
Chris@161 1182 {
Chris@161 1183 if (m_playSource && m_playSource->isPlaying()) {
Chris@161 1184 return m_playSource->getCurrentPlayingFrame();
Chris@161 1185 } else {
Chris@161 1186 return m_viewManager->getPlaybackFrame();
Chris@161 1187 }
Chris@161 1188 }
Chris@161 1189
Chris@45 1190 void
Chris@45 1191 MainWindowBase::insertInstant()
Chris@45 1192 {
Chris@161 1193 insertInstantAt(getFrame());
Chris@45 1194 }
Chris@45 1195
Chris@45 1196 void
Chris@45 1197 MainWindowBase::insertInstantsAtBoundaries()
Chris@45 1198 {
Chris@45 1199 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1200 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1201 i != selections.end(); ++i) {
Chris@435 1202 sv_frame_t start = i->getStartFrame();
Chris@435 1203 sv_frame_t end = i->getEndFrame();
Chris@45 1204 if (start != end) {
Chris@184 1205 insertInstantAt(start);
Chris@184 1206 insertInstantAt(end);
Chris@45 1207 }
Chris@45 1208 }
Chris@45 1209 }
Chris@45 1210
Chris@45 1211 void
Chris@435 1212 MainWindowBase::insertInstantAt(sv_frame_t frame)
Chris@45 1213 {
Chris@45 1214 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1215 if (!pane) {
Chris@45 1216 return;
Chris@45 1217 }
Chris@45 1218
Chris@74 1219 frame = pane->alignFromReference(frame);
Chris@74 1220
Chris@45 1221 Layer *layer = dynamic_cast<TimeInstantLayer *>
Chris@45 1222 (pane->getSelectedLayer());
Chris@45 1223
Chris@45 1224 if (!layer) {
Chris@45 1225 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 1226 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
Chris@45 1227 if (layer) break;
Chris@45 1228 }
Chris@45 1229
Chris@45 1230 if (!layer) {
Chris@45 1231 CommandHistory::getInstance()->startCompoundOperation
Chris@45 1232 (tr("Add Point"), true);
Chris@45 1233 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
Chris@45 1234 if (layer) {
Chris@45 1235 m_document->addLayerToView(pane, layer);
Chris@45 1236 m_paneStack->setCurrentLayer(pane, layer);
Chris@45 1237 }
Chris@45 1238 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1239 }
Chris@45 1240 }
Chris@45 1241
Chris@45 1242 if (layer) {
Chris@45 1243
Chris@684 1244 ModelId model = layer->getModel();
Chris@684 1245 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(model);
Chris@45 1246
Chris@45 1247 if (sodm) {
Chris@651 1248 Event point(frame, "");
Chris@651 1249 Event prevPoint(0);
Chris@45 1250 bool havePrevPoint = false;
Chris@45 1251
Chris@651 1252 ChangeEventsCommand *command =
Chris@684 1253 new ChangeEventsCommand(model.untyped, tr("Add Point"));
Chris@45 1254
Chris@409 1255 if (m_labeller) {
Chris@409 1256
Chris@409 1257 if (m_labeller->requiresPrevPoint()) {
Chris@651 1258
Chris@651 1259 if (sodm->getNearestEventMatching
Chris@651 1260 (frame,
Chris@651 1261 [](Event) { return true; },
Chris@651 1262 EventSeries::Backward,
Chris@651 1263 prevPoint)) {
Chris@409 1264 havePrevPoint = true;
Chris@409 1265 }
Chris@45 1266 }
Chris@45 1267
Chris@45 1268 m_labeller->setSampleRate(sodm->getSampleRate());
Chris@45 1269
Chris@651 1270 Labeller::Relabelling relabelling = m_labeller->label
Chris@636 1271 (point, havePrevPoint ? &prevPoint : nullptr);
Chris@651 1272
Chris@651 1273 if (relabelling.first == Labeller::AppliesToPreviousEvent) {
Chris@651 1274 command->remove(prevPoint);
Chris@651 1275 command->add(relabelling.second);
Chris@651 1276 } else {
Chris@651 1277 point = relabelling.second;
Chris@45 1278 }
Chris@45 1279 }
Chris@45 1280
Chris@651 1281 command->add(point);
Chris@45 1282
Chris@45 1283 command->setName(tr("Add Point at %1 s")
Chris@45 1284 .arg(RealTime::frame2RealTime
Chris@45 1285 (frame,
Chris@45 1286 sodm->getSampleRate())
Chris@45 1287 .toText(false).c_str()));
Chris@45 1288
Chris@108 1289 Command *c = command->finish();
Chris@684 1290 if (c) {
Chris@684 1291 CommandHistory::getInstance()->addCommand(c, false);
Chris@684 1292 }
Chris@45 1293 }
Chris@45 1294 }
Chris@45 1295 }
Chris@45 1296
Chris@45 1297 void
Chris@184 1298 MainWindowBase::insertItemAtSelection()
Chris@184 1299 {
Chris@184 1300 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@184 1301 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@184 1302 i != selections.end(); ++i) {
Chris@435 1303 sv_frame_t start = i->getStartFrame();
Chris@435 1304 sv_frame_t end = i->getEndFrame();
Chris@184 1305 if (start < end) {
Chris@184 1306 insertItemAt(start, end - start);
Chris@184 1307 }
Chris@184 1308 }
Chris@184 1309 }
Chris@184 1310
Chris@184 1311 void
Chris@435 1312 MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration)
Chris@184 1313 {
Chris@184 1314 Pane *pane = m_paneStack->getCurrentPane();
Chris@184 1315 if (!pane) {
Chris@184 1316 return;
Chris@184 1317 }
Chris@184 1318
Chris@184 1319 // ugh!
Chris@184 1320
Chris@435 1321 sv_frame_t alignedStart = pane->alignFromReference(frame);
Chris@435 1322 sv_frame_t alignedEnd = pane->alignFromReference(frame + duration);
Chris@184 1323 if (alignedStart >= alignedEnd) return;
Chris@435 1324 sv_frame_t alignedDuration = alignedEnd - alignedStart;
Chris@184 1325
Chris@636 1326 Command *c = nullptr;
Chris@184 1327
Chris@184 1328 QString name = tr("Add Item at %1 s")
Chris@184 1329 .arg(RealTime::frame2RealTime
Chris@184 1330 (alignedStart,
Chris@184 1331 getMainModel()->getSampleRate())
Chris@184 1332 .toText(false).c_str());
Chris@184 1333
Chris@184 1334 Layer *layer = pane->getSelectedLayer();
Chris@184 1335 if (!layer) return;
Chris@184 1336
Chris@687 1337 ModelId modelId = layer->getModel();
Chris@687 1338
Chris@687 1339 auto rm = ModelById::getAs<RegionModel>(modelId);
Chris@184 1340 if (rm) {
Chris@648 1341 Event point(alignedStart,
Chris@648 1342 rm->getValueMaximum() + 1,
Chris@648 1343 alignedDuration,
Chris@648 1344 "");
Chris@684 1345 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@687 1346 (modelId.untyped, name);
Chris@648 1347 command->add(point);
Chris@184 1348 c = command->finish();
Chris@184 1349 }
Chris@184 1350
Chris@184 1351 if (c) {
Chris@184 1352 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1353 return;
Chris@184 1354 }
Chris@184 1355
Chris@687 1356 auto nm = ModelById::getAs<NoteModel>(modelId);
Chris@184 1357 if (nm) {
Chris@648 1358 Event point(alignedStart,
Chris@648 1359 nm->getValueMinimum(),
Chris@648 1360 alignedDuration,
Chris@648 1361 1.f,
Chris@648 1362 "");
Chris@684 1363 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@687 1364 (modelId.untyped, name);
Chris@646 1365 command->add(point);
Chris@184 1366 c = command->finish();
Chris@184 1367 }
Chris@184 1368
Chris@184 1369 if (c) {
Chris@184 1370 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1371 return;
Chris@184 1372 }
Chris@184 1373 }
Chris@184 1374
Chris@184 1375 void
Chris@45 1376 MainWindowBase::renumberInstants()
Chris@45 1377 {
Chris@45 1378 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1379 if (!pane) return;
Chris@45 1380
Chris@45 1381 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@45 1382 if (!layer) return;
Chris@45 1383
Chris@45 1384 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1385
Chris@694 1386 ModelId modelId = layer->getModel();
Chris@45 1387
Chris@694 1388 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@45 1389 if (!sodm) return;
Chris@45 1390
Chris@45 1391 if (!m_labeller) return;
Chris@45 1392
Chris@45 1393 Labeller labeller(*m_labeller);
Chris@45 1394 labeller.setSampleRate(sodm->getSampleRate());
Chris@684 1395
Chris@694 1396 Command *c = labeller.labelAll(modelId.untyped, &ms, sodm->getAllEvents());
Chris@537 1397 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@537 1398 }
Chris@537 1399
Chris@537 1400 void
Chris@537 1401 MainWindowBase::subdivideInstantsBy(int n)
Chris@537 1402 {
Chris@537 1403 Pane *pane = m_paneStack->getCurrentPane();
Chris@537 1404 if (!pane) return;
Chris@537 1405
Chris@537 1406 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@537 1407 if (!layer) return;
Chris@537 1408
Chris@537 1409 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1410
Chris@694 1411 ModelId modelId = layer->getModel();
Chris@537 1412
Chris@694 1413 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@537 1414 if (!sodm) return;
Chris@537 1415
Chris@537 1416 if (!m_labeller) return;
Chris@537 1417
Chris@537 1418 Labeller labeller(*m_labeller);
Chris@537 1419 labeller.setSampleRate(sodm->getSampleRate());
Chris@537 1420
Chris@694 1421 Command *c = labeller.subdivide(modelId.untyped, &ms, sodm->getAllEvents(), n);
Chris@537 1422 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@45 1423 }
Chris@45 1424
Chris@538 1425 void
Chris@538 1426 MainWindowBase::winnowInstantsBy(int n)
Chris@538 1427 {
Chris@538 1428 Pane *pane = m_paneStack->getCurrentPane();
Chris@538 1429 if (!pane) return;
Chris@538 1430
Chris@538 1431 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@538 1432 if (!layer) return;
Chris@538 1433
Chris@538 1434 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1435
Chris@694 1436 ModelId modelId = layer->getModel();
Chris@538 1437
Chris@694 1438 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@538 1439 if (!sodm) return;
Chris@538 1440
Chris@538 1441 if (!m_labeller) return;
Chris@538 1442
Chris@538 1443 Labeller labeller(*m_labeller);
Chris@538 1444 labeller.setSampleRate(sodm->getSampleRate());
Chris@538 1445
Chris@694 1446 Command *c = labeller.winnow(modelId.untyped, &ms, sodm->getAllEvents(), n);
Chris@538 1447 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@538 1448 }
Chris@538 1449
Chris@45 1450 MainWindowBase::FileOpenStatus
Chris@373 1451 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
Chris@45 1452 {
Chris@134 1453 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
Chris@134 1454 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1455 return open(FileSource(fileOrUrl, &dialog), mode);
Chris@45 1456 }
Chris@45 1457
Chris@45 1458 MainWindowBase::FileOpenStatus
Chris@45 1459 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
Chris@45 1460 {
Chris@45 1461 FileOpenStatus status;
Chris@45 1462
Chris@45 1463 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1464 source.waitForData();
Chris@45 1465
Chris@636 1466 bool canImportLayer = (getMainModel() != nullptr &&
Chris@636 1467 m_paneStack != nullptr &&
Chris@636 1468 m_paneStack->getCurrentPane() != nullptr);
Chris@45 1469
Chris@760 1470 QString extension = source.getExtension().toLower();
Chris@760 1471
Chris@760 1472 bool rdf = (extension == "rdf" || extension == "n3" || extension == "ttl");
Chris@760 1473 bool audio =
Chris@760 1474 AudioFileReaderFactory::getKnownExtensions().contains(extension);
Chris@145 1475
Chris@145 1476 bool rdfSession = false;
Chris@145 1477 if (rdf) {
Chris@145 1478 RDFImporter::RDFDocumentType rdfType =
Chris@145 1479 RDFImporter::identifyDocumentType
Chris@760 1480 (QUrl::fromLocalFile(source.getLocalFilename()));
Chris@145 1481 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@145 1482 rdfType == RDFImporter::AudioRef) {
Chris@145 1483 rdfSession = true;
Chris@145 1484 } else if (rdfType == RDFImporter::NotRDF) {
Chris@145 1485 rdf = false;
Chris@145 1486 }
Chris@145 1487 }
Chris@145 1488
Chris@579 1489 try {
Chris@579 1490 if (rdf) {
Chris@579 1491 if (rdfSession) {
Chris@579 1492 bool cancel = false;
Chris@579 1493 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
Chris@579 1494 return openSession(source);
Chris@579 1495 } else if (cancel) {
Chris@579 1496 return FileOpenCancelled;
Chris@579 1497 } else {
Chris@579 1498 return openLayer(source);
Chris@579 1499 }
Chris@145 1500 } else {
Chris@579 1501 if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1502 return status;
Chris@579 1503 } else if (!canImportLayer) {
Chris@579 1504 return FileOpenWrongMode;
Chris@579 1505 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1506 return status;
Chris@579 1507 } else {
Chris@579 1508 return FileOpenFailed;
Chris@579 1509 }
Chris@145 1510 }
Chris@145 1511 }
Chris@579 1512
Chris@736 1513 if (audio) {
Chris@736 1514 return openAudio(source, mode);
Chris@579 1515 } else if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1516 return status;
Chris@579 1517 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
Chris@579 1518 return status;
Chris@579 1519 } else if (!canImportLayer) {
Chris@762 1520 // We already checked whether the file is RDF: we know
Chris@762 1521 // it's not. But if it's another format that might be
Chris@762 1522 // supported as a layer, reply that we can't open a layer
Chris@762 1523 // here - otherwise assume it's an unknown file format
Chris@762 1524 if (ImageLayer::isImageFileSupported(source.getLocation())) {
Chris@762 1525 return FileOpenWrongMode;
Chris@762 1526 }
Chris@762 1527 if (extension == "mid" || extension == "midi") {
Chris@762 1528 return FileOpenWrongMode;
Chris@762 1529 }
Chris@762 1530 if (TextTest::isApparentTextDocument(source)) {
Chris@762 1531 return FileOpenWrongMode;
Chris@762 1532 }
Chris@762 1533 return FileOpenFailed;
Chris@579 1534 } else if ((status = openImage(source)) != FileOpenFailed) {
Chris@579 1535 return status;
Chris@579 1536 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1537 return status;
Chris@579 1538 } else {
Chris@579 1539 return FileOpenFailed;
Chris@579 1540 }
Chris@579 1541 } catch (const InsufficientDiscSpace &e) {
Chris@579 1542 emit hideSplash();
Chris@579 1543 m_openingAudioFile = false;
Chris@594 1544 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
Chris@579 1545 QMessageBox::critical
Chris@579 1546 (this, tr("Not enough disc space"),
Chris@579 1547 tr("<b>Not enough disc space</b><p>There doesn't appear to be enough spare disc space to accommodate any necessary temporary files.</p><p>Please clear some space and try again.</p>").arg(e.what()));
Chris@579 1548 return FileOpenFailed;
Chris@592 1549 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
Chris@592 1550 emit hideSplash();
Chris@592 1551 m_openingAudioFile = false;
Chris@594 1552 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
Chris@592 1553 QMessageBox::critical
Chris@592 1554 (this, tr("Not enough memory"),
Chris@592 1555 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
Chris@592 1556 return FileOpenFailed;
Chris@45 1557 }
Chris@45 1558 }
Chris@45 1559
Chris@45 1560 MainWindowBase::FileOpenStatus
Chris@626 1561 MainWindowBase::openAudio(FileSource source,
Chris@626 1562 AudioFileOpenMode mode,
Chris@227 1563 QString templateName)
Chris@45 1564 {
Chris@386 1565 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
Chris@45 1566
Chris@222 1567 if (templateName == "") {
Chris@231 1568 templateName = getDefaultSessionTemplate();
Chris@577 1569 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
Chris@222 1570 }
Chris@220 1571
Chris@374 1572 // cerr << "template is: \"" << templateName << "\"" << endl;
Chris@223 1573
Chris@413 1574 if (!source.isAvailable()) {
Chris@413 1575 if (source.wasCancelled()) {
Chris@413 1576 return FileOpenCancelled;
Chris@413 1577 } else {
Chris@413 1578 return FileOpenFailed;
Chris@413 1579 }
Chris@413 1580 }
Chris@413 1581
Chris@758 1582 m_openingAudioFile = true;
Chris@758 1583
Chris@45 1584 source.waitForData();
Chris@45 1585
Chris@435 1586 sv_samplerate_t rate = 0;
Chris@45 1587
Chris@626 1588 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
Chris@626 1589 << endl;
Chris@626 1590
Chris@360 1591 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
Chris@360 1592 rate = Preferences::getInstance()->getFixedSampleRate();
Chris@626 1593 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
Chris@360 1594 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 1595 if (getMainModel()) {
Chris@626 1596 if (mode == ReplaceSession || mode == ReplaceMainModel) {
Chris@626 1597 SVDEBUG << "Preferences specify resampling additional models to match main model, but we are opening this file to replace the main model according to the open mode: therefore..." << endl;
Chris@626 1598 } else {
Chris@626 1599 rate = getMainModel()->getSampleRate();
Chris@626 1600 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
Chris@626 1601 }
Chris@552 1602 }
Chris@45 1603 }
Chris@45 1604
Chris@626 1605 if (rate == 0) {
Chris@626 1606 SVDEBUG << "Yes, preserving incoming file rate" << endl;
Chris@626 1607 }
Chris@626 1608
Chris@684 1609 auto newModel = std::make_shared<ReadOnlyWaveFileModel>(source, rate);
Chris@45 1610 if (!newModel->isOK()) {
Chris@45 1611 m_openingAudioFile = false;
Chris@413 1612 if (source.wasCancelled()) {
Chris@413 1613 return FileOpenCancelled;
Chris@413 1614 } else {
Chris@413 1615 return FileOpenFailed;
Chris@413 1616 }
Chris@45 1617 }
Chris@45 1618
Chris@687 1619 auto newModelId = ModelById::add(newModel);
Chris@720 1620 auto status = addOpenedAudioModel
Chris@720 1621 (source, newModelId, mode, templateName, true);
Chris@720 1622 m_openingAudioFile = false;
Chris@720 1623 return status;
Chris@604 1624 }
Chris@604 1625
Chris@604 1626 MainWindowBase::FileOpenStatus
Chris@604 1627 MainWindowBase::addOpenedAudioModel(FileSource source,
Chris@684 1628 ModelId newModel,
Chris@604 1629 AudioFileOpenMode mode,
Chris@604 1630 QString templateName,
Chris@604 1631 bool registerSource)
Chris@604 1632 {
Chris@45 1633 if (mode == AskUser) {
Chris@45 1634 if (getMainModel()) {
Chris@45 1635
Chris@147 1636 QSettings settings;
Chris@147 1637 settings.beginGroup("MainWindow");
Chris@221 1638 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
Chris@147 1639 settings.endGroup();
Chris@221 1640 int imode = 0;
Chris@45 1641
Chris@45 1642 QStringList items;
Chris@221 1643 items << tr("Close the current session and start a new one")
Chris@221 1644 << tr("Replace the main audio file in this session")
Chris@221 1645 << tr("Add the audio file to this session");
Chris@45 1646
Chris@45 1647 bool ok = false;
Chris@45 1648 QString item = ListInputDialog::getItem
Chris@45 1649 (this, tr("Select target for import"),
Chris@221 1650 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 1651 items, lastMode, &ok);
Chris@45 1652
Chris@45 1653 if (!ok || item.isEmpty()) {
Chris@684 1654 ModelById::release(newModel);
Chris@45 1655 m_openingAudioFile = false;
Chris@45 1656 return FileOpenCancelled;
Chris@45 1657 }
Chris@45 1658
Chris@221 1659 for (int i = 0; i < items.size(); ++i) {
Chris@221 1660 if (item == items[i]) imode = i;
Chris@221 1661 }
Chris@221 1662
Chris@147 1663 settings.beginGroup("MainWindow");
Chris@221 1664 settings.setValue("lastaudioopenmode", imode);
Chris@147 1665 settings.endGroup();
Chris@45 1666
Chris@221 1667 mode = (AudioFileOpenMode)imode;
Chris@45 1668
Chris@45 1669 } else {
Chris@221 1670 // no main model: make a new session
Chris@221 1671 mode = ReplaceSession;
Chris@45 1672 }
Chris@45 1673 }
Chris@45 1674
Chris@45 1675 if (mode == ReplaceCurrentPane) {
Chris@45 1676
Chris@45 1677 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1678 if (pane) {
Chris@45 1679 if (getMainModel()) {
Chris@45 1680 View::ModelSet models(pane->getModels());
Chris@684 1681 if (models.find(getMainModelId()) != models.end()) {
Chris@221 1682 // Current pane contains main model: replace that
Chris@45 1683 mode = ReplaceMainModel;
Chris@45 1684 }
Chris@221 1685 // Otherwise the current pane has a non-default model,
Chris@221 1686 // which we will deal with later
Chris@45 1687 } else {
Chris@221 1688 // We have no main model, so start a new session with
Chris@221 1689 // optional template
Chris@221 1690 mode = ReplaceSession;
Chris@45 1691 }
Chris@45 1692 } else {
Chris@221 1693 // We seem to have no current pane! Oh well
Chris@45 1694 mode = CreateAdditionalModel;
Chris@45 1695 }
Chris@45 1696 }
Chris@45 1697
Chris@759 1698 if (mode != ReplaceSession && !m_document) {
Chris@759 1699 SVDEBUG << "File open mode requested is something other than ReplaceSession, but we have no document at all yet, so we must use ReplaceSession mode" << endl;
Chris@759 1700 mode = ReplaceSession;
Chris@759 1701 }
Chris@759 1702
Chris@684 1703 if (mode == CreateAdditionalModel && getMainModelId().isNone()) {
Chris@759 1704 SVDEBUG << "File open mode requested is CreateAdditionalModel, but we have no main model, so switching to ReplaceSession mode" << endl;
Chris@221 1705 mode = ReplaceSession;
Chris@221 1706 }
Chris@221 1707
Chris@221 1708 bool loadedTemplate = false;
Chris@221 1709
Chris@221 1710 if (mode == ReplaceSession) {
Chris@258 1711
Chris@720 1712 if (!checkSaveModified()) {
Chris@720 1713 m_openingAudioFile = false;
Chris@720 1714 return FileOpenCancelled;
Chris@720 1715 }
Chris@258 1716
Chris@386 1717 SVDEBUG << "SV looking for template " << templateName << endl;
Chris@230 1718 if (templateName != "") {
Chris@230 1719 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@258 1720 if (tplStatus == FileOpenCancelled) {
Chris@577 1721 SVDEBUG << "Template load cancelled" << endl;
Chris@720 1722 m_openingAudioFile = false;
Chris@258 1723 return FileOpenCancelled;
Chris@258 1724 }
Chris@230 1725 if (tplStatus != FileOpenFailed) {
Chris@577 1726 SVDEBUG << "Template load succeeded" << endl;
Chris@230 1727 loadedTemplate = true;
Chris@221 1728 }
Chris@221 1729 }
Chris@221 1730
Chris@221 1731 if (!loadedTemplate) {
Chris@386 1732 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
Chris@221 1733 closeSession();
Chris@221 1734 createDocument();
Chris@221 1735 }
Chris@221 1736
Chris@386 1737 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
Chris@45 1738 mode = ReplaceMainModel;
Chris@45 1739 }
Chris@45 1740
Chris@164 1741 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
Chris@669 1742
Chris@45 1743 if (mode == ReplaceMainModel) {
Chris@45 1744
Chris@684 1745 ModelId prevMain = getMainModelId();
Chris@684 1746 if (!prevMain.isNone()) {
Chris@45 1747 m_playSource->removeModel(prevMain);
Chris@45 1748 }
Chris@45 1749
Chris@248 1750 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
Chris@248 1751
Chris@595 1752 m_document->setMainModel(newModel);
Chris@595 1753
Chris@595 1754 setupMenus();
Chris@595 1755
Chris@669 1756 m_originalLocation = source.getLocation();
Chris@669 1757
Chris@595 1758 if (loadedTemplate || (m_sessionFile == "")) {
Chris@595 1759 CommandHistory::getInstance()->clear();
Chris@595 1760 CommandHistory::getInstance()->documentSaved();
Chris@595 1761 m_documentModified = false;
Chris@595 1762 } else {
Chris@595 1763 if (m_documentModified) {
Chris@595 1764 m_documentModified = false;
Chris@595 1765 }
Chris@595 1766 }
Chris@45 1767
Chris@604 1768 if (!source.isRemote() && registerSource) {
Chris@604 1769 m_audioFile = source.getLocalFilename();
Chris@604 1770 }
Chris@45 1771
Chris@669 1772 updateWindowTitle();
Chris@669 1773
Chris@45 1774 } else if (mode == CreateAdditionalModel) {
Chris@45 1775
Chris@577 1776 SVCERR << "Mode is CreateAdditionalModel" << endl;
Chris@577 1777
Chris@595 1778 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1779 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1780
Chris@691 1781 m_document->addNonDerivedModel(newModel);
Chris@595 1782
Chris@595 1783 AddPaneCommand *command = new AddPaneCommand(this);
Chris@595 1784 CommandHistory::getInstance()->addCommand(command);
Chris@595 1785
Chris@595 1786 Pane *pane = command->getPane();
Chris@45 1787
Chris@47 1788 if (m_timeRulerLayer) {
Chris@577 1789 SVCERR << "Have time ruler, adding it" << endl;
Chris@47 1790 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@577 1791 } else {
Chris@577 1792 SVCERR << "Do not have time ruler" << endl;
Chris@47 1793 }
Chris@45 1794
Chris@595 1795 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1796
Chris@595 1797 if (newLayer) {
Chris@595 1798 m_document->addLayerToView(pane, newLayer);
Chris@595 1799 }
Chris@595 1800
Chris@595 1801 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1802
Chris@45 1803 } else if (mode == ReplaceCurrentPane) {
Chris@45 1804
Chris@45 1805 // We know there is a current pane, otherwise we would have
Chris@45 1806 // reset the mode to CreateAdditionalModel above; and we know
Chris@45 1807 // the current pane does not contain the main model, otherwise
Chris@45 1808 // we would have reset it to ReplaceMainModel. But we don't
Chris@45 1809 // know whether the pane contains a waveform model at all.
Chris@45 1810
Chris@45 1811 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 1812 Layer *replace = nullptr;
Chris@45 1813
Chris@45 1814 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@45 1815 Layer *layer = pane->getLayer(i);
Chris@45 1816 if (dynamic_cast<WaveformLayer *>(layer)) {
Chris@45 1817 replace = layer;
Chris@45 1818 break;
Chris@45 1819 }
Chris@45 1820 }
Chris@45 1821
Chris@595 1822 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1823 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1824
Chris@691 1825 m_document->addNonDerivedModel(newModel);
Chris@45 1826
Chris@45 1827 if (replace) {
Chris@45 1828 m_document->removeLayerFromView(pane, replace);
Chris@45 1829 }
Chris@45 1830
Chris@595 1831 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1832
Chris@595 1833 if (newLayer) {
Chris@595 1834 m_document->addLayerToView(pane, newLayer);
Chris@595 1835 }
Chris@595 1836
Chris@595 1837 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1838 }
Chris@45 1839
Chris@45 1840 updateMenuStates();
Chris@622 1841
Chris@622 1842 if (registerSource) {
Chris@622 1843 m_recentFiles.addFile(source.getLocation());
Chris@622 1844 }
Chris@604 1845 if (!source.isRemote() && registerSource) {
Chris@45 1846 // for file dialog
Chris@45 1847 registerLastOpenedFilePath(FileFinder::AudioFile,
Chris@45 1848 source.getLocalFilename());
Chris@45 1849 }
Chris@622 1850
Chris@45 1851 m_openingAudioFile = false;
Chris@45 1852
Chris@45 1853 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@45 1854
Chris@342 1855 emit audioFileLoaded();
Chris@342 1856
Chris@45 1857 return FileOpenSucceeded;
Chris@45 1858 }
Chris@45 1859
Chris@45 1860 MainWindowBase::FileOpenStatus
Chris@45 1861 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
Chris@45 1862 {
Chris@233 1863 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
Chris@135 1864
Chris@45 1865 std::set<QString> extensions;
Chris@45 1866 PlaylistFileReader::getSupportedExtensions(extensions);
Chris@152 1867 QString extension = source.getExtension().toLower();
Chris@45 1868 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
Chris@45 1869
Chris@45 1870 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1871 source.waitForData();
Chris@45 1872
Chris@45 1873 PlaylistFileReader reader(source.getLocalFilename());
Chris@45 1874 if (!reader.isOK()) return FileOpenFailed;
Chris@45 1875
Chris@45 1876 PlaylistFileReader::Playlist playlist = reader.load();
Chris@45 1877
Chris@45 1878 bool someSuccess = false;
Chris@45 1879
Chris@45 1880 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
Chris@45 1881 i != playlist.end(); ++i) {
Chris@45 1882
Chris@134 1883 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
Chris@134 1884 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1885 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
Chris@45 1886
Chris@45 1887 if (status == FileOpenCancelled) {
Chris@45 1888 return FileOpenCancelled;
Chris@45 1889 }
Chris@45 1890
Chris@45 1891 if (status == FileOpenSucceeded) {
Chris@45 1892 someSuccess = true;
Chris@45 1893 mode = CreateAdditionalModel;
Chris@45 1894 }
Chris@45 1895 }
Chris@45 1896
Chris@45 1897 if (someSuccess) return FileOpenSucceeded;
Chris@45 1898 else return FileOpenFailed;
Chris@45 1899 }
Chris@45 1900
Chris@45 1901 MainWindowBase::FileOpenStatus
Chris@45 1902 MainWindowBase::openLayer(FileSource source)
Chris@45 1903 {
Chris@233 1904 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
Chris@135 1905
Chris@45 1906 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1907
Chris@45 1908 if (!pane) {
Chris@595 1909 // shouldn't happen, as the menu action should have been disabled
Chris@595 1910 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
Chris@595 1911 return FileOpenWrongMode;
Chris@45 1912 }
Chris@45 1913
Chris@45 1914 if (!getMainModel()) {
Chris@595 1915 // shouldn't happen, as the menu action should have been disabled
Chris@595 1916 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
Chris@595 1917 return FileOpenWrongMode;
Chris@45 1918 }
Chris@45 1919
Chris@45 1920 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1921 source.waitForData();
Chris@45 1922
Chris@45 1923 QString path = source.getLocalFilename();
Chris@45 1924
Chris@145 1925 RDFImporter::RDFDocumentType rdfType =
Chris@760 1926 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path));
Chris@145 1927
Chris@293 1928 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
Chris@148 1929
Chris@145 1930 if (rdfType != RDFImporter::NotRDF) {
Chris@145 1931
Chris@145 1932 return openLayersFromRDF(source);
Chris@134 1933
Chris@152 1934 } else if (source.getExtension().toLower() == "svl" ||
Chris@152 1935 (source.getExtension().toLower() == "xml" &&
Chris@140 1936 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@140 1937 == SVFileReader::SVLayerFile))) {
Chris@45 1938
Chris@45 1939 PaneCallback callback(this);
Chris@45 1940 QFile file(path);
Chris@45 1941
Chris@45 1942 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@293 1943 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1944 << source.getLocation()
Chris@293 1945 << "): Failed to open file for reading" << endl;
Chris@45 1946 return FileOpenFailed;
Chris@45 1947 }
Chris@45 1948
Chris@45 1949 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 1950 connect
Chris@79 1951 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 1952 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 1953 connect
Chris@79 1954 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 1955 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@45 1956 reader.setCurrentPane(pane);
Chris@45 1957
Chris@45 1958 QXmlInputSource inputSource(&file);
Chris@45 1959 reader.parse(inputSource);
Chris@45 1960
Chris@45 1961 if (!reader.isOK()) {
Chris@293 1962 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1963 << source.getLocation()
Chris@45 1964 << "): Failed to read XML file: "
Chris@293 1965 << reader.getErrorString() << endl;
Chris@45 1966 return FileOpenFailed;
Chris@45 1967 }
Chris@45 1968
Chris@164 1969 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
Chris@164 1970
Chris@45 1971 m_recentFiles.addFile(source.getLocation());
Chris@45 1972
Chris@45 1973 if (!source.isRemote()) {
Chris@45 1974 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
Chris@45 1975 }
Chris@45 1976
Chris@75 1977 return FileOpenSucceeded;
Chris@75 1978
Chris@45 1979 } else {
Chris@45 1980
Chris@45 1981 try {
Chris@45 1982
Chris@109 1983 MIDIFileImportDialog midiDlg(this);
Chris@109 1984
Chris@684 1985 Model *newModelPtr = DataFileReaderFactory::loadNonCSV
Chris@109 1986 (path, &midiDlg, getMainModel()->getSampleRate());
Chris@45 1987
Chris@684 1988 if (!newModelPtr) {
Chris@643 1989 CSVFormatDialog *dialog =
Chris@643 1990 new CSVFormatDialog(this,
Chris@643 1991 path,
Chris@643 1992 getMainModel()->getSampleRate(),
Chris@643 1993 5);
Chris@109 1994 if (dialog->exec() == QDialog::Accepted) {
Chris@684 1995 newModelPtr = DataFileReaderFactory::loadCSV
Chris@109 1996 (path, dialog->getFormat(),
Chris@109 1997 getMainModel()->getSampleRate());
Chris@109 1998 }
Chris@619 1999 delete dialog;
Chris@109 2000 }
Chris@109 2001
Chris@684 2002 if (newModelPtr) {
Chris@45 2003
Chris@233 2004 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
Chris@45 2005
Chris@164 2006 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
Chris@164 2007
Chris@687 2008 ModelId modelId =
Chris@687 2009 ModelById::add(std::shared_ptr<Model>(newModelPtr));
Chris@684 2010
Chris@684 2011 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@45 2012
Chris@45 2013 if (newLayer) {
Chris@45 2014
Chris@45 2015 m_document->addLayerToView(pane, newLayer);
Chris@88 2016 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@88 2017
Chris@45 2018 m_recentFiles.addFile(source.getLocation());
Chris@45 2019
Chris@45 2020 if (!source.isRemote()) {
Chris@45 2021 registerLastOpenedFilePath
Chris@45 2022 (FileFinder::LayerFile,
Chris@45 2023 path); // for file dialog
Chris@45 2024 }
Chris@88 2025
Chris@45 2026 return FileOpenSucceeded;
Chris@45 2027 }
Chris@45 2028 }
Chris@45 2029 } catch (DataFileReaderFactory::Exception e) {
Chris@45 2030 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@45 2031 return FileOpenCancelled;
Chris@45 2032 }
Chris@45 2033 }
Chris@45 2034 }
Chris@45 2035
Chris@45 2036 return FileOpenFailed;
Chris@45 2037 }
Chris@45 2038
Chris@45 2039 MainWindowBase::FileOpenStatus
Chris@45 2040 MainWindowBase::openImage(FileSource source)
Chris@45 2041 {
Chris@233 2042 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
Chris@135 2043
Chris@45 2044 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 2045
Chris@45 2046 if (!pane) {
Chris@595 2047 // shouldn't happen, as the menu action should have been disabled
Chris@595 2048 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
Chris@595 2049 return FileOpenWrongMode;
Chris@45 2050 }
Chris@45 2051
Chris@684 2052 if (!getMainModel()) {
Chris@45 2053 return FileOpenWrongMode;
Chris@45 2054 }
Chris@45 2055
Chris@45 2056 bool newLayer = false;
Chris@45 2057
Chris@45 2058 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
Chris@45 2059 if (!il) {
Chris@45 2060 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
Chris@45 2061 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
Chris@45 2062 if (il) break;
Chris@45 2063 }
Chris@45 2064 }
Chris@45 2065 if (!il) {
Chris@45 2066 il = dynamic_cast<ImageLayer *>
Chris@45 2067 (m_document->createEmptyLayer(LayerFactory::Image));
Chris@45 2068 if (!il) return FileOpenFailed;
Chris@45 2069 newLayer = true;
Chris@45 2070 }
Chris@45 2071
Chris@45 2072 // We don't put the image file in Recent Files
Chris@45 2073
Chris@762 2074 SVCERR << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
Chris@45 2075
Chris@45 2076 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
Chris@45 2077 if (newLayer) {
Chris@52 2078 m_document->deleteLayer(il); // also releases its model
Chris@45 2079 }
Chris@45 2080 return FileOpenFailed;
Chris@45 2081 } else {
Chris@45 2082 if (newLayer) {
Chris@45 2083 m_document->addLayerToView(pane, il);
Chris@45 2084 }
Chris@45 2085 m_paneStack->setCurrentLayer(pane, il);
Chris@45 2086 }
Chris@45 2087
Chris@45 2088 return FileOpenSucceeded;
Chris@45 2089 }
Chris@45 2090
Chris@45 2091 MainWindowBase::FileOpenStatus
Chris@427 2092 MainWindowBase::openDirOfAudio(QString dirPath)
Chris@427 2093 {
Chris@427 2094 QDir dir(dirPath);
Chris@427 2095 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
Chris@427 2096 files.sort();
Chris@427 2097
Chris@427 2098 FileOpenStatus status = FileOpenFailed;
Chris@427 2099 bool first = true;
Chris@427 2100 bool cancelled = false;
Chris@427 2101
Chris@427 2102 foreach (QString file, files) {
Chris@427 2103
Chris@427 2104 FileSource source(dir.filePath(file));
Chris@427 2105 if (!source.isAvailable()) {
Chris@427 2106 continue;
Chris@427 2107 }
Chris@427 2108
Chris@427 2109 if (AudioFileReaderFactory::getKnownExtensions().contains
Chris@427 2110 (source.getExtension().toLower())) {
Chris@427 2111
Chris@427 2112 AudioFileOpenMode mode = CreateAdditionalModel;
Chris@427 2113 if (first) mode = ReplaceSession;
Chris@427 2114
Chris@427 2115 switch (openAudio(source, mode)) {
Chris@427 2116 case FileOpenSucceeded:
Chris@427 2117 status = FileOpenSucceeded;
Chris@427 2118 first = false;
Chris@427 2119 break;
Chris@427 2120 case FileOpenFailed:
Chris@427 2121 break;
Chris@427 2122 case FileOpenCancelled:
Chris@427 2123 cancelled = true;
Chris@427 2124 break;
Chris@427 2125 case FileOpenWrongMode:
Chris@427 2126 break;
Chris@427 2127 }
Chris@427 2128 }
Chris@427 2129
Chris@427 2130 if (cancelled) break;
Chris@427 2131 }
Chris@427 2132
Chris@427 2133 return status;
Chris@427 2134 }
Chris@427 2135
Chris@427 2136 MainWindowBase::FileOpenStatus
Chris@373 2137 MainWindowBase::openSessionPath(QString fileOrUrl)
Chris@45 2138 {
Chris@134 2139 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
Chris@134 2140 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 2141 return openSession(FileSource(fileOrUrl, &dialog));
Chris@45 2142 }
Chris@45 2143
Chris@45 2144 MainWindowBase::FileOpenStatus
Chris@45 2145 MainWindowBase::openSession(FileSource source)
Chris@45 2146 {
Chris@233 2147 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
Chris@135 2148
Chris@45 2149 if (!source.isAvailable()) return FileOpenFailed;
Chris@145 2150 source.waitForData();
Chris@141 2151
Chris@341 2152 QString sessionExt =
Chris@341 2153 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
Chris@341 2154
Chris@760 2155 QString extension = source.getExtension().toLower();
Chris@760 2156
Chris@760 2157 bool rdf = (extension == "rdf" || extension == "n3" || extension == "ttl");
Chris@760 2158
Chris@760 2159 if (extension != sessionExt) {
Chris@760 2160
Chris@760 2161 if (rdf) {
Chris@760 2162
Chris@760 2163 RDFImporter::RDFDocumentType rdfType =
Chris@760 2164 RDFImporter::identifyDocumentType
Chris@760 2165 (QUrl::fromLocalFile(source.getLocalFilename()));
Chris@145 2166
Chris@293 2167 // cerr << "RDF type: " << (int)rdfType << endl;
Chris@148 2168
Chris@760 2169 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@760 2170 rdfType == RDFImporter::AudioRef) {
Chris@760 2171 return openSessionFromRDF(source);
Chris@760 2172 } else if (rdfType != RDFImporter::NotRDF) {
Chris@760 2173 return FileOpenFailed;
Chris@760 2174 }
Chris@760 2175
Chris@760 2176 } else if (extension == "xml") {
Chris@760 2177
Chris@140 2178 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
Chris@140 2179 SVFileReader::SVSessionFile) {
Chris@293 2180 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
Chris@140 2181 } else {
Chris@140 2182 return FileOpenFailed;
Chris@140 2183 }
Chris@140 2184 } else {
Chris@140 2185 return FileOpenFailed;
Chris@140 2186 }
Chris@140 2187 }
Chris@45 2188
Chris@636 2189 QXmlInputSource *inputSource = nullptr;
Chris@636 2190 BZipFileDevice *bzFile = nullptr;
Chris@636 2191 QFile *rawFile = nullptr;
Chris@140 2192
Chris@760 2193 if (extension == sessionExt) {
Chris@140 2194 bzFile = new BZipFileDevice(source.getLocalFilename());
Chris@140 2195 if (!bzFile->open(QIODevice::ReadOnly)) {
Chris@140 2196 delete bzFile;
Chris@140 2197 return FileOpenFailed;
Chris@140 2198 }
Chris@140 2199 inputSource = new QXmlInputSource(bzFile);
Chris@140 2200 } else {
Chris@140 2201 rawFile = new QFile(source.getLocalFilename());
Chris@140 2202 inputSource = new QXmlInputSource(rawFile);
Chris@140 2203 }
Chris@140 2204
Chris@140 2205 if (!checkSaveModified()) {
Chris@140 2206 if (bzFile) bzFile->close();
Chris@140 2207 delete inputSource;
Chris@140 2208 delete bzFile;
Chris@140 2209 delete rawFile;
Chris@140 2210 return FileOpenCancelled;
Chris@140 2211 }
Chris@45 2212
Chris@45 2213 QString error;
Chris@45 2214 closeSession();
Chris@45 2215 createDocument();
Chris@45 2216
Chris@45 2217 PaneCallback callback(this);
Chris@45 2218 m_viewManager->clearSelections();
Chris@45 2219
Chris@45 2220 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 2221 connect
Chris@79 2222 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 2223 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 2224 connect
Chris@79 2225 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 2226 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@140 2227
Chris@140 2228 reader.parse(*inputSource);
Chris@45 2229
Chris@45 2230 if (!reader.isOK()) {
Chris@45 2231 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@45 2232 }
Chris@45 2233
Chris@140 2234 if (bzFile) bzFile->close();
Chris@140 2235
Chris@140 2236 delete inputSource;
Chris@140 2237 delete bzFile;
Chris@140 2238 delete rawFile;
Chris@45 2239
Chris@45 2240 bool ok = (error == "");
Chris@45 2241
Chris@45 2242 if (ok) {
Chris@45 2243
Chris@164 2244 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
Chris@164 2245
Chris@601 2246 if (!source.isRemote() && !m_document->isIncomplete()) {
Chris@601 2247 // Setting the session file path enables the Save (as
Chris@601 2248 // opposed to Save As...) option. We can't do this if we
Chris@601 2249 // don't have a local path to save to, but we also don't
Chris@601 2250 // want to do it if we failed to find an audio file or
Chris@601 2251 // similar on load, as the audio reference would then end
Chris@601 2252 // up being lost from any saved or auto-saved-on-exit copy
Chris@601 2253 m_sessionFile = source.getLocalFilename();
Chris@602 2254 } else {
Chris@602 2255 QMessageBox::warning
Chris@602 2256 (this,
Chris@602 2257 tr("Incomplete session loaded"),
Chris@603 2258 tr("Some of the audio content referred to by the original session file could not be loaded.\nIf you save this session, it will be saved without any reference to that audio, and information may be lost."),
Chris@602 2259 QMessageBox::Ok);
Chris@601 2260 }
Chris@595 2261
Chris@669 2262 updateWindowTitle();
Chris@595 2263 setupMenus();
Chris@577 2264 findTimeRulerLayer();
Chris@45 2265
Chris@595 2266 CommandHistory::getInstance()->clear();
Chris@595 2267 CommandHistory::getInstance()->documentSaved();
Chris@595 2268 m_documentModified = false;
Chris@595 2269 updateMenuStates();
Chris@45 2270
Chris@227 2271 m_recentFiles.addFile(source.getLocation());
Chris@45 2272
Chris@45 2273 if (!source.isRemote()) {
Chris@45 2274 // for file dialog
Chris@45 2275 registerLastOpenedFilePath(FileFinder::SessionFile,
Chris@227 2276 source.getLocalFilename());
Chris@45 2277 }
Chris@45 2278
Chris@669 2279 m_originalLocation = source.getLocation();
Chris@669 2280
Chris@342 2281 emit sessionLoaded();
Chris@342 2282
Chris@669 2283 updateWindowTitle();
Chris@45 2284 }
Chris@669 2285
Chris@45 2286 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@45 2287 }
Chris@45 2288
Chris@141 2289 MainWindowBase::FileOpenStatus
Chris@230 2290 MainWindowBase::openSessionTemplate(QString templateName)
Chris@230 2291 {
Chris@230 2292 // Template in the user's template directory takes
Chris@230 2293 // priority over a bundled one; we don't unbundle, but
Chris@230 2294 // open directly from the bundled file (where applicable)
Chris@230 2295 ResourceFinder rf;
Chris@230 2296 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
Chris@230 2297 if (tfile != "") {
Chris@294 2298 cerr << "SV loading template file " << tfile << endl;
Chris@230 2299 return openSessionTemplate(FileSource("file:" + tfile));
Chris@230 2300 } else {
Chris@230 2301 return FileOpenFailed;
Chris@230 2302 }
Chris@230 2303 }
Chris@230 2304
Chris@230 2305 MainWindowBase::FileOpenStatus
Chris@227 2306 MainWindowBase::openSessionTemplate(FileSource source)
Chris@227 2307 {
Chris@294 2308 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
Chris@227 2309
Chris@227 2310 if (!source.isAvailable()) return FileOpenFailed;
Chris@227 2311 source.waitForData();
Chris@227 2312
Chris@636 2313 QXmlInputSource *inputSource = nullptr;
Chris@636 2314 QFile *file = nullptr;
Chris@227 2315
Chris@227 2316 file = new QFile(source.getLocalFilename());
Chris@227 2317 inputSource = new QXmlInputSource(file);
Chris@227 2318
Chris@227 2319 if (!checkSaveModified()) {
Chris@227 2320 delete inputSource;
Chris@227 2321 delete file;
Chris@227 2322 return FileOpenCancelled;
Chris@227 2323 }
Chris@227 2324
Chris@227 2325 QString error;
Chris@227 2326 closeSession();
Chris@227 2327 createDocument();
Chris@227 2328
Chris@227 2329 PaneCallback callback(this);
Chris@227 2330 m_viewManager->clearSelections();
Chris@227 2331
Chris@227 2332 SVFileReader reader(m_document, callback, source.getLocation());
Chris@227 2333 connect
Chris@227 2334 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@227 2335 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@227 2336 connect
Chris@227 2337 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@227 2338 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@227 2339
Chris@227 2340 reader.parse(*inputSource);
Chris@227 2341
Chris@227 2342 if (!reader.isOK()) {
Chris@227 2343 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@227 2344 }
Chris@227 2345
Chris@227 2346 delete inputSource;
Chris@227 2347 delete file;
Chris@227 2348
Chris@227 2349 bool ok = (error == "");
Chris@227 2350
Chris@227 2351 if (ok) {
Chris@227 2352
Chris@227 2353 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
Chris@227 2354
Chris@595 2355 setupMenus();
Chris@577 2356 findTimeRulerLayer();
Chris@227 2357
Chris@595 2358 CommandHistory::getInstance()->clear();
Chris@595 2359 CommandHistory::getInstance()->documentSaved();
Chris@595 2360 m_documentModified = false;
Chris@595 2361 updateMenuStates();
Chris@342 2362
Chris@342 2363 emit sessionLoaded();
Chris@227 2364 }
Chris@227 2365
Chris@669 2366 updateWindowTitle();
Chris@669 2367
Chris@227 2368 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@227 2369 }
Chris@227 2370
Chris@227 2371 MainWindowBase::FileOpenStatus
Chris@141 2372 MainWindowBase::openSessionFromRDF(FileSource source)
Chris@141 2373 {
Chris@233 2374 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
Chris@141 2375
Chris@141 2376 if (!source.isAvailable()) return FileOpenFailed;
Chris@141 2377 source.waitForData();
Chris@141 2378
Chris@145 2379 if (!checkSaveModified()) {
Chris@145 2380 return FileOpenCancelled;
Chris@141 2381 }
Chris@143 2382
Chris@145 2383 closeSession();
Chris@145 2384 createDocument();
Chris@145 2385
Chris@145 2386 FileOpenStatus status = openLayersFromRDF(source);
Chris@141 2387
Chris@141 2388 setupMenus();
Chris@577 2389 findTimeRulerLayer();
Chris@669 2390
Chris@141 2391 CommandHistory::getInstance()->clear();
Chris@141 2392 CommandHistory::getInstance()->documentSaved();
Chris@141 2393 m_documentModified = false;
Chris@669 2394 updateWindowTitle();
Chris@145 2395
Chris@342 2396 emit sessionLoaded();
Chris@342 2397
Chris@145 2398 return status;
Chris@145 2399 }
Chris@145 2400
Chris@145 2401 MainWindowBase::FileOpenStatus
Chris@145 2402 MainWindowBase::openLayersFromRDF(FileSource source)
Chris@145 2403 {
Chris@435 2404 sv_samplerate_t rate = 0;
Chris@145 2405
Chris@233 2406 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
Chris@186 2407
Chris@145 2408 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
Chris@145 2409 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@145 2410
Chris@145 2411 if (getMainModel()) {
Chris@145 2412 rate = getMainModel()->getSampleRate();
Chris@145 2413 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 2414 if (getMainModel()) {
Chris@552 2415 rate = getMainModel()->getSampleRate();
Chris@552 2416 }
Chris@145 2417 }
Chris@145 2418
Chris@145 2419 RDFImporter importer
Chris@145 2420 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
Chris@145 2421
Chris@145 2422 if (!importer.isOK()) {
Chris@147 2423 if (importer.getErrorString() != "") {
Chris@147 2424 QMessageBox::critical
Chris@147 2425 (this, tr("Failed to import RDF"),
Chris@147 2426 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
Chris@147 2427 .arg(source.getLocation()).arg(importer.getErrorString()));
Chris@147 2428 }
Chris@145 2429 return FileOpenFailed;
Chris@145 2430 }
Chris@145 2431
Chris@687 2432 std::vector<ModelId> modelIds = importer.getDataModels(&dialog);
Chris@145 2433
Chris@145 2434 dialog.setMessage(tr("Importing from RDF..."));
Chris@145 2435
Chris@687 2436 if (modelIds.empty()) {
Chris@186 2437 QMessageBox::critical
Chris@186 2438 (this, tr("Failed to import RDF"),
Chris@186 2439 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 2440 return FileOpenFailed;
Chris@145 2441 }
Chris@145 2442
Chris@164 2443 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
Chris@684 2444
Chris@684 2445 std::set<ModelId> added;
Chris@684 2446
Chris@684 2447 for (auto modelId: modelIds) {
Chris@684 2448
Chris@684 2449 if (ModelById::isa<WaveFileModel>(modelId)) {
Chris@145 2450
Chris@145 2451 Pane *pane = addPaneToStack();
Chris@636 2452 Layer *layer = nullptr;
Chris@145 2453
Chris@145 2454 if (m_timeRulerLayer) {
Chris@145 2455 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@145 2456 }
Chris@145 2457
Chris@145 2458 if (!getMainModel()) {
Chris@684 2459 m_document->setMainModel(modelId);
Chris@145 2460 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@145 2461 } else {
Chris@684 2462 layer = m_document->createImportedLayer(modelId);
Chris@145 2463 }
Chris@145 2464
Chris@145 2465 m_document->addLayerToView(pane, layer);
Chris@145 2466
Chris@684 2467 added.insert(modelId);
Chris@684 2468
Chris@684 2469 for (auto otherId: modelIds) {
Chris@684 2470
Chris@684 2471 if (otherId == modelId) continue;
Chris@684 2472
Chris@684 2473 bool isDependent = false;
Chris@684 2474 if (auto dm = ModelById::get(otherId)) {
Chris@684 2475 if (dm->getSourceModel() == modelId) {
Chris@684 2476 isDependent = true;
Chris@684 2477 }
Chris@684 2478 }
Chris@684 2479 if (!isDependent) continue;
Chris@684 2480
Chris@684 2481 layer = m_document->createImportedLayer(otherId);
Chris@145 2482
Chris@145 2483 if (layer->isLayerOpaque() ||
Chris@145 2484 dynamic_cast<Colour3DPlotLayer *>(layer)) {
Chris@145 2485
Chris@156 2486 // these always go in a new pane, with nothing
Chris@156 2487 // else going in the same pane
Chris@156 2488
Chris@145 2489 Pane *singleLayerPane = addPaneToStack();
Chris@145 2490 if (m_timeRulerLayer) {
Chris@145 2491 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2492 }
Chris@145 2493 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2494
Chris@156 2495 } else if (layer->getLayerColourSignificance() ==
Chris@156 2496 Layer::ColourHasMeaningfulValue) {
Chris@156 2497
Chris@156 2498 // these can go in a pane with something else, but
Chris@156 2499 // only if none of the something elses also have
Chris@156 2500 // this quality
Chris@156 2501
Chris@156 2502 bool needNewPane = false;
Chris@156 2503 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@156 2504 Layer *otherLayer = pane->getLayer(i);
Chris@156 2505 if (otherLayer &&
Chris@156 2506 (otherLayer->getLayerColourSignificance() ==
Chris@156 2507 Layer::ColourHasMeaningfulValue)) {
Chris@156 2508 needNewPane = true;
Chris@156 2509 break;
Chris@156 2510 }
Chris@156 2511 }
Chris@156 2512 if (needNewPane) {
Chris@156 2513 pane = addPaneToStack();
Chris@156 2514 }
Chris@156 2515
Chris@156 2516 m_document->addLayerToView(pane, layer);
Chris@156 2517
Chris@145 2518 } else {
Chris@145 2519
Chris@145 2520 if (pane->getLayerCount() > 4) {
Chris@145 2521 pane = addPaneToStack();
Chris@145 2522 }
Chris@145 2523
Chris@145 2524 m_document->addLayerToView(pane, layer);
Chris@145 2525 }
Chris@145 2526
Chris@684 2527 added.insert(otherId);
Chris@145 2528 }
Chris@145 2529 }
Chris@145 2530 }
Chris@145 2531
Chris@684 2532 for (auto modelId : modelIds) {
Chris@684 2533
Chris@684 2534 if (added.find(modelId) == added.end()) {
Chris@145 2535
Chris@684 2536 Layer *layer = m_document->createImportedLayer(modelId);
Chris@145 2537 if (!layer) return FileOpenFailed;
Chris@145 2538
Chris@145 2539 Pane *singleLayerPane = addPaneToStack();
Chris@145 2540 if (m_timeRulerLayer) {
Chris@145 2541 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2542 }
Chris@145 2543 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2544 }
Chris@145 2545 }
Chris@145 2546
Chris@145 2547 m_recentFiles.addFile(source.getLocation());
Chris@145 2548 return FileOpenSucceeded;
Chris@141 2549 }
Chris@141 2550
Chris@584 2551 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
Chris@584 2552 {
Chris@584 2553 public:
Chris@584 2554 void log(std::string message) const override {
Chris@584 2555 SVDEBUG << message << endl;
Chris@584 2556 }
Chris@584 2557 };
Chris@584 2558
Chris@45 2559 void
Chris@475 2560 MainWindowBase::createAudioIO()
Chris@45 2561 {
Chris@475 2562 if (m_playTarget || m_audioIO) return;
Chris@475 2563
Chris@584 2564 static AudioLogCallback audioLogCallback;
Chris@584 2565 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
Chris@714 2566
Chris@714 2567 if (m_audioMode == AUDIO_NONE) return;
Chris@45 2568
Chris@126 2569 QSettings settings;
Chris@126 2570 settings.beginGroup("Preferences");
Chris@547 2571 QString implementation = settings.value
Chris@547 2572 ("audio-target", "").toString();
Chris@547 2573 QString suffix;
Chris@547 2574 if (implementation != "") suffix = "-" + implementation;
Chris@547 2575 QString recordDevice = settings.value
Chris@547 2576 ("audio-record-device" + suffix, "").toString();
Chris@547 2577 QString playbackDevice = settings.value
Chris@547 2578 ("audio-playback-device" + suffix, "").toString();
Chris@126 2579 settings.endGroup();
Chris@547 2580
Chris@547 2581 if (implementation == "auto") {
Chris@547 2582 implementation = "";
Chris@547 2583 }
Chris@468 2584
Chris@547 2585 breakfastquay::AudioFactory::Preference preference;
Chris@547 2586 preference.implementation = implementation.toStdString();
Chris@547 2587 preference.recordDevice = recordDevice.toStdString();
Chris@547 2588 preference.playbackDevice = playbackDevice.toStdString();
Chris@547 2589
Chris@547 2590 SVCERR << "createAudioIO: Preferred implementation = \""
Chris@547 2591 << preference.implementation << "\"" << endl;
Chris@547 2592 SVCERR << "createAudioIO: Preferred playback device = \""
Chris@547 2593 << preference.playbackDevice << "\"" << endl;
Chris@547 2594 SVCERR << "createAudioIO: Preferred record device = \""
Chris@547 2595 << preference.recordDevice << "\"" << endl;
Chris@475 2596
Chris@738 2597 breakfastquay::ApplicationPlaybackSource *source =
Chris@738 2598 m_playSource->getApplicationPlaybackSource();
Chris@569 2599
Chris@569 2600 std::string errorString;
Chris@551 2601
Chris@714 2602 if (m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@475 2603 m_audioIO = breakfastquay::AudioFactory::
Chris@738 2604 createCallbackIO(m_recordTarget, source, preference, errorString);
Chris@525 2605 if (m_audioIO) {
Chris@611 2606 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2607 m_audioIO->suspend(); // start in suspended state
Chris@525 2608 m_playSource->setSystemPlaybackTarget(m_audioIO);
Chris@586 2609 } else {
Chris@586 2610 // Failed to create audio I/O; this may just mean there is
Chris@586 2611 // no record device, so fall through to see what happens
Chris@586 2612 // next. We only report complete failure if we end up with
Chris@586 2613 // neither m_audioIO nor m_playTarget.
Chris@525 2614 }
Chris@586 2615 }
Chris@586 2616
Chris@586 2617 if (!m_audioIO) {
Chris@475 2618 m_playTarget = breakfastquay::AudioFactory::
Chris@738 2619 createCallbackPlayTarget(source, preference, errorString);
Chris@525 2620 if (m_playTarget) {
Chris@611 2621 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2622 m_playTarget->suspend(); // start in suspended state
Chris@525 2623 m_playSource->setSystemPlaybackTarget(m_playTarget);
Chris@525 2624 }
Chris@475 2625 }
Chris@475 2626
Chris@475 2627 if (!m_playTarget && !m_audioIO) {
Chris@104 2628 emit hideSplash();
Chris@569 2629 QString message;
Chris@569 2630 QString error = errorString.c_str();
Chris@569 2631 QString firstBit, secondBit;
Chris@547 2632 if (implementation == "") {
Chris@569 2633 if (error == "") {
Chris@569 2634 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
Chris@569 2635 } else {
Chris@569 2636 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
Chris@569 2637 }
Chris@714 2638 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2639 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2640 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
Chris@569 2641 } else {
Chris@569 2642 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
Chris@569 2643 }
Chris@126 2644 } else {
Chris@569 2645 QString driverName = breakfastquay::AudioFactory::
Chris@569 2646 getImplementationDescription(implementation.toStdString())
Chris@569 2647 .c_str();
Chris@569 2648 if (error == "") {
Chris@569 2649 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
Chris@569 2650 } else {
Chris@569 2651 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
Chris@569 2652 }
Chris@714 2653 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2654 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2655 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
Chris@569 2656 } else {
Chris@569 2657 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
Chris@569 2658 }
Chris@126 2659 }
Chris@570 2660 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
Chris@570 2661 << implementation << "\": error is: " << error << endl;
Chris@569 2662 QMessageBox::warning(this, tr("Couldn't open audio device"),
Chris@569 2663 firstBit + secondBit, QMessageBox::Ok);
Chris@45 2664 }
Chris@45 2665 }
Chris@45 2666
Chris@556 2667 void
Chris@556 2668 MainWindowBase::deleteAudioIO()
Chris@556 2669 {
Chris@556 2670 // First prevent this trying to call target.
Chris@559 2671 if (m_playSource) {
Chris@636 2672 m_playSource->setSystemPlaybackTarget(nullptr);
Chris@559 2673 }
Chris@556 2674
Chris@556 2675 // Then delete the breakfastquay::System object.
Chris@556 2676 // Only one of these two exists!
Chris@556 2677 delete m_audioIO;
Chris@556 2678 delete m_playTarget;
Chris@556 2679
Chris@636 2680 m_audioIO = nullptr;
Chris@636 2681 m_playTarget = nullptr;
Chris@556 2682 }
Chris@556 2683
Chris@556 2684 void
Chris@556 2685 MainWindowBase::recreateAudioIO()
Chris@556 2686 {
Chris@556 2687 deleteAudioIO();
Chris@556 2688 createAudioIO();
Chris@556 2689 }
Chris@556 2690
Chris@570 2691 void
Chris@570 2692 MainWindowBase::audioChannelCountIncreased(int)
Chris@570 2693 {
Chris@611 2694 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
Chris@570 2695 recreateAudioIO();
Chris@610 2696
Chris@610 2697 if (m_recordTarget &&
Chris@610 2698 m_recordTarget->isRecording() &&
Chris@610 2699 m_audioIO) {
Chris@610 2700 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
Chris@610 2701 m_audioIO->resume();
Chris@610 2702 }
Chris@570 2703 }
Chris@570 2704
Chris@684 2705 ModelId
Chris@685 2706 MainWindowBase::getMainModelId() const
Chris@684 2707 {
Chris@684 2708 if (!m_document) return {};
Chris@684 2709 return m_document->getMainModel();
Chris@684 2710 }
Chris@684 2711
Chris@684 2712 std::shared_ptr<WaveFileModel>
Chris@685 2713 MainWindowBase::getMainModel() const
Chris@45 2714 {
Chris@684 2715 return ModelById::getAs<WaveFileModel>(getMainModelId());
Chris@45 2716 }
Chris@45 2717
Chris@45 2718 void
Chris@45 2719 MainWindowBase::createDocument()
Chris@45 2720 {
Chris@45 2721 m_document = new Document;
Chris@45 2722
Chris@45 2723 connect(m_document, SIGNAL(layerAdded(Layer *)),
Chris@595 2724 this, SLOT(layerAdded(Layer *)));
Chris@45 2725 connect(m_document, SIGNAL(layerRemoved(Layer *)),
Chris@595 2726 this, SLOT(layerRemoved(Layer *)));
Chris@45 2727 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
Chris@595 2728 this, SLOT(layerAboutToBeDeleted(Layer *)));
Chris@45 2729 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
Chris@595 2730 this, SLOT(layerInAView(Layer *, bool)));
Chris@45 2731
Chris@684 2732 connect(m_document, SIGNAL(modelAdded(ModelId )),
Chris@684 2733 this, SLOT(modelAdded(ModelId )));
Chris@687 2734 connect(m_document, SIGNAL(mainModelChanged(ModelId)),
Chris@687 2735 this, SLOT(mainModelChanged(ModelId)));
Chris@45 2736
Chris@78 2737 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
Chris@78 2738 this, SLOT(modelGenerationFailed(QString, QString)));
Chris@78 2739 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@78 2740 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@687 2741 connect(m_document, SIGNAL(alignmentComplete(ModelId)),
Chris@687 2742 this, SLOT(alignmentComplete(ModelId)));
Chris@761 2743 connect(m_document, SIGNAL(alignmentFailed(ModelId, QString)),
Chris@761 2744 this, SLOT(alignmentFailed(ModelId, QString)));
Chris@160 2745
Chris@667 2746 m_document->setAutoAlignment(m_viewManager->getAlignMode());
Chris@667 2747
Chris@160 2748 emit replacedDocument();
Chris@45 2749 }
Chris@45 2750
Chris@45 2751 bool
Chris@45 2752 MainWindowBase::saveSessionFile(QString path)
Chris@45 2753 {
Chris@217 2754 try {
Chris@217 2755
Chris@217 2756 TempWriteFile temp(path);
Chris@217 2757
Chris@217 2758 BZipFileDevice bzFile(temp.getTemporaryFilename());
Chris@217 2759 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@293 2760 cerr << "Failed to open session file \""
Chris@294 2761 << temp.getTemporaryFilename()
Chris@217 2762 << "\" for writing: "
Chris@293 2763 << bzFile.errorString() << endl;
Chris@217 2764 return false;
Chris@217 2765 }
Chris@217 2766
Chris@217 2767 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@217 2768
Chris@217 2769 QTextStream out(&bzFile);
Chris@432 2770 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2771 toXml(out, false);
Chris@217 2772 out.flush();
Chris@217 2773
Chris@217 2774 QApplication::restoreOverrideCursor();
Chris@217 2775
Chris@217 2776 if (!bzFile.isOK()) {
Chris@217 2777 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2778 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2779 .arg(path).arg(bzFile.errorString()));
Chris@217 2780 bzFile.close();
Chris@217 2781 return false;
Chris@217 2782 }
Chris@217 2783
Chris@217 2784 bzFile.close();
Chris@217 2785 temp.moveToTarget();
Chris@217 2786 return true;
Chris@217 2787
Chris@217 2788 } catch (FileOperationFailed &f) {
Chris@217 2789
Chris@217 2790 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2791 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2792 .arg(path).arg(f.what()));
Chris@45 2793 return false;
Chris@45 2794 }
Chris@45 2795 }
Chris@45 2796
Chris@224 2797 bool
Chris@224 2798 MainWindowBase::saveSessionTemplate(QString path)
Chris@224 2799 {
Chris@224 2800 try {
Chris@224 2801
Chris@224 2802 TempWriteFile temp(path);
Chris@224 2803
Chris@224 2804 QFile file(temp.getTemporaryFilename());
Chris@224 2805 if (!file.open(QIODevice::WriteOnly)) {
Chris@293 2806 cerr << "Failed to open session template file \""
Chris@294 2807 << temp.getTemporaryFilename()
Chris@224 2808 << "\" for writing: "
Chris@294 2809 << file.errorString() << endl;
Chris@224 2810 return false;
Chris@224 2811 }
Chris@224 2812
Chris@224 2813 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@224 2814
Chris@224 2815 QTextStream out(&file);
Chris@432 2816 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2817 toXml(out, true);
Chris@224 2818 out.flush();
Chris@224 2819
Chris@224 2820 QApplication::restoreOverrideCursor();
Chris@224 2821
Chris@224 2822 file.close();
Chris@224 2823 temp.moveToTarget();
Chris@224 2824 return true;
Chris@224 2825
Chris@224 2826 } catch (FileOperationFailed &f) {
Chris@224 2827
Chris@224 2828 QMessageBox::critical(this, tr("Failed to write file"),
Chris@224 2829 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@224 2830 .arg(path).arg(f.what()));
Chris@224 2831 return false;
Chris@224 2832 }
Chris@224 2833 }
Chris@224 2834
Chris@729 2835 //!!! should we pull out the whole export logic into another
Chris@729 2836 // class? then we can more reasonably query it for things like
Chris@729 2837 // "can we export this layer type to this file format? can we
Chris@729 2838 // export selections, or only the whole layer?"
Chris@729 2839
Chris@659 2840 bool
Chris@729 2841 MainWindowBase::exportLayerToSVL(Layer *layer,
Chris@729 2842 QString path, QString &error)
Chris@659 2843 {
Chris@659 2844 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@659 2845
Chris@659 2846 QString suffix = QFileInfo(path).suffix().toLower();
Chris@659 2847
Chris@729 2848 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@684 2849 if (!model) {
Chris@684 2850 error = tr("Internal error: unknown model");
Chris@684 2851 return false;
Chris@684 2852 }
Chris@659 2853
Chris@729 2854 QFile file(path);
Chris@729 2855 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@729 2856 error = tr("Failed to open file %1 for writing").arg(path);
Chris@729 2857 } else {
Chris@729 2858 QTextStream out(&file);
Chris@729 2859 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@729 2860 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@729 2861 << "<!DOCTYPE sonic-visualiser>\n"
Chris@729 2862 << "<sv>\n"
Chris@729 2863 << " <data>\n";
Chris@729 2864
Chris@729 2865 model->toXml(out, " ");
Chris@729 2866
Chris@729 2867 out << " </data>\n"
Chris@729 2868 << " <display>\n";
Chris@729 2869
Chris@729 2870 layer->toXml(out, " ");
Chris@729 2871
Chris@729 2872 out << " </display>\n"
Chris@729 2873 << "</sv>\n";
Chris@729 2874 }
Chris@729 2875
Chris@729 2876 return (error == "");
Chris@729 2877 }
Chris@729 2878
Chris@729 2879 bool
Chris@729 2880 MainWindowBase::exportLayerToMIDI(Layer *layer,
Chris@729 2881 MultiSelection *selectionsToWrite,
Chris@729 2882 QString path, QString &error)
Chris@729 2883 {
Chris@729 2884 if (QFileInfo(path).suffix() == "") path += ".mid";
Chris@729 2885
Chris@729 2886 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2887
Chris@729 2888 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@729 2889 if (!model) {
Chris@729 2890 error = tr("Internal error: unknown model");
Chris@729 2891 return false;
Chris@729 2892 }
Chris@729 2893
Chris@729 2894 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
Chris@729 2895
Chris@729 2896 if (!nm) {
Chris@729 2897 error = tr("Can't export non-note layers to MIDI");
Chris@729 2898 } else if (!selectionsToWrite) {
Chris@729 2899 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
Chris@729 2900 writer.write();
Chris@729 2901 if (!writer.isOK()) {
Chris@729 2902 error = writer.getError();
Chris@659 2903 }
Chris@729 2904 } else {
Chris@729 2905 NoteModel temporary(nm->getSampleRate(),
Chris@729 2906 nm->getResolution(),
Chris@729 2907 nm->getValueMinimum(),
Chris@729 2908 nm->getValueMaximum(),
Chris@729 2909 false);
Chris@729 2910 temporary.setScaleUnits(nm->getScaleUnits());
Chris@729 2911 for (const auto &s: selectionsToWrite->getSelections()) {
Chris@729 2912 EventVector ev(nm->getEventsStartingWithin
Chris@729 2913 (s.getStartFrame(), s.getDuration()));
Chris@729 2914 for (const auto &e: ev) {
Chris@729 2915 temporary.add(e);
Chris@724 2916 }
Chris@659 2917 }
Chris@729 2918 MIDIFileWriter writer(path, &temporary, temporary.getSampleRate());
Chris@729 2919 writer.write();
Chris@659 2920 if (!writer.isOK()) {
Chris@659 2921 error = writer.getError();
Chris@659 2922 }
Chris@659 2923 }
Chris@729 2924
Chris@659 2925 return (error == "");
Chris@659 2926 }
Chris@659 2927
Chris@729 2928 bool
Chris@729 2929 MainWindowBase::exportLayerToRDF(Layer *layer,
Chris@729 2930 QString path, QString &error)
Chris@729 2931 {
Chris@729 2932 if (QFileInfo(path).suffix() == "") path += ".ttl";
Chris@729 2933
Chris@729 2934 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2935
Chris@729 2936 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@729 2937 if (!model) {
Chris@729 2938 error = tr("Internal error: unknown model");
Chris@729 2939 return false;
Chris@729 2940 }
Chris@729 2941
Chris@729 2942 if (!RDFExporter::canExportModel(model.get())) {
Chris@729 2943 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
Chris@729 2944 } else {
Chris@729 2945 RDFExporter exporter(path, model.get());
Chris@729 2946 exporter.write();
Chris@729 2947 if (!exporter.isOK()) {
Chris@729 2948 error = exporter.getError();
Chris@729 2949 }
Chris@729 2950 }
Chris@729 2951
Chris@729 2952 return (error == "");
Chris@729 2953 }
Chris@729 2954
Chris@729 2955 bool
Chris@729 2956 MainWindowBase::exportLayerToCSV(Layer *layer, LayerGeometryProvider *provider,
Chris@729 2957 MultiSelection *selectionsToWrite,
Chris@729 2958 QString delimiter,
Chris@729 2959 DataExportOptions options,
Chris@729 2960 QString path, QString &error)
Chris@729 2961 {
Chris@729 2962 if (QFileInfo(path).suffix() == "") path += ".csv";
Chris@729 2963
Chris@729 2964 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2965
Chris@729 2966 auto model = ModelById::get(layer->getExportModel(provider));
Chris@729 2967 if (!model) {
Chris@729 2968 error = tr("Internal error: unknown model");
Chris@729 2969 return false;
Chris@729 2970 }
Chris@729 2971
Chris@729 2972 ProgressDialog dialog {
Chris@729 2973 QObject::tr("Exporting layer..."), true, 500, this,
Chris@729 2974 Qt::ApplicationModal
Chris@729 2975 };
Chris@729 2976
Chris@729 2977 CSVFileWriter writer(path, model.get(), &dialog, delimiter, options);
Chris@729 2978
Chris@729 2979 if (selectionsToWrite) {
Chris@729 2980 writer.writeSelection(*selectionsToWrite);
Chris@729 2981 } else {
Chris@729 2982 writer.write();
Chris@729 2983 }
Chris@729 2984
Chris@729 2985 if (!writer.isOK()) {
Chris@729 2986 error = writer.getError();
Chris@729 2987 if (error == "") {
Chris@729 2988 error = tr("Failed to export layer for an unknown reason");
Chris@729 2989 }
Chris@729 2990 }
Chris@729 2991
Chris@729 2992 return (error == "");
Chris@729 2993 }
Chris@729 2994
Chris@729 2995 bool
Chris@729 2996 MainWindowBase::exportLayerTo(Layer *layer, LayerGeometryProvider *provider,
Chris@729 2997 MultiSelection *selectionsToWrite,
Chris@729 2998 QString path, QString &error)
Chris@729 2999 {
Chris@731 3000 if (QFileInfo(path).suffix() == "") path += ".csv";
Chris@729 3001 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 3002
Chris@729 3003 if (suffix == "xml" || suffix == "svl") {
Chris@729 3004 return exportLayerToSVL(layer, path, error);
Chris@729 3005 } else if (suffix == "mid" || suffix == "midi") {
Chris@729 3006 return exportLayerToMIDI(layer, selectionsToWrite, path, error);
Chris@729 3007 } else if (suffix == "ttl" || suffix == "n3") {
Chris@729 3008 return exportLayerToRDF(layer, path, error);
Chris@729 3009 } else {
Chris@729 3010 return exportLayerToCSV(layer, provider, selectionsToWrite,
Chris@729 3011 (suffix == "csv" ? "," : "\t"),
Chris@729 3012 DataExportDefaults, path, error);
Chris@729 3013 }
Chris@729 3014 }
Chris@729 3015
Chris@45 3016 void
Chris@226 3017 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
Chris@45 3018 {
Chris@45 3019 QString indent(" ");
Chris@45 3020
Chris@45 3021 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@45 3022 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@45 3023 out << "<sv>\n";
Chris@45 3024
Chris@226 3025 if (asTemplate) {
Chris@226 3026 m_document->toXmlAsTemplate(out, "", "");
Chris@226 3027 } else {
Chris@226 3028 m_document->toXml(out, "", "");
Chris@226 3029 }
Chris@45 3030
Chris@45 3031 out << "<display>\n";
Chris@45 3032
Chris@45 3033 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@595 3034 .arg(width()).arg(height());
Chris@45 3035
Chris@45 3036 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 3037
Chris@595 3038 Pane *pane = m_paneStack->getPane(i);
Chris@595 3039
Chris@595 3040 if (pane) {
Chris@45 3041 pane->toXml(out, indent);
Chris@595 3042 }
Chris@45 3043 }
Chris@45 3044
Chris@45 3045 out << "</display>\n";
Chris@45 3046
Chris@45 3047 m_viewManager->getSelection().toXml(out);
Chris@45 3048
Chris@45 3049 out << "</sv>\n";
Chris@45 3050 }
Chris@45 3051
Chris@45 3052 Pane *
Chris@45 3053 MainWindowBase::addPaneToStack()
Chris@45 3054 {
Chris@342 3055 cerr << "MainWindowBase::addPaneToStack()" << endl;
Chris@45 3056 AddPaneCommand *command = new AddPaneCommand(this);
Chris@45 3057 CommandHistory::getInstance()->addCommand(command);
Chris@57 3058 Pane *pane = command->getPane();
Chris@57 3059 return pane;
Chris@45 3060 }
Chris@45 3061
Chris@45 3062 void
Chris@45 3063 MainWindowBase::zoomIn()
Chris@45 3064 {
Chris@45 3065 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3066 if (currentPane) currentPane->zoom(true);
Chris@45 3067 }
Chris@45 3068
Chris@45 3069 void
Chris@45 3070 MainWindowBase::zoomOut()
Chris@45 3071 {
Chris@45 3072 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3073 if (currentPane) currentPane->zoom(false);
Chris@45 3074 }
Chris@45 3075
Chris@45 3076 void
Chris@45 3077 MainWindowBase::zoomToFit()
Chris@45 3078 {
Chris@45 3079 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3080 if (!currentPane) return;
Chris@45 3081
Chris@684 3082 auto model = getMainModel();
Chris@45 3083 if (!model) return;
Chris@45 3084
Chris@434 3085 sv_frame_t start = model->getStartFrame();
Chris@434 3086 sv_frame_t end = model->getEndFrame();
Chris@60 3087 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
Chris@366 3088 int pixels = currentPane->width();
Chris@366 3089
Chris@366 3090 int sw = currentPane->getVerticalScaleWidth();
Chris@45 3091 if (pixels > sw * 2) pixels -= sw * 2;
Chris@45 3092 else pixels = 1;
Chris@45 3093 if (pixels > 4) pixels -= 4;
Chris@45 3094
Chris@624 3095 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
Chris@45 3096 currentPane->setZoomLevel(zoomLevel);
Chris@45 3097 currentPane->setCentreFrame((start + end) / 2);
Chris@45 3098 }
Chris@45 3099
Chris@45 3100 void
Chris@45 3101 MainWindowBase::zoomDefault()
Chris@45 3102 {
Chris@45 3103 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@302 3104 QSettings settings;
Chris@302 3105 settings.beginGroup("MainWindow");
Chris@302 3106 int zoom = settings.value("zoom-default", 1024).toInt();
Chris@302 3107 settings.endGroup();
Chris@624 3108 if (currentPane) {
Chris@624 3109 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
Chris@624 3110 }
Chris@45 3111 }
Chris@45 3112
Chris@45 3113 void
Chris@45 3114 MainWindowBase::scrollLeft()
Chris@45 3115 {
Chris@45 3116 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3117 if (currentPane) currentPane->scroll(false, false);
Chris@45 3118 }
Chris@45 3119
Chris@45 3120 void
Chris@45 3121 MainWindowBase::jumpLeft()
Chris@45 3122 {
Chris@45 3123 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3124 if (currentPane) currentPane->scroll(false, true);
Chris@45 3125 }
Chris@45 3126
Chris@45 3127 void
Chris@162 3128 MainWindowBase::peekLeft()
Chris@162 3129 {
Chris@162 3130 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3131 if (currentPane) currentPane->scroll(false, false, false);
Chris@162 3132 }
Chris@162 3133
Chris@162 3134 void
Chris@45 3135 MainWindowBase::scrollRight()
Chris@45 3136 {
Chris@45 3137 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3138 if (currentPane) currentPane->scroll(true, false);
Chris@45 3139 }
Chris@45 3140
Chris@45 3141 void
Chris@45 3142 MainWindowBase::jumpRight()
Chris@45 3143 {
Chris@45 3144 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3145 if (currentPane) currentPane->scroll(true, true);
Chris@45 3146 }
Chris@45 3147
Chris@45 3148 void
Chris@162 3149 MainWindowBase::peekRight()
Chris@162 3150 {
Chris@162 3151 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3152 if (currentPane) currentPane->scroll(true, false, false);
Chris@162 3153 }
Chris@162 3154
Chris@162 3155 void
Chris@45 3156 MainWindowBase::showNoOverlays()
Chris@45 3157 {
Chris@45 3158 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@45 3159 }
Chris@45 3160
Chris@45 3161 void
Chris@45 3162 MainWindowBase::showMinimalOverlays()
Chris@45 3163 {
Chris@335 3164 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@45 3165 }
Chris@45 3166
Chris@45 3167 void
Chris@45 3168 MainWindowBase::showAllOverlays()
Chris@45 3169 {
Chris@45 3170 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@45 3171 }
Chris@45 3172
Chris@45 3173 void
Chris@577 3174 MainWindowBase::findTimeRulerLayer()
Chris@577 3175 {
Chris@577 3176 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@577 3177 Pane *pane = m_paneStack->getPane(i);
Chris@577 3178 if (!pane) continue;
Chris@577 3179 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@577 3180 Layer *layer = pane->getLayer(j);
Chris@577 3181 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@577 3182 m_timeRulerLayer = layer;
Chris@577 3183 return;
Chris@577 3184 }
Chris@577 3185 }
Chris@577 3186 if (m_timeRulerLayer) {
Chris@577 3187 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
Chris@577 3188 delete m_timeRulerLayer;
Chris@636 3189 m_timeRulerLayer = nullptr;
Chris@577 3190 }
Chris@577 3191 }
Chris@577 3192
Chris@577 3193 void
Chris@211 3194 MainWindowBase::toggleTimeRulers()
Chris@211 3195 {
Chris@211 3196 bool haveRulers = false;
Chris@211 3197 bool someHidden = false;
Chris@211 3198
Chris@211 3199 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3200
Chris@211 3201 Pane *pane = m_paneStack->getPane(i);
Chris@211 3202 if (!pane) continue;
Chris@211 3203
Chris@211 3204 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3205
Chris@211 3206 Layer *layer = pane->getLayer(j);
Chris@211 3207 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3208
Chris@211 3209 haveRulers = true;
Chris@211 3210 if (layer->isLayerDormant(pane)) someHidden = true;
Chris@211 3211 }
Chris@211 3212 }
Chris@211 3213
Chris@211 3214 if (haveRulers) {
Chris@211 3215
Chris@211 3216 bool show = someHidden;
Chris@211 3217
Chris@211 3218 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3219
Chris@211 3220 Pane *pane = m_paneStack->getPane(i);
Chris@211 3221 if (!pane) continue;
Chris@211 3222
Chris@211 3223 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3224
Chris@211 3225 Layer *layer = pane->getLayer(j);
Chris@211 3226 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3227
Chris@211 3228 layer->showLayer(pane, show);
Chris@211 3229 }
Chris@211 3230 }
Chris@211 3231 }
Chris@211 3232 }
Chris@211 3233
Chris@211 3234 void
Chris@45 3235 MainWindowBase::toggleZoomWheels()
Chris@45 3236 {
Chris@45 3237 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@45 3238 m_viewManager->setZoomWheelsEnabled(false);
Chris@45 3239 } else {
Chris@45 3240 m_viewManager->setZoomWheelsEnabled(true);
Chris@45 3241 }
Chris@45 3242 }
Chris@45 3243
Chris@45 3244 void
Chris@45 3245 MainWindowBase::togglePropertyBoxes()
Chris@45 3246 {
Chris@712 3247 if (m_paneStack->getLayoutStyle() == PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3248 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3249 Preferences::VerticallyStacked) {
Chris@45 3250 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3251 } else {
Chris@45 3252 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3253 }
Chris@45 3254 } else {
Chris@712 3255 m_paneStack->setLayoutStyle(PaneStack::HiddenPropertyStacksLayout);
Chris@45 3256 }
Chris@45 3257 }
Chris@45 3258
Chris@378 3259 QLabel *
Chris@378 3260 MainWindowBase::getStatusLabel() const
Chris@378 3261 {
Chris@378 3262 if (!m_statusLabel) {
Chris@378 3263 m_statusLabel = new QLabel();
Chris@378 3264 statusBar()->addWidget(m_statusLabel, 1);
Chris@378 3265 }
Chris@379 3266
Chris@379 3267 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
Chris@379 3268 foreach (QFrame *f, frames) {
Chris@379 3269 f->setFrameStyle(QFrame::NoFrame);
Chris@379 3270 }
Chris@379 3271
Chris@378 3272 return m_statusLabel;
Chris@378 3273 }
Chris@378 3274
Chris@45 3275 void
Chris@45 3276 MainWindowBase::toggleStatusBar()
Chris@45 3277 {
Chris@45 3278 QSettings settings;
Chris@45 3279 settings.beginGroup("MainWindow");
Chris@45 3280 bool sb = settings.value("showstatusbar", true).toBool();
Chris@45 3281
Chris@45 3282 if (sb) {
Chris@45 3283 statusBar()->hide();
Chris@45 3284 } else {
Chris@45 3285 statusBar()->show();
Chris@45 3286 }
Chris@45 3287
Chris@45 3288 settings.setValue("showstatusbar", !sb);
Chris@45 3289
Chris@45 3290 settings.endGroup();
Chris@45 3291 }
Chris@45 3292
Chris@45 3293 void
Chris@256 3294 MainWindowBase::toggleCentreLine()
Chris@256 3295 {
Chris@256 3296 if (m_viewManager->shouldShowCentreLine()) {
Chris@256 3297 m_viewManager->setShowCentreLine(false);
Chris@256 3298 } else {
Chris@256 3299 m_viewManager->setShowCentreLine(true);
Chris@256 3300 }
Chris@256 3301 }
Chris@256 3302
Chris@256 3303 void
Chris@45 3304 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
Chris@45 3305 {
Chris@45 3306 if (name == "Property Box Layout") {
Chris@712 3307 if (m_paneStack->getLayoutStyle() != PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3308 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3309 Preferences::VerticallyStacked) {
Chris@45 3310 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3311 } else {
Chris@45 3312 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3313 }
Chris@45 3314 }
Chris@45 3315 } else if (name == "Background Mode" && m_viewManager) {
Chris@45 3316 Preferences::BackgroundMode mode =
Chris@45 3317 Preferences::getInstance()->getBackgroundMode();
Chris@45 3318 if (mode == Preferences::BackgroundFromTheme) {
Chris@45 3319 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@45 3320 } else if (mode == Preferences::DarkBackground) {
Chris@45 3321 m_viewManager->setGlobalDarkBackground(true);
Chris@45 3322 } else {
Chris@45 3323 m_viewManager->setGlobalDarkBackground(false);
Chris@45 3324 }
Chris@45 3325 }
Chris@45 3326 }
Chris@45 3327
Chris@45 3328 void
Chris@45 3329 MainWindowBase::play()
Chris@45 3330 {
Chris@516 3331 if ((m_recordTarget && m_recordTarget->isRecording()) ||
Chris@516 3332 (m_playSource && m_playSource->isPlaying())) {
Chris@45 3333 stop();
Chris@479 3334 QAction *action = qobject_cast<QAction *>(sender());
Chris@479 3335 if (action) action->setChecked(false);
Chris@45 3336 } else {
Chris@487 3337 if (m_audioIO) m_audioIO->resume();
Chris@509 3338 else if (m_playTarget) m_playTarget->resume();
Chris@45 3339 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@595 3340 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@45 3341 }
Chris@45 3342 }
Chris@45 3343
Chris@45 3344 void
Chris@477 3345 MainWindowBase::record()
Chris@477 3346 {
Chris@586 3347 QAction *action = qobject_cast<QAction *>(sender());
Chris@586 3348
Chris@714 3349 if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
Chris@586 3350 if (action) action->setChecked(false);
Chris@478 3351 return;
Chris@478 3352 }
Chris@478 3353
Chris@477 3354 if (!m_recordTarget) {
Chris@586 3355 if (action) action->setChecked(false);
Chris@477 3356 return;
Chris@477 3357 }
Chris@477 3358
Chris@714 3359 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
Chris@714 3360 SVDEBUG << "MainWindowBase::record: upgrading from "
Chris@714 3361 << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
Chris@714 3362 << "AUDIO_PLAYBACK_AND_RECORD" << endl;
Chris@714 3363 m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
Chris@714 3364 deleteAudioIO();
Chris@714 3365 }
Chris@714 3366
Chris@478 3367 if (!m_audioIO) {
Chris@611 3368 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
Chris@478 3369 createAudioIO();
Chris@478 3370 }
Chris@492 3371
Chris@492 3372 if (!m_audioIO) {
Chris@586 3373 if (!m_playTarget) {
Chris@586 3374 // Don't need to report this, createAudioIO should have
Chris@586 3375 if (action) action->setChecked(false);
Chris@586 3376 return;
Chris@586 3377 } else {
Chris@586 3378 // Need to report this: if the play target exists instead
Chris@586 3379 // of the audio IO, then that means we failed to open a
Chris@586 3380 // capture device. The record control should be disabled
Chris@586 3381 // in that situation, so if it happens here, that must
Chris@586 3382 // mean this is the first time we ever tried to open the
Chris@586 3383 // audio device, hence the need to report the problem here
Chris@586 3384 QMessageBox::critical
Chris@586 3385 (this, tr("No record device available"),
Chris@586 3386 tr("<b>No record device available</b><p>Failed to find or open an audio device for recording. Only playback will be available.</p>"));
Chris@586 3387 if (action) action->setChecked(false);
Chris@586 3388 updateMenuStates();
Chris@586 3389 return;
Chris@586 3390 }
Chris@492 3391 }
Chris@478 3392
Chris@477 3393 if (m_recordTarget->isRecording()) {
Chris@492 3394 stop();
Chris@477 3395 return;
Chris@477 3396 }
Chris@490 3397
Chris@483 3398 if (m_audioRecordMode == RecordReplaceSession) {
Chris@490 3399 if (!checkSaveModified()) {
Chris@490 3400 if (action) action->setChecked(false);
Chris@490 3401 return;
Chris@490 3402 }
Chris@483 3403 }
Chris@487 3404
Chris@557 3405 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
Chris@557 3406
Chris@611 3407 SVCERR << "MainWindowBase::record: about to resume" << endl;
Chris@492 3408 m_audioIO->resume();
Chris@509 3409
Chris@684 3410 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
Chris@684 3411 if (!modelPtr) {
Chris@586 3412 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
Chris@586 3413 QMessageBox::critical
Chris@586 3414 (this, tr("Recording failed"),
Chris@586 3415 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
Chris@490 3416 if (action) action->setChecked(false);
Chris@477 3417 return;
Chris@477 3418 }
Chris@477 3419
Chris@684 3420 if (!modelPtr->isOK()) {
Chris@611 3421 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
Chris@477 3422 m_recordTarget->stopRecording();
Chris@492 3423 m_audioIO->suspend();
Chris@586 3424 if (action) action->setChecked(false);
Chris@684 3425 delete modelPtr;
Chris@477 3426 return;
Chris@477 3427 }
Chris@611 3428
Chris@611 3429 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
Chris@684 3430
Chris@684 3431 QString location = modelPtr->getLocation();
Chris@487 3432
Chris@687 3433 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
Chris@483 3434
Chris@483 3435 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
Chris@478 3436
Chris@479 3437 //!!! duplication with openAudio here
Chris@479 3438
Chris@479 3439 QString templateName = getDefaultSessionTemplate();
Chris@479 3440 bool loadedTemplate = false;
Chris@479 3441
Chris@479 3442 if (templateName != "") {
Chris@479 3443 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@479 3444 if (tplStatus == FileOpenCancelled) {
Chris@611 3445 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
Chris@490 3446 m_recordTarget->stopRecording();
Chris@492 3447 m_audioIO->suspend();
Chris@684 3448 ModelById::release(modelId);
Chris@479 3449 return;
Chris@479 3450 }
Chris@479 3451 if (tplStatus != FileOpenFailed) {
Chris@479 3452 loadedTemplate = true;
Chris@479 3453 }
Chris@479 3454 }
Chris@479 3455
Chris@479 3456 if (!loadedTemplate) {
Chris@479 3457 closeSession();
Chris@479 3458 createDocument();
Chris@479 3459 }
Chris@479 3460
Chris@684 3461 ModelId prevMain = getMainModelId();
Chris@684 3462 if (!prevMain.isNone()) {
Chris@479 3463 m_playSource->removeModel(prevMain);
Chris@479 3464 }
Chris@479 3465
Chris@684 3466 m_document->setMainModel(modelId);
Chris@478 3467 setupMenus();
Chris@577 3468 findTimeRulerLayer();
Chris@478 3469
Chris@684 3470 m_originalLocation = location;
Chris@669 3471
Chris@595 3472 if (loadedTemplate || (m_sessionFile == "")) {
Chris@595 3473 CommandHistory::getInstance()->clear();
Chris@595 3474 CommandHistory::getInstance()->documentSaved();
Chris@595 3475 }
Chris@479 3476
Chris@669 3477 m_documentModified = false;
Chris@669 3478 updateWindowTitle();
Chris@669 3479
Chris@478 3480 } else {
Chris@478 3481
Chris@478 3482 CommandHistory::getInstance()->startCompoundOperation
Chris@478 3483 (tr("Import Recorded Audio"), true);
Chris@478 3484
Chris@691 3485 m_document->addNonDerivedModel(modelId);
Chris@478 3486
Chris@478 3487 AddPaneCommand *command = new AddPaneCommand(this);
Chris@478 3488 CommandHistory::getInstance()->addCommand(command);
Chris@478 3489
Chris@478 3490 Pane *pane = command->getPane();
Chris@478 3491
Chris@478 3492 if (m_timeRulerLayer) {
Chris@478 3493 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@478 3494 }
Chris@478 3495
Chris@684 3496 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@478 3497
Chris@478 3498 if (newLayer) {
Chris@478 3499 m_document->addLayerToView(pane, newLayer);
Chris@478 3500 }
Chris@595 3501
Chris@478 3502 CommandHistory::getInstance()->endCompoundOperation();
Chris@477 3503 }
Chris@479 3504
Chris@479 3505 updateMenuStates();
Chris@684 3506 m_recentFiles.addFile(location);
Chris@479 3507 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@611 3508
Chris@496 3509 emit audioFileLoaded();
Chris@477 3510 }
Chris@477 3511
Chris@477 3512 void
Chris@45 3513 MainWindowBase::ffwd()
Chris@45 3514 {
Chris@45 3515 if (!getMainModel()) return;
Chris@45 3516
Chris@708 3517 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3518 sv_frame_t frame = playbackFrame + 1;
Chris@45 3519
Chris@85 3520 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3521 Layer *layer = getSnapLayer();
Chris@435 3522 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3523
Chris@708 3524 if (!pane || !layer) {
Chris@45 3525
Chris@45 3526 frame = RealTime::realTime2Frame
Chris@357 3527 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
Chris@435 3528 if (frame > getMainModel()->getEndFrame()) {
Chris@45 3529 frame = getMainModel()->getEndFrame();
Chris@45 3530 }
Chris@45 3531
Chris@45 3532 } else {
Chris@45 3533
Chris@708 3534 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3535 int resolution = 0;
Chris@708 3536 bool success = false;
Chris@708 3537
Chris@708 3538 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3539 Layer::SnapRight, -1)) {
Chris@708 3540 if (pane->alignToReference(pframe) > playbackFrame) {
Chris@708 3541 success = true;
Chris@708 3542 break;
Chris@708 3543 } else {
Chris@708 3544 ++pframe;
Chris@708 3545 }
Chris@708 3546 }
Chris@708 3547
Chris@708 3548 if (success) {
Chris@708 3549 frame = pane->alignToReference(pframe);
Chris@85 3550 } else {
Chris@45 3551 frame = getMainModel()->getEndFrame();
Chris@45 3552 }
Chris@45 3553 }
Chris@45 3554
Chris@45 3555 if (frame < 0) frame = 0;
Chris@45 3556
Chris@45 3557 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3558 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3559 }
Chris@45 3560
Chris@45 3561 m_viewManager->setPlaybackFrame(frame);
Chris@166 3562
Chris@708 3563 if (frame >= getMainModel()->getEndFrame() &&
Chris@166 3564 m_playSource &&
Chris@166 3565 m_playSource->isPlaying() &&
Chris@166 3566 !m_viewManager->getPlayLoopMode()) {
Chris@166 3567 stop();
Chris@166 3568 }
Chris@45 3569 }
Chris@45 3570
Chris@45 3571 void
Chris@45 3572 MainWindowBase::ffwdEnd()
Chris@45 3573 {
Chris@45 3574 if (!getMainModel()) return;
Chris@45 3575
Chris@139 3576 if (m_playSource &&
Chris@139 3577 m_playSource->isPlaying() &&
Chris@139 3578 !m_viewManager->getPlayLoopMode()) {
Chris@139 3579 stop();
Chris@139 3580 }
Chris@139 3581
Chris@435 3582 sv_frame_t frame = getMainModel()->getEndFrame();
Chris@45 3583
Chris@45 3584 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3585 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3586 }
Chris@45 3587
Chris@45 3588 m_viewManager->setPlaybackFrame(frame);
Chris@45 3589 }
Chris@45 3590
Chris@45 3591 void
Chris@166 3592 MainWindowBase::ffwdSimilar()
Chris@166 3593 {
Chris@166 3594 if (!getMainModel()) return;
Chris@166 3595
Chris@166 3596 Layer *layer = getSnapLayer();
Chris@166 3597 if (!layer) { ffwd(); return; }
Chris@166 3598
Chris@166 3599 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3600 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3601
Chris@366 3602 int resolution = 0;
Chris@166 3603 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3604 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3605 frame, resolution, Layer::SnapRight)) {
Chris@166 3606 if (pane) frame = pane->alignToReference(frame);
Chris@166 3607 } else {
Chris@166 3608 frame = getMainModel()->getEndFrame();
Chris@166 3609 }
Chris@166 3610
Chris@166 3611 if (frame < 0) frame = 0;
Chris@166 3612
Chris@166 3613 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3614 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3615 }
Chris@166 3616
Chris@166 3617 m_viewManager->setPlaybackFrame(frame);
Chris@166 3618
Chris@435 3619 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3620 m_playSource &&
Chris@166 3621 m_playSource->isPlaying() &&
Chris@166 3622 !m_viewManager->getPlayLoopMode()) {
Chris@166 3623 stop();
Chris@166 3624 }
Chris@166 3625 }
Chris@166 3626
Chris@166 3627 void
Chris@45 3628 MainWindowBase::rewind()
Chris@45 3629 {
Chris@45 3630 if (!getMainModel()) return;
Chris@45 3631
Chris@708 3632 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3633 sv_frame_t frame = playbackFrame;
Chris@45 3634 if (frame > 0) --frame;
Chris@45 3635
Chris@85 3636 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3637 Layer *layer = getSnapLayer();
Chris@435 3638 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3639
Chris@45 3640 // when rewinding during playback, we want to allow a period
Chris@45 3641 // following a rewind target point at which the rewind will go to
Chris@45 3642 // the prior point instead of the immediately neighbouring one
Chris@45 3643 if (m_playSource && m_playSource->isPlaying()) {
Chris@45 3644 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@357 3645 ct = ct - RealTime::fromSeconds(0.15);
Chris@45 3646 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@45 3647 frame = RealTime::realTime2Frame(ct, sr);
Chris@45 3648 }
Chris@45 3649
Chris@708 3650 if (!pane || !layer) {
Chris@45 3651
Chris@45 3652 frame = RealTime::realTime2Frame
Chris@357 3653 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
Chris@435 3654 if (frame < getMainModel()->getStartFrame()) {
Chris@45 3655 frame = getMainModel()->getStartFrame();
Chris@45 3656 }
Chris@45 3657
Chris@45 3658 } else {
Chris@45 3659
Chris@708 3660 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3661 int resolution = 0;
Chris@708 3662 bool success = false;
Chris@708 3663
Chris@708 3664 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3665 Layer::SnapLeft, -1)) {
Chris@708 3666 if (pane->alignToReference(pframe) < playbackFrame ||
Chris@708 3667 pframe <= 0) {
Chris@708 3668 success = true;
Chris@708 3669 break;
Chris@708 3670 } else {
Chris@708 3671 --pframe;
Chris@708 3672 }
Chris@708 3673 }
Chris@708 3674
Chris@708 3675 if (success) {
Chris@708 3676 frame = pane->alignToReference(pframe);
Chris@85 3677 } else {
Chris@45 3678 frame = getMainModel()->getStartFrame();
Chris@45 3679 }
Chris@45 3680 }
Chris@45 3681
Chris@45 3682 if (frame < 0) frame = 0;
Chris@45 3683
Chris@45 3684 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3685 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3686 }
Chris@45 3687
Chris@45 3688 m_viewManager->setPlaybackFrame(frame);
Chris@45 3689 }
Chris@45 3690
Chris@45 3691 void
Chris@45 3692 MainWindowBase::rewindStart()
Chris@45 3693 {
Chris@45 3694 if (!getMainModel()) return;
Chris@45 3695
Chris@435 3696 sv_frame_t frame = getMainModel()->getStartFrame();
Chris@45 3697
Chris@45 3698 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3699 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3700 }
Chris@45 3701
Chris@45 3702 m_viewManager->setPlaybackFrame(frame);
Chris@45 3703 }
Chris@45 3704
Chris@166 3705 void
Chris@166 3706 MainWindowBase::rewindSimilar()
Chris@166 3707 {
Chris@166 3708 if (!getMainModel()) return;
Chris@166 3709
Chris@166 3710 Layer *layer = getSnapLayer();
Chris@166 3711 if (!layer) { rewind(); return; }
Chris@166 3712
Chris@166 3713 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3714 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3715
Chris@366 3716 int resolution = 0;
Chris@166 3717 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3718 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3719 frame, resolution, Layer::SnapLeft)) {
Chris@166 3720 if (pane) frame = pane->alignToReference(frame);
Chris@166 3721 } else {
Chris@166 3722 frame = getMainModel()->getStartFrame();
Chris@166 3723 }
Chris@166 3724
Chris@166 3725 if (frame < 0) frame = 0;
Chris@166 3726
Chris@166 3727 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3728 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3729 }
Chris@166 3730
Chris@166 3731 m_viewManager->setPlaybackFrame(frame);
Chris@166 3732 }
Chris@166 3733
Chris@45 3734 Layer *
Chris@45 3735 MainWindowBase::getSnapLayer() const
Chris@45 3736 {
Chris@45 3737 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 3738 if (!pane) return nullptr;
Chris@45 3739
Chris@45 3740 Layer *layer = pane->getSelectedLayer();
Chris@45 3741
Chris@45 3742 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@45 3743 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@194 3744 !dynamic_cast<RegionLayer *>(layer) &&
Chris@45 3745 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@45 3746
Chris@636 3747 layer = nullptr;
Chris@45 3748
Chris@45 3749 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 3750 Layer *l = pane->getLayer(i-1);
Chris@45 3751 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@45 3752 layer = l;
Chris@45 3753 break;
Chris@45 3754 }
Chris@45 3755 }
Chris@45 3756 }
Chris@45 3757
Chris@45 3758 return layer;
Chris@45 3759 }
Chris@45 3760
Chris@45 3761 void
Chris@45 3762 MainWindowBase::stop()
Chris@45 3763 {
Chris@516 3764 if (m_recordTarget &&
Chris@516 3765 m_recordTarget->isRecording()) {
Chris@477 3766 m_recordTarget->stopRecording();
Chris@477 3767 }
Chris@516 3768
Chris@516 3769 if (!m_playSource) return;
Chris@516 3770
Chris@45 3771 m_playSource->stop();
Chris@45 3772
Chris@611 3773 SVCERR << "MainWindowBase::stop: suspending" << endl;
Chris@611 3774
Chris@487 3775 if (m_audioIO) m_audioIO->suspend();
Chris@509 3776 else if (m_playTarget) m_playTarget->suspend();
Chris@487 3777
Chris@45 3778 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@45 3779 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@45 3780 } else {
Chris@45 3781 m_myStatusMessage = "";
Chris@378 3782 getStatusLabel()->setText("");
Chris@45 3783 }
Chris@45 3784 }
Chris@45 3785
Chris@45 3786 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
Chris@45 3787 m_mw(mw),
Chris@636 3788 m_pane(nullptr),
Chris@636 3789 m_prevCurrentPane(nullptr),
Chris@45 3790 m_added(false)
Chris@45 3791 {
Chris@45 3792 }
Chris@45 3793
Chris@45 3794 MainWindowBase::AddPaneCommand::~AddPaneCommand()
Chris@45 3795 {
Chris@45 3796 if (m_pane && !m_added) {
Chris@595 3797 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3798 }
Chris@45 3799 }
Chris@45 3800
Chris@45 3801 QString
Chris@45 3802 MainWindowBase::AddPaneCommand::getName() const
Chris@45 3803 {
Chris@45 3804 return tr("Add Pane");
Chris@45 3805 }
Chris@45 3806
Chris@45 3807 void
Chris@45 3808 MainWindowBase::AddPaneCommand::execute()
Chris@45 3809 {
Chris@45 3810 if (!m_pane) {
Chris@595 3811 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@595 3812 m_pane = m_mw->m_paneStack->addPane();
Chris@45 3813
Chris@45 3814 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 3815 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@45 3816 } else {
Chris@595 3817 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3818 }
Chris@45 3819
Chris@45 3820 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@45 3821 m_added = true;
Chris@45 3822 }
Chris@45 3823
Chris@45 3824 void
Chris@45 3825 MainWindowBase::AddPaneCommand::unexecute()
Chris@45 3826 {
Chris@45 3827 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3828 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3829 m_added = false;
Chris@45 3830 }
Chris@45 3831
Chris@45 3832 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
Chris@45 3833 m_mw(mw),
Chris@45 3834 m_pane(pane),
Chris@636 3835 m_prevCurrentPane(nullptr),
Chris@45 3836 m_added(true)
Chris@45 3837 {
Chris@45 3838 }
Chris@45 3839
Chris@45 3840 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
Chris@45 3841 {
Chris@45 3842 if (m_pane && !m_added) {
Chris@595 3843 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3844 }
Chris@45 3845 }
Chris@45 3846
Chris@45 3847 QString
Chris@45 3848 MainWindowBase::RemovePaneCommand::getName() const
Chris@45 3849 {
Chris@45 3850 return tr("Remove Pane");
Chris@45 3851 }
Chris@45 3852
Chris@45 3853 void
Chris@45 3854 MainWindowBase::RemovePaneCommand::execute()
Chris@45 3855 {
Chris@45 3856 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@45 3857 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3858 m_added = false;
Chris@45 3859 }
Chris@45 3860
Chris@45 3861 void
Chris@45 3862 MainWindowBase::RemovePaneCommand::unexecute()
Chris@45 3863 {
Chris@45 3864 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3865 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3866 m_added = true;
Chris@45 3867 }
Chris@45 3868
Chris@45 3869 void
Chris@45 3870 MainWindowBase::deleteCurrentPane()
Chris@45 3871 {
Chris@45 3872 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3873 (tr("Delete Pane"), true);
Chris@45 3874
Chris@45 3875 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3876 if (pane) {
Chris@595 3877 while (pane->getLayerCount() > 0) {
Chris@595 3878 Layer *layer = pane->getLayer(0);
Chris@595 3879 if (layer) {
Chris@595 3880 m_document->removeLayerFromView(pane, layer);
Chris@595 3881 } else {
Chris@595 3882 break;
Chris@595 3883 }
Chris@595 3884 }
Chris@595 3885
Chris@595 3886 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@595 3887 CommandHistory::getInstance()->addCommand(command);
Chris@45 3888 }
Chris@45 3889
Chris@45 3890 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 3891
Chris@45 3892 updateMenuStates();
Chris@45 3893 }
Chris@45 3894
Chris@45 3895 void
Chris@45 3896 MainWindowBase::deleteCurrentLayer()
Chris@45 3897 {
Chris@45 3898 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3899 if (pane) {
Chris@595 3900 Layer *layer = pane->getSelectedLayer();
Chris@595 3901 if (layer) {
Chris@595 3902 m_document->removeLayerFromView(pane, layer);
Chris@595 3903 }
Chris@45 3904 }
Chris@45 3905 updateMenuStates();
Chris@45 3906 }
Chris@45 3907
Chris@45 3908 void
Chris@123 3909 MainWindowBase::editCurrentLayer()
Chris@123 3910 {
Chris@636 3911 Layer *layer = nullptr;
Chris@123 3912 Pane *pane = m_paneStack->getCurrentPane();
Chris@123 3913 if (pane) layer = pane->getSelectedLayer();
Chris@123 3914 if (!layer) return;
Chris@123 3915
Chris@684 3916 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
Chris@124 3917 if (!tabular) {
Chris@124 3918 //!!! how to prevent this function from being active if not
Chris@124 3919 //appropriate model type? or will we ultimately support
Chris@124 3920 //tabular display for all editable models?
Chris@233 3921 SVDEBUG << "NOTE: Not a tabular model" << endl;
Chris@124 3922 return;
Chris@124 3923 }
Chris@124 3924
Chris@123 3925 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@126 3926 if (!m_layerDataDialogMap[layer].isNull()) {
Chris@126 3927 m_layerDataDialogMap[layer]->show();
Chris@126 3928 m_layerDataDialogMap[layer]->raise();
Chris@126 3929 return;
Chris@126 3930 }
Chris@123 3931 }
Chris@123 3932
Chris@125 3933 QString title = layer->getLayerPresentationName();
Chris@125 3934
Chris@684 3935 ModelDataTableDialog *dialog = new ModelDataTableDialog
Chris@684 3936 (layer->getModel(), title, this);
Chris@128 3937 dialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@128 3938
Chris@128 3939 connectLayerEditDialog(dialog);
Chris@123 3940
Chris@128 3941 m_layerDataDialogMap[layer] = dialog;
Chris@128 3942 m_viewDataDialogMap[pane].insert(dialog);
Chris@128 3943
Chris@128 3944 dialog->show();
Chris@128 3945 }
Chris@128 3946
Chris@128 3947 void
Chris@128 3948 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@128 3949 {
Chris@123 3950 connect(m_viewManager,
Chris@435 3951 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@123 3952 dialog,
Chris@435 3953 SLOT(userScrolledToFrame(sv_frame_t)));
Chris@127 3954
Chris@127 3955 connect(m_viewManager,
Chris@435 3956 SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@127 3957 dialog,
Chris@435 3958 SLOT(playbackScrolledToFrame(sv_frame_t)));
Chris@127 3959
Chris@123 3960 connect(dialog,
Chris@435 3961 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@123 3962 m_viewManager,
Chris@435 3963 SLOT(setGlobalCentreFrame(sv_frame_t)));
Chris@129 3964
Chris@129 3965 connect(dialog,
Chris@435 3966 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@129 3967 m_viewManager,
Chris@435 3968 SLOT(setPlaybackFrame(sv_frame_t)));
Chris@128 3969 }
Chris@123 3970
Chris@123 3971 void
Chris@73 3972 MainWindowBase::previousPane()
Chris@73 3973 {
Chris@73 3974 if (!m_paneStack) return;
Chris@73 3975
Chris@73 3976 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3977 if (!currentPane) return;
Chris@73 3978
Chris@73 3979 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3980 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3981 if (i == 0) return;
Chris@73 3982 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
Chris@73 3983 updateMenuStates();
Chris@73 3984 return;
Chris@73 3985 }
Chris@73 3986 }
Chris@73 3987 }
Chris@73 3988
Chris@73 3989 void
Chris@73 3990 MainWindowBase::nextPane()
Chris@73 3991 {
Chris@73 3992 if (!m_paneStack) return;
Chris@73 3993
Chris@73 3994 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3995 if (!currentPane) return;
Chris@73 3996
Chris@73 3997 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3998 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3999 if (i == m_paneStack->getPaneCount()-1) return;
Chris@73 4000 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
Chris@73 4001 updateMenuStates();
Chris@73 4002 return;
Chris@73 4003 }
Chris@73 4004 }
Chris@73 4005 }
Chris@73 4006
Chris@73 4007 void
Chris@73 4008 MainWindowBase::previousLayer()
Chris@73 4009 {
Chris@73 4010 if (!m_paneStack) return;
Chris@73 4011
Chris@73 4012 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 4013 if (!currentPane) return;
Chris@73 4014
Chris@403 4015 int count = currentPane->getLayerCount();
Chris@403 4016 if (count == 0) return;
Chris@403 4017
Chris@73 4018 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 4019
Chris@403 4020 if (!currentLayer) {
Chris@403 4021 // The pane itself is current
Chris@403 4022 m_paneStack->setCurrentLayer
Chris@403 4023 (currentPane, currentPane->getFixedOrderLayer(count-1));
Chris@403 4024 } else {
Chris@403 4025 for (int i = 0; i < count; ++i) {
Chris@403 4026 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 4027 if (i == 0) {
Chris@403 4028 m_paneStack->setCurrentLayer
Chris@636 4029 (currentPane, nullptr); // pane
Chris@403 4030 } else {
Chris@403 4031 m_paneStack->setCurrentLayer
Chris@403 4032 (currentPane, currentPane->getFixedOrderLayer(i-1));
Chris@403 4033 }
Chris@403 4034 break;
Chris@403 4035 }
Chris@73 4036 }
Chris@73 4037 }
Chris@403 4038
Chris@403 4039 updateMenuStates();
Chris@73 4040 }
Chris@73 4041
Chris@73 4042 void
Chris@73 4043 MainWindowBase::nextLayer()
Chris@73 4044 {
Chris@73 4045 if (!m_paneStack) return;
Chris@73 4046
Chris@73 4047 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 4048 if (!currentPane) return;
Chris@73 4049
Chris@403 4050 int count = currentPane->getLayerCount();
Chris@403 4051 if (count == 0) return;
Chris@403 4052
Chris@73 4053 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 4054
Chris@403 4055 if (!currentLayer) {
Chris@403 4056 // The pane itself is current
Chris@403 4057 m_paneStack->setCurrentLayer
Chris@403 4058 (currentPane, currentPane->getFixedOrderLayer(0));
Chris@403 4059 } else {
Chris@403 4060 for (int i = 0; i < count; ++i) {
Chris@403 4061 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 4062 if (i == currentPane->getLayerCount()-1) {
Chris@403 4063 m_paneStack->setCurrentLayer
Chris@636 4064 (currentPane, nullptr); // pane
Chris@403 4065 } else {
Chris@403 4066 m_paneStack->setCurrentLayer
Chris@403 4067 (currentPane, currentPane->getFixedOrderLayer(i+1));
Chris@403 4068 }
Chris@403 4069 break;
Chris@403 4070 }
Chris@73 4071 }
Chris@73 4072 }
Chris@403 4073
Chris@403 4074 updateMenuStates();
Chris@73 4075 }
Chris@73 4076
Chris@73 4077 void
Chris@435 4078 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
Chris@45 4079 {
Chris@45 4080 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@45 4081
Chris@187 4082 updatePositionStatusDisplays();
Chris@187 4083
Chris@45 4084 RealTime now = RealTime::frame2RealTime
Chris@45 4085 (frame, getMainModel()->getSampleRate());
Chris@45 4086
Chris@45 4087 if (now.sec == m_lastPlayStatusSec) return;
Chris@45 4088
Chris@45 4089 RealTime then = RealTime::frame2RealTime
Chris@45 4090 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@45 4091
Chris@45 4092 QString nowStr;
Chris@45 4093 QString thenStr;
Chris@45 4094 QString remainingStr;
Chris@45 4095
Chris@45 4096 if (then.sec > 10) {
Chris@45 4097 nowStr = now.toSecText().c_str();
Chris@45 4098 thenStr = then.toSecText().c_str();
Chris@45 4099 remainingStr = (then - now).toSecText().c_str();
Chris@45 4100 m_lastPlayStatusSec = now.sec;
Chris@45 4101 } else {
Chris@45 4102 nowStr = now.toText(true).c_str();
Chris@45 4103 thenStr = then.toText(true).c_str();
Chris@45 4104 remainingStr = (then - now).toText(true).c_str();
Chris@45 4105 }
Chris@45 4106
Chris@45 4107 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@45 4108 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@45 4109
Chris@378 4110 getStatusLabel()->setText(m_myStatusMessage);
Chris@45 4111 }
Chris@45 4112
Chris@45 4113 void
Chris@486 4114 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
Chris@486 4115 {
Chris@486 4116 RealTime duration = RealTime::frame2RealTime(frame, rate);
Chris@486 4117 QString durStr = duration.toSecText().c_str();
Chris@486 4118
Chris@486 4119 m_myStatusMessage = tr("Recording: %1").arg(durStr);
Chris@486 4120
Chris@486 4121 getStatusLabel()->setText(m_myStatusMessage);
Chris@486 4122 }
Chris@486 4123
Chris@486 4124 void
Chris@435 4125 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
Chris@45 4126 {
Chris@45 4127 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4128 Pane *p = nullptr;
Chris@45 4129 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4130 if (!p->getFollowGlobalPan()) return;
Chris@45 4131 updateVisibleRangeDisplay(p);
Chris@45 4132 }
Chris@45 4133
Chris@45 4134 void
Chris@435 4135 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
Chris@45 4136 {
Chris@233 4137 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
Chris@123 4138
Chris@123 4139 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
Chris@123 4140 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
Chris@123 4141 i != m_viewDataDialogMap[v].end(); ++i) {
Chris@127 4142 (*i)->userScrolledToFrame(frame);
Chris@123 4143 }
Chris@123 4144 }
Chris@45 4145 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4146 Pane *p = nullptr;
Chris@45 4147 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4148 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4149 }
Chris@45 4150
Chris@45 4151 void
Chris@624 4152 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
Chris@45 4153 {
Chris@45 4154 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4155 Pane *p = nullptr;
Chris@45 4156 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4157 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4158 }
Chris@45 4159
Chris@45 4160 void
Chris@45 4161 MainWindowBase::layerAdded(Layer *)
Chris@45 4162 {
Chris@233 4163 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
Chris@45 4164 updateMenuStates();
Chris@45 4165 }
Chris@45 4166
Chris@45 4167 void
Chris@45 4168 MainWindowBase::layerRemoved(Layer *)
Chris@45 4169 {
Chris@766 4170 Profiler profiler("MainWindowBase::layerRemoved");
Chris@233 4171 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
Chris@45 4172 updateMenuStates();
Chris@45 4173 }
Chris@45 4174
Chris@45 4175 void
Chris@45 4176 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
Chris@45 4177 {
Chris@233 4178 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
Chris@123 4179
Chris@128 4180 removeLayerEditDialog(layer);
Chris@123 4181
Chris@47 4182 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
Chris@595 4183 // cerr << "(this is the time ruler layer)" << endl;
Chris@636 4184 m_timeRulerLayer = nullptr;
Chris@45 4185 }
Chris@45 4186 }
Chris@45 4187
Chris@45 4188 void
Chris@45 4189 MainWindowBase::layerInAView(Layer *layer, bool inAView)
Chris@45 4190 {
Chris@233 4191 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
Chris@128 4192
Chris@128 4193 if (!inAView) removeLayerEditDialog(layer);
Chris@45 4194
Chris@45 4195 // Check whether we need to add or remove model from play source
Chris@684 4196 ModelId modelId = layer->getModel();
Chris@684 4197 if (!modelId.isNone()) {
Chris@45 4198 if (inAView) {
Chris@684 4199 m_playSource->addModel(modelId);
Chris@45 4200 } else {
Chris@45 4201 bool found = false;
Chris@45 4202 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 4203 Pane *pane = m_paneStack->getPane(i);
Chris@45 4204 if (!pane) continue;
Chris@45 4205 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@45 4206 Layer *pl = pane->getLayer(j);
Chris@183 4207 if (pl &&
Chris@183 4208 !dynamic_cast<TimeRulerLayer *>(pl) &&
Chris@684 4209 (pl->getModel() == modelId)) {
Chris@45 4210 found = true;
Chris@45 4211 break;
Chris@45 4212 }
Chris@45 4213 }
Chris@45 4214 if (found) break;
Chris@45 4215 }
Chris@173 4216 if (!found) {
Chris@684 4217 m_playSource->removeModel(modelId);
Chris@173 4218 }
Chris@45 4219 }
Chris@45 4220 }
Chris@45 4221
Chris@45 4222 updateMenuStates();
Chris@45 4223 }
Chris@45 4224
Chris@45 4225 void
Chris@128 4226 MainWindowBase::removeLayerEditDialog(Layer *layer)
Chris@128 4227 {
Chris@128 4228 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@128 4229
Chris@128 4230 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
Chris@128 4231
Chris@128 4232 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
Chris@128 4233 vi != m_viewDataDialogMap.end(); ++vi) {
Chris@128 4234 vi->second.erase(dialog);
Chris@128 4235 }
Chris@128 4236
Chris@128 4237 m_layerDataDialogMap.erase(layer);
Chris@128 4238 delete dialog;
Chris@128 4239 }
Chris@128 4240 }
Chris@128 4241
Chris@128 4242 void
Chris@684 4243 MainWindowBase::modelAdded(ModelId model)
Chris@45 4244 {
Chris@233 4245 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
Chris@684 4246 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
Chris@45 4247 m_playSource->addModel(model);
Chris@45 4248 }
Chris@45 4249
Chris@45 4250 void
Chris@684 4251 MainWindowBase::mainModelChanged(ModelId modelId)
Chris@45 4252 {
Chris@233 4253 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
Chris@45 4254 updateDescriptionLabel();
Chris@684 4255 auto model = ModelById::getAs<WaveFileModel>(modelId);
Chris@45 4256 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@714 4257 if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
Chris@475 4258 createAudioIO();
Chris@360 4259 }
Chris@45 4260 }
Chris@45 4261
Chris@45 4262 void
Chris@55 4263 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
Chris@55 4264 {
Chris@55 4265 bool found = false;
Chris@55 4266 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@55 4267 if (m_paneStack->getPane(i) == pane) {
Chris@55 4268 found = true;
Chris@55 4269 break;
Chris@55 4270 }
Chris@55 4271 }
Chris@55 4272 if (!found) {
Chris@233 4273 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
Chris@229 4274 << pane << endl;
Chris@55 4275 return;
Chris@55 4276 }
Chris@55 4277
Chris@55 4278 CommandHistory::getInstance()->startCompoundOperation
Chris@595 4279 (tr("Delete Pane"), true);
Chris@55 4280
Chris@55 4281 while (pane->getLayerCount() > 0) {
Chris@637 4282 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@55 4283 if (layer) {
Chris@55 4284 m_document->removeLayerFromView(pane, layer);
Chris@55 4285 } else {
Chris@55 4286 break;
Chris@55 4287 }
Chris@55 4288 }
Chris@55 4289
Chris@55 4290 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@55 4291 CommandHistory::getInstance()->addCommand(command);
Chris@55 4292
Chris@55 4293 CommandHistory::getInstance()->endCompoundOperation();
Chris@55 4294
Chris@55 4295 updateMenuStates();
Chris@55 4296 }
Chris@55 4297
Chris@55 4298 void
Chris@684 4299 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
Chris@429 4300 {
Chris@684 4301 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
Chris@429 4302 }
Chris@429 4303
Chris@429 4304 void
Chris@45 4305 MainWindowBase::pollOSC()
Chris@45 4306 {
Chris@45 4307 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@725 4308
Chris@758 4309 if (m_handlingOSC) {
Chris@758 4310 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@758 4311 << "not making nested invocations, waiting"
Chris@758 4312 << endl;
Chris@758 4313 return;
Chris@758 4314 }
Chris@758 4315
Chris@758 4316 m_handlingOSC = true;
Chris@758 4317
Chris@725 4318 while (!m_oscQueue->isEmpty()) {
Chris@725 4319
Chris@725 4320 if (m_openingAudioFile) {
Chris@725 4321 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4322 << "waiting for audio to finish loading"
Chris@725 4323 << endl;
Chris@758 4324 m_handlingOSC = false;
Chris@725 4325 return;
Chris@725 4326 }
Chris@725 4327
Chris@725 4328 if (ModelTransformerFactory::getInstance()->haveRunningTransformers()) {
Chris@725 4329 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4330 << "waiting for running transforms to complete"
Chris@725 4331 << endl;
Chris@758 4332 m_handlingOSC = false;
Chris@725 4333 return;
Chris@725 4334 }
Chris@725 4335
Chris@725 4336 SVDEBUG << "MainWindowBase::pollOSC: have "
Chris@725 4337 << m_oscQueue->getMessagesAvailable()
Chris@725 4338 << " messages" << endl;
Chris@725 4339
Chris@725 4340 OSCMessage message = m_oscQueue->readMessage();
Chris@725 4341
Chris@725 4342 if (message.getTarget() != 0) {
Chris@725 4343 SVCERR << "MainWindowBase::pollOSC: ignoring message with target "
Chris@725 4344 << message.getTarget() << " (we are target 0)" << endl;
Chris@758 4345 m_handlingOSC = false;
Chris@725 4346 continue;
Chris@725 4347 }
Chris@725 4348
Chris@725 4349 handleOSCMessage(message);
Chris@725 4350
Chris@725 4351 disconnect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@725 4352 this, SLOT(pollOSC()));
Chris@725 4353
Chris@725 4354 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
Chris@725 4355 QEventLoop::ExcludeSocketNotifiers);
Chris@45 4356 }
Chris@758 4357
Chris@758 4358 m_handlingOSC = false;
Chris@758 4359
Chris@758 4360 connect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@758 4361 this, SLOT(pollOSC()));
Chris@45 4362 }
Chris@45 4363
Chris@45 4364 void
Chris@45 4365 MainWindowBase::inProgressSelectionChanged()
Chris@45 4366 {
Chris@636 4367 Pane *currentPane = nullptr;
Chris@45 4368 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
justin@331 4369 if (currentPane) {
justin@331 4370 //cerr << "JTEST: mouse event on selection pane" << endl;
justin@331 4371 updateVisibleRangeDisplay(currentPane);
justin@331 4372 }
Chris@45 4373 }
Chris@45 4374
Chris@45 4375 void
Chris@45 4376 MainWindowBase::contextHelpChanged(const QString &s)
Chris@45 4377 {
Chris@378 4378 QLabel *lab = getStatusLabel();
Chris@375 4379
Chris@45 4380 if (s == "" && m_myStatusMessage != "") {
Chris@378 4381 if (lab->text() != m_myStatusMessage) {
Chris@378 4382 lab->setText(m_myStatusMessage);
Chris@375 4383 }
Chris@45 4384 return;
Chris@45 4385 }
Chris@375 4386
Chris@378 4387 lab->setText(s);
Chris@45 4388 }
Chris@45 4389
Chris@45 4390 void
Chris@45 4391 MainWindowBase::openHelpUrl(QString url)
Chris@45 4392 {
Chris@45 4393 // This method mostly lifted from Qt Assistant source code
Chris@45 4394
Chris@45 4395 QProcess *process = new QProcess(this);
Chris@45 4396 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@45 4397
Chris@45 4398 QStringList args;
Chris@45 4399
Chris@45 4400 #ifdef Q_OS_MAC
Chris@45 4401 args.append(url);
Chris@45 4402 process->start("open", args);
Chris@45 4403 #else
Chris@45 4404 #ifdef Q_OS_WIN32
Chris@599 4405 std::string pfiles;
Chris@599 4406 (void)getEnvUtf8("ProgramFiles", pfiles);
Chris@599 4407 QString command =
Chris@599 4408 QString::fromStdString(pfiles) +
Chris@599 4409 QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@358 4410
Chris@358 4411 args.append(url);
Chris@358 4412 process->start(command, args);
Chris@45 4413 #else
Chris@45 4414 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@45 4415 args.append("exec");
Chris@45 4416 args.append(url);
Chris@45 4417 process->start("kfmclient", args);
Chris@45 4418 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@45 4419 args.append(url);
Chris@45 4420 process->start(qgetenv("BROWSER"), args);
Chris@45 4421 } else {
Chris@45 4422 args.append(url);
Chris@45 4423 process->start("firefox", args);
Chris@45 4424 }
Chris@45 4425 #endif
Chris@45 4426 #endif
Chris@45 4427 }
Chris@45 4428
Chris@483 4429 void
Chris@483 4430 MainWindowBase::openLocalFolder(QString path)
Chris@483 4431 {
Chris@483 4432 QDir d(path);
Chris@483 4433 if (d.exists()) {
Chris@483 4434 QStringList args;
Chris@483 4435 QString path = d.canonicalPath();
Chris@483 4436 #if defined Q_OS_WIN32
Chris@483 4437 // Although the Win32 API is quite happy to have
Chris@483 4438 // forward slashes as directory separators, Windows
Chris@483 4439 // Explorer is not
Chris@483 4440 path = path.replace('/', '\\');
Chris@483 4441 args << path;
Chris@483 4442 QProcess::execute("c:/windows/explorer.exe", args);
Chris@483 4443 #else
Chris@483 4444 args << path;
Chris@605 4445 QProcess process;
Chris@605 4446 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@606 4447 env.insert("LD_LIBRARY_PATH", "");
Chris@605 4448 process.setProcessEnvironment(env);
Chris@605 4449 process.start(
Chris@483 4450 #if defined Q_OS_MAC
Chris@483 4451 "/usr/bin/open",
Chris@483 4452 #else
Chris@483 4453 "/usr/bin/xdg-open",
Chris@483 4454 #endif
Chris@483 4455 args);
Chris@608 4456 process.waitForFinished();
Chris@483 4457 #endif
Chris@483 4458 }
Chris@483 4459 }
Chris@483 4460