annotate framework/MainWindowBase.cpp @ 685:7540733f5480 by-id

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