annotate framework/MainWindowBase.cpp @ 722:b06b18c15fd4 spectrogram-export

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