annotate framework/MainWindowBase.cpp @ 715:ec8f21a20fa7

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