annotate framework/MainWindowBase.cpp @ 760:3a63f1f61bd6

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