annotate framework/MainWindowBase.cpp @ 727:e2ad6fe768b0 spectrogram-export

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