annotate framework/MainWindowBase.cpp @ 721:453029d6e9bf spectrogram-export

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