annotate framework/MainWindowBase.cpp @ 656:d8beda8a2126 single-point

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