annotate framework/MainWindowBase.cpp @ 658:94f518af106c single-point

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