annotate framework/MainWindowBase.cpp @ 687:e0b0f3e163ca by-id

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