annotate framework/MainWindowBase.cpp @ 676:0d4236961c97

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