annotate framework/MainWindowBase.cpp @ 734:de7969894402 background-mode

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