annotate framework/MainWindowBase.cpp @ 704:286bd8bb13cc

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