annotate framework/MainWindowBase.cpp @ 659:49cf3787cf22 single-point

Add exportLayerTo (moved here from MainWindow)
author Chris Cannam
date Thu, 28 Mar 2019 14:34:00 +0000
parents 94f518af106c
children 31ea416fea3c
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@160 2683 emit replacedDocument();
Chris@45 2684 }
Chris@45 2685
Chris@45 2686 bool
Chris@45 2687 MainWindowBase::saveSessionFile(QString path)
Chris@45 2688 {
Chris@217 2689 try {
Chris@217 2690
Chris@217 2691 TempWriteFile temp(path);
Chris@217 2692
Chris@217 2693 BZipFileDevice bzFile(temp.getTemporaryFilename());
Chris@217 2694 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@293 2695 cerr << "Failed to open session file \""
Chris@294 2696 << temp.getTemporaryFilename()
Chris@217 2697 << "\" for writing: "
Chris@293 2698 << bzFile.errorString() << endl;
Chris@217 2699 return false;
Chris@217 2700 }
Chris@217 2701
Chris@217 2702 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@217 2703
Chris@217 2704 QTextStream out(&bzFile);
Chris@432 2705 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2706 toXml(out, false);
Chris@217 2707 out.flush();
Chris@217 2708
Chris@217 2709 QApplication::restoreOverrideCursor();
Chris@217 2710
Chris@217 2711 if (!bzFile.isOK()) {
Chris@217 2712 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2713 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2714 .arg(path).arg(bzFile.errorString()));
Chris@217 2715 bzFile.close();
Chris@217 2716 return false;
Chris@217 2717 }
Chris@217 2718
Chris@217 2719 bzFile.close();
Chris@217 2720 temp.moveToTarget();
Chris@217 2721 return true;
Chris@217 2722
Chris@217 2723 } catch (FileOperationFailed &f) {
Chris@217 2724
Chris@217 2725 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2726 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2727 .arg(path).arg(f.what()));
Chris@45 2728 return false;
Chris@45 2729 }
Chris@45 2730 }
Chris@45 2731
Chris@224 2732 bool
Chris@224 2733 MainWindowBase::saveSessionTemplate(QString path)
Chris@224 2734 {
Chris@224 2735 try {
Chris@224 2736
Chris@224 2737 TempWriteFile temp(path);
Chris@224 2738
Chris@224 2739 QFile file(temp.getTemporaryFilename());
Chris@224 2740 if (!file.open(QIODevice::WriteOnly)) {
Chris@293 2741 cerr << "Failed to open session template file \""
Chris@294 2742 << temp.getTemporaryFilename()
Chris@224 2743 << "\" for writing: "
Chris@294 2744 << file.errorString() << endl;
Chris@224 2745 return false;
Chris@224 2746 }
Chris@224 2747
Chris@224 2748 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@224 2749
Chris@224 2750 QTextStream out(&file);
Chris@432 2751 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2752 toXml(out, true);
Chris@224 2753 out.flush();
Chris@224 2754
Chris@224 2755 QApplication::restoreOverrideCursor();
Chris@224 2756
Chris@224 2757 file.close();
Chris@224 2758 temp.moveToTarget();
Chris@224 2759 return true;
Chris@224 2760
Chris@224 2761 } catch (FileOperationFailed &f) {
Chris@224 2762
Chris@224 2763 QMessageBox::critical(this, tr("Failed to write file"),
Chris@224 2764 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@224 2765 .arg(path).arg(f.what()));
Chris@224 2766 return false;
Chris@224 2767 }
Chris@224 2768 }
Chris@224 2769
Chris@659 2770 bool
Chris@659 2771 MainWindowBase::exportLayerTo(Layer *layer, QString path, QString &error)
Chris@659 2772 {
Chris@659 2773 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@659 2774
Chris@659 2775 QString suffix = QFileInfo(path).suffix().toLower();
Chris@659 2776
Chris@659 2777 Model *model = layer->getModel();
Chris@659 2778
Chris@659 2779 if (suffix == "xml" || suffix == "svl") {
Chris@659 2780
Chris@659 2781 QFile file(path);
Chris@659 2782 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@659 2783 error = tr("Failed to open file %1 for writing").arg(path);
Chris@659 2784 } else {
Chris@659 2785 QTextStream out(&file);
Chris@659 2786 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@659 2787 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@659 2788 << "<!DOCTYPE sonic-visualiser>\n"
Chris@659 2789 << "<sv>\n"
Chris@659 2790 << " <data>\n";
Chris@659 2791
Chris@659 2792 model->toXml(out, " ");
Chris@659 2793
Chris@659 2794 out << " </data>\n"
Chris@659 2795 << " <display>\n";
Chris@659 2796
Chris@659 2797 layer->toXml(out, " ");
Chris@659 2798
Chris@659 2799 out << " </display>\n"
Chris@659 2800 << "</sv>\n";
Chris@659 2801 }
Chris@659 2802
Chris@659 2803 } else if (suffix == "mid" || suffix == "midi") {
Chris@659 2804
Chris@659 2805 NoteModel *nm = dynamic_cast<NoteModel *>(model);
Chris@659 2806
Chris@659 2807 if (!nm) {
Chris@659 2808 error = tr("Can't export non-note layers to MIDI");
Chris@659 2809 } else {
Chris@659 2810 MIDIFileWriter writer(path, nm, nm->getSampleRate());
Chris@659 2811 writer.write();
Chris@659 2812 if (!writer.isOK()) {
Chris@659 2813 error = writer.getError();
Chris@659 2814 }
Chris@659 2815 }
Chris@659 2816
Chris@659 2817 } else if (suffix == "ttl" || suffix == "n3") {
Chris@659 2818
Chris@659 2819 if (!RDFExporter::canExportModel(model)) {
Chris@659 2820 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
Chris@659 2821 } else {
Chris@659 2822 RDFExporter exporter(path, model);
Chris@659 2823 exporter.write();
Chris@659 2824 if (!exporter.isOK()) {
Chris@659 2825 error = exporter.getError();
Chris@659 2826 }
Chris@659 2827 }
Chris@659 2828
Chris@659 2829 } else {
Chris@659 2830
Chris@659 2831 CSVFileWriter writer(path, model,
Chris@659 2832 ((suffix == "csv") ? "," : "\t"));
Chris@659 2833 writer.write();
Chris@659 2834
Chris@659 2835 if (!writer.isOK()) {
Chris@659 2836 error = writer.getError();
Chris@659 2837 }
Chris@659 2838 }
Chris@659 2839
Chris@659 2840 return (error == "");
Chris@659 2841 }
Chris@659 2842
Chris@45 2843 void
Chris@226 2844 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
Chris@45 2845 {
Chris@45 2846 QString indent(" ");
Chris@45 2847
Chris@45 2848 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@45 2849 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@45 2850 out << "<sv>\n";
Chris@45 2851
Chris@226 2852 if (asTemplate) {
Chris@226 2853 m_document->toXmlAsTemplate(out, "", "");
Chris@226 2854 } else {
Chris@226 2855 m_document->toXml(out, "", "");
Chris@226 2856 }
Chris@45 2857
Chris@45 2858 out << "<display>\n";
Chris@45 2859
Chris@45 2860 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@595 2861 .arg(width()).arg(height());
Chris@45 2862
Chris@45 2863 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 2864
Chris@595 2865 Pane *pane = m_paneStack->getPane(i);
Chris@595 2866
Chris@595 2867 if (pane) {
Chris@45 2868 pane->toXml(out, indent);
Chris@595 2869 }
Chris@45 2870 }
Chris@45 2871
Chris@45 2872 out << "</display>\n";
Chris@45 2873
Chris@45 2874 m_viewManager->getSelection().toXml(out);
Chris@45 2875
Chris@45 2876 out << "</sv>\n";
Chris@45 2877 }
Chris@45 2878
Chris@45 2879 Pane *
Chris@45 2880 MainWindowBase::addPaneToStack()
Chris@45 2881 {
Chris@342 2882 cerr << "MainWindowBase::addPaneToStack()" << endl;
Chris@45 2883 AddPaneCommand *command = new AddPaneCommand(this);
Chris@45 2884 CommandHistory::getInstance()->addCommand(command);
Chris@57 2885 Pane *pane = command->getPane();
Chris@57 2886 return pane;
Chris@45 2887 }
Chris@45 2888
Chris@45 2889 void
Chris@45 2890 MainWindowBase::zoomIn()
Chris@45 2891 {
Chris@45 2892 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2893 if (currentPane) currentPane->zoom(true);
Chris@45 2894 }
Chris@45 2895
Chris@45 2896 void
Chris@45 2897 MainWindowBase::zoomOut()
Chris@45 2898 {
Chris@45 2899 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2900 if (currentPane) currentPane->zoom(false);
Chris@45 2901 }
Chris@45 2902
Chris@45 2903 void
Chris@45 2904 MainWindowBase::zoomToFit()
Chris@45 2905 {
Chris@45 2906 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2907 if (!currentPane) return;
Chris@45 2908
Chris@45 2909 Model *model = getMainModel();
Chris@45 2910 if (!model) return;
Chris@45 2911
Chris@434 2912 sv_frame_t start = model->getStartFrame();
Chris@434 2913 sv_frame_t end = model->getEndFrame();
Chris@60 2914 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
Chris@366 2915 int pixels = currentPane->width();
Chris@366 2916
Chris@366 2917 int sw = currentPane->getVerticalScaleWidth();
Chris@45 2918 if (pixels > sw * 2) pixels -= sw * 2;
Chris@45 2919 else pixels = 1;
Chris@45 2920 if (pixels > 4) pixels -= 4;
Chris@45 2921
Chris@624 2922 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
Chris@45 2923 currentPane->setZoomLevel(zoomLevel);
Chris@45 2924 currentPane->setCentreFrame((start + end) / 2);
Chris@45 2925 }
Chris@45 2926
Chris@45 2927 void
Chris@45 2928 MainWindowBase::zoomDefault()
Chris@45 2929 {
Chris@45 2930 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@302 2931 QSettings settings;
Chris@302 2932 settings.beginGroup("MainWindow");
Chris@302 2933 int zoom = settings.value("zoom-default", 1024).toInt();
Chris@302 2934 settings.endGroup();
Chris@624 2935 if (currentPane) {
Chris@624 2936 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
Chris@624 2937 }
Chris@45 2938 }
Chris@45 2939
Chris@45 2940 void
Chris@45 2941 MainWindowBase::scrollLeft()
Chris@45 2942 {
Chris@45 2943 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2944 if (currentPane) currentPane->scroll(false, false);
Chris@45 2945 }
Chris@45 2946
Chris@45 2947 void
Chris@45 2948 MainWindowBase::jumpLeft()
Chris@45 2949 {
Chris@45 2950 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2951 if (currentPane) currentPane->scroll(false, true);
Chris@45 2952 }
Chris@45 2953
Chris@45 2954 void
Chris@162 2955 MainWindowBase::peekLeft()
Chris@162 2956 {
Chris@162 2957 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 2958 if (currentPane) currentPane->scroll(false, false, false);
Chris@162 2959 }
Chris@162 2960
Chris@162 2961 void
Chris@45 2962 MainWindowBase::scrollRight()
Chris@45 2963 {
Chris@45 2964 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2965 if (currentPane) currentPane->scroll(true, false);
Chris@45 2966 }
Chris@45 2967
Chris@45 2968 void
Chris@45 2969 MainWindowBase::jumpRight()
Chris@45 2970 {
Chris@45 2971 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2972 if (currentPane) currentPane->scroll(true, true);
Chris@45 2973 }
Chris@45 2974
Chris@45 2975 void
Chris@162 2976 MainWindowBase::peekRight()
Chris@162 2977 {
Chris@162 2978 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 2979 if (currentPane) currentPane->scroll(true, false, false);
Chris@162 2980 }
Chris@162 2981
Chris@162 2982 void
Chris@45 2983 MainWindowBase::showNoOverlays()
Chris@45 2984 {
Chris@45 2985 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@45 2986 }
Chris@45 2987
Chris@45 2988 void
Chris@45 2989 MainWindowBase::showMinimalOverlays()
Chris@45 2990 {
Chris@335 2991 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@45 2992 }
Chris@45 2993
Chris@45 2994 void
Chris@45 2995 MainWindowBase::showAllOverlays()
Chris@45 2996 {
Chris@45 2997 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@45 2998 }
Chris@45 2999
Chris@45 3000 void
Chris@577 3001 MainWindowBase::findTimeRulerLayer()
Chris@577 3002 {
Chris@577 3003 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@577 3004 Pane *pane = m_paneStack->getPane(i);
Chris@577 3005 if (!pane) continue;
Chris@577 3006 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@577 3007 Layer *layer = pane->getLayer(j);
Chris@577 3008 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@577 3009 m_timeRulerLayer = layer;
Chris@577 3010 return;
Chris@577 3011 }
Chris@577 3012 }
Chris@577 3013 if (m_timeRulerLayer) {
Chris@577 3014 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
Chris@577 3015 delete m_timeRulerLayer;
Chris@636 3016 m_timeRulerLayer = nullptr;
Chris@577 3017 }
Chris@577 3018 }
Chris@577 3019
Chris@577 3020 void
Chris@211 3021 MainWindowBase::toggleTimeRulers()
Chris@211 3022 {
Chris@211 3023 bool haveRulers = false;
Chris@211 3024 bool someHidden = false;
Chris@211 3025
Chris@211 3026 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3027
Chris@211 3028 Pane *pane = m_paneStack->getPane(i);
Chris@211 3029 if (!pane) continue;
Chris@211 3030
Chris@211 3031 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3032
Chris@211 3033 Layer *layer = pane->getLayer(j);
Chris@211 3034 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3035
Chris@211 3036 haveRulers = true;
Chris@211 3037 if (layer->isLayerDormant(pane)) someHidden = true;
Chris@211 3038 }
Chris@211 3039 }
Chris@211 3040
Chris@211 3041 if (haveRulers) {
Chris@211 3042
Chris@211 3043 bool show = someHidden;
Chris@211 3044
Chris@211 3045 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 3046
Chris@211 3047 Pane *pane = m_paneStack->getPane(i);
Chris@211 3048 if (!pane) continue;
Chris@211 3049
Chris@211 3050 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 3051
Chris@211 3052 Layer *layer = pane->getLayer(j);
Chris@211 3053 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 3054
Chris@211 3055 layer->showLayer(pane, show);
Chris@211 3056 }
Chris@211 3057 }
Chris@211 3058 }
Chris@211 3059 }
Chris@211 3060
Chris@211 3061 void
Chris@45 3062 MainWindowBase::toggleZoomWheels()
Chris@45 3063 {
Chris@45 3064 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@45 3065 m_viewManager->setZoomWheelsEnabled(false);
Chris@45 3066 } else {
Chris@45 3067 m_viewManager->setZoomWheelsEnabled(true);
Chris@45 3068 }
Chris@45 3069 }
Chris@45 3070
Chris@45 3071 void
Chris@45 3072 MainWindowBase::togglePropertyBoxes()
Chris@45 3073 {
Chris@45 3074 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
Chris@45 3075 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3076 Preferences::VerticallyStacked) {
Chris@45 3077 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3078 } else {
Chris@45 3079 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3080 }
Chris@45 3081 } else {
Chris@45 3082 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@45 3083 }
Chris@45 3084 }
Chris@45 3085
Chris@378 3086 QLabel *
Chris@378 3087 MainWindowBase::getStatusLabel() const
Chris@378 3088 {
Chris@378 3089 if (!m_statusLabel) {
Chris@378 3090 m_statusLabel = new QLabel();
Chris@378 3091 statusBar()->addWidget(m_statusLabel, 1);
Chris@378 3092 }
Chris@379 3093
Chris@379 3094 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
Chris@379 3095 foreach (QFrame *f, frames) {
Chris@379 3096 f->setFrameStyle(QFrame::NoFrame);
Chris@379 3097 }
Chris@379 3098
Chris@378 3099 return m_statusLabel;
Chris@378 3100 }
Chris@378 3101
Chris@45 3102 void
Chris@45 3103 MainWindowBase::toggleStatusBar()
Chris@45 3104 {
Chris@45 3105 QSettings settings;
Chris@45 3106 settings.beginGroup("MainWindow");
Chris@45 3107 bool sb = settings.value("showstatusbar", true).toBool();
Chris@45 3108
Chris@45 3109 if (sb) {
Chris@45 3110 statusBar()->hide();
Chris@45 3111 } else {
Chris@45 3112 statusBar()->show();
Chris@45 3113 }
Chris@45 3114
Chris@45 3115 settings.setValue("showstatusbar", !sb);
Chris@45 3116
Chris@45 3117 settings.endGroup();
Chris@45 3118 }
Chris@45 3119
Chris@45 3120 void
Chris@256 3121 MainWindowBase::toggleCentreLine()
Chris@256 3122 {
Chris@256 3123 if (m_viewManager->shouldShowCentreLine()) {
Chris@256 3124 m_viewManager->setShowCentreLine(false);
Chris@256 3125 } else {
Chris@256 3126 m_viewManager->setShowCentreLine(true);
Chris@256 3127 }
Chris@256 3128 }
Chris@256 3129
Chris@256 3130 void
Chris@45 3131 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
Chris@45 3132 {
Chris@45 3133 if (name == "Property Box Layout") {
Chris@45 3134 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
Chris@45 3135 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3136 Preferences::VerticallyStacked) {
Chris@45 3137 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3138 } else {
Chris@45 3139 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3140 }
Chris@45 3141 }
Chris@45 3142 } else if (name == "Background Mode" && m_viewManager) {
Chris@45 3143 Preferences::BackgroundMode mode =
Chris@45 3144 Preferences::getInstance()->getBackgroundMode();
Chris@45 3145 if (mode == Preferences::BackgroundFromTheme) {
Chris@45 3146 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@45 3147 } else if (mode == Preferences::DarkBackground) {
Chris@45 3148 m_viewManager->setGlobalDarkBackground(true);
Chris@45 3149 } else {
Chris@45 3150 m_viewManager->setGlobalDarkBackground(false);
Chris@45 3151 }
Chris@45 3152 }
Chris@45 3153 }
Chris@45 3154
Chris@45 3155 void
Chris@45 3156 MainWindowBase::play()
Chris@45 3157 {
Chris@516 3158 if ((m_recordTarget && m_recordTarget->isRecording()) ||
Chris@516 3159 (m_playSource && m_playSource->isPlaying())) {
Chris@45 3160 stop();
Chris@479 3161 QAction *action = qobject_cast<QAction *>(sender());
Chris@479 3162 if (action) action->setChecked(false);
Chris@45 3163 } else {
Chris@487 3164 if (m_audioIO) m_audioIO->resume();
Chris@509 3165 else if (m_playTarget) m_playTarget->resume();
Chris@45 3166 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@595 3167 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@45 3168 }
Chris@45 3169 }
Chris@45 3170
Chris@45 3171 void
Chris@477 3172 MainWindowBase::record()
Chris@477 3173 {
Chris@586 3174 QAction *action = qobject_cast<QAction *>(sender());
Chris@586 3175
Chris@478 3176 if (!(m_soundOptions & WithAudioInput)) {
Chris@586 3177 if (action) action->setChecked(false);
Chris@478 3178 return;
Chris@478 3179 }
Chris@478 3180
Chris@477 3181 if (!m_recordTarget) {
Chris@586 3182 if (action) action->setChecked(false);
Chris@477 3183 return;
Chris@477 3184 }
Chris@477 3185
Chris@478 3186 if (!m_audioIO) {
Chris@611 3187 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
Chris@478 3188 createAudioIO();
Chris@478 3189 }
Chris@492 3190
Chris@492 3191 if (!m_audioIO) {
Chris@586 3192 if (!m_playTarget) {
Chris@586 3193 // Don't need to report this, createAudioIO should have
Chris@586 3194 if (action) action->setChecked(false);
Chris@586 3195 return;
Chris@586 3196 } else {
Chris@586 3197 // Need to report this: if the play target exists instead
Chris@586 3198 // of the audio IO, then that means we failed to open a
Chris@586 3199 // capture device. The record control should be disabled
Chris@586 3200 // in that situation, so if it happens here, that must
Chris@586 3201 // mean this is the first time we ever tried to open the
Chris@586 3202 // audio device, hence the need to report the problem here
Chris@586 3203 QMessageBox::critical
Chris@586 3204 (this, tr("No record device available"),
Chris@586 3205 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 3206 if (action) action->setChecked(false);
Chris@586 3207 updateMenuStates();
Chris@586 3208 return;
Chris@586 3209 }
Chris@492 3210 }
Chris@478 3211
Chris@477 3212 if (m_recordTarget->isRecording()) {
Chris@492 3213 stop();
Chris@477 3214 return;
Chris@477 3215 }
Chris@490 3216
Chris@483 3217 if (m_audioRecordMode == RecordReplaceSession) {
Chris@490 3218 if (!checkSaveModified()) {
Chris@490 3219 if (action) action->setChecked(false);
Chris@490 3220 return;
Chris@490 3221 }
Chris@483 3222 }
Chris@487 3223
Chris@557 3224 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
Chris@557 3225
Chris@611 3226 SVCERR << "MainWindowBase::record: about to resume" << endl;
Chris@492 3227 m_audioIO->resume();
Chris@509 3228
Chris@477 3229 WritableWaveFileModel *model = m_recordTarget->startRecording();
Chris@477 3230 if (!model) {
Chris@586 3231 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
Chris@586 3232 QMessageBox::critical
Chris@586 3233 (this, tr("Recording failed"),
Chris@586 3234 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
Chris@490 3235 if (action) action->setChecked(false);
Chris@477 3236 return;
Chris@477 3237 }
Chris@477 3238
Chris@477 3239 if (!model->isOK()) {
Chris@611 3240 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
Chris@477 3241 m_recordTarget->stopRecording();
Chris@492 3242 m_audioIO->suspend();
Chris@586 3243 if (action) action->setChecked(false);
Chris@477 3244 delete model;
Chris@477 3245 return;
Chris@477 3246 }
Chris@611 3247
Chris@611 3248 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
Chris@487 3249
Chris@478 3250 PlayParameterRepository::getInstance()->addPlayable(model);
Chris@483 3251
Chris@483 3252 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
Chris@478 3253
Chris@479 3254 //!!! duplication with openAudio here
Chris@479 3255
Chris@479 3256 QString templateName = getDefaultSessionTemplate();
Chris@479 3257 bool loadedTemplate = false;
Chris@479 3258
Chris@479 3259 if (templateName != "") {
Chris@479 3260 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@479 3261 if (tplStatus == FileOpenCancelled) {
Chris@611 3262 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
Chris@490 3263 m_recordTarget->stopRecording();
Chris@492 3264 m_audioIO->suspend();
Chris@490 3265 PlayParameterRepository::getInstance()->removePlayable(model);
Chris@479 3266 return;
Chris@479 3267 }
Chris@479 3268 if (tplStatus != FileOpenFailed) {
Chris@479 3269 loadedTemplate = true;
Chris@479 3270 }
Chris@479 3271 }
Chris@479 3272
Chris@479 3273 if (!loadedTemplate) {
Chris@479 3274 closeSession();
Chris@479 3275 createDocument();
Chris@479 3276 }
Chris@479 3277
Chris@479 3278 Model *prevMain = getMainModel();
Chris@479 3279 if (prevMain) {
Chris@479 3280 m_playSource->removeModel(prevMain);
Chris@479 3281 PlayParameterRepository::getInstance()->removePlayable(prevMain);
Chris@479 3282 }
Chris@479 3283
Chris@478 3284 m_document->setMainModel(model);
Chris@478 3285 setupMenus();
Chris@577 3286 findTimeRulerLayer();
Chris@478 3287
Chris@595 3288 if (loadedTemplate || (m_sessionFile == "")) {
Chris@479 3289 //!!! shouldn't be dealing directly with title from here -- call a method
Chris@595 3290 setWindowTitle(tr("%1: %2")
Chris@479 3291 .arg(QApplication::applicationName())
Chris@479 3292 .arg(model->getLocation()));
Chris@595 3293 CommandHistory::getInstance()->clear();
Chris@595 3294 CommandHistory::getInstance()->documentSaved();
Chris@595 3295 m_documentModified = false;
Chris@595 3296 } else {
Chris@595 3297 setWindowTitle(tr("%1: %2 [%3]")
Chris@479 3298 .arg(QApplication::applicationName())
Chris@595 3299 .arg(QFileInfo(m_sessionFile).fileName())
Chris@595 3300 .arg(model->getLocation()));
Chris@595 3301 if (m_documentModified) {
Chris@595 3302 m_documentModified = false;
Chris@595 3303 documentModified(); // so as to restore "(modified)" window title
Chris@595 3304 }
Chris@595 3305 }
Chris@479 3306
Chris@478 3307 } else {
Chris@478 3308
Chris@478 3309 CommandHistory::getInstance()->startCompoundOperation
Chris@478 3310 (tr("Import Recorded Audio"), true);
Chris@478 3311
Chris@478 3312 m_document->addImportedModel(model);
Chris@478 3313
Chris@478 3314 AddPaneCommand *command = new AddPaneCommand(this);
Chris@478 3315 CommandHistory::getInstance()->addCommand(command);
Chris@478 3316
Chris@478 3317 Pane *pane = command->getPane();
Chris@478 3318
Chris@478 3319 if (m_timeRulerLayer) {
Chris@478 3320 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@478 3321 }
Chris@478 3322
Chris@478 3323 Layer *newLayer = m_document->createImportedLayer(model);
Chris@478 3324
Chris@478 3325 if (newLayer) {
Chris@478 3326 m_document->addLayerToView(pane, newLayer);
Chris@478 3327 }
Chris@595 3328
Chris@478 3329 CommandHistory::getInstance()->endCompoundOperation();
Chris@477 3330 }
Chris@479 3331
Chris@479 3332 updateMenuStates();
Chris@479 3333 m_recentFiles.addFile(model->getLocation());
Chris@479 3334 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@611 3335
Chris@496 3336 emit audioFileLoaded();
Chris@477 3337 }
Chris@477 3338
Chris@477 3339 void
Chris@45 3340 MainWindowBase::ffwd()
Chris@45 3341 {
Chris@45 3342 if (!getMainModel()) return;
Chris@45 3343
Chris@435 3344 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@45 3345 ++frame;
Chris@45 3346
Chris@85 3347 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3348 Layer *layer = getSnapLayer();
Chris@435 3349 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3350
Chris@45 3351 if (!layer) {
Chris@45 3352
Chris@45 3353 frame = RealTime::realTime2Frame
Chris@357 3354 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
Chris@435 3355 if (frame > getMainModel()->getEndFrame()) {
Chris@45 3356 frame = getMainModel()->getEndFrame();
Chris@45 3357 }
Chris@45 3358
Chris@45 3359 } else {
Chris@45 3360
Chris@366 3361 int resolution = 0;
Chris@166 3362 if (pane) frame = pane->alignFromReference(frame);
Chris@85 3363 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@85 3364 frame, resolution, Layer::SnapRight)) {
Chris@85 3365 if (pane) frame = pane->alignToReference(frame);
Chris@85 3366 } else {
Chris@45 3367 frame = getMainModel()->getEndFrame();
Chris@45 3368 }
Chris@45 3369 }
Chris@45 3370
Chris@45 3371 if (frame < 0) frame = 0;
Chris@45 3372
Chris@45 3373 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3374 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3375 }
Chris@45 3376
Chris@45 3377 m_viewManager->setPlaybackFrame(frame);
Chris@166 3378
Chris@435 3379 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3380 m_playSource &&
Chris@166 3381 m_playSource->isPlaying() &&
Chris@166 3382 !m_viewManager->getPlayLoopMode()) {
Chris@166 3383 stop();
Chris@166 3384 }
Chris@45 3385 }
Chris@45 3386
Chris@45 3387 void
Chris@45 3388 MainWindowBase::ffwdEnd()
Chris@45 3389 {
Chris@45 3390 if (!getMainModel()) return;
Chris@45 3391
Chris@139 3392 if (m_playSource &&
Chris@139 3393 m_playSource->isPlaying() &&
Chris@139 3394 !m_viewManager->getPlayLoopMode()) {
Chris@139 3395 stop();
Chris@139 3396 }
Chris@139 3397
Chris@435 3398 sv_frame_t frame = getMainModel()->getEndFrame();
Chris@45 3399
Chris@45 3400 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3401 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3402 }
Chris@45 3403
Chris@45 3404 m_viewManager->setPlaybackFrame(frame);
Chris@45 3405 }
Chris@45 3406
Chris@45 3407 void
Chris@166 3408 MainWindowBase::ffwdSimilar()
Chris@166 3409 {
Chris@166 3410 if (!getMainModel()) return;
Chris@166 3411
Chris@166 3412 Layer *layer = getSnapLayer();
Chris@166 3413 if (!layer) { ffwd(); return; }
Chris@166 3414
Chris@166 3415 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3416 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3417
Chris@366 3418 int resolution = 0;
Chris@166 3419 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3420 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3421 frame, resolution, Layer::SnapRight)) {
Chris@166 3422 if (pane) frame = pane->alignToReference(frame);
Chris@166 3423 } else {
Chris@166 3424 frame = getMainModel()->getEndFrame();
Chris@166 3425 }
Chris@166 3426
Chris@166 3427 if (frame < 0) frame = 0;
Chris@166 3428
Chris@166 3429 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3430 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3431 }
Chris@166 3432
Chris@166 3433 m_viewManager->setPlaybackFrame(frame);
Chris@166 3434
Chris@435 3435 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3436 m_playSource &&
Chris@166 3437 m_playSource->isPlaying() &&
Chris@166 3438 !m_viewManager->getPlayLoopMode()) {
Chris@166 3439 stop();
Chris@166 3440 }
Chris@166 3441 }
Chris@166 3442
Chris@166 3443 void
Chris@45 3444 MainWindowBase::rewind()
Chris@45 3445 {
Chris@45 3446 if (!getMainModel()) return;
Chris@45 3447
Chris@435 3448 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@45 3449 if (frame > 0) --frame;
Chris@45 3450
Chris@85 3451 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3452 Layer *layer = getSnapLayer();
Chris@435 3453 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3454
Chris@45 3455 // when rewinding during playback, we want to allow a period
Chris@45 3456 // following a rewind target point at which the rewind will go to
Chris@45 3457 // the prior point instead of the immediately neighbouring one
Chris@45 3458 if (m_playSource && m_playSource->isPlaying()) {
Chris@45 3459 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@357 3460 ct = ct - RealTime::fromSeconds(0.15);
Chris@45 3461 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@45 3462 frame = RealTime::realTime2Frame(ct, sr);
Chris@45 3463 }
Chris@45 3464
Chris@45 3465 if (!layer) {
Chris@45 3466
Chris@45 3467 frame = RealTime::realTime2Frame
Chris@357 3468 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
Chris@435 3469 if (frame < getMainModel()->getStartFrame()) {
Chris@45 3470 frame = getMainModel()->getStartFrame();
Chris@45 3471 }
Chris@45 3472
Chris@45 3473 } else {
Chris@45 3474
Chris@366 3475 int resolution = 0;
Chris@166 3476 if (pane) frame = pane->alignFromReference(frame);
Chris@85 3477 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@85 3478 frame, resolution, Layer::SnapLeft)) {
Chris@85 3479 if (pane) frame = pane->alignToReference(frame);
Chris@85 3480 } else {
Chris@45 3481 frame = getMainModel()->getStartFrame();
Chris@45 3482 }
Chris@45 3483 }
Chris@45 3484
Chris@45 3485 if (frame < 0) frame = 0;
Chris@45 3486
Chris@45 3487 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3488 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3489 }
Chris@45 3490
Chris@45 3491 m_viewManager->setPlaybackFrame(frame);
Chris@45 3492 }
Chris@45 3493
Chris@45 3494 void
Chris@45 3495 MainWindowBase::rewindStart()
Chris@45 3496 {
Chris@45 3497 if (!getMainModel()) return;
Chris@45 3498
Chris@435 3499 sv_frame_t frame = getMainModel()->getStartFrame();
Chris@45 3500
Chris@45 3501 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3502 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3503 }
Chris@45 3504
Chris@45 3505 m_viewManager->setPlaybackFrame(frame);
Chris@45 3506 }
Chris@45 3507
Chris@166 3508 void
Chris@166 3509 MainWindowBase::rewindSimilar()
Chris@166 3510 {
Chris@166 3511 if (!getMainModel()) return;
Chris@166 3512
Chris@166 3513 Layer *layer = getSnapLayer();
Chris@166 3514 if (!layer) { rewind(); return; }
Chris@166 3515
Chris@166 3516 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3517 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3518
Chris@366 3519 int resolution = 0;
Chris@166 3520 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3521 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3522 frame, resolution, Layer::SnapLeft)) {
Chris@166 3523 if (pane) frame = pane->alignToReference(frame);
Chris@166 3524 } else {
Chris@166 3525 frame = getMainModel()->getStartFrame();
Chris@166 3526 }
Chris@166 3527
Chris@166 3528 if (frame < 0) frame = 0;
Chris@166 3529
Chris@166 3530 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3531 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3532 }
Chris@166 3533
Chris@166 3534 m_viewManager->setPlaybackFrame(frame);
Chris@166 3535 }
Chris@166 3536
Chris@45 3537 Layer *
Chris@45 3538 MainWindowBase::getSnapLayer() const
Chris@45 3539 {
Chris@45 3540 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 3541 if (!pane) return nullptr;
Chris@45 3542
Chris@45 3543 Layer *layer = pane->getSelectedLayer();
Chris@45 3544
Chris@45 3545 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@45 3546 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@194 3547 !dynamic_cast<RegionLayer *>(layer) &&
Chris@45 3548 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@45 3549
Chris@636 3550 layer = nullptr;
Chris@45 3551
Chris@45 3552 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 3553 Layer *l = pane->getLayer(i-1);
Chris@45 3554 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@45 3555 layer = l;
Chris@45 3556 break;
Chris@45 3557 }
Chris@45 3558 }
Chris@45 3559 }
Chris@45 3560
Chris@45 3561 return layer;
Chris@45 3562 }
Chris@45 3563
Chris@45 3564 void
Chris@45 3565 MainWindowBase::stop()
Chris@45 3566 {
Chris@516 3567 if (m_recordTarget &&
Chris@516 3568 m_recordTarget->isRecording()) {
Chris@477 3569 m_recordTarget->stopRecording();
Chris@477 3570 }
Chris@516 3571
Chris@516 3572 if (!m_playSource) return;
Chris@516 3573
Chris@45 3574 m_playSource->stop();
Chris@45 3575
Chris@611 3576 SVCERR << "MainWindowBase::stop: suspending" << endl;
Chris@611 3577
Chris@487 3578 if (m_audioIO) m_audioIO->suspend();
Chris@509 3579 else if (m_playTarget) m_playTarget->suspend();
Chris@487 3580
Chris@45 3581 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@45 3582 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@45 3583 } else {
Chris@45 3584 m_myStatusMessage = "";
Chris@378 3585 getStatusLabel()->setText("");
Chris@45 3586 }
Chris@45 3587 }
Chris@45 3588
Chris@45 3589 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
Chris@45 3590 m_mw(mw),
Chris@636 3591 m_pane(nullptr),
Chris@636 3592 m_prevCurrentPane(nullptr),
Chris@45 3593 m_added(false)
Chris@45 3594 {
Chris@45 3595 }
Chris@45 3596
Chris@45 3597 MainWindowBase::AddPaneCommand::~AddPaneCommand()
Chris@45 3598 {
Chris@45 3599 if (m_pane && !m_added) {
Chris@595 3600 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3601 }
Chris@45 3602 }
Chris@45 3603
Chris@45 3604 QString
Chris@45 3605 MainWindowBase::AddPaneCommand::getName() const
Chris@45 3606 {
Chris@45 3607 return tr("Add Pane");
Chris@45 3608 }
Chris@45 3609
Chris@45 3610 void
Chris@45 3611 MainWindowBase::AddPaneCommand::execute()
Chris@45 3612 {
Chris@45 3613 if (!m_pane) {
Chris@595 3614 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@595 3615 m_pane = m_mw->m_paneStack->addPane();
Chris@45 3616
Chris@45 3617 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 3618 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@45 3619 } else {
Chris@595 3620 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3621 }
Chris@45 3622
Chris@45 3623 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@45 3624 m_added = true;
Chris@45 3625 }
Chris@45 3626
Chris@45 3627 void
Chris@45 3628 MainWindowBase::AddPaneCommand::unexecute()
Chris@45 3629 {
Chris@45 3630 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3631 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3632 m_added = false;
Chris@45 3633 }
Chris@45 3634
Chris@45 3635 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
Chris@45 3636 m_mw(mw),
Chris@45 3637 m_pane(pane),
Chris@636 3638 m_prevCurrentPane(nullptr),
Chris@45 3639 m_added(true)
Chris@45 3640 {
Chris@45 3641 }
Chris@45 3642
Chris@45 3643 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
Chris@45 3644 {
Chris@45 3645 if (m_pane && !m_added) {
Chris@595 3646 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3647 }
Chris@45 3648 }
Chris@45 3649
Chris@45 3650 QString
Chris@45 3651 MainWindowBase::RemovePaneCommand::getName() const
Chris@45 3652 {
Chris@45 3653 return tr("Remove Pane");
Chris@45 3654 }
Chris@45 3655
Chris@45 3656 void
Chris@45 3657 MainWindowBase::RemovePaneCommand::execute()
Chris@45 3658 {
Chris@45 3659 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@45 3660 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3661 m_added = false;
Chris@45 3662 }
Chris@45 3663
Chris@45 3664 void
Chris@45 3665 MainWindowBase::RemovePaneCommand::unexecute()
Chris@45 3666 {
Chris@45 3667 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3668 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3669 m_added = true;
Chris@45 3670 }
Chris@45 3671
Chris@45 3672 void
Chris@45 3673 MainWindowBase::deleteCurrentPane()
Chris@45 3674 {
Chris@45 3675 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3676 (tr("Delete Pane"), true);
Chris@45 3677
Chris@45 3678 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3679 if (pane) {
Chris@595 3680 while (pane->getLayerCount() > 0) {
Chris@595 3681 Layer *layer = pane->getLayer(0);
Chris@595 3682 if (layer) {
Chris@595 3683 m_document->removeLayerFromView(pane, layer);
Chris@595 3684 } else {
Chris@595 3685 break;
Chris@595 3686 }
Chris@595 3687 }
Chris@595 3688
Chris@595 3689 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@595 3690 CommandHistory::getInstance()->addCommand(command);
Chris@45 3691 }
Chris@45 3692
Chris@45 3693 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 3694
Chris@45 3695 updateMenuStates();
Chris@45 3696 }
Chris@45 3697
Chris@45 3698 void
Chris@45 3699 MainWindowBase::deleteCurrentLayer()
Chris@45 3700 {
Chris@45 3701 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3702 if (pane) {
Chris@595 3703 Layer *layer = pane->getSelectedLayer();
Chris@595 3704 if (layer) {
Chris@595 3705 m_document->removeLayerFromView(pane, layer);
Chris@595 3706 }
Chris@45 3707 }
Chris@45 3708 updateMenuStates();
Chris@45 3709 }
Chris@45 3710
Chris@45 3711 void
Chris@123 3712 MainWindowBase::editCurrentLayer()
Chris@123 3713 {
Chris@636 3714 Layer *layer = nullptr;
Chris@123 3715 Pane *pane = m_paneStack->getCurrentPane();
Chris@123 3716 if (pane) layer = pane->getSelectedLayer();
Chris@123 3717 if (!layer) return;
Chris@123 3718
Chris@123 3719 Model *model = layer->getModel();
Chris@123 3720 if (!model) return;
Chris@123 3721
Chris@124 3722 TabularModel *tabular = dynamic_cast<TabularModel *>(model);
Chris@124 3723 if (!tabular) {
Chris@124 3724 //!!! how to prevent this function from being active if not
Chris@124 3725 //appropriate model type? or will we ultimately support
Chris@124 3726 //tabular display for all editable models?
Chris@233 3727 SVDEBUG << "NOTE: Not a tabular model" << endl;
Chris@124 3728 return;
Chris@124 3729 }
Chris@124 3730
Chris@123 3731 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@126 3732 if (!m_layerDataDialogMap[layer].isNull()) {
Chris@126 3733 m_layerDataDialogMap[layer]->show();
Chris@126 3734 m_layerDataDialogMap[layer]->raise();
Chris@126 3735 return;
Chris@126 3736 }
Chris@123 3737 }
Chris@123 3738
Chris@125 3739 QString title = layer->getLayerPresentationName();
Chris@125 3740
Chris@125 3741 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
Chris@128 3742 dialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@128 3743
Chris@128 3744 connectLayerEditDialog(dialog);
Chris@123 3745
Chris@128 3746 m_layerDataDialogMap[layer] = dialog;
Chris@128 3747 m_viewDataDialogMap[pane].insert(dialog);
Chris@128 3748
Chris@128 3749 dialog->show();
Chris@128 3750 }
Chris@128 3751
Chris@128 3752 void
Chris@128 3753 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@128 3754 {
Chris@123 3755 connect(m_viewManager,
Chris@435 3756 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@123 3757 dialog,
Chris@435 3758 SLOT(userScrolledToFrame(sv_frame_t)));
Chris@127 3759
Chris@127 3760 connect(m_viewManager,
Chris@435 3761 SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@127 3762 dialog,
Chris@435 3763 SLOT(playbackScrolledToFrame(sv_frame_t)));
Chris@127 3764
Chris@123 3765 connect(dialog,
Chris@435 3766 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@123 3767 m_viewManager,
Chris@435 3768 SLOT(setGlobalCentreFrame(sv_frame_t)));
Chris@129 3769
Chris@129 3770 connect(dialog,
Chris@435 3771 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@129 3772 m_viewManager,
Chris@435 3773 SLOT(setPlaybackFrame(sv_frame_t)));
Chris@128 3774 }
Chris@123 3775
Chris@123 3776 void
Chris@73 3777 MainWindowBase::previousPane()
Chris@73 3778 {
Chris@73 3779 if (!m_paneStack) return;
Chris@73 3780
Chris@73 3781 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3782 if (!currentPane) return;
Chris@73 3783
Chris@73 3784 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3785 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3786 if (i == 0) return;
Chris@73 3787 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
Chris@73 3788 updateMenuStates();
Chris@73 3789 return;
Chris@73 3790 }
Chris@73 3791 }
Chris@73 3792 }
Chris@73 3793
Chris@73 3794 void
Chris@73 3795 MainWindowBase::nextPane()
Chris@73 3796 {
Chris@73 3797 if (!m_paneStack) return;
Chris@73 3798
Chris@73 3799 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3800 if (!currentPane) return;
Chris@73 3801
Chris@73 3802 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3803 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3804 if (i == m_paneStack->getPaneCount()-1) return;
Chris@73 3805 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
Chris@73 3806 updateMenuStates();
Chris@73 3807 return;
Chris@73 3808 }
Chris@73 3809 }
Chris@73 3810 }
Chris@73 3811
Chris@73 3812 void
Chris@73 3813 MainWindowBase::previousLayer()
Chris@73 3814 {
Chris@73 3815 if (!m_paneStack) return;
Chris@73 3816
Chris@73 3817 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3818 if (!currentPane) return;
Chris@73 3819
Chris@403 3820 int count = currentPane->getLayerCount();
Chris@403 3821 if (count == 0) return;
Chris@403 3822
Chris@73 3823 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3824
Chris@403 3825 if (!currentLayer) {
Chris@403 3826 // The pane itself is current
Chris@403 3827 m_paneStack->setCurrentLayer
Chris@403 3828 (currentPane, currentPane->getFixedOrderLayer(count-1));
Chris@403 3829 } else {
Chris@403 3830 for (int i = 0; i < count; ++i) {
Chris@403 3831 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3832 if (i == 0) {
Chris@403 3833 m_paneStack->setCurrentLayer
Chris@636 3834 (currentPane, nullptr); // pane
Chris@403 3835 } else {
Chris@403 3836 m_paneStack->setCurrentLayer
Chris@403 3837 (currentPane, currentPane->getFixedOrderLayer(i-1));
Chris@403 3838 }
Chris@403 3839 break;
Chris@403 3840 }
Chris@73 3841 }
Chris@73 3842 }
Chris@403 3843
Chris@403 3844 updateMenuStates();
Chris@73 3845 }
Chris@73 3846
Chris@73 3847 void
Chris@73 3848 MainWindowBase::nextLayer()
Chris@73 3849 {
Chris@73 3850 if (!m_paneStack) return;
Chris@73 3851
Chris@73 3852 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3853 if (!currentPane) return;
Chris@73 3854
Chris@403 3855 int count = currentPane->getLayerCount();
Chris@403 3856 if (count == 0) return;
Chris@403 3857
Chris@73 3858 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3859
Chris@403 3860 if (!currentLayer) {
Chris@403 3861 // The pane itself is current
Chris@403 3862 m_paneStack->setCurrentLayer
Chris@403 3863 (currentPane, currentPane->getFixedOrderLayer(0));
Chris@403 3864 } else {
Chris@403 3865 for (int i = 0; i < count; ++i) {
Chris@403 3866 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3867 if (i == currentPane->getLayerCount()-1) {
Chris@403 3868 m_paneStack->setCurrentLayer
Chris@636 3869 (currentPane, nullptr); // pane
Chris@403 3870 } else {
Chris@403 3871 m_paneStack->setCurrentLayer
Chris@403 3872 (currentPane, currentPane->getFixedOrderLayer(i+1));
Chris@403 3873 }
Chris@403 3874 break;
Chris@403 3875 }
Chris@73 3876 }
Chris@73 3877 }
Chris@403 3878
Chris@403 3879 updateMenuStates();
Chris@73 3880 }
Chris@73 3881
Chris@73 3882 void
Chris@435 3883 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
Chris@45 3884 {
Chris@45 3885 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@45 3886
Chris@187 3887 updatePositionStatusDisplays();
Chris@187 3888
Chris@45 3889 RealTime now = RealTime::frame2RealTime
Chris@45 3890 (frame, getMainModel()->getSampleRate());
Chris@45 3891
Chris@45 3892 if (now.sec == m_lastPlayStatusSec) return;
Chris@45 3893
Chris@45 3894 RealTime then = RealTime::frame2RealTime
Chris@45 3895 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@45 3896
Chris@45 3897 QString nowStr;
Chris@45 3898 QString thenStr;
Chris@45 3899 QString remainingStr;
Chris@45 3900
Chris@45 3901 if (then.sec > 10) {
Chris@45 3902 nowStr = now.toSecText().c_str();
Chris@45 3903 thenStr = then.toSecText().c_str();
Chris@45 3904 remainingStr = (then - now).toSecText().c_str();
Chris@45 3905 m_lastPlayStatusSec = now.sec;
Chris@45 3906 } else {
Chris@45 3907 nowStr = now.toText(true).c_str();
Chris@45 3908 thenStr = then.toText(true).c_str();
Chris@45 3909 remainingStr = (then - now).toText(true).c_str();
Chris@45 3910 }
Chris@45 3911
Chris@45 3912 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@45 3913 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@45 3914
Chris@378 3915 getStatusLabel()->setText(m_myStatusMessage);
Chris@45 3916 }
Chris@45 3917
Chris@45 3918 void
Chris@486 3919 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
Chris@486 3920 {
Chris@486 3921 RealTime duration = RealTime::frame2RealTime(frame, rate);
Chris@486 3922 QString durStr = duration.toSecText().c_str();
Chris@486 3923
Chris@486 3924 m_myStatusMessage = tr("Recording: %1").arg(durStr);
Chris@486 3925
Chris@486 3926 getStatusLabel()->setText(m_myStatusMessage);
Chris@486 3927 }
Chris@486 3928
Chris@486 3929 void
Chris@435 3930 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
Chris@45 3931 {
Chris@45 3932 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3933 Pane *p = nullptr;
Chris@45 3934 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3935 if (!p->getFollowGlobalPan()) return;
Chris@45 3936 updateVisibleRangeDisplay(p);
Chris@45 3937 }
Chris@45 3938
Chris@45 3939 void
Chris@435 3940 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
Chris@45 3941 {
Chris@233 3942 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
Chris@123 3943
Chris@123 3944 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
Chris@123 3945 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
Chris@123 3946 i != m_viewDataDialogMap[v].end(); ++i) {
Chris@127 3947 (*i)->userScrolledToFrame(frame);
Chris@123 3948 }
Chris@123 3949 }
Chris@45 3950 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3951 Pane *p = nullptr;
Chris@45 3952 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3953 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 3954 }
Chris@45 3955
Chris@45 3956 void
Chris@624 3957 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
Chris@45 3958 {
Chris@45 3959 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3960 Pane *p = nullptr;
Chris@45 3961 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3962 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 3963 }
Chris@45 3964
Chris@45 3965 void
Chris@45 3966 MainWindowBase::layerAdded(Layer *)
Chris@45 3967 {
Chris@233 3968 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
Chris@45 3969 updateMenuStates();
Chris@45 3970 }
Chris@45 3971
Chris@45 3972 void
Chris@45 3973 MainWindowBase::layerRemoved(Layer *)
Chris@45 3974 {
Chris@233 3975 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
Chris@45 3976 updateMenuStates();
Chris@45 3977 }
Chris@45 3978
Chris@45 3979 void
Chris@45 3980 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
Chris@45 3981 {
Chris@233 3982 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
Chris@123 3983
Chris@128 3984 removeLayerEditDialog(layer);
Chris@123 3985
Chris@47 3986 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
Chris@595 3987 // cerr << "(this is the time ruler layer)" << endl;
Chris@636 3988 m_timeRulerLayer = nullptr;
Chris@45 3989 }
Chris@45 3990 }
Chris@45 3991
Chris@45 3992 void
Chris@45 3993 MainWindowBase::layerInAView(Layer *layer, bool inAView)
Chris@45 3994 {
Chris@233 3995 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
Chris@128 3996
Chris@128 3997 if (!inAView) removeLayerEditDialog(layer);
Chris@45 3998
Chris@45 3999 // Check whether we need to add or remove model from play source
Chris@45 4000 Model *model = layer->getModel();
Chris@45 4001 if (model) {
Chris@45 4002 if (inAView) {
Chris@45 4003 m_playSource->addModel(model);
Chris@45 4004 } else {
Chris@45 4005 bool found = false;
Chris@45 4006 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 4007 Pane *pane = m_paneStack->getPane(i);
Chris@45 4008 if (!pane) continue;
Chris@45 4009 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@45 4010 Layer *pl = pane->getLayer(j);
Chris@183 4011 if (pl &&
Chris@183 4012 !dynamic_cast<TimeRulerLayer *>(pl) &&
Chris@183 4013 (pl->getModel() == model)) {
Chris@45 4014 found = true;
Chris@45 4015 break;
Chris@45 4016 }
Chris@45 4017 }
Chris@45 4018 if (found) break;
Chris@45 4019 }
Chris@173 4020 if (!found) {
Chris@173 4021 m_playSource->removeModel(model);
Chris@173 4022 }
Chris@45 4023 }
Chris@45 4024 }
Chris@45 4025
Chris@45 4026 updateMenuStates();
Chris@45 4027 }
Chris@45 4028
Chris@45 4029 void
Chris@128 4030 MainWindowBase::removeLayerEditDialog(Layer *layer)
Chris@128 4031 {
Chris@128 4032 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@128 4033
Chris@128 4034 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
Chris@128 4035
Chris@128 4036 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
Chris@128 4037 vi != m_viewDataDialogMap.end(); ++vi) {
Chris@128 4038 vi->second.erase(dialog);
Chris@128 4039 }
Chris@128 4040
Chris@128 4041 m_layerDataDialogMap.erase(layer);
Chris@128 4042 delete dialog;
Chris@128 4043 }
Chris@128 4044 }
Chris@128 4045
Chris@128 4046 void
Chris@45 4047 MainWindowBase::modelAdded(Model *model)
Chris@45 4048 {
Chris@233 4049 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
Chris@595 4050 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
Chris@45 4051 m_playSource->addModel(model);
Chris@45 4052 }
Chris@45 4053
Chris@45 4054 void
Chris@45 4055 MainWindowBase::mainModelChanged(WaveFileModel *model)
Chris@45 4056 {
Chris@233 4057 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
Chris@45 4058 updateDescriptionLabel();
Chris@45 4059 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@475 4060 if (model && !(m_playTarget || m_audioIO) &&
Chris@475 4061 (m_soundOptions & WithAudioOutput)) {
Chris@475 4062 createAudioIO();
Chris@360 4063 }
Chris@45 4064 }
Chris@45 4065
Chris@45 4066 void
Chris@45 4067 MainWindowBase::modelAboutToBeDeleted(Model *model)
Chris@45 4068 {
Chris@233 4069 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
Chris@45 4070 if (model == m_viewManager->getPlaybackModel()) {
Chris@636 4071 m_viewManager->setPlaybackModel(nullptr);
Chris@45 4072 }
Chris@45 4073 m_playSource->removeModel(model);
Chris@45 4074 }
Chris@45 4075
Chris@45 4076 void
Chris@55 4077 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
Chris@55 4078 {
Chris@55 4079 bool found = false;
Chris@55 4080 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@55 4081 if (m_paneStack->getPane(i) == pane) {
Chris@55 4082 found = true;
Chris@55 4083 break;
Chris@55 4084 }
Chris@55 4085 }
Chris@55 4086 if (!found) {
Chris@233 4087 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
Chris@229 4088 << pane << endl;
Chris@55 4089 return;
Chris@55 4090 }
Chris@55 4091
Chris@55 4092 CommandHistory::getInstance()->startCompoundOperation
Chris@595 4093 (tr("Delete Pane"), true);
Chris@55 4094
Chris@55 4095 while (pane->getLayerCount() > 0) {
Chris@637 4096 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@55 4097 if (layer) {
Chris@55 4098 m_document->removeLayerFromView(pane, layer);
Chris@55 4099 } else {
Chris@55 4100 break;
Chris@55 4101 }
Chris@55 4102 }
Chris@55 4103
Chris@55 4104 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@55 4105 CommandHistory::getInstance()->addCommand(command);
Chris@55 4106
Chris@55 4107 CommandHistory::getInstance()->endCompoundOperation();
Chris@55 4108
Chris@55 4109 updateMenuStates();
Chris@55 4110 }
Chris@55 4111
Chris@55 4112 void
Chris@429 4113 MainWindowBase::alignmentComplete(AlignmentModel *model)
Chris@429 4114 {
Chris@429 4115 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
Chris@429 4116 }
Chris@429 4117
Chris@429 4118 void
Chris@45 4119 MainWindowBase::pollOSC()
Chris@45 4120 {
Chris@45 4121 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@233 4122 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
Chris@45 4123
Chris@45 4124 if (m_openingAudioFile) return;
Chris@45 4125
Chris@45 4126 OSCMessage message = m_oscQueue->readMessage();
Chris@45 4127
Chris@45 4128 if (message.getTarget() != 0) {
Chris@45 4129 return; //!!! for now -- this class is target 0, others not handled yet
Chris@45 4130 }
Chris@45 4131
Chris@45 4132 handleOSCMessage(message);
Chris@45 4133 }
Chris@45 4134
Chris@45 4135 void
Chris@45 4136 MainWindowBase::inProgressSelectionChanged()
Chris@45 4137 {
Chris@636 4138 Pane *currentPane = nullptr;
Chris@45 4139 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
justin@331 4140 if (currentPane) {
justin@331 4141 //cerr << "JTEST: mouse event on selection pane" << endl;
justin@331 4142 updateVisibleRangeDisplay(currentPane);
justin@331 4143 }
Chris@45 4144 }
Chris@45 4145
Chris@45 4146 void
Chris@45 4147 MainWindowBase::contextHelpChanged(const QString &s)
Chris@45 4148 {
Chris@378 4149 QLabel *lab = getStatusLabel();
Chris@375 4150
Chris@45 4151 if (s == "" && m_myStatusMessage != "") {
Chris@378 4152 if (lab->text() != m_myStatusMessage) {
Chris@378 4153 lab->setText(m_myStatusMessage);
Chris@375 4154 }
Chris@45 4155 return;
Chris@45 4156 }
Chris@375 4157
Chris@378 4158 lab->setText(s);
Chris@45 4159 }
Chris@45 4160
Chris@45 4161 void
Chris@45 4162 MainWindowBase::openHelpUrl(QString url)
Chris@45 4163 {
Chris@45 4164 // This method mostly lifted from Qt Assistant source code
Chris@45 4165
Chris@45 4166 QProcess *process = new QProcess(this);
Chris@45 4167 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@45 4168
Chris@45 4169 QStringList args;
Chris@45 4170
Chris@45 4171 #ifdef Q_OS_MAC
Chris@45 4172 args.append(url);
Chris@45 4173 process->start("open", args);
Chris@45 4174 #else
Chris@45 4175 #ifdef Q_OS_WIN32
Chris@599 4176 std::string pfiles;
Chris@599 4177 (void)getEnvUtf8("ProgramFiles", pfiles);
Chris@599 4178 QString command =
Chris@599 4179 QString::fromStdString(pfiles) +
Chris@599 4180 QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@358 4181
Chris@358 4182 args.append(url);
Chris@358 4183 process->start(command, args);
Chris@45 4184 #else
Chris@45 4185 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@45 4186 args.append("exec");
Chris@45 4187 args.append(url);
Chris@45 4188 process->start("kfmclient", args);
Chris@45 4189 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@45 4190 args.append(url);
Chris@45 4191 process->start(qgetenv("BROWSER"), args);
Chris@45 4192 } else {
Chris@45 4193 args.append(url);
Chris@45 4194 process->start("firefox", args);
Chris@45 4195 }
Chris@45 4196 #endif
Chris@45 4197 #endif
Chris@45 4198 }
Chris@45 4199
Chris@483 4200 void
Chris@483 4201 MainWindowBase::openLocalFolder(QString path)
Chris@483 4202 {
Chris@483 4203 QDir d(path);
Chris@483 4204 if (d.exists()) {
Chris@483 4205 QStringList args;
Chris@483 4206 QString path = d.canonicalPath();
Chris@483 4207 #if defined Q_OS_WIN32
Chris@483 4208 // Although the Win32 API is quite happy to have
Chris@483 4209 // forward slashes as directory separators, Windows
Chris@483 4210 // Explorer is not
Chris@483 4211 path = path.replace('/', '\\');
Chris@483 4212 args << path;
Chris@483 4213 QProcess::execute("c:/windows/explorer.exe", args);
Chris@483 4214 #else
Chris@483 4215 args << path;
Chris@605 4216 QProcess process;
Chris@605 4217 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@606 4218 env.insert("LD_LIBRARY_PATH", "");
Chris@605 4219 process.setProcessEnvironment(env);
Chris@605 4220 process.start(
Chris@483 4221 #if defined Q_OS_MAC
Chris@483 4222 "/usr/bin/open",
Chris@483 4223 #else
Chris@483 4224 "/usr/bin/xdg-open",
Chris@483 4225 #endif
Chris@483 4226 args);
Chris@608 4227 process.waitForFinished();
Chris@483 4228 #endif
Chris@483 4229 }
Chris@483 4230 }
Chris@483 4231