annotate framework/MainWindowBase.cpp @ 686:610fa108fbcc by-id

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