annotate framework/MainWindowBase.cpp @ 726:1e2e03197b8c spectrogram-export

Remove m_abandoning: set m_documentModified to false instead (the purpose of it is just to avoid asking user to save)
author Chris Cannam
date Wed, 08 Jan 2020 15:34:10 +0000
parents 16f6737fa557
children e2ad6fe768b0
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@659 2806 bool
Chris@721 2807 MainWindowBase::exportLayerTo(Layer *layer, View *fromView,
Chris@724 2808 MultiSelection *selectionsToWrite,
Chris@721 2809 QString path, QString &error)
Chris@659 2810 {
Chris@724 2811 //!!! should we pull out the whole export logic into another
Chris@724 2812 // class? then we can more reasonably query it for things like
Chris@724 2813 // "can we export this layer type to this file format? can we
Chris@724 2814 // export selections, or only the whole layer?"
Chris@724 2815
Chris@659 2816 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@659 2817
Chris@659 2818 QString suffix = QFileInfo(path).suffix().toLower();
Chris@659 2819
Chris@721 2820 auto model = ModelById::get(layer->getExportModel(fromView));
Chris@684 2821 if (!model) {
Chris@684 2822 error = tr("Internal error: unknown model");
Chris@684 2823 return false;
Chris@684 2824 }
Chris@659 2825
Chris@659 2826 if (suffix == "xml" || suffix == "svl") {
Chris@659 2827
Chris@724 2828 //!!! +selection
Chris@724 2829
Chris@659 2830 QFile file(path);
Chris@659 2831 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@659 2832 error = tr("Failed to open file %1 for writing").arg(path);
Chris@659 2833 } else {
Chris@659 2834 QTextStream out(&file);
Chris@659 2835 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@659 2836 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@659 2837 << "<!DOCTYPE sonic-visualiser>\n"
Chris@659 2838 << "<sv>\n"
Chris@659 2839 << " <data>\n";
Chris@659 2840
Chris@659 2841 model->toXml(out, " ");
Chris@659 2842
Chris@659 2843 out << " </data>\n"
Chris@659 2844 << " <display>\n";
Chris@659 2845
Chris@659 2846 layer->toXml(out, " ");
Chris@659 2847
Chris@659 2848 out << " </display>\n"
Chris@659 2849 << "</sv>\n";
Chris@659 2850 }
Chris@659 2851
Chris@659 2852 } else if (suffix == "mid" || suffix == "midi") {
Chris@659 2853
Chris@684 2854 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
Chris@659 2855
Chris@659 2856 if (!nm) {
Chris@659 2857 error = tr("Can't export non-note layers to MIDI");
Chris@724 2858 } else if (!selectionsToWrite) {
Chris@684 2859 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
Chris@659 2860 writer.write();
Chris@659 2861 if (!writer.isOK()) {
Chris@659 2862 error = writer.getError();
Chris@659 2863 }
Chris@724 2864 } else {
Chris@724 2865 NoteModel temporary(nm->getSampleRate(),
Chris@724 2866 nm->getResolution(),
Chris@724 2867 nm->getValueMinimum(),
Chris@724 2868 nm->getValueMaximum(),
Chris@724 2869 false);
Chris@724 2870 temporary.setScaleUnits(nm->getScaleUnits());
Chris@724 2871 for (const auto &s: selectionsToWrite->getSelections()) {
Chris@724 2872 EventVector ev(nm->getEventsStartingWithin
Chris@724 2873 (s.getStartFrame(), s.getDuration()));
Chris@724 2874 for (const auto &e: ev) {
Chris@724 2875 temporary.add(e);
Chris@724 2876 }
Chris@724 2877 }
Chris@724 2878 MIDIFileWriter writer(path, &temporary, temporary.getSampleRate());
Chris@724 2879 writer.write();
Chris@724 2880 if (!writer.isOK()) {
Chris@724 2881 error = writer.getError();
Chris@724 2882 }
Chris@659 2883 }
Chris@659 2884
Chris@659 2885 } else if (suffix == "ttl" || suffix == "n3") {
Chris@659 2886
Chris@724 2887 //!!! +selection
Chris@724 2888
Chris@684 2889 if (!RDFExporter::canExportModel(model.get())) {
Chris@659 2890 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
Chris@659 2891 } else {
Chris@684 2892 RDFExporter exporter(path, model.get());
Chris@659 2893 exporter.write();
Chris@659 2894 if (!exporter.isOK()) {
Chris@659 2895 error = exporter.getError();
Chris@659 2896 }
Chris@659 2897 }
Chris@659 2898
Chris@659 2899 } else {
Chris@659 2900
Chris@722 2901 ProgressDialog dialog {
Chris@722 2902 QObject::tr("Exporting layer..."), true, 500, this,
Chris@722 2903 Qt::ApplicationModal
Chris@722 2904 };
Chris@722 2905
Chris@722 2906 CSVFileWriter writer(path, model.get(), &dialog,
Chris@659 2907 ((suffix == "csv") ? "," : "\t"));
Chris@724 2908
Chris@724 2909 if (selectionsToWrite) {
Chris@724 2910 writer.writeSelection(*selectionsToWrite);
Chris@724 2911 } else {
Chris@724 2912 writer.write();
Chris@724 2913 }
Chris@659 2914
Chris@659 2915 if (!writer.isOK()) {
Chris@659 2916 error = writer.getError();
Chris@722 2917 if (error == "") {
Chris@722 2918 error = tr("Failed to export layer for an unknown reason");
Chris@722 2919 }
Chris@659 2920 }
Chris@659 2921 }
Chris@659 2922
Chris@659 2923 return (error == "");
Chris@659 2924 }
Chris@659 2925
Chris@45 2926 void
Chris@226 2927 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
Chris@45 2928 {
Chris@45 2929 QString indent(" ");
Chris@45 2930
Chris@45 2931 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@45 2932 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@45 2933 out << "<sv>\n";
Chris@45 2934
Chris@226 2935 if (asTemplate) {
Chris@226 2936 m_document->toXmlAsTemplate(out, "", "");
Chris@226 2937 } else {
Chris@226 2938 m_document->toXml(out, "", "");
Chris@226 2939 }
Chris@45 2940
Chris@45 2941 out << "<display>\n";
Chris@45 2942
Chris@45 2943 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@595 2944 .arg(width()).arg(height());
Chris@45 2945
Chris@45 2946 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 2947
Chris@595 2948 Pane *pane = m_paneStack->getPane(i);
Chris@595 2949
Chris@595 2950 if (pane) {
Chris@45 2951 pane->toXml(out, indent);
Chris@595 2952 }
Chris@45 2953 }
Chris@45 2954
Chris@45 2955 out << "</display>\n";
Chris@45 2956
Chris@45 2957 m_viewManager->getSelection().toXml(out);
Chris@45 2958
Chris@45 2959 out << "</sv>\n";
Chris@45 2960 }
Chris@45 2961
Chris@45 2962 Pane *
Chris@45 2963 MainWindowBase::addPaneToStack()
Chris@45 2964 {
Chris@342 2965 cerr << "MainWindowBase::addPaneToStack()" << endl;
Chris@45 2966 AddPaneCommand *command = new AddPaneCommand(this);
Chris@45 2967 CommandHistory::getInstance()->addCommand(command);
Chris@57 2968 Pane *pane = command->getPane();
Chris@57 2969 return pane;
Chris@45 2970 }
Chris@45 2971
Chris@45 2972 void
Chris@45 2973 MainWindowBase::zoomIn()
Chris@45 2974 {
Chris@45 2975 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2976 if (currentPane) currentPane->zoom(true);
Chris@45 2977 }
Chris@45 2978
Chris@45 2979 void
Chris@45 2980 MainWindowBase::zoomOut()
Chris@45 2981 {
Chris@45 2982 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2983 if (currentPane) currentPane->zoom(false);
Chris@45 2984 }
Chris@45 2985
Chris@45 2986 void
Chris@45 2987 MainWindowBase::zoomToFit()
Chris@45 2988 {
Chris@45 2989 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2990 if (!currentPane) return;
Chris@45 2991
Chris@684 2992 auto model = getMainModel();
Chris@45 2993 if (!model) return;
Chris@45 2994
Chris@434 2995 sv_frame_t start = model->getStartFrame();
Chris@434 2996 sv_frame_t end = model->getEndFrame();
Chris@60 2997 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
Chris@366 2998 int pixels = currentPane->width();
Chris@366 2999
Chris@366 3000 int sw = currentPane->getVerticalScaleWidth();
Chris@45 3001 if (pixels > sw * 2) pixels -= sw * 2;
Chris@45 3002 else pixels = 1;
Chris@45 3003 if (pixels > 4) pixels -= 4;
Chris@45 3004
Chris@624 3005 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
Chris@45 3006 currentPane->setZoomLevel(zoomLevel);
Chris@45 3007 currentPane->setCentreFrame((start + end) / 2);
Chris@45 3008 }
Chris@45 3009
Chris@45 3010 void
Chris@45 3011 MainWindowBase::zoomDefault()
Chris@45 3012 {
Chris@45 3013 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@302 3014 QSettings settings;
Chris@302 3015 settings.beginGroup("MainWindow");
Chris@302 3016 int zoom = settings.value("zoom-default", 1024).toInt();
Chris@302 3017 settings.endGroup();
Chris@624 3018 if (currentPane) {
Chris@624 3019 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
Chris@624 3020 }
Chris@45 3021 }
Chris@45 3022
Chris@45 3023 void
Chris@45 3024 MainWindowBase::scrollLeft()
Chris@45 3025 {
Chris@45 3026 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3027 if (currentPane) currentPane->scroll(false, false);
Chris@45 3028 }
Chris@45 3029
Chris@45 3030 void
Chris@45 3031 MainWindowBase::jumpLeft()
Chris@45 3032 {
Chris@45 3033 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3034 if (currentPane) currentPane->scroll(false, true);
Chris@45 3035 }
Chris@45 3036
Chris@45 3037 void
Chris@162 3038 MainWindowBase::peekLeft()
Chris@162 3039 {
Chris@162 3040 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3041 if (currentPane) currentPane->scroll(false, false, false);
Chris@162 3042 }
Chris@162 3043
Chris@162 3044 void
Chris@45 3045 MainWindowBase::scrollRight()
Chris@45 3046 {
Chris@45 3047 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3048 if (currentPane) currentPane->scroll(true, false);
Chris@45 3049 }
Chris@45 3050
Chris@45 3051 void
Chris@45 3052 MainWindowBase::jumpRight()
Chris@45 3053 {
Chris@45 3054 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3055 if (currentPane) currentPane->scroll(true, true);
Chris@45 3056 }
Chris@45 3057
Chris@45 3058 void
Chris@162 3059 MainWindowBase::peekRight()
Chris@162 3060 {
Chris@162 3061 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3062 if (currentPane) currentPane->scroll(true, false, false);
Chris@162 3063 }
Chris@162 3064
Chris@162 3065 void
Chris@45 3066 MainWindowBase::showNoOverlays()
Chris@45 3067 {
Chris@45 3068 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@45 3069 }
Chris@45 3070
Chris@45 3071 void
Chris@45 3072 MainWindowBase::showMinimalOverlays()
Chris@45 3073 {
Chris@335 3074 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@45 3075 }
Chris@45 3076
Chris@45 3077 void
Chris@45 3078 MainWindowBase::showAllOverlays()
Chris@45 3079 {
Chris@45 3080 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@45 3081 }
Chris@45 3082
Chris@45 3083 void
Chris@577 3084 MainWindowBase::findTimeRulerLayer()
Chris@577 3085 {
Chris@577 3086 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@577 3087 Pane *pane = m_paneStack->getPane(i);
Chris@577 3088 if (!pane) continue;
Chris@577 3089 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@577 3090 Layer *layer = pane->getLayer(j);
Chris@577 3091 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@577 3092 m_timeRulerLayer = layer;
Chris@577 3093 return;
Chris@577 3094 }
Chris@577 3095 }
Chris@577 3096 if (m_timeRulerLayer) {
Chris@577 3097 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
Chris@577 3098 delete m_timeRulerLayer;
Chris@636 3099 m_timeRulerLayer = nullptr;
Chris@577 3100 }
Chris@577 3101 }
Chris@577 3102
Chris@577 3103 void
Chris@211 3104 MainWindowBase::toggleTimeRulers()
Chris@211 3105 {
Chris@211 3106 bool haveRulers = false;
Chris@211 3107 bool someHidden = false;
Chris@211 3108
Chris@211 3109 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3110
Chris@211 3111 Pane *pane = m_paneStack->getPane(i);
Chris@211 3112 if (!pane) continue;
Chris@211 3113
Chris@211 3114 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3115
Chris@211 3116 Layer *layer = pane->getLayer(j);
Chris@211 3117 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3118
Chris@211 3119 haveRulers = true;
Chris@211 3120 if (layer->isLayerDormant(pane)) someHidden = true;
Chris@211 3121 }
Chris@211 3122 }
Chris@211 3123
Chris@211 3124 if (haveRulers) {
Chris@211 3125
Chris@211 3126 bool show = someHidden;
Chris@211 3127
Chris@211 3128 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3129
Chris@211 3130 Pane *pane = m_paneStack->getPane(i);
Chris@211 3131 if (!pane) continue;
Chris@211 3132
Chris@211 3133 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3134
Chris@211 3135 Layer *layer = pane->getLayer(j);
Chris@211 3136 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3137
Chris@211 3138 layer->showLayer(pane, show);
Chris@211 3139 }
Chris@211 3140 }
Chris@211 3141 }
Chris@211 3142 }
Chris@211 3143
Chris@211 3144 void
Chris@45 3145 MainWindowBase::toggleZoomWheels()
Chris@45 3146 {
Chris@45 3147 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@45 3148 m_viewManager->setZoomWheelsEnabled(false);
Chris@45 3149 } else {
Chris@45 3150 m_viewManager->setZoomWheelsEnabled(true);
Chris@45 3151 }
Chris@45 3152 }
Chris@45 3153
Chris@45 3154 void
Chris@45 3155 MainWindowBase::togglePropertyBoxes()
Chris@45 3156 {
Chris@712 3157 if (m_paneStack->getLayoutStyle() == PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3158 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3159 Preferences::VerticallyStacked) {
Chris@45 3160 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3161 } else {
Chris@45 3162 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3163 }
Chris@45 3164 } else {
Chris@712 3165 m_paneStack->setLayoutStyle(PaneStack::HiddenPropertyStacksLayout);
Chris@45 3166 }
Chris@45 3167 }
Chris@45 3168
Chris@378 3169 QLabel *
Chris@378 3170 MainWindowBase::getStatusLabel() const
Chris@378 3171 {
Chris@378 3172 if (!m_statusLabel) {
Chris@378 3173 m_statusLabel = new QLabel();
Chris@378 3174 statusBar()->addWidget(m_statusLabel, 1);
Chris@378 3175 }
Chris@379 3176
Chris@379 3177 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
Chris@379 3178 foreach (QFrame *f, frames) {
Chris@379 3179 f->setFrameStyle(QFrame::NoFrame);
Chris@379 3180 }
Chris@379 3181
Chris@378 3182 return m_statusLabel;
Chris@378 3183 }
Chris@378 3184
Chris@45 3185 void
Chris@45 3186 MainWindowBase::toggleStatusBar()
Chris@45 3187 {
Chris@45 3188 QSettings settings;
Chris@45 3189 settings.beginGroup("MainWindow");
Chris@45 3190 bool sb = settings.value("showstatusbar", true).toBool();
Chris@45 3191
Chris@45 3192 if (sb) {
Chris@45 3193 statusBar()->hide();
Chris@45 3194 } else {
Chris@45 3195 statusBar()->show();
Chris@45 3196 }
Chris@45 3197
Chris@45 3198 settings.setValue("showstatusbar", !sb);
Chris@45 3199
Chris@45 3200 settings.endGroup();
Chris@45 3201 }
Chris@45 3202
Chris@45 3203 void
Chris@256 3204 MainWindowBase::toggleCentreLine()
Chris@256 3205 {
Chris@256 3206 if (m_viewManager->shouldShowCentreLine()) {
Chris@256 3207 m_viewManager->setShowCentreLine(false);
Chris@256 3208 } else {
Chris@256 3209 m_viewManager->setShowCentreLine(true);
Chris@256 3210 }
Chris@256 3211 }
Chris@256 3212
Chris@256 3213 void
Chris@45 3214 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
Chris@45 3215 {
Chris@45 3216 if (name == "Property Box Layout") {
Chris@712 3217 if (m_paneStack->getLayoutStyle() != PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3218 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3219 Preferences::VerticallyStacked) {
Chris@45 3220 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3221 } else {
Chris@45 3222 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3223 }
Chris@45 3224 }
Chris@45 3225 } else if (name == "Background Mode" && m_viewManager) {
Chris@45 3226 Preferences::BackgroundMode mode =
Chris@45 3227 Preferences::getInstance()->getBackgroundMode();
Chris@45 3228 if (mode == Preferences::BackgroundFromTheme) {
Chris@45 3229 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@45 3230 } else if (mode == Preferences::DarkBackground) {
Chris@45 3231 m_viewManager->setGlobalDarkBackground(true);
Chris@45 3232 } else {
Chris@45 3233 m_viewManager->setGlobalDarkBackground(false);
Chris@45 3234 }
Chris@45 3235 }
Chris@45 3236 }
Chris@45 3237
Chris@45 3238 void
Chris@45 3239 MainWindowBase::play()
Chris@45 3240 {
Chris@516 3241 if ((m_recordTarget && m_recordTarget->isRecording()) ||
Chris@516 3242 (m_playSource && m_playSource->isPlaying())) {
Chris@45 3243 stop();
Chris@479 3244 QAction *action = qobject_cast<QAction *>(sender());
Chris@479 3245 if (action) action->setChecked(false);
Chris@45 3246 } else {
Chris@487 3247 if (m_audioIO) m_audioIO->resume();
Chris@509 3248 else if (m_playTarget) m_playTarget->resume();
Chris@45 3249 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@595 3250 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@45 3251 }
Chris@45 3252 }
Chris@45 3253
Chris@45 3254 void
Chris@477 3255 MainWindowBase::record()
Chris@477 3256 {
Chris@586 3257 QAction *action = qobject_cast<QAction *>(sender());
Chris@586 3258
Chris@714 3259 if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
Chris@586 3260 if (action) action->setChecked(false);
Chris@478 3261 return;
Chris@478 3262 }
Chris@478 3263
Chris@477 3264 if (!m_recordTarget) {
Chris@586 3265 if (action) action->setChecked(false);
Chris@477 3266 return;
Chris@477 3267 }
Chris@477 3268
Chris@714 3269 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
Chris@714 3270 SVDEBUG << "MainWindowBase::record: upgrading from "
Chris@714 3271 << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
Chris@714 3272 << "AUDIO_PLAYBACK_AND_RECORD" << endl;
Chris@714 3273 m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
Chris@714 3274 deleteAudioIO();
Chris@714 3275 }
Chris@714 3276
Chris@478 3277 if (!m_audioIO) {
Chris@611 3278 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
Chris@478 3279 createAudioIO();
Chris@478 3280 }
Chris@492 3281
Chris@492 3282 if (!m_audioIO) {
Chris@586 3283 if (!m_playTarget) {
Chris@586 3284 // Don't need to report this, createAudioIO should have
Chris@586 3285 if (action) action->setChecked(false);
Chris@586 3286 return;
Chris@586 3287 } else {
Chris@586 3288 // Need to report this: if the play target exists instead
Chris@586 3289 // of the audio IO, then that means we failed to open a
Chris@586 3290 // capture device. The record control should be disabled
Chris@586 3291 // in that situation, so if it happens here, that must
Chris@586 3292 // mean this is the first time we ever tried to open the
Chris@586 3293 // audio device, hence the need to report the problem here
Chris@586 3294 QMessageBox::critical
Chris@586 3295 (this, tr("No record device available"),
Chris@586 3296 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 3297 if (action) action->setChecked(false);
Chris@586 3298 updateMenuStates();
Chris@586 3299 return;
Chris@586 3300 }
Chris@492 3301 }
Chris@478 3302
Chris@477 3303 if (m_recordTarget->isRecording()) {
Chris@492 3304 stop();
Chris@477 3305 return;
Chris@477 3306 }
Chris@490 3307
Chris@483 3308 if (m_audioRecordMode == RecordReplaceSession) {
Chris@490 3309 if (!checkSaveModified()) {
Chris@490 3310 if (action) action->setChecked(false);
Chris@490 3311 return;
Chris@490 3312 }
Chris@483 3313 }
Chris@487 3314
Chris@557 3315 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
Chris@557 3316
Chris@611 3317 SVCERR << "MainWindowBase::record: about to resume" << endl;
Chris@492 3318 m_audioIO->resume();
Chris@509 3319
Chris@684 3320 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
Chris@684 3321 if (!modelPtr) {
Chris@586 3322 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
Chris@586 3323 QMessageBox::critical
Chris@586 3324 (this, tr("Recording failed"),
Chris@586 3325 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
Chris@490 3326 if (action) action->setChecked(false);
Chris@477 3327 return;
Chris@477 3328 }
Chris@477 3329
Chris@684 3330 if (!modelPtr->isOK()) {
Chris@611 3331 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
Chris@477 3332 m_recordTarget->stopRecording();
Chris@492 3333 m_audioIO->suspend();
Chris@586 3334 if (action) action->setChecked(false);
Chris@684 3335 delete modelPtr;
Chris@477 3336 return;
Chris@477 3337 }
Chris@611 3338
Chris@611 3339 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
Chris@684 3340
Chris@684 3341 QString location = modelPtr->getLocation();
Chris@487 3342
Chris@687 3343 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
Chris@483 3344
Chris@483 3345 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
Chris@478 3346
Chris@479 3347 //!!! duplication with openAudio here
Chris@479 3348
Chris@479 3349 QString templateName = getDefaultSessionTemplate();
Chris@479 3350 bool loadedTemplate = false;
Chris@479 3351
Chris@479 3352 if (templateName != "") {
Chris@479 3353 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@479 3354 if (tplStatus == FileOpenCancelled) {
Chris@611 3355 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
Chris@490 3356 m_recordTarget->stopRecording();
Chris@492 3357 m_audioIO->suspend();
Chris@684 3358 ModelById::release(modelId);
Chris@479 3359 return;
Chris@479 3360 }
Chris@479 3361 if (tplStatus != FileOpenFailed) {
Chris@479 3362 loadedTemplate = true;
Chris@479 3363 }
Chris@479 3364 }
Chris@479 3365
Chris@479 3366 if (!loadedTemplate) {
Chris@479 3367 closeSession();
Chris@479 3368 createDocument();
Chris@479 3369 }
Chris@479 3370
Chris@684 3371 ModelId prevMain = getMainModelId();
Chris@684 3372 if (!prevMain.isNone()) {
Chris@479 3373 m_playSource->removeModel(prevMain);
Chris@479 3374 }
Chris@479 3375
Chris@684 3376 m_document->setMainModel(modelId);
Chris@478 3377 setupMenus();
Chris@577 3378 findTimeRulerLayer();
Chris@478 3379
Chris@684 3380 m_originalLocation = location;
Chris@669 3381
Chris@595 3382 if (loadedTemplate || (m_sessionFile == "")) {
Chris@595 3383 CommandHistory::getInstance()->clear();
Chris@595 3384 CommandHistory::getInstance()->documentSaved();
Chris@595 3385 }
Chris@479 3386
Chris@669 3387 m_documentModified = false;
Chris@669 3388 updateWindowTitle();
Chris@669 3389
Chris@478 3390 } else {
Chris@478 3391
Chris@478 3392 CommandHistory::getInstance()->startCompoundOperation
Chris@478 3393 (tr("Import Recorded Audio"), true);
Chris@478 3394
Chris@691 3395 m_document->addNonDerivedModel(modelId);
Chris@478 3396
Chris@478 3397 AddPaneCommand *command = new AddPaneCommand(this);
Chris@478 3398 CommandHistory::getInstance()->addCommand(command);
Chris@478 3399
Chris@478 3400 Pane *pane = command->getPane();
Chris@478 3401
Chris@478 3402 if (m_timeRulerLayer) {
Chris@478 3403 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@478 3404 }
Chris@478 3405
Chris@684 3406 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@478 3407
Chris@478 3408 if (newLayer) {
Chris@478 3409 m_document->addLayerToView(pane, newLayer);
Chris@478 3410 }
Chris@595 3411
Chris@478 3412 CommandHistory::getInstance()->endCompoundOperation();
Chris@477 3413 }
Chris@479 3414
Chris@479 3415 updateMenuStates();
Chris@684 3416 m_recentFiles.addFile(location);
Chris@479 3417 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@611 3418
Chris@496 3419 emit audioFileLoaded();
Chris@477 3420 }
Chris@477 3421
Chris@477 3422 void
Chris@45 3423 MainWindowBase::ffwd()
Chris@45 3424 {
Chris@45 3425 if (!getMainModel()) return;
Chris@45 3426
Chris@708 3427 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3428 sv_frame_t frame = playbackFrame + 1;
Chris@45 3429
Chris@85 3430 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3431 Layer *layer = getSnapLayer();
Chris@435 3432 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3433
Chris@708 3434 if (!pane || !layer) {
Chris@45 3435
Chris@45 3436 frame = RealTime::realTime2Frame
Chris@357 3437 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
Chris@435 3438 if (frame > getMainModel()->getEndFrame()) {
Chris@45 3439 frame = getMainModel()->getEndFrame();
Chris@45 3440 }
Chris@45 3441
Chris@45 3442 } else {
Chris@45 3443
Chris@708 3444 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3445 int resolution = 0;
Chris@708 3446 bool success = false;
Chris@708 3447
Chris@708 3448 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3449 Layer::SnapRight, -1)) {
Chris@708 3450 if (pane->alignToReference(pframe) > playbackFrame) {
Chris@708 3451 success = true;
Chris@708 3452 break;
Chris@708 3453 } else {
Chris@708 3454 ++pframe;
Chris@708 3455 }
Chris@708 3456 }
Chris@708 3457
Chris@708 3458 if (success) {
Chris@708 3459 frame = pane->alignToReference(pframe);
Chris@85 3460 } else {
Chris@45 3461 frame = getMainModel()->getEndFrame();
Chris@45 3462 }
Chris@45 3463 }
Chris@45 3464
Chris@45 3465 if (frame < 0) frame = 0;
Chris@45 3466
Chris@45 3467 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3468 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3469 }
Chris@45 3470
Chris@45 3471 m_viewManager->setPlaybackFrame(frame);
Chris@166 3472
Chris@708 3473 if (frame >= getMainModel()->getEndFrame() &&
Chris@166 3474 m_playSource &&
Chris@166 3475 m_playSource->isPlaying() &&
Chris@166 3476 !m_viewManager->getPlayLoopMode()) {
Chris@166 3477 stop();
Chris@166 3478 }
Chris@45 3479 }
Chris@45 3480
Chris@45 3481 void
Chris@45 3482 MainWindowBase::ffwdEnd()
Chris@45 3483 {
Chris@45 3484 if (!getMainModel()) return;
Chris@45 3485
Chris@139 3486 if (m_playSource &&
Chris@139 3487 m_playSource->isPlaying() &&
Chris@139 3488 !m_viewManager->getPlayLoopMode()) {
Chris@139 3489 stop();
Chris@139 3490 }
Chris@139 3491
Chris@435 3492 sv_frame_t frame = getMainModel()->getEndFrame();
Chris@45 3493
Chris@45 3494 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3495 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3496 }
Chris@45 3497
Chris@45 3498 m_viewManager->setPlaybackFrame(frame);
Chris@45 3499 }
Chris@45 3500
Chris@45 3501 void
Chris@166 3502 MainWindowBase::ffwdSimilar()
Chris@166 3503 {
Chris@166 3504 if (!getMainModel()) return;
Chris@166 3505
Chris@166 3506 Layer *layer = getSnapLayer();
Chris@166 3507 if (!layer) { ffwd(); return; }
Chris@166 3508
Chris@166 3509 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3510 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3511
Chris@366 3512 int resolution = 0;
Chris@166 3513 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3514 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3515 frame, resolution, Layer::SnapRight)) {
Chris@166 3516 if (pane) frame = pane->alignToReference(frame);
Chris@166 3517 } else {
Chris@166 3518 frame = getMainModel()->getEndFrame();
Chris@166 3519 }
Chris@166 3520
Chris@166 3521 if (frame < 0) frame = 0;
Chris@166 3522
Chris@166 3523 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3524 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3525 }
Chris@166 3526
Chris@166 3527 m_viewManager->setPlaybackFrame(frame);
Chris@166 3528
Chris@435 3529 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3530 m_playSource &&
Chris@166 3531 m_playSource->isPlaying() &&
Chris@166 3532 !m_viewManager->getPlayLoopMode()) {
Chris@166 3533 stop();
Chris@166 3534 }
Chris@166 3535 }
Chris@166 3536
Chris@166 3537 void
Chris@45 3538 MainWindowBase::rewind()
Chris@45 3539 {
Chris@45 3540 if (!getMainModel()) return;
Chris@45 3541
Chris@708 3542 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3543 sv_frame_t frame = playbackFrame;
Chris@45 3544 if (frame > 0) --frame;
Chris@45 3545
Chris@85 3546 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3547 Layer *layer = getSnapLayer();
Chris@435 3548 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3549
Chris@45 3550 // when rewinding during playback, we want to allow a period
Chris@45 3551 // following a rewind target point at which the rewind will go to
Chris@45 3552 // the prior point instead of the immediately neighbouring one
Chris@45 3553 if (m_playSource && m_playSource->isPlaying()) {
Chris@45 3554 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@357 3555 ct = ct - RealTime::fromSeconds(0.15);
Chris@45 3556 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@45 3557 frame = RealTime::realTime2Frame(ct, sr);
Chris@45 3558 }
Chris@45 3559
Chris@708 3560 if (!pane || !layer) {
Chris@45 3561
Chris@45 3562 frame = RealTime::realTime2Frame
Chris@357 3563 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
Chris@435 3564 if (frame < getMainModel()->getStartFrame()) {
Chris@45 3565 frame = getMainModel()->getStartFrame();
Chris@45 3566 }
Chris@45 3567
Chris@45 3568 } else {
Chris@45 3569
Chris@708 3570 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3571 int resolution = 0;
Chris@708 3572 bool success = false;
Chris@708 3573
Chris@708 3574 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3575 Layer::SnapLeft, -1)) {
Chris@708 3576 if (pane->alignToReference(pframe) < playbackFrame ||
Chris@708 3577 pframe <= 0) {
Chris@708 3578 success = true;
Chris@708 3579 break;
Chris@708 3580 } else {
Chris@708 3581 --pframe;
Chris@708 3582 }
Chris@708 3583 }
Chris@708 3584
Chris@708 3585 if (success) {
Chris@708 3586 frame = pane->alignToReference(pframe);
Chris@85 3587 } else {
Chris@45 3588 frame = getMainModel()->getStartFrame();
Chris@45 3589 }
Chris@45 3590 }
Chris@45 3591
Chris@45 3592 if (frame < 0) frame = 0;
Chris@45 3593
Chris@45 3594 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3595 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3596 }
Chris@45 3597
Chris@45 3598 m_viewManager->setPlaybackFrame(frame);
Chris@45 3599 }
Chris@45 3600
Chris@45 3601 void
Chris@45 3602 MainWindowBase::rewindStart()
Chris@45 3603 {
Chris@45 3604 if (!getMainModel()) return;
Chris@45 3605
Chris@435 3606 sv_frame_t frame = getMainModel()->getStartFrame();
Chris@45 3607
Chris@45 3608 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3609 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3610 }
Chris@45 3611
Chris@45 3612 m_viewManager->setPlaybackFrame(frame);
Chris@45 3613 }
Chris@45 3614
Chris@166 3615 void
Chris@166 3616 MainWindowBase::rewindSimilar()
Chris@166 3617 {
Chris@166 3618 if (!getMainModel()) return;
Chris@166 3619
Chris@166 3620 Layer *layer = getSnapLayer();
Chris@166 3621 if (!layer) { rewind(); return; }
Chris@166 3622
Chris@166 3623 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3624 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3625
Chris@366 3626 int resolution = 0;
Chris@166 3627 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3628 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3629 frame, resolution, Layer::SnapLeft)) {
Chris@166 3630 if (pane) frame = pane->alignToReference(frame);
Chris@166 3631 } else {
Chris@166 3632 frame = getMainModel()->getStartFrame();
Chris@166 3633 }
Chris@166 3634
Chris@166 3635 if (frame < 0) frame = 0;
Chris@166 3636
Chris@166 3637 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3638 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3639 }
Chris@166 3640
Chris@166 3641 m_viewManager->setPlaybackFrame(frame);
Chris@166 3642 }
Chris@166 3643
Chris@45 3644 Layer *
Chris@45 3645 MainWindowBase::getSnapLayer() const
Chris@45 3646 {
Chris@45 3647 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 3648 if (!pane) return nullptr;
Chris@45 3649
Chris@45 3650 Layer *layer = pane->getSelectedLayer();
Chris@45 3651
Chris@45 3652 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@45 3653 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@194 3654 !dynamic_cast<RegionLayer *>(layer) &&
Chris@45 3655 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@45 3656
Chris@636 3657 layer = nullptr;
Chris@45 3658
Chris@45 3659 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 3660 Layer *l = pane->getLayer(i-1);
Chris@45 3661 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@45 3662 layer = l;
Chris@45 3663 break;
Chris@45 3664 }
Chris@45 3665 }
Chris@45 3666 }
Chris@45 3667
Chris@45 3668 return layer;
Chris@45 3669 }
Chris@45 3670
Chris@45 3671 void
Chris@45 3672 MainWindowBase::stop()
Chris@45 3673 {
Chris@516 3674 if (m_recordTarget &&
Chris@516 3675 m_recordTarget->isRecording()) {
Chris@477 3676 m_recordTarget->stopRecording();
Chris@477 3677 }
Chris@516 3678
Chris@516 3679 if (!m_playSource) return;
Chris@516 3680
Chris@45 3681 m_playSource->stop();
Chris@45 3682
Chris@611 3683 SVCERR << "MainWindowBase::stop: suspending" << endl;
Chris@611 3684
Chris@487 3685 if (m_audioIO) m_audioIO->suspend();
Chris@509 3686 else if (m_playTarget) m_playTarget->suspend();
Chris@487 3687
Chris@45 3688 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@45 3689 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@45 3690 } else {
Chris@45 3691 m_myStatusMessage = "";
Chris@378 3692 getStatusLabel()->setText("");
Chris@45 3693 }
Chris@45 3694 }
Chris@45 3695
Chris@45 3696 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
Chris@45 3697 m_mw(mw),
Chris@636 3698 m_pane(nullptr),
Chris@636 3699 m_prevCurrentPane(nullptr),
Chris@45 3700 m_added(false)
Chris@45 3701 {
Chris@45 3702 }
Chris@45 3703
Chris@45 3704 MainWindowBase::AddPaneCommand::~AddPaneCommand()
Chris@45 3705 {
Chris@45 3706 if (m_pane && !m_added) {
Chris@595 3707 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3708 }
Chris@45 3709 }
Chris@45 3710
Chris@45 3711 QString
Chris@45 3712 MainWindowBase::AddPaneCommand::getName() const
Chris@45 3713 {
Chris@45 3714 return tr("Add Pane");
Chris@45 3715 }
Chris@45 3716
Chris@45 3717 void
Chris@45 3718 MainWindowBase::AddPaneCommand::execute()
Chris@45 3719 {
Chris@45 3720 if (!m_pane) {
Chris@595 3721 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@595 3722 m_pane = m_mw->m_paneStack->addPane();
Chris@45 3723
Chris@45 3724 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 3725 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@45 3726 } else {
Chris@595 3727 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3728 }
Chris@45 3729
Chris@45 3730 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@45 3731 m_added = true;
Chris@45 3732 }
Chris@45 3733
Chris@45 3734 void
Chris@45 3735 MainWindowBase::AddPaneCommand::unexecute()
Chris@45 3736 {
Chris@45 3737 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3738 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3739 m_added = false;
Chris@45 3740 }
Chris@45 3741
Chris@45 3742 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
Chris@45 3743 m_mw(mw),
Chris@45 3744 m_pane(pane),
Chris@636 3745 m_prevCurrentPane(nullptr),
Chris@45 3746 m_added(true)
Chris@45 3747 {
Chris@45 3748 }
Chris@45 3749
Chris@45 3750 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
Chris@45 3751 {
Chris@45 3752 if (m_pane && !m_added) {
Chris@595 3753 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3754 }
Chris@45 3755 }
Chris@45 3756
Chris@45 3757 QString
Chris@45 3758 MainWindowBase::RemovePaneCommand::getName() const
Chris@45 3759 {
Chris@45 3760 return tr("Remove Pane");
Chris@45 3761 }
Chris@45 3762
Chris@45 3763 void
Chris@45 3764 MainWindowBase::RemovePaneCommand::execute()
Chris@45 3765 {
Chris@45 3766 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@45 3767 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3768 m_added = false;
Chris@45 3769 }
Chris@45 3770
Chris@45 3771 void
Chris@45 3772 MainWindowBase::RemovePaneCommand::unexecute()
Chris@45 3773 {
Chris@45 3774 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3775 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3776 m_added = true;
Chris@45 3777 }
Chris@45 3778
Chris@45 3779 void
Chris@45 3780 MainWindowBase::deleteCurrentPane()
Chris@45 3781 {
Chris@45 3782 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3783 (tr("Delete Pane"), true);
Chris@45 3784
Chris@45 3785 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3786 if (pane) {
Chris@595 3787 while (pane->getLayerCount() > 0) {
Chris@595 3788 Layer *layer = pane->getLayer(0);
Chris@595 3789 if (layer) {
Chris@595 3790 m_document->removeLayerFromView(pane, layer);
Chris@595 3791 } else {
Chris@595 3792 break;
Chris@595 3793 }
Chris@595 3794 }
Chris@595 3795
Chris@595 3796 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@595 3797 CommandHistory::getInstance()->addCommand(command);
Chris@45 3798 }
Chris@45 3799
Chris@45 3800 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 3801
Chris@45 3802 updateMenuStates();
Chris@45 3803 }
Chris@45 3804
Chris@45 3805 void
Chris@45 3806 MainWindowBase::deleteCurrentLayer()
Chris@45 3807 {
Chris@45 3808 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3809 if (pane) {
Chris@595 3810 Layer *layer = pane->getSelectedLayer();
Chris@595 3811 if (layer) {
Chris@595 3812 m_document->removeLayerFromView(pane, layer);
Chris@595 3813 }
Chris@45 3814 }
Chris@45 3815 updateMenuStates();
Chris@45 3816 }
Chris@45 3817
Chris@45 3818 void
Chris@123 3819 MainWindowBase::editCurrentLayer()
Chris@123 3820 {
Chris@636 3821 Layer *layer = nullptr;
Chris@123 3822 Pane *pane = m_paneStack->getCurrentPane();
Chris@123 3823 if (pane) layer = pane->getSelectedLayer();
Chris@123 3824 if (!layer) return;
Chris@123 3825
Chris@684 3826 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
Chris@124 3827 if (!tabular) {
Chris@124 3828 //!!! how to prevent this function from being active if not
Chris@124 3829 //appropriate model type? or will we ultimately support
Chris@124 3830 //tabular display for all editable models?
Chris@233 3831 SVDEBUG << "NOTE: Not a tabular model" << endl;
Chris@124 3832 return;
Chris@124 3833 }
Chris@124 3834
Chris@123 3835 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@126 3836 if (!m_layerDataDialogMap[layer].isNull()) {
Chris@126 3837 m_layerDataDialogMap[layer]->show();
Chris@126 3838 m_layerDataDialogMap[layer]->raise();
Chris@126 3839 return;
Chris@126 3840 }
Chris@123 3841 }
Chris@123 3842
Chris@125 3843 QString title = layer->getLayerPresentationName();
Chris@125 3844
Chris@684 3845 ModelDataTableDialog *dialog = new ModelDataTableDialog
Chris@684 3846 (layer->getModel(), title, this);
Chris@128 3847 dialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@128 3848
Chris@128 3849 connectLayerEditDialog(dialog);
Chris@123 3850
Chris@128 3851 m_layerDataDialogMap[layer] = dialog;
Chris@128 3852 m_viewDataDialogMap[pane].insert(dialog);
Chris@128 3853
Chris@128 3854 dialog->show();
Chris@128 3855 }
Chris@128 3856
Chris@128 3857 void
Chris@128 3858 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@128 3859 {
Chris@123 3860 connect(m_viewManager,
Chris@435 3861 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@123 3862 dialog,
Chris@435 3863 SLOT(userScrolledToFrame(sv_frame_t)));
Chris@127 3864
Chris@127 3865 connect(m_viewManager,
Chris@435 3866 SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@127 3867 dialog,
Chris@435 3868 SLOT(playbackScrolledToFrame(sv_frame_t)));
Chris@127 3869
Chris@123 3870 connect(dialog,
Chris@435 3871 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@123 3872 m_viewManager,
Chris@435 3873 SLOT(setGlobalCentreFrame(sv_frame_t)));
Chris@129 3874
Chris@129 3875 connect(dialog,
Chris@435 3876 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@129 3877 m_viewManager,
Chris@435 3878 SLOT(setPlaybackFrame(sv_frame_t)));
Chris@128 3879 }
Chris@123 3880
Chris@123 3881 void
Chris@73 3882 MainWindowBase::previousPane()
Chris@73 3883 {
Chris@73 3884 if (!m_paneStack) return;
Chris@73 3885
Chris@73 3886 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3887 if (!currentPane) return;
Chris@73 3888
Chris@73 3889 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3890 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3891 if (i == 0) return;
Chris@73 3892 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
Chris@73 3893 updateMenuStates();
Chris@73 3894 return;
Chris@73 3895 }
Chris@73 3896 }
Chris@73 3897 }
Chris@73 3898
Chris@73 3899 void
Chris@73 3900 MainWindowBase::nextPane()
Chris@73 3901 {
Chris@73 3902 if (!m_paneStack) return;
Chris@73 3903
Chris@73 3904 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3905 if (!currentPane) return;
Chris@73 3906
Chris@73 3907 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3908 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3909 if (i == m_paneStack->getPaneCount()-1) return;
Chris@73 3910 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
Chris@73 3911 updateMenuStates();
Chris@73 3912 return;
Chris@73 3913 }
Chris@73 3914 }
Chris@73 3915 }
Chris@73 3916
Chris@73 3917 void
Chris@73 3918 MainWindowBase::previousLayer()
Chris@73 3919 {
Chris@73 3920 if (!m_paneStack) return;
Chris@73 3921
Chris@73 3922 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3923 if (!currentPane) return;
Chris@73 3924
Chris@403 3925 int count = currentPane->getLayerCount();
Chris@403 3926 if (count == 0) return;
Chris@403 3927
Chris@73 3928 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3929
Chris@403 3930 if (!currentLayer) {
Chris@403 3931 // The pane itself is current
Chris@403 3932 m_paneStack->setCurrentLayer
Chris@403 3933 (currentPane, currentPane->getFixedOrderLayer(count-1));
Chris@403 3934 } else {
Chris@403 3935 for (int i = 0; i < count; ++i) {
Chris@403 3936 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3937 if (i == 0) {
Chris@403 3938 m_paneStack->setCurrentLayer
Chris@636 3939 (currentPane, nullptr); // pane
Chris@403 3940 } else {
Chris@403 3941 m_paneStack->setCurrentLayer
Chris@403 3942 (currentPane, currentPane->getFixedOrderLayer(i-1));
Chris@403 3943 }
Chris@403 3944 break;
Chris@403 3945 }
Chris@73 3946 }
Chris@73 3947 }
Chris@403 3948
Chris@403 3949 updateMenuStates();
Chris@73 3950 }
Chris@73 3951
Chris@73 3952 void
Chris@73 3953 MainWindowBase::nextLayer()
Chris@73 3954 {
Chris@73 3955 if (!m_paneStack) return;
Chris@73 3956
Chris@73 3957 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3958 if (!currentPane) return;
Chris@73 3959
Chris@403 3960 int count = currentPane->getLayerCount();
Chris@403 3961 if (count == 0) return;
Chris@403 3962
Chris@73 3963 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3964
Chris@403 3965 if (!currentLayer) {
Chris@403 3966 // The pane itself is current
Chris@403 3967 m_paneStack->setCurrentLayer
Chris@403 3968 (currentPane, currentPane->getFixedOrderLayer(0));
Chris@403 3969 } else {
Chris@403 3970 for (int i = 0; i < count; ++i) {
Chris@403 3971 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3972 if (i == currentPane->getLayerCount()-1) {
Chris@403 3973 m_paneStack->setCurrentLayer
Chris@636 3974 (currentPane, nullptr); // pane
Chris@403 3975 } else {
Chris@403 3976 m_paneStack->setCurrentLayer
Chris@403 3977 (currentPane, currentPane->getFixedOrderLayer(i+1));
Chris@403 3978 }
Chris@403 3979 break;
Chris@403 3980 }
Chris@73 3981 }
Chris@73 3982 }
Chris@403 3983
Chris@403 3984 updateMenuStates();
Chris@73 3985 }
Chris@73 3986
Chris@73 3987 void
Chris@435 3988 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
Chris@45 3989 {
Chris@45 3990 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@45 3991
Chris@187 3992 updatePositionStatusDisplays();
Chris@187 3993
Chris@45 3994 RealTime now = RealTime::frame2RealTime
Chris@45 3995 (frame, getMainModel()->getSampleRate());
Chris@45 3996
Chris@45 3997 if (now.sec == m_lastPlayStatusSec) return;
Chris@45 3998
Chris@45 3999 RealTime then = RealTime::frame2RealTime
Chris@45 4000 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@45 4001
Chris@45 4002 QString nowStr;
Chris@45 4003 QString thenStr;
Chris@45 4004 QString remainingStr;
Chris@45 4005
Chris@45 4006 if (then.sec > 10) {
Chris@45 4007 nowStr = now.toSecText().c_str();
Chris@45 4008 thenStr = then.toSecText().c_str();
Chris@45 4009 remainingStr = (then - now).toSecText().c_str();
Chris@45 4010 m_lastPlayStatusSec = now.sec;
Chris@45 4011 } else {
Chris@45 4012 nowStr = now.toText(true).c_str();
Chris@45 4013 thenStr = then.toText(true).c_str();
Chris@45 4014 remainingStr = (then - now).toText(true).c_str();
Chris@45 4015 }
Chris@45 4016
Chris@45 4017 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@45 4018 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@45 4019
Chris@378 4020 getStatusLabel()->setText(m_myStatusMessage);
Chris@45 4021 }
Chris@45 4022
Chris@45 4023 void
Chris@486 4024 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
Chris@486 4025 {
Chris@486 4026 RealTime duration = RealTime::frame2RealTime(frame, rate);
Chris@486 4027 QString durStr = duration.toSecText().c_str();
Chris@486 4028
Chris@486 4029 m_myStatusMessage = tr("Recording: %1").arg(durStr);
Chris@486 4030
Chris@486 4031 getStatusLabel()->setText(m_myStatusMessage);
Chris@486 4032 }
Chris@486 4033
Chris@486 4034 void
Chris@435 4035 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
Chris@45 4036 {
Chris@45 4037 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4038 Pane *p = nullptr;
Chris@45 4039 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4040 if (!p->getFollowGlobalPan()) return;
Chris@45 4041 updateVisibleRangeDisplay(p);
Chris@45 4042 }
Chris@45 4043
Chris@45 4044 void
Chris@435 4045 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
Chris@45 4046 {
Chris@233 4047 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
Chris@123 4048
Chris@123 4049 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
Chris@123 4050 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
Chris@123 4051 i != m_viewDataDialogMap[v].end(); ++i) {
Chris@127 4052 (*i)->userScrolledToFrame(frame);
Chris@123 4053 }
Chris@123 4054 }
Chris@45 4055 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4056 Pane *p = nullptr;
Chris@45 4057 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4058 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4059 }
Chris@45 4060
Chris@45 4061 void
Chris@624 4062 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
Chris@45 4063 {
Chris@45 4064 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4065 Pane *p = nullptr;
Chris@45 4066 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4067 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4068 }
Chris@45 4069
Chris@45 4070 void
Chris@45 4071 MainWindowBase::layerAdded(Layer *)
Chris@45 4072 {
Chris@233 4073 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
Chris@45 4074 updateMenuStates();
Chris@45 4075 }
Chris@45 4076
Chris@45 4077 void
Chris@45 4078 MainWindowBase::layerRemoved(Layer *)
Chris@45 4079 {
Chris@233 4080 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
Chris@45 4081 updateMenuStates();
Chris@45 4082 }
Chris@45 4083
Chris@45 4084 void
Chris@45 4085 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
Chris@45 4086 {
Chris@233 4087 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
Chris@123 4088
Chris@128 4089 removeLayerEditDialog(layer);
Chris@123 4090
Chris@47 4091 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
Chris@595 4092 // cerr << "(this is the time ruler layer)" << endl;
Chris@636 4093 m_timeRulerLayer = nullptr;
Chris@45 4094 }
Chris@45 4095 }
Chris@45 4096
Chris@45 4097 void
Chris@45 4098 MainWindowBase::layerInAView(Layer *layer, bool inAView)
Chris@45 4099 {
Chris@233 4100 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
Chris@128 4101
Chris@128 4102 if (!inAView) removeLayerEditDialog(layer);
Chris@45 4103
Chris@45 4104 // Check whether we need to add or remove model from play source
Chris@684 4105 ModelId modelId = layer->getModel();
Chris@684 4106 if (!modelId.isNone()) {
Chris@45 4107 if (inAView) {
Chris@684 4108 m_playSource->addModel(modelId);
Chris@45 4109 } else {
Chris@45 4110 bool found = false;
Chris@45 4111 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 4112 Pane *pane = m_paneStack->getPane(i);
Chris@45 4113 if (!pane) continue;
Chris@45 4114 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@45 4115 Layer *pl = pane->getLayer(j);
Chris@183 4116 if (pl &&
Chris@183 4117 !dynamic_cast<TimeRulerLayer *>(pl) &&
Chris@684 4118 (pl->getModel() == modelId)) {
Chris@45 4119 found = true;
Chris@45 4120 break;
Chris@45 4121 }
Chris@45 4122 }
Chris@45 4123 if (found) break;
Chris@45 4124 }
Chris@173 4125 if (!found) {
Chris@684 4126 m_playSource->removeModel(modelId);
Chris@173 4127 }
Chris@45 4128 }
Chris@45 4129 }
Chris@45 4130
Chris@45 4131 updateMenuStates();
Chris@45 4132 }
Chris@45 4133
Chris@45 4134 void
Chris@128 4135 MainWindowBase::removeLayerEditDialog(Layer *layer)
Chris@128 4136 {
Chris@128 4137 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@128 4138
Chris@128 4139 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
Chris@128 4140
Chris@128 4141 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
Chris@128 4142 vi != m_viewDataDialogMap.end(); ++vi) {
Chris@128 4143 vi->second.erase(dialog);
Chris@128 4144 }
Chris@128 4145
Chris@128 4146 m_layerDataDialogMap.erase(layer);
Chris@128 4147 delete dialog;
Chris@128 4148 }
Chris@128 4149 }
Chris@128 4150
Chris@128 4151 void
Chris@684 4152 MainWindowBase::modelAdded(ModelId model)
Chris@45 4153 {
Chris@233 4154 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
Chris@684 4155 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
Chris@45 4156 m_playSource->addModel(model);
Chris@45 4157 }
Chris@45 4158
Chris@45 4159 void
Chris@684 4160 MainWindowBase::mainModelChanged(ModelId modelId)
Chris@45 4161 {
Chris@233 4162 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
Chris@45 4163 updateDescriptionLabel();
Chris@684 4164 auto model = ModelById::getAs<WaveFileModel>(modelId);
Chris@45 4165 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@714 4166 if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
Chris@475 4167 createAudioIO();
Chris@360 4168 }
Chris@45 4169 }
Chris@45 4170
Chris@45 4171 void
Chris@55 4172 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
Chris@55 4173 {
Chris@55 4174 bool found = false;
Chris@55 4175 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@55 4176 if (m_paneStack->getPane(i) == pane) {
Chris@55 4177 found = true;
Chris@55 4178 break;
Chris@55 4179 }
Chris@55 4180 }
Chris@55 4181 if (!found) {
Chris@233 4182 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
Chris@229 4183 << pane << endl;
Chris@55 4184 return;
Chris@55 4185 }
Chris@55 4186
Chris@55 4187 CommandHistory::getInstance()->startCompoundOperation
Chris@595 4188 (tr("Delete Pane"), true);
Chris@55 4189
Chris@55 4190 while (pane->getLayerCount() > 0) {
Chris@637 4191 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@55 4192 if (layer) {
Chris@55 4193 m_document->removeLayerFromView(pane, layer);
Chris@55 4194 } else {
Chris@55 4195 break;
Chris@55 4196 }
Chris@55 4197 }
Chris@55 4198
Chris@55 4199 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@55 4200 CommandHistory::getInstance()->addCommand(command);
Chris@55 4201
Chris@55 4202 CommandHistory::getInstance()->endCompoundOperation();
Chris@55 4203
Chris@55 4204 updateMenuStates();
Chris@55 4205 }
Chris@55 4206
Chris@55 4207 void
Chris@684 4208 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
Chris@429 4209 {
Chris@684 4210 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
Chris@429 4211 }
Chris@429 4212
Chris@429 4213 void
Chris@45 4214 MainWindowBase::pollOSC()
Chris@45 4215 {
Chris@45 4216 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@725 4217
Chris@725 4218 while (!m_oscQueue->isEmpty()) {
Chris@725 4219
Chris@725 4220 if (m_openingAudioFile) {
Chris@725 4221 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4222 << "waiting for audio to finish loading"
Chris@725 4223 << endl;
Chris@725 4224 return;
Chris@725 4225 }
Chris@725 4226
Chris@725 4227 if (ModelTransformerFactory::getInstance()->haveRunningTransformers()) {
Chris@725 4228 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4229 << "waiting for running transforms to complete"
Chris@725 4230 << endl;
Chris@725 4231 return;
Chris@725 4232 }
Chris@725 4233
Chris@725 4234 SVDEBUG << "MainWindowBase::pollOSC: have "
Chris@725 4235 << m_oscQueue->getMessagesAvailable()
Chris@725 4236 << " messages" << endl;
Chris@725 4237
Chris@725 4238 OSCMessage message = m_oscQueue->readMessage();
Chris@725 4239
Chris@725 4240 if (message.getTarget() != 0) {
Chris@725 4241 SVCERR << "MainWindowBase::pollOSC: ignoring message with target "
Chris@725 4242 << message.getTarget() << " (we are target 0)" << endl;
Chris@725 4243 continue;
Chris@725 4244 }
Chris@725 4245
Chris@725 4246 handleOSCMessage(message);
Chris@725 4247
Chris@725 4248 disconnect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@725 4249 this, SLOT(pollOSC()));
Chris@725 4250
Chris@725 4251 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
Chris@725 4252 QEventLoop::ExcludeSocketNotifiers);
Chris@725 4253
Chris@725 4254 connect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@725 4255 this, SLOT(pollOSC()));
Chris@45 4256 }
Chris@45 4257 }
Chris@45 4258
Chris@45 4259 void
Chris@45 4260 MainWindowBase::inProgressSelectionChanged()
Chris@45 4261 {
Chris@636 4262 Pane *currentPane = nullptr;
Chris@45 4263 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
justin@331 4264 if (currentPane) {
justin@331 4265 //cerr << "JTEST: mouse event on selection pane" << endl;
justin@331 4266 updateVisibleRangeDisplay(currentPane);
justin@331 4267 }
Chris@45 4268 }
Chris@45 4269
Chris@45 4270 void
Chris@45 4271 MainWindowBase::contextHelpChanged(const QString &s)
Chris@45 4272 {
Chris@378 4273 QLabel *lab = getStatusLabel();
Chris@375 4274
Chris@45 4275 if (s == "" && m_myStatusMessage != "") {
Chris@378 4276 if (lab->text() != m_myStatusMessage) {
Chris@378 4277 lab->setText(m_myStatusMessage);
Chris@375 4278 }
Chris@45 4279 return;
Chris@45 4280 }
Chris@375 4281
Chris@378 4282 lab->setText(s);
Chris@45 4283 }
Chris@45 4284
Chris@45 4285 void
Chris@45 4286 MainWindowBase::openHelpUrl(QString url)
Chris@45 4287 {
Chris@45 4288 // This method mostly lifted from Qt Assistant source code
Chris@45 4289
Chris@45 4290 QProcess *process = new QProcess(this);
Chris@45 4291 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@45 4292
Chris@45 4293 QStringList args;
Chris@45 4294
Chris@45 4295 #ifdef Q_OS_MAC
Chris@45 4296 args.append(url);
Chris@45 4297 process->start("open", args);
Chris@45 4298 #else
Chris@45 4299 #ifdef Q_OS_WIN32
Chris@599 4300 std::string pfiles;
Chris@599 4301 (void)getEnvUtf8("ProgramFiles", pfiles);
Chris@599 4302 QString command =
Chris@599 4303 QString::fromStdString(pfiles) +
Chris@599 4304 QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@358 4305
Chris@358 4306 args.append(url);
Chris@358 4307 process->start(command, args);
Chris@45 4308 #else
Chris@45 4309 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@45 4310 args.append("exec");
Chris@45 4311 args.append(url);
Chris@45 4312 process->start("kfmclient", args);
Chris@45 4313 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@45 4314 args.append(url);
Chris@45 4315 process->start(qgetenv("BROWSER"), args);
Chris@45 4316 } else {
Chris@45 4317 args.append(url);
Chris@45 4318 process->start("firefox", args);
Chris@45 4319 }
Chris@45 4320 #endif
Chris@45 4321 #endif
Chris@45 4322 }
Chris@45 4323
Chris@483 4324 void
Chris@483 4325 MainWindowBase::openLocalFolder(QString path)
Chris@483 4326 {
Chris@483 4327 QDir d(path);
Chris@483 4328 if (d.exists()) {
Chris@483 4329 QStringList args;
Chris@483 4330 QString path = d.canonicalPath();
Chris@483 4331 #if defined Q_OS_WIN32
Chris@483 4332 // Although the Win32 API is quite happy to have
Chris@483 4333 // forward slashes as directory separators, Windows
Chris@483 4334 // Explorer is not
Chris@483 4335 path = path.replace('/', '\\');
Chris@483 4336 args << path;
Chris@483 4337 QProcess::execute("c:/windows/explorer.exe", args);
Chris@483 4338 #else
Chris@483 4339 args << path;
Chris@605 4340 QProcess process;
Chris@605 4341 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@606 4342 env.insert("LD_LIBRARY_PATH", "");
Chris@605 4343 process.setProcessEnvironment(env);
Chris@605 4344 process.start(
Chris@483 4345 #if defined Q_OS_MAC
Chris@483 4346 "/usr/bin/open",
Chris@483 4347 #else
Chris@483 4348 "/usr/bin/xdg-open",
Chris@483 4349 #endif
Chris@483 4350 args);
Chris@608 4351 process.waitForFinished();
Chris@483 4352 #endif
Chris@483 4353 }
Chris@483 4354 }
Chris@483 4355