annotate framework/MainWindowBase.cpp @ 744:36772d79cf44 pitch-align

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