annotate framework/MainWindowBase.cpp @ 773:699b5b130ea2 pitch-align

Fixes to aligner destruction sequence when re-aligning during alignment; hide debug output behind a flag
author Chris Cannam
date Mon, 01 Jun 2020 17:13:09 +0100
parents 6429a164b7e1
children da57ab54f0e8
rev   line source
Chris@45 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@45 2
Chris@45 3 /*
Chris@45 4 Sonic Visualiser
Chris@45 5 An audio file viewer and annotation editor.
Chris@45 6 Centre for Digital Music, Queen Mary, University of London.
Chris@45 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@45 8
Chris@45 9 This program is free software; you can redistribute it and/or
Chris@45 10 modify it under the terms of the GNU General Public License as
Chris@45 11 published by the Free Software Foundation; either version 2 of the
Chris@45 12 License, or (at your option) any later version. See the file
Chris@45 13 COPYING included with this distribution for more information.
Chris@45 14 */
Chris@45 15
Chris@45 16 #include "MainWindowBase.h"
Chris@46 17 #include "Document.h"
Chris@45 18
Chris@45 19 #include "view/Pane.h"
Chris@45 20 #include "view/PaneStack.h"
Chris@479 21 #include "data/model/ReadOnlyWaveFileModel.h"
Chris@477 22 #include "data/model/WritableWaveFileModel.h"
Chris@45 23 #include "data/model/SparseOneDimensionalModel.h"
Chris@45 24 #include "data/model/NoteModel.h"
Chris@45 25 #include "data/model/Labeller.h"
Chris@124 26 #include "data/model/TabularModel.h"
Chris@45 27 #include "view/ViewManager.h"
Chris@45 28
Chris@45 29 #include "layer/WaveformLayer.h"
Chris@45 30 #include "layer/TimeRulerLayer.h"
Chris@45 31 #include "layer/TimeInstantLayer.h"
Chris@45 32 #include "layer/TimeValueLayer.h"
Chris@45 33 #include "layer/Colour3DPlotLayer.h"
Chris@45 34 #include "layer/SliceLayer.h"
Chris@45 35 #include "layer/SliceableLayer.h"
Chris@45 36 #include "layer/ImageLayer.h"
Chris@184 37 #include "layer/NoteLayer.h"
matthiasm@267 38 #include "layer/FlexiNoteLayer.h"
Chris@184 39 #include "layer/RegionLayer.h"
Chris@722 40 #include "layer/SpectrogramLayer.h"
Chris@45 41
Chris@45 42 #include "widgets/ListInputDialog.h"
Chris@105 43 #include "widgets/CommandHistory.h"
Chris@109 44 #include "widgets/ProgressDialog.h"
Chris@109 45 #include "widgets/MIDIFileImportDialog.h"
Chris@109 46 #include "widgets/CSVFormatDialog.h"
Chris@123 47 #include "widgets/ModelDataTableDialog.h"
Chris@341 48 #include "widgets/InteractiveFileFinder.h"
Chris@45 49
Chris@468 50 #include "audio/AudioCallbackPlaySource.h"
Chris@574 51 #include "audio/AudioCallbackRecordTarget.h"
Chris@468 52 #include "audio/PlaySpeedRangeMapper.h"
Chris@475 53
Chris@45 54 #include "data/fileio/DataFileReaderFactory.h"
Chris@45 55 #include "data/fileio/PlaylistFileReader.h"
Chris@45 56 #include "data/fileio/WavFileWriter.h"
Chris@45 57 #include "data/fileio/MIDIFileWriter.h"
Chris@659 58 #include "data/fileio/CSVFileWriter.h"
Chris@45 59 #include "data/fileio/BZipFileDevice.h"
Chris@45 60 #include "data/fileio/FileSource.h"
Chris@152 61 #include "data/fileio/AudioFileReaderFactory.h"
Chris@134 62 #include "rdf/RDFImporter.h"
Chris@659 63 #include "rdf/RDFExporter.h"
Chris@45 64
Chris@724 65 #include "transform/ModelTransformerFactory.h"
Chris@724 66
Chris@45 67 #include "base/RecentFiles.h"
Chris@45 68
Chris@45 69 #include "base/XmlExportable.h"
Chris@45 70 #include "base/Profiler.h"
Chris@45 71 #include "base/Preferences.h"
Chris@217 72 #include "base/TempWriteFile.h"
Chris@217 73 #include "base/Exceptions.h"
Chris@223 74 #include "base/ResourceFinder.h"
Chris@45 75
Chris@45 76 #include "data/osc/OSCQueue.h"
Chris@157 77 #include "data/midi/MIDIInput.h"
Chris@654 78 #include "OSCScript.h"
Chris@45 79
Chris@599 80 #include "system/System.h"
Chris@599 81
Chris@468 82 #include <bqaudioio/SystemPlaybackTarget.h>
Chris@475 83 #include <bqaudioio/SystemAudioIO.h>
Chris@468 84 #include <bqaudioio/AudioFactory.h>
Chris@468 85
Chris@45 86 #include <QApplication>
Chris@45 87 #include <QMessageBox>
Chris@45 88 #include <QGridLayout>
Chris@45 89 #include <QLabel>
Chris@45 90 #include <QAction>
Chris@45 91 #include <QMenuBar>
Chris@45 92 #include <QToolBar>
Chris@45 93 #include <QInputDialog>
Chris@45 94 #include <QStatusBar>
Chris@45 95 #include <QTreeView>
Chris@45 96 #include <QFile>
Chris@45 97 #include <QFileInfo>
Chris@45 98 #include <QDir>
Chris@45 99 #include <QTextStream>
Chris@432 100 #include <QTextCodec>
Chris@45 101 #include <QProcess>
Chris@45 102 #include <QShortcut>
Chris@45 103 #include <QSettings>
Chris@45 104 #include <QDateTime>
Chris@45 105 #include <QProcess>
Chris@45 106 #include <QCheckBox>
Chris@45 107 #include <QRegExp>
Chris@45 108 #include <QScrollArea>
Chris@684 109 #include <QScreen>
Chris@354 110 #include <QSignalMapper>
Chris@45 111
Chris@45 112 #include <iostream>
Chris@45 113 #include <cstdio>
Chris@45 114 #include <errno.h>
Chris@45 115
Chris@45 116 using std::vector;
Chris@45 117 using std::map;
Chris@45 118 using std::set;
Chris@45 119
Chris@255 120 #ifdef Q_WS_X11
Chris@255 121 #define Window X11Window
Chris@255 122 #include <X11/Xlib.h>
Chris@255 123 #include <X11/Xutil.h>
Chris@255 124 #include <X11/Xatom.h>
Chris@255 125 #include <X11/SM/SMlib.h>
Chris@255 126
Chris@255 127 static int handle_x11_error(Display *dpy, XErrorEvent *err)
Chris@255 128 {
Chris@255 129 char errstr[256];
Chris@255 130 XGetErrorText(dpy, err->error_code, errstr, 256);
Chris@255 131 if (err->error_code != BadWindow) {
Chris@595 132 cerr << "Sonic Visualiser: X Error: "
Chris@595 133 << errstr << " " << int(err->error_code)
Chris@595 134 << "\nin major opcode: "
Chris@595 135 << int(err->request_code) << endl;
Chris@255 136 }
Chris@255 137 return 0;
Chris@255 138 }
Chris@255 139 #undef Window
Chris@255 140 #endif
Chris@45 141
Chris@714 142 MainWindowBase::MainWindowBase(AudioMode audioMode,
Chris@714 143 MIDIMode midiMode,
Chris@712 144 PaneStack::Options paneStackOptions) :
Chris@636 145 m_document(nullptr),
Chris@636 146 m_paneStack(nullptr),
Chris@636 147 m_viewManager(nullptr),
Chris@636 148 m_timeRulerLayer(nullptr),
Chris@714 149 m_audioMode(audioMode),
Chris@714 150 m_midiMode(midiMode),
Chris@636 151 m_playSource(nullptr),
Chris@636 152 m_recordTarget(nullptr),
Chris@636 153 m_playTarget(nullptr),
Chris@636 154 m_audioIO(nullptr),
Chris@636 155 m_oscQueue(nullptr),
Chris@636 156 m_oscQueueStarter(nullptr),
Chris@654 157 m_oscScript(nullptr),
Chris@636 158 m_midiInput(nullptr),
Chris@45 159 m_recentFiles("RecentFiles", 20),
Chris@54 160 m_recentTransforms("RecentTransforms", 20),
Chris@45 161 m_documentModified(false),
Chris@45 162 m_openingAudioFile(false),
Chris@636 163 m_labeller(nullptr),
Chris@357 164 m_lastPlayStatusSec(0),
Chris@357 165 m_initialDarkBackground(false),
Chris@378 166 m_defaultFfwdRwdStep(2, 0),
Chris@483 167 m_audioRecordMode(RecordCreateAdditionalModel),
Chris@636 168 m_statusLabel(nullptr),
Chris@426 169 m_iconsVisibleInMenus(true),
Chris@636 170 m_menuShortcutMapper(nullptr)
Chris@45 171 {
Chris@113 172 Profiler profiler("MainWindowBase::MainWindowBase");
Chris@113 173
Chris@591 174 SVDEBUG << "MainWindowBase::MainWindowBase" << endl;
Chris@591 175
Chris@438 176 qRegisterMetaType<sv_frame_t>("sv_frame_t");
Chris@438 177 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
Chris@687 178 qRegisterMetaType<ModelId>("ModelId");
Chris@438 179
Chris@255 180 #ifdef Q_WS_X11
Chris@255 181 XSetErrorHandler(handle_x11_error);
Chris@255 182 #endif
Chris@255 183
Chris@452 184 connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash()));
Chris@452 185
Chris@45 186 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
Chris@595 187 this, SLOT(documentModified()));
Chris@45 188 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
Chris@595 189 this, SLOT(documentRestored()));
Chris@45 190
Chris@591 191 SVDEBUG << "MainWindowBase: Creating view manager" << endl;
Chris@591 192
Chris@45 193 m_viewManager = new ViewManager();
Chris@45 194 connect(m_viewManager, SIGNAL(selectionChanged()),
Chris@595 195 this, SLOT(updateMenuStates()));
Chris@45 196 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
Chris@595 197 this, SLOT(inProgressSelectionChanged()));
Chris@45 198
Chris@591 199 SVDEBUG << "MainWindowBase: Calculating view font size" << endl;
Chris@591 200
Chris@105 201 // set a sensible default font size for views -- cannot do this
Chris@105 202 // in Preferences, which is in base and not supposed to use QtGui
Chris@436 203 int viewFontSize = int(QApplication::font().pointSize() * 0.9);
Chris@105 204 QSettings settings;
Chris@105 205 settings.beginGroup("Preferences");
Chris@105 206 viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
Chris@105 207 settings.setValue("view-font-size", viewFontSize);
Chris@105 208 settings.endGroup();
Chris@105 209
Chris@591 210 SVDEBUG << "MainWindowBase: View font size is " << viewFontSize << endl;
Chris@591 211
Chris@732 212 #ifndef Q_OS_MAC
Chris@734 213
Chris@45 214 Preferences::BackgroundMode mode =
Chris@45 215 Preferences::getInstance()->getBackgroundMode();
Chris@734 216
Chris@734 217 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
Chris@734 218
Chris@734 219 if (OSReportsDarkThemeActive()) {
Chris@734 220 // NB !(OSReportsDarkThemeActive()) doesn't necessarily mean
Chris@734 221 // the theme is light - the function also cunningly returns
Chris@734 222 // false if it has no way to tell
Chris@734 223 m_initialDarkBackground = true;
Chris@733 224 }
Chris@734 225
Chris@734 226 if (mode == Preferences::BackgroundFromTheme) {
Chris@734 227 m_viewManager->setGlobalDarkBackground
Chris@734 228 (m_initialDarkBackground);
Chris@734 229 } else {
Chris@45 230 m_viewManager->setGlobalDarkBackground
Chris@45 231 (mode == Preferences::DarkBackground);
Chris@45 232 }
Chris@734 233
Chris@511 234 #endif
Chris@45 235
Chris@712 236 m_paneStack = new PaneStack(nullptr, m_viewManager, paneStackOptions);
Chris@45 237 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
Chris@595 238 this, SLOT(currentPaneChanged(Pane *)));
Chris@45 239 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
Chris@595 240 this, SLOT(currentLayerChanged(Pane *, Layer *)));
Chris@743 241 connect(m_paneStack, SIGNAL(paneRightButtonMenuRequested(Pane *, QPoint)),
Chris@743 242 this, SLOT(paneRightButtonMenuRequested(Pane *, QPoint)));
Chris@743 243 connect(m_paneStack, SIGNAL(panePropertiesRightButtonMenuRequested(Pane *, QPoint)),
Chris@743 244 this, SLOT(panePropertiesRightButtonMenuRequested(Pane *, QPoint)));
Chris@743 245 connect(m_paneStack, SIGNAL(layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint)),
Chris@743 246 this, SLOT(layerPropertiesRightButtonMenuRequested(Pane *, Layer *, QPoint)));
Chris@45 247 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 248 this, SLOT(contextHelpChanged(const QString &)));
Chris@45 249 connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
Chris@45 250 this, SLOT(paneAdded(Pane *)));
Chris@45 251 connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
Chris@45 252 this, SLOT(paneHidden(Pane *)));
Chris@45 253 connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
Chris@45 254 this, SLOT(paneAboutToBeDeleted(Pane *)));
Chris@45 255 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
Chris@45 256 this, SLOT(paneDropAccepted(Pane *, QStringList)));
Chris@45 257 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
Chris@45 258 this, SLOT(paneDropAccepted(Pane *, QString)));
Chris@55 259 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
Chris@55 260 this, SLOT(paneDeleteButtonClicked(Pane *)));
Chris@591 261
Chris@591 262 SVDEBUG << "MainWindowBase: Creating play source" << endl;
Chris@571 263
Chris@574 264 m_playSource = new AudioCallbackPlaySource
Chris@574 265 (m_viewManager, QApplication::applicationName());
Chris@574 266
Chris@714 267 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 268 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@591 269 SVDEBUG << "MainWindowBase: Creating record target" << endl;
Chris@574 270 m_recordTarget = new AudioCallbackRecordTarget
Chris@574 271 (m_viewManager, QApplication::applicationName());
Chris@572 272 connect(m_recordTarget,
Chris@572 273 SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
Chris@572 274 this,
Chris@572 275 SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
Chris@475 276 }
Chris@45 277
Chris@436 278 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
Chris@591 279 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
Chris@570 280 connect(m_playSource, SIGNAL(channelCountIncreased(int)),
Chris@570 281 this, SLOT(audioChannelCountIncreased(int)));
Chris@45 282 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
Chris@45 283 this, SLOT(audioOverloadPluginDisabled()));
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@736 1512 if (audio) {
Chris@736 1513 return openAudio(source, mode);
Chris@579 1514 } else if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1515 return status;
Chris@579 1516 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
Chris@579 1517 return status;
Chris@579 1518 } else if (!canImportLayer) {
Chris@579 1519 return FileOpenWrongMode;
Chris@579 1520 } else if ((status = openImage(source)) != FileOpenFailed) {
Chris@579 1521 return status;
Chris@579 1522 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1523 return status;
Chris@579 1524 } else {
Chris@579 1525 return FileOpenFailed;
Chris@579 1526 }
Chris@579 1527 } catch (const InsufficientDiscSpace &e) {
Chris@579 1528 emit hideSplash();
Chris@579 1529 m_openingAudioFile = false;
Chris@594 1530 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
Chris@579 1531 QMessageBox::critical
Chris@579 1532 (this, tr("Not enough disc space"),
Chris@579 1533 tr("<b>Not enough disc space</b><p>There doesn't appear to be enough spare disc space to accommodate any necessary temporary files.</p><p>Please clear some space and try again.</p>").arg(e.what()));
Chris@579 1534 return FileOpenFailed;
Chris@592 1535 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
Chris@592 1536 emit hideSplash();
Chris@592 1537 m_openingAudioFile = false;
Chris@594 1538 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
Chris@592 1539 QMessageBox::critical
Chris@592 1540 (this, tr("Not enough memory"),
Chris@592 1541 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
Chris@592 1542 return FileOpenFailed;
Chris@45 1543 }
Chris@45 1544 }
Chris@45 1545
Chris@45 1546 MainWindowBase::FileOpenStatus
Chris@626 1547 MainWindowBase::openAudio(FileSource source,
Chris@626 1548 AudioFileOpenMode mode,
Chris@227 1549 QString templateName)
Chris@45 1550 {
Chris@386 1551 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
Chris@45 1552
Chris@222 1553 if (templateName == "") {
Chris@231 1554 templateName = getDefaultSessionTemplate();
Chris@577 1555 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
Chris@222 1556 }
Chris@220 1557
Chris@374 1558 // cerr << "template is: \"" << templateName << "\"" << endl;
Chris@223 1559
Chris@413 1560 if (!source.isAvailable()) {
Chris@413 1561 if (source.wasCancelled()) {
Chris@413 1562 return FileOpenCancelled;
Chris@413 1563 } else {
Chris@413 1564 return FileOpenFailed;
Chris@413 1565 }
Chris@413 1566 }
Chris@413 1567
Chris@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@738 2571 breakfastquay::ApplicationPlaybackSource *source =
Chris@738 2572 m_playSource->getApplicationPlaybackSource();
Chris@569 2573
Chris@569 2574 std::string errorString;
Chris@551 2575
Chris@714 2576 if (m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@475 2577 m_audioIO = breakfastquay::AudioFactory::
Chris@738 2578 createCallbackIO(m_recordTarget, source, preference, errorString);
Chris@525 2579 if (m_audioIO) {
Chris@611 2580 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2581 m_audioIO->suspend(); // start in suspended state
Chris@525 2582 m_playSource->setSystemPlaybackTarget(m_audioIO);
Chris@586 2583 } else {
Chris@586 2584 // Failed to create audio I/O; this may just mean there is
Chris@586 2585 // no record device, so fall through to see what happens
Chris@586 2586 // next. We only report complete failure if we end up with
Chris@586 2587 // neither m_audioIO nor m_playTarget.
Chris@525 2588 }
Chris@586 2589 }
Chris@586 2590
Chris@586 2591 if (!m_audioIO) {
Chris@475 2592 m_playTarget = breakfastquay::AudioFactory::
Chris@738 2593 createCallbackPlayTarget(source, preference, errorString);
Chris@525 2594 if (m_playTarget) {
Chris@611 2595 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2596 m_playTarget->suspend(); // start in suspended state
Chris@525 2597 m_playSource->setSystemPlaybackTarget(m_playTarget);
Chris@525 2598 }
Chris@475 2599 }
Chris@475 2600
Chris@475 2601 if (!m_playTarget && !m_audioIO) {
Chris@104 2602 emit hideSplash();
Chris@569 2603 QString message;
Chris@569 2604 QString error = errorString.c_str();
Chris@569 2605 QString firstBit, secondBit;
Chris@547 2606 if (implementation == "") {
Chris@569 2607 if (error == "") {
Chris@569 2608 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
Chris@569 2609 } else {
Chris@569 2610 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
Chris@569 2611 }
Chris@714 2612 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2613 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2614 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
Chris@569 2615 } else {
Chris@569 2616 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
Chris@569 2617 }
Chris@126 2618 } else {
Chris@569 2619 QString driverName = breakfastquay::AudioFactory::
Chris@569 2620 getImplementationDescription(implementation.toStdString())
Chris@569 2621 .c_str();
Chris@569 2622 if (error == "") {
Chris@569 2623 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
Chris@569 2624 } else {
Chris@569 2625 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
Chris@569 2626 }
Chris@714 2627 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2628 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2629 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
Chris@569 2630 } else {
Chris@569 2631 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
Chris@569 2632 }
Chris@126 2633 }
Chris@570 2634 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
Chris@570 2635 << implementation << "\": error is: " << error << endl;
Chris@569 2636 QMessageBox::warning(this, tr("Couldn't open audio device"),
Chris@569 2637 firstBit + secondBit, QMessageBox::Ok);
Chris@45 2638 }
Chris@45 2639 }
Chris@45 2640
Chris@556 2641 void
Chris@556 2642 MainWindowBase::deleteAudioIO()
Chris@556 2643 {
Chris@556 2644 // First prevent this trying to call target.
Chris@559 2645 if (m_playSource) {
Chris@636 2646 m_playSource->setSystemPlaybackTarget(nullptr);
Chris@559 2647 }
Chris@556 2648
Chris@556 2649 // Then delete the breakfastquay::System object.
Chris@556 2650 // Only one of these two exists!
Chris@556 2651 delete m_audioIO;
Chris@556 2652 delete m_playTarget;
Chris@556 2653
Chris@636 2654 m_audioIO = nullptr;
Chris@636 2655 m_playTarget = nullptr;
Chris@556 2656 }
Chris@556 2657
Chris@556 2658 void
Chris@556 2659 MainWindowBase::recreateAudioIO()
Chris@556 2660 {
Chris@556 2661 deleteAudioIO();
Chris@556 2662 createAudioIO();
Chris@556 2663 }
Chris@556 2664
Chris@570 2665 void
Chris@570 2666 MainWindowBase::audioChannelCountIncreased(int)
Chris@570 2667 {
Chris@611 2668 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
Chris@570 2669 recreateAudioIO();
Chris@610 2670
Chris@610 2671 if (m_recordTarget &&
Chris@610 2672 m_recordTarget->isRecording() &&
Chris@610 2673 m_audioIO) {
Chris@610 2674 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
Chris@610 2675 m_audioIO->resume();
Chris@610 2676 }
Chris@570 2677 }
Chris@570 2678
Chris@684 2679 ModelId
Chris@685 2680 MainWindowBase::getMainModelId() const
Chris@684 2681 {
Chris@684 2682 if (!m_document) return {};
Chris@684 2683 return m_document->getMainModel();
Chris@684 2684 }
Chris@684 2685
Chris@684 2686 std::shared_ptr<WaveFileModel>
Chris@685 2687 MainWindowBase::getMainModel() const
Chris@45 2688 {
Chris@684 2689 return ModelById::getAs<WaveFileModel>(getMainModelId());
Chris@45 2690 }
Chris@45 2691
Chris@45 2692 void
Chris@45 2693 MainWindowBase::createDocument()
Chris@45 2694 {
Chris@45 2695 m_document = new Document;
Chris@45 2696
Chris@45 2697 connect(m_document, SIGNAL(layerAdded(Layer *)),
Chris@595 2698 this, SLOT(layerAdded(Layer *)));
Chris@45 2699 connect(m_document, SIGNAL(layerRemoved(Layer *)),
Chris@595 2700 this, SLOT(layerRemoved(Layer *)));
Chris@45 2701 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
Chris@595 2702 this, SLOT(layerAboutToBeDeleted(Layer *)));
Chris@45 2703 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
Chris@595 2704 this, SLOT(layerInAView(Layer *, bool)));
Chris@45 2705
Chris@684 2706 connect(m_document, SIGNAL(modelAdded(ModelId )),
Chris@684 2707 this, SLOT(modelAdded(ModelId )));
Chris@687 2708 connect(m_document, SIGNAL(mainModelChanged(ModelId)),
Chris@687 2709 this, SLOT(mainModelChanged(ModelId)));
Chris@45 2710
Chris@78 2711 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
Chris@78 2712 this, SLOT(modelGenerationFailed(QString, QString)));
Chris@78 2713 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@78 2714 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@687 2715 connect(m_document, SIGNAL(alignmentComplete(ModelId)),
Chris@687 2716 this, SLOT(alignmentComplete(ModelId)));
Chris@761 2717 connect(m_document, SIGNAL(alignmentFailed(ModelId, QString)),
Chris@761 2718 this, SLOT(alignmentFailed(ModelId, QString)));
Chris@160 2719
Chris@667 2720 m_document->setAutoAlignment(m_viewManager->getAlignMode());
Chris@667 2721
Chris@160 2722 emit replacedDocument();
Chris@45 2723 }
Chris@45 2724
Chris@45 2725 bool
Chris@45 2726 MainWindowBase::saveSessionFile(QString path)
Chris@45 2727 {
Chris@217 2728 try {
Chris@217 2729
Chris@217 2730 TempWriteFile temp(path);
Chris@217 2731
Chris@217 2732 BZipFileDevice bzFile(temp.getTemporaryFilename());
Chris@217 2733 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@293 2734 cerr << "Failed to open session file \""
Chris@294 2735 << temp.getTemporaryFilename()
Chris@217 2736 << "\" for writing: "
Chris@293 2737 << bzFile.errorString() << endl;
Chris@217 2738 return false;
Chris@217 2739 }
Chris@217 2740
Chris@217 2741 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@217 2742
Chris@217 2743 QTextStream out(&bzFile);
Chris@432 2744 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2745 toXml(out, false);
Chris@217 2746 out.flush();
Chris@217 2747
Chris@217 2748 QApplication::restoreOverrideCursor();
Chris@217 2749
Chris@217 2750 if (!bzFile.isOK()) {
Chris@217 2751 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2752 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2753 .arg(path).arg(bzFile.errorString()));
Chris@217 2754 bzFile.close();
Chris@217 2755 return false;
Chris@217 2756 }
Chris@217 2757
Chris@217 2758 bzFile.close();
Chris@217 2759 temp.moveToTarget();
Chris@217 2760 return true;
Chris@217 2761
Chris@217 2762 } catch (FileOperationFailed &f) {
Chris@217 2763
Chris@217 2764 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2765 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2766 .arg(path).arg(f.what()));
Chris@45 2767 return false;
Chris@45 2768 }
Chris@45 2769 }
Chris@45 2770
Chris@224 2771 bool
Chris@224 2772 MainWindowBase::saveSessionTemplate(QString path)
Chris@224 2773 {
Chris@224 2774 try {
Chris@224 2775
Chris@224 2776 TempWriteFile temp(path);
Chris@224 2777
Chris@224 2778 QFile file(temp.getTemporaryFilename());
Chris@224 2779 if (!file.open(QIODevice::WriteOnly)) {
Chris@293 2780 cerr << "Failed to open session template file \""
Chris@294 2781 << temp.getTemporaryFilename()
Chris@224 2782 << "\" for writing: "
Chris@294 2783 << file.errorString() << endl;
Chris@224 2784 return false;
Chris@224 2785 }
Chris@224 2786
Chris@224 2787 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@224 2788
Chris@224 2789 QTextStream out(&file);
Chris@432 2790 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2791 toXml(out, true);
Chris@224 2792 out.flush();
Chris@224 2793
Chris@224 2794 QApplication::restoreOverrideCursor();
Chris@224 2795
Chris@224 2796 file.close();
Chris@224 2797 temp.moveToTarget();
Chris@224 2798 return true;
Chris@224 2799
Chris@224 2800 } catch (FileOperationFailed &f) {
Chris@224 2801
Chris@224 2802 QMessageBox::critical(this, tr("Failed to write file"),
Chris@224 2803 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@224 2804 .arg(path).arg(f.what()));
Chris@224 2805 return false;
Chris@224 2806 }
Chris@224 2807 }
Chris@224 2808
Chris@729 2809 //!!! should we pull out the whole export logic into another
Chris@729 2810 // class? then we can more reasonably query it for things like
Chris@729 2811 // "can we export this layer type to this file format? can we
Chris@729 2812 // export selections, or only the whole layer?"
Chris@729 2813
Chris@659 2814 bool
Chris@729 2815 MainWindowBase::exportLayerToSVL(Layer *layer,
Chris@729 2816 QString path, QString &error)
Chris@659 2817 {
Chris@659 2818 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@659 2819
Chris@659 2820 QString suffix = QFileInfo(path).suffix().toLower();
Chris@659 2821
Chris@729 2822 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@684 2823 if (!model) {
Chris@684 2824 error = tr("Internal error: unknown model");
Chris@684 2825 return false;
Chris@684 2826 }
Chris@659 2827
Chris@729 2828 QFile file(path);
Chris@729 2829 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@729 2830 error = tr("Failed to open file %1 for writing").arg(path);
Chris@729 2831 } else {
Chris@729 2832 QTextStream out(&file);
Chris@729 2833 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@729 2834 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@729 2835 << "<!DOCTYPE sonic-visualiser>\n"
Chris@729 2836 << "<sv>\n"
Chris@729 2837 << " <data>\n";
Chris@729 2838
Chris@729 2839 model->toXml(out, " ");
Chris@729 2840
Chris@729 2841 out << " </data>\n"
Chris@729 2842 << " <display>\n";
Chris@729 2843
Chris@729 2844 layer->toXml(out, " ");
Chris@729 2845
Chris@729 2846 out << " </display>\n"
Chris@729 2847 << "</sv>\n";
Chris@729 2848 }
Chris@729 2849
Chris@729 2850 return (error == "");
Chris@729 2851 }
Chris@729 2852
Chris@729 2853 bool
Chris@729 2854 MainWindowBase::exportLayerToMIDI(Layer *layer,
Chris@729 2855 MultiSelection *selectionsToWrite,
Chris@729 2856 QString path, QString &error)
Chris@729 2857 {
Chris@729 2858 if (QFileInfo(path).suffix() == "") path += ".mid";
Chris@729 2859
Chris@729 2860 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2861
Chris@729 2862 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@729 2863 if (!model) {
Chris@729 2864 error = tr("Internal error: unknown model");
Chris@729 2865 return false;
Chris@729 2866 }
Chris@729 2867
Chris@729 2868 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
Chris@729 2869
Chris@729 2870 if (!nm) {
Chris@729 2871 error = tr("Can't export non-note layers to MIDI");
Chris@729 2872 } else if (!selectionsToWrite) {
Chris@729 2873 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
Chris@729 2874 writer.write();
Chris@729 2875 if (!writer.isOK()) {
Chris@729 2876 error = writer.getError();
Chris@659 2877 }
Chris@729 2878 } else {
Chris@729 2879 NoteModel temporary(nm->getSampleRate(),
Chris@729 2880 nm->getResolution(),
Chris@729 2881 nm->getValueMinimum(),
Chris@729 2882 nm->getValueMaximum(),
Chris@729 2883 false);
Chris@729 2884 temporary.setScaleUnits(nm->getScaleUnits());
Chris@729 2885 for (const auto &s: selectionsToWrite->getSelections()) {
Chris@729 2886 EventVector ev(nm->getEventsStartingWithin
Chris@729 2887 (s.getStartFrame(), s.getDuration()));
Chris@729 2888 for (const auto &e: ev) {
Chris@729 2889 temporary.add(e);
Chris@724 2890 }
Chris@659 2891 }
Chris@729 2892 MIDIFileWriter writer(path, &temporary, temporary.getSampleRate());
Chris@729 2893 writer.write();
Chris@659 2894 if (!writer.isOK()) {
Chris@659 2895 error = writer.getError();
Chris@659 2896 }
Chris@659 2897 }
Chris@729 2898
Chris@659 2899 return (error == "");
Chris@659 2900 }
Chris@659 2901
Chris@729 2902 bool
Chris@729 2903 MainWindowBase::exportLayerToRDF(Layer *layer,
Chris@729 2904 QString path, QString &error)
Chris@729 2905 {
Chris@729 2906 if (QFileInfo(path).suffix() == "") path += ".ttl";
Chris@729 2907
Chris@729 2908 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2909
Chris@729 2910 auto model = ModelById::get(layer->getExportModel(nullptr));
Chris@729 2911 if (!model) {
Chris@729 2912 error = tr("Internal error: unknown model");
Chris@729 2913 return false;
Chris@729 2914 }
Chris@729 2915
Chris@729 2916 if (!RDFExporter::canExportModel(model.get())) {
Chris@729 2917 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
Chris@729 2918 } else {
Chris@729 2919 RDFExporter exporter(path, model.get());
Chris@729 2920 exporter.write();
Chris@729 2921 if (!exporter.isOK()) {
Chris@729 2922 error = exporter.getError();
Chris@729 2923 }
Chris@729 2924 }
Chris@729 2925
Chris@729 2926 return (error == "");
Chris@729 2927 }
Chris@729 2928
Chris@729 2929 bool
Chris@729 2930 MainWindowBase::exportLayerToCSV(Layer *layer, LayerGeometryProvider *provider,
Chris@729 2931 MultiSelection *selectionsToWrite,
Chris@729 2932 QString delimiter,
Chris@729 2933 DataExportOptions options,
Chris@729 2934 QString path, QString &error)
Chris@729 2935 {
Chris@729 2936 if (QFileInfo(path).suffix() == "") path += ".csv";
Chris@729 2937
Chris@729 2938 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2939
Chris@729 2940 auto model = ModelById::get(layer->getExportModel(provider));
Chris@729 2941 if (!model) {
Chris@729 2942 error = tr("Internal error: unknown model");
Chris@729 2943 return false;
Chris@729 2944 }
Chris@729 2945
Chris@729 2946 ProgressDialog dialog {
Chris@729 2947 QObject::tr("Exporting layer..."), true, 500, this,
Chris@729 2948 Qt::ApplicationModal
Chris@729 2949 };
Chris@729 2950
Chris@729 2951 CSVFileWriter writer(path, model.get(), &dialog, delimiter, options);
Chris@729 2952
Chris@729 2953 if (selectionsToWrite) {
Chris@729 2954 writer.writeSelection(*selectionsToWrite);
Chris@729 2955 } else {
Chris@729 2956 writer.write();
Chris@729 2957 }
Chris@729 2958
Chris@729 2959 if (!writer.isOK()) {
Chris@729 2960 error = writer.getError();
Chris@729 2961 if (error == "") {
Chris@729 2962 error = tr("Failed to export layer for an unknown reason");
Chris@729 2963 }
Chris@729 2964 }
Chris@729 2965
Chris@729 2966 return (error == "");
Chris@729 2967 }
Chris@729 2968
Chris@729 2969 bool
Chris@729 2970 MainWindowBase::exportLayerTo(Layer *layer, LayerGeometryProvider *provider,
Chris@729 2971 MultiSelection *selectionsToWrite,
Chris@729 2972 QString path, QString &error)
Chris@729 2973 {
Chris@731 2974 if (QFileInfo(path).suffix() == "") path += ".csv";
Chris@729 2975 QString suffix = QFileInfo(path).suffix().toLower();
Chris@729 2976
Chris@729 2977 if (suffix == "xml" || suffix == "svl") {
Chris@729 2978 return exportLayerToSVL(layer, path, error);
Chris@729 2979 } else if (suffix == "mid" || suffix == "midi") {
Chris@729 2980 return exportLayerToMIDI(layer, selectionsToWrite, path, error);
Chris@729 2981 } else if (suffix == "ttl" || suffix == "n3") {
Chris@729 2982 return exportLayerToRDF(layer, path, error);
Chris@729 2983 } else {
Chris@729 2984 return exportLayerToCSV(layer, provider, selectionsToWrite,
Chris@729 2985 (suffix == "csv" ? "," : "\t"),
Chris@729 2986 DataExportDefaults, path, error);
Chris@729 2987 }
Chris@729 2988 }
Chris@729 2989
Chris@45 2990 void
Chris@226 2991 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
Chris@45 2992 {
Chris@45 2993 QString indent(" ");
Chris@45 2994
Chris@45 2995 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@45 2996 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@45 2997 out << "<sv>\n";
Chris@45 2998
Chris@226 2999 if (asTemplate) {
Chris@226 3000 m_document->toXmlAsTemplate(out, "", "");
Chris@226 3001 } else {
Chris@226 3002 m_document->toXml(out, "", "");
Chris@226 3003 }
Chris@45 3004
Chris@45 3005 out << "<display>\n";
Chris@45 3006
Chris@45 3007 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@595 3008 .arg(width()).arg(height());
Chris@45 3009
Chris@45 3010 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 3011
Chris@595 3012 Pane *pane = m_paneStack->getPane(i);
Chris@595 3013
Chris@595 3014 if (pane) {
Chris@45 3015 pane->toXml(out, indent);
Chris@595 3016 }
Chris@45 3017 }
Chris@45 3018
Chris@45 3019 out << "</display>\n";
Chris@45 3020
Chris@45 3021 m_viewManager->getSelection().toXml(out);
Chris@45 3022
Chris@45 3023 out << "</sv>\n";
Chris@45 3024 }
Chris@45 3025
Chris@45 3026 Pane *
Chris@45 3027 MainWindowBase::addPaneToStack()
Chris@45 3028 {
Chris@342 3029 cerr << "MainWindowBase::addPaneToStack()" << endl;
Chris@45 3030 AddPaneCommand *command = new AddPaneCommand(this);
Chris@45 3031 CommandHistory::getInstance()->addCommand(command);
Chris@57 3032 Pane *pane = command->getPane();
Chris@57 3033 return pane;
Chris@45 3034 }
Chris@45 3035
Chris@45 3036 void
Chris@45 3037 MainWindowBase::zoomIn()
Chris@45 3038 {
Chris@45 3039 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3040 if (currentPane) currentPane->zoom(true);
Chris@45 3041 }
Chris@45 3042
Chris@45 3043 void
Chris@45 3044 MainWindowBase::zoomOut()
Chris@45 3045 {
Chris@45 3046 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3047 if (currentPane) currentPane->zoom(false);
Chris@45 3048 }
Chris@45 3049
Chris@45 3050 void
Chris@45 3051 MainWindowBase::zoomToFit()
Chris@45 3052 {
Chris@45 3053 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3054 if (!currentPane) return;
Chris@45 3055
Chris@684 3056 auto model = getMainModel();
Chris@45 3057 if (!model) return;
Chris@45 3058
Chris@434 3059 sv_frame_t start = model->getStartFrame();
Chris@434 3060 sv_frame_t end = model->getEndFrame();
Chris@60 3061 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
Chris@366 3062 int pixels = currentPane->width();
Chris@366 3063
Chris@366 3064 int sw = currentPane->getVerticalScaleWidth();
Chris@45 3065 if (pixels > sw * 2) pixels -= sw * 2;
Chris@45 3066 else pixels = 1;
Chris@45 3067 if (pixels > 4) pixels -= 4;
Chris@45 3068
Chris@624 3069 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
Chris@45 3070 currentPane->setZoomLevel(zoomLevel);
Chris@45 3071 currentPane->setCentreFrame((start + end) / 2);
Chris@45 3072 }
Chris@45 3073
Chris@45 3074 void
Chris@45 3075 MainWindowBase::zoomDefault()
Chris@45 3076 {
Chris@45 3077 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@302 3078 QSettings settings;
Chris@302 3079 settings.beginGroup("MainWindow");
Chris@302 3080 int zoom = settings.value("zoom-default", 1024).toInt();
Chris@302 3081 settings.endGroup();
Chris@624 3082 if (currentPane) {
Chris@624 3083 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
Chris@624 3084 }
Chris@45 3085 }
Chris@45 3086
Chris@45 3087 void
Chris@45 3088 MainWindowBase::scrollLeft()
Chris@45 3089 {
Chris@45 3090 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3091 if (currentPane) currentPane->scroll(false, false);
Chris@45 3092 }
Chris@45 3093
Chris@45 3094 void
Chris@45 3095 MainWindowBase::jumpLeft()
Chris@45 3096 {
Chris@45 3097 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3098 if (currentPane) currentPane->scroll(false, true);
Chris@45 3099 }
Chris@45 3100
Chris@45 3101 void
Chris@162 3102 MainWindowBase::peekLeft()
Chris@162 3103 {
Chris@162 3104 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3105 if (currentPane) currentPane->scroll(false, false, false);
Chris@162 3106 }
Chris@162 3107
Chris@162 3108 void
Chris@45 3109 MainWindowBase::scrollRight()
Chris@45 3110 {
Chris@45 3111 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3112 if (currentPane) currentPane->scroll(true, false);
Chris@45 3113 }
Chris@45 3114
Chris@45 3115 void
Chris@45 3116 MainWindowBase::jumpRight()
Chris@45 3117 {
Chris@45 3118 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 3119 if (currentPane) currentPane->scroll(true, true);
Chris@45 3120 }
Chris@45 3121
Chris@45 3122 void
Chris@162 3123 MainWindowBase::peekRight()
Chris@162 3124 {
Chris@162 3125 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 3126 if (currentPane) currentPane->scroll(true, false, false);
Chris@162 3127 }
Chris@162 3128
Chris@162 3129 void
Chris@45 3130 MainWindowBase::showNoOverlays()
Chris@45 3131 {
Chris@45 3132 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@45 3133 }
Chris@45 3134
Chris@45 3135 void
Chris@45 3136 MainWindowBase::showMinimalOverlays()
Chris@45 3137 {
Chris@335 3138 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@45 3139 }
Chris@45 3140
Chris@45 3141 void
Chris@45 3142 MainWindowBase::showAllOverlays()
Chris@45 3143 {
Chris@45 3144 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@45 3145 }
Chris@45 3146
Chris@45 3147 void
Chris@577 3148 MainWindowBase::findTimeRulerLayer()
Chris@577 3149 {
Chris@577 3150 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@577 3151 Pane *pane = m_paneStack->getPane(i);
Chris@577 3152 if (!pane) continue;
Chris@577 3153 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@577 3154 Layer *layer = pane->getLayer(j);
Chris@577 3155 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@577 3156 m_timeRulerLayer = layer;
Chris@577 3157 return;
Chris@577 3158 }
Chris@577 3159 }
Chris@577 3160 if (m_timeRulerLayer) {
Chris@577 3161 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
Chris@577 3162 delete m_timeRulerLayer;
Chris@636 3163 m_timeRulerLayer = nullptr;
Chris@577 3164 }
Chris@577 3165 }
Chris@577 3166
Chris@577 3167 void
Chris@211 3168 MainWindowBase::toggleTimeRulers()
Chris@211 3169 {
Chris@211 3170 bool haveRulers = false;
Chris@211 3171 bool someHidden = false;
Chris@211 3172
Chris@211 3173 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3174
Chris@211 3175 Pane *pane = m_paneStack->getPane(i);
Chris@211 3176 if (!pane) continue;
Chris@211 3177
Chris@211 3178 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3179
Chris@211 3180 Layer *layer = pane->getLayer(j);
Chris@211 3181 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3182
Chris@211 3183 haveRulers = true;
Chris@211 3184 if (layer->isLayerDormant(pane)) someHidden = true;
Chris@211 3185 }
Chris@211 3186 }
Chris@211 3187
Chris@211 3188 if (haveRulers) {
Chris@211 3189
Chris@211 3190 bool show = someHidden;
Chris@211 3191
Chris@211 3192 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3193
Chris@211 3194 Pane *pane = m_paneStack->getPane(i);
Chris@211 3195 if (!pane) continue;
Chris@211 3196
Chris@211 3197 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3198
Chris@211 3199 Layer *layer = pane->getLayer(j);
Chris@211 3200 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3201
Chris@211 3202 layer->showLayer(pane, show);
Chris@211 3203 }
Chris@211 3204 }
Chris@211 3205 }
Chris@211 3206 }
Chris@211 3207
Chris@211 3208 void
Chris@45 3209 MainWindowBase::toggleZoomWheels()
Chris@45 3210 {
Chris@45 3211 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@45 3212 m_viewManager->setZoomWheelsEnabled(false);
Chris@45 3213 } else {
Chris@45 3214 m_viewManager->setZoomWheelsEnabled(true);
Chris@45 3215 }
Chris@45 3216 }
Chris@45 3217
Chris@45 3218 void
Chris@45 3219 MainWindowBase::togglePropertyBoxes()
Chris@45 3220 {
Chris@712 3221 if (m_paneStack->getLayoutStyle() == PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3222 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3223 Preferences::VerticallyStacked) {
Chris@45 3224 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3225 } else {
Chris@45 3226 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3227 }
Chris@45 3228 } else {
Chris@712 3229 m_paneStack->setLayoutStyle(PaneStack::HiddenPropertyStacksLayout);
Chris@45 3230 }
Chris@45 3231 }
Chris@45 3232
Chris@378 3233 QLabel *
Chris@378 3234 MainWindowBase::getStatusLabel() const
Chris@378 3235 {
Chris@378 3236 if (!m_statusLabel) {
Chris@378 3237 m_statusLabel = new QLabel();
Chris@378 3238 statusBar()->addWidget(m_statusLabel, 1);
Chris@378 3239 }
Chris@379 3240
Chris@379 3241 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
Chris@379 3242 foreach (QFrame *f, frames) {
Chris@379 3243 f->setFrameStyle(QFrame::NoFrame);
Chris@379 3244 }
Chris@379 3245
Chris@378 3246 return m_statusLabel;
Chris@378 3247 }
Chris@378 3248
Chris@45 3249 void
Chris@45 3250 MainWindowBase::toggleStatusBar()
Chris@45 3251 {
Chris@45 3252 QSettings settings;
Chris@45 3253 settings.beginGroup("MainWindow");
Chris@45 3254 bool sb = settings.value("showstatusbar", true).toBool();
Chris@45 3255
Chris@45 3256 if (sb) {
Chris@45 3257 statusBar()->hide();
Chris@45 3258 } else {
Chris@45 3259 statusBar()->show();
Chris@45 3260 }
Chris@45 3261
Chris@45 3262 settings.setValue("showstatusbar", !sb);
Chris@45 3263
Chris@45 3264 settings.endGroup();
Chris@45 3265 }
Chris@45 3266
Chris@45 3267 void
Chris@256 3268 MainWindowBase::toggleCentreLine()
Chris@256 3269 {
Chris@256 3270 if (m_viewManager->shouldShowCentreLine()) {
Chris@256 3271 m_viewManager->setShowCentreLine(false);
Chris@256 3272 } else {
Chris@256 3273 m_viewManager->setShowCentreLine(true);
Chris@256 3274 }
Chris@256 3275 }
Chris@256 3276
Chris@256 3277 void
Chris@45 3278 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
Chris@45 3279 {
Chris@45 3280 if (name == "Property Box Layout") {
Chris@712 3281 if (m_paneStack->getLayoutStyle() != PaneStack::HiddenPropertyStacksLayout) {
Chris@45 3282 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3283 Preferences::VerticallyStacked) {
Chris@45 3284 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3285 } else {
Chris@45 3286 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3287 }
Chris@45 3288 }
Chris@45 3289 } else if (name == "Background Mode" && m_viewManager) {
Chris@45 3290 Preferences::BackgroundMode mode =
Chris@45 3291 Preferences::getInstance()->getBackgroundMode();
Chris@45 3292 if (mode == Preferences::BackgroundFromTheme) {
Chris@45 3293 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@45 3294 } else if (mode == Preferences::DarkBackground) {
Chris@45 3295 m_viewManager->setGlobalDarkBackground(true);
Chris@45 3296 } else {
Chris@45 3297 m_viewManager->setGlobalDarkBackground(false);
Chris@45 3298 }
Chris@45 3299 }
Chris@45 3300 }
Chris@45 3301
Chris@45 3302 void
Chris@45 3303 MainWindowBase::play()
Chris@45 3304 {
Chris@516 3305 if ((m_recordTarget && m_recordTarget->isRecording()) ||
Chris@516 3306 (m_playSource && m_playSource->isPlaying())) {
Chris@45 3307 stop();
Chris@479 3308 QAction *action = qobject_cast<QAction *>(sender());
Chris@479 3309 if (action) action->setChecked(false);
Chris@45 3310 } else {
Chris@487 3311 if (m_audioIO) m_audioIO->resume();
Chris@509 3312 else if (m_playTarget) m_playTarget->resume();
Chris@45 3313 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@595 3314 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@45 3315 }
Chris@45 3316 }
Chris@45 3317
Chris@45 3318 void
Chris@477 3319 MainWindowBase::record()
Chris@477 3320 {
Chris@586 3321 QAction *action = qobject_cast<QAction *>(sender());
Chris@586 3322
Chris@714 3323 if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
Chris@586 3324 if (action) action->setChecked(false);
Chris@478 3325 return;
Chris@478 3326 }
Chris@478 3327
Chris@477 3328 if (!m_recordTarget) {
Chris@586 3329 if (action) action->setChecked(false);
Chris@477 3330 return;
Chris@477 3331 }
Chris@477 3332
Chris@714 3333 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
Chris@714 3334 SVDEBUG << "MainWindowBase::record: upgrading from "
Chris@714 3335 << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
Chris@714 3336 << "AUDIO_PLAYBACK_AND_RECORD" << endl;
Chris@714 3337 m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
Chris@714 3338 deleteAudioIO();
Chris@714 3339 }
Chris@714 3340
Chris@478 3341 if (!m_audioIO) {
Chris@611 3342 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
Chris@478 3343 createAudioIO();
Chris@478 3344 }
Chris@492 3345
Chris@492 3346 if (!m_audioIO) {
Chris@586 3347 if (!m_playTarget) {
Chris@586 3348 // Don't need to report this, createAudioIO should have
Chris@586 3349 if (action) action->setChecked(false);
Chris@586 3350 return;
Chris@586 3351 } else {
Chris@586 3352 // Need to report this: if the play target exists instead
Chris@586 3353 // of the audio IO, then that means we failed to open a
Chris@586 3354 // capture device. The record control should be disabled
Chris@586 3355 // in that situation, so if it happens here, that must
Chris@586 3356 // mean this is the first time we ever tried to open the
Chris@586 3357 // audio device, hence the need to report the problem here
Chris@586 3358 QMessageBox::critical
Chris@586 3359 (this, tr("No record device available"),
Chris@586 3360 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 3361 if (action) action->setChecked(false);
Chris@586 3362 updateMenuStates();
Chris@586 3363 return;
Chris@586 3364 }
Chris@492 3365 }
Chris@478 3366
Chris@477 3367 if (m_recordTarget->isRecording()) {
Chris@492 3368 stop();
Chris@477 3369 return;
Chris@477 3370 }
Chris@490 3371
Chris@483 3372 if (m_audioRecordMode == RecordReplaceSession) {
Chris@490 3373 if (!checkSaveModified()) {
Chris@490 3374 if (action) action->setChecked(false);
Chris@490 3375 return;
Chris@490 3376 }
Chris@483 3377 }
Chris@487 3378
Chris@557 3379 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
Chris@557 3380
Chris@611 3381 SVCERR << "MainWindowBase::record: about to resume" << endl;
Chris@492 3382 m_audioIO->resume();
Chris@509 3383
Chris@684 3384 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
Chris@684 3385 if (!modelPtr) {
Chris@586 3386 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
Chris@586 3387 QMessageBox::critical
Chris@586 3388 (this, tr("Recording failed"),
Chris@586 3389 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
Chris@490 3390 if (action) action->setChecked(false);
Chris@477 3391 return;
Chris@477 3392 }
Chris@477 3393
Chris@684 3394 if (!modelPtr->isOK()) {
Chris@611 3395 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
Chris@477 3396 m_recordTarget->stopRecording();
Chris@492 3397 m_audioIO->suspend();
Chris@586 3398 if (action) action->setChecked(false);
Chris@684 3399 delete modelPtr;
Chris@477 3400 return;
Chris@477 3401 }
Chris@611 3402
Chris@611 3403 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
Chris@684 3404
Chris@684 3405 QString location = modelPtr->getLocation();
Chris@487 3406
Chris@687 3407 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
Chris@483 3408
Chris@483 3409 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
Chris@478 3410
Chris@479 3411 //!!! duplication with openAudio here
Chris@479 3412
Chris@479 3413 QString templateName = getDefaultSessionTemplate();
Chris@479 3414 bool loadedTemplate = false;
Chris@479 3415
Chris@479 3416 if (templateName != "") {
Chris@479 3417 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@479 3418 if (tplStatus == FileOpenCancelled) {
Chris@611 3419 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
Chris@490 3420 m_recordTarget->stopRecording();
Chris@492 3421 m_audioIO->suspend();
Chris@684 3422 ModelById::release(modelId);
Chris@479 3423 return;
Chris@479 3424 }
Chris@479 3425 if (tplStatus != FileOpenFailed) {
Chris@479 3426 loadedTemplate = true;
Chris@479 3427 }
Chris@479 3428 }
Chris@479 3429
Chris@479 3430 if (!loadedTemplate) {
Chris@479 3431 closeSession();
Chris@479 3432 createDocument();
Chris@479 3433 }
Chris@479 3434
Chris@684 3435 ModelId prevMain = getMainModelId();
Chris@684 3436 if (!prevMain.isNone()) {
Chris@479 3437 m_playSource->removeModel(prevMain);
Chris@479 3438 }
Chris@479 3439
Chris@684 3440 m_document->setMainModel(modelId);
Chris@478 3441 setupMenus();
Chris@577 3442 findTimeRulerLayer();
Chris@478 3443
Chris@684 3444 m_originalLocation = location;
Chris@669 3445
Chris@595 3446 if (loadedTemplate || (m_sessionFile == "")) {
Chris@595 3447 CommandHistory::getInstance()->clear();
Chris@595 3448 CommandHistory::getInstance()->documentSaved();
Chris@595 3449 }
Chris@479 3450
Chris@669 3451 m_documentModified = false;
Chris@669 3452 updateWindowTitle();
Chris@669 3453
Chris@478 3454 } else {
Chris@478 3455
Chris@478 3456 CommandHistory::getInstance()->startCompoundOperation
Chris@478 3457 (tr("Import Recorded Audio"), true);
Chris@478 3458
Chris@691 3459 m_document->addNonDerivedModel(modelId);
Chris@478 3460
Chris@478 3461 AddPaneCommand *command = new AddPaneCommand(this);
Chris@478 3462 CommandHistory::getInstance()->addCommand(command);
Chris@478 3463
Chris@478 3464 Pane *pane = command->getPane();
Chris@478 3465
Chris@478 3466 if (m_timeRulerLayer) {
Chris@478 3467 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@478 3468 }
Chris@478 3469
Chris@684 3470 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@478 3471
Chris@478 3472 if (newLayer) {
Chris@478 3473 m_document->addLayerToView(pane, newLayer);
Chris@478 3474 }
Chris@595 3475
Chris@478 3476 CommandHistory::getInstance()->endCompoundOperation();
Chris@477 3477 }
Chris@479 3478
Chris@479 3479 updateMenuStates();
Chris@684 3480 m_recentFiles.addFile(location);
Chris@479 3481 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@611 3482
Chris@496 3483 emit audioFileLoaded();
Chris@477 3484 }
Chris@477 3485
Chris@477 3486 void
Chris@45 3487 MainWindowBase::ffwd()
Chris@45 3488 {
Chris@45 3489 if (!getMainModel()) return;
Chris@45 3490
Chris@708 3491 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3492 sv_frame_t frame = playbackFrame + 1;
Chris@45 3493
Chris@85 3494 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3495 Layer *layer = getSnapLayer();
Chris@435 3496 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3497
Chris@708 3498 if (!pane || !layer) {
Chris@45 3499
Chris@45 3500 frame = RealTime::realTime2Frame
Chris@357 3501 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
Chris@435 3502 if (frame > getMainModel()->getEndFrame()) {
Chris@45 3503 frame = getMainModel()->getEndFrame();
Chris@45 3504 }
Chris@45 3505
Chris@45 3506 } else {
Chris@45 3507
Chris@708 3508 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3509 int resolution = 0;
Chris@708 3510 bool success = false;
Chris@708 3511
Chris@708 3512 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3513 Layer::SnapRight, -1)) {
Chris@708 3514 if (pane->alignToReference(pframe) > playbackFrame) {
Chris@708 3515 success = true;
Chris@708 3516 break;
Chris@708 3517 } else {
Chris@708 3518 ++pframe;
Chris@708 3519 }
Chris@708 3520 }
Chris@708 3521
Chris@708 3522 if (success) {
Chris@708 3523 frame = pane->alignToReference(pframe);
Chris@85 3524 } else {
Chris@45 3525 frame = getMainModel()->getEndFrame();
Chris@45 3526 }
Chris@45 3527 }
Chris@45 3528
Chris@45 3529 if (frame < 0) frame = 0;
Chris@45 3530
Chris@45 3531 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3532 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3533 }
Chris@45 3534
Chris@45 3535 m_viewManager->setPlaybackFrame(frame);
Chris@166 3536
Chris@708 3537 if (frame >= getMainModel()->getEndFrame() &&
Chris@166 3538 m_playSource &&
Chris@166 3539 m_playSource->isPlaying() &&
Chris@166 3540 !m_viewManager->getPlayLoopMode()) {
Chris@166 3541 stop();
Chris@166 3542 }
Chris@45 3543 }
Chris@45 3544
Chris@45 3545 void
Chris@45 3546 MainWindowBase::ffwdEnd()
Chris@45 3547 {
Chris@45 3548 if (!getMainModel()) return;
Chris@45 3549
Chris@139 3550 if (m_playSource &&
Chris@139 3551 m_playSource->isPlaying() &&
Chris@139 3552 !m_viewManager->getPlayLoopMode()) {
Chris@139 3553 stop();
Chris@139 3554 }
Chris@139 3555
Chris@435 3556 sv_frame_t frame = getMainModel()->getEndFrame();
Chris@45 3557
Chris@45 3558 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3559 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3560 }
Chris@45 3561
Chris@45 3562 m_viewManager->setPlaybackFrame(frame);
Chris@45 3563 }
Chris@45 3564
Chris@45 3565 void
Chris@166 3566 MainWindowBase::ffwdSimilar()
Chris@166 3567 {
Chris@166 3568 if (!getMainModel()) return;
Chris@166 3569
Chris@166 3570 Layer *layer = getSnapLayer();
Chris@166 3571 if (!layer) { ffwd(); return; }
Chris@166 3572
Chris@166 3573 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3574 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3575
Chris@366 3576 int resolution = 0;
Chris@166 3577 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3578 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3579 frame, resolution, Layer::SnapRight)) {
Chris@166 3580 if (pane) frame = pane->alignToReference(frame);
Chris@166 3581 } else {
Chris@166 3582 frame = getMainModel()->getEndFrame();
Chris@166 3583 }
Chris@166 3584
Chris@166 3585 if (frame < 0) frame = 0;
Chris@166 3586
Chris@166 3587 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3588 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3589 }
Chris@166 3590
Chris@166 3591 m_viewManager->setPlaybackFrame(frame);
Chris@166 3592
Chris@435 3593 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3594 m_playSource &&
Chris@166 3595 m_playSource->isPlaying() &&
Chris@166 3596 !m_viewManager->getPlayLoopMode()) {
Chris@166 3597 stop();
Chris@166 3598 }
Chris@166 3599 }
Chris@166 3600
Chris@166 3601 void
Chris@45 3602 MainWindowBase::rewind()
Chris@45 3603 {
Chris@45 3604 if (!getMainModel()) return;
Chris@45 3605
Chris@708 3606 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
Chris@708 3607 sv_frame_t frame = playbackFrame;
Chris@45 3608 if (frame > 0) --frame;
Chris@45 3609
Chris@85 3610 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3611 Layer *layer = getSnapLayer();
Chris@435 3612 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3613
Chris@45 3614 // when rewinding during playback, we want to allow a period
Chris@45 3615 // following a rewind target point at which the rewind will go to
Chris@45 3616 // the prior point instead of the immediately neighbouring one
Chris@45 3617 if (m_playSource && m_playSource->isPlaying()) {
Chris@45 3618 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@357 3619 ct = ct - RealTime::fromSeconds(0.15);
Chris@45 3620 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@45 3621 frame = RealTime::realTime2Frame(ct, sr);
Chris@45 3622 }
Chris@45 3623
Chris@708 3624 if (!pane || !layer) {
Chris@45 3625
Chris@45 3626 frame = RealTime::realTime2Frame
Chris@357 3627 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
Chris@435 3628 if (frame < getMainModel()->getStartFrame()) {
Chris@45 3629 frame = getMainModel()->getStartFrame();
Chris@45 3630 }
Chris@45 3631
Chris@45 3632 } else {
Chris@45 3633
Chris@708 3634 sv_frame_t pframe = pane->alignFromReference(frame);
Chris@366 3635 int resolution = 0;
Chris@708 3636 bool success = false;
Chris@708 3637
Chris@708 3638 while (layer->snapToFeatureFrame(pane, pframe, resolution,
Chris@715 3639 Layer::SnapLeft, -1)) {
Chris@708 3640 if (pane->alignToReference(pframe) < playbackFrame ||
Chris@708 3641 pframe <= 0) {
Chris@708 3642 success = true;
Chris@708 3643 break;
Chris@708 3644 } else {
Chris@708 3645 --pframe;
Chris@708 3646 }
Chris@708 3647 }
Chris@708 3648
Chris@708 3649 if (success) {
Chris@708 3650 frame = pane->alignToReference(pframe);
Chris@85 3651 } else {
Chris@45 3652 frame = getMainModel()->getStartFrame();
Chris@45 3653 }
Chris@45 3654 }
Chris@45 3655
Chris@45 3656 if (frame < 0) frame = 0;
Chris@45 3657
Chris@45 3658 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3659 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3660 }
Chris@45 3661
Chris@45 3662 m_viewManager->setPlaybackFrame(frame);
Chris@45 3663 }
Chris@45 3664
Chris@45 3665 void
Chris@45 3666 MainWindowBase::rewindStart()
Chris@45 3667 {
Chris@45 3668 if (!getMainModel()) return;
Chris@45 3669
Chris@435 3670 sv_frame_t frame = getMainModel()->getStartFrame();
Chris@45 3671
Chris@45 3672 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3673 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3674 }
Chris@45 3675
Chris@45 3676 m_viewManager->setPlaybackFrame(frame);
Chris@45 3677 }
Chris@45 3678
Chris@166 3679 void
Chris@166 3680 MainWindowBase::rewindSimilar()
Chris@166 3681 {
Chris@166 3682 if (!getMainModel()) return;
Chris@166 3683
Chris@166 3684 Layer *layer = getSnapLayer();
Chris@166 3685 if (!layer) { rewind(); return; }
Chris@166 3686
Chris@166 3687 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3688 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3689
Chris@366 3690 int resolution = 0;
Chris@166 3691 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3692 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3693 frame, resolution, Layer::SnapLeft)) {
Chris@166 3694 if (pane) frame = pane->alignToReference(frame);
Chris@166 3695 } else {
Chris@166 3696 frame = getMainModel()->getStartFrame();
Chris@166 3697 }
Chris@166 3698
Chris@166 3699 if (frame < 0) frame = 0;
Chris@166 3700
Chris@166 3701 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3702 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3703 }
Chris@166 3704
Chris@166 3705 m_viewManager->setPlaybackFrame(frame);
Chris@166 3706 }
Chris@166 3707
Chris@45 3708 Layer *
Chris@45 3709 MainWindowBase::getSnapLayer() const
Chris@45 3710 {
Chris@45 3711 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 3712 if (!pane) return nullptr;
Chris@45 3713
Chris@45 3714 Layer *layer = pane->getSelectedLayer();
Chris@45 3715
Chris@45 3716 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@45 3717 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@194 3718 !dynamic_cast<RegionLayer *>(layer) &&
Chris@45 3719 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@45 3720
Chris@636 3721 layer = nullptr;
Chris@45 3722
Chris@45 3723 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 3724 Layer *l = pane->getLayer(i-1);
Chris@45 3725 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@45 3726 layer = l;
Chris@45 3727 break;
Chris@45 3728 }
Chris@45 3729 }
Chris@45 3730 }
Chris@45 3731
Chris@45 3732 return layer;
Chris@45 3733 }
Chris@45 3734
Chris@45 3735 void
Chris@45 3736 MainWindowBase::stop()
Chris@45 3737 {
Chris@516 3738 if (m_recordTarget &&
Chris@516 3739 m_recordTarget->isRecording()) {
Chris@477 3740 m_recordTarget->stopRecording();
Chris@477 3741 }
Chris@516 3742
Chris@516 3743 if (!m_playSource) return;
Chris@516 3744
Chris@45 3745 m_playSource->stop();
Chris@45 3746
Chris@611 3747 SVCERR << "MainWindowBase::stop: suspending" << endl;
Chris@611 3748
Chris@487 3749 if (m_audioIO) m_audioIO->suspend();
Chris@509 3750 else if (m_playTarget) m_playTarget->suspend();
Chris@487 3751
Chris@45 3752 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@45 3753 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@45 3754 } else {
Chris@45 3755 m_myStatusMessage = "";
Chris@378 3756 getStatusLabel()->setText("");
Chris@45 3757 }
Chris@45 3758 }
Chris@45 3759
Chris@45 3760 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
Chris@45 3761 m_mw(mw),
Chris@636 3762 m_pane(nullptr),
Chris@636 3763 m_prevCurrentPane(nullptr),
Chris@45 3764 m_added(false)
Chris@45 3765 {
Chris@45 3766 }
Chris@45 3767
Chris@45 3768 MainWindowBase::AddPaneCommand::~AddPaneCommand()
Chris@45 3769 {
Chris@45 3770 if (m_pane && !m_added) {
Chris@595 3771 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3772 }
Chris@45 3773 }
Chris@45 3774
Chris@45 3775 QString
Chris@45 3776 MainWindowBase::AddPaneCommand::getName() const
Chris@45 3777 {
Chris@45 3778 return tr("Add Pane");
Chris@45 3779 }
Chris@45 3780
Chris@45 3781 void
Chris@45 3782 MainWindowBase::AddPaneCommand::execute()
Chris@45 3783 {
Chris@45 3784 if (!m_pane) {
Chris@595 3785 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@595 3786 m_pane = m_mw->m_paneStack->addPane();
Chris@45 3787
Chris@45 3788 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 3789 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@45 3790 } else {
Chris@595 3791 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3792 }
Chris@45 3793
Chris@45 3794 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@45 3795 m_added = true;
Chris@45 3796 }
Chris@45 3797
Chris@45 3798 void
Chris@45 3799 MainWindowBase::AddPaneCommand::unexecute()
Chris@45 3800 {
Chris@45 3801 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3802 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3803 m_added = false;
Chris@45 3804 }
Chris@45 3805
Chris@45 3806 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
Chris@45 3807 m_mw(mw),
Chris@45 3808 m_pane(pane),
Chris@636 3809 m_prevCurrentPane(nullptr),
Chris@45 3810 m_added(true)
Chris@45 3811 {
Chris@45 3812 }
Chris@45 3813
Chris@45 3814 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
Chris@45 3815 {
Chris@45 3816 if (m_pane && !m_added) {
Chris@595 3817 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3818 }
Chris@45 3819 }
Chris@45 3820
Chris@45 3821 QString
Chris@45 3822 MainWindowBase::RemovePaneCommand::getName() const
Chris@45 3823 {
Chris@45 3824 return tr("Remove Pane");
Chris@45 3825 }
Chris@45 3826
Chris@45 3827 void
Chris@45 3828 MainWindowBase::RemovePaneCommand::execute()
Chris@45 3829 {
Chris@45 3830 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@45 3831 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3832 m_added = false;
Chris@45 3833 }
Chris@45 3834
Chris@45 3835 void
Chris@45 3836 MainWindowBase::RemovePaneCommand::unexecute()
Chris@45 3837 {
Chris@45 3838 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3839 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3840 m_added = true;
Chris@45 3841 }
Chris@45 3842
Chris@45 3843 void
Chris@45 3844 MainWindowBase::deleteCurrentPane()
Chris@45 3845 {
Chris@45 3846 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3847 (tr("Delete Pane"), true);
Chris@45 3848
Chris@45 3849 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3850 if (pane) {
Chris@595 3851 while (pane->getLayerCount() > 0) {
Chris@595 3852 Layer *layer = pane->getLayer(0);
Chris@595 3853 if (layer) {
Chris@595 3854 m_document->removeLayerFromView(pane, layer);
Chris@595 3855 } else {
Chris@595 3856 break;
Chris@595 3857 }
Chris@595 3858 }
Chris@595 3859
Chris@595 3860 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@595 3861 CommandHistory::getInstance()->addCommand(command);
Chris@45 3862 }
Chris@45 3863
Chris@45 3864 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 3865
Chris@45 3866 updateMenuStates();
Chris@45 3867 }
Chris@45 3868
Chris@45 3869 void
Chris@45 3870 MainWindowBase::deleteCurrentLayer()
Chris@45 3871 {
Chris@45 3872 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3873 if (pane) {
Chris@595 3874 Layer *layer = pane->getSelectedLayer();
Chris@595 3875 if (layer) {
Chris@595 3876 m_document->removeLayerFromView(pane, layer);
Chris@595 3877 }
Chris@45 3878 }
Chris@45 3879 updateMenuStates();
Chris@45 3880 }
Chris@45 3881
Chris@45 3882 void
Chris@123 3883 MainWindowBase::editCurrentLayer()
Chris@123 3884 {
Chris@636 3885 Layer *layer = nullptr;
Chris@123 3886 Pane *pane = m_paneStack->getCurrentPane();
Chris@123 3887 if (pane) layer = pane->getSelectedLayer();
Chris@123 3888 if (!layer) return;
Chris@123 3889
Chris@684 3890 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
Chris@124 3891 if (!tabular) {
Chris@124 3892 //!!! how to prevent this function from being active if not
Chris@124 3893 //appropriate model type? or will we ultimately support
Chris@124 3894 //tabular display for all editable models?
Chris@233 3895 SVDEBUG << "NOTE: Not a tabular model" << endl;
Chris@124 3896 return;
Chris@124 3897 }
Chris@124 3898
Chris@123 3899 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@126 3900 if (!m_layerDataDialogMap[layer].isNull()) {
Chris@126 3901 m_layerDataDialogMap[layer]->show();
Chris@126 3902 m_layerDataDialogMap[layer]->raise();
Chris@126 3903 return;
Chris@126 3904 }
Chris@123 3905 }
Chris@123 3906
Chris@125 3907 QString title = layer->getLayerPresentationName();
Chris@125 3908
Chris@684 3909 ModelDataTableDialog *dialog = new ModelDataTableDialog
Chris@684 3910 (layer->getModel(), title, this);
Chris@128 3911 dialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@128 3912
Chris@128 3913 connectLayerEditDialog(dialog);
Chris@123 3914
Chris@128 3915 m_layerDataDialogMap[layer] = dialog;
Chris@128 3916 m_viewDataDialogMap[pane].insert(dialog);
Chris@128 3917
Chris@128 3918 dialog->show();
Chris@128 3919 }
Chris@128 3920
Chris@128 3921 void
Chris@128 3922 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@128 3923 {
Chris@123 3924 connect(m_viewManager,
Chris@435 3925 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@123 3926 dialog,
Chris@435 3927 SLOT(userScrolledToFrame(sv_frame_t)));
Chris@127 3928
Chris@127 3929 connect(m_viewManager,
Chris@435 3930 SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@127 3931 dialog,
Chris@435 3932 SLOT(playbackScrolledToFrame(sv_frame_t)));
Chris@127 3933
Chris@123 3934 connect(dialog,
Chris@435 3935 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@123 3936 m_viewManager,
Chris@435 3937 SLOT(setGlobalCentreFrame(sv_frame_t)));
Chris@129 3938
Chris@129 3939 connect(dialog,
Chris@435 3940 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@129 3941 m_viewManager,
Chris@435 3942 SLOT(setPlaybackFrame(sv_frame_t)));
Chris@128 3943 }
Chris@123 3944
Chris@123 3945 void
Chris@73 3946 MainWindowBase::previousPane()
Chris@73 3947 {
Chris@73 3948 if (!m_paneStack) return;
Chris@73 3949
Chris@73 3950 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3951 if (!currentPane) return;
Chris@73 3952
Chris@73 3953 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3954 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3955 if (i == 0) return;
Chris@73 3956 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
Chris@73 3957 updateMenuStates();
Chris@73 3958 return;
Chris@73 3959 }
Chris@73 3960 }
Chris@73 3961 }
Chris@73 3962
Chris@73 3963 void
Chris@73 3964 MainWindowBase::nextPane()
Chris@73 3965 {
Chris@73 3966 if (!m_paneStack) return;
Chris@73 3967
Chris@73 3968 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3969 if (!currentPane) return;
Chris@73 3970
Chris@73 3971 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3972 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3973 if (i == m_paneStack->getPaneCount()-1) return;
Chris@73 3974 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
Chris@73 3975 updateMenuStates();
Chris@73 3976 return;
Chris@73 3977 }
Chris@73 3978 }
Chris@73 3979 }
Chris@73 3980
Chris@73 3981 void
Chris@73 3982 MainWindowBase::previousLayer()
Chris@73 3983 {
Chris@73 3984 if (!m_paneStack) return;
Chris@73 3985
Chris@73 3986 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3987 if (!currentPane) return;
Chris@73 3988
Chris@403 3989 int count = currentPane->getLayerCount();
Chris@403 3990 if (count == 0) return;
Chris@403 3991
Chris@73 3992 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3993
Chris@403 3994 if (!currentLayer) {
Chris@403 3995 // The pane itself is current
Chris@403 3996 m_paneStack->setCurrentLayer
Chris@403 3997 (currentPane, currentPane->getFixedOrderLayer(count-1));
Chris@403 3998 } else {
Chris@403 3999 for (int i = 0; i < count; ++i) {
Chris@403 4000 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 4001 if (i == 0) {
Chris@403 4002 m_paneStack->setCurrentLayer
Chris@636 4003 (currentPane, nullptr); // pane
Chris@403 4004 } else {
Chris@403 4005 m_paneStack->setCurrentLayer
Chris@403 4006 (currentPane, currentPane->getFixedOrderLayer(i-1));
Chris@403 4007 }
Chris@403 4008 break;
Chris@403 4009 }
Chris@73 4010 }
Chris@73 4011 }
Chris@403 4012
Chris@403 4013 updateMenuStates();
Chris@73 4014 }
Chris@73 4015
Chris@73 4016 void
Chris@73 4017 MainWindowBase::nextLayer()
Chris@73 4018 {
Chris@73 4019 if (!m_paneStack) return;
Chris@73 4020
Chris@73 4021 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 4022 if (!currentPane) return;
Chris@73 4023
Chris@403 4024 int count = currentPane->getLayerCount();
Chris@403 4025 if (count == 0) return;
Chris@403 4026
Chris@73 4027 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 4028
Chris@403 4029 if (!currentLayer) {
Chris@403 4030 // The pane itself is current
Chris@403 4031 m_paneStack->setCurrentLayer
Chris@403 4032 (currentPane, currentPane->getFixedOrderLayer(0));
Chris@403 4033 } else {
Chris@403 4034 for (int i = 0; i < count; ++i) {
Chris@403 4035 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 4036 if (i == currentPane->getLayerCount()-1) {
Chris@403 4037 m_paneStack->setCurrentLayer
Chris@636 4038 (currentPane, nullptr); // pane
Chris@403 4039 } else {
Chris@403 4040 m_paneStack->setCurrentLayer
Chris@403 4041 (currentPane, currentPane->getFixedOrderLayer(i+1));
Chris@403 4042 }
Chris@403 4043 break;
Chris@403 4044 }
Chris@73 4045 }
Chris@73 4046 }
Chris@403 4047
Chris@403 4048 updateMenuStates();
Chris@73 4049 }
Chris@73 4050
Chris@73 4051 void
Chris@435 4052 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
Chris@45 4053 {
Chris@45 4054 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@45 4055
Chris@187 4056 updatePositionStatusDisplays();
Chris@187 4057
Chris@45 4058 RealTime now = RealTime::frame2RealTime
Chris@45 4059 (frame, getMainModel()->getSampleRate());
Chris@45 4060
Chris@45 4061 if (now.sec == m_lastPlayStatusSec) return;
Chris@45 4062
Chris@45 4063 RealTime then = RealTime::frame2RealTime
Chris@45 4064 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@45 4065
Chris@45 4066 QString nowStr;
Chris@45 4067 QString thenStr;
Chris@45 4068 QString remainingStr;
Chris@45 4069
Chris@45 4070 if (then.sec > 10) {
Chris@45 4071 nowStr = now.toSecText().c_str();
Chris@45 4072 thenStr = then.toSecText().c_str();
Chris@45 4073 remainingStr = (then - now).toSecText().c_str();
Chris@45 4074 m_lastPlayStatusSec = now.sec;
Chris@45 4075 } else {
Chris@45 4076 nowStr = now.toText(true).c_str();
Chris@45 4077 thenStr = then.toText(true).c_str();
Chris@45 4078 remainingStr = (then - now).toText(true).c_str();
Chris@45 4079 }
Chris@45 4080
Chris@45 4081 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@45 4082 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@45 4083
Chris@378 4084 getStatusLabel()->setText(m_myStatusMessage);
Chris@45 4085 }
Chris@45 4086
Chris@45 4087 void
Chris@486 4088 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
Chris@486 4089 {
Chris@486 4090 RealTime duration = RealTime::frame2RealTime(frame, rate);
Chris@486 4091 QString durStr = duration.toSecText().c_str();
Chris@486 4092
Chris@486 4093 m_myStatusMessage = tr("Recording: %1").arg(durStr);
Chris@486 4094
Chris@486 4095 getStatusLabel()->setText(m_myStatusMessage);
Chris@486 4096 }
Chris@486 4097
Chris@486 4098 void
Chris@435 4099 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
Chris@45 4100 {
Chris@45 4101 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4102 Pane *p = nullptr;
Chris@45 4103 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4104 if (!p->getFollowGlobalPan()) return;
Chris@45 4105 updateVisibleRangeDisplay(p);
Chris@45 4106 }
Chris@45 4107
Chris@45 4108 void
Chris@435 4109 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
Chris@45 4110 {
Chris@233 4111 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
Chris@123 4112
Chris@123 4113 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
Chris@123 4114 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
Chris@123 4115 i != m_viewDataDialogMap[v].end(); ++i) {
Chris@127 4116 (*i)->userScrolledToFrame(frame);
Chris@123 4117 }
Chris@123 4118 }
Chris@45 4119 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4120 Pane *p = nullptr;
Chris@45 4121 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4122 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4123 }
Chris@45 4124
Chris@45 4125 void
Chris@624 4126 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
Chris@45 4127 {
Chris@45 4128 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 4129 Pane *p = nullptr;
Chris@45 4130 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 4131 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 4132 }
Chris@45 4133
Chris@45 4134 void
Chris@45 4135 MainWindowBase::layerAdded(Layer *)
Chris@45 4136 {
Chris@233 4137 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
Chris@45 4138 updateMenuStates();
Chris@45 4139 }
Chris@45 4140
Chris@45 4141 void
Chris@45 4142 MainWindowBase::layerRemoved(Layer *)
Chris@45 4143 {
Chris@233 4144 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
Chris@45 4145 updateMenuStates();
Chris@45 4146 }
Chris@45 4147
Chris@45 4148 void
Chris@45 4149 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
Chris@45 4150 {
Chris@233 4151 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
Chris@123 4152
Chris@128 4153 removeLayerEditDialog(layer);
Chris@123 4154
Chris@47 4155 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
Chris@595 4156 // cerr << "(this is the time ruler layer)" << endl;
Chris@636 4157 m_timeRulerLayer = nullptr;
Chris@45 4158 }
Chris@45 4159 }
Chris@45 4160
Chris@45 4161 void
Chris@45 4162 MainWindowBase::layerInAView(Layer *layer, bool inAView)
Chris@45 4163 {
Chris@233 4164 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
Chris@128 4165
Chris@128 4166 if (!inAView) removeLayerEditDialog(layer);
Chris@45 4167
Chris@45 4168 // Check whether we need to add or remove model from play source
Chris@684 4169 ModelId modelId = layer->getModel();
Chris@684 4170 if (!modelId.isNone()) {
Chris@45 4171 if (inAView) {
Chris@684 4172 m_playSource->addModel(modelId);
Chris@45 4173 } else {
Chris@45 4174 bool found = false;
Chris@45 4175 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 4176 Pane *pane = m_paneStack->getPane(i);
Chris@45 4177 if (!pane) continue;
Chris@45 4178 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@45 4179 Layer *pl = pane->getLayer(j);
Chris@183 4180 if (pl &&
Chris@183 4181 !dynamic_cast<TimeRulerLayer *>(pl) &&
Chris@684 4182 (pl->getModel() == modelId)) {
Chris@45 4183 found = true;
Chris@45 4184 break;
Chris@45 4185 }
Chris@45 4186 }
Chris@45 4187 if (found) break;
Chris@45 4188 }
Chris@173 4189 if (!found) {
Chris@684 4190 m_playSource->removeModel(modelId);
Chris@173 4191 }
Chris@45 4192 }
Chris@45 4193 }
Chris@45 4194
Chris@45 4195 updateMenuStates();
Chris@45 4196 }
Chris@45 4197
Chris@45 4198 void
Chris@128 4199 MainWindowBase::removeLayerEditDialog(Layer *layer)
Chris@128 4200 {
Chris@128 4201 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@128 4202
Chris@128 4203 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
Chris@128 4204
Chris@128 4205 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
Chris@128 4206 vi != m_viewDataDialogMap.end(); ++vi) {
Chris@128 4207 vi->second.erase(dialog);
Chris@128 4208 }
Chris@128 4209
Chris@128 4210 m_layerDataDialogMap.erase(layer);
Chris@128 4211 delete dialog;
Chris@128 4212 }
Chris@128 4213 }
Chris@128 4214
Chris@128 4215 void
Chris@684 4216 MainWindowBase::modelAdded(ModelId model)
Chris@45 4217 {
Chris@233 4218 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
Chris@684 4219 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
Chris@45 4220 m_playSource->addModel(model);
Chris@45 4221 }
Chris@45 4222
Chris@45 4223 void
Chris@684 4224 MainWindowBase::mainModelChanged(ModelId modelId)
Chris@45 4225 {
Chris@233 4226 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
Chris@45 4227 updateDescriptionLabel();
Chris@684 4228 auto model = ModelById::getAs<WaveFileModel>(modelId);
Chris@45 4229 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@714 4230 if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
Chris@475 4231 createAudioIO();
Chris@360 4232 }
Chris@45 4233 }
Chris@45 4234
Chris@45 4235 void
Chris@55 4236 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
Chris@55 4237 {
Chris@55 4238 bool found = false;
Chris@55 4239 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@55 4240 if (m_paneStack->getPane(i) == pane) {
Chris@55 4241 found = true;
Chris@55 4242 break;
Chris@55 4243 }
Chris@55 4244 }
Chris@55 4245 if (!found) {
Chris@233 4246 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
Chris@229 4247 << pane << endl;
Chris@55 4248 return;
Chris@55 4249 }
Chris@55 4250
Chris@55 4251 CommandHistory::getInstance()->startCompoundOperation
Chris@595 4252 (tr("Delete Pane"), true);
Chris@55 4253
Chris@55 4254 while (pane->getLayerCount() > 0) {
Chris@637 4255 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@55 4256 if (layer) {
Chris@55 4257 m_document->removeLayerFromView(pane, layer);
Chris@55 4258 } else {
Chris@55 4259 break;
Chris@55 4260 }
Chris@55 4261 }
Chris@55 4262
Chris@55 4263 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@55 4264 CommandHistory::getInstance()->addCommand(command);
Chris@55 4265
Chris@55 4266 CommandHistory::getInstance()->endCompoundOperation();
Chris@55 4267
Chris@55 4268 updateMenuStates();
Chris@55 4269 }
Chris@55 4270
Chris@55 4271 void
Chris@684 4272 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
Chris@429 4273 {
Chris@684 4274 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
Chris@429 4275 }
Chris@429 4276
Chris@429 4277 void
Chris@45 4278 MainWindowBase::pollOSC()
Chris@45 4279 {
Chris@45 4280 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@725 4281
Chris@725 4282 while (!m_oscQueue->isEmpty()) {
Chris@725 4283
Chris@725 4284 if (m_openingAudioFile) {
Chris@725 4285 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4286 << "waiting for audio to finish loading"
Chris@725 4287 << endl;
Chris@725 4288 return;
Chris@725 4289 }
Chris@725 4290
Chris@725 4291 if (ModelTransformerFactory::getInstance()->haveRunningTransformers()) {
Chris@725 4292 SVDEBUG << "MainWindowBase::pollOSC: "
Chris@725 4293 << "waiting for running transforms to complete"
Chris@725 4294 << endl;
Chris@725 4295 return;
Chris@725 4296 }
Chris@725 4297
Chris@725 4298 SVDEBUG << "MainWindowBase::pollOSC: have "
Chris@725 4299 << m_oscQueue->getMessagesAvailable()
Chris@725 4300 << " messages" << endl;
Chris@725 4301
Chris@725 4302 OSCMessage message = m_oscQueue->readMessage();
Chris@725 4303
Chris@725 4304 if (message.getTarget() != 0) {
Chris@725 4305 SVCERR << "MainWindowBase::pollOSC: ignoring message with target "
Chris@725 4306 << message.getTarget() << " (we are target 0)" << endl;
Chris@725 4307 continue;
Chris@725 4308 }
Chris@725 4309
Chris@725 4310 handleOSCMessage(message);
Chris@725 4311
Chris@725 4312 disconnect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@725 4313 this, SLOT(pollOSC()));
Chris@725 4314
Chris@725 4315 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
Chris@725 4316 QEventLoop::ExcludeSocketNotifiers);
Chris@725 4317
Chris@725 4318 connect(m_oscQueue, SIGNAL(messagesAvailable()),
Chris@725 4319 this, SLOT(pollOSC()));
Chris@45 4320 }
Chris@45 4321 }
Chris@45 4322
Chris@45 4323 void
Chris@45 4324 MainWindowBase::inProgressSelectionChanged()
Chris@45 4325 {
Chris@636 4326 Pane *currentPane = nullptr;
Chris@45 4327 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
justin@331 4328 if (currentPane) {
justin@331 4329 //cerr << "JTEST: mouse event on selection pane" << endl;
justin@331 4330 updateVisibleRangeDisplay(currentPane);
justin@331 4331 }
Chris@45 4332 }
Chris@45 4333
Chris@45 4334 void
Chris@45 4335 MainWindowBase::contextHelpChanged(const QString &s)
Chris@45 4336 {
Chris@378 4337 QLabel *lab = getStatusLabel();
Chris@375 4338
Chris@45 4339 if (s == "" && m_myStatusMessage != "") {
Chris@378 4340 if (lab->text() != m_myStatusMessage) {
Chris@378 4341 lab->setText(m_myStatusMessage);
Chris@375 4342 }
Chris@45 4343 return;
Chris@45 4344 }
Chris@375 4345
Chris@378 4346 lab->setText(s);
Chris@45 4347 }
Chris@45 4348
Chris@45 4349 void
Chris@45 4350 MainWindowBase::openHelpUrl(QString url)
Chris@45 4351 {
Chris@45 4352 // This method mostly lifted from Qt Assistant source code
Chris@45 4353
Chris@45 4354 QProcess *process = new QProcess(this);
Chris@45 4355 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@45 4356
Chris@45 4357 QStringList args;
Chris@45 4358
Chris@45 4359 #ifdef Q_OS_MAC
Chris@45 4360 args.append(url);
Chris@45 4361 process->start("open", args);
Chris@45 4362 #else
Chris@45 4363 #ifdef Q_OS_WIN32
Chris@599 4364 std::string pfiles;
Chris@599 4365 (void)getEnvUtf8("ProgramFiles", pfiles);
Chris@599 4366 QString command =
Chris@599 4367 QString::fromStdString(pfiles) +
Chris@599 4368 QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@358 4369
Chris@358 4370 args.append(url);
Chris@358 4371 process->start(command, args);
Chris@45 4372 #else
Chris@45 4373 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@45 4374 args.append("exec");
Chris@45 4375 args.append(url);
Chris@45 4376 process->start("kfmclient", args);
Chris@45 4377 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@45 4378 args.append(url);
Chris@45 4379 process->start(qgetenv("BROWSER"), args);
Chris@45 4380 } else {
Chris@45 4381 args.append(url);
Chris@45 4382 process->start("firefox", args);
Chris@45 4383 }
Chris@45 4384 #endif
Chris@45 4385 #endif
Chris@45 4386 }
Chris@45 4387
Chris@483 4388 void
Chris@483 4389 MainWindowBase::openLocalFolder(QString path)
Chris@483 4390 {
Chris@483 4391 QDir d(path);
Chris@483 4392 if (d.exists()) {
Chris@483 4393 QStringList args;
Chris@483 4394 QString path = d.canonicalPath();
Chris@483 4395 #if defined Q_OS_WIN32
Chris@483 4396 // Although the Win32 API is quite happy to have
Chris@483 4397 // forward slashes as directory separators, Windows
Chris@483 4398 // Explorer is not
Chris@483 4399 path = path.replace('/', '\\');
Chris@483 4400 args << path;
Chris@483 4401 QProcess::execute("c:/windows/explorer.exe", args);
Chris@483 4402 #else
Chris@483 4403 args << path;
Chris@605 4404 QProcess process;
Chris@605 4405 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@606 4406 env.insert("LD_LIBRARY_PATH", "");
Chris@605 4407 process.setProcessEnvironment(env);
Chris@605 4408 process.start(
Chris@483 4409 #if defined Q_OS_MAC
Chris@483 4410 "/usr/bin/open",
Chris@483 4411 #else
Chris@483 4412 "/usr/bin/xdg-open",
Chris@483 4413 #endif
Chris@483 4414 args);
Chris@608 4415 process.waitForFinished();
Chris@483 4416 #endif
Chris@483 4417 }
Chris@483 4418 }
Chris@483 4419