annotate framework/MainWindowBase.cpp @ 648:eec0c50bb44f single-point

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