annotate framework/MainWindowBase.cpp @ 667:31ea416fea3c single-point

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