annotate framework/MainWindowBase.cpp @ 712:8e9702c0b9c7

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