annotate framework/MainWindowBase.cpp @ 724:cd1e8c731095 spectrogram-export

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