annotate framework/MainWindowBase.cpp @ 651:b63dfd7e2780 single-point

Updates for SparseOneDimensionalModel in new API
author Chris Cannam
date Thu, 21 Mar 2019 13:34:34 +0000
parents 284cb118368e
children 4f53620962d9
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@651 1138 SparseOneDimensionalModel *sodm =
Chris@651 1139 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@45 1140
Chris@45 1141 if (sodm) {
Chris@651 1142 Event point(frame, "");
Chris@651 1143 Event prevPoint(0);
Chris@45 1144 bool havePrevPoint = false;
Chris@45 1145
Chris@651 1146 ChangeEventsCommand *command =
Chris@651 1147 new ChangeEventsCommand(sodm, tr("Add Point"));
Chris@45 1148
Chris@409 1149 if (m_labeller) {
Chris@409 1150
Chris@409 1151 if (m_labeller->requiresPrevPoint()) {
Chris@651 1152
Chris@651 1153 if (sodm->getNearestEventMatching
Chris@651 1154 (frame,
Chris@651 1155 [](Event) { return true; },
Chris@651 1156 EventSeries::Backward,
Chris@651 1157 prevPoint)) {
Chris@409 1158 havePrevPoint = true;
Chris@409 1159 }
Chris@45 1160 }
Chris@45 1161
Chris@45 1162 m_labeller->setSampleRate(sodm->getSampleRate());
Chris@45 1163
Chris@651 1164 Labeller::Relabelling relabelling = m_labeller->label
Chris@636 1165 (point, havePrevPoint ? &prevPoint : nullptr);
Chris@651 1166
Chris@651 1167 if (relabelling.first == Labeller::AppliesToPreviousEvent) {
Chris@651 1168 command->remove(prevPoint);
Chris@651 1169 command->add(relabelling.second);
Chris@651 1170 } else {
Chris@651 1171 point = relabelling.second;
Chris@45 1172 }
Chris@45 1173 }
Chris@45 1174
Chris@651 1175 command->add(point);
Chris@45 1176
Chris@45 1177 command->setName(tr("Add Point at %1 s")
Chris@45 1178 .arg(RealTime::frame2RealTime
Chris@45 1179 (frame,
Chris@45 1180 sodm->getSampleRate())
Chris@45 1181 .toText(false).c_str()));
Chris@45 1182
Chris@108 1183 Command *c = command->finish();
Chris@108 1184 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@45 1185 }
Chris@45 1186 }
Chris@45 1187 }
Chris@45 1188
Chris@45 1189 void
Chris@184 1190 MainWindowBase::insertItemAtSelection()
Chris@184 1191 {
Chris@184 1192 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@184 1193 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@184 1194 i != selections.end(); ++i) {
Chris@435 1195 sv_frame_t start = i->getStartFrame();
Chris@435 1196 sv_frame_t end = i->getEndFrame();
Chris@184 1197 if (start < end) {
Chris@184 1198 insertItemAt(start, end - start);
Chris@184 1199 }
Chris@184 1200 }
Chris@184 1201 }
Chris@184 1202
Chris@184 1203 void
Chris@435 1204 MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration)
Chris@184 1205 {
Chris@184 1206 Pane *pane = m_paneStack->getCurrentPane();
Chris@184 1207 if (!pane) {
Chris@184 1208 return;
Chris@184 1209 }
Chris@184 1210
Chris@184 1211 // ugh!
Chris@184 1212
Chris@435 1213 sv_frame_t alignedStart = pane->alignFromReference(frame);
Chris@435 1214 sv_frame_t alignedEnd = pane->alignFromReference(frame + duration);
Chris@184 1215 if (alignedStart >= alignedEnd) return;
Chris@435 1216 sv_frame_t alignedDuration = alignedEnd - alignedStart;
Chris@184 1217
Chris@636 1218 Command *c = nullptr;
Chris@184 1219
Chris@184 1220 QString name = tr("Add Item at %1 s")
Chris@184 1221 .arg(RealTime::frame2RealTime
Chris@184 1222 (alignedStart,
Chris@184 1223 getMainModel()->getSampleRate())
Chris@184 1224 .toText(false).c_str());
Chris@184 1225
Chris@184 1226 Layer *layer = pane->getSelectedLayer();
Chris@184 1227 if (!layer) return;
Chris@184 1228
Chris@184 1229 RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel());
Chris@184 1230 if (rm) {
Chris@648 1231 Event point(alignedStart,
Chris@648 1232 rm->getValueMaximum() + 1,
Chris@648 1233 alignedDuration,
Chris@648 1234 "");
Chris@648 1235 ChangeEventsCommand *command = new ChangeEventsCommand(rm, name);
Chris@648 1236 command->add(point);
Chris@184 1237 c = command->finish();
Chris@184 1238 }
Chris@184 1239
Chris@184 1240 if (c) {
Chris@184 1241 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1242 return;
Chris@184 1243 }
Chris@184 1244
Chris@184 1245 NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel());
Chris@184 1246 if (nm) {
Chris@648 1247 Event point(alignedStart,
Chris@648 1248 nm->getValueMinimum(),
Chris@648 1249 alignedDuration,
Chris@648 1250 1.f,
Chris@648 1251 "");
Chris@647 1252 ChangeEventsCommand *command = new ChangeEventsCommand(nm, name);
Chris@646 1253 command->add(point);
Chris@184 1254 c = command->finish();
Chris@184 1255 }
Chris@184 1256
Chris@184 1257 if (c) {
Chris@184 1258 CommandHistory::getInstance()->addCommand(c, false);
Chris@184 1259 return;
Chris@184 1260 }
Chris@184 1261 }
Chris@184 1262
Chris@184 1263 void
Chris@45 1264 MainWindowBase::renumberInstants()
Chris@45 1265 {
Chris@45 1266 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1267 if (!pane) return;
Chris@45 1268
Chris@45 1269 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@45 1270 if (!layer) return;
Chris@45 1271
Chris@45 1272 MultiSelection ms(m_viewManager->getSelection());
Chris@45 1273
Chris@45 1274 Model *model = layer->getModel();
Chris@45 1275 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
Chris@45 1276 (model);
Chris@45 1277 if (!sodm) return;
Chris@45 1278
Chris@45 1279 if (!m_labeller) return;
Chris@45 1280
Chris@45 1281 Labeller labeller(*m_labeller);
Chris@45 1282 labeller.setSampleRate(sodm->getSampleRate());
Chris@649 1283 /*!!! to be updated after SODM API update
Chris@537 1284 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
Chris@537 1285 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@649 1286 */
Chris@537 1287 }
Chris@537 1288
Chris@537 1289 void
Chris@537 1290 MainWindowBase::subdivideInstantsBy(int n)
Chris@537 1291 {
Chris@537 1292 Pane *pane = m_paneStack->getCurrentPane();
Chris@537 1293 if (!pane) return;
Chris@537 1294
Chris@537 1295 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@537 1296 if (!layer) return;
Chris@537 1297
Chris@537 1298 MultiSelection ms(m_viewManager->getSelection());
Chris@537 1299
Chris@537 1300 Model *model = layer->getModel();
Chris@537 1301 SparseOneDimensionalModel *sodm =
Chris@537 1302 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@537 1303 if (!sodm) return;
Chris@537 1304
Chris@537 1305 if (!m_labeller) return;
Chris@537 1306
Chris@537 1307 Labeller labeller(*m_labeller);
Chris@537 1308 labeller.setSampleRate(sodm->getSampleRate());
Chris@537 1309
Chris@649 1310 (void)n;
Chris@649 1311 /*!!! to be updated after SODM API update
Chris@537 1312 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point>
Chris@537 1313 (*sodm, &ms, n);
Chris@537 1314 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@649 1315 */
Chris@45 1316 }
Chris@45 1317
Chris@538 1318 void
Chris@538 1319 MainWindowBase::winnowInstantsBy(int n)
Chris@538 1320 {
Chris@538 1321 Pane *pane = m_paneStack->getCurrentPane();
Chris@538 1322 if (!pane) return;
Chris@538 1323
Chris@538 1324 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
Chris@538 1325 if (!layer) return;
Chris@538 1326
Chris@538 1327 MultiSelection ms(m_viewManager->getSelection());
Chris@538 1328
Chris@538 1329 Model *model = layer->getModel();
Chris@538 1330 SparseOneDimensionalModel *sodm =
Chris@538 1331 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@538 1332 if (!sodm) return;
Chris@538 1333
Chris@538 1334 if (!m_labeller) return;
Chris@538 1335
Chris@538 1336 Labeller labeller(*m_labeller);
Chris@538 1337 labeller.setSampleRate(sodm->getSampleRate());
Chris@538 1338
Chris@649 1339 (void)n;
Chris@649 1340 /*!!! to be updated after SODM API update
Chris@538 1341 Command *c = labeller.winnow<SparseOneDimensionalModel::Point>
Chris@538 1342 (*sodm, &ms, n);
Chris@538 1343 if (c) CommandHistory::getInstance()->addCommand(c, false);
Chris@649 1344 */
Chris@538 1345 }
Chris@538 1346
Chris@45 1347 MainWindowBase::FileOpenStatus
Chris@373 1348 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
Chris@45 1349 {
Chris@134 1350 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
Chris@134 1351 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1352 return open(FileSource(fileOrUrl, &dialog), mode);
Chris@45 1353 }
Chris@45 1354
Chris@45 1355 MainWindowBase::FileOpenStatus
Chris@45 1356 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
Chris@45 1357 {
Chris@45 1358 FileOpenStatus status;
Chris@45 1359
Chris@45 1360 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1361 source.waitForData();
Chris@45 1362
Chris@636 1363 bool canImportLayer = (getMainModel() != nullptr &&
Chris@636 1364 m_paneStack != nullptr &&
Chris@636 1365 m_paneStack->getCurrentPane() != nullptr);
Chris@45 1366
Chris@152 1367 bool rdf = (source.getExtension().toLower() == "rdf" ||
Chris@152 1368 source.getExtension().toLower() == "n3" ||
Chris@152 1369 source.getExtension().toLower() == "ttl");
Chris@152 1370
Chris@152 1371 bool audio = AudioFileReaderFactory::getKnownExtensions().contains
Chris@152 1372 (source.getExtension().toLower());
Chris@145 1373
Chris@145 1374 bool rdfSession = false;
Chris@145 1375 if (rdf) {
Chris@145 1376 RDFImporter::RDFDocumentType rdfType =
Chris@145 1377 RDFImporter::identifyDocumentType
Chris@145 1378 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
Chris@145 1379 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@145 1380 rdfType == RDFImporter::AudioRef) {
Chris@145 1381 rdfSession = true;
Chris@145 1382 } else if (rdfType == RDFImporter::NotRDF) {
Chris@145 1383 rdf = false;
Chris@145 1384 }
Chris@145 1385 }
Chris@145 1386
Chris@579 1387 try {
Chris@579 1388 if (rdf) {
Chris@579 1389 if (rdfSession) {
Chris@579 1390 bool cancel = false;
Chris@579 1391 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
Chris@579 1392 return openSession(source);
Chris@579 1393 } else if (cancel) {
Chris@579 1394 return FileOpenCancelled;
Chris@579 1395 } else {
Chris@579 1396 return openLayer(source);
Chris@579 1397 }
Chris@145 1398 } else {
Chris@579 1399 if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1400 return status;
Chris@579 1401 } else if (!canImportLayer) {
Chris@579 1402 return FileOpenWrongMode;
Chris@579 1403 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1404 return status;
Chris@579 1405 } else {
Chris@579 1406 return FileOpenFailed;
Chris@579 1407 }
Chris@145 1408 }
Chris@145 1409 }
Chris@579 1410
Chris@579 1411 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
Chris@579 1412 return status;
Chris@579 1413 } else if ((status = openSession(source)) != FileOpenFailed) {
Chris@579 1414 return status;
Chris@579 1415 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
Chris@579 1416 return status;
Chris@579 1417 } else if (!canImportLayer) {
Chris@579 1418 return FileOpenWrongMode;
Chris@579 1419 } else if ((status = openImage(source)) != FileOpenFailed) {
Chris@579 1420 return status;
Chris@579 1421 } else if ((status = openLayer(source)) != FileOpenFailed) {
Chris@579 1422 return status;
Chris@579 1423 } else {
Chris@579 1424 return FileOpenFailed;
Chris@579 1425 }
Chris@579 1426 } catch (const InsufficientDiscSpace &e) {
Chris@579 1427 emit hideSplash();
Chris@579 1428 m_openingAudioFile = false;
Chris@594 1429 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
Chris@579 1430 QMessageBox::critical
Chris@579 1431 (this, tr("Not enough disc space"),
Chris@579 1432 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 1433 return FileOpenFailed;
Chris@592 1434 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
Chris@592 1435 emit hideSplash();
Chris@592 1436 m_openingAudioFile = false;
Chris@594 1437 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
Chris@592 1438 QMessageBox::critical
Chris@592 1439 (this, tr("Not enough memory"),
Chris@592 1440 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
Chris@592 1441 return FileOpenFailed;
Chris@45 1442 }
Chris@45 1443 }
Chris@45 1444
Chris@45 1445 MainWindowBase::FileOpenStatus
Chris@626 1446 MainWindowBase::openAudio(FileSource source,
Chris@626 1447 AudioFileOpenMode mode,
Chris@227 1448 QString templateName)
Chris@45 1449 {
Chris@386 1450 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
Chris@45 1451
Chris@222 1452 if (templateName == "") {
Chris@231 1453 templateName = getDefaultSessionTemplate();
Chris@577 1454 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
Chris@222 1455 }
Chris@220 1456
Chris@374 1457 // cerr << "template is: \"" << templateName << "\"" << endl;
Chris@223 1458
Chris@413 1459 if (!source.isAvailable()) {
Chris@413 1460 if (source.wasCancelled()) {
Chris@413 1461 return FileOpenCancelled;
Chris@413 1462 } else {
Chris@413 1463 return FileOpenFailed;
Chris@413 1464 }
Chris@413 1465 }
Chris@413 1466
Chris@45 1467 source.waitForData();
Chris@45 1468
Chris@45 1469 m_openingAudioFile = true;
Chris@45 1470
Chris@435 1471 sv_samplerate_t rate = 0;
Chris@45 1472
Chris@626 1473 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
Chris@626 1474 << endl;
Chris@626 1475
Chris@360 1476 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
Chris@360 1477 rate = Preferences::getInstance()->getFixedSampleRate();
Chris@626 1478 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
Chris@360 1479 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 1480 if (getMainModel()) {
Chris@626 1481 if (mode == ReplaceSession || mode == ReplaceMainModel) {
Chris@626 1482 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 1483 } else {
Chris@626 1484 rate = getMainModel()->getSampleRate();
Chris@626 1485 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
Chris@626 1486 }
Chris@552 1487 }
Chris@45 1488 }
Chris@45 1489
Chris@626 1490 if (rate == 0) {
Chris@626 1491 SVDEBUG << "Yes, preserving incoming file rate" << endl;
Chris@626 1492 }
Chris@626 1493
Chris@479 1494 ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate);
Chris@45 1495
Chris@45 1496 if (!newModel->isOK()) {
Chris@595 1497 delete newModel;
Chris@45 1498 m_openingAudioFile = false;
Chris@413 1499 if (source.wasCancelled()) {
Chris@413 1500 return FileOpenCancelled;
Chris@413 1501 } else {
Chris@413 1502 return FileOpenFailed;
Chris@413 1503 }
Chris@45 1504 }
Chris@45 1505
Chris@604 1506 return addOpenedAudioModel(source, newModel, mode, templateName, true);
Chris@604 1507 }
Chris@604 1508
Chris@604 1509 MainWindowBase::FileOpenStatus
Chris@604 1510 MainWindowBase::addOpenedAudioModel(FileSource source,
Chris@604 1511 WaveFileModel *newModel,
Chris@604 1512 AudioFileOpenMode mode,
Chris@604 1513 QString templateName,
Chris@604 1514 bool registerSource)
Chris@604 1515 {
Chris@45 1516 if (mode == AskUser) {
Chris@45 1517 if (getMainModel()) {
Chris@45 1518
Chris@147 1519 QSettings settings;
Chris@147 1520 settings.beginGroup("MainWindow");
Chris@221 1521 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
Chris@147 1522 settings.endGroup();
Chris@221 1523 int imode = 0;
Chris@45 1524
Chris@45 1525 QStringList items;
Chris@221 1526 items << tr("Close the current session and start a new one")
Chris@221 1527 << tr("Replace the main audio file in this session")
Chris@221 1528 << tr("Add the audio file to this session");
Chris@45 1529
Chris@45 1530 bool ok = false;
Chris@45 1531 QString item = ListInputDialog::getItem
Chris@45 1532 (this, tr("Select target for import"),
Chris@221 1533 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 1534 items, lastMode, &ok);
Chris@45 1535
Chris@45 1536 if (!ok || item.isEmpty()) {
Chris@45 1537 delete newModel;
Chris@45 1538 m_openingAudioFile = false;
Chris@45 1539 return FileOpenCancelled;
Chris@45 1540 }
Chris@45 1541
Chris@221 1542 for (int i = 0; i < items.size(); ++i) {
Chris@221 1543 if (item == items[i]) imode = i;
Chris@221 1544 }
Chris@221 1545
Chris@147 1546 settings.beginGroup("MainWindow");
Chris@221 1547 settings.setValue("lastaudioopenmode", imode);
Chris@147 1548 settings.endGroup();
Chris@45 1549
Chris@221 1550 mode = (AudioFileOpenMode)imode;
Chris@45 1551
Chris@45 1552 } else {
Chris@221 1553 // no main model: make a new session
Chris@221 1554 mode = ReplaceSession;
Chris@45 1555 }
Chris@45 1556 }
Chris@45 1557
Chris@45 1558 if (mode == ReplaceCurrentPane) {
Chris@45 1559
Chris@45 1560 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1561 if (pane) {
Chris@45 1562 if (getMainModel()) {
Chris@45 1563 View::ModelSet models(pane->getModels());
Chris@45 1564 if (models.find(getMainModel()) != models.end()) {
Chris@221 1565 // Current pane contains main model: replace that
Chris@45 1566 mode = ReplaceMainModel;
Chris@45 1567 }
Chris@221 1568 // Otherwise the current pane has a non-default model,
Chris@221 1569 // which we will deal with later
Chris@45 1570 } else {
Chris@221 1571 // We have no main model, so start a new session with
Chris@221 1572 // optional template
Chris@221 1573 mode = ReplaceSession;
Chris@45 1574 }
Chris@45 1575 } else {
Chris@221 1576 // We seem to have no current pane! Oh well
Chris@45 1577 mode = CreateAdditionalModel;
Chris@45 1578 }
Chris@45 1579 }
Chris@45 1580
Chris@45 1581 if (mode == CreateAdditionalModel && !getMainModel()) {
Chris@386 1582 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
Chris@221 1583 mode = ReplaceSession;
Chris@221 1584 }
Chris@221 1585
Chris@221 1586 bool loadedTemplate = false;
Chris@221 1587
Chris@221 1588 if (mode == ReplaceSession) {
Chris@258 1589
Chris@258 1590 if (!checkSaveModified()) return FileOpenCancelled;
Chris@258 1591
Chris@386 1592 SVDEBUG << "SV looking for template " << templateName << endl;
Chris@230 1593 if (templateName != "") {
Chris@230 1594 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@258 1595 if (tplStatus == FileOpenCancelled) {
Chris@577 1596 SVDEBUG << "Template load cancelled" << endl;
Chris@258 1597 return FileOpenCancelled;
Chris@258 1598 }
Chris@230 1599 if (tplStatus != FileOpenFailed) {
Chris@577 1600 SVDEBUG << "Template load succeeded" << endl;
Chris@230 1601 loadedTemplate = true;
Chris@221 1602 }
Chris@221 1603 }
Chris@221 1604
Chris@221 1605 if (!loadedTemplate) {
Chris@386 1606 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
Chris@221 1607 closeSession();
Chris@221 1608 createDocument();
Chris@221 1609 }
Chris@221 1610
Chris@386 1611 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
Chris@45 1612 mode = ReplaceMainModel;
Chris@45 1613 }
Chris@45 1614
Chris@164 1615 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
Chris@164 1616
Chris@45 1617 if (mode == ReplaceMainModel) {
Chris@45 1618
Chris@45 1619 Model *prevMain = getMainModel();
Chris@45 1620 if (prevMain) {
Chris@45 1621 m_playSource->removeModel(prevMain);
Chris@108 1622 PlayParameterRepository::getInstance()->removePlayable(prevMain);
Chris@45 1623 }
Chris@108 1624 PlayParameterRepository::getInstance()->addPlayable(newModel);
Chris@45 1625
Chris@248 1626 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
Chris@248 1627
Chris@595 1628 m_document->setMainModel(newModel);
Chris@595 1629
Chris@595 1630 setupMenus();
Chris@595 1631
Chris@595 1632 if (loadedTemplate || (m_sessionFile == "")) {
Chris@45 1633 //!!! shouldn't be dealing directly with title from here -- call a method
Chris@595 1634 setWindowTitle(tr("%1: %2")
Chris@57 1635 .arg(QApplication::applicationName())
Chris@45 1636 .arg(source.getLocation()));
Chris@595 1637 CommandHistory::getInstance()->clear();
Chris@595 1638 CommandHistory::getInstance()->documentSaved();
Chris@595 1639 m_documentModified = false;
Chris@595 1640 } else {
Chris@595 1641 setWindowTitle(tr("%1: %2 [%3]")
Chris@57 1642 .arg(QApplication::applicationName())
Chris@595 1643 .arg(QFileInfo(m_sessionFile).fileName())
Chris@595 1644 .arg(source.getLocation()));
Chris@595 1645 if (m_documentModified) {
Chris@595 1646 m_documentModified = false;
Chris@595 1647 documentModified(); // so as to restore "(modified)" window title
Chris@595 1648 }
Chris@595 1649 }
Chris@45 1650
Chris@604 1651 if (!source.isRemote() && registerSource) {
Chris@604 1652 m_audioFile = source.getLocalFilename();
Chris@604 1653 }
Chris@45 1654
Chris@45 1655 } else if (mode == CreateAdditionalModel) {
Chris@45 1656
Chris@577 1657 SVCERR << "Mode is CreateAdditionalModel" << endl;
Chris@577 1658
Chris@595 1659 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1660 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1661
Chris@595 1662 m_document->addImportedModel(newModel);
Chris@595 1663
Chris@595 1664 AddPaneCommand *command = new AddPaneCommand(this);
Chris@595 1665 CommandHistory::getInstance()->addCommand(command);
Chris@595 1666
Chris@595 1667 Pane *pane = command->getPane();
Chris@45 1668
Chris@47 1669 if (m_timeRulerLayer) {
Chris@577 1670 SVCERR << "Have time ruler, adding it" << endl;
Chris@47 1671 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@577 1672 } else {
Chris@577 1673 SVCERR << "Do not have time ruler" << endl;
Chris@47 1674 }
Chris@45 1675
Chris@595 1676 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1677
Chris@595 1678 if (newLayer) {
Chris@595 1679 m_document->addLayerToView(pane, newLayer);
Chris@595 1680 }
Chris@595 1681
Chris@595 1682 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1683
Chris@45 1684 } else if (mode == ReplaceCurrentPane) {
Chris@45 1685
Chris@45 1686 // We know there is a current pane, otherwise we would have
Chris@45 1687 // reset the mode to CreateAdditionalModel above; and we know
Chris@45 1688 // the current pane does not contain the main model, otherwise
Chris@45 1689 // we would have reset it to ReplaceMainModel. But we don't
Chris@45 1690 // know whether the pane contains a waveform model at all.
Chris@45 1691
Chris@45 1692 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 1693 Layer *replace = nullptr;
Chris@45 1694
Chris@45 1695 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@45 1696 Layer *layer = pane->getLayer(i);
Chris@45 1697 if (dynamic_cast<WaveformLayer *>(layer)) {
Chris@45 1698 replace = layer;
Chris@45 1699 break;
Chris@45 1700 }
Chris@45 1701 }
Chris@45 1702
Chris@595 1703 CommandHistory::getInstance()->startCompoundOperation
Chris@595 1704 (tr("Import \"%1\"").arg(source.getBasename()), true);
Chris@595 1705
Chris@595 1706 m_document->addImportedModel(newModel);
Chris@45 1707
Chris@45 1708 if (replace) {
Chris@45 1709 m_document->removeLayerFromView(pane, replace);
Chris@45 1710 }
Chris@45 1711
Chris@595 1712 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@595 1713
Chris@595 1714 if (newLayer) {
Chris@595 1715 m_document->addLayerToView(pane, newLayer);
Chris@595 1716 }
Chris@595 1717
Chris@595 1718 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 1719 }
Chris@45 1720
Chris@45 1721 updateMenuStates();
Chris@622 1722
Chris@622 1723 if (registerSource) {
Chris@622 1724 m_recentFiles.addFile(source.getLocation());
Chris@622 1725 }
Chris@604 1726 if (!source.isRemote() && registerSource) {
Chris@45 1727 // for file dialog
Chris@45 1728 registerLastOpenedFilePath(FileFinder::AudioFile,
Chris@45 1729 source.getLocalFilename());
Chris@45 1730 }
Chris@622 1731
Chris@45 1732 m_openingAudioFile = false;
Chris@45 1733
Chris@45 1734 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@45 1735
Chris@342 1736 emit audioFileLoaded();
Chris@342 1737
Chris@45 1738 return FileOpenSucceeded;
Chris@45 1739 }
Chris@45 1740
Chris@45 1741 MainWindowBase::FileOpenStatus
Chris@45 1742 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
Chris@45 1743 {
Chris@233 1744 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
Chris@135 1745
Chris@45 1746 std::set<QString> extensions;
Chris@45 1747 PlaylistFileReader::getSupportedExtensions(extensions);
Chris@152 1748 QString extension = source.getExtension().toLower();
Chris@45 1749 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
Chris@45 1750
Chris@45 1751 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1752 source.waitForData();
Chris@45 1753
Chris@45 1754 PlaylistFileReader reader(source.getLocalFilename());
Chris@45 1755 if (!reader.isOK()) return FileOpenFailed;
Chris@45 1756
Chris@45 1757 PlaylistFileReader::Playlist playlist = reader.load();
Chris@45 1758
Chris@45 1759 bool someSuccess = false;
Chris@45 1760
Chris@45 1761 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
Chris@45 1762 i != playlist.end(); ++i) {
Chris@45 1763
Chris@134 1764 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
Chris@134 1765 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 1766 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
Chris@45 1767
Chris@45 1768 if (status == FileOpenCancelled) {
Chris@45 1769 return FileOpenCancelled;
Chris@45 1770 }
Chris@45 1771
Chris@45 1772 if (status == FileOpenSucceeded) {
Chris@45 1773 someSuccess = true;
Chris@45 1774 mode = CreateAdditionalModel;
Chris@45 1775 }
Chris@45 1776 }
Chris@45 1777
Chris@45 1778 if (someSuccess) return FileOpenSucceeded;
Chris@45 1779 else return FileOpenFailed;
Chris@45 1780 }
Chris@45 1781
Chris@45 1782 MainWindowBase::FileOpenStatus
Chris@45 1783 MainWindowBase::openLayer(FileSource source)
Chris@45 1784 {
Chris@233 1785 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
Chris@135 1786
Chris@45 1787 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1788
Chris@45 1789 if (!pane) {
Chris@595 1790 // shouldn't happen, as the menu action should have been disabled
Chris@595 1791 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
Chris@595 1792 return FileOpenWrongMode;
Chris@45 1793 }
Chris@45 1794
Chris@45 1795 if (!getMainModel()) {
Chris@595 1796 // shouldn't happen, as the menu action should have been disabled
Chris@595 1797 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
Chris@595 1798 return FileOpenWrongMode;
Chris@45 1799 }
Chris@45 1800
Chris@45 1801 if (!source.isAvailable()) return FileOpenFailed;
Chris@45 1802 source.waitForData();
Chris@45 1803
Chris@45 1804 QString path = source.getLocalFilename();
Chris@45 1805
Chris@145 1806 RDFImporter::RDFDocumentType rdfType =
Chris@145 1807 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
Chris@145 1808
Chris@293 1809 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
Chris@148 1810
Chris@145 1811 if (rdfType != RDFImporter::NotRDF) {
Chris@145 1812
Chris@145 1813 return openLayersFromRDF(source);
Chris@134 1814
Chris@152 1815 } else if (source.getExtension().toLower() == "svl" ||
Chris@152 1816 (source.getExtension().toLower() == "xml" &&
Chris@140 1817 (SVFileReader::identifyXmlFile(source.getLocalFilename())
Chris@140 1818 == SVFileReader::SVLayerFile))) {
Chris@45 1819
Chris@45 1820 PaneCallback callback(this);
Chris@45 1821 QFile file(path);
Chris@45 1822
Chris@45 1823 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@293 1824 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1825 << source.getLocation()
Chris@293 1826 << "): Failed to open file for reading" << endl;
Chris@45 1827 return FileOpenFailed;
Chris@45 1828 }
Chris@45 1829
Chris@45 1830 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 1831 connect
Chris@79 1832 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 1833 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 1834 connect
Chris@79 1835 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 1836 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@45 1837 reader.setCurrentPane(pane);
Chris@45 1838
Chris@45 1839 QXmlInputSource inputSource(&file);
Chris@45 1840 reader.parse(inputSource);
Chris@45 1841
Chris@45 1842 if (!reader.isOK()) {
Chris@293 1843 cerr << "ERROR: MainWindowBase::openLayer("
Chris@294 1844 << source.getLocation()
Chris@45 1845 << "): Failed to read XML file: "
Chris@293 1846 << reader.getErrorString() << endl;
Chris@45 1847 return FileOpenFailed;
Chris@45 1848 }
Chris@45 1849
Chris@164 1850 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
Chris@164 1851
Chris@45 1852 m_recentFiles.addFile(source.getLocation());
Chris@45 1853
Chris@45 1854 if (!source.isRemote()) {
Chris@45 1855 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
Chris@45 1856 }
Chris@45 1857
Chris@75 1858 return FileOpenSucceeded;
Chris@75 1859
Chris@45 1860 } else {
Chris@45 1861
Chris@45 1862 try {
Chris@45 1863
Chris@109 1864 MIDIFileImportDialog midiDlg(this);
Chris@109 1865
Chris@109 1866 Model *model = DataFileReaderFactory::loadNonCSV
Chris@109 1867 (path, &midiDlg, getMainModel()->getSampleRate());
Chris@45 1868
Chris@109 1869 if (!model) {
Chris@643 1870 CSVFormatDialog *dialog =
Chris@643 1871 new CSVFormatDialog(this,
Chris@643 1872 path,
Chris@643 1873 getMainModel()->getSampleRate(),
Chris@643 1874 5);
Chris@109 1875 if (dialog->exec() == QDialog::Accepted) {
Chris@109 1876 model = DataFileReaderFactory::loadCSV
Chris@109 1877 (path, dialog->getFormat(),
Chris@109 1878 getMainModel()->getSampleRate());
Chris@109 1879 }
Chris@619 1880 delete dialog;
Chris@109 1881 }
Chris@109 1882
Chris@45 1883 if (model) {
Chris@45 1884
Chris@233 1885 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
Chris@45 1886
Chris@164 1887 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
Chris@164 1888
Chris@45 1889 Layer *newLayer = m_document->createImportedLayer(model);
Chris@45 1890
Chris@45 1891 if (newLayer) {
Chris@45 1892
Chris@45 1893 m_document->addLayerToView(pane, newLayer);
Chris@88 1894 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@88 1895
Chris@45 1896 m_recentFiles.addFile(source.getLocation());
Chris@45 1897
Chris@45 1898 if (!source.isRemote()) {
Chris@45 1899 registerLastOpenedFilePath
Chris@45 1900 (FileFinder::LayerFile,
Chris@45 1901 path); // for file dialog
Chris@45 1902 }
Chris@88 1903
Chris@45 1904 return FileOpenSucceeded;
Chris@45 1905 }
Chris@45 1906 }
Chris@45 1907 } catch (DataFileReaderFactory::Exception e) {
Chris@45 1908 if (e == DataFileReaderFactory::ImportCancelled) {
Chris@45 1909 return FileOpenCancelled;
Chris@45 1910 }
Chris@45 1911 }
Chris@45 1912 }
Chris@45 1913
Chris@45 1914 return FileOpenFailed;
Chris@45 1915 }
Chris@45 1916
Chris@45 1917 MainWindowBase::FileOpenStatus
Chris@45 1918 MainWindowBase::openImage(FileSource source)
Chris@45 1919 {
Chris@233 1920 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
Chris@135 1921
Chris@45 1922 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 1923
Chris@45 1924 if (!pane) {
Chris@595 1925 // shouldn't happen, as the menu action should have been disabled
Chris@595 1926 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
Chris@595 1927 return FileOpenWrongMode;
Chris@45 1928 }
Chris@45 1929
Chris@45 1930 if (!m_document->getMainModel()) {
Chris@45 1931 return FileOpenWrongMode;
Chris@45 1932 }
Chris@45 1933
Chris@45 1934 bool newLayer = false;
Chris@45 1935
Chris@45 1936 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
Chris@45 1937 if (!il) {
Chris@45 1938 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
Chris@45 1939 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
Chris@45 1940 if (il) break;
Chris@45 1941 }
Chris@45 1942 }
Chris@45 1943 if (!il) {
Chris@45 1944 il = dynamic_cast<ImageLayer *>
Chris@45 1945 (m_document->createEmptyLayer(LayerFactory::Image));
Chris@45 1946 if (!il) return FileOpenFailed;
Chris@45 1947 newLayer = true;
Chris@45 1948 }
Chris@45 1949
Chris@45 1950 // We don't put the image file in Recent Files
Chris@45 1951
Chris@293 1952 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
Chris@45 1953
Chris@45 1954 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
Chris@45 1955 if (newLayer) {
Chris@52 1956 m_document->deleteLayer(il); // also releases its model
Chris@45 1957 }
Chris@45 1958 return FileOpenFailed;
Chris@45 1959 } else {
Chris@45 1960 if (newLayer) {
Chris@45 1961 m_document->addLayerToView(pane, il);
Chris@45 1962 }
Chris@45 1963 m_paneStack->setCurrentLayer(pane, il);
Chris@45 1964 }
Chris@45 1965
Chris@45 1966 return FileOpenSucceeded;
Chris@45 1967 }
Chris@45 1968
Chris@45 1969 MainWindowBase::FileOpenStatus
Chris@427 1970 MainWindowBase::openDirOfAudio(QString dirPath)
Chris@427 1971 {
Chris@427 1972 QDir dir(dirPath);
Chris@427 1973 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
Chris@427 1974 files.sort();
Chris@427 1975
Chris@427 1976 FileOpenStatus status = FileOpenFailed;
Chris@427 1977 bool first = true;
Chris@427 1978 bool cancelled = false;
Chris@427 1979
Chris@427 1980 foreach (QString file, files) {
Chris@427 1981
Chris@427 1982 FileSource source(dir.filePath(file));
Chris@427 1983 if (!source.isAvailable()) {
Chris@427 1984 continue;
Chris@427 1985 }
Chris@427 1986
Chris@427 1987 if (AudioFileReaderFactory::getKnownExtensions().contains
Chris@427 1988 (source.getExtension().toLower())) {
Chris@427 1989
Chris@427 1990 AudioFileOpenMode mode = CreateAdditionalModel;
Chris@427 1991 if (first) mode = ReplaceSession;
Chris@427 1992
Chris@427 1993 switch (openAudio(source, mode)) {
Chris@427 1994 case FileOpenSucceeded:
Chris@427 1995 status = FileOpenSucceeded;
Chris@427 1996 first = false;
Chris@427 1997 break;
Chris@427 1998 case FileOpenFailed:
Chris@427 1999 break;
Chris@427 2000 case FileOpenCancelled:
Chris@427 2001 cancelled = true;
Chris@427 2002 break;
Chris@427 2003 case FileOpenWrongMode:
Chris@427 2004 break;
Chris@427 2005 }
Chris@427 2006 }
Chris@427 2007
Chris@427 2008 if (cancelled) break;
Chris@427 2009 }
Chris@427 2010
Chris@427 2011 return status;
Chris@427 2012 }
Chris@427 2013
Chris@427 2014 MainWindowBase::FileOpenStatus
Chris@373 2015 MainWindowBase::openSessionPath(QString fileOrUrl)
Chris@45 2016 {
Chris@134 2017 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
Chris@134 2018 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@109 2019 return openSession(FileSource(fileOrUrl, &dialog));
Chris@45 2020 }
Chris@45 2021
Chris@45 2022 MainWindowBase::FileOpenStatus
Chris@45 2023 MainWindowBase::openSession(FileSource source)
Chris@45 2024 {
Chris@233 2025 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
Chris@135 2026
Chris@45 2027 if (!source.isAvailable()) return FileOpenFailed;
Chris@145 2028 source.waitForData();
Chris@141 2029
Chris@341 2030 QString sessionExt =
Chris@341 2031 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
Chris@341 2032
Chris@341 2033 if (source.getExtension().toLower() != sessionExt) {
Chris@145 2034
Chris@145 2035 RDFImporter::RDFDocumentType rdfType =
Chris@145 2036 RDFImporter::identifyDocumentType
Chris@145 2037 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
Chris@145 2038
Chris@293 2039 // cerr << "RDF type: " << (int)rdfType << endl;
Chris@148 2040
Chris@145 2041 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
Chris@145 2042 rdfType == RDFImporter::AudioRef) {
Chris@145 2043 return openSessionFromRDF(source);
Chris@145 2044 } else if (rdfType != RDFImporter::NotRDF) {
Chris@145 2045 return FileOpenFailed;
Chris@145 2046 }
Chris@145 2047
Chris@152 2048 if (source.getExtension().toLower() == "xml") {
Chris@140 2049 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
Chris@140 2050 SVFileReader::SVSessionFile) {
Chris@293 2051 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
Chris@140 2052 } else {
Chris@140 2053 return FileOpenFailed;
Chris@140 2054 }
Chris@140 2055 } else {
Chris@140 2056 return FileOpenFailed;
Chris@140 2057 }
Chris@140 2058 }
Chris@45 2059
Chris@636 2060 QXmlInputSource *inputSource = nullptr;
Chris@636 2061 BZipFileDevice *bzFile = nullptr;
Chris@636 2062 QFile *rawFile = nullptr;
Chris@140 2063
Chris@341 2064 if (source.getExtension().toLower() == sessionExt) {
Chris@140 2065 bzFile = new BZipFileDevice(source.getLocalFilename());
Chris@140 2066 if (!bzFile->open(QIODevice::ReadOnly)) {
Chris@140 2067 delete bzFile;
Chris@140 2068 return FileOpenFailed;
Chris@140 2069 }
Chris@140 2070 inputSource = new QXmlInputSource(bzFile);
Chris@140 2071 } else {
Chris@140 2072 rawFile = new QFile(source.getLocalFilename());
Chris@140 2073 inputSource = new QXmlInputSource(rawFile);
Chris@140 2074 }
Chris@140 2075
Chris@140 2076 if (!checkSaveModified()) {
Chris@140 2077 if (bzFile) bzFile->close();
Chris@140 2078 delete inputSource;
Chris@140 2079 delete bzFile;
Chris@140 2080 delete rawFile;
Chris@140 2081 return FileOpenCancelled;
Chris@140 2082 }
Chris@45 2083
Chris@45 2084 QString error;
Chris@45 2085 closeSession();
Chris@45 2086 createDocument();
Chris@45 2087
Chris@45 2088 PaneCallback callback(this);
Chris@45 2089 m_viewManager->clearSelections();
Chris@45 2090
Chris@45 2091 SVFileReader reader(m_document, callback, source.getLocation());
Chris@79 2092 connect
Chris@79 2093 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@79 2094 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@79 2095 connect
Chris@79 2096 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@79 2097 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@140 2098
Chris@140 2099 reader.parse(*inputSource);
Chris@45 2100
Chris@45 2101 if (!reader.isOK()) {
Chris@45 2102 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@45 2103 }
Chris@45 2104
Chris@140 2105 if (bzFile) bzFile->close();
Chris@140 2106
Chris@140 2107 delete inputSource;
Chris@140 2108 delete bzFile;
Chris@140 2109 delete rawFile;
Chris@45 2110
Chris@45 2111 bool ok = (error == "");
Chris@45 2112
Chris@45 2113 if (ok) {
Chris@45 2114
Chris@164 2115 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
Chris@164 2116
Chris@595 2117 setWindowTitle(tr("%1: %2")
Chris@57 2118 .arg(QApplication::applicationName())
Chris@595 2119 .arg(source.getLocation()));
Chris@595 2120
Chris@601 2121 if (!source.isRemote() && !m_document->isIncomplete()) {
Chris@601 2122 // Setting the session file path enables the Save (as
Chris@601 2123 // opposed to Save As...) option. We can't do this if we
Chris@601 2124 // don't have a local path to save to, but we also don't
Chris@601 2125 // want to do it if we failed to find an audio file or
Chris@601 2126 // similar on load, as the audio reference would then end
Chris@601 2127 // up being lost from any saved or auto-saved-on-exit copy
Chris@601 2128 m_sessionFile = source.getLocalFilename();
Chris@602 2129 } else {
Chris@602 2130 QMessageBox::warning
Chris@602 2131 (this,
Chris@602 2132 tr("Incomplete session loaded"),
Chris@603 2133 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 2134 QMessageBox::Ok);
Chris@601 2135 }
Chris@595 2136
Chris@595 2137 setupMenus();
Chris@577 2138 findTimeRulerLayer();
Chris@45 2139
Chris@595 2140 CommandHistory::getInstance()->clear();
Chris@595 2141 CommandHistory::getInstance()->documentSaved();
Chris@595 2142 m_documentModified = false;
Chris@595 2143 updateMenuStates();
Chris@45 2144
Chris@227 2145 m_recentFiles.addFile(source.getLocation());
Chris@45 2146
Chris@45 2147 if (!source.isRemote()) {
Chris@45 2148 // for file dialog
Chris@45 2149 registerLastOpenedFilePath(FileFinder::SessionFile,
Chris@227 2150 source.getLocalFilename());
Chris@45 2151 }
Chris@45 2152
Chris@342 2153 emit sessionLoaded();
Chris@342 2154
Chris@45 2155 } else {
Chris@595 2156 setWindowTitle(QApplication::applicationName());
Chris@45 2157 }
Chris@45 2158
Chris@45 2159 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@45 2160 }
Chris@45 2161
Chris@141 2162 MainWindowBase::FileOpenStatus
Chris@230 2163 MainWindowBase::openSessionTemplate(QString templateName)
Chris@230 2164 {
Chris@230 2165 // Template in the user's template directory takes
Chris@230 2166 // priority over a bundled one; we don't unbundle, but
Chris@230 2167 // open directly from the bundled file (where applicable)
Chris@230 2168 ResourceFinder rf;
Chris@230 2169 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
Chris@230 2170 if (tfile != "") {
Chris@294 2171 cerr << "SV loading template file " << tfile << endl;
Chris@230 2172 return openSessionTemplate(FileSource("file:" + tfile));
Chris@230 2173 } else {
Chris@230 2174 return FileOpenFailed;
Chris@230 2175 }
Chris@230 2176 }
Chris@230 2177
Chris@230 2178 MainWindowBase::FileOpenStatus
Chris@227 2179 MainWindowBase::openSessionTemplate(FileSource source)
Chris@227 2180 {
Chris@294 2181 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
Chris@227 2182
Chris@227 2183 if (!source.isAvailable()) return FileOpenFailed;
Chris@227 2184 source.waitForData();
Chris@227 2185
Chris@636 2186 QXmlInputSource *inputSource = nullptr;
Chris@636 2187 QFile *file = nullptr;
Chris@227 2188
Chris@227 2189 file = new QFile(source.getLocalFilename());
Chris@227 2190 inputSource = new QXmlInputSource(file);
Chris@227 2191
Chris@227 2192 if (!checkSaveModified()) {
Chris@227 2193 delete inputSource;
Chris@227 2194 delete file;
Chris@227 2195 return FileOpenCancelled;
Chris@227 2196 }
Chris@227 2197
Chris@227 2198 QString error;
Chris@227 2199 closeSession();
Chris@227 2200 createDocument();
Chris@227 2201
Chris@227 2202 PaneCallback callback(this);
Chris@227 2203 m_viewManager->clearSelections();
Chris@227 2204
Chris@227 2205 SVFileReader reader(m_document, callback, source.getLocation());
Chris@227 2206 connect
Chris@227 2207 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
Chris@227 2208 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
Chris@227 2209 connect
Chris@227 2210 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@227 2211 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@227 2212
Chris@227 2213 reader.parse(*inputSource);
Chris@227 2214
Chris@227 2215 if (!reader.isOK()) {
Chris@227 2216 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@227 2217 }
Chris@227 2218
Chris@227 2219 delete inputSource;
Chris@227 2220 delete file;
Chris@227 2221
Chris@227 2222 bool ok = (error == "");
Chris@227 2223
Chris@227 2224 setWindowTitle(QApplication::applicationName());
Chris@227 2225
Chris@227 2226 if (ok) {
Chris@227 2227
Chris@227 2228 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
Chris@227 2229
Chris@595 2230 setupMenus();
Chris@577 2231 findTimeRulerLayer();
Chris@227 2232
Chris@595 2233 CommandHistory::getInstance()->clear();
Chris@595 2234 CommandHistory::getInstance()->documentSaved();
Chris@595 2235 m_documentModified = false;
Chris@595 2236 updateMenuStates();
Chris@342 2237
Chris@342 2238 emit sessionLoaded();
Chris@227 2239 }
Chris@227 2240
Chris@227 2241 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@227 2242 }
Chris@227 2243
Chris@227 2244 MainWindowBase::FileOpenStatus
Chris@141 2245 MainWindowBase::openSessionFromRDF(FileSource source)
Chris@141 2246 {
Chris@233 2247 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
Chris@141 2248
Chris@141 2249 if (!source.isAvailable()) return FileOpenFailed;
Chris@141 2250 source.waitForData();
Chris@141 2251
Chris@145 2252 if (!checkSaveModified()) {
Chris@145 2253 return FileOpenCancelled;
Chris@141 2254 }
Chris@143 2255
Chris@145 2256 closeSession();
Chris@145 2257 createDocument();
Chris@145 2258
Chris@145 2259 FileOpenStatus status = openLayersFromRDF(source);
Chris@141 2260
Chris@141 2261 setupMenus();
Chris@577 2262 findTimeRulerLayer();
Chris@141 2263
Chris@141 2264 setWindowTitle(tr("%1: %2")
Chris@141 2265 .arg(QApplication::applicationName())
Chris@141 2266 .arg(source.getLocation()));
Chris@141 2267 CommandHistory::getInstance()->clear();
Chris@141 2268 CommandHistory::getInstance()->documentSaved();
Chris@141 2269 m_documentModified = false;
Chris@145 2270
Chris@342 2271 emit sessionLoaded();
Chris@342 2272
Chris@145 2273 return status;
Chris@145 2274 }
Chris@145 2275
Chris@145 2276 MainWindowBase::FileOpenStatus
Chris@145 2277 MainWindowBase::openLayersFromRDF(FileSource source)
Chris@145 2278 {
Chris@435 2279 sv_samplerate_t rate = 0;
Chris@145 2280
Chris@233 2281 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
Chris@186 2282
Chris@145 2283 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
Chris@145 2284 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
Chris@145 2285
Chris@145 2286 if (getMainModel()) {
Chris@145 2287 rate = getMainModel()->getSampleRate();
Chris@145 2288 } else if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@552 2289 if (getMainModel()) {
Chris@552 2290 rate = getMainModel()->getSampleRate();
Chris@552 2291 }
Chris@145 2292 }
Chris@145 2293
Chris@145 2294 RDFImporter importer
Chris@145 2295 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
Chris@145 2296
Chris@145 2297 if (!importer.isOK()) {
Chris@147 2298 if (importer.getErrorString() != "") {
Chris@147 2299 QMessageBox::critical
Chris@147 2300 (this, tr("Failed to import RDF"),
Chris@147 2301 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
Chris@147 2302 .arg(source.getLocation()).arg(importer.getErrorString()));
Chris@147 2303 }
Chris@145 2304 return FileOpenFailed;
Chris@145 2305 }
Chris@145 2306
Chris@145 2307 std::vector<Model *> models = importer.getDataModels(&dialog);
Chris@145 2308
Chris@145 2309 dialog.setMessage(tr("Importing from RDF..."));
Chris@145 2310
Chris@145 2311 if (models.empty()) {
Chris@186 2312 QMessageBox::critical
Chris@186 2313 (this, tr("Failed to import RDF"),
Chris@186 2314 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 2315 return FileOpenFailed;
Chris@145 2316 }
Chris@145 2317
Chris@164 2318 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
Chris@164 2319
Chris@145 2320 std::set<Model *> added;
Chris@145 2321
Chris@221 2322 for (int i = 0; i < (int)models.size(); ++i) {
Chris@145 2323
Chris@145 2324 Model *m = models[i];
Chris@145 2325 WaveFileModel *w = dynamic_cast<WaveFileModel *>(m);
Chris@145 2326
Chris@145 2327 if (w) {
Chris@145 2328
Chris@145 2329 Pane *pane = addPaneToStack();
Chris@636 2330 Layer *layer = nullptr;
Chris@145 2331
Chris@145 2332 if (m_timeRulerLayer) {
Chris@145 2333 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@145 2334 }
Chris@145 2335
Chris@145 2336 if (!getMainModel()) {
Chris@145 2337 m_document->setMainModel(w);
Chris@145 2338 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@145 2339 } else {
Chris@145 2340 layer = m_document->createImportedLayer(w);
Chris@145 2341 }
Chris@145 2342
Chris@145 2343 m_document->addLayerToView(pane, layer);
Chris@145 2344
Chris@145 2345 added.insert(w);
Chris@145 2346
Chris@221 2347 for (int j = 0; j < (int)models.size(); ++j) {
Chris@145 2348
Chris@145 2349 Model *dm = models[j];
Chris@145 2350
Chris@145 2351 if (dm == m) continue;
Chris@145 2352 if (dm->getSourceModel() != m) continue;
Chris@145 2353
Chris@145 2354 layer = m_document->createImportedLayer(dm);
Chris@145 2355
Chris@145 2356 if (layer->isLayerOpaque() ||
Chris@145 2357 dynamic_cast<Colour3DPlotLayer *>(layer)) {
Chris@145 2358
Chris@156 2359 // these always go in a new pane, with nothing
Chris@156 2360 // else going in the same pane
Chris@156 2361
Chris@145 2362 Pane *singleLayerPane = addPaneToStack();
Chris@145 2363 if (m_timeRulerLayer) {
Chris@145 2364 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2365 }
Chris@145 2366 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2367
Chris@156 2368 } else if (layer->getLayerColourSignificance() ==
Chris@156 2369 Layer::ColourHasMeaningfulValue) {
Chris@156 2370
Chris@156 2371 // these can go in a pane with something else, but
Chris@156 2372 // only if none of the something elses also have
Chris@156 2373 // this quality
Chris@156 2374
Chris@156 2375 bool needNewPane = false;
Chris@156 2376 for (int i = 0; i < pane->getLayerCount(); ++i) {
Chris@156 2377 Layer *otherLayer = pane->getLayer(i);
Chris@156 2378 if (otherLayer &&
Chris@156 2379 (otherLayer->getLayerColourSignificance() ==
Chris@156 2380 Layer::ColourHasMeaningfulValue)) {
Chris@156 2381 needNewPane = true;
Chris@156 2382 break;
Chris@156 2383 }
Chris@156 2384 }
Chris@156 2385 if (needNewPane) {
Chris@156 2386 pane = addPaneToStack();
Chris@156 2387 }
Chris@156 2388
Chris@156 2389 m_document->addLayerToView(pane, layer);
Chris@156 2390
Chris@145 2391 } else {
Chris@145 2392
Chris@145 2393 if (pane->getLayerCount() > 4) {
Chris@145 2394 pane = addPaneToStack();
Chris@145 2395 }
Chris@145 2396
Chris@145 2397 m_document->addLayerToView(pane, layer);
Chris@145 2398 }
Chris@145 2399
Chris@145 2400 added.insert(dm);
Chris@145 2401 }
Chris@145 2402 }
Chris@145 2403 }
Chris@145 2404
Chris@221 2405 for (int i = 0; i < (int)models.size(); ++i) {
Chris@145 2406
Chris@145 2407 Model *m = models[i];
Chris@145 2408
Chris@145 2409 if (added.find(m) == added.end()) {
Chris@145 2410
Chris@145 2411 Layer *layer = m_document->createImportedLayer(m);
Chris@145 2412 if (!layer) return FileOpenFailed;
Chris@145 2413
Chris@145 2414 Pane *singleLayerPane = addPaneToStack();
Chris@145 2415 if (m_timeRulerLayer) {
Chris@145 2416 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
Chris@145 2417 }
Chris@145 2418 m_document->addLayerToView(singleLayerPane, layer);
Chris@145 2419 }
Chris@145 2420 }
Chris@145 2421
Chris@145 2422 m_recentFiles.addFile(source.getLocation());
Chris@145 2423 return FileOpenSucceeded;
Chris@141 2424 }
Chris@141 2425
Chris@584 2426 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
Chris@584 2427 {
Chris@584 2428 public:
Chris@584 2429 void log(std::string message) const override {
Chris@584 2430 SVDEBUG << message << endl;
Chris@584 2431 }
Chris@584 2432 };
Chris@584 2433
Chris@45 2434 void
Chris@475 2435 MainWindowBase::createAudioIO()
Chris@45 2436 {
Chris@475 2437 if (m_playTarget || m_audioIO) return;
Chris@475 2438
Chris@584 2439 static AudioLogCallback audioLogCallback;
Chris@584 2440 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
Chris@584 2441
Chris@475 2442 if (!(m_soundOptions & WithAudioOutput)) return;
Chris@45 2443
Chris@126 2444 QSettings settings;
Chris@126 2445 settings.beginGroup("Preferences");
Chris@547 2446 QString implementation = settings.value
Chris@547 2447 ("audio-target", "").toString();
Chris@547 2448 QString suffix;
Chris@547 2449 if (implementation != "") suffix = "-" + implementation;
Chris@547 2450 QString recordDevice = settings.value
Chris@547 2451 ("audio-record-device" + suffix, "").toString();
Chris@547 2452 QString playbackDevice = settings.value
Chris@547 2453 ("audio-playback-device" + suffix, "").toString();
Chris@126 2454 settings.endGroup();
Chris@547 2455
Chris@547 2456 if (implementation == "auto") {
Chris@547 2457 implementation = "";
Chris@547 2458 }
Chris@468 2459
Chris@547 2460 breakfastquay::AudioFactory::Preference preference;
Chris@547 2461 preference.implementation = implementation.toStdString();
Chris@547 2462 preference.recordDevice = recordDevice.toStdString();
Chris@547 2463 preference.playbackDevice = playbackDevice.toStdString();
Chris@547 2464
Chris@547 2465 SVCERR << "createAudioIO: Preferred implementation = \""
Chris@547 2466 << preference.implementation << "\"" << endl;
Chris@547 2467 SVCERR << "createAudioIO: Preferred playback device = \""
Chris@547 2468 << preference.playbackDevice << "\"" << endl;
Chris@547 2469 SVCERR << "createAudioIO: Preferred record device = \""
Chris@547 2470 << preference.recordDevice << "\"" << endl;
Chris@475 2471
Chris@551 2472 if (!m_resamplerWrapper) {
Chris@551 2473 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource);
Chris@551 2474 m_playSource->setResamplerWrapper(m_resamplerWrapper);
Chris@551 2475 }
Chris@569 2476
Chris@569 2477 std::string errorString;
Chris@551 2478
Chris@475 2479 if (m_soundOptions & WithAudioInput) {
Chris@475 2480 m_audioIO = breakfastquay::AudioFactory::
Chris@569 2481 createCallbackIO(m_recordTarget, m_resamplerWrapper,
Chris@569 2482 preference, errorString);
Chris@525 2483 if (m_audioIO) {
Chris@611 2484 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2485 m_audioIO->suspend(); // start in suspended state
Chris@525 2486 m_playSource->setSystemPlaybackTarget(m_audioIO);
Chris@586 2487 } else {
Chris@586 2488 // Failed to create audio I/O; this may just mean there is
Chris@586 2489 // no record device, so fall through to see what happens
Chris@586 2490 // next. We only report complete failure if we end up with
Chris@586 2491 // neither m_audioIO nor m_playTarget.
Chris@525 2492 }
Chris@586 2493 }
Chris@586 2494
Chris@586 2495 if (!m_audioIO) {
Chris@475 2496 m_playTarget = breakfastquay::AudioFactory::
Chris@569 2497 createCallbackPlayTarget(m_resamplerWrapper,
Chris@569 2498 preference, errorString);
Chris@525 2499 if (m_playTarget) {
Chris@611 2500 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
Chris@525 2501 m_playTarget->suspend(); // start in suspended state
Chris@525 2502 m_playSource->setSystemPlaybackTarget(m_playTarget);
Chris@525 2503 }
Chris@475 2504 }
Chris@475 2505
Chris@475 2506 if (!m_playTarget && !m_audioIO) {
Chris@104 2507 emit hideSplash();
Chris@569 2508 QString message;
Chris@569 2509 QString error = errorString.c_str();
Chris@569 2510 QString firstBit, secondBit;
Chris@547 2511 if (implementation == "") {
Chris@569 2512 if (error == "") {
Chris@569 2513 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
Chris@569 2514 } else {
Chris@569 2515 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
Chris@569 2516 }
Chris@569 2517 if (m_soundOptions & WithAudioInput) {
Chris@569 2518 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
Chris@569 2519 } else {
Chris@569 2520 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
Chris@569 2521 }
Chris@126 2522 } else {
Chris@569 2523 QString driverName = breakfastquay::AudioFactory::
Chris@569 2524 getImplementationDescription(implementation.toStdString())
Chris@569 2525 .c_str();
Chris@569 2526 if (error == "") {
Chris@569 2527 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
Chris@569 2528 } else {
Chris@569 2529 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
Chris@569 2530 }
Chris@569 2531 if (m_soundOptions & WithAudioInput) {
Chris@569 2532 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
Chris@569 2533 } else {
Chris@569 2534 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
Chris@569 2535 }
Chris@126 2536 }
Chris@570 2537 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
Chris@570 2538 << implementation << "\": error is: " << error << endl;
Chris@569 2539 QMessageBox::warning(this, tr("Couldn't open audio device"),
Chris@569 2540 firstBit + secondBit, QMessageBox::Ok);
Chris@45 2541 }
Chris@45 2542 }
Chris@45 2543
Chris@556 2544 void
Chris@556 2545 MainWindowBase::deleteAudioIO()
Chris@556 2546 {
Chris@556 2547 // First prevent this trying to call target.
Chris@559 2548 if (m_playSource) {
Chris@636 2549 m_playSource->setSystemPlaybackTarget(nullptr);
Chris@636 2550 m_playSource->setResamplerWrapper(nullptr);
Chris@559 2551 }
Chris@556 2552
Chris@556 2553 // Then delete the breakfastquay::System object.
Chris@556 2554 // Only one of these two exists!
Chris@556 2555 delete m_audioIO;
Chris@556 2556 delete m_playTarget;
Chris@556 2557
Chris@559 2558 // And the breakfastquay resampler wrapper. We need to
Chris@559 2559 // delete/recreate this if the channel count changes, which is one
Chris@559 2560 // of the use cases for recreateAudioIO() calling this
Chris@559 2561 delete m_resamplerWrapper;
Chris@559 2562
Chris@636 2563 m_audioIO = nullptr;
Chris@636 2564 m_playTarget = nullptr;
Chris@636 2565 m_resamplerWrapper = nullptr;
Chris@556 2566 }
Chris@556 2567
Chris@556 2568 void
Chris@556 2569 MainWindowBase::recreateAudioIO()
Chris@556 2570 {
Chris@556 2571 deleteAudioIO();
Chris@556 2572 createAudioIO();
Chris@556 2573 }
Chris@556 2574
Chris@570 2575 void
Chris@570 2576 MainWindowBase::audioChannelCountIncreased(int)
Chris@570 2577 {
Chris@611 2578 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
Chris@570 2579 recreateAudioIO();
Chris@610 2580
Chris@610 2581 if (m_recordTarget &&
Chris@610 2582 m_recordTarget->isRecording() &&
Chris@610 2583 m_audioIO) {
Chris@610 2584 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
Chris@610 2585 m_audioIO->resume();
Chris@610 2586 }
Chris@570 2587 }
Chris@570 2588
Chris@45 2589 WaveFileModel *
Chris@45 2590 MainWindowBase::getMainModel()
Chris@45 2591 {
Chris@636 2592 if (!m_document) return nullptr;
Chris@45 2593 return m_document->getMainModel();
Chris@45 2594 }
Chris@45 2595
Chris@45 2596 const WaveFileModel *
Chris@45 2597 MainWindowBase::getMainModel() const
Chris@45 2598 {
Chris@636 2599 if (!m_document) return nullptr;
Chris@45 2600 return m_document->getMainModel();
Chris@45 2601 }
Chris@45 2602
Chris@45 2603 void
Chris@45 2604 MainWindowBase::createDocument()
Chris@45 2605 {
Chris@45 2606 m_document = new Document;
Chris@45 2607
Chris@45 2608 connect(m_document, SIGNAL(layerAdded(Layer *)),
Chris@595 2609 this, SLOT(layerAdded(Layer *)));
Chris@45 2610 connect(m_document, SIGNAL(layerRemoved(Layer *)),
Chris@595 2611 this, SLOT(layerRemoved(Layer *)));
Chris@45 2612 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
Chris@595 2613 this, SLOT(layerAboutToBeDeleted(Layer *)));
Chris@45 2614 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
Chris@595 2615 this, SLOT(layerInAView(Layer *, bool)));
Chris@45 2616
Chris@45 2617 connect(m_document, SIGNAL(modelAdded(Model *)),
Chris@595 2618 this, SLOT(modelAdded(Model *)));
Chris@45 2619 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
Chris@595 2620 this, SLOT(mainModelChanged(WaveFileModel *)));
Chris@45 2621 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@595 2622 this, SLOT(modelAboutToBeDeleted(Model *)));
Chris@45 2623
Chris@78 2624 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
Chris@78 2625 this, SLOT(modelGenerationFailed(QString, QString)));
Chris@78 2626 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
Chris@78 2627 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
Chris@429 2628 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)),
Chris@429 2629 this, SLOT(alignmentComplete(AlignmentModel *)));
Chris@423 2630 connect(m_document, SIGNAL(alignmentFailed(QString)),
Chris@423 2631 this, SLOT(alignmentFailed(QString)));
Chris@160 2632
Chris@160 2633 emit replacedDocument();
Chris@45 2634 }
Chris@45 2635
Chris@45 2636 bool
Chris@45 2637 MainWindowBase::saveSessionFile(QString path)
Chris@45 2638 {
Chris@217 2639 try {
Chris@217 2640
Chris@217 2641 TempWriteFile temp(path);
Chris@217 2642
Chris@217 2643 BZipFileDevice bzFile(temp.getTemporaryFilename());
Chris@217 2644 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@293 2645 cerr << "Failed to open session file \""
Chris@294 2646 << temp.getTemporaryFilename()
Chris@217 2647 << "\" for writing: "
Chris@293 2648 << bzFile.errorString() << endl;
Chris@217 2649 return false;
Chris@217 2650 }
Chris@217 2651
Chris@217 2652 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@217 2653
Chris@217 2654 QTextStream out(&bzFile);
Chris@432 2655 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2656 toXml(out, false);
Chris@217 2657 out.flush();
Chris@217 2658
Chris@217 2659 QApplication::restoreOverrideCursor();
Chris@217 2660
Chris@217 2661 if (!bzFile.isOK()) {
Chris@217 2662 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2663 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2664 .arg(path).arg(bzFile.errorString()));
Chris@217 2665 bzFile.close();
Chris@217 2666 return false;
Chris@217 2667 }
Chris@217 2668
Chris@217 2669 bzFile.close();
Chris@217 2670 temp.moveToTarget();
Chris@217 2671 return true;
Chris@217 2672
Chris@217 2673 } catch (FileOperationFailed &f) {
Chris@217 2674
Chris@217 2675 QMessageBox::critical(this, tr("Failed to write file"),
Chris@217 2676 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@217 2677 .arg(path).arg(f.what()));
Chris@45 2678 return false;
Chris@45 2679 }
Chris@45 2680 }
Chris@45 2681
Chris@224 2682 bool
Chris@224 2683 MainWindowBase::saveSessionTemplate(QString path)
Chris@224 2684 {
Chris@224 2685 try {
Chris@224 2686
Chris@224 2687 TempWriteFile temp(path);
Chris@224 2688
Chris@224 2689 QFile file(temp.getTemporaryFilename());
Chris@224 2690 if (!file.open(QIODevice::WriteOnly)) {
Chris@293 2691 cerr << "Failed to open session template file \""
Chris@294 2692 << temp.getTemporaryFilename()
Chris@224 2693 << "\" for writing: "
Chris@294 2694 << file.errorString() << endl;
Chris@224 2695 return false;
Chris@224 2696 }
Chris@224 2697
Chris@224 2698 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@224 2699
Chris@224 2700 QTextStream out(&file);
Chris@432 2701 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@226 2702 toXml(out, true);
Chris@224 2703 out.flush();
Chris@224 2704
Chris@224 2705 QApplication::restoreOverrideCursor();
Chris@224 2706
Chris@224 2707 file.close();
Chris@224 2708 temp.moveToTarget();
Chris@224 2709 return true;
Chris@224 2710
Chris@224 2711 } catch (FileOperationFailed &f) {
Chris@224 2712
Chris@224 2713 QMessageBox::critical(this, tr("Failed to write file"),
Chris@224 2714 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
Chris@224 2715 .arg(path).arg(f.what()));
Chris@224 2716 return false;
Chris@224 2717 }
Chris@224 2718 }
Chris@224 2719
Chris@45 2720 void
Chris@226 2721 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
Chris@45 2722 {
Chris@45 2723 QString indent(" ");
Chris@45 2724
Chris@45 2725 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@45 2726 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@45 2727 out << "<sv>\n";
Chris@45 2728
Chris@226 2729 if (asTemplate) {
Chris@226 2730 m_document->toXmlAsTemplate(out, "", "");
Chris@226 2731 } else {
Chris@226 2732 m_document->toXml(out, "", "");
Chris@226 2733 }
Chris@45 2734
Chris@45 2735 out << "<display>\n";
Chris@45 2736
Chris@45 2737 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@595 2738 .arg(width()).arg(height());
Chris@45 2739
Chris@45 2740 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 2741
Chris@595 2742 Pane *pane = m_paneStack->getPane(i);
Chris@595 2743
Chris@595 2744 if (pane) {
Chris@45 2745 pane->toXml(out, indent);
Chris@595 2746 }
Chris@45 2747 }
Chris@45 2748
Chris@45 2749 out << "</display>\n";
Chris@45 2750
Chris@45 2751 m_viewManager->getSelection().toXml(out);
Chris@45 2752
Chris@45 2753 out << "</sv>\n";
Chris@45 2754 }
Chris@45 2755
Chris@45 2756 Pane *
Chris@45 2757 MainWindowBase::addPaneToStack()
Chris@45 2758 {
Chris@342 2759 cerr << "MainWindowBase::addPaneToStack()" << endl;
Chris@45 2760 AddPaneCommand *command = new AddPaneCommand(this);
Chris@45 2761 CommandHistory::getInstance()->addCommand(command);
Chris@57 2762 Pane *pane = command->getPane();
Chris@57 2763 return pane;
Chris@45 2764 }
Chris@45 2765
Chris@45 2766 void
Chris@45 2767 MainWindowBase::zoomIn()
Chris@45 2768 {
Chris@45 2769 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2770 if (currentPane) currentPane->zoom(true);
Chris@45 2771 }
Chris@45 2772
Chris@45 2773 void
Chris@45 2774 MainWindowBase::zoomOut()
Chris@45 2775 {
Chris@45 2776 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2777 if (currentPane) currentPane->zoom(false);
Chris@45 2778 }
Chris@45 2779
Chris@45 2780 void
Chris@45 2781 MainWindowBase::zoomToFit()
Chris@45 2782 {
Chris@45 2783 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2784 if (!currentPane) return;
Chris@45 2785
Chris@45 2786 Model *model = getMainModel();
Chris@45 2787 if (!model) return;
Chris@45 2788
Chris@434 2789 sv_frame_t start = model->getStartFrame();
Chris@434 2790 sv_frame_t end = model->getEndFrame();
Chris@60 2791 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
Chris@366 2792 int pixels = currentPane->width();
Chris@366 2793
Chris@366 2794 int sw = currentPane->getVerticalScaleWidth();
Chris@45 2795 if (pixels > sw * 2) pixels -= sw * 2;
Chris@45 2796 else pixels = 1;
Chris@45 2797 if (pixels > 4) pixels -= 4;
Chris@45 2798
Chris@624 2799 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
Chris@45 2800 currentPane->setZoomLevel(zoomLevel);
Chris@45 2801 currentPane->setCentreFrame((start + end) / 2);
Chris@45 2802 }
Chris@45 2803
Chris@45 2804 void
Chris@45 2805 MainWindowBase::zoomDefault()
Chris@45 2806 {
Chris@45 2807 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@302 2808 QSettings settings;
Chris@302 2809 settings.beginGroup("MainWindow");
Chris@302 2810 int zoom = settings.value("zoom-default", 1024).toInt();
Chris@302 2811 settings.endGroup();
Chris@624 2812 if (currentPane) {
Chris@624 2813 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
Chris@624 2814 }
Chris@45 2815 }
Chris@45 2816
Chris@45 2817 void
Chris@45 2818 MainWindowBase::scrollLeft()
Chris@45 2819 {
Chris@45 2820 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2821 if (currentPane) currentPane->scroll(false, false);
Chris@45 2822 }
Chris@45 2823
Chris@45 2824 void
Chris@45 2825 MainWindowBase::jumpLeft()
Chris@45 2826 {
Chris@45 2827 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2828 if (currentPane) currentPane->scroll(false, true);
Chris@45 2829 }
Chris@45 2830
Chris@45 2831 void
Chris@162 2832 MainWindowBase::peekLeft()
Chris@162 2833 {
Chris@162 2834 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 2835 if (currentPane) currentPane->scroll(false, false, false);
Chris@162 2836 }
Chris@162 2837
Chris@162 2838 void
Chris@45 2839 MainWindowBase::scrollRight()
Chris@45 2840 {
Chris@45 2841 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2842 if (currentPane) currentPane->scroll(true, false);
Chris@45 2843 }
Chris@45 2844
Chris@45 2845 void
Chris@45 2846 MainWindowBase::jumpRight()
Chris@45 2847 {
Chris@45 2848 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@45 2849 if (currentPane) currentPane->scroll(true, true);
Chris@45 2850 }
Chris@45 2851
Chris@45 2852 void
Chris@162 2853 MainWindowBase::peekRight()
Chris@162 2854 {
Chris@162 2855 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@162 2856 if (currentPane) currentPane->scroll(true, false, false);
Chris@162 2857 }
Chris@162 2858
Chris@162 2859 void
Chris@45 2860 MainWindowBase::showNoOverlays()
Chris@45 2861 {
Chris@45 2862 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@45 2863 }
Chris@45 2864
Chris@45 2865 void
Chris@45 2866 MainWindowBase::showMinimalOverlays()
Chris@45 2867 {
Chris@335 2868 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@45 2869 }
Chris@45 2870
Chris@45 2871 void
Chris@45 2872 MainWindowBase::showAllOverlays()
Chris@45 2873 {
Chris@45 2874 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@45 2875 }
Chris@45 2876
Chris@45 2877 void
Chris@577 2878 MainWindowBase::findTimeRulerLayer()
Chris@577 2879 {
Chris@577 2880 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@577 2881 Pane *pane = m_paneStack->getPane(i);
Chris@577 2882 if (!pane) continue;
Chris@577 2883 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@577 2884 Layer *layer = pane->getLayer(j);
Chris@577 2885 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@577 2886 m_timeRulerLayer = layer;
Chris@577 2887 return;
Chris@577 2888 }
Chris@577 2889 }
Chris@577 2890 if (m_timeRulerLayer) {
Chris@577 2891 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
Chris@577 2892 delete m_timeRulerLayer;
Chris@636 2893 m_timeRulerLayer = nullptr;
Chris@577 2894 }
Chris@577 2895 }
Chris@577 2896
Chris@577 2897 void
Chris@211 2898 MainWindowBase::toggleTimeRulers()
Chris@211 2899 {
Chris@211 2900 bool haveRulers = false;
Chris@211 2901 bool someHidden = false;
Chris@211 2902
Chris@211 2903 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 2904
Chris@211 2905 Pane *pane = m_paneStack->getPane(i);
Chris@211 2906 if (!pane) continue;
Chris@211 2907
Chris@211 2908 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 2909
Chris@211 2910 Layer *layer = pane->getLayer(j);
Chris@211 2911 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 2912
Chris@211 2913 haveRulers = true;
Chris@211 2914 if (layer->isLayerDormant(pane)) someHidden = true;
Chris@211 2915 }
Chris@211 2916 }
Chris@211 2917
Chris@211 2918 if (haveRulers) {
Chris@211 2919
Chris@211 2920 bool show = someHidden;
Chris@211 2921
Chris@211 2922 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@211 2923
Chris@211 2924 Pane *pane = m_paneStack->getPane(i);
Chris@211 2925 if (!pane) continue;
Chris@211 2926
Chris@211 2927 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@211 2928
Chris@211 2929 Layer *layer = pane->getLayer(j);
Chris@211 2930 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
Chris@211 2931
Chris@211 2932 layer->showLayer(pane, show);
Chris@211 2933 }
Chris@211 2934 }
Chris@211 2935 }
Chris@211 2936 }
Chris@211 2937
Chris@211 2938 void
Chris@45 2939 MainWindowBase::toggleZoomWheels()
Chris@45 2940 {
Chris@45 2941 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@45 2942 m_viewManager->setZoomWheelsEnabled(false);
Chris@45 2943 } else {
Chris@45 2944 m_viewManager->setZoomWheelsEnabled(true);
Chris@45 2945 }
Chris@45 2946 }
Chris@45 2947
Chris@45 2948 void
Chris@45 2949 MainWindowBase::togglePropertyBoxes()
Chris@45 2950 {
Chris@45 2951 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
Chris@45 2952 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 2953 Preferences::VerticallyStacked) {
Chris@45 2954 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 2955 } else {
Chris@45 2956 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 2957 }
Chris@45 2958 } else {
Chris@45 2959 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@45 2960 }
Chris@45 2961 }
Chris@45 2962
Chris@378 2963 QLabel *
Chris@378 2964 MainWindowBase::getStatusLabel() const
Chris@378 2965 {
Chris@378 2966 if (!m_statusLabel) {
Chris@378 2967 m_statusLabel = new QLabel();
Chris@378 2968 statusBar()->addWidget(m_statusLabel, 1);
Chris@378 2969 }
Chris@379 2970
Chris@379 2971 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
Chris@379 2972 foreach (QFrame *f, frames) {
Chris@379 2973 f->setFrameStyle(QFrame::NoFrame);
Chris@379 2974 }
Chris@379 2975
Chris@378 2976 return m_statusLabel;
Chris@378 2977 }
Chris@378 2978
Chris@45 2979 void
Chris@45 2980 MainWindowBase::toggleStatusBar()
Chris@45 2981 {
Chris@45 2982 QSettings settings;
Chris@45 2983 settings.beginGroup("MainWindow");
Chris@45 2984 bool sb = settings.value("showstatusbar", true).toBool();
Chris@45 2985
Chris@45 2986 if (sb) {
Chris@45 2987 statusBar()->hide();
Chris@45 2988 } else {
Chris@45 2989 statusBar()->show();
Chris@45 2990 }
Chris@45 2991
Chris@45 2992 settings.setValue("showstatusbar", !sb);
Chris@45 2993
Chris@45 2994 settings.endGroup();
Chris@45 2995 }
Chris@45 2996
Chris@45 2997 void
Chris@256 2998 MainWindowBase::toggleCentreLine()
Chris@256 2999 {
Chris@256 3000 if (m_viewManager->shouldShowCentreLine()) {
Chris@256 3001 m_viewManager->setShowCentreLine(false);
Chris@256 3002 } else {
Chris@256 3003 m_viewManager->setShowCentreLine(true);
Chris@256 3004 }
Chris@256 3005 }
Chris@256 3006
Chris@256 3007 void
Chris@45 3008 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
Chris@45 3009 {
Chris@45 3010 if (name == "Property Box Layout") {
Chris@45 3011 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
Chris@45 3012 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@45 3013 Preferences::VerticallyStacked) {
Chris@45 3014 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@45 3015 } else {
Chris@45 3016 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@45 3017 }
Chris@45 3018 }
Chris@45 3019 } else if (name == "Background Mode" && m_viewManager) {
Chris@45 3020 Preferences::BackgroundMode mode =
Chris@45 3021 Preferences::getInstance()->getBackgroundMode();
Chris@45 3022 if (mode == Preferences::BackgroundFromTheme) {
Chris@45 3023 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@45 3024 } else if (mode == Preferences::DarkBackground) {
Chris@45 3025 m_viewManager->setGlobalDarkBackground(true);
Chris@45 3026 } else {
Chris@45 3027 m_viewManager->setGlobalDarkBackground(false);
Chris@45 3028 }
Chris@45 3029 }
Chris@45 3030 }
Chris@45 3031
Chris@45 3032 void
Chris@45 3033 MainWindowBase::play()
Chris@45 3034 {
Chris@516 3035 if ((m_recordTarget && m_recordTarget->isRecording()) ||
Chris@516 3036 (m_playSource && m_playSource->isPlaying())) {
Chris@45 3037 stop();
Chris@479 3038 QAction *action = qobject_cast<QAction *>(sender());
Chris@479 3039 if (action) action->setChecked(false);
Chris@45 3040 } else {
Chris@487 3041 if (m_audioIO) m_audioIO->resume();
Chris@509 3042 else if (m_playTarget) m_playTarget->resume();
Chris@45 3043 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@595 3044 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@45 3045 }
Chris@45 3046 }
Chris@45 3047
Chris@45 3048 void
Chris@477 3049 MainWindowBase::record()
Chris@477 3050 {
Chris@586 3051 QAction *action = qobject_cast<QAction *>(sender());
Chris@586 3052
Chris@478 3053 if (!(m_soundOptions & WithAudioInput)) {
Chris@586 3054 if (action) action->setChecked(false);
Chris@478 3055 return;
Chris@478 3056 }
Chris@478 3057
Chris@477 3058 if (!m_recordTarget) {
Chris@586 3059 if (action) action->setChecked(false);
Chris@477 3060 return;
Chris@477 3061 }
Chris@477 3062
Chris@478 3063 if (!m_audioIO) {
Chris@611 3064 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
Chris@478 3065 createAudioIO();
Chris@478 3066 }
Chris@492 3067
Chris@492 3068 if (!m_audioIO) {
Chris@586 3069 if (!m_playTarget) {
Chris@586 3070 // Don't need to report this, createAudioIO should have
Chris@586 3071 if (action) action->setChecked(false);
Chris@586 3072 return;
Chris@586 3073 } else {
Chris@586 3074 // Need to report this: if the play target exists instead
Chris@586 3075 // of the audio IO, then that means we failed to open a
Chris@586 3076 // capture device. The record control should be disabled
Chris@586 3077 // in that situation, so if it happens here, that must
Chris@586 3078 // mean this is the first time we ever tried to open the
Chris@586 3079 // audio device, hence the need to report the problem here
Chris@586 3080 QMessageBox::critical
Chris@586 3081 (this, tr("No record device available"),
Chris@586 3082 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 3083 if (action) action->setChecked(false);
Chris@586 3084 updateMenuStates();
Chris@586 3085 return;
Chris@586 3086 }
Chris@492 3087 }
Chris@478 3088
Chris@477 3089 if (m_recordTarget->isRecording()) {
Chris@492 3090 stop();
Chris@477 3091 return;
Chris@477 3092 }
Chris@490 3093
Chris@483 3094 if (m_audioRecordMode == RecordReplaceSession) {
Chris@490 3095 if (!checkSaveModified()) {
Chris@490 3096 if (action) action->setChecked(false);
Chris@490 3097 return;
Chris@490 3098 }
Chris@483 3099 }
Chris@487 3100
Chris@557 3101 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
Chris@557 3102
Chris@611 3103 SVCERR << "MainWindowBase::record: about to resume" << endl;
Chris@492 3104 m_audioIO->resume();
Chris@509 3105
Chris@477 3106 WritableWaveFileModel *model = m_recordTarget->startRecording();
Chris@477 3107 if (!model) {
Chris@586 3108 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
Chris@586 3109 QMessageBox::critical
Chris@586 3110 (this, tr("Recording failed"),
Chris@586 3111 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
Chris@490 3112 if (action) action->setChecked(false);
Chris@477 3113 return;
Chris@477 3114 }
Chris@477 3115
Chris@477 3116 if (!model->isOK()) {
Chris@611 3117 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
Chris@477 3118 m_recordTarget->stopRecording();
Chris@492 3119 m_audioIO->suspend();
Chris@586 3120 if (action) action->setChecked(false);
Chris@477 3121 delete model;
Chris@477 3122 return;
Chris@477 3123 }
Chris@611 3124
Chris@611 3125 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
Chris@487 3126
Chris@478 3127 PlayParameterRepository::getInstance()->addPlayable(model);
Chris@483 3128
Chris@483 3129 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
Chris@478 3130
Chris@479 3131 //!!! duplication with openAudio here
Chris@479 3132
Chris@479 3133 QString templateName = getDefaultSessionTemplate();
Chris@479 3134 bool loadedTemplate = false;
Chris@479 3135
Chris@479 3136 if (templateName != "") {
Chris@479 3137 FileOpenStatus tplStatus = openSessionTemplate(templateName);
Chris@479 3138 if (tplStatus == FileOpenCancelled) {
Chris@611 3139 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
Chris@490 3140 m_recordTarget->stopRecording();
Chris@492 3141 m_audioIO->suspend();
Chris@490 3142 PlayParameterRepository::getInstance()->removePlayable(model);
Chris@479 3143 return;
Chris@479 3144 }
Chris@479 3145 if (tplStatus != FileOpenFailed) {
Chris@479 3146 loadedTemplate = true;
Chris@479 3147 }
Chris@479 3148 }
Chris@479 3149
Chris@479 3150 if (!loadedTemplate) {
Chris@479 3151 closeSession();
Chris@479 3152 createDocument();
Chris@479 3153 }
Chris@479 3154
Chris@479 3155 Model *prevMain = getMainModel();
Chris@479 3156 if (prevMain) {
Chris@479 3157 m_playSource->removeModel(prevMain);
Chris@479 3158 PlayParameterRepository::getInstance()->removePlayable(prevMain);
Chris@479 3159 }
Chris@479 3160
Chris@478 3161 m_document->setMainModel(model);
Chris@478 3162 setupMenus();
Chris@577 3163 findTimeRulerLayer();
Chris@478 3164
Chris@595 3165 if (loadedTemplate || (m_sessionFile == "")) {
Chris@479 3166 //!!! shouldn't be dealing directly with title from here -- call a method
Chris@595 3167 setWindowTitle(tr("%1: %2")
Chris@479 3168 .arg(QApplication::applicationName())
Chris@479 3169 .arg(model->getLocation()));
Chris@595 3170 CommandHistory::getInstance()->clear();
Chris@595 3171 CommandHistory::getInstance()->documentSaved();
Chris@595 3172 m_documentModified = false;
Chris@595 3173 } else {
Chris@595 3174 setWindowTitle(tr("%1: %2 [%3]")
Chris@479 3175 .arg(QApplication::applicationName())
Chris@595 3176 .arg(QFileInfo(m_sessionFile).fileName())
Chris@595 3177 .arg(model->getLocation()));
Chris@595 3178 if (m_documentModified) {
Chris@595 3179 m_documentModified = false;
Chris@595 3180 documentModified(); // so as to restore "(modified)" window title
Chris@595 3181 }
Chris@595 3182 }
Chris@479 3183
Chris@478 3184 } else {
Chris@478 3185
Chris@478 3186 CommandHistory::getInstance()->startCompoundOperation
Chris@478 3187 (tr("Import Recorded Audio"), true);
Chris@478 3188
Chris@478 3189 m_document->addImportedModel(model);
Chris@478 3190
Chris@478 3191 AddPaneCommand *command = new AddPaneCommand(this);
Chris@478 3192 CommandHistory::getInstance()->addCommand(command);
Chris@478 3193
Chris@478 3194 Pane *pane = command->getPane();
Chris@478 3195
Chris@478 3196 if (m_timeRulerLayer) {
Chris@478 3197 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@478 3198 }
Chris@478 3199
Chris@478 3200 Layer *newLayer = m_document->createImportedLayer(model);
Chris@478 3201
Chris@478 3202 if (newLayer) {
Chris@478 3203 m_document->addLayerToView(pane, newLayer);
Chris@478 3204 }
Chris@595 3205
Chris@478 3206 CommandHistory::getInstance()->endCompoundOperation();
Chris@477 3207 }
Chris@479 3208
Chris@479 3209 updateMenuStates();
Chris@479 3210 m_recentFiles.addFile(model->getLocation());
Chris@479 3211 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@611 3212
Chris@496 3213 emit audioFileLoaded();
Chris@477 3214 }
Chris@477 3215
Chris@477 3216 void
Chris@45 3217 MainWindowBase::ffwd()
Chris@45 3218 {
Chris@45 3219 if (!getMainModel()) return;
Chris@45 3220
Chris@435 3221 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@45 3222 ++frame;
Chris@45 3223
Chris@85 3224 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3225 Layer *layer = getSnapLayer();
Chris@435 3226 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3227
Chris@45 3228 if (!layer) {
Chris@45 3229
Chris@45 3230 frame = RealTime::realTime2Frame
Chris@357 3231 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
Chris@435 3232 if (frame > getMainModel()->getEndFrame()) {
Chris@45 3233 frame = getMainModel()->getEndFrame();
Chris@45 3234 }
Chris@45 3235
Chris@45 3236 } else {
Chris@45 3237
Chris@366 3238 int resolution = 0;
Chris@166 3239 if (pane) frame = pane->alignFromReference(frame);
Chris@85 3240 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@85 3241 frame, resolution, Layer::SnapRight)) {
Chris@85 3242 if (pane) frame = pane->alignToReference(frame);
Chris@85 3243 } else {
Chris@45 3244 frame = getMainModel()->getEndFrame();
Chris@45 3245 }
Chris@45 3246 }
Chris@45 3247
Chris@45 3248 if (frame < 0) frame = 0;
Chris@45 3249
Chris@45 3250 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3251 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3252 }
Chris@45 3253
Chris@45 3254 m_viewManager->setPlaybackFrame(frame);
Chris@166 3255
Chris@435 3256 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3257 m_playSource &&
Chris@166 3258 m_playSource->isPlaying() &&
Chris@166 3259 !m_viewManager->getPlayLoopMode()) {
Chris@166 3260 stop();
Chris@166 3261 }
Chris@45 3262 }
Chris@45 3263
Chris@45 3264 void
Chris@45 3265 MainWindowBase::ffwdEnd()
Chris@45 3266 {
Chris@45 3267 if (!getMainModel()) return;
Chris@45 3268
Chris@139 3269 if (m_playSource &&
Chris@139 3270 m_playSource->isPlaying() &&
Chris@139 3271 !m_viewManager->getPlayLoopMode()) {
Chris@139 3272 stop();
Chris@139 3273 }
Chris@139 3274
Chris@435 3275 sv_frame_t frame = getMainModel()->getEndFrame();
Chris@45 3276
Chris@45 3277 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3278 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3279 }
Chris@45 3280
Chris@45 3281 m_viewManager->setPlaybackFrame(frame);
Chris@45 3282 }
Chris@45 3283
Chris@45 3284 void
Chris@166 3285 MainWindowBase::ffwdSimilar()
Chris@166 3286 {
Chris@166 3287 if (!getMainModel()) return;
Chris@166 3288
Chris@166 3289 Layer *layer = getSnapLayer();
Chris@166 3290 if (!layer) { ffwd(); return; }
Chris@166 3291
Chris@166 3292 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3293 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3294
Chris@366 3295 int resolution = 0;
Chris@166 3296 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3297 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3298 frame, resolution, Layer::SnapRight)) {
Chris@166 3299 if (pane) frame = pane->alignToReference(frame);
Chris@166 3300 } else {
Chris@166 3301 frame = getMainModel()->getEndFrame();
Chris@166 3302 }
Chris@166 3303
Chris@166 3304 if (frame < 0) frame = 0;
Chris@166 3305
Chris@166 3306 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3307 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3308 }
Chris@166 3309
Chris@166 3310 m_viewManager->setPlaybackFrame(frame);
Chris@166 3311
Chris@435 3312 if (frame == getMainModel()->getEndFrame() &&
Chris@166 3313 m_playSource &&
Chris@166 3314 m_playSource->isPlaying() &&
Chris@166 3315 !m_viewManager->getPlayLoopMode()) {
Chris@166 3316 stop();
Chris@166 3317 }
Chris@166 3318 }
Chris@166 3319
Chris@166 3320 void
Chris@45 3321 MainWindowBase::rewind()
Chris@45 3322 {
Chris@45 3323 if (!getMainModel()) return;
Chris@45 3324
Chris@435 3325 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@45 3326 if (frame > 0) --frame;
Chris@45 3327
Chris@85 3328 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3329 Layer *layer = getSnapLayer();
Chris@435 3330 sv_samplerate_t sr = getMainModel()->getSampleRate();
Chris@45 3331
Chris@45 3332 // when rewinding during playback, we want to allow a period
Chris@45 3333 // following a rewind target point at which the rewind will go to
Chris@45 3334 // the prior point instead of the immediately neighbouring one
Chris@45 3335 if (m_playSource && m_playSource->isPlaying()) {
Chris@45 3336 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@357 3337 ct = ct - RealTime::fromSeconds(0.15);
Chris@45 3338 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@45 3339 frame = RealTime::realTime2Frame(ct, sr);
Chris@45 3340 }
Chris@45 3341
Chris@45 3342 if (!layer) {
Chris@45 3343
Chris@45 3344 frame = RealTime::realTime2Frame
Chris@357 3345 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
Chris@435 3346 if (frame < getMainModel()->getStartFrame()) {
Chris@45 3347 frame = getMainModel()->getStartFrame();
Chris@45 3348 }
Chris@45 3349
Chris@45 3350 } else {
Chris@45 3351
Chris@366 3352 int resolution = 0;
Chris@166 3353 if (pane) frame = pane->alignFromReference(frame);
Chris@85 3354 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@85 3355 frame, resolution, Layer::SnapLeft)) {
Chris@85 3356 if (pane) frame = pane->alignToReference(frame);
Chris@85 3357 } else {
Chris@45 3358 frame = getMainModel()->getStartFrame();
Chris@45 3359 }
Chris@45 3360 }
Chris@45 3361
Chris@45 3362 if (frame < 0) frame = 0;
Chris@45 3363
Chris@45 3364 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3365 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3366 }
Chris@45 3367
Chris@45 3368 m_viewManager->setPlaybackFrame(frame);
Chris@45 3369 }
Chris@45 3370
Chris@45 3371 void
Chris@45 3372 MainWindowBase::rewindStart()
Chris@45 3373 {
Chris@45 3374 if (!getMainModel()) return;
Chris@45 3375
Chris@435 3376 sv_frame_t frame = getMainModel()->getStartFrame();
Chris@45 3377
Chris@45 3378 if (m_viewManager->getPlaySelectionMode()) {
Chris@45 3379 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@45 3380 }
Chris@45 3381
Chris@45 3382 m_viewManager->setPlaybackFrame(frame);
Chris@45 3383 }
Chris@45 3384
Chris@166 3385 void
Chris@166 3386 MainWindowBase::rewindSimilar()
Chris@166 3387 {
Chris@166 3388 if (!getMainModel()) return;
Chris@166 3389
Chris@166 3390 Layer *layer = getSnapLayer();
Chris@166 3391 if (!layer) { rewind(); return; }
Chris@166 3392
Chris@166 3393 Pane *pane = m_paneStack->getCurrentPane();
Chris@435 3394 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@166 3395
Chris@366 3396 int resolution = 0;
Chris@166 3397 if (pane) frame = pane->alignFromReference(frame);
Chris@166 3398 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
Chris@166 3399 frame, resolution, Layer::SnapLeft)) {
Chris@166 3400 if (pane) frame = pane->alignToReference(frame);
Chris@166 3401 } else {
Chris@166 3402 frame = getMainModel()->getStartFrame();
Chris@166 3403 }
Chris@166 3404
Chris@166 3405 if (frame < 0) frame = 0;
Chris@166 3406
Chris@166 3407 if (m_viewManager->getPlaySelectionMode()) {
Chris@435 3408 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@166 3409 }
Chris@166 3410
Chris@166 3411 m_viewManager->setPlaybackFrame(frame);
Chris@166 3412 }
Chris@166 3413
Chris@45 3414 Layer *
Chris@45 3415 MainWindowBase::getSnapLayer() const
Chris@45 3416 {
Chris@45 3417 Pane *pane = m_paneStack->getCurrentPane();
Chris@636 3418 if (!pane) return nullptr;
Chris@45 3419
Chris@45 3420 Layer *layer = pane->getSelectedLayer();
Chris@45 3421
Chris@45 3422 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@45 3423 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@194 3424 !dynamic_cast<RegionLayer *>(layer) &&
Chris@45 3425 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@45 3426
Chris@636 3427 layer = nullptr;
Chris@45 3428
Chris@45 3429 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@45 3430 Layer *l = pane->getLayer(i-1);
Chris@45 3431 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@45 3432 layer = l;
Chris@45 3433 break;
Chris@45 3434 }
Chris@45 3435 }
Chris@45 3436 }
Chris@45 3437
Chris@45 3438 return layer;
Chris@45 3439 }
Chris@45 3440
Chris@45 3441 void
Chris@45 3442 MainWindowBase::stop()
Chris@45 3443 {
Chris@516 3444 if (m_recordTarget &&
Chris@516 3445 m_recordTarget->isRecording()) {
Chris@477 3446 m_recordTarget->stopRecording();
Chris@477 3447 }
Chris@516 3448
Chris@516 3449 if (!m_playSource) return;
Chris@516 3450
Chris@45 3451 m_playSource->stop();
Chris@45 3452
Chris@611 3453 SVCERR << "MainWindowBase::stop: suspending" << endl;
Chris@611 3454
Chris@487 3455 if (m_audioIO) m_audioIO->suspend();
Chris@509 3456 else if (m_playTarget) m_playTarget->suspend();
Chris@487 3457
Chris@45 3458 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@45 3459 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@45 3460 } else {
Chris@45 3461 m_myStatusMessage = "";
Chris@378 3462 getStatusLabel()->setText("");
Chris@45 3463 }
Chris@45 3464 }
Chris@45 3465
Chris@45 3466 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
Chris@45 3467 m_mw(mw),
Chris@636 3468 m_pane(nullptr),
Chris@636 3469 m_prevCurrentPane(nullptr),
Chris@45 3470 m_added(false)
Chris@45 3471 {
Chris@45 3472 }
Chris@45 3473
Chris@45 3474 MainWindowBase::AddPaneCommand::~AddPaneCommand()
Chris@45 3475 {
Chris@45 3476 if (m_pane && !m_added) {
Chris@595 3477 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3478 }
Chris@45 3479 }
Chris@45 3480
Chris@45 3481 QString
Chris@45 3482 MainWindowBase::AddPaneCommand::getName() const
Chris@45 3483 {
Chris@45 3484 return tr("Add Pane");
Chris@45 3485 }
Chris@45 3486
Chris@45 3487 void
Chris@45 3488 MainWindowBase::AddPaneCommand::execute()
Chris@45 3489 {
Chris@45 3490 if (!m_pane) {
Chris@595 3491 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@595 3492 m_pane = m_mw->m_paneStack->addPane();
Chris@45 3493
Chris@45 3494 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@45 3495 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@45 3496 } else {
Chris@595 3497 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3498 }
Chris@45 3499
Chris@45 3500 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@45 3501 m_added = true;
Chris@45 3502 }
Chris@45 3503
Chris@45 3504 void
Chris@45 3505 MainWindowBase::AddPaneCommand::unexecute()
Chris@45 3506 {
Chris@45 3507 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3508 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3509 m_added = false;
Chris@45 3510 }
Chris@45 3511
Chris@45 3512 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
Chris@45 3513 m_mw(mw),
Chris@45 3514 m_pane(pane),
Chris@636 3515 m_prevCurrentPane(nullptr),
Chris@45 3516 m_added(true)
Chris@45 3517 {
Chris@45 3518 }
Chris@45 3519
Chris@45 3520 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
Chris@45 3521 {
Chris@45 3522 if (m_pane && !m_added) {
Chris@595 3523 m_mw->m_paneStack->deletePane(m_pane);
Chris@45 3524 }
Chris@45 3525 }
Chris@45 3526
Chris@45 3527 QString
Chris@45 3528 MainWindowBase::RemovePaneCommand::getName() const
Chris@45 3529 {
Chris@45 3530 return tr("Remove Pane");
Chris@45 3531 }
Chris@45 3532
Chris@45 3533 void
Chris@45 3534 MainWindowBase::RemovePaneCommand::execute()
Chris@45 3535 {
Chris@45 3536 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@45 3537 m_mw->m_paneStack->hidePane(m_pane);
Chris@45 3538 m_added = false;
Chris@45 3539 }
Chris@45 3540
Chris@45 3541 void
Chris@45 3542 MainWindowBase::RemovePaneCommand::unexecute()
Chris@45 3543 {
Chris@45 3544 m_mw->m_paneStack->showPane(m_pane);
Chris@45 3545 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@45 3546 m_added = true;
Chris@45 3547 }
Chris@45 3548
Chris@45 3549 void
Chris@45 3550 MainWindowBase::deleteCurrentPane()
Chris@45 3551 {
Chris@45 3552 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3553 (tr("Delete Pane"), true);
Chris@45 3554
Chris@45 3555 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3556 if (pane) {
Chris@595 3557 while (pane->getLayerCount() > 0) {
Chris@595 3558 Layer *layer = pane->getLayer(0);
Chris@595 3559 if (layer) {
Chris@595 3560 m_document->removeLayerFromView(pane, layer);
Chris@595 3561 } else {
Chris@595 3562 break;
Chris@595 3563 }
Chris@595 3564 }
Chris@595 3565
Chris@595 3566 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@595 3567 CommandHistory::getInstance()->addCommand(command);
Chris@45 3568 }
Chris@45 3569
Chris@45 3570 CommandHistory::getInstance()->endCompoundOperation();
Chris@45 3571
Chris@45 3572 updateMenuStates();
Chris@45 3573 }
Chris@45 3574
Chris@45 3575 void
Chris@45 3576 MainWindowBase::deleteCurrentLayer()
Chris@45 3577 {
Chris@45 3578 Pane *pane = m_paneStack->getCurrentPane();
Chris@45 3579 if (pane) {
Chris@595 3580 Layer *layer = pane->getSelectedLayer();
Chris@595 3581 if (layer) {
Chris@595 3582 m_document->removeLayerFromView(pane, layer);
Chris@595 3583 }
Chris@45 3584 }
Chris@45 3585 updateMenuStates();
Chris@45 3586 }
Chris@45 3587
Chris@45 3588 void
Chris@123 3589 MainWindowBase::editCurrentLayer()
Chris@123 3590 {
Chris@636 3591 Layer *layer = nullptr;
Chris@123 3592 Pane *pane = m_paneStack->getCurrentPane();
Chris@123 3593 if (pane) layer = pane->getSelectedLayer();
Chris@123 3594 if (!layer) return;
Chris@123 3595
Chris@123 3596 Model *model = layer->getModel();
Chris@123 3597 if (!model) return;
Chris@123 3598
Chris@124 3599 TabularModel *tabular = dynamic_cast<TabularModel *>(model);
Chris@124 3600 if (!tabular) {
Chris@124 3601 //!!! how to prevent this function from being active if not
Chris@124 3602 //appropriate model type? or will we ultimately support
Chris@124 3603 //tabular display for all editable models?
Chris@233 3604 SVDEBUG << "NOTE: Not a tabular model" << endl;
Chris@124 3605 return;
Chris@124 3606 }
Chris@124 3607
Chris@123 3608 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@126 3609 if (!m_layerDataDialogMap[layer].isNull()) {
Chris@126 3610 m_layerDataDialogMap[layer]->show();
Chris@126 3611 m_layerDataDialogMap[layer]->raise();
Chris@126 3612 return;
Chris@126 3613 }
Chris@123 3614 }
Chris@123 3615
Chris@125 3616 QString title = layer->getLayerPresentationName();
Chris@125 3617
Chris@125 3618 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
Chris@128 3619 dialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@128 3620
Chris@128 3621 connectLayerEditDialog(dialog);
Chris@123 3622
Chris@128 3623 m_layerDataDialogMap[layer] = dialog;
Chris@128 3624 m_viewDataDialogMap[pane].insert(dialog);
Chris@128 3625
Chris@128 3626 dialog->show();
Chris@128 3627 }
Chris@128 3628
Chris@128 3629 void
Chris@128 3630 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@128 3631 {
Chris@123 3632 connect(m_viewManager,
Chris@435 3633 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
Chris@123 3634 dialog,
Chris@435 3635 SLOT(userScrolledToFrame(sv_frame_t)));
Chris@127 3636
Chris@127 3637 connect(m_viewManager,
Chris@435 3638 SIGNAL(playbackFrameChanged(sv_frame_t)),
Chris@127 3639 dialog,
Chris@435 3640 SLOT(playbackScrolledToFrame(sv_frame_t)));
Chris@127 3641
Chris@123 3642 connect(dialog,
Chris@435 3643 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@123 3644 m_viewManager,
Chris@435 3645 SLOT(setGlobalCentreFrame(sv_frame_t)));
Chris@129 3646
Chris@129 3647 connect(dialog,
Chris@435 3648 SIGNAL(scrollToFrame(sv_frame_t)),
Chris@129 3649 m_viewManager,
Chris@435 3650 SLOT(setPlaybackFrame(sv_frame_t)));
Chris@128 3651 }
Chris@123 3652
Chris@123 3653 void
Chris@73 3654 MainWindowBase::previousPane()
Chris@73 3655 {
Chris@73 3656 if (!m_paneStack) return;
Chris@73 3657
Chris@73 3658 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3659 if (!currentPane) return;
Chris@73 3660
Chris@73 3661 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3662 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3663 if (i == 0) return;
Chris@73 3664 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
Chris@73 3665 updateMenuStates();
Chris@73 3666 return;
Chris@73 3667 }
Chris@73 3668 }
Chris@73 3669 }
Chris@73 3670
Chris@73 3671 void
Chris@73 3672 MainWindowBase::nextPane()
Chris@73 3673 {
Chris@73 3674 if (!m_paneStack) return;
Chris@73 3675
Chris@73 3676 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3677 if (!currentPane) return;
Chris@73 3678
Chris@73 3679 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@73 3680 if (m_paneStack->getPane(i) == currentPane) {
Chris@73 3681 if (i == m_paneStack->getPaneCount()-1) return;
Chris@73 3682 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
Chris@73 3683 updateMenuStates();
Chris@73 3684 return;
Chris@73 3685 }
Chris@73 3686 }
Chris@73 3687 }
Chris@73 3688
Chris@73 3689 void
Chris@73 3690 MainWindowBase::previousLayer()
Chris@73 3691 {
Chris@73 3692 if (!m_paneStack) return;
Chris@73 3693
Chris@73 3694 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3695 if (!currentPane) return;
Chris@73 3696
Chris@403 3697 int count = currentPane->getLayerCount();
Chris@403 3698 if (count == 0) return;
Chris@403 3699
Chris@73 3700 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3701
Chris@403 3702 if (!currentLayer) {
Chris@403 3703 // The pane itself is current
Chris@403 3704 m_paneStack->setCurrentLayer
Chris@403 3705 (currentPane, currentPane->getFixedOrderLayer(count-1));
Chris@403 3706 } else {
Chris@403 3707 for (int i = 0; i < count; ++i) {
Chris@403 3708 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3709 if (i == 0) {
Chris@403 3710 m_paneStack->setCurrentLayer
Chris@636 3711 (currentPane, nullptr); // pane
Chris@403 3712 } else {
Chris@403 3713 m_paneStack->setCurrentLayer
Chris@403 3714 (currentPane, currentPane->getFixedOrderLayer(i-1));
Chris@403 3715 }
Chris@403 3716 break;
Chris@403 3717 }
Chris@73 3718 }
Chris@73 3719 }
Chris@403 3720
Chris@403 3721 updateMenuStates();
Chris@73 3722 }
Chris@73 3723
Chris@73 3724 void
Chris@73 3725 MainWindowBase::nextLayer()
Chris@73 3726 {
Chris@73 3727 if (!m_paneStack) return;
Chris@73 3728
Chris@73 3729 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@73 3730 if (!currentPane) return;
Chris@73 3731
Chris@403 3732 int count = currentPane->getLayerCount();
Chris@403 3733 if (count == 0) return;
Chris@403 3734
Chris@73 3735 Layer *currentLayer = currentPane->getSelectedLayer();
Chris@403 3736
Chris@403 3737 if (!currentLayer) {
Chris@403 3738 // The pane itself is current
Chris@403 3739 m_paneStack->setCurrentLayer
Chris@403 3740 (currentPane, currentPane->getFixedOrderLayer(0));
Chris@403 3741 } else {
Chris@403 3742 for (int i = 0; i < count; ++i) {
Chris@403 3743 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
Chris@403 3744 if (i == currentPane->getLayerCount()-1) {
Chris@403 3745 m_paneStack->setCurrentLayer
Chris@636 3746 (currentPane, nullptr); // pane
Chris@403 3747 } else {
Chris@403 3748 m_paneStack->setCurrentLayer
Chris@403 3749 (currentPane, currentPane->getFixedOrderLayer(i+1));
Chris@403 3750 }
Chris@403 3751 break;
Chris@403 3752 }
Chris@73 3753 }
Chris@73 3754 }
Chris@403 3755
Chris@403 3756 updateMenuStates();
Chris@73 3757 }
Chris@73 3758
Chris@73 3759 void
Chris@435 3760 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
Chris@45 3761 {
Chris@45 3762 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@45 3763
Chris@187 3764 updatePositionStatusDisplays();
Chris@187 3765
Chris@45 3766 RealTime now = RealTime::frame2RealTime
Chris@45 3767 (frame, getMainModel()->getSampleRate());
Chris@45 3768
Chris@45 3769 if (now.sec == m_lastPlayStatusSec) return;
Chris@45 3770
Chris@45 3771 RealTime then = RealTime::frame2RealTime
Chris@45 3772 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@45 3773
Chris@45 3774 QString nowStr;
Chris@45 3775 QString thenStr;
Chris@45 3776 QString remainingStr;
Chris@45 3777
Chris@45 3778 if (then.sec > 10) {
Chris@45 3779 nowStr = now.toSecText().c_str();
Chris@45 3780 thenStr = then.toSecText().c_str();
Chris@45 3781 remainingStr = (then - now).toSecText().c_str();
Chris@45 3782 m_lastPlayStatusSec = now.sec;
Chris@45 3783 } else {
Chris@45 3784 nowStr = now.toText(true).c_str();
Chris@45 3785 thenStr = then.toText(true).c_str();
Chris@45 3786 remainingStr = (then - now).toText(true).c_str();
Chris@45 3787 }
Chris@45 3788
Chris@45 3789 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@45 3790 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@45 3791
Chris@378 3792 getStatusLabel()->setText(m_myStatusMessage);
Chris@45 3793 }
Chris@45 3794
Chris@45 3795 void
Chris@486 3796 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
Chris@486 3797 {
Chris@486 3798 RealTime duration = RealTime::frame2RealTime(frame, rate);
Chris@486 3799 QString durStr = duration.toSecText().c_str();
Chris@486 3800
Chris@486 3801 m_myStatusMessage = tr("Recording: %1").arg(durStr);
Chris@486 3802
Chris@486 3803 getStatusLabel()->setText(m_myStatusMessage);
Chris@486 3804 }
Chris@486 3805
Chris@486 3806 void
Chris@435 3807 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
Chris@45 3808 {
Chris@45 3809 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3810 Pane *p = nullptr;
Chris@45 3811 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3812 if (!p->getFollowGlobalPan()) return;
Chris@45 3813 updateVisibleRangeDisplay(p);
Chris@45 3814 }
Chris@45 3815
Chris@45 3816 void
Chris@435 3817 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
Chris@45 3818 {
Chris@233 3819 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
Chris@123 3820
Chris@123 3821 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
Chris@123 3822 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
Chris@123 3823 i != m_viewDataDialogMap[v].end(); ++i) {
Chris@127 3824 (*i)->userScrolledToFrame(frame);
Chris@123 3825 }
Chris@123 3826 }
Chris@45 3827 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3828 Pane *p = nullptr;
Chris@45 3829 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3830 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 3831 }
Chris@45 3832
Chris@45 3833 void
Chris@624 3834 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
Chris@45 3835 {
Chris@45 3836 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@636 3837 Pane *p = nullptr;
Chris@45 3838 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@45 3839 if (v == p) updateVisibleRangeDisplay(p);
Chris@45 3840 }
Chris@45 3841
Chris@45 3842 void
Chris@45 3843 MainWindowBase::layerAdded(Layer *)
Chris@45 3844 {
Chris@233 3845 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
Chris@45 3846 updateMenuStates();
Chris@45 3847 }
Chris@45 3848
Chris@45 3849 void
Chris@45 3850 MainWindowBase::layerRemoved(Layer *)
Chris@45 3851 {
Chris@233 3852 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
Chris@45 3853 updateMenuStates();
Chris@45 3854 }
Chris@45 3855
Chris@45 3856 void
Chris@45 3857 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
Chris@45 3858 {
Chris@233 3859 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
Chris@123 3860
Chris@128 3861 removeLayerEditDialog(layer);
Chris@123 3862
Chris@47 3863 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
Chris@595 3864 // cerr << "(this is the time ruler layer)" << endl;
Chris@636 3865 m_timeRulerLayer = nullptr;
Chris@45 3866 }
Chris@45 3867 }
Chris@45 3868
Chris@45 3869 void
Chris@45 3870 MainWindowBase::layerInAView(Layer *layer, bool inAView)
Chris@45 3871 {
Chris@233 3872 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
Chris@128 3873
Chris@128 3874 if (!inAView) removeLayerEditDialog(layer);
Chris@45 3875
Chris@45 3876 // Check whether we need to add or remove model from play source
Chris@45 3877 Model *model = layer->getModel();
Chris@45 3878 if (model) {
Chris@45 3879 if (inAView) {
Chris@45 3880 m_playSource->addModel(model);
Chris@45 3881 } else {
Chris@45 3882 bool found = false;
Chris@45 3883 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@45 3884 Pane *pane = m_paneStack->getPane(i);
Chris@45 3885 if (!pane) continue;
Chris@45 3886 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@45 3887 Layer *pl = pane->getLayer(j);
Chris@183 3888 if (pl &&
Chris@183 3889 !dynamic_cast<TimeRulerLayer *>(pl) &&
Chris@183 3890 (pl->getModel() == model)) {
Chris@45 3891 found = true;
Chris@45 3892 break;
Chris@45 3893 }
Chris@45 3894 }
Chris@45 3895 if (found) break;
Chris@45 3896 }
Chris@173 3897 if (!found) {
Chris@173 3898 m_playSource->removeModel(model);
Chris@173 3899 }
Chris@45 3900 }
Chris@45 3901 }
Chris@45 3902
Chris@45 3903 updateMenuStates();
Chris@45 3904 }
Chris@45 3905
Chris@45 3906 void
Chris@128 3907 MainWindowBase::removeLayerEditDialog(Layer *layer)
Chris@128 3908 {
Chris@128 3909 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
Chris@128 3910
Chris@128 3911 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
Chris@128 3912
Chris@128 3913 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
Chris@128 3914 vi != m_viewDataDialogMap.end(); ++vi) {
Chris@128 3915 vi->second.erase(dialog);
Chris@128 3916 }
Chris@128 3917
Chris@128 3918 m_layerDataDialogMap.erase(layer);
Chris@128 3919 delete dialog;
Chris@128 3920 }
Chris@128 3921 }
Chris@128 3922
Chris@128 3923 void
Chris@45 3924 MainWindowBase::modelAdded(Model *model)
Chris@45 3925 {
Chris@233 3926 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
Chris@595 3927 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
Chris@45 3928 m_playSource->addModel(model);
Chris@45 3929 }
Chris@45 3930
Chris@45 3931 void
Chris@45 3932 MainWindowBase::mainModelChanged(WaveFileModel *model)
Chris@45 3933 {
Chris@233 3934 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
Chris@45 3935 updateDescriptionLabel();
Chris@45 3936 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@475 3937 if (model && !(m_playTarget || m_audioIO) &&
Chris@475 3938 (m_soundOptions & WithAudioOutput)) {
Chris@475 3939 createAudioIO();
Chris@360 3940 }
Chris@45 3941 }
Chris@45 3942
Chris@45 3943 void
Chris@45 3944 MainWindowBase::modelAboutToBeDeleted(Model *model)
Chris@45 3945 {
Chris@233 3946 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
Chris@45 3947 if (model == m_viewManager->getPlaybackModel()) {
Chris@636 3948 m_viewManager->setPlaybackModel(nullptr);
Chris@45 3949 }
Chris@45 3950 m_playSource->removeModel(model);
Chris@45 3951 }
Chris@45 3952
Chris@45 3953 void
Chris@55 3954 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
Chris@55 3955 {
Chris@55 3956 bool found = false;
Chris@55 3957 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@55 3958 if (m_paneStack->getPane(i) == pane) {
Chris@55 3959 found = true;
Chris@55 3960 break;
Chris@55 3961 }
Chris@55 3962 }
Chris@55 3963 if (!found) {
Chris@233 3964 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
Chris@229 3965 << pane << endl;
Chris@55 3966 return;
Chris@55 3967 }
Chris@55 3968
Chris@55 3969 CommandHistory::getInstance()->startCompoundOperation
Chris@595 3970 (tr("Delete Pane"), true);
Chris@55 3971
Chris@55 3972 while (pane->getLayerCount() > 0) {
Chris@637 3973 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@55 3974 if (layer) {
Chris@55 3975 m_document->removeLayerFromView(pane, layer);
Chris@55 3976 } else {
Chris@55 3977 break;
Chris@55 3978 }
Chris@55 3979 }
Chris@55 3980
Chris@55 3981 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@55 3982 CommandHistory::getInstance()->addCommand(command);
Chris@55 3983
Chris@55 3984 CommandHistory::getInstance()->endCompoundOperation();
Chris@55 3985
Chris@55 3986 updateMenuStates();
Chris@55 3987 }
Chris@55 3988
Chris@55 3989 void
Chris@429 3990 MainWindowBase::alignmentComplete(AlignmentModel *model)
Chris@429 3991 {
Chris@429 3992 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
Chris@429 3993 }
Chris@429 3994
Chris@429 3995 void
Chris@45 3996 MainWindowBase::pollOSC()
Chris@45 3997 {
Chris@45 3998 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@233 3999 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
Chris@45 4000
Chris@45 4001 if (m_openingAudioFile) return;
Chris@45 4002
Chris@45 4003 OSCMessage message = m_oscQueue->readMessage();
Chris@45 4004
Chris@45 4005 if (message.getTarget() != 0) {
Chris@45 4006 return; //!!! for now -- this class is target 0, others not handled yet
Chris@45 4007 }
Chris@45 4008
Chris@45 4009 handleOSCMessage(message);
Chris@45 4010 }
Chris@45 4011
Chris@45 4012 void
Chris@45 4013 MainWindowBase::inProgressSelectionChanged()
Chris@45 4014 {
Chris@636 4015 Pane *currentPane = nullptr;
Chris@45 4016 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
justin@331 4017 if (currentPane) {
justin@331 4018 //cerr << "JTEST: mouse event on selection pane" << endl;
justin@331 4019 updateVisibleRangeDisplay(currentPane);
justin@331 4020 }
Chris@45 4021 }
Chris@45 4022
Chris@45 4023 void
Chris@45 4024 MainWindowBase::contextHelpChanged(const QString &s)
Chris@45 4025 {
Chris@378 4026 QLabel *lab = getStatusLabel();
Chris@375 4027
Chris@45 4028 if (s == "" && m_myStatusMessage != "") {
Chris@378 4029 if (lab->text() != m_myStatusMessage) {
Chris@378 4030 lab->setText(m_myStatusMessage);
Chris@375 4031 }
Chris@45 4032 return;
Chris@45 4033 }
Chris@375 4034
Chris@378 4035 lab->setText(s);
Chris@45 4036 }
Chris@45 4037
Chris@45 4038 void
Chris@45 4039 MainWindowBase::openHelpUrl(QString url)
Chris@45 4040 {
Chris@45 4041 // This method mostly lifted from Qt Assistant source code
Chris@45 4042
Chris@45 4043 QProcess *process = new QProcess(this);
Chris@45 4044 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@45 4045
Chris@45 4046 QStringList args;
Chris@45 4047
Chris@45 4048 #ifdef Q_OS_MAC
Chris@45 4049 args.append(url);
Chris@45 4050 process->start("open", args);
Chris@45 4051 #else
Chris@45 4052 #ifdef Q_OS_WIN32
Chris@599 4053 std::string pfiles;
Chris@599 4054 (void)getEnvUtf8("ProgramFiles", pfiles);
Chris@599 4055 QString command =
Chris@599 4056 QString::fromStdString(pfiles) +
Chris@599 4057 QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@358 4058
Chris@358 4059 args.append(url);
Chris@358 4060 process->start(command, args);
Chris@45 4061 #else
Chris@45 4062 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@45 4063 args.append("exec");
Chris@45 4064 args.append(url);
Chris@45 4065 process->start("kfmclient", args);
Chris@45 4066 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@45 4067 args.append(url);
Chris@45 4068 process->start(qgetenv("BROWSER"), args);
Chris@45 4069 } else {
Chris@45 4070 args.append(url);
Chris@45 4071 process->start("firefox", args);
Chris@45 4072 }
Chris@45 4073 #endif
Chris@45 4074 #endif
Chris@45 4075 }
Chris@45 4076
Chris@483 4077 void
Chris@483 4078 MainWindowBase::openLocalFolder(QString path)
Chris@483 4079 {
Chris@483 4080 QDir d(path);
Chris@483 4081 if (d.exists()) {
Chris@483 4082 QStringList args;
Chris@483 4083 QString path = d.canonicalPath();
Chris@483 4084 #if defined Q_OS_WIN32
Chris@483 4085 // Although the Win32 API is quite happy to have
Chris@483 4086 // forward slashes as directory separators, Windows
Chris@483 4087 // Explorer is not
Chris@483 4088 path = path.replace('/', '\\');
Chris@483 4089 args << path;
Chris@483 4090 QProcess::execute("c:/windows/explorer.exe", args);
Chris@483 4091 #else
Chris@483 4092 args << path;
Chris@605 4093 QProcess process;
Chris@605 4094 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@606 4095 env.insert("LD_LIBRARY_PATH", "");
Chris@605 4096 process.setProcessEnvironment(env);
Chris@605 4097 process.start(
Chris@483 4098 #if defined Q_OS_MAC
Chris@483 4099 "/usr/bin/open",
Chris@483 4100 #else
Chris@483 4101 "/usr/bin/xdg-open",
Chris@483 4102 #endif
Chris@483 4103 args);
Chris@608 4104 process.waitForFinished();
Chris@483 4105 #endif
Chris@483 4106 }
Chris@483 4107 }
Chris@483 4108