annotate framework/MainWindowBase.cpp @ 733:a39538eaed9d background-mode

Follow desktop theme (on Windows)
author Chris Cannam
date Fri, 17 Jan 2020 21:38:43 +0000
parents c980a0300c58
children de7969894402
rev   line source
Chris@45 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@45 2
Chris@45 3 /*
Chris@45 4 Sonic Visualiser
Chris@45 5 An audio file viewer and annotation editor.
Chris@45 6 Centre for Digital Music, Queen Mary, University of London.
Chris@45 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@45 8
Chris@45 9 This program is free software; you can redistribute it and/or
Chris@45 10 modify it under the terms of the GNU General Public License as
Chris@45 11 published by the Free Software Foundation; either version 2 of the
Chris@45 12 License, or (at your option) any later version. See the file
Chris@45 13 COPYING included with this distribution for more information.
Chris@45 14 */
Chris@45 15
Chris@45 16 #include "MainWindowBase.h"
Chris@46 17 #include "Document.h"
Chris@45 18
Chris@45 19 #include "view/Pane.h"
Chris@45 20 #include "view/PaneStack.h"
Chris@479 21 #include "data/model/ReadOnlyWaveFileModel.h"
Chris@477 22 #include "data/model/WritableWaveFileModel.h"
Chris@45 23 #include "data/model/SparseOneDimensionalModel.h"
Chris@45 24 #include "data/model/NoteModel.h"
Chris@45 25 #include "data/model/Labeller.h"
Chris@124 26 #include "data/model/TabularModel.h"
Chris@45 27 #include "view/ViewManager.h"
Chris@45 28
Chris@45 29 #include "layer/WaveformLayer.h"
Chris@45 30 #include "layer/TimeRulerLayer.h"
Chris@45 31 #include "layer/TimeInstantLayer.h"
Chris@45 32 #include "layer/TimeValueLayer.h"
Chris@45 33 #include "layer/Colour3DPlotLayer.h"
Chris@45 34 #include "layer/SliceLayer.h"
Chris@45 35 #include "layer/SliceableLayer.h"
Chris@45 36 #include "layer/ImageLayer.h"
Chris@184 37 #include "layer/NoteLayer.h"
matthiasm@267 38 #include "layer/FlexiNoteLayer.h"
Chris@184 39 #include "layer/RegionLayer.h"
Chris@722 40 #include "layer/SpectrogramLayer.h"
Chris@45 41
Chris@45 42 #include "widgets/ListInputDialog.h"
Chris@105 43 #include "widgets/CommandHistory.h"
Chris@109 44 #include "widgets/ProgressDialog.h"
Chris@109 45 #include "widgets/MIDIFileImportDialog.h"
Chris@109 46 #include "widgets/CSVFormatDialog.h"
Chris@123 47 #include "widgets/ModelDataTableDialog.h"
Chris@341 48 #include "widgets/InteractiveFileFinder.h"
Chris@45 49
Chris@468 50 #include "audio/AudioCallbackPlaySource.h"
Chris@574 51 #include "audio/AudioCallbackRecordTarget.h"
Chris@468 52 #include "audio/PlaySpeedRangeMapper.h"
Chris@475 53
Chris@45 54 #include "data/fileio/DataFileReaderFactory.h"
Chris@45 55 #include "data/fileio/PlaylistFileReader.h"
Chris@45 56 #include "data/fileio/WavFileWriter.h"
Chris@45 57 #include "data/fileio/MIDIFileWriter.h"
Chris@659 58 #include "data/fileio/CSVFileWriter.h"
Chris@45 59 #include "data/fileio/BZipFileDevice.h"
Chris@45 60 #include "data/fileio/FileSource.h"
Chris@152 61 #include "data/fileio/AudioFileReaderFactory.h"
Chris@134 62 #include "rdf/RDFImporter.h"
Chris@659 63 #include "rdf/RDFExporter.h"
Chris@45 64
Chris@724 65 #include "transform/ModelTransformerFactory.h"
Chris@724 66
Chris@45 67 #include "base/RecentFiles.h"
Chris@45 68
Chris@45 69 #include "base/XmlExportable.h"
Chris@45 70 #include "base/Profiler.h"
Chris@45 71 #include "base/Preferences.h"
Chris@217 72 #include "base/TempWriteFile.h"
Chris@217 73 #include "base/Exceptions.h"
Chris@223 74 #include "base/ResourceFinder.h"
Chris@45 75
Chris@45 76 #include "data/osc/OSCQueue.h"
Chris@157 77 #include "data/midi/MIDIInput.h"
Chris@654 78 #include "OSCScript.h"
Chris@45 79
Chris@599 80 #include "system/System.h"
Chris@599 81
Chris@468 82 #include <bqaudioio/SystemPlaybackTarget.h>
Chris@475 83 #include <bqaudioio/SystemAudioIO.h>
Chris@468 84 #include <bqaudioio/AudioFactory.h>
Chris@551 85 #include <bqaudioio/ResamplerWrapper.h>
Chris@468 86
Chris@45 87 #include <QApplication>
Chris@45 88 #include <QMessageBox>
Chris@45 89 #include <QGridLayout>
Chris@45 90 #include <QLabel>
Chris@45 91 #include <QAction>
Chris@45 92 #include <QMenuBar>
Chris@45 93 #include <QToolBar>
Chris@45 94 #include <QInputDialog>
Chris@45 95 #include <QStatusBar>
Chris@45 96 #include <QTreeView>
Chris@45 97 #include <QFile>
Chris@45 98 #include <QFileInfo>
Chris@45 99 #include <QDir>
Chris@45 100 #include <QTextStream>
Chris@432 101 #include <QTextCodec>
Chris@45 102 #include <QProcess>
Chris@45 103 #include <QShortcut>
Chris@45 104 #include <QSettings>
Chris@45 105 #include <QDateTime>
Chris@45 106 #include <QProcess>
Chris@45 107 #include <QCheckBox>
Chris@45 108 #include <QRegExp>
Chris@45 109 #include <QScrollArea>
Chris@684 110 #include <QScreen>
Chris@354 111 #include <QSignalMapper>
Chris@45 112
Chris@45 113 #include <iostream>
Chris@45 114 #include <cstdio>
Chris@45 115 #include <errno.h>
Chris@45 116
Chris@45 117 using std::vector;
Chris@45 118 using std::map;
Chris@45 119 using std::set;
Chris@45 120
Chris@255 121 #ifdef Q_WS_X11
Chris@255 122 #define Window X11Window
Chris@255 123 #include <X11/Xlib.h>
Chris@255 124 #include <X11/Xutil.h>
Chris@255 125 #include <X11/Xatom.h>
Chris@255 126 #include <X11/SM/SMlib.h>
Chris@255 127
Chris@255 128 static int handle_x11_error(Display *dpy, XErrorEvent *err)
Chris@255 129 {
Chris@255 130 char errstr[256];
Chris@255 131 XGetErrorText(dpy, err->error_code, errstr, 256);
Chris@255 132 if (err->error_code != BadWindow) {
Chris@595 133 cerr << "Sonic Visualiser: X Error: "
Chris@595 134 << errstr << " " << int(err->error_code)
Chris@595 135 << "\nin major opcode: "
Chris@595 136 << int(err->request_code) << endl;
Chris@255 137 }
Chris@255 138 return 0;
Chris@255 139 }
Chris@255 140 #undef Window
Chris@255 141 #endif
Chris@45 142
Chris@714 143 MainWindowBase::MainWindowBase(AudioMode audioMode,
Chris@714 144 MIDIMode midiMode,
Chris@712 145 PaneStack::Options paneStackOptions) :
Chris@636 146 m_document(nullptr),
Chris@636 147 m_paneStack(nullptr),
Chris@636 148 m_viewManager(nullptr),
Chris@636 149 m_timeRulerLayer(nullptr),
Chris@714 150 m_audioMode(audioMode),
Chris@714 151 m_midiMode(midiMode),
Chris@636 152 m_playSource(nullptr),
Chris@636 153 m_recordTarget(nullptr),
Chris@636 154 m_resamplerWrapper(nullptr),
Chris@636 155 m_playTarget(nullptr),
Chris@636 156 m_audioIO(nullptr),
Chris@636 157 m_oscQueue(nullptr),
Chris@636 158 m_oscQueueStarter(nullptr),
Chris@654 159 m_oscScript(nullptr),
Chris@636 160 m_midiInput(nullptr),
Chris@45 161 m_recentFiles("RecentFiles", 20),
Chris@54 162 m_recentTransforms("RecentTransforms", 20),
Chris@45 163 m_documentModified(false),
Chris@45 164 m_openingAudioFile(false),
Chris@636 165 m_labeller(nullptr),
Chris@357 166 m_lastPlayStatusSec(0),
Chris@357 167 m_initialDarkBackground(false),
Chris@378 168 m_defaultFfwdRwdStep(2, 0),
Chris@483 169 m_audioRecordMode(RecordCreateAdditionalModel),
Chris@636 170 m_statusLabel(nullptr),
Chris@426 171 m_iconsVisibleInMenus(true),
Chris@636 172 m_menuShortcutMapper(nullptr)
Chris@45 173 {
Chris@113 174 Profiler profiler("MainWindowBase::MainWindowBase");
Chris@113 175
Chris@591 176 SVDEBUG << "MainWindowBase::MainWindowBase" << endl;
Chris@591 177
Chris@438 178 qRegisterMetaType<sv_frame_t>("sv_frame_t");
Chris@438 179 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
Chris@687 180 qRegisterMetaType<ModelId>("ModelId");
Chris@438 181
Chris@255 182 #ifdef Q_WS_X11
Chris@255 183 XSetErrorHandler(handle_x11_error);
Chris@255 184 #endif
Chris@255 185
Chris@452 186 connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash()));
Chris@452 187
Chris@45 188 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
Chris@595 189 this, SLOT(documentModified()));
Chris@45 190 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
Chris@595 191 this, SLOT(documentRestored()));
Chris@45 192
Chris@591 193 SVDEBUG << "MainWindowBase: Creating view manager" << endl;
Chris@591 194
Chris@45 195 m_viewManager = new ViewManager();
Chris@45 196 connect(m_viewManager, SIGNAL(selectionChanged()),
Chris@595 197 this, SLOT(updateMenuStates()));
Chris@45 198 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
Chris@595 199 this, SLOT(inProgressSelectionChanged()));
Chris@45 200
Chris@591 201 SVDEBUG << "MainWindowBase: Calculating view font size" << endl;
Chris@591 202
Chris@105 203 // set a sensible default font size for views -- cannot do this
Chris@105 204 // in Preferences, which is in base and not supposed to use QtGui
Chris@436 205 int viewFontSize = int(QApplication::font().pointSize() * 0.9);
Chris@105 206 QSettings settings;
Chris@105 207 settings.beginGroup("Preferences");
Chris@105 208 viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
Chris@105 209 settings.setValue("view-font-size", viewFontSize);
Chris@105 210 settings.endGroup();
Chris@105 211
Chris@591 212 SVDEBUG << "MainWindowBase: View font size is " << viewFontSize << endl;
Chris@591 213
Chris@732 214 #ifndef Q_OS_MAC
Chris@45 215 Preferences::BackgroundMode mode =
Chris@45 216 Preferences::getInstance()->getBackgroundMode();
Chris@733 217 if (mode == Preferences::BackgroundFromTheme && OSThemeIsDark()) {
Chris@733 218 mode = Preferences::DarkBackground;
Chris@733 219 }
Chris@45 220 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
Chris@45 221 if (mode != Preferences::BackgroundFromTheme) {
Chris@45 222 m_viewManager->setGlobalDarkBackground
Chris@45 223 (mode == Preferences::DarkBackground);
Chris@45 224 }
Chris@511 225 #endif
Chris@45 226
Chris@712 227 m_paneStack = new PaneStack(nullptr, m_viewManager, paneStackOptions);
Chris@45 228 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
Chris@595 229 this, SLOT(currentPaneChanged(Pane *)));
Chris@45 230 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
Chris@595 231 this, SLOT(currentLayerChanged(Pane *, Layer *)));
Chris@45 232 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
Chris@45 233 this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
Chris@45 234 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 235 this, SLOT(contextHelpChanged(const QString &)));
Chris@45 236 connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
Chris@45 237 this, SLOT(paneAdded(Pane *)));
Chris@45 238 connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
Chris@45 239 this, SLOT(paneHidden(Pane *)));
Chris@45 240 connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
Chris@45 241 this, SLOT(paneAboutToBeDeleted(Pane *)));
Chris@45 242 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
Chris@45 243 this, SLOT(paneDropAccepted(Pane *, QStringList)));
Chris@45 244 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
Chris@45 245 this, SLOT(paneDropAccepted(Pane *, QString)));
Chris@55 246 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
Chris@55 247 this, SLOT(paneDeleteButtonClicked(Pane *)));
Chris@591 248
Chris@591 249 SVDEBUG << "MainWindowBase: Creating play source" << endl;
Chris@571 250
Chris@574 251 m_playSource = new AudioCallbackPlaySource
Chris@574 252 (m_viewManager, QApplication::applicationName());
Chris@574 253
Chris@714 254 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 255 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@591 256 SVDEBUG << "MainWindowBase: Creating record target" << endl;
Chris@574 257 m_recordTarget = new AudioCallbackRecordTarget
Chris@574 258 (m_viewManager, QApplication::applicationName());
Chris@572 259 connect(m_recordTarget,
Chris@572 260 SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
Chris@572 261 this,
Chris@572 262 SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
Chris@475 263 }
Chris@45 264
Chris@436 265 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
Chris@591 266 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
Chris@570 267 connect(m_playSource, SIGNAL(channelCountIncreased(int)),
Chris@570 268 this, SLOT(audioChannelCountIncreased(int)));
Chris@45 269 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
Chris@45 270 this, SLOT(audioOverloadPluginDisabled()));
Chris@130 271 connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()),
Chris@130 272 this, SLOT(audioTimeStretchMultiChannelDisabled()));
Chris@45 273
Chris@574 274 connect(m_viewManager, SIGNAL(monitoringLevelsChanged(float, float)),
Chris@595 275 this, SLOT(monitoringLevelsChanged(float, float)));
Chris@45 276
Chris@435 277 connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@435 278 this, SLOT(playbackFrameChanged(sv_frame_t)));
Chris@435 279
Chris@435 280 connect(m_viewManager, SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@435 281 this, SLOT(globalCentreFrameChanged(sv_frame_t)));
Chris@435 282
Chris@435 283 connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)),
Chris@435 284 this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
Chris@366 285
Chris@640 286 connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, ZoomLevel, bool)),
Chris@640 287 this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
Chris@45 288
Chris@45 289 connect(Preferences::getInstance(),
Chris@45 290 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@45 291 this,
Chris@45 292 SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@45 293
Chris@591 294 SVDEBUG << "MainWindowBase: Creating labeller" << endl;
Chris@591 295
Chris@45 296 Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
Chris@45 297 settings.beginGroup("MainWindow");
Chris@230 298
Chris@45 299 labellerType = (Labeller::ValueType)
Chris@45 300 settings.value("labellertype", (int)labellerType).toInt();
Chris@45 301 int cycle = settings.value("labellercycle", 4).toInt();
Chris@230 302
Chris@45 303 settings.endGroup();
Chris@45 304
Chris@45 305 m_labeller = new Labeller(labellerType);
Chris@45 306 m_labeller->setCounterCycleSize(cycle);
Chris@113 307
Chris@714 308 if (m_midiMode == MIDI_LISTEN) {
Chris@591 309 SVDEBUG << "MainWindowBase: Creating MIDI input" << endl;
Chris@161 310 m_midiInput = new MIDIInput(QApplication::applicationName(), this);
Chris@161 311 }
Chris@452 312
Chris@452 313 QTimer::singleShot(1500, this, SIGNAL(hideSplash()));
Chris@591 314
Chris@591 315 SVDEBUG << "MainWindowBase: Constructor done" << endl;
Chris@45 316 }
Chris@45 317
Chris@45 318 MainWindowBase::~MainWindowBase()
Chris@45 319 {
Chris@233 320 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
Chris@540 321
Chris@540 322 // We have to delete the breakfastquay::SystemPlaybackTarget or
Chris@540 323 // breakfastquay::SystemAudioIO object (whichever we have -- it
Chris@540 324 // depends on whether we handle recording or not) before we delete
Chris@540 325 // the ApplicationPlaybackSource and ApplicationRecordTarget that
Chris@540 326 // they refer to.
Chris@556 327
Chris@556 328 deleteAudioIO();
Chris@540 329
Chris@540 330 // Then delete the Application objects.
Chris@45 331 delete m_playSource;
Chris@475 332 delete m_recordTarget;
Chris@540 333
Chris@45 334 delete m_viewManager;
cannam@632 335 delete m_midiInput;
cannam@632 336
Chris@654 337 if (m_oscScript) {
Chris@654 338 disconnect(m_oscScript, nullptr, nullptr, nullptr);
Chris@654 339 m_oscScript->abandon();
Chris@654 340 m_oscScript->wait(1000);
Chris@654 341 if (m_oscScript->isRunning()) {
Chris@654 342 m_oscScript->terminate();
Chris@654 343 m_oscScript->wait(1000);
Chris@654 344 }
Chris@654 345 delete m_oscScript;
Chris@654 346 }
Chris@654 347
Chris@639 348 if (m_oscQueueStarter) {
Chris@644 349 disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr);
cannam@632 350 m_oscQueueStarter->wait(1000);
Chris@639 351 if (m_oscQueueStarter->isRunning()) {
Chris@639 352 m_oscQueueStarter->terminate();
Chris@639 353 m_oscQueueStarter->wait(1000);
Chris@639 354 }
Chris@639 355 delete m_oscQueueStarter;
Chris@639 356 delete m_oscQueue;
cannam@632 357 }
cannam@632 358
Chris@45 359 Profiles::getInstance()->dump();
Chris@45 360 }
Chris@45 361
Chris@113 362 void
Chris@452 363 MainWindowBase::emitHideSplash()
Chris@452 364 {
Chris@591 365 SVDEBUG << "MainWindowBase: Hiding splash screen" << endl;
Chris@452 366 emit hideSplash(this);
Chris@452 367 }
Chris@452 368
Chris@452 369 void
Chris@354 370 MainWindowBase::finaliseMenus()
Chris@354 371 {
Chris@591 372 SVDEBUG << "MainWindowBase::finaliseMenus called" << endl;
Chris@591 373
Chris@390 374 delete m_menuShortcutMapper;
Chris@636 375 m_menuShortcutMapper = nullptr;
Chris@390 376
Chris@391 377 foreach (QShortcut *sc, m_appShortcuts) {
Chris@391 378 delete sc;
Chris@391 379 }
Chris@391 380 m_appShortcuts.clear();
Chris@391 381
Chris@354 382 QMenuBar *mb = menuBar();
Chris@394 383
Chris@396 384 // This used to find all children of QMenu type, and call
Chris@396 385 // finaliseMenu on those. But it seems we are getting hold of some
Chris@396 386 // menus that way that are not actually active in the menu bar and
Chris@396 387 // are not returned in their parent menu's actions() list, and if
Chris@396 388 // we finalise those, we end up with duplicate shortcuts in the
Chris@396 389 // app shortcut mapper. So we should do this by descending the
Chris@396 390 // menu tree through only those menus accessible via actions()
Chris@396 391 // from their parents instead.
Chris@396 392
Chris@394 393 QList<QMenu *> menus = mb->findChildren<QMenu *>
Chris@394 394 (QString(), Qt::FindDirectChildrenOnly);
Chris@394 395
Chris@354 396 foreach (QMenu *menu, menus) {
Chris@354 397 if (menu) finaliseMenu(menu);
Chris@354 398 }
Chris@591 399
Chris@591 400 SVDEBUG << "MainWindowBase::finaliseMenus done" << endl;
Chris@354 401 }
Chris@354 402
Chris@354 403 void
Chris@426 404 MainWindowBase::finaliseMenu(QMenu *menu)
Chris@354 405 {
Chris@426 406 foreach (QAction *a, menu->actions()) {
Chris@426 407 a->setIconVisibleInMenu(m_iconsVisibleInMenus);
Chris@426 408 }
Chris@426 409
Chris@354 410 #ifdef Q_OS_MAC
Chris@354 411 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
Chris@354 412 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
Chris@354 413 // single-key shortcuts that are associated only with a menu
Chris@384 414 // action (and not with a toolbar button) do not work with Qt 5.x
Chris@384 415 // under OS/X.
Chris@354 416 //
Chris@354 417 // Apparently Cocoa never handled them as a matter of course, but
Chris@354 418 // earlier versions of Qt picked them up as widget shortcuts and
Chris@354 419 // handled them anyway. That behaviour was removed to fix a crash
Chris@354 420 // when invoking a menu while its window was overridden by a modal
Chris@354 421 // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
Chris@354 422 //
Chris@354 423 // This workaround restores the single-key shortcut behaviour by
Chris@384 424 // searching in menus for single-key shortcuts that are associated
Chris@384 425 // only with the menu and not with a toolbar button, and
Chris@384 426 // augmenting them with global application shortcuts that invoke
Chris@384 427 // the relevant actions, testing whether the actions are enabled
Chris@384 428 // on invocation.
Chris@354 429 //
Chris@384 430 // (Previously this acted on all single-key shortcuts in menus,
Chris@384 431 // and it removed the shortcut from the action when it created
Chris@384 432 // each new global one, in order to avoid an "ambiguous shortcut"
Chris@384 433 // error in the case where the action was also associated with a
Chris@384 434 // toolbar button. But that has the unwelcome side-effect of
Chris@384 435 // removing the shortcut hint from the menu entry. So now we leave
Chris@384 436 // the shortcut in the menu action as well as creating a global
Chris@384 437 // one, and we only act on shortcuts that have no toolbar button,
Chris@384 438 // i.e. that will not otherwise work. The downside is that if this
Chris@384 439 // bug is fixed in a future Qt release, we will start getting
Chris@384 440 // "ambiguous shortcut" errors from the menu entry actions and
Chris@384 441 // will need to update the code.)
Chris@354 442
Chris@443 443 // Update: The bug was fixed in Qt 5.4 for shortcuts with no
Chris@443 444 // modifier, and I believe it is fixed in Qt 5.5 for shortcuts
Chris@443 445 // with Shift modifiers. The below reflects that
Chris@443 446
Chris@443 447 #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
Chris@443 448
Chris@390 449 if (!m_menuShortcutMapper) {
Chris@390 450 m_menuShortcutMapper = new QSignalMapper(this);
Chris@392 451 connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)),
Chris@392 452 this, SLOT(menuActionMapperInvoked(QObject *)));
Chris@390 453 }
Chris@390 454
Chris@354 455 foreach (QAction *a, menu->actions()) {
Chris@394 456
Chris@394 457 if (a->isSeparator()) {
Chris@394 458 continue;
Chris@394 459 } else if (a->menu()) {
Chris@394 460 finaliseMenu(a->menu());
Chris@394 461 } else {
Chris@394 462
Chris@394 463 QWidgetList ww = a->associatedWidgets();
Chris@394 464 bool hasButton = false;
Chris@394 465 foreach (QWidget *w, ww) {
Chris@394 466 if (qobject_cast<QAbstractButton *>(w)) {
Chris@394 467 hasButton = true;
Chris@394 468 break;
Chris@394 469 }
Chris@394 470 }
Chris@394 471 if (hasButton) continue;
Chris@394 472 QKeySequence sc = a->shortcut();
Chris@399 473
Chris@399 474 // Note that the set of "single-key shortcuts" that aren't
Chris@399 475 // working and that we need to handle here includes those
Chris@399 476 // with the Shift modifier mask as well as those with no
Chris@399 477 // modifier at all
Chris@443 478 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
Chris@443 479 // Nothing needed
Chris@443 480 if (false) {
Chris@443 481 #elif (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
Chris@443 482 if (sc.count() == 1 &&
Chris@443 483 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier) {
Chris@443 484 #else
Chris@399 485 if (sc.count() == 1 &&
Chris@399 486 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier ||
Chris@399 487 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) {
Chris@443 488 #endif
Chris@394 489 QShortcut *newSc = new QShortcut(sc, a->parentWidget());
Chris@394 490 QObject::connect(newSc, SIGNAL(activated()),
Chris@394 491 m_menuShortcutMapper, SLOT(map()));
Chris@394 492 m_menuShortcutMapper->setMapping(newSc, a);
Chris@394 493 m_appShortcuts.push_back(newSc);
Chris@384 494 }
Chris@384 495 }
Chris@354 496 }
Chris@354 497 #endif
Chris@443 498 #endif
Chris@354 499 }
Chris@354 500
Chris@354 501 void
Chris@354 502 MainWindowBase::menuActionMapperInvoked(QObject *o)
Chris@354 503 {
Chris@354 504 QAction *a = qobject_cast<QAction *>(o);
Chris@354 505 if (a && a->isEnabled()) {
Chris@354 506 a->trigger();
Chris@354 507 }
Chris@354 508 }
Chris@354 509
Chris@354 510 void
Chris@168 511 MainWindowBase::resizeConstrained(QSize size)
Chris@168 512 {
Chris@684 513 QScreen *screen = QApplication::primaryScreen();
Chris@684 514 QRect available = screen->availableGeometry();
Chris@168 515 QSize actual(std::min(size.width(), available.width()),
Chris@168 516 std::min(size.height(), available.height()));
Chris@168 517 resize(actual);
Chris@168 518 }
Chris@168 519
Chris@168 520 void
Chris@658 521 MainWindowBase::startOSCQueue(bool withNetworkPort)
Chris@304 522 {
Chris@658 523 m_oscQueueStarter = new OSCQueueStarter(this, withNetworkPort);
Chris@304 524 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
Chris@304 525 m_oscQueueStarter->start();
Chris@304 526 }
Chris@304 527
Chris@304 528 void
Chris@113 529 MainWindowBase::oscReady()
Chris@113 530 {
Chris@113 531 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@113 532 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
Chris@113 533 QTimer *oscTimer = new QTimer(this);
Chris@113 534 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
Chris@725 535 oscTimer->start(2000);
Chris@658 536
Chris@658 537 if (m_oscQueue->hasPort()) {
Chris@658 538 SVDEBUG << "Finished setting up OSC interface" << endl;
Chris@658 539 } else {
Chris@658 540 SVDEBUG << "Finished setting up internal-only OSC queue" << endl;
Chris@658 541 }
Chris@654 542
Chris@654 543 if (m_oscScriptFile != QString()) {
Chris@654 544 startOSCScript();
Chris@654 545 }
Chris@113 546 }
Chris@113 547 }
Chris@113 548
Chris@654 549 void
Chris@654 550 MainWindowBase::startOSCScript()
Chris@654 551 {
Chris@654 552 m_oscScript = new OSCScript(m_oscScriptFile, m_oscQueue);
Chris@654 553 connect(m_oscScript, SIGNAL(finished()),
Chris@654 554 this, SLOT(oscScriptFinished()));
Chris@654 555 m_oscScriptFile = QString();
Chris@654 556 m_oscScript->start();
Chris@654 557 }
Chris@654 558
Chris@654 559 void
Chris@654 560 MainWindowBase::cueOSCScript(QString fileName)
Chris@654 561 {
Chris@654 562 m_oscScriptFile = fileName;
Chris@654 563 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@654 564 startOSCScript();
Chris@654 565 }
Chris@654 566 }
Chris@654 567
Chris@654 568 void
Chris@654 569 MainWindowBase::oscScriptFinished()
Chris@654 570 {
Chris@654 571 delete m_oscScript;
Chris@654 572 m_oscScript = 0;
Chris@654 573 }
Chris@654 574
Chris@45 575 QString
Chris@45 576 MainWindowBase::getOpenFileName(FileFinder::FileType type)
Chris@45 577 {
Chris@45 578 FileFinder *ff = FileFinder::getInstance();
Chris@358 579
Chris@358 580 if (type == FileFinder::AnyFile) {
Chris@684 581 if (!getMainModelId().isNone() &&
Chris@636 582 m_paneStack != nullptr &&
Chris@636 583 m_paneStack->getCurrentPane() != nullptr) { // can import a layer
Chris@45 584 return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
Chris@45 585 } else {
Chris@45 586 return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
Chris@45 587 m_sessionFile);
Chris@45 588 }
Chris@358 589 }
Chris@358 590
Chris@358 591 QString lastPath = m_sessionFile;
Chris@358 592
Chris@358 593 if (type == FileFinder::AudioFile) {
Chris@358 594 lastPath = m_audioFile;
Chris@45 595 }
Chris@358 596
Chris@358 597 return ff->getOpenFileName(type, lastPath);
Chris@45 598 }
Chris@45 599
Chris@45 600 QString
Chris@45 601 MainWindowBase::getSaveFileName(FileFinder::FileType type)
Chris@45 602 {
Chris@358 603 QString lastPath = m_sessionFile;
Chris@358 604
Chris@358 605 if (type == FileFinder::AudioFile) {
Chris@358 606 lastPath = m_audioFile;
Chris@358 607 }
Chris@358 608
Chris@45 609 FileFinder *ff = FileFinder::getInstance();
Chris@358 610 return ff->getSaveFileName(type, lastPath);
Chris@45 611 }
Chris@45 612
Chris@45 613 void
Chris@45 614 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
Chris@45 615 {
Chris@45 616 FileFinder *ff = FileFinder::getInstance();
Chris@45 617 ff->registerLastOpenedFilePath(type, path);
Chris@45 618 }
Chris@45 619
Chris@222 620 QString
Chris@222 621 MainWindowBase::getDefaultSessionTemplate() const
Chris@222 622 {
Chris@231 623 QSettings settings;
Chris@231 624 settings.beginGroup("MainWindow");
Chris@231 625 QString templateName = settings.value("sessiontemplate", "").toString();
Chris@231 626 if (templateName == "") templateName = "default";
Chris@231 627 return templateName;
Chris@222 628 }
Chris@222 629
Chris@222 630 void
Chris@251 631 MainWindowBase::setDefaultSessionTemplate(QString n)
Chris@251 632 {
Chris@251 633 QSettings settings;
Chris@251 634 settings.beginGroup("MainWindow");
Chris@251 635 settings.setValue("sessiontemplate", n);
Chris@251 636 }
Chris@251 637
Chris@251 638 void
Chris@45 639 MainWindowBase::updateMenuStates()
Chris@45 640 {
Chris@636 641 Pane *currentPane = nullptr;
Chris@636 642 Layer *currentLayer = nullptr;
Chris@45 643
Chris@45 644 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@45 645 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@45 646
Chris@73 647 bool havePrevPane = false, haveNextPane = false;
Chris@73 648 bool havePrevLayer = false, haveNextLayer = false;
Chris@73 649
Chris@73 650 if (currentPane) {
Chris@73 651 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 652 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 653 if (i > 0) havePrevPane = true;
Chris@73 654 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true;
Chris@73 655 break;
Chris@73 656 }
Chris@73 657 }
Chris@403 658 // the prev/next layer commands actually include the pane
Chris@403 659 // itself as one of the selectables -- so we always have a
Chris@403 660 // prev and next layer, as long as we have a pane with at
Chris@403 661 // least one layer in it
Chris@403 662 if (currentPane->getLayerCount() > 0) {
Chris@403 663 havePrevLayer = true;
Chris@403 664 haveNextLayer = true;
Chris@73 665 }
Chris@73 666 }
Chris@73 667
Chris@45 668 bool haveCurrentPane =
Chris@636 669 (currentPane != nullptr);
Chris@45 670 bool haveCurrentLayer =
Chris@45 671 (haveCurrentPane &&
Chris@636 672 (currentLayer != nullptr));
Chris@45 673 bool haveMainModel =
Chris@684 674 (!getMainModelId().isNone());
Chris@45 675 bool havePlayTarget =
Chris@636 676 (m_playTarget != nullptr || m_audioIO != nullptr);
Chris@45 677 bool haveSelection =
Chris@595 678 (m_viewManager &&
Chris@595 679 !m_viewManager->getSelections().empty());
Chris@45 680 bool haveCurrentEditableLayer =
Chris@595 681 (haveCurrentLayer &&
Chris@595 682 currentLayer->isLayerEditable());
Chris@45 683 bool haveCurrentTimeInstantsLayer =
Chris@595 684 (haveCurrentLayer &&
Chris@595 685 dynamic_cast<TimeInstantLayer *>(currentLayer));
Chris@184 686 bool haveCurrentDurationLayer =
Chris@595 687 (haveCurrentLayer &&
Chris@595 688 (dynamic_cast<NoteLayer *>(currentLayer) ||
Chris@595 689 dynamic_cast<FlexiNoteLayer *>(currentLayer) ||
Chris@184 690 dynamic_cast<RegionLayer *>(currentLayer)));
Chris@45 691 bool haveCurrentColour3DPlot =
Chris@45 692 (haveCurrentLayer &&
Chris@45 693 dynamic_cast<Colour3DPlotLayer *>(currentLayer));
Chris@722 694 bool haveCurrentSpectrogram =
Chris@722 695 (haveCurrentLayer &&
Chris@722 696 dynamic_cast<SpectrogramLayer *>(currentLayer));
Chris@45 697 bool haveClipboardContents =
Chris@45 698 (m_viewManager &&
Chris@45 699 !m_viewManager->getClipboard().empty());
Chris@146 700 bool haveTabularLayer =
Chris@146 701 (haveCurrentLayer &&
Chris@684 702 ModelById::isa<TabularModel>(currentLayer->getModel()));
Chris@45 703
Chris@45 704 emit canAddPane(haveMainModel);
Chris@45 705 emit canDeleteCurrentPane(haveCurrentPane);
Chris@45 706 emit canZoom(haveMainModel && haveCurrentPane);
Chris@45 707 emit canScroll(haveMainModel && haveCurrentPane);
Chris@45 708 emit canAddLayer(haveMainModel && haveCurrentPane);
Chris@45 709 emit canImportMoreAudio(haveMainModel);
Chris@259 710 emit canReplaceMainAudio(haveMainModel);
Chris@45 711 emit canImportLayer(haveMainModel && haveCurrentPane);
Chris@45 712 emit canExportAudio(haveMainModel);
Chris@289 713 emit canChangeSessionTemplate(haveMainModel);
Chris@45 714 emit canExportLayer(haveMainModel &&
Chris@722 715 (haveCurrentEditableLayer ||
Chris@722 716 haveCurrentColour3DPlot ||
Chris@722 717 haveCurrentSpectrogram));
Chris@45 718 emit canExportImage(haveMainModel && haveCurrentPane);
Chris@45 719 emit canDeleteCurrentLayer(haveCurrentLayer);
Chris@45 720 emit canRenameLayer(haveCurrentLayer);
Chris@45 721 emit canEditLayer(haveCurrentEditableLayer);
Chris@146 722 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer);
Chris@45 723 emit canMeasureLayer(haveCurrentLayer);
Chris@45 724 emit canSelect(haveMainModel && haveCurrentPane);
Chris@188 725 emit canPlay(haveMainModel && havePlayTarget);
Chris@453 726 emit canFfwd(haveMainModel);
Chris@453 727 emit canRewind(haveMainModel);
Chris@87 728 emit canPaste(haveClipboardContents);
Chris@45 729 emit canInsertInstant(haveCurrentPane);
Chris@45 730 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
Chris@184 731 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer);
Chris@45 732 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@537 733 emit canSubdivideInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@538 734 emit canWinnowInstants(haveCurrentTimeInstantsLayer && haveSelection);
Chris@45 735 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
Chris@45 736 emit canClearSelection(haveSelection);
Chris@45 737 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
Chris@45 738 emit canSave(m_sessionFile != "" && m_documentModified);
Chris@601 739 emit canSaveAs(haveMainModel); // possibly used only in Tony, not SV
Chris@73 740 emit canSelectPreviousPane(havePrevPane);
Chris@73 741 emit canSelectNextPane(haveNextPane);
Chris@73 742 emit canSelectPreviousLayer(havePrevLayer);
Chris@73 743 emit canSelectNextLayer(haveNextLayer);
Chris@586 744
Chris@586 745 // This is quite subtle -- whereas we can play back only if a
Chris@586 746 // system play target or I/O exists, we can record even if no
Chris@586 747 // record source (i.e. audioIO) exists because we can record into
Chris@586 748 // an empty session before the audio device has been
Chris@714 749 // opened.
Chris@714 750 //
Chris@714 751 // However, if there is no record *target* then recording was
Chris@714 752 // actively disabled via the audio mode setting.
Chris@714 753 //
Chris@714 754 // If we have a play target instead of an audioIO, then if the
Chris@714 755 // audio mode is AUDIO_PLAYBACK_NOW_RECORD_LATER, we are still
Chris@714 756 // expecting to open the IO on demand, but if it is
Chris@714 757 // AUDIO_PLAYBACK_AND_RECORD then we must have tried to open the
Chris@714 758 // device and failed to find any capture source.
Chris@714 759 //
Chris@586 760 bool recordDisabled = (m_recordTarget == nullptr);
Chris@714 761 bool recordDeviceFailed =
Chris@714 762 (m_audioMode == AUDIO_PLAYBACK_AND_RECORD &&
Chris@714 763 (m_playTarget != nullptr && m_audioIO == nullptr));
Chris@586 764 emit canRecord(!recordDisabled && !recordDeviceFailed);
Chris@45 765 }
Chris@45 766
Chris@45 767 void
Chris@669 768 MainWindowBase::updateWindowTitle()
Chris@669 769 {
Chris@669 770 QString title;
Chris@669 771
Chris@669 772 if (m_sessionFile != "") {
Chris@669 773 if (m_originalLocation != "" &&
Chris@669 774 m_originalLocation != m_sessionFile) { // session + location
Chris@669 775 title = tr("%1: %2 [%3]")
Chris@669 776 .arg(QApplication::applicationName())
Chris@669 777 .arg(QFileInfo(m_sessionFile).fileName())
Chris@669 778 .arg(m_originalLocation);
Chris@669 779 } else { // session only
Chris@669 780 title = tr("%1: %2")
Chris@669 781 .arg(QApplication::applicationName())
Chris@669 782 .arg(QFileInfo(m_sessionFile).fileName());
Chris@669 783 }
Chris@669 784 } else {
Chris@669 785 if (m_originalLocation != "") { // location only
Chris@669 786 title = tr("%1: %2")
Chris@669 787 .arg(QApplication::applicationName())
Chris@669 788 .arg(m_originalLocation);
Chris@669 789 } else { // neither
Chris@669 790 title = QApplication::applicationName();
Chris@669 791 }
Chris@669 792 }
Chris@669 793
Chris@669 794 if (m_documentModified) {
Chris@669 795 title = tr("%1 (modified)").arg(title);
Chris@669 796 }
Chris@669 797
Chris@669 798 setWindowTitle(title);
Chris@669 799 }
Chris@669 800
Chris@669 801 void
Chris@45 802 MainWindowBase::documentModified()
Chris@45 803 {
Chris@233 804 // SVDEBUG << "MainWindowBase::documentModified" << endl;
Chris@45 805
Chris@45 806 m_documentModified = true;
Chris@669 807 updateWindowTitle();
Chris@45 808 updateMenuStates();
Chris@45 809 }
Chris@45 810
Chris@45 811 void
Chris@45 812 MainWindowBase::documentRestored()
Chris@45 813 {
Chris@233 814 // SVDEBUG << "MainWindowBase::documentRestored" << endl;
Chris@45 815
Chris@45 816 m_documentModified = false;
Chris@669 817 updateWindowTitle();
Chris@45 818 updateMenuStates();
Chris@45 819 }
Chris@45 820
Chris@45 821 void
Chris@45 822 MainWindowBase::playLoopToggled()
Chris@45 823 {
Chris@45 824 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 825
Chris@45 826 if (action) {
Chris@595 827 m_viewManager->setPlayLoopMode(action->isChecked());
Chris@45 828 } else {
Chris@595 829 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
Chris@45 830 }
Chris@45 831 }
Chris@45 832
Chris@45 833 void
Chris@45 834 MainWindowBase::playSelectionToggled()
Chris@45 835 {
Chris@45 836 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 837
Chris@45 838 if (action) {
Chris@595 839 m_viewManager->setPlaySelectionMode(action->isChecked());
Chris@45 840 } else {
Chris@595 841 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
Chris@45 842 }
Chris@45 843 }
Chris@45 844
Chris@45 845 void
Chris@45 846 MainWindowBase::playSoloToggled()
Chris@45 847 {
Chris@45 848 QAction *action = dynamic_cast<QAction *>(sender());
Chris@45 849
Chris@45 850 if (action) {
Chris@595 851 m_viewManager->setPlaySoloMode(action->isChecked());
Chris@45 852 } else {
Chris@595 853 m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
Chris@45 854 }
Chris@45 855
Chris@45 856 if (m_viewManager->getPlaySoloMode()) {
Chris@45 857 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@45 858 } else {
Chris@684 859 m_viewManager->setPlaybackModel({});
Chris@45 860 if (m_playSource) {
Chris@45 861 m_playSource->clearSoloModelSet();
Chris@45 862 }
Chris@45 863 }
Chris@45 864 }
Chris@45 865
Chris@45 866 void
Chris@45 867 MainWindowBase::currentPaneChanged(Pane *p)
Chris@45 868 {
Chris@45 869 updateMenuStates();
Chris@45 870 updateVisibleRangeDisplay(p);
Chris@45 871
Chris@45 872 if (!p) return;
Chris@45 873
Chris@45 874 if (!(m_viewManager &&
Chris@45 875 m_playSource &&
Chris@45 876 m_viewManager->getPlaySoloMode())) {
Chris@684 877 if (m_viewManager) {
Chris@684 878 m_viewManager->setPlaybackModel(ModelId());
Chris@684 879 }
Chris@45 880 return;
Chris@45 881 }
Chris@45 882
Chris@684 883 ModelId prevPlaybackModel = m_viewManager->getPlaybackModel();
Chris@60 884
Chris@93 885 // What we want here is not the currently playing frame (unless we
Chris@93 886 // are about to clear out the audio playback buffers -- which may
Chris@93 887 // or may not be possible, depending on the audio driver). What
Chris@93 888 // we want is the frame that was last committed to the soundcard
Chris@93 889 // buffers, as the audio driver will continue playing up to that
Chris@93 890 // frame before switching to whichever one we decide we want to
Chris@93 891 // switch to, regardless of our efforts.
Chris@93 892
Chris@435 893 sv_frame_t frame = m_playSource->getCurrentBufferedFrame();
Chris@93 894
Chris@388 895 cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
Chris@45 896
Chris@45 897 View::ModelSet soloModels = p->getModels();
Chris@45 898
Chris@57 899 View::ModelSet sources;
Chris@684 900 for (ModelId modelId: sources) {
Chris@190 901 // If a model in this pane is derived from something else,
Chris@190 902 // then we want to play that model as well -- if the model
Chris@190 903 // that's derived from it is not something that is itself
Chris@190 904 // individually playable (e.g. a waveform)
Chris@684 905 if (auto model = ModelById::get(modelId)) {
Chris@684 906 if (!ModelById::isa<RangeSummarisableTimeValueModel>(modelId) &&
Chris@684 907 !model->getSourceModel().isNone()) {
Chris@684 908 sources.insert(model->getSourceModel());
Chris@684 909 }
Chris@57 910 }
Chris@57 911 }
Chris@684 912 for (ModelId modelId: sources) {
Chris@684 913 soloModels.insert(modelId);
Chris@57 914 }
Chris@57 915
Chris@60 916 //!!! Need an "atomic" way of telling the play source that the
Chris@60 917 //playback model has changed, and changing it on ViewManager --
Chris@60 918 //the play source should be making the setPlaybackModel call to
Chris@60 919 //ViewManager
Chris@60 920
Chris@684 921 ModelId newPlaybackModel;
Chris@684 922
Chris@684 923 for (ModelId modelId: soloModels) {
Chris@684 924 if (ModelById::isa<RangeSummarisableTimeValueModel>(modelId)) {
Chris@684 925 m_viewManager->setPlaybackModel(modelId);
Chris@684 926 newPlaybackModel = modelId;
Chris@45 927 }
Chris@45 928 }
Chris@45 929
Chris@45 930 m_playSource->setSoloModelSet(soloModels);
Chris@45 931
Chris@684 932 if (!prevPlaybackModel.isNone() && !newPlaybackModel.isNone() &&
Chris@684 933 prevPlaybackModel != newPlaybackModel) {
Chris@684 934 if (m_playSource->isPlaying()) {
Chris@684 935 m_playSource->play(frame);
Chris@684 936 }
Chris@45 937 }
Chris@45 938 }
Chris@45 939
Chris@45 940 void
Chris@45 941 MainWindowBase::currentLayerChanged(Pane *p, Layer *)
Chris@45 942 {
Chris@45 943 updateMenuStates();
Chris@45 944 updateVisibleRangeDisplay(p);
Chris@45 945 }
Chris@45 946
Chris@597 947 sv_frame_t
Chris@597 948 MainWindowBase::getModelsStartFrame() const
Chris@597 949 {
Chris@597 950 sv_frame_t startFrame = 0;
Chris@597 951 if (!m_paneStack) return startFrame;
Chris@597 952 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@597 953 sv_frame_t thisStart = m_paneStack->getPane(i)->getModelsStartFrame();
Chris@597 954 if (i == 0 || thisStart < startFrame) {
Chris@597 955 startFrame = thisStart;
Chris@597 956 }
Chris@597 957 }
Chris@597 958 return startFrame;
Chris@597 959 }
Chris@597 960
Chris@597 961 sv_frame_t
Chris@597 962 MainWindowBase::getModelsEndFrame() const
Chris@597 963 {
Chris@597 964 sv_frame_t endFrame = 0;
Chris@597 965 if (!m_paneStack) return endFrame;
Chris@597 966 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@597 967 sv_frame_t thisEnd = m_paneStack->getPane(i)->getModelsEndFrame();
Chris@597 968 if (i == 0 || thisEnd > endFrame) {
Chris@597 969 endFrame = thisEnd;
Chris@597 970 }
Chris@597 971 }
Chris@597 972 return endFrame;
Chris@597 973 }
Chris@597 974
Chris@45 975 void
Chris@45 976 MainWindowBase::selectAll()
Chris@45 977 {
Chris@597 978 m_viewManager->setSelection(Selection(getModelsStartFrame(),
Chris@597 979 getModelsEndFrame()));
Chris@45 980 }
Chris@45 981
Chris@45 982 void
Chris@45 983 MainWindowBase::selectToStart()
Chris@45 984 {
Chris@684 985 m_viewManager->setSelection(Selection(getModelsStartFrame(),
Chris@595 986 m_viewManager->getGlobalCentreFrame()));
Chris@45 987 }
Chris@45 988
Chris@45 989 void
Chris@45 990 MainWindowBase::selectToEnd()
Chris@45 991 {
Chris@45 992 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
Chris@684 993 getModelsEndFrame()));
Chris@45 994 }
Chris@45 995
Chris@45 996 void
Chris@45 997 MainWindowBase::selectVisible()
Chris@45 998 {
Chris@684 999 auto model = getMainModel();
Chris@45 1000 if (!model) return;
Chris@45 1001
Chris@45 1002 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1003 if (!currentPane) return;
Chris@45 1004
Chris@435 1005 sv_frame_t startFrame, endFrame;
Chris@45 1006
Chris@684 1007 if (currentPane->getStartFrame() < 0) {
Chris@684 1008 startFrame = 0;
Chris@684 1009 } else {
Chris@684 1010 startFrame = currentPane->getStartFrame();
Chris@684 1011 }
Chris@684 1012
Chris@684 1013 if (currentPane->getEndFrame() > model->getEndFrame()) {
Chris@684 1014 endFrame = model->getEndFrame();
Chris@684 1015 } else {
Chris@684 1016 endFrame = currentPane->getEndFrame();
Chris@684 1017 }
Chris@45 1018
Chris@45 1019 m_viewManager->setSelection(Selection(startFrame, endFrame));
Chris@45 1020 }
Chris@45 1021
Chris@45 1022 void
Chris@45 1023 MainWindowBase::clearSelection()
Chris@45 1024 {
Chris@45 1025 m_viewManager->clearSelections();
Chris@45 1026 }
Chris@45 1027
Chris@45 1028 void
Chris@45 1029 MainWindowBase::cut()
Chris@45 1030 {
Chris@45 1031 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1032 if (!currentPane) return;
Chris@45 1033
Chris@45 1034 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1035 if (!layer) return;
Chris@45 1036
Chris@45 1037 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@45 1038 clipboard.clear();
Chris@45 1039
Chris@45 1040 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1041
Chris@45 1042 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
Chris@45 1043
Chris@45 1044 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1045 i != selections.end(); ++i) {
Chris@86 1046 layer->copy(currentPane, *i, clipboard);
Chris@45 1047 layer->deleteSelection(*i);
Chris@45 1048 }
Chris@45 1049
Chris@45 1050 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1051 }
Chris@45 1052
Chris@45 1053 void
Chris@45 1054 MainWindowBase::copy()
Chris@45 1055 {
Chris@45 1056 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1057 if (!currentPane) return;
Chris@45 1058
Chris@45 1059 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1060 if (!layer) return;
Chris@45 1061
Chris@45 1062 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@45 1063 clipboard.clear();
Chris@45 1064
Chris@45 1065 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1066
Chris@45 1067 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1068 i != selections.end(); ++i) {
Chris@86 1069 layer->copy(currentPane, *i, clipboard);
Chris@45 1070 }
Chris@45 1071 }
Chris@45 1072
Chris@45 1073 void
Chris@45 1074 MainWindowBase::paste()
Chris@45 1075 {
Chris@215 1076 pasteRelative(0);
Chris@215 1077 }
Chris@215 1078
Chris@215 1079 void
Chris@215 1080 MainWindowBase::pasteAtPlaybackPosition()
Chris@215 1081 {
Chris@435 1082 sv_frame_t pos = getFrame();
Chris@215 1083 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@215 1084 if (!clipboard.empty()) {
Chris@435 1085 sv_frame_t firstEventFrame = clipboard.getPoints()[0].getFrame();
Chris@435 1086 sv_frame_t offset = 0;
Chris@215 1087 if (firstEventFrame < 0) {
Chris@366 1088 offset = pos - firstEventFrame;
Chris@354 1089 } else if (firstEventFrame < pos) {
Chris@366 1090 offset = pos - firstEventFrame;
Chris@215 1091 } else {
Chris@366 1092 offset = -(firstEventFrame - pos);
Chris@215 1093 }
Chris@215 1094 pasteRelative(offset);
Chris@215 1095 }
Chris@215 1096 }
Chris@215 1097
Chris@215 1098 void
Chris@435 1099 MainWindowBase::pasteRelative(sv_frame_t offset)
Chris@215 1100 {
Chris@45 1101 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 1102 if (!currentPane) return;
Chris@45 1103
Chris@45 1104 Layer *layer = currentPane->getSelectedLayer();
Chris@45 1105
Chris@45 1106 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@87 1107
Chris@98 1108 bool inCompound = false;
Chris@87 1109
Chris@87 1110 if (!layer || !layer->isLayerEditable()) {
Chris@87 1111
Chris@87 1112 CommandHistory::getInstance()->startCompoundOperation
Chris@87 1113 (tr("Paste"), true);
Chris@87 1114
Chris@87 1115 // no suitable current layer: create one of the most
Chris@87 1116 // appropriate sort
Chris@87 1117 LayerFactory::LayerType type =
Chris@87 1118 LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard);
Chris@87 1119 layer = m_document->createEmptyLayer(type);
Chris@87 1120
Chris@87 1121 if (!layer) {
Chris@87 1122 CommandHistory::getInstance()->endCompoundOperation();
Chris@87 1123 return;
Chris@45 1124 }
Chris@87 1125
Chris@87 1126 m_document->addLayerToView(currentPane, layer);
Chris@87 1127 m_paneStack->setCurrentLayer(currentPane, layer);
Chris@87 1128
Chris@87 1129 inCompound = true;
Chris@45 1130 }
Chris@45 1131
Chris@215 1132 layer->paste(currentPane, clipboard, offset, true);
Chris@45 1133
Chris@87 1134 if (inCompound) CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1135 }
Chris@45 1136
Chris@45 1137 void
Chris@45 1138 MainWindowBase::deleteSelected()
Chris@45 1139 {
Chris@45 1140 if (m_paneStack->getCurrentPane() &&
Chris@595 1141 m_paneStack->getCurrentPane()->getSelectedLayer()) {
Chris@45 1142
Chris@45 1143 Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
Chris@45 1144
Chris@409 1145 if (m_viewManager) {
Chris@409 1146
Chris@409 1147 if (m_viewManager->getToolMode() == ViewManager::MeasureMode) {
Chris@409 1148
Chris@409 1149 layer->deleteCurrentMeasureRect();
Chris@45 1150
Chris@409 1151 } else {
Chris@409 1152
Chris@409 1153 MultiSelection::SelectionList selections =
Chris@409 1154 m_viewManager->getSelections();
Chris@409 1155
Chris@409 1156 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@409 1157 i != selections.end(); ++i) {
Chris@409 1158 layer->deleteSelection(*i);
Chris@409 1159 }
Chris@45 1160 }
Chris@595 1161 }
Chris@45 1162 }
Chris@45 1163 }
Chris@45 1164
Chris@161 1165 // FrameTimer method
Chris@161 1166
Chris@435 1167 sv_frame_t
Chris@161 1168 MainWindowBase::getFrame() const
Chris@161 1169 {
Chris@161 1170 if (m_playSource && m_playSource->isPlaying()) {
Chris@161 1171 return m_playSource->getCurrentPlayingFrame();
Chris@161 1172 } else {
Chris@161 1173 return m_viewManager->getPlaybackFrame();
Chris@161 1174 }
Chris@161 1175 }
Chris@161 1176
Chris@45 1177 void
Chris@45 1178 MainWindowBase::insertInstant()
Chris@45 1179 {
Chris@161 1180 insertInstantAt(getFrame());
Chris@45 1181 }
Chris@45 1182
Chris@45 1183 void
Chris@45 1184 MainWindowBase::insertInstantsAtBoundaries()
Chris@45 1185 {
Chris@45 1186 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@45 1187 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@45 1188 i != selections.end(); ++i) {
Chris@435 1189 sv_frame_t start = i->getStartFrame();
Chris@435 1190 sv_frame_t end = i->getEndFrame();
Chris@45 1191 if (start != end) {
Chris@184 1192 insertInstantAt(start);
Chris@184 1193 insertInstantAt(end);
Chris@45 1194 }
Chris@45 1195 }
Chris@45 1196 }
Chris@45 1197
Chris@45 1198 void
Chris@435 1199 MainWindowBase::insertInstantAt(sv_frame_t frame)
Chris@45 1200 {
Chris@45 1201 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1202 if (!pane) {
Chris@45 1203 return;
Chris@45 1204 }
Chris@45 1205
Chris@74 1206 frame = pane->alignFromReference(frame);
Chris@74 1207
Chris@45 1208 Layer *layer = dynamic_cast<TimeInstantLayer *>
Chris@45 1209 (pane->getSelectedLayer());
Chris@45 1210
Chris@45 1211 if (!layer) {
Chris@45 1212 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 1213 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
Chris@45 1214 if (layer) break;
Chris@45 1215 }
Chris@45 1216
Chris@45 1217 if (!layer) {
Chris@45 1218 CommandHistory::getInstance()->startCompoundOperation
Chris@45 1219 (tr("Add Point"), true);
Chris@45 1220 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
Chris@45 1221 if (layer) {
Chris@45 1222 m_document->addLayerToView(pane, layer);
Chris@45 1223 m_paneStack->setCurrentLayer(pane, layer);
Chris@45 1224 }
Chris@45 1225 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1226 }
Chris@45 1227 }
Chris@45 1228
Chris@45 1229 if (layer) {
Chris@45 1230
Chris@684 1231 ModelId model = layer->getModel();
Chris@684 1232 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(model);
Chris@45 1233
Chris@45 1234 if (sodm) {
Chris@651 1235 Event point(frame, "");
Chris@651 1236 Event prevPoint(0);
Chris@45 1237 bool havePrevPoint = false;
Chris@45 1238
Chris@651 1239 ChangeEventsCommand *command =
Chris@684 1240 new ChangeEventsCommand(model.untyped, tr("Add Point"));
Chris@45 1241
Chris@409 1242 if (m_labeller) {
Chris@409 1243
Chris@409 1244 if (m_labeller->requiresPrevPoint()) {
Chris@651 1245
Chris@651 1246 if (sodm->getNearestEventMatching
Chris@651 1247 (frame,
Chris@651 1248 [](Event) { return true; },
Chris@651 1249 EventSeries::Backward,
Chris@651 1250 prevPoint)) {
Chris@409 1251 havePrevPoint = true;
Chris@409 1252 }
Chris@45 1253 }
Chris@45 1254
Chris@45 1255 m_labeller->setSampleRate(sodm->getSampleRate());
Chris@45 1256
Chris@651 1257 Labeller::Relabelling relabelling = m_labeller->label
Chris@636 1258 (point, havePrevPoint ? &prevPoint : nullptr);
Chris@651 1259
Chris@651 1260 if (relabelling.first == Labeller::AppliesToPreviousEvent) {
Chris@651 1261 command->remove(prevPoint);
Chris@651 1262 command->add(relabelling.second);
Chris@651 1263 } else {
Chris@651 1264 point = relabelling.second;
Chris@45 1265 }
Chris@45 1266 }
Chris@45 1267
Chris@651 1268 command->add(point);
Chris@45 1269
Chris@45 1270 command->setName(tr("Add Point at %1 s")
Chris@45 1271 .arg(RealTime::frame2RealTime
Chris@45 1272 (frame,
Chris@45 1273 sodm->getSampleRate())
Chris@45 1274 .toText(false).c_str()));
Chris@45 1275
Chris@108 1276 Command *c = command->finish();
Chris@684 1277 if (c) {
Chris@684 1278 CommandHistory::getInstance()->addCommand(c, false);
Chris@684 1279 }
Chris@45 1280 }
Chris@45 1281 }
Chris@45 1282 }
Chris@45 1283
Chris@45 1284 void
Chris@184 1285 MainWindowBase::insertItemAtSelection()
Chris@184 1286 {
Chris@184 1287 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@184 1288 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@184 1289 i != selections.end(); ++i) {
Chris@435 1290 sv_frame_t start = i->getStartFrame();
Chris@435 1291 sv_frame_t end = i->getEndFrame();
Chris@184 1292 if (start < end) {
Chris@184 1293 insertItemAt(start, end - start);
Chris@184 1294 }
Chris@184 1295 }
Chris@184 1296 }
Chris@184 1297
Chris@184 1298 void
Chris@435 1299 MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration)
Chris@184 1300 {
Chris@184 1301 Pane *pane = m_paneStack->getCurrentPane();
Chris@184 1302 if (!pane) {
Chris@184 1303 return;
Chris@184 1304 }
Chris@184 1305
Chris@184 1306 // ugh!
Chris@184 1307
Chris@435 1308 sv_frame_t alignedStart = pane->alignFromReference(frame);
Chris@435 1309 sv_frame_t alignedEnd = pane->alignFromReference(frame + duration);
Chris@184 1310 if (alignedStart >= alignedEnd) return;
Chris@435 1311 sv_frame_t alignedDuration = alignedEnd - alignedStart;
Chris@184 1312
Chris@636 1313 Command *c = nullptr;
Chris@184 1314
Chris@184 1315 QString name = tr("Add Item at %1 s")
Chris@184 1316 .arg(RealTime::frame2RealTime
Chris@184 1317 (alignedStart,
Chris@184 1318 getMainModel()->getSampleRate())
Chris@184 1319 .toText(false).c_str());
Chris@184 1320
Chris@184 1321 Layer *layer = pane->getSelectedLayer();
Chris@184 1322 if (!layer) return;
Chris@184 1323
Chris@687 1324 ModelId modelId = layer->getModel();
Chris@687 1325
Chris@687 1326 auto rm = ModelById::getAs<RegionModel>(modelId);
Chris@184 1327 if (rm) {
Chris@648 1328 Event point(alignedStart,
Chris@648 1329 rm->getValueMaximum() + 1,
Chris@648 1330 alignedDuration,
Chris@648 1331 "");
Chris@684 1332 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@687 1333 (modelId.untyped, name);
Chris@648 1334 command->add(point);
Chris@184 1335 c = command->finish();
Chris@184 1336 }
Chris@184 1337
Chris@184 1338 if (c) {
Chris@184 1339 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1340 return;
Chris@184 1341 }
Chris@184 1342
Chris@687 1343 auto nm = ModelById::getAs<NoteModel>(modelId);
Chris@184 1344 if (nm) {
Chris@648 1345 Event point(alignedStart,
Chris@648 1346 nm->getValueMinimum(),
Chris@648 1347 alignedDuration,
Chris@648 1348 1.f,
Chris@648 1349 "");
Chris@684 1350 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@687 1351 (modelId.untyped, name);
Chris@646 1352 command->add(point);
Chris@184 1353 c = command->finish();
Chris@184 1354 }
Chris@184 1355
Chris@184 1356 if (c) {
Chris@184 1357 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1358 return;
Chris@184 1359 }
Chris@184 1360 }
Chris@184 1361
Chris@184 1362 void
Chris@45 1363 MainWindowBase::renumberInstants()
Chris@45 1364 {
Chris@45 1365 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1366 if (!pane) return;
Chris@45 1367
Chris@45 1368 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@45 1369 if (!layer) return;
Chris@45 1370
Chris@45 1371 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1372
Chris@694 1373 ModelId modelId = layer->getModel();
Chris@45 1374
Chris@694 1375 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@45 1376 if (!sodm) return;
Chris@45 1377
Chris@45 1378 if (!m_labeller) return;
Chris@45 1379
Chris@45 1380 Labeller labeller(*m_labeller);
Chris@45 1381 labeller.setSampleRate(sodm->getSampleRate());
Chris@684 1382
Chris@694 1383 Command *c = labeller.labelAll(modelId.untyped, &ms, sodm->getAllEvents());
Chris@537 1384 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@537 1385 }
Chris@537 1386
Chris@537 1387 void
Chris@537 1388 MainWindowBase::subdivideInstantsBy(int n)
Chris@537 1389 {
Chris@537 1390 Pane *pane = m_paneStack->getCurrentPane();
Chris@537 1391 if (!pane) return;
Chris@537 1392
Chris@537 1393 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@537 1394 if (!layer) return;
Chris@537 1395
Chris@537 1396 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1397
Chris@694 1398 ModelId modelId = layer->getModel();
Chris@537 1399
Chris@694 1400 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@537 1401 if (!sodm) return;
Chris@537 1402
Chris@537 1403 if (!m_labeller) return;
Chris@537 1404
Chris@537 1405 Labeller labeller(*m_labeller);
Chris@537 1406 labeller.setSampleRate(sodm->getSampleRate());
Chris@537 1407
Chris@694 1408 Command *c = labeller.subdivide(modelId.untyped, &ms, sodm->getAllEvents(), n);
Chris@537 1409 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@45 1410 }
Chris@45 1411
Chris@538 1412 void
Chris@538 1413 MainWindowBase::winnowInstantsBy(int n)
Chris@538 1414 {
Chris@538 1415 Pane *pane = m_paneStack->getCurrentPane();
Chris@538 1416 if (!pane) return;
Chris@538 1417
Chris@538 1418 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@538 1419 if (!layer) return;
Chris@538 1420
Chris@538 1421 MultiSelection ms(m_viewManager->getSelection());
Chris@694 1422
Chris@694 1423 ModelId modelId = layer->getModel();
Chris@538 1424
Chris@694 1425 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
Chris@538 1426 if (!sodm) return;
Chris@538 1427
Chris@538 1428 if (!m_labeller) return;
Chris@538 1429
Chris@538 1430 Labeller labeller(*m_labeller);
Chris@538 1431 labeller.setSampleRate(sodm->getSampleRate());
Chris@538 1432
Chris@694 1433 Command *c = labeller.winnow(modelId.untyped, &ms, sodm->getAllEvents(), n);
Chris@538 1434 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@538 1435 }
Chris@538 1436
Chris@45 1437 MainWindowBase::FileOpenStatus
Chris@373 1438 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
Chris@45 1439 {
Chris@134 1440 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
Chris@134 1441 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1442 return open(FileSource(fileOrUrl, &dialog), mode);
Chris@45 1443 }
Chris@45 1444
Chris@45 1445 MainWindowBase::FileOpenStatus
Chris@45 1446 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
Chris@45 1447 {
Chris@45 1448 FileOpenStatus status;
Chris@45 1449
Chris@45 1450 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1451 source.waitForData();
Chris@45 1452
Chris@636 1453 bool canImportLayer = (getMainModel() != nullptr &&
Chris@636 1454 m_paneStack != nullptr &&
Chris@636 1455 m_paneStack->getCurrentPane() != nullptr);
Chris@45 1456
Chris@152 1457 bool rdf = (source.getExtension().toLower() == "rdf" ||
Chris@152 1458 source.getExtension().toLower() == "n3" ||
Chris@152 1459 source.getExtension().toLower() == "ttl");
Chris@152 1460
Chris@152 1461 bool audio = AudioFileReaderFactory::getKnownExtensions().contains
Chris@152 1462 (source.getExtension().toLower());
Chris@145 1463
Chris@145 1464 bool rdfSession = false;
Chris@145 1465 if (rdf) {
Chris@145 1466 RDFImporter::RDFDocumentType rdfType =
Chris@145 1467 RDFImporter::identifyDocumentType
Chris@145 1468 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
Chris@145 1469 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@145 1470 rdfType == RDFImporter::AudioRef) {
Chris@145 1471 rdfSession = true;
Chris@145 1472 } else if (rdfType == RDFImporter::NotRDF) {
Chris@145 1473 rdf = false;
Chris@145 1474 }
Chris@145 1475 }
Chris@145 1476
Chris@579 1477 try {
Chris@579 1478 if (rdf) {
Chris@579 1479 if (rdfSession) {
Chris@579 1480 bool cancel = false;
Chris@579 1481 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
Chris@579 1482 return openSession(source);
Chris@579 1483 } else if (cancel) {
Chris@579 1484 return FileOpenCancelled;
Chris@579 1485 } else {
Chris@579 1486 return openLayer(source);
Chris@579 1487 }
Chris@145 1488 } else {
Chris@579 1489 if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1490 return status;
Chris@579 1491 } else if (!canImportLayer) {
Chris@579 1492 return FileOpenWrongMode;
Chris@579 1493 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1494 return status;
Chris@579 1495 } else {
Chris@579 1496 return FileOpenFailed;
Chris@579 1497 }
Chris@145 1498 }
Chris@145 1499 }
Chris@579 1500
Chris@579 1501 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
Chris@579 1502 return status;
Chris@579 1503 } else if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1504 return status;
Chris@579 1505 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
Chris@579 1506 return status;
Chris@579 1507 } else if (!canImportLayer) {
Chris@579 1508 return FileOpenWrongMode;
Chris@579 1509 } else if ((status = openImage(source)) != FileOpenFailed) {
Chris@579 1510 return status;
Chris@579 1511 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1512 return status;
Chris@579 1513 } else {
Chris@579 1514 return FileOpenFailed;
Chris@579 1515 }
Chris@579 1516 } catch (const InsufficientDiscSpace &e) {
Chris@579 1517 emit hideSplash();
Chris@579 1518 m_openingAudioFile = false;
Chris@594 1519 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
Chris@579 1520 QMessageBox::critical
Chris@579 1521 (this, tr("Not enough disc space"),
Chris@579 1522 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 1523 return FileOpenFailed;
Chris@592 1524 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
Chris@592 1525 emit hideSplash();
Chris@592 1526 m_openingAudioFile = false;
Chris@594 1527 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
Chris@592 1528 QMessageBox::critical
Chris@592 1529 (this, tr("Not enough memory"),
Chris@592 1530 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
Chris@592 1531 return FileOpenFailed;
Chris@45 1532 }
Chris@45 1533 }
Chris@45 1534
Chris@45 1535 MainWindowBase::FileOpenStatus
Chris@626 1536 MainWindowBase::openAudio(FileSource source,
Chris@626 1537 AudioFileOpenMode mode,
Chris@227 1538 QString templateName)
Chris@45 1539 {
Chris@386 1540 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
Chris@45 1541
Chris@222 1542 if (templateName == "") {
Chris@231 1543 templateName = getDefaultSessionTemplate();
Chris@577 1544 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
Chris@222 1545 }
Chris@220 1546
Chris@374 1547 // cerr << "template is: \"" << templateName << "\"" << endl;
Chris@223 1548
Chris@413 1549 if (!source.isAvailable()) {
Chris@413 1550 if (source.wasCancelled()) {
Chris@413 1551 return FileOpenCancelled;
Chris@413 1552 } else {
Chris@413 1553 return FileOpenFailed;
Chris@413 1554 }
Chris@413 1555 }
Chris@413 1556
Chris@45 1557 source.waitForData();
Chris@45 1558
Chris@45 1559 m_openingAudioFile = true;
Chris@45 1560
Chris@435 1561 sv_samplerate_t rate = 0;
Chris@45 1562
Chris@626 1563 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
Chris@626 1564 << endl;
Chris@626 1565
Chris@360 1566 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
Chris@360 1567 rate = Preferences::getInstance()->getFixedSampleRate();
Chris@626 1568 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
Chris@360 1569 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 1570 if (getMainModel()) {
Chris@626 1571 if (mode == ReplaceSession || mode == ReplaceMainModel) {
Chris@626 1572 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 1573 } else {
Chris@626 1574 rate = getMainModel()->getSampleRate();
Chris@626 1575 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
Chris@626 1576 }
Chris@552 1577 }
Chris@45 1578 }
Chris@45 1579
Chris@626 1580 if (rate == 0) {
Chris@626 1581 SVDEBUG << "Yes, preserving incoming file rate" << endl;
Chris@626 1582 }
Chris@626 1583
Chris@684 1584 auto newModel = std::make_shared<ReadOnlyWaveFileModel>(source, rate);
Chris@45 1585 if (!newModel->isOK()) {
Chris@45 1586 m_openingAudioFile = false;
Chris@413 1587 if (source.wasCancelled()) {
Chris@413 1588 return FileOpenCancelled;
Chris@413 1589 } else {
Chris@413 1590 return FileOpenFailed;
Chris@413 1591 }
Chris@45 1592 }
Chris@45 1593
Chris@687 1594 auto newModelId = ModelById::add(newModel);
Chris@720 1595 auto status = addOpenedAudioModel
Chris@720 1596 (source, newModelId, mode, templateName, true);
Chris@720 1597 m_openingAudioFile = false;
Chris@720 1598 return status;
Chris@604 1599 }
Chris@604 1600
Chris@604 1601 MainWindowBase::FileOpenStatus
Chris@604 1602 MainWindowBase::addOpenedAudioModel(FileSource source,
Chris@684 1603 ModelId newModel,
Chris@604 1604 AudioFileOpenMode mode,
Chris@604 1605 QString templateName,
Chris@604 1606 bool registerSource)
Chris@604 1607 {
Chris@45 1608 if (mode == AskUser) {
Chris@45 1609 if (getMainModel()) {
Chris@45 1610
Chris@147 1611 QSettings settings;
Chris@147 1612 settings.beginGroup("MainWindow");
Chris@221 1613 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
Chris@147 1614 settings.endGroup();
Chris@221 1615 int imode = 0;
Chris@45 1616
Chris@45 1617 QStringList items;
Chris@221 1618 items << tr("Close the current session and start a new one")
Chris@221 1619 << tr("Replace the main audio file in this session")
Chris@221 1620 << tr("Add the audio file to this session");
Chris@45 1621
Chris@45 1622 bool ok = false;
Chris@45 1623 QString item = ListInputDialog::getItem
Chris@45 1624 (this, tr("Select target for import"),
Chris@221 1625 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 1626 items, lastMode, &ok);
Chris@45 1627
Chris@45 1628 if (!ok || item.isEmpty()) {
Chris@684 1629 ModelById::release(newModel);
Chris@45 1630 m_openingAudioFile = false;
Chris@45 1631 return FileOpenCancelled;
Chris@45 1632 }
Chris@45 1633
Chris@221 1634 for (int i = 0; i < items.size(); ++i) {
Chris@221 1635 if (item == items[i]) imode = i;
Chris@221 1636 }
Chris@221 1637
Chris@147 1638 settings.beginGroup("MainWindow");
Chris@221 1639 settings.setValue("lastaudioopenmode", imode);
Chris@147 1640 settings.endGroup();
Chris@45 1641
Chris@221 1642 mode = (AudioFileOpenMode)imode;
Chris@45 1643
Chris@45 1644 } else {
Chris@221 1645 // no main model: make a new session
Chris@221 1646 mode = ReplaceSession;
Chris@45 1647 }
Chris@45 1648 }
Chris@45 1649
Chris@45 1650 if (mode == ReplaceCurrentPane) {
Chris@45 1651
Chris@45 1652 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1653 if (pane) {
Chris@45 1654 if (getMainModel()) {
Chris@45 1655 View::ModelSet models(pane->getModels());
Chris@684 1656 if (models.find(getMainModelId()) != models.end()) {
Chris@221 1657 // Current pane contains main model: replace that
Chris@45 1658 mode = ReplaceMainModel;
Chris@45 1659 }
Chris@221 1660 // Otherwise the current pane has a non-default model,
Chris@221 1661 // which we will deal with later
Chris@45 1662 } else {
Chris@221 1663 // We have no main model, so start a new session with
Chris@221 1664 // optional template
Chris@221 1665 mode = ReplaceSession;
Chris@45 1666 }
Chris@45 1667 } else {
Chris@221 1668 // We seem to have no current pane! Oh well
Chris@45 1669 mode = CreateAdditionalModel;
Chris@45 1670 }
Chris@45 1671 }
Chris@45 1672
Chris@684 1673 if (mode == CreateAdditionalModel && getMainModelId().isNone()) {
Chris@386 1674 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
Chris@221 1675 mode = ReplaceSession;
Chris@221 1676 }
Chris@221 1677
Chris@221 1678 bool loadedTemplate = false;
Chris@221 1679
Chris@221 1680 if (mode == ReplaceSession) {
Chris@258 1681
Chris@720 1682 if (!checkSaveModified()) {
Chris@720 1683 m_openingAudioFile = false;
Chris@720 1684 return FileOpenCancelled;
Chris@720 1685 }
Chris@258 1686
Chris@386 1687 SVDEBUG << "SV looking for template " << templateName << endl;
Chris@230 1688 if (templateName != "") {
Chris@230 1689 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@258 1690 if (tplStatus == FileOpenCancelled) {
Chris@577 1691 SVDEBUG << "Template load cancelled" << endl;
Chris@720 1692 m_openingAudioFile = false;
Chris@258 1693 return FileOpenCancelled;
Chris@258 1694 }
Chris@230 1695 if (tplStatus != FileOpenFailed) {
Chris@577 1696 SVDEBUG << "Template load succeeded" << endl;
Chris@230 1697 loadedTemplate = true;
Chris@221 1698 }
Chris@221 1699 }
Chris@221 1700
Chris@221 1701 if (!loadedTemplate) {
Chris@386 1702 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
Chris@221 1703 closeSession();
Chris@221 1704 createDocument();
Chris@221 1705 }
Chris@221 1706
Chris@386 1707 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
Chris@45 1708 mode = ReplaceMainModel;
Chris@45 1709 }
Chris@45 1710
Chris@164 1711 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
Chris@669 1712
Chris@45 1713 if (mode == ReplaceMainModel) {
Chris@45 1714
Chris@684 1715 ModelId prevMain = getMainModelId();
Chris@684 1716 if (!prevMain.isNone()) {
Chris@45 1717 m_playSource->removeModel(prevMain);
Chris@45 1718 }
Chris@45 1719
Chris@248 1720 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
Chris@248 1721
Chris@595 1722 m_document->setMainModel(newModel);
Chris@595 1723
Chris@595 1724 setupMenus();
Chris@595 1725
Chris@669 1726 m_originalLocation = source.getLocation();
Chris@669 1727
Chris@595 1728 if (loadedTemplate || (m_sessionFile == "")) {
Chris@595 1729 CommandHistory::getInstance()->clear();
Chris@595 1730 CommandHistory::getInstance()->documentSaved();
Chris@595 1731 m_documentModified = false;
Chris@595 1732 } else {
Chris@595 1733 if (m_documentModified) {
Chris@595 1734 m_documentModified = false;
Chris@595 1735 }
Chris@595 1736 }
Chris@45 1737
Chris@604 1738 if (!source.isRemote() && registerSource) {
Chris@604 1739 m_audioFile = source.getLocalFilename();
Chris@604 1740 }
Chris@45 1741
Chris@669 1742 updateWindowTitle();
Chris@669 1743
Chris@45 1744 } else if (mode == CreateAdditionalModel) {
Chris@45 1745
Chris@577 1746 SVCERR << "Mode is CreateAdditionalModel" << endl;
Chris@577 1747
Chris@595 1748 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1749 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1750
Chris@691 1751 m_document->addNonDerivedModel(newModel);
Chris@595 1752
Chris@595 1753 AddPaneCommand *command = new AddPaneCommand(this);
Chris@595 1754 CommandHistory::getInstance()->addCommand(command);
Chris@595 1755
Chris@595 1756 Pane *pane = command->getPane();
Chris@45 1757
Chris@47 1758 if (m_timeRulerLayer) {
Chris@577 1759 SVCERR << "Have time ruler, adding it" << endl;
Chris@47 1760 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@577 1761 } else {
Chris@577 1762 SVCERR << "Do not have time ruler" << endl;
Chris@47 1763 }
Chris@45 1764
Chris@595 1765 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1766
Chris@595 1767 if (newLayer) {
Chris@595 1768 m_document->addLayerToView(pane, newLayer);
Chris@595 1769 }
Chris@595 1770
Chris@595 1771 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1772
Chris@45 1773 } else if (mode == ReplaceCurrentPane) {
Chris@45 1774
Chris@45 1775 // We know there is a current pane, otherwise we would have
Chris@45 1776 // reset the mode to CreateAdditionalModel above; and we know
Chris@45 1777 // the current pane does not contain the main model, otherwise
Chris@45 1778 // we would have reset it to ReplaceMainModel. But we don't
Chris@45 1779 // know whether the pane contains a waveform model at all.
Chris@45 1780
Chris@45 1781 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 1782 Layer *replace = nullptr;
Chris@45 1783
Chris@45 1784 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@45 1785 Layer *layer = pane->getLayer(i);
Chris@45 1786 if (dynamic_cast<WaveformLayer *>(layer)) {
Chris@45 1787 replace = layer;
Chris@45 1788 break;
Chris@45 1789 }
Chris@45 1790 }
Chris@45 1791
Chris@595 1792 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1793 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1794
Chris@691 1795 m_document->addNonDerivedModel(newModel);
Chris@45 1796
Chris@45 1797 if (replace) {
Chris@45 1798 m_document->removeLayerFromView(pane, replace);
Chris@45 1799 }
Chris@45 1800
Chris@595 1801 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1802
Chris@595 1803 if (newLayer) {
Chris@595 1804 m_document->addLayerToView(pane, newLayer);
Chris@595 1805 }
Chris@595 1806
Chris@595 1807 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1808 }
Chris@45 1809
Chris@45 1810 updateMenuStates();
Chris@622 1811
Chris@622 1812 if (registerSource) {
Chris@622 1813 m_recentFiles.addFile(source.getLocation());
Chris@622 1814 }
Chris@604 1815 if (!source.isRemote() && registerSource) {
Chris@45 1816 // for file dialog
Chris@45 1817 registerLastOpenedFilePath(FileFinder::AudioFile,
Chris@45 1818 source.getLocalFilename());
Chris@45 1819 }
Chris@622 1820
Chris@45 1821 m_openingAudioFile = false;
Chris@45 1822
Chris@45 1823 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@45 1824
Chris@342 1825 emit audioFileLoaded();
Chris@342 1826
Chris@45 1827 return FileOpenSucceeded;
Chris@45 1828 }
Chris@45 1829
Chris@45 1830 MainWindowBase::FileOpenStatus
Chris@45 1831 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
Chris@45 1832 {
Chris@233 1833 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
Chris@135 1834
Chris@45 1835 std::set<QString> extensions;
Chris@45 1836 PlaylistFileReader::getSupportedExtensions(extensions);
Chris@152 1837 QString extension = source.getExtension().toLower();
Chris@45 1838 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
Chris@45 1839
Chris@45 1840 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1841 source.waitForData();
Chris@45 1842
Chris@45 1843 PlaylistFileReader reader(source.getLocalFilename());
Chris@45 1844 if (!reader.isOK()) return FileOpenFailed;
Chris@45 1845
Chris@45 1846 PlaylistFileReader::Playlist playlist = reader.load();
Chris@45 1847
Chris@45 1848 bool someSuccess = false;
Chris@45 1849
Chris@45 1850 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
Chris@45 1851 i != playlist.end(); ++i) {
Chris@45 1852
Chris@134 1853 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
Chris@134 1854 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1855 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
Chris@45 1856
Chris@45 1857 if (status == FileOpenCancelled) {
Chris@45 1858 return FileOpenCancelled;
Chris@45 1859 }
Chris@45 1860
Chris@45 1861 if (status == FileOpenSucceeded) {
Chris@45 1862 someSuccess = true;
Chris@45 1863 mode = CreateAdditionalModel;
Chris@45 1864 }
Chris@45 1865 }
Chris@45 1866
Chris@45 1867 if (someSuccess) return FileOpenSucceeded;
Chris@45 1868 else return FileOpenFailed;
Chris@45 1869 }
Chris@45 1870
Chris@45 1871 MainWindowBase::FileOpenStatus
Chris@45 1872 MainWindowBase::openLayer(FileSource source)
Chris@45 1873 {
Chris@233 1874 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
Chris@135 1875
Chris@45 1876 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1877
Chris@45 1878 if (!pane) {
Chris@595 1879 // shouldn't happen, as the menu action should have been disabled
Chris@595 1880 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
Chris@595 1881 return FileOpenWrongMode;
Chris@45 1882 }
Chris@45 1883
Chris@45 1884 if (!getMainModel()) {
Chris@595 1885 // shouldn't happen, as the menu action should have been disabled
Chris@595 1886 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
Chris@595 1887 return FileOpenWrongMode;
Chris@45 1888 }
Chris@45 1889
Chris@45 1890 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1891 source.waitForData();
Chris@45 1892
Chris@45 1893 QString path = source.getLocalFilename();
Chris@45 1894
Chris@145 1895 RDFImporter::RDFDocumentType rdfType =
Chris@145 1896 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@145 1897
Chris@293 1898 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
Chris@148 1899
Chris@145 1900 if (rdfType != RDFImporter::NotRDF) {
Chris@145 1901
Chris@145 1902 return openLayersFromRDF(source);
Chris@134 1903
Chris@152 1904 } else if (source.getExtension().toLower() == "svl" ||
Chris@152 1905 (source.getExtension().toLower() == "xml" &&
Chris@140 1906 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@140 1907 == SVFileReader::SVLayerFile))) {
Chris@45 1908
Chris@45 1909 PaneCallback callback(this);
Chris@45 1910 QFile file(path);
Chris@45 1911
Chris@45 1912 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@293 1913 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1914 << source.getLocation()
Chris@293 1915 << "): Failed to open file for reading" << endl;
Chris@45 1916 return FileOpenFailed;
Chris@45 1917 }
Chris@45 1918
Chris@45 1919 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 1920 connect
Chris@79 1921 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 1922 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 1923 connect
Chris@79 1924 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 1925 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@45 1926 reader.setCurrentPane(pane);
Chris@45 1927
Chris@45 1928 QXmlInputSource inputSource(&file);
Chris@45 1929 reader.parse(inputSource);
Chris@45 1930
Chris@45 1931 if (!reader.isOK()) {
Chris@293 1932 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1933 << source.getLocation()
Chris@45 1934 << "): Failed to read XML file: "
Chris@293 1935 << reader.getErrorString() << endl;
Chris@45 1936 return FileOpenFailed;
Chris@45 1937 }
Chris@45 1938
Chris@164 1939 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
Chris@164 1940
Chris@45 1941 m_recentFiles.addFile(source.getLocation());
Chris@45 1942
Chris@45 1943 if (!source.isRemote()) {
Chris@45 1944 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
Chris@45 1945 }
Chris@45 1946
Chris@75 1947 return FileOpenSucceeded;
Chris@75 1948
Chris@45 1949 } else {
Chris@45 1950
Chris@45 1951 try {
Chris@45 1952
Chris@109 1953 MIDIFileImportDialog midiDlg(this);
Chris@109 1954
Chris@684 1955 Model *newModelPtr = DataFileReaderFactory::loadNonCSV
Chris@109 1956 (path, &midiDlg, getMainModel()->getSampleRate());
Chris@45 1957
Chris@684 1958 if (!newModelPtr) {
Chris@643 1959 CSVFormatDialog *dialog =
Chris@643 1960 new CSVFormatDialog(this,
Chris@643 1961 path,
Chris@643 1962 getMainModel()->getSampleRate(),
Chris@643 1963 5);
Chris@109 1964 if (dialog->exec() == QDialog::Accepted) {
Chris@684 1965 newModelPtr = DataFileReaderFactory::loadCSV
Chris@109 1966 (path, dialog->getFormat(),
Chris@109 1967 getMainModel()->getSampleRate());
Chris@109 1968 }
Chris@619 1969 delete dialog;
Chris@109 1970 }
Chris@109 1971
Chris@684 1972 if (newModelPtr) {
Chris@45 1973
Chris@233 1974 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
Chris@45 1975
Chris@164 1976 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
Chris@164 1977
Chris@687 1978 ModelId modelId =
Chris@687 1979 ModelById::add(std::shared_ptr<Model>(newModelPtr));
Chris@684 1980
Chris@684 1981 Layer *newLayer = m_document->createImportedLayer(modelId);
Chris@45 1982
Chris@45 1983 if (newLayer) {
Chris@45 1984
Chris@45 1985 m_document->addLayerToView(pane, newLayer);
Chris@88 1986 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@88 1987
Chris@45 1988 m_recentFiles.addFile(source.getLocation());
Chris@45 1989
Chris@45 1990 if (!source.isRemote()) {
Chris@45 1991 registerLastOpenedFilePath
Chris@45 1992 (FileFinder::LayerFile,
Chris@45 1993 path); // for file dialog
Chris@45 1994 }
Chris@88 1995
Chris@45 1996 return FileOpenSucceeded;
Chris@45 1997 }
Chris@45 1998 }
Chris@45 1999 } catch (DataFileReaderFactory::Exception e) {
Chris@45 2000 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@45 2001 return FileOpenCancelled;
Chris@45 2002 }
Chris@45 2003 }
Chris@45 2004 }
Chris@45 2005
Chris@45 2006 return FileOpenFailed;
Chris@45 2007 }
Chris@45 2008
Chris@45 2009 MainWindowBase::FileOpenStatus
Chris@45 2010 MainWindowBase::openImage(FileSource source)
Chris@45 2011 {
Chris@233 2012 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
Chris@135 2013
Chris@45 2014 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 2015
Chris@45 2016 if (!pane) {
Chris@595 2017 // shouldn't happen, as the menu action should have been disabled
Chris@595 2018 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
Chris@595 2019 return FileOpenWrongMode;
Chris@45 2020 }
Chris@45 2021
Chris@684 2022 if (!getMainModel()) {
Chris@45 2023 return FileOpenWrongMode;
Chris@45 2024 }
Chris@45 2025
Chris@45 2026 bool newLayer = false;
Chris@45 2027
Chris@45 2028 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
Chris@45 2029 if (!il) {
Chris@45 2030 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
Chris@45 2031 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
Chris@45 2032 if (il) break;
Chris@45 2033 }
Chris@45 2034 }
Chris@45 2035 if (!il) {
Chris@45 2036 il = dynamic_cast<ImageLayer *>
Chris@45 2037 (m_document->createEmptyLayer(LayerFactory::Image));
Chris@45 2038 if (!il) return FileOpenFailed;
Chris@45 2039 newLayer = true;
Chris@45 2040 }
Chris@45 2041
Chris@45 2042 // We don't put the image file in Recent Files
Chris@45 2043
Chris@293 2044 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
Chris@45 2045
Chris@45 2046 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
Chris@45 2047 if (newLayer) {
Chris@52 2048 m_document->deleteLayer(il); // also releases its model
Chris@45 2049 }
Chris@45 2050 return FileOpenFailed;
Chris@45 2051 } else {
Chris@45 2052 if (newLayer) {
Chris@45 2053 m_document->addLayerToView(pane, il);
Chris@45 2054 }
Chris@45 2055 m_paneStack->setCurrentLayer(pane, il);
Chris@45 2056 }
Chris@45 2057
Chris@45 2058 return FileOpenSucceeded;
Chris@45 2059 }
Chris@45 2060
Chris@45 2061 MainWindowBase::FileOpenStatus
Chris@427 2062 MainWindowBase::openDirOfAudio(QString dirPath)
Chris@427 2063 {
Chris@427 2064 QDir dir(dirPath);
Chris@427 2065 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
Chris@427 2066 files.sort();
Chris@427 2067
Chris@427 2068 FileOpenStatus status = FileOpenFailed;
Chris@427 2069 bool first = true;
Chris@427 2070 bool cancelled = false;
Chris@427 2071
Chris@427 2072 foreach (QString file, files) {
Chris@427 2073
Chris@427 2074 FileSource source(dir.filePath(file));
Chris@427 2075 if (!source.isAvailable()) {
Chris@427 2076 continue;
Chris@427 2077 }
Chris@427 2078
Chris@427 2079 if (AudioFileReaderFactory::getKnownExtensions().contains
Chris@427 2080 (source.getExtension().toLower())) {
Chris@427 2081
Chris@427 2082 AudioFileOpenMode mode = CreateAdditionalModel;
Chris@427 2083 if (first) mode = ReplaceSession;
Chris@427 2084
Chris@427 2085 switch (openAudio(source, mode)) {
Chris@427 2086 case FileOpenSucceeded:
Chris@427 2087 status = FileOpenSucceeded;
Chris@427 2088 first = false;
Chris@427 2089 break;
Chris@427 2090 case FileOpenFailed:
Chris@427 2091 break;
Chris@427 2092 case FileOpenCancelled:
Chris@427 2093 cancelled = true;
Chris@427 2094 break;
Chris@427 2095 case FileOpenWrongMode:
Chris@427 2096 break;
Chris@427 2097 }
Chris@427 2098 }
Chris@427 2099
Chris@427 2100 if (cancelled) break;
Chris@427 2101 }
Chris@427 2102
Chris@427 2103 return status;
Chris@427 2104 }
Chris@427 2105
Chris@427 2106 MainWindowBase::FileOpenStatus
Chris@373 2107 MainWindowBase::openSessionPath(QString fileOrUrl)
Chris@45 2108 {
Chris@134 2109 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
Chris@134 2110 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 2111 return openSession(FileSource(fileOrUrl, &dialog));
Chris@45 2112 }
Chris@45 2113
Chris@45 2114 MainWindowBase::FileOpenStatus
Chris@45 2115 MainWindowBase::openSession(FileSource source)
Chris@45 2116 {
Chris@233 2117 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
Chris@135 2118
Chris@45 2119 if (!source.isAvailable()) return FileOpenFailed;
Chris@145 2120 source.waitForData();
Chris@141 2121
Chris@341 2122 QString sessionExt =
Chris@341 2123 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
Chris@341 2124
Chris@341 2125 if (source.getExtension().toLower() != sessionExt) {
Chris@145 2126
Chris@145 2127 RDFImporter::RDFDocumentType rdfType =
Chris@145 2128 RDFImporter::identifyDocumentType
Chris@145 2129 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
Chris@145 2130
Chris@293 2131 // cerr << "RDF type: " << (int)rdfType << endl;
Chris@148 2132
Chris@145 2133 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@145 2134 rdfType == RDFImporter::AudioRef) {
Chris@145 2135 return openSessionFromRDF(source);
Chris@145 2136 } else if (rdfType != RDFImporter::NotRDF) {
Chris@145 2137 return FileOpenFailed;
Chris@145 2138 }
Chris@145 2139
Chris@152 2140 if (source.getExtension().toLower() == "xml") {
Chris@140 2141 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
Chris@140 2142 SVFileReader::SVSessionFile) {
Chris@293 2143 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
Chris@140 2144 } else {
Chris@140 2145 return FileOpenFailed;
Chris@140 2146 }
Chris@140 2147 } else {
Chris@140 2148 return FileOpenFailed;
Chris@140 2149 }
Chris@140 2150 }
Chris@45 2151
Chris@636 2152 QXmlInputSource *inputSource = nullptr;
Chris@636 2153 BZipFileDevice *bzFile = nullptr;
Chris@636 2154 QFile *rawFile = nullptr;
Chris@140 2155
Chris@341 2156 if (source.getExtension().toLower() == sessionExt) {
Chris@140 2157 bzFile = new BZipFileDevice(source.getLocalFilename());
Chris@140 2158 if (!bzFile->open(QIODevice::ReadOnly)) {
Chris@140 2159 delete bzFile;
Chris@140 2160 return FileOpenFailed;
Chris@140 2161 }
Chris@140 2162 inputSource = new QXmlInputSource(bzFile);
Chris@140 2163 } else {
Chris@140 2164 rawFile = new QFile(source.getLocalFilename());
Chris@140 2165 inputSource = new QXmlInputSource(rawFile);
Chris@140 2166 }
Chris@140 2167
Chris@140 2168 if (!checkSaveModified()) {
Chris@140 2169 if (bzFile) bzFile->close();
Chris@140 2170 delete inputSource;
Chris@140 2171 delete bzFile;
Chris@140 2172 delete rawFile;
Chris@140 2173 return FileOpenCancelled;
Chris@140 2174 }
Chris@45 2175
Chris@45 2176 QString error;
Chris@45 2177 closeSession();
Chris@45 2178 createDocument();
Chris@45 2179
Chris@45 2180 PaneCallback callback(this);
Chris@45 2181 m_viewManager->clearSelections();
Chris@45 2182
Chris@45 2183 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 2184 connect
Chris@79 2185 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 2186 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 2187 connect
Chris@79 2188 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 2189 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@140 2190
Chris@140 2191 reader.parse(*inputSource);
Chris@45 2192
Chris@45 2193 if (!reader.isOK()) {
Chris@45 2194 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@45 2195 }
Chris@45 2196
Chris@140 2197 if (bzFile) bzFile->close();
Chris@140 2198
Chris@140 2199 delete inputSource;
Chris@140 2200 delete bzFile;
Chris@140 2201 delete rawFile;
Chris@45 2202
Chris@45 2203 bool ok = (error == "");
Chris@45 2204
Chris@45 2205 if (ok) {
Chris@45 2206
Chris@164 2207 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
Chris@164 2208
Chris@601 2209 if (!source.isRemote() && !m_document->isIncomplete()) {
Chris@601 2210 // Setting the session file path enables the Save (as
Chris@601 2211 // opposed to Save As...) option. We can't do this if we
Chris@601 2212 // don't have a local path to save to, but we also don't
Chris@601 2213 // want to do it if we failed to find an audio file or
Chris@601 2214 // similar on load, as the audio reference would then end
Chris@601 2215 // up being lost from any saved or auto-saved-on-exit copy
Chris@601 2216 m_sessionFile = source.getLocalFilename();
Chris@602 2217 } else {
Chris@602 2218 QMessageBox::warning
Chris@602 2219 (this,
Chris@602 2220 tr("Incomplete session loaded"),
Chris@603 2221 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 2222 QMessageBox::Ok);
Chris@601 2223 }
Chris@595 2224
Chris@669 2225 updateWindowTitle();
Chris@595 2226 setupMenus();
Chris@577 2227 findTimeRulerLayer();
Chris@45 2228
Chris@595 2229 CommandHistory::getInstance()->clear();
Chris@595 2230 CommandHistory::getInstance()->documentSaved();
Chris@595 2231 m_documentModified = false;
Chris@595 2232 updateMenuStates();
Chris@45 2233
Chris@227 2234 m_recentFiles.addFile(source.getLocation());
Chris@45 2235
Chris@45 2236 if (!source.isRemote()) {
Chris@45 2237 // for file dialog
Chris@45 2238 registerLastOpenedFilePath(FileFinder::SessionFile,
Chris@227 2239 source.getLocalFilename());
Chris@45 2240 }
Chris@45 2241
Chris@669 2242 m_originalLocation = source.getLocation();
Chris@669 2243
Chris@342 2244 emit sessionLoaded();
Chris@342 2245
Chris@669 2246 updateWindowTitle();
Chris@45 2247 }
Chris@669 2248
Chris@45 2249 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@45 2250 }
Chris@45 2251
Chris@141 2252 MainWindowBase::FileOpenStatus
Chris@230 2253 MainWindowBase::openSessionTemplate(QString templateName)
Chris@230 2254 {
Chris@230 2255 // Template in the user's template directory takes
Chris@230 2256 // priority over a bundled one; we don't unbundle, but
Chris@230 2257 // open directly from the bundled file (where applicable)
Chris@230 2258 ResourceFinder rf;
Chris@230 2259 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
Chris@230 2260 if (tfile != "") {
Chris@294 2261 cerr << "SV loading template file " << tfile << endl;
Chris@230 2262 return openSessionTemplate(FileSource("file:" + tfile));
Chris@230 2263 } else {
Chris@230 2264 return FileOpenFailed;
Chris@230 2265 }
Chris@230 2266 }
Chris@230 2267
Chris@230 2268 MainWindowBase::FileOpenStatus
Chris@227 2269 MainWindowBase::openSessionTemplate(FileSource source)
Chris@227 2270 {
Chris@294 2271 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
Chris@227 2272
Chris@227 2273 if (!source.isAvailable()) return FileOpenFailed;
Chris@227 2274 source.waitForData();
Chris@227 2275
Chris@636 2276 QXmlInputSource *inputSource = nullptr;
Chris@636 2277 QFile *file = nullptr;
Chris@227 2278
Chris@227 2279 file = new QFile(source.getLocalFilename());
Chris@227 2280 inputSource = new QXmlInputSource(file);
Chris@227 2281
Chris@227 2282 if (!checkSaveModified()) {
Chris@227 2283 delete inputSource;
Chris@227 2284 delete file;
Chris@227 2285 return FileOpenCancelled;
Chris@227 2286 }
Chris@227 2287
Chris@227 2288 QString error;
Chris@227 2289 closeSession();
Chris@227 2290 createDocument();
Chris@227 2291
Chris@227 2292 PaneCallback callback(this);
Chris@227 2293 m_viewManager->clearSelections();
Chris@227 2294
Chris@227 2295 SVFileReader reader(m_document, callback, source.getLocation());
Chris@227 2296 connect
Chris@227 2297 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@227 2298 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@227 2299 connect
Chris@227 2300 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@227 2301 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@227 2302
Chris@227 2303 reader.parse(*inputSource);
Chris@227 2304
Chris@227 2305 if (!reader.isOK()) {
Chris@227 2306 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@227 2307 }
Chris@227 2308
Chris@227 2309 delete inputSource;
Chris@227 2310 delete file;
Chris@227 2311
Chris@227 2312 bool ok = (error == "");
Chris@227 2313
Chris@227 2314 if (ok) {
Chris@227 2315
Chris@227 2316 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
Chris@227 2317
Chris@595 2318 setupMenus();
Chris@577 2319 findTimeRulerLayer();
Chris@227 2320
Chris@595 2321 CommandHistory::getInstance()->clear();
Chris@595 2322 CommandHistory::getInstance()->documentSaved();
Chris@595 2323 m_documentModified = false;
Chris@595 2324 updateMenuStates();
Chris@342 2325
Chris@342 2326 emit sessionLoaded();
Chris@227 2327 }
Chris@227 2328
Chris@669 2329 updateWindowTitle();
Chris@669 2330
Chris@227 2331 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@227 2332 }
Chris@227 2333
Chris@227 2334 MainWindowBase::FileOpenStatus
Chris@141 2335 MainWindowBase::openSessionFromRDF(FileSource source)
Chris@141 2336 {
Chris@233 2337 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
Chris@141 2338
Chris@141 2339 if (!source.isAvailable()) return FileOpenFailed;
Chris@141 2340 source.waitForData();
Chris@141 2341
Chris@145 2342 if (!checkSaveModified()) {
Chris@145 2343 return FileOpenCancelled;
Chris@141 2344 }
Chris@143 2345
Chris@145 2346 closeSession();
Chris@145 2347 createDocument();
Chris@145 2348
Chris@145 2349 FileOpenStatus status = openLayersFromRDF(source);
Chris@141 2350
Chris@141 2351 setupMenus();
Chris@577 2352 findTimeRulerLayer();
Chris@669 2353
Chris@141 2354 CommandHistory::getInstance()->clear();
Chris@141 2355 CommandHistory::getInstance()->documentSaved();
Chris@141 2356 m_documentModified = false;
Chris@669 2357 updateWindowTitle();
Chris@145 2358
Chris@342 2359 emit sessionLoaded();
Chris@342 2360
Chris@145 2361 return status;
Chris@145 2362 }
Chris@145 2363
Chris@145 2364 MainWindowBase::FileOpenStatus
Chris@145 2365 MainWindowBase::openLayersFromRDF(FileSource source)
Chris@145 2366 {
Chris@435 2367 sv_samplerate_t rate = 0;
Chris@145 2368
Chris@233 2369 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
Chris@186 2370
Chris@145 2371 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
Chris@145 2372 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@145 2373
Chris@145 2374 if (getMainModel()) {
Chris@145 2375 rate = getMainModel()->getSampleRate();
Chris@145 2376 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 2377 if (getMainModel()) {
Chris@552 2378 rate = getMainModel()->getSampleRate();
Chris@552 2379 }
Chris@145 2380 }
Chris@145 2381
Chris@145 2382 RDFImporter importer
Chris@145 2383 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
Chris@145 2384
Chris@145 2385 if (!importer.isOK()) {
Chris@147 2386 if (importer.getErrorString() != "") {
Chris@147 2387 QMessageBox::critical
Chris@147 2388 (this, tr("Failed to import RDF"),
Chris@147 2389 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
Chris@147 2390 .arg(source.getLocation()).arg(importer.getErrorString()));
Chris@147 2391 }
Chris@145 2392 return FileOpenFailed;
Chris@145 2393 }
Chris@145 2394
Chris@687 2395 std::vector<ModelId> modelIds = importer.getDataModels(&dialog);
Chris@145 2396
Chris@145 2397 dialog.setMessage(tr("Importing from RDF..."));
Chris@145 2398
Chris@687 2399 if (modelIds.empty()) {
Chris@186 2400 QMessageBox::critical
Chris@186 2401 (this, tr("Failed to import RDF"),
Chris@186 2402 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 2403 return FileOpenFailed;
Chris@145 2404 }
Chris@145 2405
Chris@164 2406 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
Chris@684 2407
Chris@684 2408 std::set<ModelId> added;
Chris@684 2409
Chris@684 2410 for (auto modelId: modelIds) {
Chris@684 2411
Chris@684 2412 if (ModelById::isa<WaveFileModel>(modelId)) {
Chris@145 2413
Chris@145 2414 Pane *pane = addPaneToStack();
Chris@636 2415 Layer *layer = nullptr;
Chris@145 2416
Chris@145 2417 if (m_timeRulerLayer) {
Chris@145 2418 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@145 2419 }
Chris@145 2420
Chris@145 2421 if (!getMainModel()) {
Chris@684 2422 m_document->setMainModel(modelId);
Chris@145 2423 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@145 2424 } else {
Chris@684 2425 layer = m_document->createImportedLayer(modelId);
Chris@145 2426 }
Chris@145 2427
Chris@145 2428 m_document->addLayerToView(pane, layer);
Chris@145 2429
Chris@684 2430 added.insert(modelId);
Chris@684 2431
Chris@684 2432 for (auto otherId: modelIds) {
Chris@684 2433
Chris@684 2434 if (otherId == modelId) continue;
Chris@684 2435
Chris@684 2436 bool isDependent = false;
Chris@684 2437 if (auto dm = ModelById::get(otherId)) {
Chris@684 2438 if (dm->getSourceModel() == modelId) {
Chris@684 2439 isDependent = true;
Chris@684 2440 }
Chris@684 2441 }
Chris@684 2442 if (!isDependent) continue;
Chris@684 2443
Chris@684 2444 layer = m_document->createImportedLayer(otherId);
Chris@145 2445
Chris@145 2446 if (layer->isLayerOpaque() ||
Chris@145 2447 dynamic_cast<Colour3DPlotLayer *>(layer)) {
Chris@145 2448
Chris@156 2449 // these always go in a new pane, with nothing
Chris@156 2450 // else going in the same pane
Chris@156 2451
Chris@145 2452 Pane *singleLayerPane = addPaneToStack();
Chris@145 2453 if (m_timeRulerLayer) {
Chris@145 2454 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2455 }
Chris@145 2456 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2457
Chris@156 2458 } else if (layer->getLayerColourSignificance() ==
Chris@156 2459 Layer::ColourHasMeaningfulValue) {
Chris@156 2460
Chris@156 2461 // these can go in a pane with something else, but
Chris@156 2462 // only if none of the something elses also have
Chris@156 2463 // this quality
Chris@156 2464
Chris@156 2465 bool needNewPane = false;
Chris@156 2466 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@156 2467 Layer *otherLayer = pane->getLayer(i);
Chris@156 2468 if (otherLayer &&
Chris@156 2469 (otherLayer->getLayerColourSignificance() ==
Chris@156 2470 Layer::ColourHasMeaningfulValue)) {
Chris@156 2471 needNewPane = true;
Chris@156 2472 break;
Chris@156 2473 }
Chris@156 2474 }
Chris@156 2475 if (needNewPane) {
Chris@156 2476 pane = addPaneToStack();
Chris@156 2477 }
Chris@156 2478
Chris@156 2479 m_document->addLayerToView(pane, layer);
Chris@156 2480
Chris@145 2481 } else {
Chris@145 2482
Chris@145 2483 if (pane->getLayerCount() > 4) {
Chris@145 2484 pane = addPaneToStack();
Chris@145 2485 }
Chris@145 2486
Chris@145 2487 m_document->addLayerToView(pane, layer);
Chris@145 2488 }
Chris@145 2489
Chris@684 2490 added.insert(otherId);
Chris@145 2491 }
Chris@145 2492 }
Chris@145 2493 }
Chris@145 2494
Chris@684 2495 for (auto modelId : modelIds) {
Chris@684 2496
Chris@684 2497 if (added.find(modelId) == added.end()) {
Chris@145 2498
Chris@684 2499 Layer *layer = m_document->createImportedLayer(modelId);
Chris@145 2500 if (!layer) return FileOpenFailed;
Chris@145 2501
Chris@145 2502 Pane *singleLayerPane = addPaneToStack();
Chris@145 2503 if (m_timeRulerLayer) {
Chris@145 2504 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2505 }
Chris@145 2506 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2507 }
Chris@145 2508 }
Chris@145 2509
Chris@145 2510 m_recentFiles.addFile(source.getLocation());
Chris@145 2511 return FileOpenSucceeded;
Chris@141 2512 }
Chris@141 2513
Chris@584 2514 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
Chris@584 2515 {
Chris@584 2516 public:
Chris@584 2517 void log(std::string message) const override {
Chris@584 2518 SVDEBUG << message << endl;
Chris@584 2519 }
Chris@584 2520 };
Chris@584 2521
Chris@45 2522 void
Chris@475 2523 MainWindowBase::createAudioIO()
Chris@45 2524 {
Chris@475 2525 if (m_playTarget || m_audioIO) return;
Chris@475 2526
Chris@584 2527 static AudioLogCallback audioLogCallback;
Chris@584 2528 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
Chris@714 2529
Chris@714 2530 if (m_audioMode == AUDIO_NONE) return;
Chris@45 2531
Chris@126 2532 QSettings settings;
Chris@126 2533 settings.beginGroup("Preferences");
Chris@547 2534 QString implementation = settings.value
Chris@547 2535 ("audio-target", "").toString();
Chris@547 2536 QString suffix;
Chris@547 2537 if (implementation != "") suffix = "-" + implementation;
Chris@547 2538 QString recordDevice = settings.value
Chris@547 2539 ("audio-record-device" + suffix, "").toString();
Chris@547 2540 QString playbackDevice = settings.value
Chris@547 2541 ("audio-playback-device" + suffix, "").toString();
Chris@126 2542 settings.endGroup();
Chris@547 2543
Chris@547 2544 if (implementation == "auto") {
Chris@547 2545 implementation = "";
Chris@547 2546 }
Chris@468 2547
Chris@547 2548 breakfastquay::AudioFactory::Preference preference;
Chris@547 2549 preference.implementation = implementation.toStdString();
Chris@547 2550 preference.recordDevice = recordDevice.toStdString();
Chris@547 2551 preference.playbackDevice = playbackDevice.toStdString();
Chris@547 2552
Chris@547 2553 SVCERR << "createAudioIO: Preferred implementation = \""
Chris@547 2554 << preference.implementation << "\"" << endl;
Chris@547 2555 SVCERR << "createAudioIO: Preferred playback device = \""
Chris@547 2556 << preference.playbackDevice << "\"" << endl;
Chris@547 2557 SVCERR << "createAudioIO: Preferred record device = \""
Chris@547 2558 << preference.recordDevice << "\"" << endl;
Chris@475 2559
Chris@551 2560 if (!m_resamplerWrapper) {
Chris@551 2561 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource);
Chris@551 2562 m_playSource->setResamplerWrapper(m_resamplerWrapper);
Chris@551 2563 }
Chris@569 2564
Chris@569 2565 std::string errorString;
Chris@551 2566
Chris@714 2567 if (m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@475 2568 m_audioIO = breakfastquay::AudioFactory::
Chris@569 2569 createCallbackIO(m_recordTarget, m_resamplerWrapper,
Chris@569 2570 preference, errorString);
Chris@525 2571 if (m_audioIO) {
Chris@611 2572 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2573 m_audioIO->suspend(); // start in suspended state
Chris@525 2574 m_playSource->setSystemPlaybackTarget(m_audioIO);
Chris@586 2575 } else {
Chris@586 2576 // Failed to create audio I/O; this may just mean there is
Chris@586 2577 // no record device, so fall through to see what happens
Chris@586 2578 // next. We only report complete failure if we end up with
Chris@586 2579 // neither m_audioIO nor m_playTarget.
Chris@525 2580 }
Chris@586 2581 }
Chris@586 2582
Chris@586 2583 if (!m_audioIO) {
Chris@475 2584 m_playTarget = breakfastquay::AudioFactory::
Chris@569 2585 createCallbackPlayTarget(m_resamplerWrapper,
Chris@569 2586 preference, errorString);
Chris@525 2587 if (m_playTarget) {
Chris@611 2588 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2589 m_playTarget->suspend(); // start in suspended state
Chris@525 2590 m_playSource->setSystemPlaybackTarget(m_playTarget);
Chris@525 2591 }
Chris@475 2592 }
Chris@475 2593
Chris@475 2594 if (!m_playTarget && !m_audioIO) {
Chris@104 2595 emit hideSplash();
Chris@569 2596 QString message;
Chris@569 2597 QString error = errorString.c_str();
Chris@569 2598 QString firstBit, secondBit;
Chris@547 2599 if (implementation == "") {
Chris@569 2600 if (error == "") {
Chris@569 2601 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
Chris@569 2602 } else {
Chris@569 2603 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
Chris@569 2604 }
Chris@714 2605 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2606 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2607 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
Chris@569 2608 } else {
Chris@569 2609 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
Chris@569 2610 }
Chris@126 2611 } else {
Chris@569 2612 QString driverName = breakfastquay::AudioFactory::
Chris@569 2613 getImplementationDescription(implementation.toStdString())
Chris@569 2614 .c_str();
Chris@569 2615 if (error == "") {
Chris@569 2616 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
Chris@569 2617 } else {
Chris@569 2618 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
Chris@569 2619 }
Chris@714 2620 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
Chris@714 2621 m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
Chris@569 2622 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
Chris@569 2623 } else {
Chris@569 2624 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
Chris@569 2625 }
Chris@126 2626 }
Chris@570 2627 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
Chris@570 2628 << implementation << "\": error is: " << error << endl;
Chris@569 2629 QMessageBox::warning(this, tr("Couldn't open audio device"),
Chris@569 2630 firstBit + secondBit, QMessageBox::Ok);
Chris@45 2631 }
Chris@45 2632 }
Chris@45 2633
Chris@556 2634 void
Chris@556 2635 MainWindowBase::deleteAudioIO()
Chris@556 2636 {
Chris@556 2637 // First prevent this trying to call target.
Chris@559 2638 if (m_playSource) {
Chris@636 2639 m_playSource->setSystemPlaybackTarget(nullptr);
Chris@636 2640 m_playSource->setResamplerWrapper(nullptr);
Chris@559 2641 }
Chris@556 2642
Chris@556 2643 // Then delete the breakfastquay::System object.
Chris@556 2644 // Only one of these two exists!
Chris@556 2645 delete m_audioIO;
Chris@556 2646 delete m_playTarget;
Chris@556 2647
Chris@559 2648 // And the breakfastquay resampler wrapper. We need to
Chris@559 2649 // delete/recreate this if the channel count changes, which is one
Chris@559 2650 // of the use cases for recreateAudioIO() calling this
Chris@559 2651 delete m_resamplerWrapper;
Chris@559 2652
Chris@636 2653 m_audioIO = nullptr;
Chris@636 2654 m_playTarget = nullptr;
Chris@636 2655 m_resamplerWrapper = 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@423 2717 connect(m_document, SIGNAL(alignmentFailed(QString)),
Chris@423 2718 this, SLOT(alignmentFailed(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