annotate framework/MainWindowBase.cpp @ 738:48001ed9143b audio-source-refactor

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