annotate framework/MainWindowBase.cpp @ 731:16932dfaf64e

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